Commit | Line | Data |
---|---|---|
7329404e BB |
1 | /****************************************************************************** |
2 | * Copyright (c) 2000-2016 Ericsson Telecom AB | |
3 | * All rights reserved. This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License v1.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * http://www.eclipse.org/legal/epl-v10.html | |
7 | * | |
8 | * Contributors: | |
9 | * | |
10 | * Baranyi, Botond – initial implementation | |
11 | * | |
12 | ******************************************************************************/ | |
13 | ||
14 | #include "Debugger.hh" | |
15 | ||
16 | ////////////////////////////////////////////////////// | |
17 | ////////////////// TTCN3_Debugger //////////////////// | |
18 | ////////////////////////////////////////////////////// | |
19 | ||
20 | TTCN3_Debugger ttcn3_debugger; | |
21 | ||
22 | void TTCN3_Debugger::switch_off() | |
23 | { | |
24 | if (!active) { | |
25 | print("The debugger is already switched off.\n"); | |
26 | } | |
27 | else { | |
28 | print("Debugger switched off.\n"); | |
29 | } | |
30 | active = false; | |
31 | } | |
32 | ||
33 | void TTCN3_Debugger::switch_on() | |
34 | { | |
35 | if (active) { | |
36 | print("The debugger is already switched on.\n"); | |
37 | } | |
38 | else { | |
39 | print("Debugger switched on.\n"); | |
40 | } | |
41 | active = true; | |
42 | } | |
43 | ||
44 | void TTCN3_Debugger::add_breakpoint(const char* p_module, int p_line /*const char* batch_file*/) | |
45 | { | |
46 | if (find_breakpoint(p_module, p_line) == breakpoints.size()) { | |
47 | breakpoint_t bp; | |
48 | bp.module = mcopystr(p_module); | |
49 | bp.line = p_line; | |
50 | breakpoints.push_back(bp); | |
51 | print("Breakpoint added in module '%s' at line %d.\n", p_module, p_line); | |
52 | } | |
53 | else { | |
54 | print("Breakpoint already set in module '%s' at line %d.\n", p_module, p_line); | |
55 | } | |
56 | } | |
57 | ||
58 | void TTCN3_Debugger::remove_breakpoint(const char* p_module, int p_line) | |
59 | { | |
60 | size_t pos = find_breakpoint(p_module, p_line); | |
61 | if (pos != breakpoints.size()) { | |
62 | Free(breakpoints[pos].module); | |
63 | breakpoints.erase_at(pos); | |
64 | print("Breakpoint removed in module '%s' from line %d.\n", p_module, p_line); | |
65 | } | |
66 | else { | |
67 | print("No breakpoint found in module '%s' at line %d.\n", p_module, p_line); | |
68 | } | |
69 | } | |
70 | ||
71 | void TTCN3_Debugger::set_special_breakpoint(special_breakpoint_t p_type, const char* p_state_str) | |
72 | { | |
73 | bool new_state; | |
74 | if (!strcmp(p_state_str, "yes")) { | |
75 | new_state = true; | |
76 | } | |
77 | else if(!strcmp(p_state_str, "no")) { | |
78 | new_state = false; | |
79 | } | |
80 | // else if "batch" | |
81 | else { | |
82 | print("Argument 1 is invalid.\n"); | |
83 | return; | |
84 | } | |
85 | const char* sbp_type_str; | |
86 | bool state_changed; | |
87 | switch (p_type) { | |
88 | case SBP_FAIL_VERDICT: | |
89 | state_changed = (fail_behavior != new_state); | |
90 | fail_behavior = new_state; | |
91 | sbp_type_str = "Fail"; | |
92 | break; | |
93 | case SBP_ERROR_VERDICT: | |
94 | state_changed = (error_behavior != new_state); | |
95 | error_behavior = new_state; | |
96 | sbp_type_str = "Error"; | |
97 | break; | |
98 | default: | |
99 | // should never happen | |
100 | return; | |
101 | } | |
102 | print("%s verdict behavior %sset to %s.\n", sbp_type_str, | |
103 | state_changed ? "" : "was already ", | |
104 | new_state ? "halt the program" : "do nothing"); | |
105 | } | |
106 | ||
107 | void TTCN3_Debugger::print_call_stack() | |
108 | { | |
109 | for (size_t i = call_stack.size(); i != 0; --i) { | |
110 | print("%d.\t", (int)call_stack.size() - (int)i + 1); | |
111 | call_stack[i - 1]->print_function(); | |
112 | } | |
113 | } | |
114 | ||
115 | void TTCN3_Debugger::set_stack_level(int new_level) | |
116 | { | |
117 | if (new_level < 0 || (size_t)new_level > call_stack.size()) { | |
118 | print("Invalid new stack level.\n"); | |
119 | } | |
120 | else { | |
121 | stack_level = new_level; | |
122 | } | |
123 | } | |
124 | ||
125 | void TTCN3_Debugger::print_variable(const TTCN3_Debugger::variable_t* p_var) const | |
126 | { | |
127 | print("%s := %s\n", p_var->name, (const char*)p_var->print_function(*p_var)); | |
128 | } | |
129 | ||
130 | void TTCN3_Debugger::set_output(const char* p_output_type, const char* p_file_name) | |
131 | { | |
132 | FILE* new_fp; | |
133 | if (!strcmp(p_output_type, "stdout")) { | |
134 | new_fp = stdout; | |
135 | } | |
136 | else if (!strcmp(p_output_type, "stderr")) { | |
137 | new_fp = stderr; | |
138 | } | |
139 | else if (!strcmp(p_output_type, "file")) { | |
140 | if (p_file_name == NULL) { | |
141 | print("Missing output file name.\n"); | |
142 | return; | |
143 | } | |
144 | new_fp = fopen(p_file_name, "w"); | |
145 | if (new_fp == NULL) { | |
146 | print("Failed to open file '%s' for writing.\n"); | |
147 | return; | |
148 | } | |
149 | } | |
150 | else { | |
151 | print("Argument 1 is invalid.\n"); | |
152 | return; | |
153 | } | |
154 | // don't close the previous file, if the command's parameters are invalid | |
155 | if (output != stdout && output != stderr) { | |
156 | fclose(output); | |
157 | } | |
158 | output = new_fp; | |
159 | } | |
160 | ||
161 | size_t TTCN3_Debugger::find_breakpoint(const char* p_module, int p_line) const | |
162 | { | |
163 | for (size_t i = 0; i < breakpoints.size(); ++i) { | |
164 | if (!strcmp(breakpoints[i].module, p_module) && breakpoints[i].line == p_line) { | |
165 | return i; | |
166 | } | |
167 | } | |
168 | return breakpoints.size(); | |
169 | } | |
170 | ||
171 | TTCN3_Debugger::variable_t* TTCN3_Debugger::find_variable(const void* p_value) const | |
172 | { | |
173 | for (size_t i = 0; i < variables.size(); ++i) { | |
174 | if (variables[i]->value == p_value) { | |
175 | return variables[i]; | |
176 | } | |
177 | } | |
178 | return NULL; | |
179 | } | |
180 | ||
181 | TTCN3_Debugger::TTCN3_Debugger() | |
182 | { | |
183 | active = false; | |
184 | output = stderr; | |
185 | snapshots = NULL; | |
186 | last_breakpoint_entry.module = NULL; | |
187 | last_breakpoint_entry.line = 0; | |
188 | stack_level = -1; | |
189 | fail_behavior = false; | |
190 | error_behavior = false; | |
191 | } | |
192 | ||
193 | TTCN3_Debugger::~TTCN3_Debugger() | |
194 | { | |
195 | if (output != stdout && output != stderr) { | |
196 | fclose(output); | |
197 | } | |
198 | for (size_t i = 0; i < breakpoints.size(); ++i) { | |
199 | Free(breakpoints[i].module); | |
200 | } | |
201 | for (size_t i = 0; i < global_scopes.size(); ++i) { | |
202 | delete global_scopes[i].scope; | |
203 | } | |
204 | for (size_t i = 0; i < component_scopes.size(); ++i) { | |
205 | delete component_scopes[i].scope; | |
206 | } | |
207 | for (size_t i = 0; i < variables.size(); ++i) { | |
208 | delete variables[i]; | |
209 | } | |
210 | Free(snapshots); | |
211 | } | |
212 | ||
213 | TTCN3_Debug_Scope* TTCN3_Debugger::add_global_scope(const char* p_module) | |
214 | { | |
215 | named_scope_t global_scope; | |
216 | global_scope.name = p_module; | |
217 | global_scope.scope = new TTCN3_Debug_Scope(); | |
218 | global_scopes.push_back(global_scope); | |
219 | return global_scope.scope; | |
220 | } | |
221 | ||
222 | TTCN3_Debug_Scope* TTCN3_Debugger::add_component_scope(const char* p_component) | |
223 | { | |
224 | named_scope_t component_scope; | |
225 | component_scope.name = p_component; | |
226 | component_scope.scope = new TTCN3_Debug_Scope(); | |
227 | component_scopes.push_back(component_scope); | |
228 | return component_scope.scope; | |
229 | } | |
230 | ||
231 | void TTCN3_Debugger::set_return_value(const CHARSTRING& p_value) | |
232 | { | |
233 | if (active) { | |
234 | call_stack[call_stack.size() - 1]->set_return_value(p_value); | |
235 | } | |
236 | } | |
237 | ||
238 | void TTCN3_Debugger::breakpoint_entry(int p_line /*bool p_stepping_helper*/) | |
239 | { | |
240 | if (active && !call_stack.empty()) { | |
241 | const char* module_name = call_stack[call_stack.size() - 1]->get_module_name(); | |
242 | bool trigger = false; | |
243 | const char* trigger_type; | |
244 | int actual_line; | |
245 | switch (p_line) { | |
246 | case SBP_FAIL_VERDICT: | |
247 | trigger = fail_behavior; | |
248 | trigger_type = "Fail verdict"; | |
249 | actual_line = last_breakpoint_entry.line; | |
250 | break; | |
251 | case SBP_ERROR_VERDICT: | |
252 | trigger = error_behavior; | |
253 | trigger_type = "Error verdict"; | |
254 | actual_line = last_breakpoint_entry.line; | |
255 | break; | |
256 | default: | |
257 | // code lines | |
258 | trigger = (last_breakpoint_entry.line == 0 || p_line != last_breakpoint_entry.line || | |
259 | module_name != last_breakpoint_entry.module) && | |
260 | find_breakpoint(module_name, p_line) != breakpoints.size(); | |
261 | trigger_type = "Breakpoint"; | |
262 | actual_line = p_line; | |
263 | break; | |
264 | } | |
265 | // make sure it's not the same breakpoint entry as last time | |
266 | if (trigger) { | |
267 | stack_level = call_stack.size() - 1; | |
268 | print("%s reached in module '%s' at line %d.\n", trigger_type, | |
269 | module_name, actual_line); | |
270 | /////////////////////////////////////////////////////////////////////////////////// | |
271 | /*print("##################################################\n"); | |
272 | print("Call stack:\n"); | |
273 | charstring_list params = NULL_VALUE; | |
274 | execute_command(D_PRINT_CALL_STACK, params); | |
275 | print("##################################################\n"); | |
276 | print("Variables: "); | |
277 | params[0] = "global"; | |
278 | execute_command(D_LIST_VARIABLES, params); | |
279 | params.set_size(0); | |
280 | size_t idx = 0; | |
281 | const TTCN3_Debug_Scope* glob_scope = get_global_scope(module_name); | |
282 | for (size_t i = 0; i < variables.size(); ++i) { | |
283 | if (glob_scope->find_variable(variables[i]->name) != NULL) { | |
284 | params[idx++] = variables[i]->name; | |
285 | } | |
286 | } | |
287 | execute_command(D_PRINT_VARIABLE, params); | |
288 | print("##################################################\n"); | |
289 | print("Function call snapshots:\n"); | |
290 | params.set_size(0); | |
291 | execute_command(D_PRINT_SNAPSHOTS, params);*/ | |
292 | /////////////////////////////////////////////////////////////////////////////////// | |
293 | } | |
294 | last_breakpoint_entry.module = (char*)module_name; | |
295 | last_breakpoint_entry.line = p_line; | |
296 | } | |
297 | } | |
298 | ||
299 | CHARSTRING TTCN3_Debugger::print_base_var(const TTCN3_Debugger::variable_t& p_var) | |
300 | { | |
301 | TTCN_Logger::begin_event_log2str(); | |
302 | if (!strcmp(p_var.type_name, "bitstring")) { | |
303 | ((const BITSTRING*)p_var.value)->log(); | |
304 | } | |
305 | else if (!strcmp(p_var.type_name, "bitstring template")) { | |
306 | ((const BITSTRING_template*)p_var.value)->log(); | |
307 | } | |
308 | else if (!strcmp(p_var.type_name, "boolean")) { | |
309 | ((const BOOLEAN*)p_var.value)->log(); | |
310 | } | |
311 | else if (!strcmp(p_var.type_name, "boolean template")) { | |
312 | ((const BOOLEAN_template*)p_var.value)->log(); | |
313 | } | |
314 | else if (!strcmp(p_var.type_name, "charstring")) { | |
315 | ((const CHARSTRING*)p_var.value)->log(); | |
316 | } | |
317 | else if (!strcmp(p_var.type_name, "charstring template")) { | |
318 | ((const CHARSTRING_template*)p_var.value)->log(); | |
319 | } | |
320 | else if (!strcmp(p_var.type_name, "float")) { | |
321 | ((const FLOAT*)p_var.value)->log(); | |
322 | } | |
323 | else if (!strcmp(p_var.type_name, "float template")) { | |
324 | ((const FLOAT_template*)p_var.value)->log(); | |
325 | } | |
326 | else if (!strcmp(p_var.type_name, "hexstring")) { | |
327 | ((const HEXSTRING*)p_var.value)->log(); | |
328 | } | |
329 | else if (!strcmp(p_var.type_name, "hexstring template")) { | |
330 | ((const HEXSTRING_template*)p_var.value)->log(); | |
331 | } | |
332 | else if (!strcmp(p_var.type_name, "integer")) { | |
333 | ((const INTEGER*)p_var.value)->log(); | |
334 | } | |
335 | else if (!strcmp(p_var.type_name, "integer template")) { | |
336 | ((const INTEGER_template*)p_var.value)->log(); | |
337 | } | |
338 | else if (!strcmp(p_var.type_name, "objid")) { | |
339 | ((const OBJID*)p_var.value)->log(); | |
340 | } | |
341 | else if (!strcmp(p_var.type_name, "objid template")) { | |
342 | ((const OBJID_template*)p_var.value)->log(); | |
343 | } | |
344 | else if (!strcmp(p_var.type_name, "octetstring")) { | |
345 | ((const OCTETSTRING*)p_var.value)->log(); | |
346 | } | |
347 | else if (!strcmp(p_var.type_name, "octetstring template")) { | |
348 | ((const OCTETSTRING_template*)p_var.value)->log(); | |
349 | } | |
350 | else if (!strcmp(p_var.type_name, "universal charstring")) { | |
351 | ((const UNIVERSAL_CHARSTRING*)p_var.value)->log(); | |
352 | } | |
353 | else if (!strcmp(p_var.type_name, "universal charstring template")) { | |
354 | ((const UNIVERSAL_CHARSTRING_template*)p_var.value)->log(); | |
355 | } | |
356 | else if (!strcmp(p_var.type_name, "verdicttype")) { | |
357 | ((const VERDICTTYPE*)p_var.value)->log(); | |
358 | } | |
359 | else if (!strcmp(p_var.type_name, "verdicttype template")) { | |
360 | ((const VERDICTTYPE_template*)p_var.value)->log(); | |
361 | } | |
362 | else if (!strcmp(p_var.type_name, "component")) { | |
363 | ((const COMPONENT*)p_var.value)->log(); | |
364 | } | |
365 | else if (!strcmp(p_var.type_name, "component template")) { | |
366 | ((const COMPONENT_template*)p_var.value)->log(); | |
367 | } | |
368 | else if (!strcmp(p_var.type_name, "default")) { | |
369 | ((const DEFAULT*)p_var.value)->log(); | |
370 | } | |
371 | else if (!strcmp(p_var.type_name, "default template")) { | |
372 | ((const DEFAULT_template*)p_var.value)->log(); | |
373 | } | |
374 | else if (!strcmp(p_var.type_name, "timer")) { | |
375 | ((const TIMER*)p_var.value)->log(); | |
376 | } | |
377 | else if (!strcmp(p_var.type_name, "NULL")) { | |
378 | ((const ASN_NULL*)p_var.value)->log(); | |
379 | } | |
380 | else if (!strcmp(p_var.type_name, "NULL template")) { | |
381 | ((const ASN_NULL_template*)p_var.value)->log(); | |
382 | } | |
383 | else if (!strcmp(p_var.type_name, "CHARACTER STRING")) { | |
384 | ((const CHARACTER_STRING*)p_var.value)->log(); | |
385 | } | |
386 | else if (!strcmp(p_var.type_name, "CHARACTER STRING template")) { | |
387 | ((const CHARACTER_STRING_template*)p_var.value)->log(); | |
388 | } | |
389 | else if (!strcmp(p_var.type_name, "EMBEDDED PDV")) { | |
390 | ((const EMBEDDED_PDV*)p_var.value)->log(); | |
391 | } | |
392 | else if (!strcmp(p_var.type_name, "EMBEDDED PDV template")) { | |
393 | ((const EMBEDDED_PDV_template*)p_var.value)->log(); | |
394 | } | |
395 | else if (!strcmp(p_var.type_name, "EXTERNAL")) { | |
396 | ((const EXTERNAL*)p_var.value)->log(); | |
397 | } | |
398 | else if (!strcmp(p_var.type_name, "EXTERNAL template")) { | |
399 | ((const EXTERNAL_template*)p_var.value)->log(); | |
400 | } | |
401 | else { | |
402 | TTCN_Logger::log_event_str("<unrecognized value or template>"); | |
403 | } | |
404 | return TTCN_Logger::end_event_log2str(); | |
405 | } | |
406 | ||
407 | void TTCN3_Debugger::print(const char* fmt, ...) const | |
408 | { | |
409 | va_list parameters; | |
410 | va_start(parameters, fmt); | |
411 | vfprintf(output, fmt, parameters); | |
412 | va_end(parameters); | |
413 | fflush(output); | |
414 | } | |
415 | ||
416 | void TTCN3_Debugger::add_function(TTCN3_Debug_Function* p_function) | |
417 | { | |
418 | if (active) { | |
419 | call_stack.push_back(p_function); | |
420 | } | |
421 | } | |
422 | ||
423 | void TTCN3_Debugger::add_scope(TTCN3_Debug_Scope* p_scope) | |
424 | { | |
425 | if (active && !call_stack.empty()) { | |
426 | call_stack[call_stack.size() - 1]->add_scope(p_scope); | |
427 | } | |
428 | } | |
429 | ||
430 | void TTCN3_Debugger::remove_function(TTCN3_Debug_Function* p_function) | |
431 | { | |
432 | if (!call_stack.empty() && call_stack[call_stack.size() - 1] == p_function) { | |
433 | call_stack.erase_at(call_stack.size() - 1); | |
434 | } | |
435 | } | |
436 | ||
437 | void TTCN3_Debugger::remove_scope(TTCN3_Debug_Scope* p_scope) | |
438 | { | |
439 | if (!call_stack.empty()) { | |
440 | call_stack[call_stack.size() - 1]->remove_scope(p_scope); | |
441 | } | |
442 | } | |
443 | ||
444 | const TTCN3_Debugger::variable_t* TTCN3_Debugger::add_variable(const void* p_value, | |
445 | const char* p_name, | |
446 | const char* p_type, | |
447 | CHARSTRING (*p_print_function)(const TTCN3_Debugger::variable_t&)) | |
448 | { | |
449 | ||
450 | if (call_stack.empty()) { | |
451 | // no call stack yet, so this is a global or component variable | |
452 | variable_t* var = find_variable(p_value); | |
453 | if (var == NULL) { | |
454 | var = new TTCN3_Debugger::variable_t; | |
455 | var->value = p_value; | |
456 | var->name = p_name; | |
457 | var->type_name = p_type; | |
458 | var->print_function = p_print_function; | |
459 | variables.push_back(var); | |
460 | } | |
461 | return var; | |
462 | } | |
463 | else if (active) { | |
464 | // it's a local variable for the top-most function | |
465 | return call_stack[call_stack.size() - 1]->add_variable(p_value, p_name, p_type, p_print_function); | |
466 | } | |
467 | return NULL; | |
468 | } | |
469 | ||
470 | void TTCN3_Debugger::remove_variable(const variable_t* p_var) | |
471 | { | |
472 | if (active && !call_stack.empty()) { | |
473 | call_stack[call_stack.size() - 1]->remove_variable(p_var); | |
474 | } | |
475 | } | |
476 | ||
477 | const TTCN3_Debug_Scope* TTCN3_Debugger::get_global_scope(const char* p_module) const | |
478 | { | |
479 | for (size_t i = 0; i < global_scopes.size(); ++i) { | |
480 | if (strcmp(global_scopes[i].name, p_module) == 0) { | |
481 | return global_scopes[i].scope; | |
482 | } | |
483 | } | |
484 | return NULL; | |
485 | } | |
486 | ||
487 | const TTCN3_Debug_Scope* TTCN3_Debugger::get_component_scope(const char* p_component) const | |
488 | { | |
489 | for (size_t i = 0; i < component_scopes.size(); ++i) { | |
490 | if (strcmp(component_scopes[i].name, p_component) == 0) { | |
491 | return component_scopes[i].scope; | |
492 | } | |
493 | } | |
494 | return NULL; | |
495 | } | |
496 | ||
497 | void TTCN3_Debugger::add_snapshot(const char* p_snapshot) | |
498 | { | |
499 | snapshots = mputstr(snapshots, p_snapshot); | |
500 | } | |
501 | ||
502 | #define CHECK_NOF_ARGUMENTS(exp_num) \ | |
503 | if (exp_num != p_arguments.size_of()) { \ | |
504 | print("Invalid number of arguments. Expected %d, got %d.\n", \ | |
505 | (int)exp_num, (int)p_arguments.size_of()); \ | |
506 | return; \ | |
507 | } | |
508 | ||
509 | #define CHECK_NOF_ARGUMENTS_RANGE(min, max) \ | |
510 | if ((int)min > p_arguments.size_of() || (int)max < p_arguments.size_of()) { \ | |
511 | print("Invalid number of arguments. Expected at least %d and at most %d, got %d.\n", \ | |
512 | (int)min, (int)max, p_arguments.size_of()); \ | |
513 | return; \ | |
514 | } | |
515 | ||
516 | #define CHECK_NOF_ARGUMENTS_MIN(min) \ | |
517 | if ((int)min > p_arguments.size_of()) { \ | |
518 | print("Invalid number of arguments. Expected at least %d, got %d.\n", \ | |
519 | (int)min, p_arguments.size_of()); \ | |
520 | return; \ | |
521 | } | |
522 | ||
523 | #define CHECK_INT_ARGUMENT(arg_idx) \ | |
524 | { \ | |
525 | const char* str = (const char*)p_arguments[arg_idx]; \ | |
526 | for (int i = 0; i < p_arguments[arg_idx].lengthof(); ++i) { \ | |
527 | if (str[i] < '0' || str[i] > '9') { \ | |
528 | print("Argument %d is not an integer.\n", (int)(arg_idx + 1)); \ | |
529 | return; \ | |
530 | } \ | |
531 | } \ | |
532 | } | |
533 | ||
534 | #define CHECK_CALL_STACK \ | |
535 | if (call_stack.empty()) { \ | |
536 | print("This command can only be executed when the program is running.\n"); \ | |
537 | return; \ | |
538 | } | |
539 | ||
540 | void TTCN3_Debugger::execute_command(TTCN3_Debugger::debug_command_t p_command, | |
541 | const charstring_list& p_arguments) | |
542 | { | |
543 | if (!active && p_command != D_SWITCH_ON && p_command != D_SWITCH_OFF) { | |
544 | print("Cannot run debug commands while the debugger is switched off.\n"); | |
545 | return; | |
546 | } | |
547 | for (int i = 0; i < p_arguments.size_of(); ++i) { | |
548 | if (!p_arguments[i].is_bound()) { | |
549 | print("Argument %d is unbound.\n", i + 1); | |
550 | return; | |
551 | } | |
552 | } | |
553 | switch (p_command) { | |
554 | case D_SWITCH_OFF: | |
555 | CHECK_NOF_ARGUMENTS(0) | |
556 | switch_off(); | |
557 | break; | |
558 | case D_SWITCH_ON: | |
559 | CHECK_NOF_ARGUMENTS(0) | |
560 | switch_on(); | |
561 | break; | |
562 | case D_ADD_BREAKPOINT: | |
563 | CHECK_NOF_ARGUMENTS(2) | |
564 | CHECK_INT_ARGUMENT(1) | |
565 | add_breakpoint(p_arguments[0], str2int(p_arguments[1])); | |
566 | break; | |
567 | case D_REMOVE_BREAKPOINT: | |
568 | CHECK_NOF_ARGUMENTS(2) | |
569 | CHECK_INT_ARGUMENT(1) | |
570 | remove_breakpoint(p_arguments[0], str2int(p_arguments[1])); | |
571 | break; | |
572 | case D_SET_ERROR_BEHAVIOR: | |
573 | CHECK_NOF_ARGUMENTS(1) | |
574 | set_special_breakpoint(SBP_ERROR_VERDICT, p_arguments[0]); | |
575 | break; | |
576 | case D_SET_FAIL_BEHAVIOR: | |
577 | CHECK_NOF_ARGUMENTS(1) | |
578 | set_special_breakpoint(SBP_FAIL_VERDICT, p_arguments[0]); | |
579 | break; | |
580 | // ... | |
581 | case D_SET_OUTPUT: | |
582 | CHECK_NOF_ARGUMENTS_RANGE(1, 2) | |
583 | set_output(p_arguments[0], (p_arguments.size_of() == 2) ? (const char*)p_arguments[1] : NULL); | |
584 | break; | |
585 | // ... | |
586 | case D_PRINT_CALL_STACK: | |
587 | CHECK_CALL_STACK | |
588 | CHECK_NOF_ARGUMENTS(0) | |
589 | print_call_stack(); | |
590 | break; | |
591 | case D_SET_STACK_LEVEL: | |
592 | CHECK_CALL_STACK | |
593 | CHECK_NOF_ARGUMENTS(1) | |
594 | CHECK_INT_ARGUMENT(0) | |
595 | set_stack_level(str2int(p_arguments[0])); | |
596 | break; | |
597 | case D_LIST_VARIABLES: | |
598 | CHECK_CALL_STACK | |
599 | CHECK_NOF_ARGUMENTS_RANGE(1, 2) | |
600 | call_stack[stack_level]->list_variables(p_arguments[0], | |
601 | (p_arguments.size_of() == 2) ? (const char*)p_arguments[1] : NULL); | |
602 | break; | |
603 | case D_PRINT_VARIABLE: | |
604 | CHECK_CALL_STACK | |
605 | CHECK_NOF_ARGUMENTS_MIN(1) | |
606 | for (int i = 0; i < p_arguments.size_of(); ++i) { | |
607 | const variable_t* var = call_stack[stack_level]->find_variable(p_arguments[i]); | |
608 | if (var != NULL) { | |
609 | print_variable(var); | |
610 | } | |
611 | else { | |
612 | print("Variable '%s' not found.\n", (const char*)p_arguments[i]); | |
613 | } | |
614 | } | |
615 | break; | |
616 | // ... | |
617 | case D_PRINT_SNAPSHOTS: | |
618 | CHECK_NOF_ARGUMENTS(0) | |
619 | print("%s", snapshots); | |
620 | break; | |
621 | // ... | |
622 | default: | |
623 | print("Command not implemented.\n"); | |
624 | break; | |
625 | } | |
626 | } | |
627 | ||
628 | ////////////////////////////////////////////////////// | |
629 | //////////////// TTCN3_Debug_Scope /////////////////// | |
630 | ////////////////////////////////////////////////////// | |
631 | ||
632 | TTCN3_Debug_Scope::TTCN3_Debug_Scope() | |
633 | { | |
634 | ttcn3_debugger.add_scope(this); | |
635 | } | |
636 | ||
637 | TTCN3_Debug_Scope::~TTCN3_Debug_Scope() | |
638 | { | |
639 | for (size_t i = 0; i < variables.size(); ++i) { | |
640 | ttcn3_debugger.remove_variable(variables[i]); | |
641 | } | |
642 | ttcn3_debugger.remove_scope(this); | |
643 | } | |
644 | ||
645 | void TTCN3_Debug_Scope::add_variable(const void* p_value, | |
646 | const char* p_name, | |
647 | const char* p_type, | |
648 | CHARSTRING (*p_print_function)(const TTCN3_Debugger::variable_t&)) | |
649 | { | |
650 | const TTCN3_Debugger::variable_t* var = ttcn3_debugger.add_variable(p_value, p_name, p_type, p_print_function); | |
651 | if (var != NULL) { | |
652 | variables.push_back(var); | |
653 | } | |
654 | } | |
655 | ||
656 | const TTCN3_Debugger::variable_t* TTCN3_Debug_Scope::find_variable(const char* p_name) const | |
657 | { | |
658 | for (size_t i = 0; i < variables.size(); ++i) { | |
659 | if (strcmp(variables[i]->name, p_name) == 0) { | |
660 | return variables[i]; | |
661 | } | |
662 | } | |
663 | return NULL; | |
664 | } | |
665 | ||
666 | void TTCN3_Debug_Scope::list_variables(const char* p_filter, bool& p_first) const | |
667 | { | |
668 | for (size_t i = 0; i < variables.size(); ++i) { | |
669 | // the filter is currently ignored | |
670 | ttcn3_debugger.print("%s%s", p_first ? "" : " ", variables[i]->name); | |
671 | p_first = false; | |
672 | } | |
673 | } | |
674 | ||
675 | ////////////////////////////////////////////////////// | |
676 | /////////////// TTCN3_Debug_Function ///////////////// | |
677 | ////////////////////////////////////////////////////// | |
678 | ||
679 | TTCN3_Debug_Function::TTCN3_Debug_Function(const char* p_name, | |
680 | const char* p_type, | |
681 | const char* p_module, | |
682 | const charstring_list& p_parameter_names, | |
683 | const charstring_list& p_parameter_types, | |
684 | const char* p_component_name) | |
685 | : function_name(p_name), function_type(p_type), module_name(p_module) | |
686 | , parameter_names(new charstring_list(p_parameter_names)) | |
687 | , parameter_types(new charstring_list(p_parameter_types)) | |
688 | { | |
689 | ttcn3_debugger.add_function(this); | |
690 | global_scope = ttcn3_debugger.get_global_scope(p_module); | |
691 | component_scope = (p_component_name != NULL) ? | |
692 | ttcn3_debugger.get_component_scope(p_component_name) : NULL; | |
693 | if (function_name == NULL) { | |
694 | function_name = p_module; // for control parts | |
695 | } | |
696 | } | |
697 | ||
698 | TTCN3_Debug_Function::~TTCN3_Debug_Function() | |
699 | { | |
700 | if (ttcn3_debugger.is_on()) { | |
701 | char* snapshot = mprintf("[%s]\tfinished\t%s(", function_type, function_name); | |
702 | if (parameter_names->size_of() > 0) { | |
703 | for (int i = 0; i < parameter_names->size_of(); ++i) { | |
704 | if (i > 0) { | |
705 | snapshot = mputstr(snapshot, ", "); | |
706 | } | |
707 | snapshot = mputprintf(snapshot, "[%s] %s := ", (const char*)((*parameter_types)[i]), | |
708 | (const char*)((*parameter_names)[i])); | |
709 | if ((*parameter_types)[i] == "out" || (*parameter_types)[i] == "inout") { | |
710 | const TTCN3_Debugger::variable_t* parameter = find_variable((*parameter_names)[i]); | |
711 | snapshot = mputstr(snapshot, parameter->print_function(*parameter)); | |
712 | } | |
713 | else { | |
714 | snapshot = mputc(snapshot, '-'); | |
715 | } | |
716 | } | |
717 | } | |
718 | snapshot = mputc(snapshot, ')'); | |
719 | if (return_value.is_bound()) { | |
720 | snapshot = mputprintf(snapshot, " returned %s", (const char*)return_value); | |
721 | } | |
722 | snapshot = mputc(snapshot, '\n'); | |
723 | ttcn3_debugger.add_snapshot(snapshot); | |
724 | Free(snapshot); | |
725 | } | |
726 | for (size_t i = 0; i < variables.size(); ++i) { | |
727 | delete variables[i]; | |
728 | } | |
729 | delete parameter_names; | |
730 | delete parameter_types; | |
731 | ttcn3_debugger.remove_function(this); | |
732 | } | |
733 | ||
734 | const TTCN3_Debugger::variable_t* TTCN3_Debug_Function::add_variable(const void* p_value, | |
735 | const char* p_name, | |
736 | const char* p_type, | |
737 | CHARSTRING (*p_print_function)(const TTCN3_Debugger::variable_t&)) | |
738 | { | |
739 | if (ttcn3_debugger.is_on()) { | |
740 | TTCN3_Debugger::variable_t* var = new TTCN3_Debugger::variable_t; | |
741 | var->value = p_value; | |
742 | var->name = p_name; | |
743 | var->type_name = p_type; | |
744 | var->print_function = p_print_function; | |
745 | variables.push_back(var); | |
746 | return var; | |
747 | } | |
748 | return NULL; | |
749 | } | |
750 | ||
751 | void TTCN3_Debug_Function::set_return_value(const CHARSTRING& p_value) | |
752 | { | |
753 | return_value = p_value; | |
754 | } | |
755 | ||
756 | void TTCN3_Debug_Function::initial_snapshot() const | |
757 | { | |
758 | if (ttcn3_debugger.is_on()) { | |
759 | char* snapshot = mprintf("[%s]\tstarted \t%s(", function_type, function_name); | |
760 | if (parameter_names->size_of() > 0) { | |
761 | for (int i = 0; i < parameter_names->size_of(); ++i) { | |
762 | if (i > 0) { | |
763 | snapshot = mputstr(snapshot, ", "); | |
764 | } | |
765 | snapshot = mputprintf(snapshot, "[%s] %s := ", (const char*)((*parameter_types)[i]), | |
766 | (const char*)((*parameter_names)[i])); | |
767 | if ((*parameter_types)[i] == "in" || (*parameter_types)[i] == "inout") { | |
768 | const TTCN3_Debugger::variable_t* parameter = find_variable((*parameter_names)[i]); | |
769 | snapshot = mputstr(snapshot, parameter->print_function(*parameter)); | |
770 | } | |
771 | else { | |
772 | snapshot = mputc(snapshot, '-'); | |
773 | } | |
774 | } | |
775 | } | |
776 | snapshot = mputstr(snapshot, ")\n"); | |
777 | ttcn3_debugger.add_snapshot(snapshot); | |
778 | Free(snapshot); | |
779 | } | |
780 | } | |
781 | ||
782 | void TTCN3_Debug_Function::add_scope(TTCN3_Debug_Scope* p_scope) | |
783 | { | |
784 | scopes.push_back(p_scope); | |
785 | } | |
786 | ||
787 | void TTCN3_Debug_Function::remove_scope(TTCN3_Debug_Scope* p_scope) | |
788 | { | |
789 | if (scopes[scopes.size() - 1] == p_scope) { | |
790 | scopes.erase_at(scopes.size() - 1); | |
791 | } | |
792 | } | |
793 | ||
794 | void TTCN3_Debug_Function::remove_variable(const TTCN3_Debugger::variable_t* p_var) | |
795 | { | |
796 | for (size_t i = 0; i < variables.size(); ++i) { | |
797 | if (variables[i] == p_var) { | |
798 | variables.erase_at(i); | |
799 | delete p_var; | |
800 | break; | |
801 | } | |
802 | } | |
803 | } | |
804 | ||
805 | const TTCN3_Debugger::variable_t* TTCN3_Debug_Function::find_variable(const char* p_name) const | |
806 | { | |
807 | for (size_t i = 0; i < variables.size(); ++i) { | |
808 | if (strcmp(variables[i]->name, p_name) == 0) { | |
809 | return variables[i]; | |
810 | } | |
811 | } | |
812 | // it's not a local variable, it might still be a global or component variable | |
813 | if (component_scope != NULL) { | |
814 | const TTCN3_Debugger::variable_t* res = component_scope->find_variable(p_name); | |
815 | if (res != NULL) { | |
816 | return res; | |
817 | } | |
818 | } | |
819 | return (global_scope != NULL) ? global_scope->find_variable(p_name) : NULL; | |
820 | } | |
821 | ||
822 | void TTCN3_Debug_Function::print_function() const | |
823 | { | |
824 | ttcn3_debugger.print("[%s]\t%s(", function_type, function_name); | |
825 | if (parameter_names->size_of() > 0) { | |
826 | for (int i = 0; i < parameter_names->size_of(); ++i) { | |
827 | if (i > 0) { | |
828 | ttcn3_debugger.print(", "); | |
829 | } | |
830 | const TTCN3_Debugger::variable_t* parameter = find_variable((*parameter_names)[i]); | |
831 | ttcn3_debugger.print("[%s] %s := %s", (const char*)(*parameter_types)[i], | |
832 | (const char*)(*parameter_names)[i], (const char*)parameter->print_function(*parameter)); | |
833 | } | |
834 | } | |
835 | ttcn3_debugger.print(")\n"); | |
836 | } | |
837 | ||
838 | void TTCN3_Debug_Function::list_variables(const char* p_scope, const char* p_filter) const | |
839 | { | |
840 | bool first = true; | |
841 | bool list_local = false; | |
842 | bool list_global = false; | |
843 | bool list_comp = false; | |
844 | if (!strcmp(p_scope, "local")) { | |
845 | list_local = true; | |
846 | } | |
847 | else if (!strcmp(p_scope, "global")) { | |
848 | list_global = true; | |
849 | } | |
850 | else if (!strcmp(p_scope, "comp") || !strcmp(p_scope, "component")) { | |
851 | list_comp = true; | |
852 | } | |
853 | else { | |
854 | if (strcmp(p_scope, "all")) { | |
855 | ttcn3_debugger.print("Invalid scope. Listing variables in all scopes.\n"); | |
856 | } | |
857 | list_local = true; | |
858 | list_global = true; | |
859 | list_comp = true; | |
860 | } | |
861 | if (list_local) { | |
862 | for (size_t i = 0; i < variables.size(); ++i) { | |
863 | ttcn3_debugger.print("%s%s", first ? "" : " ", variables[i]->name); | |
864 | first = false; | |
865 | } | |
866 | } | |
867 | if (list_global && global_scope != NULL && global_scope->has_variables()) { | |
868 | global_scope->list_variables(p_filter, first); | |
869 | } | |
870 | if (list_comp && component_scope != NULL && component_scope->has_variables()) { | |
871 | component_scope->list_variables(p_filter, first); | |
872 | } | |
873 | if (first) { | |
874 | ttcn3_debugger.print("No variables found."); | |
875 | } | |
876 | ttcn3_debugger.print("\n"); | |
877 | } | |
878 |