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
10 * Baranyi, Botond – initial implementation
12 ******************************************************************************/
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"
23 //////////////////////////////////////////////////////
24 ////////////////// TTCN3_Debugger ////////////////////
25 //////////////////////////////////////////////////////
27 #define BUFFER_INCREASE 100
29 TTCN3_Debugger ttcn3_debugger
;
31 void TTCN3_Debugger::switch_state(const char* p_state_str
)
33 if (!strcmp(p_state_str
, "on")) {
35 print(DRET_NOTIFICATION
, "The debugger is already switched on.");
39 print(DRET_SETTING_CHANGE
, "Debugger switched on.");
42 else if(!strcmp(p_state_str
, "off")) {
44 print(DRET_NOTIFICATION
, "The debugger is already switched off.");
48 print(DRET_SETTING_CHANGE
, "Debugger switched off.");
52 print(DRET_NOTIFICATION
, "Argument 1 is invalid. Expected 'on' or 'off'.");
56 static bool is_numeric(const char* p_str
)
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') {
67 void TTCN3_Debugger::set_breakpoint(const char* p_module
, const char* p_location
,
68 const char* batch_file
)
71 char* function
= NULL
;
72 if (is_numeric(p_location
)) {
73 // it's a line breakpoint
74 line
= strtol(p_location
, NULL
, 10);
77 // it's a function breakpoint
78 function
= mcopystr(p_location
);
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()) {
85 bp
.module
= mcopystr(p_module
);
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.",
92 batch_file
!= NULL
? "with batch file '" : "with no batch file",
93 batch_file
!= NULL
? batch_file
: "", batch_file
!= NULL
? "'" : "");
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
);
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
);
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
);
113 Free(breakpoints
[pos
].batch_file
);
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
);
121 print(DRET_NOTIFICATION
, "Breakpoint already set in module '%s' at %s "
122 "with no batch file.", p_module
, loc_str
);
125 breakpoints
[pos
].batch_file
= batch_file
!= NULL
? mcopystr(batch_file
) : NULL
;
130 void TTCN3_Debugger::remove_breakpoint(const char* p_module
, const char* p_location
)
132 bool all_breakpoints
= !strcmp(p_module
, "all");
133 if (p_location
!= NULL
) {
134 if (!strcmp(p_location
, "all")) {
136 for (size_t i
= breakpoints
.size(); i
> 0; --i
) {
137 if (!strcmp(breakpoints
[i
- 1].module
, p_module
)) {
139 Free(breakpoints
[i
- 1].module
);
140 Free(breakpoints
[i
- 1].batch_file
);
141 breakpoints
.erase_at(i
- 1);
145 print(DRET_SETTING_CHANGE
, "Removed all breakpoints in module '%s'.", p_module
);
148 print(DRET_NOTIFICATION
, "No breakpoints found in module '%s'.", p_module
);
153 if (all_breakpoints
) {
154 print(DRET_NOTIFICATION
, "Unexpected 2nd argument, when the first "
155 "argument is 'all'.");
159 char* function
= NULL
;
160 if (is_numeric(p_location
)) {
161 // it's a line breakpoint
162 line
= strtol(p_location
, NULL
, 10);
165 // it's a function breakpoint
166 function
= mcopystr(p_location
);
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.",
180 print(DRET_NOTIFICATION
, "No breakpoint found in module '%s' at %s.",
187 else if (!all_breakpoints
) {
188 print(DRET_NOTIFICATION
, "2 arguments expected, when the first argument is "
192 // delete all breakpoints if we got this far
193 if (breakpoints
.empty()) {
194 print(DRET_NOTIFICATION
, "No breakpoints found.");
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
);
203 print(DRET_SETTING_CHANGE
, "Removed all breakpoints.");
207 void TTCN3_Debugger::set_automatic_breakpoint(const char* p_event_str
,
208 const char* p_state_str
,
209 const char* p_batch_file
)
212 if (!strcmp(p_state_str
, "on")) {
215 else if(!strcmp(p_state_str
, "off")) {
219 print(DRET_NOTIFICATION
, "Argument 2 is invalid. Expected 'on' or 'off'.");
222 const char* sbp_type_str
;
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";
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";
238 print(DRET_NOTIFICATION
, "Argument 1 is invalid. Expected 'error' or 'fail'.");
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
) ? "'" : "");
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
);
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
,
263 print(DRET_SETTING_CHANGE
, "Batch file '%s' removed from automatic "
264 "breakpoint at %s.", *old_batch_file_ptr
, sbp_type_str
);
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
);
273 print(DRET_NOTIFICATION
, "Automatic breakpoint at %s was already "
274 "switched on with no batch file.", sbp_type_str
);
279 print(DRET_NOTIFICATION
, "Automatic breakpoint at %s was already switched off.");
282 Free(*old_batch_file_ptr
);
283 *old_batch_file_ptr
= p_batch_file
!= NULL
? mcopystr(p_batch_file
) : NULL
;
286 void TTCN3_Debugger::print_settings()
289 add_to_result("Debugger is switched %s.\n", active
? "on" : "off");
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
: "");
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
);
310 add_to_result("buffer size: ");
311 if (function_calls
.cfg
== CALLS_STORE_ALL
) {
312 add_to_result("infinite.\n");
315 add_to_result("%d.\n", function_calls
.buffer
.size
);
319 if (breakpoints
.empty()) {
320 add_to_result("No user breakpoints.\n");
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
);
331 add_to_result("%s", bp
.function
);
333 if (bp
.batch_file
!= NULL
) {
334 add_to_result(" %s", bp
.batch_file
);
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
: "");
347 #define STACK_LEVEL (stack_level >= 0) ? (size_t)stack_level : (call_stack.size() - 1)
349 void TTCN3_Debugger::print_call_stack()
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
364 void TTCN3_Debugger::set_stack_level(int new_level
)
367 print(DRET_NOTIFICATION
, "Stack level can only be set if test execution is halted.");
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());
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
;
382 void TTCN3_Debugger::print_variable(const char* p_var_name
)
384 const variable_t
* var
= call_stack
[STACK_LEVEL
].function
->find_variable(p_var_name
);
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
));
391 add_to_result("Variable '%s' not found.", p_var_name
);
395 void TTCN3_Debugger::overwrite_variable(const char* p_var_name
,
396 int p_value_element_count
,
397 char** p_value_elements
)
399 variable_t
* var
= call_stack
[STACK_LEVEL
].function
->find_variable(p_var_name
);
401 if (var
->set_function
== NULL
) {
402 print(DRET_NOTIFICATION
, "Constant variables cannot be overwritten.");
405 char* new_value_str
= NULL
;
406 for (int i
= 0; i
< p_value_element_count
; ++i
) {
408 new_value_str
= mputc(new_value_str
, ' ');
410 new_value_str
= mputstr(new_value_str
, p_value_elements
[i
]);
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
) {
416 Debugger_Value_Parsing debug_value_parsing
;
417 boolean handled
= var
->set_function(*var
, *parsed_mp
);
419 print(DRET_NOTIFICATION
, "Variables of type '%s' cannot be overwritten.",
423 add_to_result("[%s] %s := %s", var
->type_name
, var
->name
,
424 (const char*)var
->print_function(*var
));
427 catch (const TC_Error
&) {
428 // do nothing, an error message has already been displayed in this case
435 print(DRET_NOTIFICATION
, "Variable '%s' not found.", p_var_name
);
439 void TTCN3_Debugger::clean_up_function_calls()
441 if (function_calls
.cfg
== CALLS_TO_FILE
) {
442 if (!TTCN_Runtime::is_hc()) {
443 fclose(function_calls
.file
.ptr
);
445 Free(function_calls
.file
.name
);
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
]);
455 Free(function_calls
.buffer
.ptr
[function_calls
.buffer
.end
]);
457 Free(function_calls
.buffer
.ptr
);
462 void TTCN3_Debugger::configure_function_calls(const char* p_config
,
463 const char* p_file_name
)
465 function_call_data_config_t cfg
;
466 // check the command's parameters before actually changing anything
467 if (strcmp(p_config
, "file") == 0) {
470 else if (strcmp(p_config
, "all") == 0) {
471 cfg
= CALLS_STORE_ALL
;
473 else if (is_numeric(p_config
)) {
474 cfg
= CALLS_RING_BUFFER
;
477 print(DRET_NOTIFICATION
, "Argument 1 is invalid. Expected 'file', 'all' or "
478 "ring buffer size.");
482 char* final_file_name
= NULL
;
483 bool same_setting
= false;
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
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
);
505 print(DRET_NOTIFICATION
, "Argument 2 (file name) is missing.");
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
;
514 case CALLS_STORE_ALL
:
515 same_setting
= function_calls
.cfg
== CALLS_STORE_ALL
;
519 clean_up_function_calls();
520 function_calls
.cfg
= cfg
;
523 function_calls
.file
.name
= mcopystr(p_file_name
);
524 if (!TTCN_Runtime::is_hc()) {
525 function_calls
.file
.ptr
= new_fp
;
528 case CALLS_RING_BUFFER
:
529 function_calls
.buffer
.size
= new_size
;
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*));
539 function_calls
.buffer
.ptr
= NULL
;
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
);
550 case CALLS_RING_BUFFER
:
552 print(DRET_SETTING_CHANGE
, "Debugger %sset to not store function call data.",
553 same_setting
? "was already " : "");
556 print(DRET_SETTING_CHANGE
, "Debugger %sset to store only the last %d function calls.",
557 same_setting
? "was already " : "", new_size
);
560 case CALLS_STORE_ALL
:
561 print(DRET_SETTING_CHANGE
, "Debugger %sset to store all function call data.",
562 same_setting
? "was already " : "");
567 void TTCN3_Debugger::print_function_calls(const char* p_amount
)
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.");
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 bool invalid_arg
= false;
580 if (p_amount
== NULL
|| strcmp(p_amount
, "all") == 0) {
583 else if (is_numeric(p_amount
)) {
584 amount
= strtol(p_amount
, NULL
, 10);
588 else if (amount
> limit
) {
596 print(DRET_NOTIFICATION
, "Argument 1 is invalid. Expected 'all' or non-zero "
597 "integer value (number of calls).");
600 for (int i
= (function_calls
.buffer
.end
- amount
+ function_calls
.buffer
.size
+ 1) %
601 function_calls
.buffer
.size
; amount
> 0;
602 i
= (i
+ 1) % function_calls
.buffer
.size
, --amount
) {
603 add_to_result(function_calls
.buffer
.ptr
[i
]);
610 void TTCN3_Debugger::set_output(const char* p_output_type
, const char* p_file_name
)
614 bool same_file
= false;
615 char* final_file_name
= NULL
;
616 // check the command's parameters before actually changing anything
617 if (!strcmp(p_output_type
, "console")) {
621 else if (!strcmp(p_output_type
, "file")) {
625 else if (!strcmp(p_output_type
, "both")) {
630 print(DRET_NOTIFICATION
, "Argument 1 is invalid. Expected 'console', 'file' or 'both'.");
634 if (p_file_name
== NULL
) {
635 print(DRET_NOTIFICATION
, "Argument 2 (output file name) is missing.");
638 if (output_file_name
!= NULL
&& !strcmp(p_file_name
, output_file_name
)) {
639 // don't reopen it if it's the same file as before
642 else if (!TTCN_Runtime::is_hc()) {
643 // don't open any files on HCs, just store settings for future PTCs
644 final_file_name
= finalize_file_name(p_file_name
);
645 new_fp
= fopen(final_file_name
, TTCN_Runtime::is_mtc() ? "w" : "a");
646 if (new_fp
== NULL
) {
647 print(DRET_NOTIFICATION
, "Failed to open file '%s' for writing.", final_file_name
);
648 Free(final_file_name
);
653 // print the change notification to the old output
654 char* file_str
= file
? mprintf("file '%s'", final_file_name
) : NULL
;
655 Free(final_file_name
);
656 print(DRET_SETTING_CHANGE
, "Debugger set to print its output to %s%s%s.",
657 console
? "the console" : "", (console
&& file
) ? " and to " : "",
658 file
? file_str
: "");
662 if (!same_file
&& !TTCN_Runtime::is_hc()) {
663 if (output_file
!= NULL
) {
666 output_file
= new_fp
;
668 send_to_console
= console
;
669 Free(output_file_name
);
671 output_file_name
= mcopystr(p_file_name
);
675 void TTCN3_Debugger::set_global_batch_file(const char* p_state_str
,
676 const char* p_file_name
)
678 bool delete_old
= false;
679 bool copy_new
= false;
680 if (!strcmp(p_state_str
, "on")) {
681 if (p_file_name
!= NULL
) {
682 if (global_batch_file
!= NULL
) {
683 if (!strcmp(p_file_name
, global_batch_file
)) {
684 print(DRET_NOTIFICATION
, "Global batch file was already switched on "
685 "and set to '%s'.", p_file_name
);
688 print(DRET_SETTING_CHANGE
, "Global batch file changed from '%s' to '%s'.",
689 global_batch_file
, p_file_name
);
695 print(DRET_SETTING_CHANGE
, "Global batch file switched on and set to '%s'.",
701 print(DRET_NOTIFICATION
, "Missing batch file name argument.");
704 else if (!strcmp(p_state_str
, "off")) {
705 if (global_batch_file
!= NULL
) {
706 print(DRET_SETTING_CHANGE
, "Global batch file switched off.");
710 print(DRET_NOTIFICATION
, "Global batch file was already switched off.");
714 print(DRET_NOTIFICATION
, "Argument 1 is invalid. Expected 'on' or 'off'.");
717 Free(global_batch_file
);
718 global_batch_file
= NULL
;
721 global_batch_file
= mcopystr(p_file_name
);
725 void TTCN3_Debugger::step(stepping_t p_stepping_type
)
728 print(DRET_NOTIFICATION
, "Stepping commands can only be used when test "
729 "execution is halted.");
732 stepping_type
= p_stepping_type
;
733 stepping_stack_size
= call_stack
.size();
734 if (!TTCN_Runtime::is_single()) {
735 TTCN_Communication::send_debug_continue_req();
740 void TTCN3_Debugger::run_to_cursor(const char* p_module
, const char* p_location
)
742 // all processes receive this command, since the specified location might be
743 // reached in any process, even a PTC that hasn't been created yet
745 print(DRET_NOTIFICATION
, "The 'run to' command can only be used when test "
746 "execution is halted.");
749 temporary_breakpoint
.module
= mcopystr(p_module
);
750 if (is_numeric(p_location
)) {
751 temporary_breakpoint
.line
= strtol(p_location
, NULL
, 10);
752 temporary_breakpoint
.function
= NULL
;
755 temporary_breakpoint
.line
= 0;
756 temporary_breakpoint
.function
= mcopystr(p_location
);
761 void TTCN3_Debugger::halt(const char* p_batch_file
, bool p_run_global_batch
)
765 Free(temporary_breakpoint
.module
);
766 temporary_breakpoint
.module
= NULL
;
767 temporary_breakpoint
.line
= 0;
768 Free(temporary_breakpoint
.function
);
769 temporary_breakpoint
.function
= NULL
;
770 if (!TTCN_Runtime::is_hc()) {
771 stepping_type
= NOT_STEPPING
;
772 stack_level
= call_stack
.size() - 1;
773 print(DRET_NOTIFICATION
, "Test execution halted.");
774 if (p_batch_file
!= NULL
) {
775 if (TTCN_Runtime::is_single()) {
776 TTCN_Debugger_UI::execute_batch_file(p_batch_file
);
779 TTCN_Communication::send_debug_batch(p_batch_file
);
782 else if (p_run_global_batch
&& global_batch_file
!= NULL
) {
783 if (TTCN_Runtime::is_single()) {
784 TTCN_Debugger_UI::execute_batch_file(global_batch_file
);
787 TTCN_Communication::send_debug_batch(global_batch_file
);
790 if (TTCN_Runtime::is_single()) {
791 if (halted
&& !halt_at_start
) {
795 TTCN_Debugger_UI::read_loop();
799 TTCN_Communication::process_debug_messages();
804 print(DRET_NOTIFICATION
, "Test execution is already halted.");
808 void TTCN3_Debugger::resume()
813 print(DRET_NOTIFICATION
, "Test execution resumed.");
816 print(DRET_NOTIFICATION
, "Test execution is not halted.");
820 void TTCN3_Debugger::exit_(const char* p_what
)
822 if (!strcmp(p_what
, "test")) {
825 else if (!strcmp(p_what
, "all")) {
829 print(DRET_NOTIFICATION
, "Argument 1 is invalid. Expected 'test' or 'all'.");
833 if (!TTCN_Runtime::is_hc()) {
834 print((exiting
&& TTCN_Runtime::is_mtc()) ? DRET_EXIT_ALL
: DRET_NOTIFICATION
,
835 "Exiting %s.", exiting
? "test execution" : "current test");
836 TTCN_Runtime::stop_execution();
840 size_t TTCN3_Debugger::find_breakpoint(const char* p_module
, int p_line
,
841 const char* p_function
) const
843 for (size_t i
= 0; i
< breakpoints
.size(); ++i
) {
844 if (!strcmp(breakpoints
[i
].module
, p_module
) &&
845 ((p_line
!= 0 && breakpoints
[i
].line
== p_line
) ||
846 (p_function
!= NULL
&& breakpoints
[i
].function
!= NULL
&&
847 strcmp(breakpoints
[i
].function
, p_function
) == 0))) {
851 return breakpoints
.size();
854 TTCN3_Debugger::variable_t
* TTCN3_Debugger::find_variable(const void* p_value
) const
856 for (size_t i
= 0; i
< variables
.size(); ++i
) {
857 if (variables
[i
]->value
== p_value
) {
864 char* TTCN3_Debugger::finalize_file_name(const char* p_file_name_skeleton
)
866 if (p_file_name_skeleton
== NULL
) {
869 size_t len
= strlen(p_file_name_skeleton
);
871 char* ret_val
= NULL
;
872 for (size_t i
= 0; i
< len
- 1; ++i
) {
873 if (p_file_name_skeleton
[i
] == '%') {
874 ret_val
= mputstrn(ret_val
, p_file_name_skeleton
+ next_idx
, i
- next_idx
);
875 switch (p_file_name_skeleton
[i
+ 1]) {
876 case 'e': // %e -> executable name
877 ret_val
= mputstr(ret_val
, TTCN_Logger::get_executable_name());
879 case 'h': // %h -> host name
880 ret_val
= mputstr(ret_val
, TTCN_Runtime::get_host_name());
882 case 'p': // %p -> process ID
883 ret_val
= mputprintf(ret_val
, "%ld", (long)getpid());
885 case 'l': { // %l -> login name
887 struct passwd
*p
= getpwuid(getuid());
889 ret_val
= mputstr(ret_val
, p
->pw_name
);
893 case 'r': // %r -> component reference
894 if (TTCN_Runtime::is_single()) {
895 ret_val
= mputstr(ret_val
, "single");
897 else if (TTCN_Runtime::is_mtc()) {
898 ret_val
= mputstr(ret_val
, "mtc");
900 else if (TTCN_Runtime::is_ptc()) {
901 ret_val
= mputprintf(ret_val
, "%d", (component
)self
);
904 case 'n': // %n -> component name
905 if (TTCN_Runtime::is_mtc()) {
906 ret_val
= mputstr(ret_val
, "MTC");
908 else if (TTCN_Runtime::is_ptc()) {
909 ret_val
= mputstr(ret_val
, TTCN_Runtime::get_component_name());
912 case '%': // %% -> single %
913 ret_val
= mputc(ret_val
, '%');
915 default: // unknown sequence -> leave it as it is
916 ret_val
= mputstrn(ret_val
, p_file_name_skeleton
+ i
, 2);
923 if (next_idx
< len
) {
924 ret_val
= mputstr(ret_val
, p_file_name_skeleton
+ next_idx
);
929 void TTCN3_Debugger::test_execution_started()
931 if (function_calls
.cfg
!= CALLS_TO_FILE
) {
932 if (function_calls
.buffer
.size
!= 0 && function_calls
.buffer
.end
!= -1) {
933 for (int i
= function_calls
.buffer
.start
;
934 i
!= function_calls
.buffer
.end
;
935 i
= (i
+ 1) % function_calls
.buffer
.size
) {
936 Free(function_calls
.buffer
.ptr
[i
]);
938 Free(function_calls
.buffer
.ptr
[function_calls
.buffer
.end
]);
940 if (function_calls
.cfg
== CALLS_STORE_ALL
) {
941 Free(function_calls
.buffer
.ptr
);
942 function_calls
.buffer
.ptr
= NULL
;
944 function_calls
.buffer
.start
= 0;
945 function_calls
.buffer
.end
= -1;
948 if (TTCN_Runtime::is_single()) {
949 TTCN_Debugger_UI::init();
950 if (initial_batch_file
) {
951 halt(initial_batch_file
, false);
953 else if (halt_at_start
) {
957 halt_at_start
= true;
960 void TTCN3_Debugger::test_execution_finished()
962 stepping_type
= NOT_STEPPING
;
963 Free(temporary_breakpoint
.module
);
964 temporary_breakpoint
.module
= NULL
;
965 temporary_breakpoint
.line
= 0;
966 Free(temporary_breakpoint
.function
);
967 temporary_breakpoint
.function
= NULL
;
968 last_breakpoint_entry
.module
= NULL
;
969 last_breakpoint_entry
.line
= 0;
970 last_breakpoint_entry
.stack_size
= 0;
971 if (TTCN_Runtime::is_single()) {
972 TTCN_Debugger_UI::clean_up();
976 void TTCN3_Debugger::print(int return_type
, const char* fmt
, ...) const
978 if (TTCN_Runtime::is_hc()) {
979 // don't display anything while on the HC process
983 va_start(parameters
, fmt
);
984 char* str
= mprintf_va_list(fmt
, parameters
);
986 if (TTCN_Runtime::is_single()) {
987 if (send_to_console
) {
988 TTCN_Debugger_UI::print(str
);
992 TTCN_Communication::send_debug_return_value(return_type
, send_to_console
? str
: NULL
);
994 if (output_file
!= NULL
) {
995 fseek(output_file
, 0, SEEK_END
); // in case multiple processes are writing the same file
996 fputs(str
, output_file
);
997 fputc('\n', output_file
);
1003 TTCN3_Debugger::TTCN3_Debugger()
1009 output_file_name
= NULL
;
1010 send_to_console
= true;
1011 function_calls
.cfg
= CALLS_STORE_ALL
;
1012 function_calls
.buffer
.size
= 0;
1013 function_calls
.buffer
.start
= 0;
1014 function_calls
.buffer
.end
= -1;
1015 function_calls
.buffer
.ptr
= NULL
;
1016 last_breakpoint_entry
.module
= NULL
;
1017 last_breakpoint_entry
.line
= 0;
1018 last_breakpoint_entry
.stack_size
= 0;
1020 fail_behavior
.trigger
= false;
1021 fail_behavior
.batch_file
= NULL
;
1022 error_behavior
.trigger
= false;
1023 error_behavior
.batch_file
= NULL
;
1024 global_batch_file
= NULL
;
1025 command_result
= NULL
;
1026 last_variable_list
= NULL
;
1027 stepping_type
= NOT_STEPPING
;
1028 stepping_stack_size
= 0;
1029 temporary_breakpoint
.module
= NULL
;
1030 temporary_breakpoint
.line
= 0;
1031 temporary_breakpoint
.function
= NULL
;
1032 temporary_breakpoint
.batch_file
= NULL
; // not used
1034 halt_at_start
= false;
1035 initial_batch_file
= NULL
;
1038 TTCN3_Debugger::~TTCN3_Debugger()
1040 if (output_file
!= NULL
) {
1041 fclose(output_file
);
1042 Free(output_file_name
);
1044 for (size_t i
= 0; i
< breakpoints
.size(); ++i
) {
1045 Free(breakpoints
[i
].module
);
1046 Free(breakpoints
[i
].function
);
1047 Free(breakpoints
[i
].batch_file
);
1049 for (size_t i
= 0; i
< global_scopes
.size(); ++i
) {
1050 delete global_scopes
[i
].scope
;
1052 for (size_t i
= 0; i
< component_scopes
.size(); ++i
) {
1053 delete component_scopes
[i
].scope
;
1055 for (size_t i
= 0; i
< variables
.size(); ++i
) {
1056 delete variables
[i
];
1058 Free(fail_behavior
.batch_file
);
1059 Free(error_behavior
.batch_file
);
1060 Free(global_batch_file
);
1061 clean_up_function_calls();
1062 Free(last_variable_list
);
1065 TTCN3_Debug_Scope
* TTCN3_Debugger::add_global_scope(const char* p_module
)
1067 named_scope_t global_scope
;
1068 global_scope
.name
= p_module
;
1069 global_scope
.scope
= new TTCN3_Debug_Scope();
1070 global_scopes
.push_back(global_scope
);
1071 return global_scope
.scope
;
1074 TTCN3_Debug_Scope
* TTCN3_Debugger::add_component_scope(const char* p_component
)
1076 named_scope_t component_scope
;
1077 component_scope
.name
= p_component
;
1078 component_scope
.scope
= new TTCN3_Debug_Scope();
1079 component_scopes
.push_back(component_scope
);
1080 return component_scope
.scope
;
1083 void TTCN3_Debugger::set_return_value(const CHARSTRING
& p_value
)
1085 if (active
&& !call_stack
.empty()) {
1086 call_stack
[call_stack
.size() - 1].function
->set_return_value(p_value
);
1090 void TTCN3_Debugger::breakpoint_entry(int p_line
)
1092 if (active
&& !call_stack
.empty()) {
1093 const char* module_name
= call_stack
[call_stack
.size() - 1].function
->get_module_name();
1094 bool trigger
= false;
1095 const char* trigger_type
;
1097 const char* batch_file
= NULL
;
1099 case SBP_FAIL_VERDICT
:
1100 trigger
= fail_behavior
.trigger
;
1101 trigger_type
= "Automatic breakpoint (fail verdict) reached at";
1102 actual_line
= TTCN_Location::get_line_number();
1103 batch_file
= fail_behavior
.batch_file
;
1105 case SBP_ERROR_VERDICT
:
1106 trigger
= error_behavior
.trigger
;
1107 trigger_type
= "Automatic breakpoint (error verdict) reached at";
1108 actual_line
= TTCN_Location::get_line_number();
1109 batch_file
= error_behavior
.batch_file
;
1111 default: // code lines
1112 // first: make sure it's not the same breakpoint entry as last time
1113 if (p_line
== last_breakpoint_entry
.line
&&
1114 module_name
== last_breakpoint_entry
.module
) {
1117 actual_line
= p_line
;
1118 // second: check if a stepping operation ends here
1119 if (stepping_type
== STEP_INTO
||
1120 (stepping_type
== STEP_OVER
&& call_stack
.size() <= stepping_stack_size
) ||
1121 (stepping_type
== STEP_OUT
&& call_stack
.size() < stepping_stack_size
)) {
1123 trigger_type
= "Stepped to";
1126 const char* function_name
=
1127 call_stack
[call_stack
.size() - 1].function
->get_function_name();
1128 // third: check if this is the destination of a 'run to cursor' operation
1129 if (temporary_breakpoint
.module
!= NULL
&&
1130 strcmp(module_name
, temporary_breakpoint
.module
) == 0 &&
1131 (p_line
== temporary_breakpoint
.line
||
1132 (temporary_breakpoint
.function
!= NULL
&&
1133 last_breakpoint_entry
.stack_size
== call_stack
.size() - 1 &&
1134 strcmp(temporary_breakpoint
.function
, function_name
) == 0))) {
1136 trigger_type
= "Ran to";
1139 // fourth: check if the location matches one of the breakpoints in the list
1140 size_t pos
= find_breakpoint(module_name
, p_line
, NULL
);
1141 if (pos
== breakpoints
.size() &&
1142 last_breakpoint_entry
.stack_size
== call_stack
.size() - 1) {
1143 // this is the first breakpoint entry in the function,
1144 // check for a matching function breakpoint
1145 pos
= find_breakpoint(module_name
, 0, function_name
);
1147 if (pos
!= breakpoints
.size()) {
1149 batch_file
= breakpoints
[pos
].batch_file
;
1151 trigger_type
= "User breakpoint reached at";
1155 print(DRET_NOTIFICATION
, "%s line %d in module '%s'.",
1156 trigger_type
, actual_line
, module_name
);
1157 if (!TTCN_Runtime::is_single()) {
1158 TTCN_Communication::send_debug_halt_req();
1160 halt(batch_file
, true);
1162 last_breakpoint_entry
.module
= module_name
;
1163 last_breakpoint_entry
.line
= p_line
;
1164 last_breakpoint_entry
.stack_size
= call_stack
.size();
1168 CHARSTRING
TTCN3_Debugger::print_base_var(const TTCN3_Debugger::variable_t
& p_var
)
1170 const void* ptr
= p_var
.set_function
!= NULL
? p_var
.value
: p_var
.cvalue
;
1171 TTCN_Logger::begin_event_log2str();
1172 if (!strcmp(p_var
.type_name
, "bitstring")) {
1173 ((const BITSTRING
*)ptr
)->log();
1175 else if (!strcmp(p_var
.type_name
, "bitstring template")) {
1176 ((const BITSTRING_template
*)ptr
)->log();
1178 else if (!strcmp(p_var
.type_name
, "boolean")) {
1179 ((const BOOLEAN
*)ptr
)->log();
1181 else if (!strcmp(p_var
.type_name
, "boolean template")) {
1182 ((const BOOLEAN_template
*)ptr
)->log();
1184 else if (!strcmp(p_var
.type_name
, "charstring")) {
1185 ((const CHARSTRING
*)ptr
)->log();
1187 else if (!strcmp(p_var
.type_name
, "charstring template")) {
1188 ((const CHARSTRING_template
*)ptr
)->log();
1190 else if (!strcmp(p_var
.type_name
, "float")) {
1191 ((const FLOAT
*)ptr
)->log();
1193 else if (!strcmp(p_var
.type_name
, "float template")) {
1194 ((const FLOAT_template
*)ptr
)->log();
1196 else if (!strcmp(p_var
.type_name
, "hexstring")) {
1197 ((const HEXSTRING
*)ptr
)->log();
1199 else if (!strcmp(p_var
.type_name
, "hexstring template")) {
1200 ((const HEXSTRING_template
*)ptr
)->log();
1202 else if (!strcmp(p_var
.type_name
, "integer")) {
1203 ((const INTEGER
*)ptr
)->log();
1205 else if (!strcmp(p_var
.type_name
, "integer template")) {
1206 ((const INTEGER_template
*)ptr
)->log();
1208 else if (!strcmp(p_var
.type_name
, "objid")) {
1209 ((const OBJID
*)ptr
)->log();
1211 else if (!strcmp(p_var
.type_name
, "objid template")) {
1212 ((const OBJID_template
*)ptr
)->log();
1214 else if (!strcmp(p_var
.type_name
, "octetstring")) {
1215 ((const OCTETSTRING
*)ptr
)->log();
1217 else if (!strcmp(p_var
.type_name
, "octetstring template")) {
1218 ((const OCTETSTRING_template
*)ptr
)->log();
1220 else if (!strcmp(p_var
.type_name
, "universal charstring")) {
1221 ((const UNIVERSAL_CHARSTRING
*)ptr
)->log();
1223 else if (!strcmp(p_var
.type_name
, "universal charstring template")) {
1224 ((const UNIVERSAL_CHARSTRING_template
*)ptr
)->log();
1226 else if (!strcmp(p_var
.type_name
, "verdicttype")) {
1227 ((const VERDICTTYPE
*)ptr
)->log();
1229 else if (!strcmp(p_var
.type_name
, "verdicttype template")) {
1230 ((const VERDICTTYPE_template
*)ptr
)->log();
1232 else if (!strcmp(p_var
.type_name
, "component")) {
1233 ((const COMPONENT
*)ptr
)->log();
1235 else if (!strcmp(p_var
.type_name
, "component template")) {
1236 ((const COMPONENT_template
*)ptr
)->log();
1238 else if (!strcmp(p_var
.type_name
, "port")) {
1239 ((const PORT
*)ptr
)->log(); // virtual
1241 else if (!strcmp(p_var
.type_name
, "default")) {
1242 ((const DEFAULT
*)ptr
)->log();
1244 else if (!strcmp(p_var
.type_name
, "default template")) {
1245 ((const DEFAULT_template
*)ptr
)->log();
1247 else if (!strcmp(p_var
.type_name
, "timer")) {
1248 ((const TIMER
*)ptr
)->log();
1250 else if (!strcmp(p_var
.type_name
, "NULL")) {
1251 ((const ASN_NULL
*)ptr
)->log();
1253 else if (!strcmp(p_var
.type_name
, "NULL template")) {
1254 ((const ASN_NULL_template
*)ptr
)->log();
1256 else if (!strcmp(p_var
.type_name
, "CHARACTER STRING")) {
1257 ((const CHARACTER_STRING
*)ptr
)->log();
1259 else if (!strcmp(p_var
.type_name
, "CHARACTER STRING template")) {
1260 ((const CHARACTER_STRING_template
*)ptr
)->log();
1262 else if (!strcmp(p_var
.type_name
, "EMBEDDED PDV")) {
1263 ((const EMBEDDED_PDV
*)ptr
)->log();
1265 else if (!strcmp(p_var
.type_name
, "EMBEDDED PDV template")) {
1266 ((const EMBEDDED_PDV_template
*)ptr
)->log();
1268 else if (!strcmp(p_var
.type_name
, "EXTERNAL")) {
1269 ((const EXTERNAL
*)ptr
)->log();
1271 else if (!strcmp(p_var
.type_name
, "EXTERNAL template")) {
1272 ((const EXTERNAL_template
*)ptr
)->log();
1275 TTCN_Logger::log_event_str("<unrecognized value or template>");
1277 return TTCN_Logger::end_event_log2str();
1280 boolean
TTCN3_Debugger::set_base_var(variable_t
& p_var
, Module_Param
& p_new_value
)
1282 if (!strcmp(p_var
.type_name
, "bitstring")) {
1283 ((BITSTRING
*)p_var
.value
)->set_param(p_new_value
);
1285 else if (!strcmp(p_var
.type_name
, "bitstring template")) {
1286 ((BITSTRING_template
*)p_var
.value
)->set_param(p_new_value
);
1288 else if (!strcmp(p_var
.type_name
, "boolean")) {
1289 ((BOOLEAN
*)p_var
.value
)->set_param(p_new_value
);
1291 else if (!strcmp(p_var
.type_name
, "boolean template")) {
1292 ((BOOLEAN_template
*)p_var
.value
)->set_param(p_new_value
);
1294 else if (!strcmp(p_var
.type_name
, "charstring")) {
1295 ((CHARSTRING
*)p_var
.value
)->set_param(p_new_value
);
1297 else if (!strcmp(p_var
.type_name
, "charstring template")) {
1298 ((CHARSTRING_template
*)p_var
.value
)->set_param(p_new_value
);
1300 else if (!strcmp(p_var
.type_name
, "float")) {
1301 ((FLOAT
*)p_var
.value
)->set_param(p_new_value
);
1303 else if (!strcmp(p_var
.type_name
, "float template")) {
1304 ((FLOAT_template
*)p_var
.value
)->set_param(p_new_value
);
1306 else if (!strcmp(p_var
.type_name
, "hexstring")) {
1307 ((HEXSTRING
*)p_var
.value
)->set_param(p_new_value
);
1309 else if (!strcmp(p_var
.type_name
, "hexstring template")) {
1310 ((HEXSTRING_template
*)p_var
.value
)->set_param(p_new_value
);
1312 else if (!strcmp(p_var
.type_name
, "integer")) {
1313 ((INTEGER
*)p_var
.value
)->set_param(p_new_value
);
1315 else if (!strcmp(p_var
.type_name
, "integer template")) {
1316 ((INTEGER_template
*)p_var
.value
)->set_param(p_new_value
);
1318 else if (!strcmp(p_var
.type_name
, "objid")) {
1319 ((OBJID
*)p_var
.value
)->set_param(p_new_value
);
1321 else if (!strcmp(p_var
.type_name
, "objid template")) {
1322 ((OBJID_template
*)p_var
.value
)->set_param(p_new_value
);
1324 else if (!strcmp(p_var
.type_name
, "octetstring")) {
1325 ((OCTETSTRING
*)p_var
.value
)->set_param(p_new_value
);
1327 else if (!strcmp(p_var
.type_name
, "octetstring template")) {
1328 ((OCTETSTRING_template
*)p_var
.value
)->set_param(p_new_value
);
1330 else if (!strcmp(p_var
.type_name
, "universal charstring")) {
1331 ((UNIVERSAL_CHARSTRING
*)p_var
.value
)->set_param(p_new_value
);
1333 else if (!strcmp(p_var
.type_name
, "universal charstring template")) {
1334 ((UNIVERSAL_CHARSTRING_template
*)p_var
.value
)->set_param(p_new_value
);
1336 else if (!strcmp(p_var
.type_name
, "verdicttype")) {
1337 ((VERDICTTYPE
*)p_var
.value
)->set_param(p_new_value
);
1339 else if (!strcmp(p_var
.type_name
, "verdicttype template")) {
1340 ((VERDICTTYPE_template
*)p_var
.value
)->set_param(p_new_value
);
1342 else if (!strcmp(p_var
.type_name
, "component")) {
1343 ((COMPONENT
*)p_var
.value
)->set_param(p_new_value
);
1345 else if (!strcmp(p_var
.type_name
, "component template")) {
1346 ((COMPONENT_template
*)p_var
.value
)->set_param(p_new_value
);
1348 else if (!strcmp(p_var
.type_name
, "default")) {
1349 ((DEFAULT
*)p_var
.value
)->set_param(p_new_value
);
1351 else if (!strcmp(p_var
.type_name
, "default template")) {
1352 ((DEFAULT_template
*)p_var
.value
)->set_param(p_new_value
);
1354 else if (!strcmp(p_var
.type_name
, "NULL")) {
1355 ((ASN_NULL
*)p_var
.value
)->set_param(p_new_value
);
1357 else if (!strcmp(p_var
.type_name
, "NULL template")) {
1358 ((ASN_NULL_template
*)p_var
.value
)->set_param(p_new_value
);
1360 else if (!strcmp(p_var
.type_name
, "CHARACTER STRING")) {
1361 ((CHARACTER_STRING
*)p_var
.value
)->set_param(p_new_value
);
1363 else if (!strcmp(p_var
.type_name
, "CHARACTER STRING template")) {
1364 ((CHARACTER_STRING_template
*)p_var
.value
)->set_param(p_new_value
);
1366 else if (!strcmp(p_var
.type_name
, "EMBEDDED PDV")) {
1367 ((EMBEDDED_PDV
*)p_var
.value
)->set_param(p_new_value
);
1369 else if (!strcmp(p_var
.type_name
, "EMBEDDED PDV template")) {
1370 ((EMBEDDED_PDV_template
*)p_var
.value
)->set_param(p_new_value
);
1372 else if (!strcmp(p_var
.type_name
, "EXTERNAL")) {
1373 ((EXTERNAL
*)p_var
.value
)->set_param(p_new_value
);
1375 else if (!strcmp(p_var
.type_name
, "EXTERNAL template")) {
1376 ((EXTERNAL_template
*)p_var
.value
)->set_param(p_new_value
);
1384 void TTCN3_Debugger::add_to_result(const char* fmt
, ...)
1387 va_start(parameters
, fmt
);
1388 command_result
= mputprintf_va_list(command_result
, fmt
, parameters
);
1392 void TTCN3_Debugger::add_function(TTCN3_Debug_Function
* p_function
)
1394 function_call_t function_call
;
1395 if (call_stack
.empty()) {
1396 test_execution_started();
1397 function_call
.caller_line
= 0;
1400 function_call
.caller_line
= last_breakpoint_entry
.line
;
1402 function_call
.function
= p_function
;
1403 call_stack
.push_back(function_call
);
1406 void TTCN3_Debugger::add_scope(TTCN3_Debug_Scope
* p_scope
)
1408 if (active
&& !call_stack
.empty()) {
1409 call_stack
[call_stack
.size() - 1].function
->add_scope(p_scope
);
1413 void TTCN3_Debugger::remove_function(TTCN3_Debug_Function
* p_function
)
1415 if (!call_stack
.empty() && call_stack
[call_stack
.size() - 1].function
== p_function
) {
1416 bool removing_test_case
= call_stack
[call_stack
.size() - 1].function
->is_test_case();
1417 int caller_line
= call_stack
[call_stack
.size() - 1].caller_line
;
1418 call_stack
.erase_at(call_stack
.size() - 1);
1419 if (call_stack
.empty()) {
1420 test_execution_finished();
1422 if (caller_line
!= 0 && (stepping_type
== STEP_INTO
|| stepping_type
== STEP_OUT
||
1423 (stepping_type
== STEP_OVER
&& call_stack
.size() != stepping_stack_size
))) {
1424 breakpoint_entry(caller_line
);
1426 if (exiting
&& TTCN_Runtime::is_single() && !call_stack
.empty() && removing_test_case
&&
1427 call_stack
[call_stack
.size() - 1].function
->is_control_part()) {
1428 // 'exit all' was requested while executing a test case called by a control
1429 // part, which means the test case caught the original TC_End exception;
1430 // another exception must be thrown to stop the control part, too
1436 void TTCN3_Debugger::remove_scope(TTCN3_Debug_Scope
* p_scope
)
1438 if (!call_stack
.empty()) {
1439 call_stack
[call_stack
.size() - 1].function
->remove_scope(p_scope
);
1443 TTCN3_Debugger::variable_t
* TTCN3_Debugger::add_variable(const void* p_value
,
1446 const char* p_module
,
1447 TTCN3_Debugger::print_function_t p_print_function
)
1449 if (call_stack
.empty()) {
1450 // no call stack yet, so this is a global or component variable
1451 variable_t
* var
= find_variable(p_value
);
1453 var
= new TTCN3_Debugger::variable_t
;
1454 var
->cvalue
= p_value
;
1456 var
->type_name
= p_type
;
1457 var
->module
= p_module
;
1458 var
->print_function
= p_print_function
;
1459 var
->set_function
= NULL
;
1460 variables
.push_back(var
);
1465 // it's a local variable for the top-most function
1466 return call_stack
[call_stack
.size() - 1].function
->add_variable(
1467 p_value
, p_name
, p_type
, p_module
, p_print_function
);
1472 TTCN3_Debugger::variable_t
* TTCN3_Debugger::add_variable(void* p_value
,
1475 const char* p_module
,
1476 TTCN3_Debugger::print_function_t p_print_function
,
1477 TTCN3_Debugger::set_function_t p_set_function
)
1479 if (call_stack
.empty()) {
1480 // no call stack yet, so this is a global or component variable
1481 variable_t
* var
= find_variable(p_value
);
1483 var
= new TTCN3_Debugger::variable_t
;
1484 var
->value
= p_value
;
1486 var
->type_name
= p_type
;
1487 var
->module
= p_module
;
1488 var
->print_function
= p_print_function
;
1489 var
->set_function
= p_set_function
;
1490 variables
.push_back(var
);
1495 // it's a local variable for the top-most function
1496 return call_stack
[call_stack
.size() - 1].function
->add_variable(
1497 p_value
, p_name
, p_type
, p_module
, p_print_function
, p_set_function
);
1502 void TTCN3_Debugger::remove_variable(const variable_t
* p_var
)
1504 if (active
&& !call_stack
.empty()) {
1505 call_stack
[call_stack
.size() - 1].function
->remove_variable(p_var
);
1509 const TTCN3_Debug_Scope
* TTCN3_Debugger::get_global_scope(const char* p_module
) const
1511 for (size_t i
= 0; i
< global_scopes
.size(); ++i
) {
1512 if (strcmp(global_scopes
[i
].name
, p_module
) == 0) {
1513 return global_scopes
[i
].scope
;
1519 const TTCN3_Debug_Scope
* TTCN3_Debugger::get_component_scope(const char* p_component
) const
1521 for (size_t i
= 0; i
< component_scopes
.size(); ++i
) {
1522 if (strcmp(component_scopes
[i
].name
, p_component
) == 0) {
1523 return component_scopes
[i
].scope
;
1529 void TTCN3_Debugger::store_function_call(char* p_snapshot
)
1531 if (function_calls
.cfg
== CALLS_RING_BUFFER
&& function_calls
.buffer
.size
== 0) {
1535 // append timestamp to the beginning of the snapshot
1537 gettimeofday(&tv
, NULL
);
1538 struct tm
*lt
= localtime(&tv
.tv_sec
);
1540 char* temp
= mprintf("%02d:%02d:%02d.%06ld\t%s", lt
->tm_hour
, lt
->tm_min
,
1541 lt
->tm_sec
, tv
.tv_usec
, p_snapshot
);
1545 switch (function_calls
.cfg
) {
1547 fseek(function_calls
.file
.ptr
, 0, SEEK_END
); // in case multiple processes are writing the same file
1548 fputs(p_snapshot
, function_calls
.file
.ptr
);
1550 fputc('\n', function_calls
.file
.ptr
);
1551 fflush(function_calls
.file
.ptr
);
1553 case CALLS_RING_BUFFER
: {
1554 bool first
= function_calls
.buffer
.end
== -1;
1555 function_calls
.buffer
.end
= (function_calls
.buffer
.end
+ 1) %
1556 function_calls
.buffer
.size
;
1557 function_calls
.buffer
.ptr
[function_calls
.buffer
.end
] = p_snapshot
;
1558 if (!first
&& function_calls
.buffer
.start
== function_calls
.buffer
.end
) {
1559 function_calls
.buffer
.start
= (function_calls
.buffer
.start
+ 1) %
1560 function_calls
.buffer
.size
;
1563 case CALLS_STORE_ALL
:
1564 if (function_calls
.buffer
.end
== function_calls
.buffer
.size
- 1) {
1565 function_calls
.buffer
.size
+= BUFFER_INCREASE
;
1566 function_calls
.buffer
.ptr
= (char**)Realloc(function_calls
.buffer
.ptr
,
1567 function_calls
.buffer
.size
* sizeof(char*));
1569 ++function_calls
.buffer
.end
;
1570 function_calls
.buffer
.ptr
[function_calls
.buffer
.end
] = p_snapshot
;
1575 #define CHECK_NOF_ARGUMENTS(exp_num) \
1576 if (exp_num != p_argument_count) { \
1577 print(DRET_NOTIFICATION, "Invalid number of arguments. Expected %d, got %d.", \
1578 (int)exp_num, (int)p_argument_count); \
1582 #define CHECK_NOF_ARGUMENTS_RANGE(min, max) \
1583 if ((int)min > p_argument_count || (int)max < p_argument_count) { \
1584 print(DRET_NOTIFICATION, "Invalid number of arguments. Expected at least %d " \
1585 "and at most %d, got %d.", (int)min, (int)max, p_argument_count); \
1589 #define CHECK_NOF_ARGUMENTS_MIN(min) \
1590 if ((int)min > p_argument_count) { \
1591 print(DRET_NOTIFICATION, "Invalid number of arguments. Expected at least %d, got %d.", \
1592 (int)min, p_argument_count); \
1596 #define CHECK_INT_ARGUMENT(arg_idx) \
1598 if (!is_numeric(p_arguments[arg_idx])) { \
1599 print(DRET_NOTIFICATION, "Argument %d is not an integer.", (int)(arg_idx + 1)); \
1604 #define CHECK_CALL_STACK(print_msg) \
1607 print(DRET_NOTIFICATION, "This command can only be used if the debugger " \
1608 "is switched on."); \
1612 if (call_stack.empty()) { \
1614 print(DRET_NOTIFICATION, "This command can only be used if the debugger's " \
1615 "call stack is not empty."); \
1620 void TTCN3_Debugger::execute_command(int p_command
, int p_argument_count
,
1626 for (int i
= 0; i
< p_argument_count
; ++i
) {
1627 if (p_arguments
[i
] == NULL
) {
1628 print(DRET_NOTIFICATION
, "Argument %d is a null pointer.", i
+ 1);
1632 switch (p_command
) {
1634 CHECK_NOF_ARGUMENTS(1)
1635 switch_state(p_arguments
[0]);
1637 case D_SET_BREAKPOINT
:
1638 CHECK_NOF_ARGUMENTS_RANGE(2, 3)
1639 set_breakpoint(p_arguments
[0], p_arguments
[1],
1640 (p_argument_count
== 3) ? p_arguments
[2] : NULL
);
1642 case D_REMOVE_BREAKPOINT
:
1643 CHECK_NOF_ARGUMENTS_RANGE(1, 2)
1644 remove_breakpoint(p_arguments
[0], (p_argument_count
== 2) ? p_arguments
[1] : NULL
);
1646 case D_SET_AUTOMATIC_BREAKPOINT
:
1647 CHECK_NOF_ARGUMENTS_RANGE(2, 3)
1648 set_automatic_breakpoint(p_arguments
[0], p_arguments
[1],
1649 (p_argument_count
== 3) ? p_arguments
[2] : NULL
);
1652 CHECK_NOF_ARGUMENTS_RANGE(1, 2)
1653 set_output(p_arguments
[0], (p_argument_count
== 2) ? p_arguments
[1] : NULL
);
1655 case D_SET_GLOBAL_BATCH_FILE
:
1656 CHECK_NOF_ARGUMENTS_RANGE(1, 2)
1657 set_global_batch_file(p_arguments
[0], (p_argument_count
== 2) ? p_arguments
[1] : NULL
);
1659 case D_FUNCTION_CALL_CONFIG
:
1660 CHECK_NOF_ARGUMENTS_RANGE(1, 2)
1661 configure_function_calls(p_arguments
[0], (p_argument_count
== 2) ? p_arguments
[1] : NULL
);
1663 case D_PRINT_SETTINGS
:
1664 CHECK_NOF_ARGUMENTS(0)
1667 case D_PRINT_CALL_STACK
:
1668 CHECK_CALL_STACK(true)
1669 CHECK_NOF_ARGUMENTS(0)
1672 case D_SET_STACK_LEVEL
:
1673 CHECK_CALL_STACK(true)
1674 CHECK_NOF_ARGUMENTS(1)
1675 CHECK_INT_ARGUMENT(0)
1676 set_stack_level(str2int(p_arguments
[0]));
1678 case D_LIST_VARIABLES
:
1679 CHECK_CALL_STACK(true)
1680 CHECK_NOF_ARGUMENTS_RANGE(0, 2)
1681 call_stack
[STACK_LEVEL
].function
->list_variables(
1682 (p_argument_count
> 0) ? p_arguments
[0] : NULL
,
1683 (p_argument_count
== 2) ? p_arguments
[1] : NULL
);
1685 case D_PRINT_VARIABLE
:
1686 CHECK_CALL_STACK(true)
1687 CHECK_NOF_ARGUMENTS_MIN(1)
1688 for (int i
= 0; i
< p_argument_count
; ++i
) {
1690 add_to_result("\n");
1692 if (!strcmp(p_arguments
[i
], "$")) {
1693 // '$' refers to the result of the last D_LIST_VARIABLES command
1694 // these variable names are separated by spaces
1695 if (last_variable_list
!= NULL
) {
1696 size_t len
= mstrlen(last_variable_list
);
1698 for (size_t j
= 0; j
< len
; ++j
) {
1699 if (last_variable_list
[j
] == ' ') {
1700 // extract the variable name before this space
1701 char* var_name
= mcopystrn(last_variable_list
+ start
, j
- start
);
1702 print_variable(var_name
);
1704 add_to_result("\n");
1708 // extract the last (or only) variable name
1709 char* var_name
= mcopystrn(last_variable_list
+ start
, len
- start
);
1710 print_variable(var_name
);
1714 add_to_result("No previous " D_LIST_VARIABLES_TEXT
" result.");
1718 print_variable(p_arguments
[i
]);
1722 case D_OVERWRITE_VARIABLE
:
1723 CHECK_CALL_STACK(true)
1724 CHECK_NOF_ARGUMENTS_MIN(2)
1725 overwrite_variable(p_arguments
[0], p_argument_count
- 1, p_arguments
+ 1);
1727 case D_PRINT_FUNCTION_CALLS
:
1728 CHECK_NOF_ARGUMENTS_RANGE(0, 1)
1729 print_function_calls((p_argument_count
> 0) ? p_arguments
[0] : NULL
);
1732 CHECK_CALL_STACK(true)
1733 CHECK_NOF_ARGUMENTS(0)
1737 CHECK_CALL_STACK(true)
1738 CHECK_NOF_ARGUMENTS(0)
1742 CHECK_CALL_STACK(true)
1743 CHECK_NOF_ARGUMENTS(0)
1746 case D_RUN_TO_CURSOR
:
1747 if (!TTCN_Runtime::is_hc() && !TTCN_Runtime::is_single()) {
1748 CHECK_CALL_STACK(TTCN_Runtime::is_mtc())
1750 CHECK_NOF_ARGUMENTS(2)
1751 run_to_cursor(p_arguments
[0], p_arguments
[1]);
1754 if (!TTCN_Runtime::is_hc() && !TTCN_Runtime::is_single()) {
1755 CHECK_CALL_STACK(TTCN_Runtime::is_mtc())
1757 CHECK_NOF_ARGUMENTS(0)
1761 CHECK_NOF_ARGUMENTS(0)
1765 if (!TTCN_Runtime::is_hc() && !TTCN_Runtime::is_single()) {
1766 CHECK_CALL_STACK(TTCN_Runtime::is_mtc())
1768 CHECK_NOF_ARGUMENTS(1)
1769 exit_(p_arguments
[0]);
1772 CHECK_NOF_ARGUMENTS_MIN(11)
1773 if (strlen(p_arguments
[0]) > 0) {
1774 switch_state(p_arguments
[0]);
1776 if (strlen(p_arguments
[1]) > 0) {
1777 set_output(p_arguments
[1], p_arguments
[2]);
1779 if (strlen(p_arguments
[3]) > 0) {
1780 set_automatic_breakpoint("error", p_arguments
[3],
1781 strlen(p_arguments
[4]) > 0 ? p_arguments
[4] : NULL
);
1783 if (strlen(p_arguments
[5]) > 0) {
1784 set_automatic_breakpoint("fail", p_arguments
[5],
1785 strlen(p_arguments
[6]) > 0 ? p_arguments
[6] : NULL
);
1787 if (strlen(p_arguments
[7]) > 0) {
1788 set_global_batch_file(p_arguments
[7],
1789 strlen(p_arguments
[8]) > 0 ? p_arguments
[8] : NULL
);
1791 if (strlen(p_arguments
[9]) > 0) {
1792 configure_function_calls(p_arguments
[9],
1793 strlen(p_arguments
[10]) > 0 ? p_arguments
[10] : NULL
);
1795 for (int i
= 11; i
< p_argument_count
; i
+= 3) {
1796 set_breakpoint(p_arguments
[i
], p_arguments
[i
+ 1],
1797 strlen(p_arguments
[i
+ 2]) > 0 ? p_arguments
[i
+ 2] : NULL
);
1801 print(DRET_NOTIFICATION
, "Invalid command received (ID: %d).", p_command
);
1804 if (command_result
!= NULL
) {
1805 print(DRET_DATA
, command_result
);
1806 if (p_command
== D_LIST_VARIABLES
) {
1807 Free(last_variable_list
);
1808 last_variable_list
= command_result
;
1811 Free(command_result
);
1813 command_result
= NULL
;
1817 void TTCN3_Debugger::init_PTC_settings()
1819 if (output_file
== NULL
&& output_file_name
!= NULL
) {
1820 char* final_file_name
= finalize_file_name(output_file_name
);
1821 output_file
= fopen(final_file_name
, "a");
1822 if (output_file
== NULL
) {
1823 print(DRET_NOTIFICATION
, "Failed to open file '%s' for writing.", final_file_name
);
1825 Free(final_file_name
);
1827 if (function_calls
.cfg
== CALLS_TO_FILE
) {
1828 char* final_file_name
= finalize_file_name(function_calls
.file
.name
);
1829 function_calls
.file
.ptr
= fopen(final_file_name
, "a");
1830 if (function_calls
.file
.ptr
== NULL
) {
1831 print(DRET_NOTIFICATION
, "Failed to open file '%s' for writing.", final_file_name
);
1833 Free(final_file_name
);
1835 else if (function_calls
.cfg
== CALLS_RING_BUFFER
&& function_calls
.buffer
.size
!= 0) {
1836 function_calls
.buffer
.ptr
= (char**)Malloc(function_calls
.buffer
.size
* sizeof(char*));
1840 //////////////////////////////////////////////////////
1841 //////////////// TTCN3_Debug_Scope ///////////////////
1842 //////////////////////////////////////////////////////
1844 TTCN3_Debug_Scope::TTCN3_Debug_Scope()
1846 ttcn3_debugger
.add_scope(this);
1849 TTCN3_Debug_Scope::~TTCN3_Debug_Scope()
1851 for (size_t i
= 0; i
< variables
.size(); ++i
) {
1852 ttcn3_debugger
.remove_variable(variables
[i
]);
1854 ttcn3_debugger
.remove_scope(this);
1857 void TTCN3_Debug_Scope::add_variable(const void* p_value
,
1860 const char* p_module
,
1861 TTCN3_Debugger::print_function_t p_print_function
)
1863 TTCN3_Debugger::variable_t
* var
= ttcn3_debugger
.add_variable(p_value
, p_name
,
1864 p_type
, p_module
, p_print_function
);
1866 variables
.push_back(var
);
1870 void TTCN3_Debug_Scope::add_variable(void* p_value
,
1873 const char* p_module
,
1874 TTCN3_Debugger::print_function_t p_print_function
,
1875 TTCN3_Debugger::set_function_t p_set_function
)
1877 TTCN3_Debugger::variable_t
* var
= ttcn3_debugger
.add_variable(p_value
, p_name
,
1878 p_type
, p_module
, p_print_function
, p_set_function
);
1880 variables
.push_back(var
);
1884 TTCN3_Debugger::variable_t
* TTCN3_Debug_Scope::find_variable(const char* p_name
) const
1886 for (size_t i
= 0; i
< variables
.size(); ++i
) {
1887 TTCN3_Debugger::variable_t
* var
= variables
[i
];
1888 if (strcmp(var
->name
, p_name
) == 0) {
1889 // the string matches the variable's name
1892 else if (var
->module
!= NULL
) {
1893 size_t name_len
= strlen(var
->name
);
1894 size_t mod_len
= strlen(var
->module
);
1895 size_t len
= strlen(p_name
);
1896 if (len
== mod_len
+ name_len
+ 1 && p_name
[mod_len
] == '.' &&
1897 strncmp(p_name
, var
->module
, mod_len
) == 0 &&
1898 strncmp(p_name
+ mod_len
+ 1, var
->name
, name_len
) == 0) {
1899 // the string matches the variable's name prefixed by its module name
1907 void TTCN3_Debug_Scope::list_variables(regex_t
* p_posix_regexp
, bool& p_first
,
1908 const char* p_module
) const
1910 for (size_t i
= 0; i
< variables
.size(); ++i
) {
1911 if (p_posix_regexp
== NULL
||
1912 regexec(p_posix_regexp
, variables
[i
]->name
, 0, NULL
, 0) == 0) {
1913 bool imported
= p_module
!= NULL
&& strcmp(p_module
, variables
[i
]->module
) != 0;
1914 ttcn3_debugger
.add_to_result("%s%s%s%s", p_first
? "" : " ",
1915 imported
? variables
[i
]->module
: "", imported
? "." : "", variables
[i
]->name
);
1921 //////////////////////////////////////////////////////
1922 /////////////// TTCN3_Debug_Function /////////////////
1923 //////////////////////////////////////////////////////
1925 TTCN3_Debug_Function::TTCN3_Debug_Function(const char* p_name
,
1927 const char* p_module
,
1928 const charstring_list
& p_parameter_names
,
1929 const charstring_list
& p_parameter_types
,
1930 const char* p_component_name
)
1931 : function_name(p_name
), function_type(p_type
), module_name(p_module
)
1932 , parameter_names(new charstring_list(p_parameter_names
))
1933 , parameter_types(new charstring_list(p_parameter_types
))
1935 ttcn3_debugger
.add_function(this);
1936 global_scope
= ttcn3_debugger
.get_global_scope(p_module
);
1937 component_scope
= (p_component_name
!= NULL
) ?
1938 ttcn3_debugger
.get_component_scope(p_component_name
) : NULL
;
1939 if (function_name
== NULL
) {
1940 function_name
= p_module
; // for control parts
1944 TTCN3_Debug_Function::~TTCN3_Debug_Function()
1946 if (ttcn3_debugger
.is_on()) {
1947 char* snapshot
= mprintf("[%s]\tfinished\t%s(", function_type
, function_name
);
1948 if (parameter_names
->size_of() > 0) {
1949 for (int i
= 0; i
< parameter_names
->size_of(); ++i
) {
1951 snapshot
= mputstr(snapshot
, ", ");
1953 snapshot
= mputprintf(snapshot
, "[%s] %s := ", (const char*)((*parameter_types
)[i
]),
1954 (const char*)((*parameter_names
)[i
]));
1955 if ((*parameter_types
)[i
] == "out" || (*parameter_types
)[i
] == "inout") {
1956 const TTCN3_Debugger::variable_t
* parameter
= find_variable((*parameter_names
)[i
]);
1957 snapshot
= mputstr(snapshot
, parameter
->print_function(*parameter
));
1960 snapshot
= mputc(snapshot
, '-');
1964 snapshot
= mputc(snapshot
, ')');
1965 if (return_value
.is_bound()) {
1966 snapshot
= mputprintf(snapshot
, " returned %s", (const char*)return_value
);
1968 ttcn3_debugger
.store_function_call(snapshot
);
1970 for (size_t i
= 0; i
< variables
.size(); ++i
) {
1971 delete variables
[i
];
1973 delete parameter_names
;
1974 delete parameter_types
;
1975 ttcn3_debugger
.remove_function(this);
1978 TTCN3_Debugger::variable_t
* TTCN3_Debug_Function::add_variable(const void* p_value
,
1981 const char* p_module
,
1982 TTCN3_Debugger::print_function_t p_print_function
)
1984 if (ttcn3_debugger
.is_on()) {
1985 TTCN3_Debugger::variable_t
* var
= new TTCN3_Debugger::variable_t
;
1986 var
->cvalue
= p_value
;
1988 var
->type_name
= p_type
;
1989 var
->module
= p_module
;
1990 var
->print_function
= p_print_function
;
1991 var
->set_function
= NULL
;
1992 variables
.push_back(var
);
1998 TTCN3_Debugger::variable_t
* TTCN3_Debug_Function::add_variable(void* p_value
,
2001 const char* p_module
,
2002 TTCN3_Debugger::print_function_t p_print_function
,
2003 TTCN3_Debugger::set_function_t p_set_function
)
2005 if (ttcn3_debugger
.is_on()) {
2006 TTCN3_Debugger::variable_t
* var
= new TTCN3_Debugger::variable_t
;
2007 var
->value
= p_value
;
2009 var
->type_name
= p_type
;
2010 var
->module
= p_module
;
2011 var
->print_function
= p_print_function
;
2012 var
->set_function
= p_set_function
;
2013 variables
.push_back(var
);
2019 void TTCN3_Debug_Function::set_return_value(const CHARSTRING
& p_value
)
2021 return_value
= p_value
;
2024 void TTCN3_Debug_Function::initial_snapshot() const
2026 if (ttcn3_debugger
.is_on()) {
2027 char* snapshot
= mprintf("[%s]\tstarted \t%s(", function_type
, function_name
);
2028 if (parameter_names
->size_of() > 0) {
2029 for (int i
= 0; i
< parameter_names
->size_of(); ++i
) {
2031 snapshot
= mputstr(snapshot
, ", ");
2033 snapshot
= mputprintf(snapshot
, "[%s] %s := ", (const char*)((*parameter_types
)[i
]),
2034 (const char*)((*parameter_names
)[i
]));
2035 if ((*parameter_types
)[i
] == "in" || (*parameter_types
)[i
] == "inout") {
2036 const TTCN3_Debugger::variable_t
* parameter
= find_variable((*parameter_names
)[i
]);
2037 snapshot
= mputstr(snapshot
, parameter
->print_function(*parameter
));
2040 snapshot
= mputc(snapshot
, '-');
2044 snapshot
= mputstr(snapshot
, ")");
2045 ttcn3_debugger
.store_function_call(snapshot
);
2049 void TTCN3_Debug_Function::add_scope(TTCN3_Debug_Scope
* p_scope
)
2051 scopes
.push_back(p_scope
);
2054 void TTCN3_Debug_Function::remove_scope(TTCN3_Debug_Scope
* p_scope
)
2056 if (!scopes
.empty() && scopes
[scopes
.size() - 1] == p_scope
) {
2057 scopes
.erase_at(scopes
.size() - 1);
2061 void TTCN3_Debug_Function::remove_variable(const TTCN3_Debugger::variable_t
* p_var
)
2063 for (size_t i
= 0; i
< variables
.size(); ++i
) {
2064 if (variables
[i
] == p_var
) {
2065 variables
.erase_at(i
);
2072 TTCN3_Debugger::variable_t
* TTCN3_Debug_Function::find_variable(const char* p_name
) const
2074 for (size_t i
= 0; i
< variables
.size(); ++i
) {
2075 if (strcmp(variables
[i
]->name
, p_name
) == 0) {
2076 return variables
[i
];
2079 // it's not a local variable, it might still be a global or component variable
2080 if (component_scope
!= NULL
) {
2081 TTCN3_Debugger::variable_t
* res
= component_scope
->find_variable(p_name
);
2086 return (global_scope
!= NULL
) ? global_scope
->find_variable(p_name
) : NULL
;
2089 void TTCN3_Debug_Function::print_function() const
2091 ttcn3_debugger
.add_to_result("[%s]\t%s(", function_type
, function_name
);
2092 if (parameter_names
->size_of() > 0) {
2093 for (int i
= 0; i
< parameter_names
->size_of(); ++i
) {
2095 ttcn3_debugger
.add_to_result(", ");
2097 const TTCN3_Debugger::variable_t
* parameter
= find_variable((*parameter_names
)[i
]);
2098 ttcn3_debugger
.add_to_result("[%s] %s := %s", (const char*)(*parameter_types
)[i
],
2099 (const char*)(*parameter_names
)[i
], (const char*)parameter
->print_function(*parameter
));
2102 ttcn3_debugger
.add_to_result(")");
2105 void TTCN3_Debug_Function::list_variables(const char* p_scope
, const char* p_filter
) const
2108 bool list_local
= false;
2109 bool list_global
= false;
2110 bool list_comp
= false;
2111 if (p_scope
== NULL
|| !strcmp(p_scope
, "all")) {
2116 else if (!strcmp(p_scope
, "local")) {
2119 else if (!strcmp(p_scope
, "global")) {
2122 else if (!strcmp(p_scope
, "comp")) {
2126 ttcn3_debugger
.print(DRET_NOTIFICATION
, "Argument 1 is invalid. "
2127 "Expected 'local', 'global', 'comp' or 'all'.");
2130 regex_t
* posix_regexp
= NULL
;
2131 if (p_filter
!= NULL
) {
2132 char* posix_str
= TTCN_pattern_to_regexp(p_filter
);
2133 if (posix_str
== NULL
) {
2134 ttcn3_debugger
.print(DRET_NOTIFICATION
, "Argument 2 is invalid. "
2135 "Expected a valid TTCN-3 character pattern.");
2138 posix_regexp
= new regex_t
;
2139 int ret_val
= regcomp(posix_regexp
, posix_str
, REG_EXTENDED
| REG_NOSUB
);
2143 regerror(ret_val
, posix_regexp
, msg
, sizeof(msg
));
2144 regfree(posix_regexp
);
2145 delete posix_regexp
;
2146 ttcn3_debugger
.print(DRET_NOTIFICATION
, "Compilation of POSIX regular "
2147 "expression failed.");
2152 for (size_t i
= 0; i
< variables
.size(); ++i
) {
2153 if (posix_regexp
== NULL
||
2154 regexec(posix_regexp
, variables
[i
]->name
, 0, NULL
, 0) == 0) {
2155 ttcn3_debugger
.add_to_result("%s%s", first
? "" : " ", variables
[i
]->name
);
2160 if (list_global
&& global_scope
!= NULL
&& global_scope
->has_variables()) {
2161 global_scope
->list_variables(posix_regexp
, first
, module_name
);
2163 if (list_comp
&& component_scope
!= NULL
&& component_scope
->has_variables()) {
2164 component_scope
->list_variables(posix_regexp
, first
, NULL
);
2167 ttcn3_debugger
.print(DRET_NOTIFICATION
, "No variables found.");
2169 if (posix_regexp
!= NULL
) {
2170 regfree(posix_regexp
);
2171 delete posix_regexp
;
2175 bool TTCN3_Debug_Function::is_control_part() const
2177 return !strcmp(function_type
, "control");
2180 bool TTCN3_Debug_Function::is_test_case() const
2182 return !strcmp(function_type
, "testcase");