debugger: added function breakpoints and added a setting for handling function call...
[deliverable/titan.core.git] / core / Debugger.cc
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 #include "DebugCommands.hh"
16 #include "Communication.hh"
17 #include "../common/pattern.hh"
18 #include "Param_Types.hh"
19 #include "DebuggerUI.hh"
20 #include <unistd.h>
21 #include <pwd.h>
22
23 //////////////////////////////////////////////////////
24 ////////////////// TTCN3_Debugger ////////////////////
25 //////////////////////////////////////////////////////
26
27 #define BUFFER_INCREASE 100
28
29 TTCN3_Debugger ttcn3_debugger;
30
31 void TTCN3_Debugger::switch_state(const char* p_state_str)
32 {
33 if (!strcmp(p_state_str, "on")) {
34 if (active) {
35 print(DRET_NOTIFICATION, "The debugger is already switched on.");
36 }
37 else {
38 active = true;
39 print(DRET_SETTING_CHANGE, "Debugger switched on.");
40 }
41 }
42 else if(!strcmp(p_state_str, "off")) {
43 if (!active) {
44 print(DRET_NOTIFICATION, "The debugger is already switched off.");
45 }
46 else {
47 active = false;
48 print(DRET_SETTING_CHANGE, "Debugger switched off.");
49 }
50 }
51 else {
52 print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'on' or 'off'.");
53 }
54 }
55
56 static bool is_numeric(const char* p_str)
57 {
58 size_t len = strlen(p_str);
59 for (size_t i = 0; i < len; ++i) {
60 if (p_str[i] < '0' || p_str[i] > '9') {
61 return false;
62 }
63 }
64 return true;
65 }
66
67 void TTCN3_Debugger::set_breakpoint(const char* p_module, const char* p_location,
68 const char* batch_file)
69 {
70 int line = 0;
71 char* function = NULL;
72 if (is_numeric(p_location)) {
73 // it's a line breakpoint
74 line = strtol(p_location, NULL, 10);
75 }
76 else {
77 // it's a function breakpoint
78 function = mcopystr(p_location);
79 }
80 char* loc_str = function != NULL ? mprintf("function '%s'", function) :
81 mprintf("line %d", line);
82 size_t pos = find_breakpoint(p_module, line, function);
83 if (pos == breakpoints.size()) {
84 breakpoint_t bp;
85 bp.module = mcopystr(p_module);
86 bp.line = line;
87 bp.function = function;
88 bp.batch_file = batch_file != NULL ? mcopystr(batch_file) : NULL;
89 breakpoints.push_back(bp);
90 print(DRET_SETTING_CHANGE, "Breakpoint added in module '%s' at %s %s%s%s.",
91 p_module, loc_str,
92 batch_file != NULL ? "with batch file '" : "with no batch file",
93 batch_file != NULL ? batch_file : "", batch_file != NULL ? "'" : "");
94 }
95 else {
96 if (breakpoints[pos].batch_file != NULL) {
97 if (batch_file != NULL) {
98 if (!strcmp(batch_file, breakpoints[pos].batch_file)) {
99 print(DRET_NOTIFICATION, "Breakpoint already set in module '%s' at "
100 "%s with batch file '%s'.",
101 p_module, loc_str, batch_file);
102 }
103 else {
104 print(DRET_SETTING_CHANGE, "Batch file was changed from '%s' to '%s' for "
105 "breakpoint in module '%s' at %s.", breakpoints[pos].batch_file,
106 batch_file, p_module, loc_str);
107 }
108 }
109 else {
110 print(DRET_SETTING_CHANGE, "Batch file '%s' removed from breakpoint in "
111 "module '%s' at %s.", breakpoints[pos].batch_file, p_module, loc_str);
112 }
113 Free(breakpoints[pos].batch_file);
114 }
115 else {
116 if (batch_file != NULL) {
117 print(DRET_SETTING_CHANGE, "Batch file '%s' added to breakpoint in module "
118 "'%s' at %s.", batch_file, p_module, loc_str);
119 }
120 else {
121 print(DRET_NOTIFICATION, "Breakpoint already set in module '%s' at %s "
122 "with no batch file.", p_module, loc_str);
123 }
124 }
125 breakpoints[pos].batch_file = batch_file != NULL ? mcopystr(batch_file) : NULL;
126 }
127 Free(loc_str);
128 }
129
130 void TTCN3_Debugger::remove_breakpoint(const char* p_module, const char* p_location)
131 {
132 bool all_breakpoints = !strcmp(p_module, "all");
133 if (p_location != NULL) {
134 if (!strcmp(p_location, "all")) {
135 bool found = false;
136 for (size_t i = breakpoints.size(); i > 0; --i) {
137 if (!strcmp(breakpoints[i - 1].module, p_module)) {
138 found = true;
139 Free(breakpoints[i - 1].module);
140 Free(breakpoints[i - 1].batch_file);
141 breakpoints.erase_at(i - 1);
142 }
143 }
144 if (found) {
145 print(DRET_SETTING_CHANGE, "Removed all breakpoints in module '%s'.", p_module);
146 }
147 else {
148 print(DRET_NOTIFICATION, "No breakpoints found in module '%s'.", p_module);
149 }
150 return;
151 }
152 else {
153 if (all_breakpoints) {
154 print(DRET_NOTIFICATION, "Unexpected 2nd argument, when the first "
155 "argument is 'all'.");
156 return;
157 }
158 int line = 0;
159 char* function = NULL;
160 if (is_numeric(p_location)) {
161 // it's a line breakpoint
162 line = strtol(p_location, NULL, 10);
163 }
164 else {
165 // it's a function breakpoint
166 function = mcopystr(p_location);
167 }
168 char* loc_str = function != NULL ? mprintf("function '%s'", function) :
169 mprintf("line %d", line);
170 size_t pos = find_breakpoint(p_module, line, function);
171 if (pos != breakpoints.size()) {
172 Free(breakpoints[pos].module);
173 Free(breakpoints[pos].function);
174 Free(breakpoints[pos].batch_file);
175 breakpoints.erase_at(pos);
176 print(DRET_SETTING_CHANGE, "Breakpoint removed in module '%s' from %s.",
177 p_module, loc_str);
178 }
179 else {
180 print(DRET_NOTIFICATION, "No breakpoint found in module '%s' at %s.",
181 p_module, loc_str);
182 }
183 Free(loc_str);
184 return;
185 }
186 }
187 else if (!all_breakpoints) {
188 print(DRET_NOTIFICATION, "2 arguments expected, when the first argument is "
189 "not 'all'.");
190 return;
191 }
192 // delete all breakpoints if we got this far
193 if (breakpoints.empty()) {
194 print(DRET_NOTIFICATION, "No breakpoints found.");
195 }
196 else {
197 for (size_t i = 0; i < breakpoints.size(); ++i) {
198 Free(breakpoints[i].module);
199 Free(breakpoints[i].function);
200 Free(breakpoints[i].batch_file);
201 }
202 breakpoints.clear();
203 print(DRET_SETTING_CHANGE, "Removed all breakpoints.");
204 }
205 }
206
207 void TTCN3_Debugger::set_automatic_breakpoint(const char* p_event_str,
208 const char* p_state_str,
209 const char* p_batch_file)
210 {
211 bool new_state;
212 if (!strcmp(p_state_str, "on")) {
213 new_state = true;
214 }
215 else if(!strcmp(p_state_str, "off")) {
216 new_state = false;
217 }
218 else {
219 print(DRET_NOTIFICATION, "Argument 2 is invalid. Expected 'on' or 'off'.");
220 return;
221 }
222 const char* sbp_type_str;
223 bool state_changed;
224 char** old_batch_file_ptr;
225 if (!strcmp(p_event_str, "fail")) {
226 state_changed = (fail_behavior.trigger != new_state);
227 fail_behavior.trigger = new_state;
228 old_batch_file_ptr = &fail_behavior.batch_file;
229 sbp_type_str = "fail verdict";
230 }
231 else if (!strcmp(p_event_str, "error")) {
232 state_changed = (error_behavior.trigger != new_state);
233 error_behavior.trigger = new_state;
234 old_batch_file_ptr = &error_behavior.batch_file;
235 sbp_type_str = "error verdict";
236 }
237 else {
238 print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'error' or 'fail'.");
239 return;
240 }
241 if (state_changed) {
242 print(DRET_SETTING_CHANGE, "Automatic breakpoint at %s switched %s%s%s%s.",
243 sbp_type_str, new_state ? "on" : "off",
244 new_state ? (p_batch_file != NULL ? " with batch file '" : " with no batch file") : "",
245 (p_batch_file != NULL && new_state) ? p_batch_file : "",
246 (p_batch_file != NULL && new_state) ? "'" : "");
247 }
248 else {
249 if (new_state) {
250 if (*old_batch_file_ptr != NULL) {
251 if (p_batch_file != NULL) {
252 if (!strcmp(p_batch_file, *old_batch_file_ptr)) {
253 print(DRET_NOTIFICATION, "Automatic breakpoint at %s was already "
254 "switched on with batch file '%s'.", sbp_type_str, p_batch_file);
255 }
256 else {
257 print(DRET_SETTING_CHANGE, "Batch file was changed from '%s' to '%s' "
258 "for automatic breakpoint at %s.", *old_batch_file_ptr, p_batch_file,
259 sbp_type_str);
260 }
261 }
262 else {
263 print(DRET_SETTING_CHANGE, "Batch file '%s' removed from automatic "
264 "breakpoint at %s.", *old_batch_file_ptr, sbp_type_str);
265 }
266 }
267 else {
268 if (p_batch_file != NULL) {
269 print(DRET_SETTING_CHANGE, "Batch file '%s' added to automatic breakpoint "
270 "at %s.", p_batch_file, sbp_type_str);
271 }
272 else {
273 print(DRET_NOTIFICATION, "Automatic breakpoint at %s was already "
274 "switched on with no batch file.", sbp_type_str);
275 }
276 }
277 }
278 else {
279 print(DRET_NOTIFICATION, "Automatic breakpoint at %s was already switched off.");
280 }
281 }
282 Free(*old_batch_file_ptr);
283 *old_batch_file_ptr = p_batch_file != NULL ? mcopystr(p_batch_file) : NULL;
284 }
285
286 void TTCN3_Debugger::print_settings()
287 {
288 // on/off switch
289 add_to_result("Debugger is switched %s.\n", active ? "on" : "off");
290 // output
291 char* final_file_name = finalize_file_name(output_file_name);
292 char* file_str = output_file != NULL ? mprintf("file '%s'", final_file_name) : NULL;
293 Free(final_file_name);
294 add_to_result("Output is printed to %s%s%s.\n",
295 send_to_console ? "the console" : "",
296 (send_to_console && output_file != NULL) ? " and to " : "",
297 output_file != NULL ? file_str : "");
298 Free(file_str);
299 // global batch file
300 add_to_result("Global batch file%s%s.\n", global_batch_file != NULL ? ": " : "",
301 global_batch_file != NULL ? global_batch_file : " not set");
302 // function call data configuration
303 add_to_result("Function call data ");
304 if (function_calls.cfg == CALLS_TO_FILE) {
305 char* final_file_name2 = finalize_file_name(function_calls.file.name);
306 add_to_result("sent to file '%s'.\n", final_file_name2);
307 Free(final_file_name2);
308 }
309 else {
310 add_to_result("buffer size: ");
311 if (function_calls.cfg == CALLS_STORE_ALL) {
312 add_to_result("infinite.\n");
313 }
314 else {
315 add_to_result("%d.\n", function_calls.buffer.size);
316 }
317 }
318 // user breakpoints
319 if (breakpoints.empty()) {
320 add_to_result("No user breakpoints.\n");
321 }
322 else {
323 add_to_result("User breakpoints:\n");
324 for (size_t i = 0; i < breakpoints.size(); ++i) {
325 const breakpoint_t& bp = breakpoints[i];
326 add_to_result("%s ", bp.module);
327 if (bp.function == NULL) {
328 add_to_result("%d", bp.line);
329 }
330 else {
331 add_to_result("%s", bp.function);
332 }
333 if (bp.batch_file != NULL) {
334 add_to_result(" %s", bp.batch_file);
335 }
336 add_to_result("\n");
337 }
338 }
339 // automatic breakpoints
340 add_to_result("Automatic breakpoints:\nerror %s %s\nfail %s %s",
341 error_behavior.trigger ? "on" : "off",
342 error_behavior.batch_file != NULL ? error_behavior.batch_file : "",
343 fail_behavior.trigger ? "on" : "off",
344 fail_behavior.batch_file != NULL ? fail_behavior.batch_file : "");
345 }
346
347 #define STACK_LEVEL (stack_level >= 0) ? (size_t)stack_level : (call_stack.size() - 1)
348
349 void TTCN3_Debugger::print_call_stack()
350 {
351 for (size_t i = call_stack.size(); i != 0; --i) {
352 add_to_result("%d.\t", (int)call_stack.size() - (int)i + 1);
353 call_stack[i - 1].function->print_function();
354 if (i - 1 == (STACK_LEVEL)) {
355 // mark the active stack level with an asterisk
356 add_to_result("*");
357 }
358 if (i != 1) {
359 add_to_result("\n");
360 }
361 }
362 }
363
364 void TTCN3_Debugger::set_stack_level(int new_level)
365 {
366 if (!halted) {
367 print(DRET_NOTIFICATION, "Stack level can only be set if test execution is halted.");
368 }
369 else if (new_level <= 0 || (size_t)new_level > call_stack.size()) {
370 print(DRET_NOTIFICATION, "Invalid new stack level. Expected 1 - %d.",
371 (int)call_stack.size());
372 }
373 else {
374 stack_level = (int)call_stack.size() - new_level;
375 call_stack[stack_level].function->print_function();
376 print(DRET_NOTIFICATION, "Stack level set to:\n%d.\t%s", new_level, command_result);
377 Free(command_result);
378 command_result = NULL;
379 }
380 }
381
382 void TTCN3_Debugger::print_variable(const char* p_var_name)
383 {
384 const variable_t* var = call_stack[STACK_LEVEL].function->find_variable(p_var_name);
385 if (var != NULL) {
386 add_to_result("[%s] %s%s%s := %s", var->type_name,
387 var->module != NULL ? var->module : "", var->module != NULL ? "." : "",
388 var->name, (const char*)var->print_function(*var));
389 }
390 else {
391 add_to_result("Variable '%s' not found.", p_var_name);
392 }
393 }
394
395 void TTCN3_Debugger::overwrite_variable(const char* p_var_name,
396 int p_value_element_count,
397 char** p_value_elements)
398 {
399 variable_t* var = call_stack[STACK_LEVEL].function->find_variable(p_var_name);
400 if (var != NULL) {
401 if (var->set_function == NULL) {
402 print(DRET_NOTIFICATION, "Constant variables cannot be overwritten.");
403 }
404 else {
405 char* new_value_str = NULL;
406 for (int i = 0; i < p_value_element_count; ++i) {
407 if (i != 0) {
408 new_value_str = mputc(new_value_str, ' ');
409 }
410 new_value_str = mputstr(new_value_str, p_value_elements[i]);
411 }
412 Module_Param* parsed_mp = process_config_debugger_value(new_value_str);
413 // an error message has already been displayed if parsed_mp is NULL
414 if (parsed_mp != NULL) {
415 try {
416 Debugger_Value_Parsing debug_value_parsing;
417 boolean handled = var->set_function(*var, *parsed_mp);
418 if (!handled) {
419 print(DRET_NOTIFICATION, "Variables of type '%s' cannot be overwritten.",
420 var->type_name);
421 }
422 else {
423 add_to_result("[%s] %s := %s", var->type_name, var->name,
424 (const char*)var->print_function(*var));
425 }
426 }
427 catch (const TC_Error&) {
428 // do nothing, an error message has already been displayed in this case
429 }
430 delete parsed_mp;
431 }
432 }
433 }
434 else {
435 print(DRET_NOTIFICATION, "Variable '%s' not found.", p_var_name);
436 }
437 }
438
439 void TTCN3_Debugger::clean_up_function_calls()
440 {
441 if (function_calls.cfg == CALLS_TO_FILE) {
442 if (!TTCN_Runtime::is_hc()) {
443 fclose(function_calls.file.ptr);
444 }
445 Free(function_calls.file.name);
446 }
447 else if (!TTCN_Runtime::is_hc()) {
448 if (function_calls.buffer.size != 0) {
449 if (function_calls.buffer.end != -1) {
450 for (int i = function_calls.buffer.start;
451 i != function_calls.buffer.end;
452 i = (i + 1) % function_calls.buffer.size) {
453 Free(function_calls.buffer.ptr[i]);
454 }
455 Free(function_calls.buffer.ptr[function_calls.buffer.end]);
456 }
457 Free(function_calls.buffer.ptr);
458 }
459 }
460 }
461
462 void TTCN3_Debugger::configure_function_calls(const char* p_config,
463 const char* p_file_name)
464 {
465 function_call_data_config_t cfg;
466 // check the command's parameters before actually changing anything
467 if (strcmp(p_config, "file") == 0) {
468 cfg = CALLS_TO_FILE;
469 }
470 else if (strcmp(p_config, "all") == 0) {
471 cfg = CALLS_STORE_ALL;
472 }
473 else if (is_numeric(p_config)) {
474 cfg = CALLS_RING_BUFFER;
475 }
476 else {
477 print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'file', 'all' or "
478 "ring buffer size.");
479 return;
480 }
481 FILE* new_fp = NULL;
482 char* final_file_name = NULL;
483 bool same_setting = false;
484 int new_size = 0;
485 switch (cfg) {
486 case CALLS_TO_FILE:
487 if (p_file_name != NULL) {
488 if (function_calls.cfg == CALLS_TO_FILE &&
489 strcmp(p_file_name, function_calls.file.name) == 0) {
490 // don't reopen it if it's the same file as before
491 same_setting = true;
492 }
493 else if (!TTCN_Runtime::is_hc()) {
494 // don't open any files on HCs, just store settings for future PTCs
495 final_file_name = finalize_file_name(p_file_name);
496 new_fp = fopen(final_file_name, TTCN_Runtime::is_mtc() ? "w" : "a");
497 if (new_fp == NULL) {
498 print(DRET_NOTIFICATION, "Failed to open file '%s' for writing.", final_file_name);
499 Free(final_file_name);
500 return;
501 }
502 }
503 }
504 else {
505 print(DRET_NOTIFICATION, "Argument 2 (file name) is missing.");
506 return;
507 }
508 break;
509 case CALLS_RING_BUFFER:
510 new_size = strtol(p_config, NULL, 10);
511 same_setting = function_calls.cfg == CALLS_RING_BUFFER &&
512 function_calls.buffer.size == new_size;
513 break;
514 case CALLS_STORE_ALL:
515 same_setting = function_calls.cfg == CALLS_STORE_ALL;
516 break;
517 }
518 if (!same_setting) {
519 clean_up_function_calls();
520 function_calls.cfg = cfg;
521 switch (cfg) {
522 case CALLS_TO_FILE:
523 function_calls.file.name = mcopystr(p_file_name);
524 if (!TTCN_Runtime::is_hc()) {
525 function_calls.file.ptr = new_fp;
526 }
527 break;
528 case CALLS_RING_BUFFER:
529 function_calls.buffer.size = new_size;
530 // no break
531 case CALLS_STORE_ALL:
532 function_calls.buffer.start = 0;
533 function_calls.buffer.end = -1;
534 if (new_size != 0 && !TTCN_Runtime::is_hc()) {
535 // don't allocate buffers on HCs, just store settings for future PTCs
536 function_calls.buffer.ptr = (char**)Malloc(new_size * sizeof(char*));
537 }
538 else {
539 function_calls.buffer.ptr = NULL;
540 }
541 break;
542 }
543 }
544 switch (cfg) {
545 case CALLS_TO_FILE:
546 print(DRET_SETTING_CHANGE, "Debugger %sset to not store function call data, but to "
547 "send them to file '%s'.", same_setting ? "was already " : "", final_file_name);
548 Free(final_file_name);
549 break;
550 case CALLS_RING_BUFFER:
551 if (new_size == 0) {
552 print(DRET_SETTING_CHANGE, "Debugger %sset to not store function call data.",
553 same_setting ? "was already " : "");
554 }
555 else {
556 print(DRET_SETTING_CHANGE, "Debugger %sset to store only the last %d function calls.",
557 same_setting ? "was already " : "", new_size);
558 }
559 break;
560 case CALLS_STORE_ALL:
561 print(DRET_SETTING_CHANGE, "Debugger %sset to store all function call data.",
562 same_setting ? "was already " : "");
563 break;
564 }
565 }
566
567 void TTCN3_Debugger::print_function_calls(const char* p_amount)
568 {
569 if (function_calls.cfg == CALLS_TO_FILE || function_calls.buffer.size == 0 ||
570 function_calls.buffer.end == -1) {
571 print(DRET_NOTIFICATION, "No function calls are stored.");
572 return;
573 }
574 int amount;
575 int limit = (function_calls.cfg == CALLS_RING_BUFFER &&
576 function_calls.buffer.start == (function_calls.buffer.end + 1) %
577 function_calls.buffer.size) ?
578 function_calls.buffer.size : function_calls.buffer.end + 1;
579 if (p_amount == NULL || strcmp(p_amount, "all") == 0) {
580 amount = limit;
581 }
582 else if (is_numeric(p_amount)) {
583 amount = strtol(p_amount, NULL, 10);
584 if (amount == 0 || amount > limit) {
585 print(DRET_NOTIFICATION, "Invalid number of function calls. Expected 1 - %d.",
586 limit);
587 return;
588 }
589 }
590 else {
591 print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'all' or integer "
592 "value (number of calls).");
593 return;
594 }
595 for (int i = (function_calls.buffer.end - amount + function_calls.buffer.size + 1) %
596 function_calls.buffer.size; amount > 0;
597 i = (i + 1) % function_calls.buffer.size, --amount) {
598 add_to_result(function_calls.buffer.ptr[i]);
599 if (amount > 1) {
600 add_to_result("\n");
601 }
602 }
603 }
604
605 void TTCN3_Debugger::set_output(const char* p_output_type, const char* p_file_name)
606 {
607 FILE* new_fp = NULL;
608 bool file, console;
609 bool same_file = false;
610 char* final_file_name = NULL;
611 // check the command's parameters before actually changing anything
612 if (!strcmp(p_output_type, "console")) {
613 file = false;
614 console = true;
615 }
616 else if (!strcmp(p_output_type, "file")) {
617 file = true;
618 console = false;
619 }
620 else if (!strcmp(p_output_type, "both")) {
621 file = true;
622 console = true;
623 }
624 else {
625 print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'console', 'file' or 'both'.");
626 return;
627 }
628 if (file) {
629 if (p_file_name == NULL) {
630 print(DRET_NOTIFICATION, "Argument 2 (output file name) is missing.");
631 return;
632 }
633 if (output_file_name != NULL && !strcmp(p_file_name, output_file_name)) {
634 // don't reopen it if it's the same file as before
635 same_file = true;
636 }
637 else if (!TTCN_Runtime::is_hc()) {
638 // don't open any files on HCs, just store settings for future PTCs
639 final_file_name = finalize_file_name(p_file_name);
640 new_fp = fopen(final_file_name, TTCN_Runtime::is_mtc() ? "w" : "a");
641 if (new_fp == NULL) {
642 print(DRET_NOTIFICATION, "Failed to open file '%s' for writing.", final_file_name);
643 Free(final_file_name);
644 return;
645 }
646 }
647 }
648 // print the change notification to the old output
649 char* file_str = file ? mprintf("file '%s'", final_file_name) : NULL;
650 Free(final_file_name);
651 print(DRET_SETTING_CHANGE, "Debugger set to print its output to %s%s%s.",
652 console ? "the console" : "", (console && file) ? " and to " : "",
653 file ? file_str : "");
654 if (file) {
655 Free(file_str);
656 }
657 if (!same_file && !TTCN_Runtime::is_hc()) {
658 if (output_file != NULL) {
659 fclose(output_file);
660 }
661 output_file = new_fp;
662 }
663 send_to_console = console;
664 Free(output_file_name);
665 if (file) {
666 output_file_name = mcopystr(p_file_name);
667 }
668 }
669
670 void TTCN3_Debugger::set_global_batch_file(const char* p_state_str,
671 const char* p_file_name)
672 {
673 bool delete_old = false;
674 bool copy_new = false;
675 if (!strcmp(p_state_str, "on")) {
676 if (p_file_name != NULL) {
677 if (global_batch_file != NULL) {
678 if (!strcmp(p_file_name, global_batch_file)) {
679 print(DRET_NOTIFICATION, "Global batch file was already switched on "
680 "and set to '%s'.", p_file_name);
681 }
682 else {
683 print(DRET_SETTING_CHANGE, "Global batch file changed from '%s' to '%s'.",
684 global_batch_file, p_file_name);
685 delete_old = true;
686 copy_new = true;
687 }
688 }
689 else {
690 print(DRET_SETTING_CHANGE, "Global batch file switched on and set to '%s'.",
691 p_file_name);
692 copy_new = true;
693 }
694 }
695 else {
696 print(DRET_NOTIFICATION, "Missing batch file name argument.");
697 }
698 }
699 else if (!strcmp(p_state_str, "off")) {
700 if (global_batch_file != NULL) {
701 print(DRET_SETTING_CHANGE, "Global batch file switched off.");
702 delete_old = true;
703 }
704 else {
705 print(DRET_NOTIFICATION, "Global batch file was already switched off.");
706 }
707 }
708 else {
709 print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'on' or 'off'.");
710 }
711 if (delete_old) {
712 Free(global_batch_file);
713 global_batch_file = NULL;
714 }
715 if (copy_new) {
716 global_batch_file = mcopystr(p_file_name);
717 }
718 }
719
720 void TTCN3_Debugger::step(stepping_t p_stepping_type)
721 {
722 if (!halted) {
723 print(DRET_NOTIFICATION, "Stepping commands can only be used when test "
724 "execution is halted.");
725 return;
726 }
727 stepping_type = p_stepping_type;
728 stepping_stack_size = call_stack.size();
729 if (!TTCN_Runtime::is_single()) {
730 TTCN_Communication::send_debug_continue_req();
731 }
732 resume();
733 }
734
735 void TTCN3_Debugger::run_to_cursor(const char* p_module, const char* p_location)
736 {
737 // all processes receive this command, since the specified location might be
738 // reached in any process, even a PTC that hasn't been created yet
739 if (!halted) {
740 print(DRET_NOTIFICATION, "The 'run to' command can only be used when test "
741 "execution is halted.");
742 return;
743 }
744 temporary_breakpoint.module = mcopystr(p_module);
745 if (is_numeric(p_location)) {
746 temporary_breakpoint.line = strtol(p_location, NULL, 10);
747 temporary_breakpoint.function = NULL;
748 }
749 else {
750 temporary_breakpoint.line = 0;
751 temporary_breakpoint.function = mcopystr(p_location);
752 }
753 resume();
754 }
755
756 void TTCN3_Debugger::halt(const char* p_batch_file, bool p_run_global_batch)
757 {
758 if (!halted) {
759 halted = true;
760 Free(temporary_breakpoint.module);
761 temporary_breakpoint.module = NULL;
762 temporary_breakpoint.line = 0;
763 Free(temporary_breakpoint.function);
764 temporary_breakpoint.function = NULL;
765 if (!TTCN_Runtime::is_hc()) {
766 stepping_type = NOT_STEPPING;
767 stack_level = call_stack.size() - 1;
768 print(DRET_NOTIFICATION, "Test execution halted.");
769 if (p_batch_file != NULL) {
770 if (TTCN_Runtime::is_single()) {
771 TTCN_Debugger_UI::execute_batch_file(p_batch_file);
772 }
773 else {
774 TTCN_Communication::send_debug_batch(p_batch_file);
775 }
776 }
777 else if (p_run_global_batch && global_batch_file != NULL) {
778 if (TTCN_Runtime::is_single()) {
779 TTCN_Debugger_UI::execute_batch_file(global_batch_file);
780 }
781 else {
782 TTCN_Communication::send_debug_batch(global_batch_file);
783 }
784 }
785 if (TTCN_Runtime::is_single()) {
786 if (halted && !halt_at_start) {
787 resume();
788 }
789 else {
790 TTCN_Debugger_UI::read_loop();
791 }
792 }
793 else {
794 TTCN_Communication::process_debug_messages();
795 }
796 }
797 }
798 else {
799 print(DRET_NOTIFICATION, "Test execution is already halted.");
800 }
801 }
802
803 void TTCN3_Debugger::resume()
804 {
805 if (halted) {
806 halted = false;
807 stack_level = -1;
808 print(DRET_NOTIFICATION, "Test execution resumed.");
809 }
810 else {
811 print(DRET_NOTIFICATION, "Test execution is not halted.");
812 }
813 }
814
815 void TTCN3_Debugger::exit_(const char* p_what)
816 {
817 if (!strcmp(p_what, "test")) {
818 exiting = false;
819 }
820 else if (!strcmp(p_what, "all")) {
821 exiting = true;
822 }
823 else {
824 print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'test' or 'all'.");
825 return;
826 }
827 halted = false;
828 if (!TTCN_Runtime::is_hc()) {
829 print((exiting && TTCN_Runtime::is_mtc()) ? DRET_EXIT_ALL : DRET_NOTIFICATION,
830 "Exiting %s.", exiting ? "test execution" : "current test");
831 TTCN_Runtime::stop_execution();
832 }
833 }
834
835 size_t TTCN3_Debugger::find_breakpoint(const char* p_module, int p_line,
836 const char* p_function) const
837 {
838 for (size_t i = 0; i < breakpoints.size(); ++i) {
839 if (!strcmp(breakpoints[i].module, p_module) &&
840 ((p_line != 0 && breakpoints[i].line == p_line) ||
841 (p_function != NULL && breakpoints[i].function != NULL &&
842 strcmp(breakpoints[i].function, p_function) == 0))) {
843 return i;
844 }
845 }
846 return breakpoints.size();
847 }
848
849 TTCN3_Debugger::variable_t* TTCN3_Debugger::find_variable(const void* p_value) const
850 {
851 for (size_t i = 0; i < variables.size(); ++i) {
852 if (variables[i]->value == p_value) {
853 return variables[i];
854 }
855 }
856 return NULL;
857 }
858
859 char* TTCN3_Debugger::finalize_file_name(const char* p_file_name_skeleton)
860 {
861 if (p_file_name_skeleton == NULL) {
862 return NULL;
863 }
864 size_t len = strlen(p_file_name_skeleton);
865 size_t next_idx = 0;
866 char* ret_val = NULL;
867 for (size_t i = 0; i < len - 1; ++i) {
868 if (p_file_name_skeleton[i] == '%') {
869 ret_val = mputstrn(ret_val, p_file_name_skeleton + next_idx, i - next_idx);
870 switch (p_file_name_skeleton[i + 1]) {
871 case 'e': // %e -> executable name
872 ret_val = mputstr(ret_val, TTCN_Logger::get_executable_name());
873 break;
874 case 'h': // %h -> host name
875 ret_val = mputstr(ret_val, TTCN_Runtime::get_host_name());
876 break;
877 case 'p': // %p -> process ID
878 ret_val = mputprintf(ret_val, "%ld", (long)getpid());
879 break;
880 case 'l': { // %l -> login name
881 setpwent();
882 struct passwd *p = getpwuid(getuid());
883 if (NULL != p) {
884 ret_val = mputstr(ret_val, p->pw_name);
885 }
886 endpwent();
887 break; }
888 case 'r': // %r -> component reference
889 if (TTCN_Runtime::is_single()) {
890 ret_val = mputstr(ret_val, "single");
891 }
892 else if (TTCN_Runtime::is_mtc()) {
893 ret_val = mputstr(ret_val, "mtc");
894 }
895 else if (TTCN_Runtime::is_ptc()) {
896 ret_val = mputprintf(ret_val, "%d", (component)self);
897 }
898 break;
899 case 'n': // %n -> component name
900 if (TTCN_Runtime::is_mtc()) {
901 ret_val = mputstr(ret_val, "MTC");
902 }
903 else if (TTCN_Runtime::is_ptc()) {
904 ret_val = mputstr(ret_val, TTCN_Runtime::get_component_name());
905 }
906 break;
907 case '%': // %% -> single %
908 ret_val = mputc(ret_val, '%');
909 break;
910 default: // unknown sequence -> leave it as it is
911 ret_val = mputstrn(ret_val, p_file_name_skeleton + i, 2);
912 break;
913 }
914 next_idx = i + 2;
915 ++i;
916 }
917 }
918 if (next_idx < len) {
919 ret_val = mputstr(ret_val, p_file_name_skeleton + next_idx);
920 }
921 return ret_val;
922 }
923
924 void TTCN3_Debugger::test_execution_started()
925 {
926 if (function_calls.cfg != CALLS_TO_FILE) {
927 if (function_calls.buffer.size != 0 && function_calls.buffer.end != -1) {
928 for (int i = function_calls.buffer.start;
929 i != function_calls.buffer.end;
930 i = (i + 1) % function_calls.buffer.size) {
931 Free(function_calls.buffer.ptr[i]);
932 }
933 Free(function_calls.buffer.ptr[function_calls.buffer.end]);
934 }
935 if (function_calls.cfg == CALLS_STORE_ALL) {
936 Free(function_calls.buffer.ptr);
937 function_calls.buffer.ptr = NULL;
938 }
939 function_calls.buffer.start = 0;
940 function_calls.buffer.end = -1;
941 }
942 exiting = false;
943 if (TTCN_Runtime::is_single()) {
944 TTCN_Debugger_UI::init();
945 if (initial_batch_file) {
946 halt(initial_batch_file, false);
947 }
948 else if (halt_at_start) {
949 halt(NULL, false);
950 }
951 }
952 halt_at_start = true;
953 }
954
955 void TTCN3_Debugger::test_execution_finished()
956 {
957 stepping_type = NOT_STEPPING;
958 Free(temporary_breakpoint.module);
959 temporary_breakpoint.module = NULL;
960 temporary_breakpoint.line = 0;
961 Free(temporary_breakpoint.function);
962 temporary_breakpoint.function = NULL;
963 last_breakpoint_entry.module = NULL;
964 last_breakpoint_entry.line = 0;
965 last_breakpoint_entry.stack_size = 0;
966 if (TTCN_Runtime::is_single()) {
967 TTCN_Debugger_UI::clean_up();
968 }
969 }
970
971 void TTCN3_Debugger::print(int return_type, const char* fmt, ...) const
972 {
973 if (TTCN_Runtime::is_hc()) {
974 // don't display anything while on the HC process
975 return;
976 }
977 va_list parameters;
978 va_start(parameters, fmt);
979 char* str = mprintf_va_list(fmt, parameters);
980 va_end(parameters);
981 if (TTCN_Runtime::is_single()) {
982 if (send_to_console) {
983 TTCN_Debugger_UI::print(str);
984 }
985 }
986 else {
987 TTCN_Communication::send_debug_return_value(return_type, send_to_console ? str : NULL);
988 }
989 if (output_file != NULL) {
990 fseek(output_file, 0, SEEK_END); // in case multiple processes are writing the same file
991 fputs(str, output_file);
992 fputc('\n', output_file);
993 fflush(output_file);
994 }
995 Free(str);
996 }
997
998 TTCN3_Debugger::TTCN3_Debugger()
999 {
1000 enabled = false;
1001 active = false;
1002 halted = false;
1003 output_file = NULL;
1004 output_file_name = NULL;
1005 send_to_console = true;
1006 function_calls.cfg = CALLS_STORE_ALL;
1007 function_calls.buffer.size = 0;
1008 function_calls.buffer.start = 0;
1009 function_calls.buffer.end = -1;
1010 function_calls.buffer.ptr = NULL;
1011 last_breakpoint_entry.module = NULL;
1012 last_breakpoint_entry.line = 0;
1013 last_breakpoint_entry.stack_size = 0;
1014 stack_level = -1;
1015 fail_behavior.trigger = false;
1016 fail_behavior.batch_file = NULL;
1017 error_behavior.trigger = false;
1018 error_behavior.batch_file = NULL;
1019 global_batch_file = NULL;
1020 command_result = NULL;
1021 last_variable_list = NULL;
1022 stepping_type = NOT_STEPPING;
1023 stepping_stack_size = 0;
1024 temporary_breakpoint.module = NULL;
1025 temporary_breakpoint.line = 0;
1026 temporary_breakpoint.function = NULL;
1027 temporary_breakpoint.batch_file = NULL; // not used
1028 exiting = false;
1029 halt_at_start = false;
1030 initial_batch_file = NULL;
1031 }
1032
1033 TTCN3_Debugger::~TTCN3_Debugger()
1034 {
1035 if (output_file != NULL) {
1036 fclose(output_file);
1037 Free(output_file_name);
1038 }
1039 for (size_t i = 0; i < breakpoints.size(); ++i) {
1040 Free(breakpoints[i].module);
1041 Free(breakpoints[i].function);
1042 Free(breakpoints[i].batch_file);
1043 }
1044 for (size_t i = 0; i < global_scopes.size(); ++i) {
1045 delete global_scopes[i].scope;
1046 }
1047 for (size_t i = 0; i < component_scopes.size(); ++i) {
1048 delete component_scopes[i].scope;
1049 }
1050 for (size_t i = 0; i < variables.size(); ++i) {
1051 delete variables[i];
1052 }
1053 Free(fail_behavior.batch_file);
1054 Free(error_behavior.batch_file);
1055 Free(global_batch_file);
1056 clean_up_function_calls();
1057 Free(last_variable_list);
1058 }
1059
1060 TTCN3_Debug_Scope* TTCN3_Debugger::add_global_scope(const char* p_module)
1061 {
1062 named_scope_t global_scope;
1063 global_scope.name = p_module;
1064 global_scope.scope = new TTCN3_Debug_Scope();
1065 global_scopes.push_back(global_scope);
1066 return global_scope.scope;
1067 }
1068
1069 TTCN3_Debug_Scope* TTCN3_Debugger::add_component_scope(const char* p_component)
1070 {
1071 named_scope_t component_scope;
1072 component_scope.name = p_component;
1073 component_scope.scope = new TTCN3_Debug_Scope();
1074 component_scopes.push_back(component_scope);
1075 return component_scope.scope;
1076 }
1077
1078 void TTCN3_Debugger::set_return_value(const CHARSTRING& p_value)
1079 {
1080 if (active && !call_stack.empty()) {
1081 call_stack[call_stack.size() - 1].function->set_return_value(p_value);
1082 }
1083 }
1084
1085 void TTCN3_Debugger::breakpoint_entry(int p_line)
1086 {
1087 if (active && !call_stack.empty()) {
1088 const char* module_name = call_stack[call_stack.size() - 1].function->get_module_name();
1089 bool trigger = false;
1090 const char* trigger_type;
1091 int actual_line;
1092 const char* batch_file = NULL;
1093 switch (p_line) {
1094 case SBP_FAIL_VERDICT:
1095 trigger = fail_behavior.trigger;
1096 trigger_type = "Automatic breakpoint (fail verdict) reached at";
1097 actual_line = TTCN_Location::get_line_number();
1098 batch_file = fail_behavior.batch_file;
1099 break;
1100 case SBP_ERROR_VERDICT:
1101 trigger = error_behavior.trigger;
1102 trigger_type = "Automatic breakpoint (error verdict) reached at";
1103 actual_line = TTCN_Location::get_line_number();
1104 batch_file = error_behavior.batch_file;
1105 break;
1106 default: // code lines
1107 // first: make sure it's not the same breakpoint entry as last time
1108 if (p_line == last_breakpoint_entry.line &&
1109 module_name == last_breakpoint_entry.module) {
1110 break;
1111 }
1112 actual_line = p_line;
1113 // second: check if a stepping operation ends here
1114 if (stepping_type == STEP_INTO ||
1115 (stepping_type == STEP_OVER && call_stack.size() <= stepping_stack_size) ||
1116 (stepping_type == STEP_OUT && call_stack.size() < stepping_stack_size)) {
1117 trigger = true;
1118 trigger_type = "Stepped to";
1119 break;
1120 }
1121 const char* function_name =
1122 call_stack[call_stack.size() - 1].function->get_function_name();
1123 // third: check if this is the destination of a 'run to cursor' operation
1124 if (temporary_breakpoint.module != NULL &&
1125 strcmp(module_name, temporary_breakpoint.module) == 0 &&
1126 (p_line == temporary_breakpoint.line ||
1127 (temporary_breakpoint.function != NULL &&
1128 last_breakpoint_entry.stack_size == call_stack.size() - 1 &&
1129 strcmp(temporary_breakpoint.function, function_name) == 0))) {
1130 trigger = true;
1131 trigger_type = "Ran to";
1132 break;
1133 }
1134 // fourth: check if the location matches one of the breakpoints in the list
1135 size_t pos = find_breakpoint(module_name, p_line, NULL);
1136 if (pos == breakpoints.size() &&
1137 last_breakpoint_entry.stack_size == call_stack.size() - 1) {
1138 // this is the first breakpoint entry in the function,
1139 // check for a matching function breakpoint
1140 pos = find_breakpoint(module_name, 0, function_name);
1141 }
1142 if (pos != breakpoints.size()) {
1143 trigger = true;
1144 batch_file = breakpoints[pos].batch_file;
1145 }
1146 trigger_type = "User breakpoint reached at";
1147 break;
1148 }
1149 if (trigger) {
1150 print(DRET_NOTIFICATION, "%s line %d in module '%s'.",
1151 trigger_type, actual_line, module_name);
1152 if (!TTCN_Runtime::is_single()) {
1153 TTCN_Communication::send_debug_halt_req();
1154 }
1155 halt(batch_file, true);
1156 }
1157 last_breakpoint_entry.module = module_name;
1158 last_breakpoint_entry.line = p_line;
1159 last_breakpoint_entry.stack_size = call_stack.size();
1160 }
1161 }
1162
1163 CHARSTRING TTCN3_Debugger::print_base_var(const TTCN3_Debugger::variable_t& p_var)
1164 {
1165 const void* ptr = p_var.set_function != NULL ? p_var.value : p_var.cvalue;
1166 TTCN_Logger::begin_event_log2str();
1167 if (!strcmp(p_var.type_name, "bitstring")) {
1168 ((const BITSTRING*)ptr)->log();
1169 }
1170 else if (!strcmp(p_var.type_name, "bitstring template")) {
1171 ((const BITSTRING_template*)ptr)->log();
1172 }
1173 else if (!strcmp(p_var.type_name, "boolean")) {
1174 ((const BOOLEAN*)ptr)->log();
1175 }
1176 else if (!strcmp(p_var.type_name, "boolean template")) {
1177 ((const BOOLEAN_template*)ptr)->log();
1178 }
1179 else if (!strcmp(p_var.type_name, "charstring")) {
1180 ((const CHARSTRING*)ptr)->log();
1181 }
1182 else if (!strcmp(p_var.type_name, "charstring template")) {
1183 ((const CHARSTRING_template*)ptr)->log();
1184 }
1185 else if (!strcmp(p_var.type_name, "float")) {
1186 ((const FLOAT*)ptr)->log();
1187 }
1188 else if (!strcmp(p_var.type_name, "float template")) {
1189 ((const FLOAT_template*)ptr)->log();
1190 }
1191 else if (!strcmp(p_var.type_name, "hexstring")) {
1192 ((const HEXSTRING*)ptr)->log();
1193 }
1194 else if (!strcmp(p_var.type_name, "hexstring template")) {
1195 ((const HEXSTRING_template*)ptr)->log();
1196 }
1197 else if (!strcmp(p_var.type_name, "integer")) {
1198 ((const INTEGER*)ptr)->log();
1199 }
1200 else if (!strcmp(p_var.type_name, "integer template")) {
1201 ((const INTEGER_template*)ptr)->log();
1202 }
1203 else if (!strcmp(p_var.type_name, "objid")) {
1204 ((const OBJID*)ptr)->log();
1205 }
1206 else if (!strcmp(p_var.type_name, "objid template")) {
1207 ((const OBJID_template*)ptr)->log();
1208 }
1209 else if (!strcmp(p_var.type_name, "octetstring")) {
1210 ((const OCTETSTRING*)ptr)->log();
1211 }
1212 else if (!strcmp(p_var.type_name, "octetstring template")) {
1213 ((const OCTETSTRING_template*)ptr)->log();
1214 }
1215 else if (!strcmp(p_var.type_name, "universal charstring")) {
1216 ((const UNIVERSAL_CHARSTRING*)ptr)->log();
1217 }
1218 else if (!strcmp(p_var.type_name, "universal charstring template")) {
1219 ((const UNIVERSAL_CHARSTRING_template*)ptr)->log();
1220 }
1221 else if (!strcmp(p_var.type_name, "verdicttype")) {
1222 ((const VERDICTTYPE*)ptr)->log();
1223 }
1224 else if (!strcmp(p_var.type_name, "verdicttype template")) {
1225 ((const VERDICTTYPE_template*)ptr)->log();
1226 }
1227 else if (!strcmp(p_var.type_name, "component")) {
1228 ((const COMPONENT*)ptr)->log();
1229 }
1230 else if (!strcmp(p_var.type_name, "component template")) {
1231 ((const COMPONENT_template*)ptr)->log();
1232 }
1233 else if (!strcmp(p_var.type_name, "default")) {
1234 ((const DEFAULT*)ptr)->log();
1235 }
1236 else if (!strcmp(p_var.type_name, "default template")) {
1237 ((const DEFAULT_template*)ptr)->log();
1238 }
1239 else if (!strcmp(p_var.type_name, "timer")) {
1240 ((const TIMER*)ptr)->log();
1241 }
1242 else if (!strcmp(p_var.type_name, "NULL")) {
1243 ((const ASN_NULL*)ptr)->log();
1244 }
1245 else if (!strcmp(p_var.type_name, "NULL template")) {
1246 ((const ASN_NULL_template*)ptr)->log();
1247 }
1248 else if (!strcmp(p_var.type_name, "CHARACTER STRING")) {
1249 ((const CHARACTER_STRING*)ptr)->log();
1250 }
1251 else if (!strcmp(p_var.type_name, "CHARACTER STRING template")) {
1252 ((const CHARACTER_STRING_template*)ptr)->log();
1253 }
1254 else if (!strcmp(p_var.type_name, "EMBEDDED PDV")) {
1255 ((const EMBEDDED_PDV*)ptr)->log();
1256 }
1257 else if (!strcmp(p_var.type_name, "EMBEDDED PDV template")) {
1258 ((const EMBEDDED_PDV_template*)ptr)->log();
1259 }
1260 else if (!strcmp(p_var.type_name, "EXTERNAL")) {
1261 ((const EXTERNAL*)ptr)->log();
1262 }
1263 else if (!strcmp(p_var.type_name, "EXTERNAL template")) {
1264 ((const EXTERNAL_template*)ptr)->log();
1265 }
1266 else {
1267 TTCN_Logger::log_event_str("<unrecognized value or template>");
1268 }
1269 return TTCN_Logger::end_event_log2str();
1270 }
1271
1272 boolean TTCN3_Debugger::set_base_var(variable_t& p_var, Module_Param& p_new_value)
1273 {
1274 if (!strcmp(p_var.type_name, "bitstring")) {
1275 ((BITSTRING*)p_var.value)->set_param(p_new_value);
1276 }
1277 else if (!strcmp(p_var.type_name, "bitstring template")) {
1278 ((BITSTRING_template*)p_var.value)->set_param(p_new_value);
1279 }
1280 else if (!strcmp(p_var.type_name, "boolean")) {
1281 ((BOOLEAN*)p_var.value)->set_param(p_new_value);
1282 }
1283 else if (!strcmp(p_var.type_name, "boolean template")) {
1284 ((BOOLEAN_template*)p_var.value)->set_param(p_new_value);
1285 }
1286 else if (!strcmp(p_var.type_name, "charstring")) {
1287 ((CHARSTRING*)p_var.value)->set_param(p_new_value);
1288 }
1289 else if (!strcmp(p_var.type_name, "charstring template")) {
1290 ((CHARSTRING_template*)p_var.value)->set_param(p_new_value);
1291 }
1292 else if (!strcmp(p_var.type_name, "float")) {
1293 ((FLOAT*)p_var.value)->set_param(p_new_value);
1294 }
1295 else if (!strcmp(p_var.type_name, "float template")) {
1296 ((FLOAT_template*)p_var.value)->set_param(p_new_value);
1297 }
1298 else if (!strcmp(p_var.type_name, "hexstring")) {
1299 ((HEXSTRING*)p_var.value)->set_param(p_new_value);
1300 }
1301 else if (!strcmp(p_var.type_name, "hexstring template")) {
1302 ((HEXSTRING_template*)p_var.value)->set_param(p_new_value);
1303 }
1304 else if (!strcmp(p_var.type_name, "integer")) {
1305 ((INTEGER*)p_var.value)->set_param(p_new_value);
1306 }
1307 else if (!strcmp(p_var.type_name, "integer template")) {
1308 ((INTEGER_template*)p_var.value)->set_param(p_new_value);
1309 }
1310 else if (!strcmp(p_var.type_name, "objid")) {
1311 ((OBJID*)p_var.value)->set_param(p_new_value);
1312 }
1313 else if (!strcmp(p_var.type_name, "objid template")) {
1314 ((OBJID_template*)p_var.value)->set_param(p_new_value);
1315 }
1316 else if (!strcmp(p_var.type_name, "octetstring")) {
1317 ((OCTETSTRING*)p_var.value)->set_param(p_new_value);
1318 }
1319 else if (!strcmp(p_var.type_name, "octetstring template")) {
1320 ((OCTETSTRING_template*)p_var.value)->set_param(p_new_value);
1321 }
1322 else if (!strcmp(p_var.type_name, "universal charstring")) {
1323 ((UNIVERSAL_CHARSTRING*)p_var.value)->set_param(p_new_value);
1324 }
1325 else if (!strcmp(p_var.type_name, "universal charstring template")) {
1326 ((UNIVERSAL_CHARSTRING_template*)p_var.value)->set_param(p_new_value);
1327 }
1328 else if (!strcmp(p_var.type_name, "verdicttype")) {
1329 ((VERDICTTYPE*)p_var.value)->set_param(p_new_value);
1330 }
1331 else if (!strcmp(p_var.type_name, "verdicttype template")) {
1332 ((VERDICTTYPE_template*)p_var.value)->set_param(p_new_value);
1333 }
1334 else if (!strcmp(p_var.type_name, "component")) {
1335 ((COMPONENT*)p_var.value)->set_param(p_new_value);
1336 }
1337 else if (!strcmp(p_var.type_name, "component template")) {
1338 ((COMPONENT_template*)p_var.value)->set_param(p_new_value);
1339 }
1340 else if (!strcmp(p_var.type_name, "default")) {
1341 ((DEFAULT*)p_var.value)->set_param(p_new_value);
1342 }
1343 else if (!strcmp(p_var.type_name, "default template")) {
1344 ((DEFAULT_template*)p_var.value)->set_param(p_new_value);
1345 }
1346 else if (!strcmp(p_var.type_name, "NULL")) {
1347 ((ASN_NULL*)p_var.value)->set_param(p_new_value);
1348 }
1349 else if (!strcmp(p_var.type_name, "NULL template")) {
1350 ((ASN_NULL_template*)p_var.value)->set_param(p_new_value);
1351 }
1352 else if (!strcmp(p_var.type_name, "CHARACTER STRING")) {
1353 ((CHARACTER_STRING*)p_var.value)->set_param(p_new_value);
1354 }
1355 else if (!strcmp(p_var.type_name, "CHARACTER STRING template")) {
1356 ((CHARACTER_STRING_template*)p_var.value)->set_param(p_new_value);
1357 }
1358 else if (!strcmp(p_var.type_name, "EMBEDDED PDV")) {
1359 ((EMBEDDED_PDV*)p_var.value)->set_param(p_new_value);
1360 }
1361 else if (!strcmp(p_var.type_name, "EMBEDDED PDV template")) {
1362 ((EMBEDDED_PDV_template*)p_var.value)->set_param(p_new_value);
1363 }
1364 else if (!strcmp(p_var.type_name, "EXTERNAL")) {
1365 ((EXTERNAL*)p_var.value)->set_param(p_new_value);
1366 }
1367 else if (!strcmp(p_var.type_name, "EXTERNAL template")) {
1368 ((EXTERNAL_template*)p_var.value)->set_param(p_new_value);
1369 }
1370 else {
1371 return FALSE;
1372 }
1373 return TRUE;
1374 }
1375
1376 void TTCN3_Debugger::add_to_result(const char* fmt, ...)
1377 {
1378 va_list parameters;
1379 va_start(parameters, fmt);
1380 command_result = mputprintf_va_list(command_result, fmt, parameters);
1381 va_end(parameters);
1382 }
1383
1384 void TTCN3_Debugger::add_function(TTCN3_Debug_Function* p_function)
1385 {
1386 function_call_t function_call;
1387 if (call_stack.empty()) {
1388 test_execution_started();
1389 function_call.caller_line = 0;
1390 }
1391 else {
1392 function_call.caller_line = last_breakpoint_entry.line;
1393 }
1394 function_call.function = p_function;
1395 call_stack.push_back(function_call);
1396 }
1397
1398 void TTCN3_Debugger::add_scope(TTCN3_Debug_Scope* p_scope)
1399 {
1400 if (active && !call_stack.empty()) {
1401 call_stack[call_stack.size() - 1].function->add_scope(p_scope);
1402 }
1403 }
1404
1405 void TTCN3_Debugger::remove_function(TTCN3_Debug_Function* p_function)
1406 {
1407 if (!call_stack.empty() && call_stack[call_stack.size() - 1].function == p_function) {
1408 bool removing_test_case = call_stack[call_stack.size() - 1].function->is_test_case();
1409 int caller_line = call_stack[call_stack.size() - 1].caller_line;
1410 call_stack.erase_at(call_stack.size() - 1);
1411 if (call_stack.empty()) {
1412 test_execution_finished();
1413 }
1414 if (caller_line != 0 && (stepping_type == STEP_INTO || stepping_type == STEP_OUT ||
1415 (stepping_type == STEP_OVER && call_stack.size() != stepping_stack_size))) {
1416 breakpoint_entry(caller_line);
1417 }
1418 if (exiting && TTCN_Runtime::is_single() && !call_stack.empty() && removing_test_case &&
1419 call_stack[call_stack.size() - 1].function->is_control_part()) {
1420 // 'exit all' was requested while executing a test case called by a control
1421 // part, which means the test case caught the original TC_End exception;
1422 // another exception must be thrown to stop the control part, too
1423 throw TC_End();
1424 }
1425 }
1426 }
1427
1428 void TTCN3_Debugger::remove_scope(TTCN3_Debug_Scope* p_scope)
1429 {
1430 if (!call_stack.empty()) {
1431 call_stack[call_stack.size() - 1].function->remove_scope(p_scope);
1432 }
1433 }
1434
1435 TTCN3_Debugger::variable_t* TTCN3_Debugger::add_variable(const void* p_value,
1436 const char* p_name,
1437 const char* p_type,
1438 const char* p_module,
1439 TTCN3_Debugger::print_function_t p_print_function)
1440 {
1441 if (call_stack.empty()) {
1442 // no call stack yet, so this is a global or component variable
1443 variable_t* var = find_variable(p_value);
1444 if (var == NULL) {
1445 var = new TTCN3_Debugger::variable_t;
1446 var->cvalue = p_value;
1447 var->name = p_name;
1448 var->type_name = p_type;
1449 var->module = p_module;
1450 var->print_function = p_print_function;
1451 var->set_function = NULL;
1452 variables.push_back(var);
1453 }
1454 return var;
1455 }
1456 else if (active) {
1457 // it's a local variable for the top-most function
1458 return call_stack[call_stack.size() - 1].function->add_variable(
1459 p_value, p_name, p_type, p_module, p_print_function);
1460 }
1461 return NULL;
1462 }
1463
1464 TTCN3_Debugger::variable_t* TTCN3_Debugger::add_variable(void* p_value,
1465 const char* p_name,
1466 const char* p_type,
1467 const char* p_module,
1468 TTCN3_Debugger::print_function_t p_print_function,
1469 TTCN3_Debugger::set_function_t p_set_function)
1470 {
1471 if (call_stack.empty()) {
1472 // no call stack yet, so this is a global or component variable
1473 variable_t* var = find_variable(p_value);
1474 if (var == NULL) {
1475 var = new TTCN3_Debugger::variable_t;
1476 var->value = p_value;
1477 var->name = p_name;
1478 var->type_name = p_type;
1479 var->module = p_module;
1480 var->print_function = p_print_function;
1481 var->set_function = p_set_function;
1482 variables.push_back(var);
1483 }
1484 return var;
1485 }
1486 else if (active) {
1487 // it's a local variable for the top-most function
1488 return call_stack[call_stack.size() - 1].function->add_variable(
1489 p_value, p_name, p_type, p_module, p_print_function, p_set_function);
1490 }
1491 return NULL;
1492 }
1493
1494 void TTCN3_Debugger::remove_variable(const variable_t* p_var)
1495 {
1496 if (active && !call_stack.empty()) {
1497 call_stack[call_stack.size() - 1].function->remove_variable(p_var);
1498 }
1499 }
1500
1501 const TTCN3_Debug_Scope* TTCN3_Debugger::get_global_scope(const char* p_module) const
1502 {
1503 for (size_t i = 0; i < global_scopes.size(); ++i) {
1504 if (strcmp(global_scopes[i].name, p_module) == 0) {
1505 return global_scopes[i].scope;
1506 }
1507 }
1508 return NULL;
1509 }
1510
1511 const TTCN3_Debug_Scope* TTCN3_Debugger::get_component_scope(const char* p_component) const
1512 {
1513 for (size_t i = 0; i < component_scopes.size(); ++i) {
1514 if (strcmp(component_scopes[i].name, p_component) == 0) {
1515 return component_scopes[i].scope;
1516 }
1517 }
1518 return NULL;
1519 }
1520
1521 void TTCN3_Debugger::store_function_call(char* p_snapshot)
1522 {
1523 if (function_calls.cfg == CALLS_RING_BUFFER && function_calls.buffer.size == 0) {
1524 Free(p_snapshot);
1525 return;
1526 }
1527 // append timestamp to the beginning of the snapshot
1528 timeval tv;
1529 gettimeofday(&tv, NULL);
1530 struct tm *lt = localtime(&tv.tv_sec);
1531 if (lt != NULL) {
1532 char* temp = mprintf("%02d:%02d:%02d.%06ld\t%s", lt->tm_hour, lt->tm_min,
1533 lt->tm_sec, tv.tv_usec, p_snapshot);
1534 Free(p_snapshot);
1535 p_snapshot = temp;
1536 }
1537 switch (function_calls.cfg) {
1538 case CALLS_TO_FILE:
1539 fseek(function_calls.file.ptr, 0, SEEK_END); // in case multiple processes are writing the same file
1540 fputs(p_snapshot, function_calls.file.ptr);
1541 Free(p_snapshot);
1542 fputc('\n', function_calls.file.ptr);
1543 fflush(function_calls.file.ptr);
1544 break;
1545 case CALLS_RING_BUFFER: {
1546 bool first = function_calls.buffer.end == -1;
1547 function_calls.buffer.end = (function_calls.buffer.end + 1) %
1548 function_calls.buffer.size;
1549 function_calls.buffer.ptr[function_calls.buffer.end] = p_snapshot;
1550 if (!first && function_calls.buffer.start == function_calls.buffer.end) {
1551 function_calls.buffer.start = (function_calls.buffer.start + 1) %
1552 function_calls.buffer.size;
1553 }
1554 break; }
1555 case CALLS_STORE_ALL:
1556 if (function_calls.buffer.end == function_calls.buffer.size - 1) {
1557 function_calls.buffer.size += BUFFER_INCREASE;
1558 function_calls.buffer.ptr = (char**)Realloc(function_calls.buffer.ptr,
1559 function_calls.buffer.size * sizeof(char*));
1560 }
1561 ++function_calls.buffer.end;
1562 function_calls.buffer.ptr[function_calls.buffer.end] = p_snapshot;
1563 break;
1564 }
1565 }
1566
1567 #define CHECK_NOF_ARGUMENTS(exp_num) \
1568 if (exp_num != p_argument_count) { \
1569 print(DRET_NOTIFICATION, "Invalid number of arguments. Expected %d, got %d.", \
1570 (int)exp_num, (int)p_argument_count); \
1571 return; \
1572 }
1573
1574 #define CHECK_NOF_ARGUMENTS_RANGE(min, max) \
1575 if ((int)min > p_argument_count || (int)max < p_argument_count) { \
1576 print(DRET_NOTIFICATION, "Invalid number of arguments. Expected at least %d " \
1577 "and at most %d, got %d.", (int)min, (int)max, p_argument_count); \
1578 return; \
1579 }
1580
1581 #define CHECK_NOF_ARGUMENTS_MIN(min) \
1582 if ((int)min > p_argument_count) { \
1583 print(DRET_NOTIFICATION, "Invalid number of arguments. Expected at least %d, got %d.", \
1584 (int)min, p_argument_count); \
1585 return; \
1586 }
1587
1588 #define CHECK_INT_ARGUMENT(arg_idx) \
1589 { \
1590 if (!is_numeric(p_arguments[arg_idx])) { \
1591 print(DRET_NOTIFICATION, "Argument %d is not an integer.", (int)(arg_idx + 1)); \
1592 return; \
1593 } \
1594 }
1595
1596 #define CHECK_CALL_STACK(print_msg) \
1597 if (!active) { \
1598 if (print_msg) { \
1599 print(DRET_NOTIFICATION, "This command can only be used if the debugger " \
1600 "is switched on."); \
1601 } \
1602 return; \
1603 } \
1604 if (call_stack.empty()) { \
1605 if (print_msg) { \
1606 print(DRET_NOTIFICATION, "This command can only be used if the debugger's " \
1607 "call stack is not empty."); \
1608 } \
1609 return; \
1610 }
1611
1612 void TTCN3_Debugger::execute_command(int p_command, int p_argument_count,
1613 char** p_arguments)
1614 {
1615 if (!enabled) {
1616 return;
1617 }
1618 for (int i = 0; i < p_argument_count; ++i) {
1619 if (p_arguments[i] == NULL) {
1620 print(DRET_NOTIFICATION, "Argument %d is a null pointer.", i + 1);
1621 return;
1622 }
1623 }
1624 switch (p_command) {
1625 case D_SWITCH:
1626 CHECK_NOF_ARGUMENTS(1)
1627 switch_state(p_arguments[0]);
1628 break;
1629 case D_SET_BREAKPOINT:
1630 CHECK_NOF_ARGUMENTS_RANGE(2, 3)
1631 set_breakpoint(p_arguments[0], p_arguments[1],
1632 (p_argument_count == 3) ? p_arguments[2] : NULL);
1633 break;
1634 case D_REMOVE_BREAKPOINT:
1635 CHECK_NOF_ARGUMENTS_RANGE(1, 2)
1636 remove_breakpoint(p_arguments[0], (p_argument_count == 2) ? p_arguments[1] : NULL);
1637 break;
1638 case D_SET_AUTOMATIC_BREAKPOINT:
1639 CHECK_NOF_ARGUMENTS_RANGE(2, 3)
1640 set_automatic_breakpoint(p_arguments[0], p_arguments[1],
1641 (p_argument_count == 3) ? p_arguments[2] : NULL);
1642 break;
1643 case D_SET_OUTPUT:
1644 CHECK_NOF_ARGUMENTS_RANGE(1, 2)
1645 set_output(p_arguments[0], (p_argument_count == 2) ? p_arguments[1] : NULL);
1646 break;
1647 case D_SET_GLOBAL_BATCH_FILE:
1648 CHECK_NOF_ARGUMENTS_RANGE(1, 2)
1649 set_global_batch_file(p_arguments[0], (p_argument_count == 2) ? p_arguments[1] : NULL);
1650 break;
1651 case D_FUNCTION_CALL_CONFIG:
1652 CHECK_NOF_ARGUMENTS_RANGE(1, 2)
1653 configure_function_calls(p_arguments[0], (p_argument_count == 2) ? p_arguments[1] : NULL);
1654 break;
1655 case D_PRINT_SETTINGS:
1656 CHECK_NOF_ARGUMENTS(0)
1657 print_settings();
1658 break;
1659 case D_PRINT_CALL_STACK:
1660 CHECK_CALL_STACK(true)
1661 CHECK_NOF_ARGUMENTS(0)
1662 print_call_stack();
1663 break;
1664 case D_SET_STACK_LEVEL:
1665 CHECK_CALL_STACK(true)
1666 CHECK_NOF_ARGUMENTS(1)
1667 CHECK_INT_ARGUMENT(0)
1668 set_stack_level(str2int(p_arguments[0]));
1669 break;
1670 case D_LIST_VARIABLES:
1671 CHECK_CALL_STACK(true)
1672 CHECK_NOF_ARGUMENTS_RANGE(0, 2)
1673 call_stack[STACK_LEVEL].function->list_variables(
1674 (p_argument_count > 0) ? p_arguments[0] : NULL,
1675 (p_argument_count == 2) ? p_arguments[1] : NULL);
1676 break;
1677 case D_PRINT_VARIABLE:
1678 CHECK_CALL_STACK(true)
1679 CHECK_NOF_ARGUMENTS_MIN(1)
1680 for (int i = 0; i < p_argument_count; ++i) {
1681 if (i != 0) {
1682 add_to_result("\n");
1683 }
1684 if (!strcmp(p_arguments[i], "$")) {
1685 // '$' refers to the result of the last D_LIST_VARIABLES command
1686 // these variable names are separated by spaces
1687 if (last_variable_list != NULL) {
1688 size_t len = mstrlen(last_variable_list);
1689 size_t start = 0;
1690 for (size_t j = 0; j < len; ++j) {
1691 if (last_variable_list[j] == ' ') {
1692 // extract the variable name before this space
1693 char* var_name = mcopystrn(last_variable_list + start, j - start);
1694 print_variable(var_name);
1695 Free(var_name);
1696 add_to_result("\n");
1697 start = j + 1;
1698 }
1699 }
1700 // extract the last (or only) variable name
1701 char* var_name = mcopystrn(last_variable_list + start, len - start);
1702 print_variable(var_name);
1703 Free(var_name);
1704 }
1705 else {
1706 add_to_result("No previous " D_LIST_VARIABLES_TEXT " result.");
1707 }
1708 }
1709 else {
1710 print_variable(p_arguments[i]);
1711 }
1712 }
1713 break;
1714 case D_OVERWRITE_VARIABLE:
1715 CHECK_CALL_STACK(true)
1716 CHECK_NOF_ARGUMENTS_MIN(2)
1717 overwrite_variable(p_arguments[0], p_argument_count - 1, p_arguments + 1);
1718 break;
1719 case D_PRINT_FUNCTION_CALLS:
1720 CHECK_NOF_ARGUMENTS_RANGE(0, 1)
1721 print_function_calls((p_argument_count > 0) ? p_arguments[0] : NULL);
1722 break;
1723 case D_STEP_OVER:
1724 CHECK_CALL_STACK(true)
1725 CHECK_NOF_ARGUMENTS(0)
1726 step(STEP_OVER);
1727 break;
1728 case D_STEP_INTO:
1729 CHECK_CALL_STACK(true)
1730 CHECK_NOF_ARGUMENTS(0)
1731 step(STEP_INTO);
1732 break;
1733 case D_STEP_OUT:
1734 CHECK_CALL_STACK(true)
1735 CHECK_NOF_ARGUMENTS(0)
1736 step(STEP_OUT);
1737 break;
1738 case D_RUN_TO_CURSOR:
1739 if (!TTCN_Runtime::is_hc() && !TTCN_Runtime::is_single()) {
1740 CHECK_CALL_STACK(TTCN_Runtime::is_mtc())
1741 }
1742 CHECK_NOF_ARGUMENTS(2)
1743 run_to_cursor(p_arguments[0], p_arguments[1]);
1744 break;
1745 case D_HALT:
1746 if (!TTCN_Runtime::is_hc() && !TTCN_Runtime::is_single()) {
1747 CHECK_CALL_STACK(TTCN_Runtime::is_mtc())
1748 }
1749 CHECK_NOF_ARGUMENTS(0)
1750 halt(NULL, false);
1751 break;
1752 case D_CONTINUE:
1753 CHECK_NOF_ARGUMENTS(0)
1754 resume();
1755 break;
1756 case D_EXIT:
1757 if (!TTCN_Runtime::is_hc() && !TTCN_Runtime::is_single()) {
1758 CHECK_CALL_STACK(TTCN_Runtime::is_mtc())
1759 }
1760 CHECK_NOF_ARGUMENTS(1)
1761 exit_(p_arguments[0]);
1762 break;
1763 case D_SETUP:
1764 CHECK_NOF_ARGUMENTS_MIN(11)
1765 if (strlen(p_arguments[0]) > 0) {
1766 switch_state(p_arguments[0]);
1767 }
1768 if (strlen(p_arguments[1]) > 0) {
1769 set_output(p_arguments[1], p_arguments[2]);
1770 }
1771 if (strlen(p_arguments[3]) > 0) {
1772 set_automatic_breakpoint("error", p_arguments[3],
1773 strlen(p_arguments[4]) > 0 ? p_arguments[4] : NULL);
1774 }
1775 if (strlen(p_arguments[5]) > 0) {
1776 set_automatic_breakpoint("fail", p_arguments[5],
1777 strlen(p_arguments[6]) > 0 ? p_arguments[6] : NULL);
1778 }
1779 if (strlen(p_arguments[7]) > 0) {
1780 set_global_batch_file(p_arguments[7],
1781 strlen(p_arguments[8]) > 0 ? p_arguments[8] : NULL);
1782 }
1783 if (strlen(p_arguments[9]) > 0) {
1784 configure_function_calls(p_arguments[9],
1785 strlen(p_arguments[10]) > 0 ? p_arguments[10] : NULL);
1786 }
1787 for (int i = 11; i < p_argument_count; i += 3) {
1788 set_breakpoint(p_arguments[i], p_arguments[i + 1],
1789 strlen(p_arguments[i + 2]) > 0 ? p_arguments[i + 2] : NULL);
1790 }
1791 break;
1792 default:
1793 print(DRET_NOTIFICATION, "Invalid command received (ID: %d).", p_command);
1794 return;
1795 }
1796 if (command_result != NULL) {
1797 print(DRET_DATA, command_result);
1798 if (p_command == D_LIST_VARIABLES) {
1799 Free(last_variable_list);
1800 last_variable_list = command_result;
1801 }
1802 else {
1803 Free(command_result);
1804 }
1805 command_result = NULL;
1806 }
1807 }
1808
1809 void TTCN3_Debugger::init_PTC_settings()
1810 {
1811 if (output_file == NULL && output_file_name != NULL) {
1812 char* final_file_name = finalize_file_name(output_file_name);
1813 output_file = fopen(final_file_name, "a");
1814 if (output_file == NULL) {
1815 print(DRET_NOTIFICATION, "Failed to open file '%s' for writing.", final_file_name);
1816 }
1817 Free(final_file_name);
1818 }
1819 if (function_calls.cfg == CALLS_TO_FILE) {
1820 char* final_file_name = finalize_file_name(function_calls.file.name);
1821 function_calls.file.ptr = fopen(final_file_name, "a");
1822 if (function_calls.file.ptr == NULL) {
1823 print(DRET_NOTIFICATION, "Failed to open file '%s' for writing.", final_file_name);
1824 }
1825 Free(final_file_name);
1826 }
1827 else if (function_calls.cfg == CALLS_RING_BUFFER && function_calls.buffer.size != 0) {
1828 function_calls.buffer.ptr = (char**)Malloc(function_calls.buffer.size * sizeof(char*));
1829 }
1830 }
1831
1832 //////////////////////////////////////////////////////
1833 //////////////// TTCN3_Debug_Scope ///////////////////
1834 //////////////////////////////////////////////////////
1835
1836 TTCN3_Debug_Scope::TTCN3_Debug_Scope()
1837 {
1838 ttcn3_debugger.add_scope(this);
1839 }
1840
1841 TTCN3_Debug_Scope::~TTCN3_Debug_Scope()
1842 {
1843 for (size_t i = 0; i < variables.size(); ++i) {
1844 ttcn3_debugger.remove_variable(variables[i]);
1845 }
1846 ttcn3_debugger.remove_scope(this);
1847 }
1848
1849 void TTCN3_Debug_Scope::add_variable(const void* p_value,
1850 const char* p_name,
1851 const char* p_type,
1852 const char* p_module,
1853 TTCN3_Debugger::print_function_t p_print_function)
1854 {
1855 TTCN3_Debugger::variable_t* var = ttcn3_debugger.add_variable(p_value, p_name,
1856 p_type, p_module, p_print_function);
1857 if (var != NULL) {
1858 variables.push_back(var);
1859 }
1860 }
1861
1862 void TTCN3_Debug_Scope::add_variable(void* p_value,
1863 const char* p_name,
1864 const char* p_type,
1865 const char* p_module,
1866 TTCN3_Debugger::print_function_t p_print_function,
1867 TTCN3_Debugger::set_function_t p_set_function)
1868 {
1869 TTCN3_Debugger::variable_t* var = ttcn3_debugger.add_variable(p_value, p_name,
1870 p_type, p_module, p_print_function, p_set_function);
1871 if (var != NULL) {
1872 variables.push_back(var);
1873 }
1874 }
1875
1876 TTCN3_Debugger::variable_t* TTCN3_Debug_Scope::find_variable(const char* p_name) const
1877 {
1878 for (size_t i = 0; i < variables.size(); ++i) {
1879 TTCN3_Debugger::variable_t* var = variables[i];
1880 if (strcmp(var->name, p_name) == 0) {
1881 // the string matches the variable's name
1882 return var;
1883 }
1884 else if (var->module != NULL) {
1885 size_t name_len = strlen(var->name);
1886 size_t mod_len = strlen(var->module);
1887 size_t len = strlen(p_name);
1888 if (len == mod_len + name_len + 1 && p_name[mod_len] == '.' &&
1889 strncmp(p_name, var->module, mod_len) == 0 &&
1890 strncmp(p_name + mod_len + 1, var->name, name_len) == 0) {
1891 // the string matches the variable's name prefixed by its module name
1892 return var;
1893 }
1894 }
1895 }
1896 return NULL;
1897 }
1898
1899 void TTCN3_Debug_Scope::list_variables(regex_t* p_posix_regexp, bool& p_first,
1900 const char* p_module) const
1901 {
1902 for (size_t i = 0; i < variables.size(); ++i) {
1903 if (p_posix_regexp == NULL ||
1904 regexec(p_posix_regexp, variables[i]->name, 0, NULL, 0) == 0) {
1905 bool imported = p_module != NULL && strcmp(p_module, variables[i]->module) != 0;
1906 ttcn3_debugger.add_to_result("%s%s%s%s", p_first ? "" : " ",
1907 imported ? variables[i]->module : "", imported ? "." : "", variables[i]->name);
1908 p_first = false;
1909 }
1910 }
1911 }
1912
1913 //////////////////////////////////////////////////////
1914 /////////////// TTCN3_Debug_Function /////////////////
1915 //////////////////////////////////////////////////////
1916
1917 TTCN3_Debug_Function::TTCN3_Debug_Function(const char* p_name,
1918 const char* p_type,
1919 const char* p_module,
1920 const charstring_list& p_parameter_names,
1921 const charstring_list& p_parameter_types,
1922 const char* p_component_name)
1923 : function_name(p_name), function_type(p_type), module_name(p_module)
1924 , parameter_names(new charstring_list(p_parameter_names))
1925 , parameter_types(new charstring_list(p_parameter_types))
1926 {
1927 ttcn3_debugger.add_function(this);
1928 global_scope = ttcn3_debugger.get_global_scope(p_module);
1929 component_scope = (p_component_name != NULL) ?
1930 ttcn3_debugger.get_component_scope(p_component_name) : NULL;
1931 if (function_name == NULL) {
1932 function_name = p_module; // for control parts
1933 }
1934 }
1935
1936 TTCN3_Debug_Function::~TTCN3_Debug_Function()
1937 {
1938 if (ttcn3_debugger.is_on()) {
1939 char* snapshot = mprintf("[%s]\tfinished\t%s(", function_type, function_name);
1940 if (parameter_names->size_of() > 0) {
1941 for (int i = 0; i < parameter_names->size_of(); ++i) {
1942 if (i > 0) {
1943 snapshot = mputstr(snapshot, ", ");
1944 }
1945 snapshot = mputprintf(snapshot, "[%s] %s := ", (const char*)((*parameter_types)[i]),
1946 (const char*)((*parameter_names)[i]));
1947 if ((*parameter_types)[i] == "out" || (*parameter_types)[i] == "inout") {
1948 const TTCN3_Debugger::variable_t* parameter = find_variable((*parameter_names)[i]);
1949 snapshot = mputstr(snapshot, parameter->print_function(*parameter));
1950 }
1951 else {
1952 snapshot = mputc(snapshot, '-');
1953 }
1954 }
1955 }
1956 snapshot = mputc(snapshot, ')');
1957 if (return_value.is_bound()) {
1958 snapshot = mputprintf(snapshot, " returned %s", (const char*)return_value);
1959 }
1960 ttcn3_debugger.store_function_call(snapshot);
1961 }
1962 for (size_t i = 0; i < variables.size(); ++i) {
1963 delete variables[i];
1964 }
1965 delete parameter_names;
1966 delete parameter_types;
1967 ttcn3_debugger.remove_function(this);
1968 }
1969
1970 TTCN3_Debugger::variable_t* TTCN3_Debug_Function::add_variable(const void* p_value,
1971 const char* p_name,
1972 const char* p_type,
1973 const char* p_module,
1974 TTCN3_Debugger::print_function_t p_print_function)
1975 {
1976 if (ttcn3_debugger.is_on()) {
1977 TTCN3_Debugger::variable_t* var = new TTCN3_Debugger::variable_t;
1978 var->cvalue = p_value;
1979 var->name = p_name;
1980 var->type_name = p_type;
1981 var->module = p_module;
1982 var->print_function = p_print_function;
1983 var->set_function = NULL;
1984 variables.push_back(var);
1985 return var;
1986 }
1987 return NULL;
1988 }
1989
1990 TTCN3_Debugger::variable_t* TTCN3_Debug_Function::add_variable(void* p_value,
1991 const char* p_name,
1992 const char* p_type,
1993 const char* p_module,
1994 TTCN3_Debugger::print_function_t p_print_function,
1995 TTCN3_Debugger::set_function_t p_set_function)
1996 {
1997 if (ttcn3_debugger.is_on()) {
1998 TTCN3_Debugger::variable_t* var = new TTCN3_Debugger::variable_t;
1999 var->value = p_value;
2000 var->name = p_name;
2001 var->type_name = p_type;
2002 var->module = p_module;
2003 var->print_function = p_print_function;
2004 var->set_function = p_set_function;
2005 variables.push_back(var);
2006 return var;
2007 }
2008 return NULL;
2009 }
2010
2011 void TTCN3_Debug_Function::set_return_value(const CHARSTRING& p_value)
2012 {
2013 return_value = p_value;
2014 }
2015
2016 void TTCN3_Debug_Function::initial_snapshot() const
2017 {
2018 if (ttcn3_debugger.is_on()) {
2019 char* snapshot = mprintf("[%s]\tstarted \t%s(", function_type, function_name);
2020 if (parameter_names->size_of() > 0) {
2021 for (int i = 0; i < parameter_names->size_of(); ++i) {
2022 if (i > 0) {
2023 snapshot = mputstr(snapshot, ", ");
2024 }
2025 snapshot = mputprintf(snapshot, "[%s] %s := ", (const char*)((*parameter_types)[i]),
2026 (const char*)((*parameter_names)[i]));
2027 if ((*parameter_types)[i] == "in" || (*parameter_types)[i] == "inout") {
2028 const TTCN3_Debugger::variable_t* parameter = find_variable((*parameter_names)[i]);
2029 snapshot = mputstr(snapshot, parameter->print_function(*parameter));
2030 }
2031 else {
2032 snapshot = mputc(snapshot, '-');
2033 }
2034 }
2035 }
2036 snapshot = mputstr(snapshot, ")");
2037 ttcn3_debugger.store_function_call(snapshot);
2038 }
2039 }
2040
2041 void TTCN3_Debug_Function::add_scope(TTCN3_Debug_Scope* p_scope)
2042 {
2043 scopes.push_back(p_scope);
2044 }
2045
2046 void TTCN3_Debug_Function::remove_scope(TTCN3_Debug_Scope* p_scope)
2047 {
2048 if (!scopes.empty() && scopes[scopes.size() - 1] == p_scope) {
2049 scopes.erase_at(scopes.size() - 1);
2050 }
2051 }
2052
2053 void TTCN3_Debug_Function::remove_variable(const TTCN3_Debugger::variable_t* p_var)
2054 {
2055 for (size_t i = 0; i < variables.size(); ++i) {
2056 if (variables[i] == p_var) {
2057 variables.erase_at(i);
2058 delete p_var;
2059 break;
2060 }
2061 }
2062 }
2063
2064 TTCN3_Debugger::variable_t* TTCN3_Debug_Function::find_variable(const char* p_name) const
2065 {
2066 for (size_t i = 0; i < variables.size(); ++i) {
2067 if (strcmp(variables[i]->name, p_name) == 0) {
2068 return variables[i];
2069 }
2070 }
2071 // it's not a local variable, it might still be a global or component variable
2072 if (component_scope != NULL) {
2073 TTCN3_Debugger::variable_t* res = component_scope->find_variable(p_name);
2074 if (res != NULL) {
2075 return res;
2076 }
2077 }
2078 return (global_scope != NULL) ? global_scope->find_variable(p_name) : NULL;
2079 }
2080
2081 void TTCN3_Debug_Function::print_function() const
2082 {
2083 ttcn3_debugger.add_to_result("[%s]\t%s(", function_type, function_name);
2084 if (parameter_names->size_of() > 0) {
2085 for (int i = 0; i < parameter_names->size_of(); ++i) {
2086 if (i > 0) {
2087 ttcn3_debugger.add_to_result(", ");
2088 }
2089 const TTCN3_Debugger::variable_t* parameter = find_variable((*parameter_names)[i]);
2090 ttcn3_debugger.add_to_result("[%s] %s := %s", (const char*)(*parameter_types)[i],
2091 (const char*)(*parameter_names)[i], (const char*)parameter->print_function(*parameter));
2092 }
2093 }
2094 ttcn3_debugger.add_to_result(")");
2095 }
2096
2097 void TTCN3_Debug_Function::list_variables(const char* p_scope, const char* p_filter) const
2098 {
2099 bool first = true;
2100 bool list_local = false;
2101 bool list_global = false;
2102 bool list_comp = false;
2103 if (p_scope == NULL || !strcmp(p_scope, "all")) {
2104 list_local = true;
2105 list_global = true;
2106 list_comp = true;
2107 }
2108 else if (!strcmp(p_scope, "local")) {
2109 list_local = true;
2110 }
2111 else if (!strcmp(p_scope, "global")) {
2112 list_global = true;
2113 }
2114 else if (!strcmp(p_scope, "comp")) {
2115 list_comp = true;
2116 }
2117 else {
2118 ttcn3_debugger.print(DRET_NOTIFICATION, "Argument 1 is invalid. "
2119 "Expected 'local', 'global', 'comp' or 'all'.");
2120 return;
2121 }
2122 regex_t* posix_regexp = NULL;
2123 if (p_filter != NULL) {
2124 char* posix_str = TTCN_pattern_to_regexp(p_filter);
2125 if (posix_str == NULL) {
2126 ttcn3_debugger.print(DRET_NOTIFICATION, "Argument 2 is invalid. "
2127 "Expected a valid TTCN-3 character pattern.");
2128 return;
2129 }
2130 posix_regexp = new regex_t;
2131 int ret_val = regcomp(posix_regexp, posix_str, REG_EXTENDED | REG_NOSUB);
2132 Free(posix_str);
2133 if (ret_val != 0) {
2134 char msg[512];
2135 regerror(ret_val, posix_regexp, msg, sizeof(msg));
2136 regfree(posix_regexp);
2137 delete posix_regexp;
2138 ttcn3_debugger.print(DRET_NOTIFICATION, "Compilation of POSIX regular "
2139 "expression failed.");
2140 return;
2141 }
2142 }
2143 if (list_local) {
2144 for (size_t i = 0; i < variables.size(); ++i) {
2145 if (posix_regexp == NULL ||
2146 regexec(posix_regexp, variables[i]->name, 0, NULL, 0) == 0) {
2147 ttcn3_debugger.add_to_result("%s%s", first ? "" : " ", variables[i]->name);
2148 first = false;
2149 }
2150 }
2151 }
2152 if (list_global && global_scope != NULL && global_scope->has_variables()) {
2153 global_scope->list_variables(posix_regexp, first, module_name);
2154 }
2155 if (list_comp && component_scope != NULL && component_scope->has_variables()) {
2156 component_scope->list_variables(posix_regexp, first, NULL);
2157 }
2158 if (first) {
2159 ttcn3_debugger.print(DRET_NOTIFICATION, "No variables found.");
2160 }
2161 if (posix_regexp != NULL) {
2162 regfree(posix_regexp);
2163 delete posix_regexp;
2164 }
2165 }
2166
2167 bool TTCN3_Debug_Function::is_control_part() const
2168 {
2169 return !strcmp(function_type, "control");
2170 }
2171
2172 bool TTCN3_Debug_Function::is_test_case() const
2173 {
2174 return !strcmp(function_type, "testcase");
2175 }
This page took 0.100056 seconds and 5 git commands to generate.