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 TTCN3_Debugger ttcn3_debugger
;
29 void TTCN3_Debugger::switch_state(const char* p_state_str
)
31 if (!strcmp(p_state_str
, "on")) {
33 print(DRET_NOTIFICATION
, "The debugger is already switched on.");
37 print(DRET_SETTING_CHANGE
, "Debugger switched on.");
40 else if(!strcmp(p_state_str
, "off")) {
42 print(DRET_NOTIFICATION
, "The debugger is already switched off.");
46 print(DRET_SETTING_CHANGE
, "Debugger switched off.");
50 print(DRET_NOTIFICATION
, "Argument 1 is invalid. Expected 'on' or 'off'.");
54 void TTCN3_Debugger::set_breakpoint(const char* p_module
, int p_line
,
55 const char* batch_file
)
57 size_t pos
= find_breakpoint(p_module
, p_line
);
58 if (pos
== breakpoints
.size()) {
60 bp
.module
= mcopystr(p_module
);
62 bp
.batch_file
= batch_file
!= NULL
? mcopystr(batch_file
) : NULL
;
63 breakpoints
.push_back(bp
);
64 print(DRET_SETTING_CHANGE
, "Breakpoint added in module '%s' at line %d%s%s%s.",
66 batch_file
!= NULL
? " with batch file '" : " with no batch file",
67 batch_file
!= NULL
? batch_file
: "", batch_file
!= NULL
? "'" : "");
70 if (breakpoints
[pos
].batch_file
!= NULL
) {
71 if (batch_file
!= NULL
) {
72 if (!strcmp(batch_file
, breakpoints
[pos
].batch_file
)) {
73 print(DRET_NOTIFICATION
, "Breakpoint already set in module '%s' at "
74 "line %d with batch file '%s'.",
75 p_module
, p_line
, batch_file
);
78 print(DRET_SETTING_CHANGE
, "Batch file was changed from '%s' to '%s' for "
79 "breakpoint in module '%s' at line %d.", breakpoints
[pos
].batch_file
,
80 batch_file
, p_module
, p_line
);
84 print(DRET_SETTING_CHANGE
, "Batch file '%s' removed from breakpoint in "
85 "module '%s' at line %d.", breakpoints
[pos
].batch_file
, p_module
, p_line
);
87 Free(breakpoints
[pos
].batch_file
);
90 if (batch_file
!= NULL
) {
91 print(DRET_SETTING_CHANGE
, "Batch file '%s' added to breakpoint in module "
92 "'%s' at line %d.", batch_file
, p_module
, p_line
);
95 print(DRET_NOTIFICATION
, "Breakpoint already set in module '%s' at line "
96 "%d with no batch file.", p_module
, p_line
);
99 breakpoints
[pos
].batch_file
= batch_file
!= NULL
? mcopystr(batch_file
) : NULL
;
103 void TTCN3_Debugger::remove_breakpoint(const char* p_module
, const char* p_line
)
105 bool all_breakpoints
= !strcmp(p_module
, "all");
106 if (p_line
!= NULL
) {
107 if (!strcmp(p_line
, "all")) {
109 for (size_t i
= breakpoints
.size(); i
> 0; --i
) {
110 if (!strcmp(breakpoints
[i
- 1].module
, p_module
)) {
112 Free(breakpoints
[i
- 1].module
);
113 Free(breakpoints
[i
- 1].batch_file
);
114 breakpoints
.erase_at(i
- 1);
118 print(DRET_SETTING_CHANGE
, "Removed all breakpoints in module '%s'.", p_module
);
121 print(DRET_NOTIFICATION
, "No breakpoints found in module '%s'.", p_module
);
126 if (all_breakpoints
) {
127 print(DRET_NOTIFICATION
, "Unexpected 2nd argument, when the first "
128 "argument is 'all'.");
131 size_t len
= strlen(p_line
);
132 for (size_t i
= 0; i
< len
; ++i
) {
133 if (p_line
[i
] < '0' || p_line
[i
] > '9') {
134 print(DRET_NOTIFICATION
, "Argument 2 is invalid. Expected 'all' or "
135 "integer value (line number).");
139 long line
= strtol(p_line
, NULL
, 10);
140 size_t pos
= find_breakpoint(p_module
, line
);
141 if (pos
!= breakpoints
.size()) {
142 Free(breakpoints
[pos
].module
);
143 Free(breakpoints
[pos
].batch_file
);
144 breakpoints
.erase_at(pos
);
145 print(DRET_SETTING_CHANGE
, "Breakpoint removed in module '%s' from line %d.",
149 print(DRET_NOTIFICATION
, "No breakpoint found in module '%s' at line %d.",
155 else if (!all_breakpoints
) {
156 print(DRET_NOTIFICATION
, "2 arguments expected, when the first argument is "
160 // delete all breakpoints if we got this far
161 if (breakpoints
.empty()) {
162 print(DRET_NOTIFICATION
, "No breakpoints found.");
165 for (size_t i
= 0; i
< breakpoints
.size(); ++i
) {
166 Free(breakpoints
[i
].module
);
167 Free(breakpoints
[i
].batch_file
);
170 print(DRET_SETTING_CHANGE
, "Removed all breakpoints.");
174 void TTCN3_Debugger::set_automatic_breakpoint(const char* p_event_str
,
175 const char* p_state_str
,
176 const char* p_batch_file
)
179 if (!strcmp(p_state_str
, "on")) {
182 else if(!strcmp(p_state_str
, "off")) {
186 print(DRET_NOTIFICATION
, "Argument 2 is invalid. Expected 'on' or 'off'.");
189 const char* sbp_type_str
;
191 char** old_batch_file_ptr
;
192 if (!strcmp(p_event_str
, "fail")) {
193 state_changed
= (fail_behavior
.trigger
!= new_state
);
194 fail_behavior
.trigger
= new_state
;
195 old_batch_file_ptr
= &fail_behavior
.batch_file
;
196 sbp_type_str
= "fail verdict";
198 else if (!strcmp(p_event_str
, "error")) {
199 state_changed
= (error_behavior
.trigger
!= new_state
);
200 error_behavior
.trigger
= new_state
;
201 old_batch_file_ptr
= &error_behavior
.batch_file
;
202 sbp_type_str
= "error verdict";
205 print(DRET_NOTIFICATION
, "Argument 1 is invalid. Expected 'error' or 'fail'.");
209 print(DRET_SETTING_CHANGE
, "Automatic breakpoint at %s switched %s%s%s%s.",
210 sbp_type_str
, new_state
? "on" : "off",
211 new_state
? (p_batch_file
!= NULL
? " with batch file '" : " with no batch file") : "",
212 (p_batch_file
!= NULL
&& new_state
) ? p_batch_file
: "",
213 (p_batch_file
!= NULL
&& new_state
) ? "'" : "");
217 if (*old_batch_file_ptr
!= NULL
) {
218 if (p_batch_file
!= NULL
) {
219 if (!strcmp(p_batch_file
, *old_batch_file_ptr
)) {
220 print(DRET_NOTIFICATION
, "Automatic breakpoint at %s was already "
221 "switched on with batch file '%s'.", sbp_type_str
, p_batch_file
);
224 print(DRET_SETTING_CHANGE
, "Batch file was changed from '%s' to '%s' "
225 "for automatic breakpoint at %s.", *old_batch_file_ptr
, p_batch_file
,
230 print(DRET_SETTING_CHANGE
, "Batch file '%s' removed from automatic "
231 "breakpoint at %s.", *old_batch_file_ptr
, sbp_type_str
);
235 if (p_batch_file
!= NULL
) {
236 print(DRET_SETTING_CHANGE
, "Batch file '%s' added to automatic breakpoint "
237 "at %s.", p_batch_file
, sbp_type_str
);
240 print(DRET_NOTIFICATION
, "Automatic breakpoint at %s was already "
241 "switched on with no batch file.", sbp_type_str
);
246 print(DRET_NOTIFICATION
, "Automatic breakpoint at %s was already switched off.");
249 Free(*old_batch_file_ptr
);
250 *old_batch_file_ptr
= p_batch_file
!= NULL
? mcopystr(p_batch_file
) : NULL
;
253 void TTCN3_Debugger::print_settings()
256 add_to_result("Debugger is switched %s.\n", active
? "on" : "off");
258 char* final_file_name
= finalize_file_name(output_file_name
);
259 char* file_str
= output_file
!= NULL
? mprintf("file '%s'", final_file_name
) : NULL
;
260 Free(final_file_name
);
261 add_to_result("Output is printed to %s%s%s.\n",
262 send_to_console
? "the console" : "",
263 (send_to_console
&& output_file
!= NULL
) ? " and to " : "",
264 output_file
!= NULL
? file_str
: "");
267 add_to_result("Global batch file%s%s.\n", global_batch_file
!= NULL
? ": " : "",
268 global_batch_file
!= NULL
? global_batch_file
: " not set");
270 if (breakpoints
.empty()) {
271 add_to_result("No user breakpoints.\n");
274 add_to_result("User breakpoints:\n");
275 for (size_t i
= 0; i
< breakpoints
.size(); ++i
) {
276 const breakpoint_t
& bp
= breakpoints
[i
];
277 add_to_result("%s %d %s\n", bp
.module
, bp
.line
,
278 bp
.batch_file
!= NULL
? bp
.batch_file
: "");
281 // automatic breakpoints
282 add_to_result("Automatic breakpoints:\nerror %s %s\nfail %s %s",
283 error_behavior
.trigger
? "on" : "off",
284 error_behavior
.batch_file
!= NULL
? error_behavior
.batch_file
: "",
285 fail_behavior
.trigger
? "on" : "off",
286 fail_behavior
.batch_file
!= NULL
? fail_behavior
.batch_file
: "");
289 #define STACK_LEVEL (stack_level >= 0) ? (size_t)stack_level : (call_stack.size() - 1)
291 void TTCN3_Debugger::print_call_stack()
293 for (size_t i
= call_stack
.size(); i
!= 0; --i
) {
294 add_to_result("%d.\t", (int)call_stack
.size() - (int)i
+ 1);
295 call_stack
[i
- 1].function
->print_function();
296 if (i
- 1 == (STACK_LEVEL
)) {
297 // mark the active stack level with an asterisk
306 void TTCN3_Debugger::set_stack_level(int new_level
)
309 print(DRET_NOTIFICATION
, "Stack level can only be set if test execution is halted.");
311 else if (new_level
<= 0 || (size_t)new_level
> call_stack
.size()) {
312 print(DRET_NOTIFICATION
, "Invalid new stack level. Expected 1 - %d.",
313 (int)call_stack
.size());
316 stack_level
= (int)call_stack
.size() - new_level
;
317 call_stack
[stack_level
].function
->print_function();
318 print(DRET_NOTIFICATION
, "Stack level set to:\n%d.\t%s", new_level
, command_result
);
319 Free(command_result
);
320 command_result
= NULL
;
324 void TTCN3_Debugger::print_variable(const char* p_var_name
)
326 const variable_t
* var
= call_stack
[STACK_LEVEL
].function
->find_variable(p_var_name
);
328 add_to_result("[%s] %s%s%s := %s", var
->type_name
,
329 var
->module
!= NULL
? var
->module
: "", var
->module
!= NULL
? "." : "",
330 var
->name
, (const char*)var
->print_function(*var
));
333 add_to_result("Variable '%s' not found.", p_var_name
);
337 void TTCN3_Debugger::overwrite_variable(const char* p_var_name
,
338 int p_value_element_count
,
339 char** p_value_elements
)
341 variable_t
* var
= call_stack
[STACK_LEVEL
].function
->find_variable(p_var_name
);
343 if (var
->set_function
== NULL
) {
344 print(DRET_NOTIFICATION
, "Constant variables cannot be overwritten.");
347 char* new_value_str
= NULL
;
348 for (int i
= 0; i
< p_value_element_count
; ++i
) {
350 new_value_str
= mputc(new_value_str
, ' ');
352 new_value_str
= mputstr(new_value_str
, p_value_elements
[i
]);
354 Module_Param
* parsed_mp
= process_config_debugger_value(new_value_str
);
355 // an error message has already been displayed if parsed_mp is NULL
356 if (parsed_mp
!= NULL
) {
358 Debugger_Value_Parsing debug_value_parsing
;
359 boolean handled
= var
->set_function(*var
, *parsed_mp
);
361 print(DRET_NOTIFICATION
, "Variables of type '%s' cannot be overwritten.",
365 add_to_result("[%s] %s := %s", var
->type_name
, var
->name
,
366 (const char*)var
->print_function(*var
));
369 catch (const TC_Error
&) {
370 // do nothing, an error message has already been displayed in this case
377 print(DRET_NOTIFICATION
, "Variable '%s' not found.", p_var_name
);
381 void TTCN3_Debugger::set_output(const char* p_output_type
, const char* p_file_name
)
385 bool same_file
= false;
386 char* final_file_name
= NULL
;
387 // check the command's parameters before actually changing anything
388 if (!strcmp(p_output_type
, "console")) {
392 else if (!strcmp(p_output_type
, "file")) {
396 else if (!strcmp(p_output_type
, "both")) {
401 print(DRET_NOTIFICATION
, "Argument 1 is invalid. Expected 'console', 'file' or 'both'.");
405 if (p_file_name
== NULL
) {
406 print(DRET_NOTIFICATION
, "Argument 2 (output file name) is missing.");
409 if (output_file_name
!= NULL
&& !strcmp(p_file_name
, output_file_name
)) {
410 // don't reopen it if it's the same file as before
413 else if (!TTCN_Runtime::is_hc()) {
414 // don't open any files on HCs, just store settings for future PTCs
415 final_file_name
= finalize_file_name(p_file_name
);
416 new_fp
= fopen(final_file_name
, TTCN_Runtime::is_mtc() ? "w" : "a");
417 if (new_fp
== NULL
) {
418 print(DRET_NOTIFICATION
, "Failed to open file '%s' for writing.", final_file_name
);
419 Free(final_file_name
);
424 // print the change notification to the old output
425 char* file_str
= file
? mprintf("file '%s'", final_file_name
) : NULL
;
426 Free(final_file_name
);
427 print(DRET_SETTING_CHANGE
, "Debugger set to print its output to %s%s%s.",
428 console
? "the console" : "", (console
&& file
) ? " and to " : "",
429 file
? file_str
: "");
433 if (!same_file
&& !TTCN_Runtime::is_hc()) {
434 if (output_file
!= NULL
) {
437 output_file
= new_fp
;
439 send_to_console
= console
;
440 Free(output_file_name
);
442 output_file_name
= mcopystr(p_file_name
);
446 void TTCN3_Debugger::set_global_batch_file(const char* p_state_str
,
447 const char* p_file_name
)
449 bool delete_old
= false;
450 bool copy_new
= false;
451 if (!strcmp(p_state_str
, "on")) {
452 if (p_file_name
!= NULL
) {
453 if (global_batch_file
!= NULL
) {
454 if (!strcmp(p_file_name
, global_batch_file
)) {
455 print(DRET_NOTIFICATION
, "Global batch file was already switched on "
456 "and set to '%s'.", p_file_name
);
459 print(DRET_SETTING_CHANGE
, "Global batch file changed from '%s' to '%s'.",
460 global_batch_file
, p_file_name
);
466 print(DRET_SETTING_CHANGE
, "Global batch file switched on and set to '%s'.",
472 print(DRET_NOTIFICATION
, "Missing batch file name argument.");
475 else if (!strcmp(p_state_str
, "off")) {
476 if (global_batch_file
!= NULL
) {
477 print(DRET_SETTING_CHANGE
, "Global batch file switched off.");
481 print(DRET_NOTIFICATION
, "Global batch file was already switched off.");
485 print(DRET_NOTIFICATION
, "Argument 1 is invalid. Expected 'on' or 'off'.");
488 Free(global_batch_file
);
489 global_batch_file
= NULL
;
492 global_batch_file
= mcopystr(p_file_name
);
496 void TTCN3_Debugger::step(stepping_t p_stepping_type
)
499 print(DRET_NOTIFICATION
, "Stepping commands can only be used when test "
500 "execution is halted.");
503 stepping_type
= p_stepping_type
;
504 stepping_stack_size
= call_stack
.size();
505 if (!TTCN_Runtime::is_single()) {
506 TTCN_Communication::send_debug_continue_req();
511 void TTCN3_Debugger::run_to_cursor(const char* p_module
, int p_line
)
513 // all processes receive this command, since the specified location might be
514 // reached in any process, even a PTC that hasn't been created yet
516 print(DRET_NOTIFICATION
, "The 'run to' command can only be used when test "
517 "execution is halted.");
520 temporary_breakpoint
.module
= mcopystr(p_module
);
521 temporary_breakpoint
.line
= p_line
;
525 void TTCN3_Debugger::halt(const char* p_batch_file
, bool p_run_global_batch
)
529 Free(temporary_breakpoint
.module
);
530 temporary_breakpoint
.module
= NULL
;
531 temporary_breakpoint
.line
= 0;
532 if (!TTCN_Runtime::is_hc()) {
533 stepping_type
= NOT_STEPPING
;
534 stack_level
= call_stack
.size() - 1;
535 print(DRET_NOTIFICATION
, "Test execution halted.");
536 if (p_batch_file
!= NULL
) {
537 if (TTCN_Runtime::is_single()) {
538 TTCN_Debugger_UI::execute_batch_file(p_batch_file
);
541 TTCN_Communication::send_debug_batch(p_batch_file
);
544 else if (p_run_global_batch
&& global_batch_file
!= NULL
) {
545 if (TTCN_Runtime::is_single()) {
546 TTCN_Debugger_UI::execute_batch_file(global_batch_file
);
549 TTCN_Communication::send_debug_batch(global_batch_file
);
552 if (TTCN_Runtime::is_single()) {
553 if (halted
&& !halt_at_start
) {
557 TTCN_Debugger_UI::read_loop();
561 TTCN_Communication::process_debug_messages();
566 print(DRET_NOTIFICATION
, "Test execution is already halted.");
570 void TTCN3_Debugger::resume()
575 print(DRET_NOTIFICATION
, "Test execution resumed.");
578 print(DRET_NOTIFICATION
, "Test execution is not halted.");
582 void TTCN3_Debugger::exit_(const char* p_what
)
584 if (!strcmp(p_what
, "test")) {
587 else if (!strcmp(p_what
, "all")) {
591 print(DRET_NOTIFICATION
, "Argument 1 is invalid. Expected 'test' or 'all'.");
595 if (!TTCN_Runtime::is_hc()) {
596 print((exiting
&& TTCN_Runtime::is_mtc()) ? DRET_EXIT_ALL
: DRET_NOTIFICATION
,
597 "Exiting %s.", exiting
? "test execution" : "current test");
598 TTCN_Runtime::stop_execution();
602 size_t TTCN3_Debugger::find_breakpoint(const char* p_module
, int p_line
) const
604 for (size_t i
= 0; i
< breakpoints
.size(); ++i
) {
605 if (!strcmp(breakpoints
[i
].module
, p_module
) && breakpoints
[i
].line
== p_line
) {
609 return breakpoints
.size();
612 TTCN3_Debugger::variable_t
* TTCN3_Debugger::find_variable(const void* p_value
) const
614 for (size_t i
= 0; i
< variables
.size(); ++i
) {
615 if (variables
[i
]->value
== p_value
) {
622 char* TTCN3_Debugger::finalize_file_name(const char* p_file_name_skeleton
)
624 if (p_file_name_skeleton
== NULL
) {
627 size_t len
= strlen(p_file_name_skeleton
);
629 char* ret_val
= NULL
;
630 for (size_t i
= 0; i
< len
- 1; ++i
) {
631 if (p_file_name_skeleton
[i
] == '%') {
632 ret_val
= mputstrn(ret_val
, p_file_name_skeleton
+ next_idx
, i
- next_idx
);
633 switch (p_file_name_skeleton
[i
+ 1]) {
634 case 'e': // %e -> executable name
635 ret_val
= mputstr(ret_val
, TTCN_Logger::get_executable_name());
637 case 'h': // %h -> host name
638 ret_val
= mputstr(ret_val
, TTCN_Runtime::get_host_name());
640 case 'p': // %p -> process ID
641 ret_val
= mputprintf(ret_val
, "%ld", (long)getpid());
643 case 'l': { // %l -> login name
645 struct passwd
*p
= getpwuid(getuid());
647 ret_val
= mputstr(ret_val
, p
->pw_name
);
651 case 'r': // %r -> component reference
652 if (TTCN_Runtime::is_single()) {
653 ret_val
= mputstr(ret_val
, "single");
655 else if (TTCN_Runtime::is_mtc()) {
656 ret_val
= mputstr(ret_val
, "mtc");
658 else if (TTCN_Runtime::is_ptc()) {
659 ret_val
= mputprintf(ret_val
, "%d", (component
)self
);
662 case 'n': // %n -> component name
663 if (TTCN_Runtime::is_mtc()) {
664 ret_val
= mputstr(ret_val
, "MTC");
666 else if (TTCN_Runtime::is_ptc()) {
667 ret_val
= mputstr(ret_val
, TTCN_Runtime::get_component_name());
670 case '%': // %% -> single %
671 ret_val
= mputc(ret_val
, '%');
673 default: // unknown sequence -> leave it as it is
674 ret_val
= mputstrn(ret_val
, p_file_name_skeleton
+ i
, 2);
681 if (next_idx
< len
) {
682 ret_val
= mputstr(ret_val
, p_file_name_skeleton
+ next_idx
);
687 void TTCN3_Debugger::test_execution_started()
692 if (TTCN_Runtime::is_single()) {
693 TTCN_Debugger_UI::init();
694 if (initial_batch_file
) {
695 halt(initial_batch_file
, false);
697 else if (halt_at_start
) {
701 halt_at_start
= true;
704 void TTCN3_Debugger::test_execution_finished()
706 stepping_type
= NOT_STEPPING
;
707 Free(temporary_breakpoint
.module
);
708 temporary_breakpoint
.module
= NULL
;
709 temporary_breakpoint
.line
= 0;
710 last_breakpoint_entry
.module
= NULL
;
711 last_breakpoint_entry
.line
= 0;
712 if (TTCN_Runtime::is_single()) {
713 TTCN_Debugger_UI::clean_up();
717 void TTCN3_Debugger::print(int return_type
, const char* fmt
, ...) const
719 if (TTCN_Runtime::is_hc()) {
720 // don't display anything while on the HC process
724 va_start(parameters
, fmt
);
725 char* str
= mprintf_va_list(fmt
, parameters
);
727 if (TTCN_Runtime::is_single()) {
728 if (send_to_console
) {
729 TTCN_Debugger_UI::print(str
);
733 TTCN_Communication::send_debug_return_value(return_type
, send_to_console
? str
: NULL
);
735 if (output_file
!= NULL
) {
736 fseek(output_file
, 0, SEEK_END
); // in case multiple processes are writing the same file
737 fputs(str
, output_file
);
738 fputc('\n', output_file
);
744 TTCN3_Debugger::TTCN3_Debugger()
750 output_file_name
= NULL
;
751 send_to_console
= true;
753 last_breakpoint_entry
.module
= NULL
;
754 last_breakpoint_entry
.line
= 0;
755 last_breakpoint_entry
.batch_file
= NULL
; // not used
757 fail_behavior
.trigger
= false;
758 fail_behavior
.batch_file
= NULL
;
759 error_behavior
.trigger
= false;
760 error_behavior
.batch_file
= NULL
;
761 global_batch_file
= NULL
;
762 command_result
= NULL
;
763 last_variable_list
= NULL
;
764 stepping_type
= NOT_STEPPING
;
765 stepping_stack_size
= 0;
766 temporary_breakpoint
.module
= NULL
;
767 temporary_breakpoint
.line
= 0;
768 temporary_breakpoint
.batch_file
= NULL
; // not used
770 halt_at_start
= false;
771 initial_batch_file
= NULL
;
774 TTCN3_Debugger::~TTCN3_Debugger()
776 if (output_file
!= NULL
) {
778 Free(output_file_name
);
780 for (size_t i
= 0; i
< breakpoints
.size(); ++i
) {
781 Free(breakpoints
[i
].module
);
782 Free(breakpoints
[i
].batch_file
);
784 for (size_t i
= 0; i
< global_scopes
.size(); ++i
) {
785 delete global_scopes
[i
].scope
;
787 for (size_t i
= 0; i
< component_scopes
.size(); ++i
) {
788 delete component_scopes
[i
].scope
;
790 for (size_t i
= 0; i
< variables
.size(); ++i
) {
793 Free(fail_behavior
.batch_file
);
794 Free(error_behavior
.batch_file
);
795 Free(global_batch_file
);
797 Free(last_variable_list
);
800 TTCN3_Debug_Scope
* TTCN3_Debugger::add_global_scope(const char* p_module
)
802 named_scope_t global_scope
;
803 global_scope
.name
= p_module
;
804 global_scope
.scope
= new TTCN3_Debug_Scope();
805 global_scopes
.push_back(global_scope
);
806 return global_scope
.scope
;
809 TTCN3_Debug_Scope
* TTCN3_Debugger::add_component_scope(const char* p_component
)
811 named_scope_t component_scope
;
812 component_scope
.name
= p_component
;
813 component_scope
.scope
= new TTCN3_Debug_Scope();
814 component_scopes
.push_back(component_scope
);
815 return component_scope
.scope
;
818 void TTCN3_Debugger::set_return_value(const CHARSTRING
& p_value
)
820 if (active
&& !call_stack
.empty()) {
821 call_stack
[call_stack
.size() - 1].function
->set_return_value(p_value
);
825 void TTCN3_Debugger::breakpoint_entry(int p_line
)
827 if (active
&& !call_stack
.empty()) {
828 const char* module_name
= call_stack
[call_stack
.size() - 1].function
->get_module_name();
829 bool trigger
= false;
830 const char* trigger_type
;
832 const char* batch_file
= NULL
;
834 case SBP_FAIL_VERDICT
:
835 trigger
= fail_behavior
.trigger
;
836 trigger_type
= "Automatic breakpoint (fail verdict) reached at";
837 actual_line
= TTCN_Location::get_line_number();
838 batch_file
= fail_behavior
.batch_file
;
840 case SBP_ERROR_VERDICT
:
841 trigger
= error_behavior
.trigger
;
842 trigger_type
= "Automatic breakpoint (error verdict) reached at";
843 actual_line
= TTCN_Location::get_line_number();
844 batch_file
= error_behavior
.batch_file
;
846 default: // code lines
847 // first: make sure it's not the same breakpoint entry as last time
848 if (p_line
== last_breakpoint_entry
.line
&&
849 module_name
== last_breakpoint_entry
.module
) {
852 actual_line
= p_line
;
853 // second: check if a stepping operation ends here
854 if (stepping_type
== STEP_INTO
||
855 (stepping_type
== STEP_OVER
&& call_stack
.size() <= stepping_stack_size
) ||
856 (stepping_type
== STEP_OUT
&& call_stack
.size() < stepping_stack_size
)) {
858 trigger_type
= "Stepped to";
861 // third: check if this is the destination of a 'run to cursor' operation
862 if (p_line
== temporary_breakpoint
.line
&&
863 !strcmp(module_name
, temporary_breakpoint
.module
)) {
865 trigger_type
= "Ran to";
868 // fourth: check if the location matches one of the breakpoints in the list
869 size_t pos
= find_breakpoint(module_name
, p_line
);
870 if (pos
!= breakpoints
.size()) {
872 batch_file
= breakpoints
[pos
].batch_file
;
874 trigger_type
= "User breakpoint reached at";
878 print(DRET_NOTIFICATION
, "%s line %d in module '%s'.",
879 trigger_type
, actual_line
, module_name
);
880 if (!TTCN_Runtime::is_single()) {
881 TTCN_Communication::send_debug_halt_req();
883 halt(batch_file
, true);
885 last_breakpoint_entry
.module
= (char*)module_name
;
886 last_breakpoint_entry
.line
= p_line
;
890 CHARSTRING
TTCN3_Debugger::print_base_var(const TTCN3_Debugger::variable_t
& p_var
)
892 const void* ptr
= p_var
.set_function
!= NULL
? p_var
.value
: p_var
.cvalue
;
893 TTCN_Logger::begin_event_log2str();
894 if (!strcmp(p_var
.type_name
, "bitstring")) {
895 ((const BITSTRING
*)ptr
)->log();
897 else if (!strcmp(p_var
.type_name
, "bitstring template")) {
898 ((const BITSTRING_template
*)ptr
)->log();
900 else if (!strcmp(p_var
.type_name
, "boolean")) {
901 ((const BOOLEAN
*)ptr
)->log();
903 else if (!strcmp(p_var
.type_name
, "boolean template")) {
904 ((const BOOLEAN_template
*)ptr
)->log();
906 else if (!strcmp(p_var
.type_name
, "charstring")) {
907 ((const CHARSTRING
*)ptr
)->log();
909 else if (!strcmp(p_var
.type_name
, "charstring template")) {
910 ((const CHARSTRING_template
*)ptr
)->log();
912 else if (!strcmp(p_var
.type_name
, "float")) {
913 ((const FLOAT
*)ptr
)->log();
915 else if (!strcmp(p_var
.type_name
, "float template")) {
916 ((const FLOAT_template
*)ptr
)->log();
918 else if (!strcmp(p_var
.type_name
, "hexstring")) {
919 ((const HEXSTRING
*)ptr
)->log();
921 else if (!strcmp(p_var
.type_name
, "hexstring template")) {
922 ((const HEXSTRING_template
*)ptr
)->log();
924 else if (!strcmp(p_var
.type_name
, "integer")) {
925 ((const INTEGER
*)ptr
)->log();
927 else if (!strcmp(p_var
.type_name
, "integer template")) {
928 ((const INTEGER_template
*)ptr
)->log();
930 else if (!strcmp(p_var
.type_name
, "objid")) {
931 ((const OBJID
*)ptr
)->log();
933 else if (!strcmp(p_var
.type_name
, "objid template")) {
934 ((const OBJID_template
*)ptr
)->log();
936 else if (!strcmp(p_var
.type_name
, "octetstring")) {
937 ((const OCTETSTRING
*)ptr
)->log();
939 else if (!strcmp(p_var
.type_name
, "octetstring template")) {
940 ((const OCTETSTRING_template
*)ptr
)->log();
942 else if (!strcmp(p_var
.type_name
, "universal charstring")) {
943 ((const UNIVERSAL_CHARSTRING
*)ptr
)->log();
945 else if (!strcmp(p_var
.type_name
, "universal charstring template")) {
946 ((const UNIVERSAL_CHARSTRING_template
*)ptr
)->log();
948 else if (!strcmp(p_var
.type_name
, "verdicttype")) {
949 ((const VERDICTTYPE
*)ptr
)->log();
951 else if (!strcmp(p_var
.type_name
, "verdicttype template")) {
952 ((const VERDICTTYPE_template
*)ptr
)->log();
954 else if (!strcmp(p_var
.type_name
, "component")) {
955 ((const COMPONENT
*)ptr
)->log();
957 else if (!strcmp(p_var
.type_name
, "component template")) {
958 ((const COMPONENT_template
*)ptr
)->log();
960 else if (!strcmp(p_var
.type_name
, "default")) {
961 ((const DEFAULT
*)ptr
)->log();
963 else if (!strcmp(p_var
.type_name
, "default template")) {
964 ((const DEFAULT_template
*)ptr
)->log();
966 else if (!strcmp(p_var
.type_name
, "timer")) {
967 ((const TIMER
*)ptr
)->log();
969 else if (!strcmp(p_var
.type_name
, "NULL")) {
970 ((const ASN_NULL
*)ptr
)->log();
972 else if (!strcmp(p_var
.type_name
, "NULL template")) {
973 ((const ASN_NULL_template
*)ptr
)->log();
975 else if (!strcmp(p_var
.type_name
, "CHARACTER STRING")) {
976 ((const CHARACTER_STRING
*)ptr
)->log();
978 else if (!strcmp(p_var
.type_name
, "CHARACTER STRING template")) {
979 ((const CHARACTER_STRING_template
*)ptr
)->log();
981 else if (!strcmp(p_var
.type_name
, "EMBEDDED PDV")) {
982 ((const EMBEDDED_PDV
*)ptr
)->log();
984 else if (!strcmp(p_var
.type_name
, "EMBEDDED PDV template")) {
985 ((const EMBEDDED_PDV_template
*)ptr
)->log();
987 else if (!strcmp(p_var
.type_name
, "EXTERNAL")) {
988 ((const EXTERNAL
*)ptr
)->log();
990 else if (!strcmp(p_var
.type_name
, "EXTERNAL template")) {
991 ((const EXTERNAL_template
*)ptr
)->log();
994 TTCN_Logger::log_event_str("<unrecognized value or template>");
996 return TTCN_Logger::end_event_log2str();
999 boolean
TTCN3_Debugger::set_base_var(variable_t
& p_var
, Module_Param
& p_new_value
)
1001 if (!strcmp(p_var
.type_name
, "bitstring")) {
1002 ((BITSTRING
*)p_var
.value
)->set_param(p_new_value
);
1004 else if (!strcmp(p_var
.type_name
, "bitstring template")) {
1005 ((BITSTRING_template
*)p_var
.value
)->set_param(p_new_value
);
1007 else if (!strcmp(p_var
.type_name
, "boolean")) {
1008 ((BOOLEAN
*)p_var
.value
)->set_param(p_new_value
);
1010 else if (!strcmp(p_var
.type_name
, "boolean template")) {
1011 ((BOOLEAN_template
*)p_var
.value
)->set_param(p_new_value
);
1013 else if (!strcmp(p_var
.type_name
, "charstring")) {
1014 ((CHARSTRING
*)p_var
.value
)->set_param(p_new_value
);
1016 else if (!strcmp(p_var
.type_name
, "charstring template")) {
1017 ((CHARSTRING_template
*)p_var
.value
)->set_param(p_new_value
);
1019 else if (!strcmp(p_var
.type_name
, "float")) {
1020 ((FLOAT
*)p_var
.value
)->set_param(p_new_value
);
1022 else if (!strcmp(p_var
.type_name
, "float template")) {
1023 ((FLOAT_template
*)p_var
.value
)->set_param(p_new_value
);
1025 else if (!strcmp(p_var
.type_name
, "hexstring")) {
1026 ((HEXSTRING
*)p_var
.value
)->set_param(p_new_value
);
1028 else if (!strcmp(p_var
.type_name
, "hexstring template")) {
1029 ((HEXSTRING_template
*)p_var
.value
)->set_param(p_new_value
);
1031 else if (!strcmp(p_var
.type_name
, "integer")) {
1032 ((INTEGER
*)p_var
.value
)->set_param(p_new_value
);
1034 else if (!strcmp(p_var
.type_name
, "integer template")) {
1035 ((INTEGER_template
*)p_var
.value
)->set_param(p_new_value
);
1037 else if (!strcmp(p_var
.type_name
, "objid")) {
1038 ((OBJID
*)p_var
.value
)->set_param(p_new_value
);
1040 else if (!strcmp(p_var
.type_name
, "objid template")) {
1041 ((OBJID_template
*)p_var
.value
)->set_param(p_new_value
);
1043 else if (!strcmp(p_var
.type_name
, "octetstring")) {
1044 ((OCTETSTRING
*)p_var
.value
)->set_param(p_new_value
);
1046 else if (!strcmp(p_var
.type_name
, "octetstring template")) {
1047 ((OCTETSTRING_template
*)p_var
.value
)->set_param(p_new_value
);
1049 else if (!strcmp(p_var
.type_name
, "universal charstring")) {
1050 ((UNIVERSAL_CHARSTRING
*)p_var
.value
)->set_param(p_new_value
);
1052 else if (!strcmp(p_var
.type_name
, "universal charstring template")) {
1053 ((UNIVERSAL_CHARSTRING_template
*)p_var
.value
)->set_param(p_new_value
);
1055 else if (!strcmp(p_var
.type_name
, "verdicttype")) {
1056 ((VERDICTTYPE
*)p_var
.value
)->set_param(p_new_value
);
1058 else if (!strcmp(p_var
.type_name
, "verdicttype template")) {
1059 ((VERDICTTYPE_template
*)p_var
.value
)->set_param(p_new_value
);
1061 else if (!strcmp(p_var
.type_name
, "component")) {
1062 ((COMPONENT
*)p_var
.value
)->set_param(p_new_value
);
1064 else if (!strcmp(p_var
.type_name
, "component template")) {
1065 ((COMPONENT_template
*)p_var
.value
)->set_param(p_new_value
);
1067 else if (!strcmp(p_var
.type_name
, "default")) {
1068 ((DEFAULT
*)p_var
.value
)->set_param(p_new_value
);
1070 else if (!strcmp(p_var
.type_name
, "default template")) {
1071 ((DEFAULT_template
*)p_var
.value
)->set_param(p_new_value
);
1073 else if (!strcmp(p_var
.type_name
, "NULL")) {
1074 ((ASN_NULL
*)p_var
.value
)->set_param(p_new_value
);
1076 else if (!strcmp(p_var
.type_name
, "NULL template")) {
1077 ((ASN_NULL_template
*)p_var
.value
)->set_param(p_new_value
);
1079 else if (!strcmp(p_var
.type_name
, "CHARACTER STRING")) {
1080 ((CHARACTER_STRING
*)p_var
.value
)->set_param(p_new_value
);
1082 else if (!strcmp(p_var
.type_name
, "CHARACTER STRING template")) {
1083 ((CHARACTER_STRING_template
*)p_var
.value
)->set_param(p_new_value
);
1085 else if (!strcmp(p_var
.type_name
, "EMBEDDED PDV")) {
1086 ((EMBEDDED_PDV
*)p_var
.value
)->set_param(p_new_value
);
1088 else if (!strcmp(p_var
.type_name
, "EMBEDDED PDV template")) {
1089 ((EMBEDDED_PDV_template
*)p_var
.value
)->set_param(p_new_value
);
1091 else if (!strcmp(p_var
.type_name
, "EXTERNAL")) {
1092 ((EXTERNAL
*)p_var
.value
)->set_param(p_new_value
);
1094 else if (!strcmp(p_var
.type_name
, "EXTERNAL template")) {
1095 ((EXTERNAL_template
*)p_var
.value
)->set_param(p_new_value
);
1103 void TTCN3_Debugger::add_to_result(const char* fmt
, ...)
1106 va_start(parameters
, fmt
);
1107 command_result
= mputprintf_va_list(command_result
, fmt
, parameters
);
1111 void TTCN3_Debugger::add_function(TTCN3_Debug_Function
* p_function
)
1113 function_call_t function_call
;
1114 if (call_stack
.empty()) {
1115 test_execution_started();
1116 function_call
.caller_line
= 0;
1119 function_call
.caller_line
= last_breakpoint_entry
.line
;
1121 function_call
.function
= p_function
;
1122 call_stack
.push_back(function_call
);
1125 void TTCN3_Debugger::add_scope(TTCN3_Debug_Scope
* p_scope
)
1127 if (active
&& !call_stack
.empty()) {
1128 call_stack
[call_stack
.size() - 1].function
->add_scope(p_scope
);
1132 void TTCN3_Debugger::remove_function(TTCN3_Debug_Function
* p_function
)
1134 if (!call_stack
.empty() && call_stack
[call_stack
.size() - 1].function
== p_function
) {
1135 bool removing_test_case
= call_stack
[call_stack
.size() - 1].function
->is_test_case();
1136 int caller_line
= call_stack
[call_stack
.size() - 1].caller_line
;
1137 call_stack
.erase_at(call_stack
.size() - 1);
1138 if (call_stack
.empty()) {
1139 test_execution_finished();
1141 if (caller_line
!= 0 && (stepping_type
== STEP_INTO
|| stepping_type
== STEP_OUT
||
1142 (stepping_type
== STEP_OVER
&& call_stack
.size() != stepping_stack_size
))) {
1143 breakpoint_entry(caller_line
);
1145 if (exiting
&& TTCN_Runtime::is_single() && !call_stack
.empty() && removing_test_case
&&
1146 call_stack
[call_stack
.size() - 1].function
->is_control_part()) {
1147 // 'exit all' was requested while executing a test case called by a control
1148 // part, which means the test case caught the original TC_End exception;
1149 // another exception must be thrown to stop the control part, too
1155 void TTCN3_Debugger::remove_scope(TTCN3_Debug_Scope
* p_scope
)
1157 if (!call_stack
.empty()) {
1158 call_stack
[call_stack
.size() - 1].function
->remove_scope(p_scope
);
1162 TTCN3_Debugger::variable_t
* TTCN3_Debugger::add_variable(const void* p_value
,
1165 const char* p_module
,
1166 TTCN3_Debugger::print_function_t p_print_function
)
1168 if (call_stack
.empty()) {
1169 // no call stack yet, so this is a global or component variable
1170 variable_t
* var
= find_variable(p_value
);
1172 var
= new TTCN3_Debugger::variable_t
;
1173 var
->cvalue
= p_value
;
1175 var
->type_name
= p_type
;
1176 var
->module
= p_module
;
1177 var
->print_function
= p_print_function
;
1178 var
->set_function
= NULL
;
1179 variables
.push_back(var
);
1184 // it's a local variable for the top-most function
1185 return call_stack
[call_stack
.size() - 1].function
->add_variable(
1186 p_value
, p_name
, p_type
, p_module
, p_print_function
);
1191 TTCN3_Debugger::variable_t
* TTCN3_Debugger::add_variable(void* p_value
,
1194 const char* p_module
,
1195 TTCN3_Debugger::print_function_t p_print_function
,
1196 TTCN3_Debugger::set_function_t p_set_function
)
1198 if (call_stack
.empty()) {
1199 // no call stack yet, so this is a global or component variable
1200 variable_t
* var
= find_variable(p_value
);
1202 var
= new TTCN3_Debugger::variable_t
;
1203 var
->value
= p_value
;
1205 var
->type_name
= p_type
;
1206 var
->module
= p_module
;
1207 var
->print_function
= p_print_function
;
1208 var
->set_function
= p_set_function
;
1209 variables
.push_back(var
);
1214 // it's a local variable for the top-most function
1215 return call_stack
[call_stack
.size() - 1].function
->add_variable(
1216 p_value
, p_name
, p_type
, p_module
, p_print_function
, p_set_function
);
1221 void TTCN3_Debugger::remove_variable(const variable_t
* p_var
)
1223 if (active
&& !call_stack
.empty()) {
1224 call_stack
[call_stack
.size() - 1].function
->remove_variable(p_var
);
1228 const TTCN3_Debug_Scope
* TTCN3_Debugger::get_global_scope(const char* p_module
) const
1230 for (size_t i
= 0; i
< global_scopes
.size(); ++i
) {
1231 if (strcmp(global_scopes
[i
].name
, p_module
) == 0) {
1232 return global_scopes
[i
].scope
;
1238 const TTCN3_Debug_Scope
* TTCN3_Debugger::get_component_scope(const char* p_component
) const
1240 for (size_t i
= 0; i
< component_scopes
.size(); ++i
) {
1241 if (strcmp(component_scopes
[i
].name
, p_component
) == 0) {
1242 return component_scopes
[i
].scope
;
1248 void TTCN3_Debugger::add_snapshot(const char* p_snapshot
)
1250 if (snapshots
!= NULL
) {
1251 snapshots
= mputc(snapshots
, '\n');
1253 snapshots
= mputstr(snapshots
, p_snapshot
);
1256 #define CHECK_NOF_ARGUMENTS(exp_num) \
1257 if (exp_num != p_argument_count) { \
1258 print(DRET_NOTIFICATION, "Invalid number of arguments. Expected %d, got %d.", \
1259 (int)exp_num, (int)p_argument_count); \
1263 #define CHECK_NOF_ARGUMENTS_RANGE(min, max) \
1264 if ((int)min > p_argument_count || (int)max < p_argument_count) { \
1265 print(DRET_NOTIFICATION, "Invalid number of arguments. Expected at least %d " \
1266 "and at most %d, got %d.", (int)min, (int)max, p_argument_count); \
1270 #define CHECK_NOF_ARGUMENTS_MIN(min) \
1271 if ((int)min > p_argument_count) { \
1272 print(DRET_NOTIFICATION, "Invalid number of arguments. Expected at least %d, got %d.", \
1273 (int)min, p_argument_count); \
1277 #define CHECK_INT_ARGUMENT(arg_idx) \
1279 size_t len = strlen(p_arguments[arg_idx]); \
1280 for (size_t i = 0; i < len; ++i) { \
1281 if (p_arguments[arg_idx][i] < '0' || p_arguments[arg_idx][i] > '9') { \
1282 print(DRET_NOTIFICATION, "Argument %d is not an integer.", (int)(arg_idx + 1)); \
1288 #define CHECK_CALL_STACK(print_msg) \
1291 print(DRET_NOTIFICATION, "This command can only be used if the debugger " \
1292 "is switched on."); \
1296 if (call_stack.empty()) { \
1298 print(DRET_NOTIFICATION, "This command can only be used if the debugger's " \
1299 "call stack is not empty."); \
1304 void TTCN3_Debugger::execute_command(int p_command
, int p_argument_count
,
1310 for (int i
= 0; i
< p_argument_count
; ++i
) {
1311 if (p_arguments
[i
] == NULL
) {
1312 print(DRET_NOTIFICATION
, "Argument %d is a null pointer.", i
+ 1);
1316 switch (p_command
) {
1318 CHECK_NOF_ARGUMENTS(1)
1319 switch_state(p_arguments
[0]);
1321 case D_SET_BREAKPOINT
:
1322 CHECK_NOF_ARGUMENTS_RANGE(2, 3)
1323 CHECK_INT_ARGUMENT(1)
1324 set_breakpoint(p_arguments
[0], str2int(p_arguments
[1]),
1325 (p_argument_count
== 3) ? p_arguments
[2] : NULL
);
1327 case D_REMOVE_BREAKPOINT
:
1328 CHECK_NOF_ARGUMENTS_RANGE(1, 2)
1329 remove_breakpoint(p_arguments
[0], (p_argument_count
== 2) ? p_arguments
[1] : NULL
);
1331 case D_SET_AUTOMATIC_BREAKPOINT
:
1332 CHECK_NOF_ARGUMENTS_RANGE(2, 3)
1333 set_automatic_breakpoint(p_arguments
[0], p_arguments
[1],
1334 (p_argument_count
== 3) ? p_arguments
[2] : NULL
);
1337 CHECK_NOF_ARGUMENTS_RANGE(1, 2)
1338 set_output(p_arguments
[0], (p_argument_count
== 2) ? p_arguments
[1] : NULL
);
1340 case D_SET_GLOBAL_BATCH_FILE
:
1341 CHECK_NOF_ARGUMENTS_RANGE(1, 2)
1342 set_global_batch_file(p_arguments
[0], (p_argument_count
== 2) ? p_arguments
[1] : NULL
);
1344 case D_PRINT_SETTINGS
:
1345 CHECK_NOF_ARGUMENTS(0)
1348 case D_PRINT_CALL_STACK
:
1349 CHECK_CALL_STACK(true)
1350 CHECK_NOF_ARGUMENTS(0)
1353 case D_SET_STACK_LEVEL
:
1354 CHECK_CALL_STACK(true)
1355 CHECK_NOF_ARGUMENTS(1)
1356 CHECK_INT_ARGUMENT(0)
1357 set_stack_level(str2int(p_arguments
[0]));
1359 case D_LIST_VARIABLES
:
1360 CHECK_CALL_STACK(true)
1361 CHECK_NOF_ARGUMENTS_RANGE(1, 2)
1362 call_stack
[STACK_LEVEL
].function
->list_variables(p_arguments
[0],
1363 (p_argument_count
== 2) ? p_arguments
[1] : NULL
);
1365 case D_PRINT_VARIABLE
:
1366 CHECK_CALL_STACK(true)
1367 CHECK_NOF_ARGUMENTS_MIN(1)
1368 for (int i
= 0; i
< p_argument_count
; ++i
) {
1370 add_to_result("\n");
1372 if (!strcmp(p_arguments
[i
], "$")) {
1373 // '$' refers to the result of the last D_LIST_VARIABLES command
1374 // these variable names are separated by spaces
1375 if (last_variable_list
!= NULL
) {
1376 size_t len
= mstrlen(last_variable_list
);
1378 for (size_t j
= 0; j
< len
; ++j
) {
1379 if (last_variable_list
[j
] == ' ') {
1380 // extract the variable name before this space
1381 char* var_name
= mcopystrn(last_variable_list
+ start
, j
- start
);
1382 print_variable(var_name
);
1384 add_to_result("\n");
1388 // extract the last (or only) variable name
1389 char* var_name
= mcopystrn(last_variable_list
+ start
, len
- start
);
1390 print_variable(var_name
);
1394 add_to_result("No previous " D_LIST_VARIABLES_TEXT
" result.");
1398 print_variable(p_arguments
[i
]);
1402 case D_OVERWRITE_VARIABLE
:
1403 CHECK_CALL_STACK(true)
1404 CHECK_NOF_ARGUMENTS_MIN(2)
1405 overwrite_variable(p_arguments
[0], p_argument_count
- 1, p_arguments
+ 1);
1407 case D_PRINT_SNAPSHOTS
:
1408 CHECK_NOF_ARGUMENTS(0)
1409 add_to_result("%s", snapshots
);
1412 CHECK_CALL_STACK(true)
1413 CHECK_NOF_ARGUMENTS(0)
1417 CHECK_CALL_STACK(true)
1418 CHECK_NOF_ARGUMENTS(0)
1422 CHECK_CALL_STACK(true)
1423 CHECK_NOF_ARGUMENTS(0)
1426 case D_RUN_TO_CURSOR
:
1427 if (!TTCN_Runtime::is_hc() && !TTCN_Runtime::is_single()) {
1428 CHECK_CALL_STACK(TTCN_Runtime::is_mtc())
1430 CHECK_NOF_ARGUMENTS(2)
1431 CHECK_INT_ARGUMENT(1)
1432 run_to_cursor(p_arguments
[0], str2int(p_arguments
[1]));
1435 if (!TTCN_Runtime::is_hc() && !TTCN_Runtime::is_single()) {
1436 CHECK_CALL_STACK(TTCN_Runtime::is_mtc())
1438 CHECK_NOF_ARGUMENTS(0)
1442 CHECK_NOF_ARGUMENTS(0)
1446 if (!TTCN_Runtime::is_hc() && !TTCN_Runtime::is_single()) {
1447 CHECK_CALL_STACK(TTCN_Runtime::is_mtc())
1449 CHECK_NOF_ARGUMENTS(1)
1450 exit_(p_arguments
[0]);
1453 CHECK_NOF_ARGUMENTS_MIN(5)
1454 if (strlen(p_arguments
[0]) > 0) {
1455 switch_state(p_arguments
[0]);
1457 if (strlen(p_arguments
[1]) > 0) {
1458 set_output(p_arguments
[1], p_arguments
[2]);
1460 if (strlen(p_arguments
[3]) > 0) {
1461 set_automatic_breakpoint("error", p_arguments
[3],
1462 strlen(p_arguments
[4]) > 0 ? p_arguments
[4] : NULL
);
1464 if (strlen(p_arguments
[5]) > 0) {
1465 set_automatic_breakpoint("fail", p_arguments
[5],
1466 strlen(p_arguments
[6]) > 0 ? p_arguments
[6] : NULL
);
1468 if (strlen(p_arguments
[7]) > 0) {
1469 set_global_batch_file(p_arguments
[7],
1470 strlen(p_arguments
[8]) > 0 ? p_arguments
[8] : NULL
);
1472 for (int i
= 9; i
< p_argument_count
; i
+= 3) {
1473 set_breakpoint(p_arguments
[i
], str2int(p_arguments
[i
+ 1]),
1474 strlen(p_arguments
[i
+ 2]) > 0 ? p_arguments
[i
+ 2] : NULL
);
1478 print(DRET_NOTIFICATION
, "Invalid command received (ID: %d).", p_command
);
1481 if (command_result
!= NULL
) {
1482 print(DRET_DATA
, command_result
);
1483 if (p_command
== D_LIST_VARIABLES
) {
1484 Free(last_variable_list
);
1485 last_variable_list
= command_result
;
1488 Free(command_result
);
1490 command_result
= NULL
;
1494 void TTCN3_Debugger::open_output_file()
1496 if (output_file
== NULL
&& output_file_name
!= NULL
) {
1497 char* final_file_name
= finalize_file_name(output_file_name
);
1498 output_file
= fopen(final_file_name
, TTCN_Runtime::is_mtc() ? "w" : "a");
1499 if (output_file
== NULL
) {
1500 print(DRET_NOTIFICATION
, "Failed to open file '%s' for writing.", final_file_name
);
1502 Free(final_file_name
);
1506 //////////////////////////////////////////////////////
1507 //////////////// TTCN3_Debug_Scope ///////////////////
1508 //////////////////////////////////////////////////////
1510 TTCN3_Debug_Scope::TTCN3_Debug_Scope()
1512 ttcn3_debugger
.add_scope(this);
1515 TTCN3_Debug_Scope::~TTCN3_Debug_Scope()
1517 for (size_t i
= 0; i
< variables
.size(); ++i
) {
1518 ttcn3_debugger
.remove_variable(variables
[i
]);
1520 ttcn3_debugger
.remove_scope(this);
1523 void TTCN3_Debug_Scope::add_variable(const void* p_value
,
1526 const char* p_module
,
1527 TTCN3_Debugger::print_function_t p_print_function
)
1529 TTCN3_Debugger::variable_t
* var
= ttcn3_debugger
.add_variable(p_value
, p_name
,
1530 p_type
, p_module
, p_print_function
);
1532 variables
.push_back(var
);
1536 void TTCN3_Debug_Scope::add_variable(void* p_value
,
1539 const char* p_module
,
1540 TTCN3_Debugger::print_function_t p_print_function
,
1541 TTCN3_Debugger::set_function_t p_set_function
)
1543 TTCN3_Debugger::variable_t
* var
= ttcn3_debugger
.add_variable(p_value
, p_name
,
1544 p_type
, p_module
, p_print_function
, p_set_function
);
1546 variables
.push_back(var
);
1550 TTCN3_Debugger::variable_t
* TTCN3_Debug_Scope::find_variable(const char* p_name
) const
1552 for (size_t i
= 0; i
< variables
.size(); ++i
) {
1553 TTCN3_Debugger::variable_t
* var
= variables
[i
];
1554 if (strcmp(var
->name
, p_name
) == 0) {
1555 // the string matches the variable's name
1558 else if (var
->module
!= NULL
) {
1559 size_t name_len
= strlen(var
->name
);
1560 size_t mod_len
= strlen(var
->module
);
1561 size_t len
= strlen(p_name
);
1562 if (len
== mod_len
+ name_len
+ 1 && p_name
[mod_len
] == '.' &&
1563 strncmp(p_name
, var
->module
, mod_len
) == 0 &&
1564 strncmp(p_name
+ mod_len
+ 1, var
->name
, name_len
) == 0) {
1565 // the string matches the variable's name prefixed by its module name
1573 void TTCN3_Debug_Scope::list_variables(regex_t
* p_posix_regexp
, bool& p_first
,
1574 const char* p_module
) const
1576 for (size_t i
= 0; i
< variables
.size(); ++i
) {
1577 if (p_posix_regexp
== NULL
||
1578 regexec(p_posix_regexp
, variables
[i
]->name
, 0, NULL
, 0) == 0) {
1579 bool imported
= p_module
!= NULL
&& strcmp(p_module
, variables
[i
]->module
) != 0;
1580 ttcn3_debugger
.add_to_result("%s%s%s%s", p_first
? "" : " ",
1581 imported
? variables
[i
]->module
: "", imported
? "." : "", variables
[i
]->name
);
1587 //////////////////////////////////////////////////////
1588 /////////////// TTCN3_Debug_Function /////////////////
1589 //////////////////////////////////////////////////////
1591 TTCN3_Debug_Function::TTCN3_Debug_Function(const char* p_name
,
1593 const char* p_module
,
1594 const charstring_list
& p_parameter_names
,
1595 const charstring_list
& p_parameter_types
,
1596 const char* p_component_name
)
1597 : function_name(p_name
), function_type(p_type
), module_name(p_module
)
1598 , parameter_names(new charstring_list(p_parameter_names
))
1599 , parameter_types(new charstring_list(p_parameter_types
))
1601 ttcn3_debugger
.add_function(this);
1602 global_scope
= ttcn3_debugger
.get_global_scope(p_module
);
1603 component_scope
= (p_component_name
!= NULL
) ?
1604 ttcn3_debugger
.get_component_scope(p_component_name
) : NULL
;
1605 if (function_name
== NULL
) {
1606 function_name
= p_module
; // for control parts
1610 TTCN3_Debug_Function::~TTCN3_Debug_Function()
1612 if (ttcn3_debugger
.is_on()) {
1613 char* snapshot
= mprintf("[%s]\tfinished\t%s(", function_type
, function_name
);
1614 if (parameter_names
->size_of() > 0) {
1615 for (int i
= 0; i
< parameter_names
->size_of(); ++i
) {
1617 snapshot
= mputstr(snapshot
, ", ");
1619 snapshot
= mputprintf(snapshot
, "[%s] %s := ", (const char*)((*parameter_types
)[i
]),
1620 (const char*)((*parameter_names
)[i
]));
1621 if ((*parameter_types
)[i
] == "out" || (*parameter_types
)[i
] == "inout") {
1622 const TTCN3_Debugger::variable_t
* parameter
= find_variable((*parameter_names
)[i
]);
1623 snapshot
= mputstr(snapshot
, parameter
->print_function(*parameter
));
1626 snapshot
= mputc(snapshot
, '-');
1630 snapshot
= mputc(snapshot
, ')');
1631 if (return_value
.is_bound()) {
1632 snapshot
= mputprintf(snapshot
, " returned %s", (const char*)return_value
);
1634 ttcn3_debugger
.add_snapshot(snapshot
);
1637 for (size_t i
= 0; i
< variables
.size(); ++i
) {
1638 delete variables
[i
];
1640 delete parameter_names
;
1641 delete parameter_types
;
1642 ttcn3_debugger
.remove_function(this);
1645 TTCN3_Debugger::variable_t
* TTCN3_Debug_Function::add_variable(const void* p_value
,
1648 const char* p_module
,
1649 TTCN3_Debugger::print_function_t p_print_function
)
1651 if (ttcn3_debugger
.is_on()) {
1652 TTCN3_Debugger::variable_t
* var
= new TTCN3_Debugger::variable_t
;
1653 var
->cvalue
= p_value
;
1655 var
->type_name
= p_type
;
1656 var
->module
= p_module
;
1657 var
->print_function
= p_print_function
;
1658 var
->set_function
= NULL
;
1659 variables
.push_back(var
);
1665 TTCN3_Debugger::variable_t
* TTCN3_Debug_Function::add_variable(void* p_value
,
1668 const char* p_module
,
1669 TTCN3_Debugger::print_function_t p_print_function
,
1670 TTCN3_Debugger::set_function_t p_set_function
)
1672 if (ttcn3_debugger
.is_on()) {
1673 TTCN3_Debugger::variable_t
* var
= new TTCN3_Debugger::variable_t
;
1674 var
->value
= p_value
;
1676 var
->type_name
= p_type
;
1677 var
->module
= p_module
;
1678 var
->print_function
= p_print_function
;
1679 var
->set_function
= p_set_function
;
1680 variables
.push_back(var
);
1686 void TTCN3_Debug_Function::set_return_value(const CHARSTRING
& p_value
)
1688 return_value
= p_value
;
1691 void TTCN3_Debug_Function::initial_snapshot() const
1693 if (ttcn3_debugger
.is_on()) {
1694 char* snapshot
= mprintf("[%s]\tstarted \t%s(", function_type
, function_name
);
1695 if (parameter_names
->size_of() > 0) {
1696 for (int i
= 0; i
< parameter_names
->size_of(); ++i
) {
1698 snapshot
= mputstr(snapshot
, ", ");
1700 snapshot
= mputprintf(snapshot
, "[%s] %s := ", (const char*)((*parameter_types
)[i
]),
1701 (const char*)((*parameter_names
)[i
]));
1702 if ((*parameter_types
)[i
] == "in" || (*parameter_types
)[i
] == "inout") {
1703 const TTCN3_Debugger::variable_t
* parameter
= find_variable((*parameter_names
)[i
]);
1704 snapshot
= mputstr(snapshot
, parameter
->print_function(*parameter
));
1707 snapshot
= mputc(snapshot
, '-');
1711 snapshot
= mputstr(snapshot
, ")");
1712 ttcn3_debugger
.add_snapshot(snapshot
);
1717 void TTCN3_Debug_Function::add_scope(TTCN3_Debug_Scope
* p_scope
)
1719 scopes
.push_back(p_scope
);
1722 void TTCN3_Debug_Function::remove_scope(TTCN3_Debug_Scope
* p_scope
)
1724 if (!scopes
.empty() && scopes
[scopes
.size() - 1] == p_scope
) {
1725 scopes
.erase_at(scopes
.size() - 1);
1729 void TTCN3_Debug_Function::remove_variable(const TTCN3_Debugger::variable_t
* p_var
)
1731 for (size_t i
= 0; i
< variables
.size(); ++i
) {
1732 if (variables
[i
] == p_var
) {
1733 variables
.erase_at(i
);
1740 TTCN3_Debugger::variable_t
* TTCN3_Debug_Function::find_variable(const char* p_name
) const
1742 for (size_t i
= 0; i
< variables
.size(); ++i
) {
1743 if (strcmp(variables
[i
]->name
, p_name
) == 0) {
1744 return variables
[i
];
1747 // it's not a local variable, it might still be a global or component variable
1748 if (component_scope
!= NULL
) {
1749 TTCN3_Debugger::variable_t
* res
= component_scope
->find_variable(p_name
);
1754 return (global_scope
!= NULL
) ? global_scope
->find_variable(p_name
) : NULL
;
1757 void TTCN3_Debug_Function::print_function() const
1759 ttcn3_debugger
.add_to_result("[%s]\t%s(", function_type
, function_name
);
1760 if (parameter_names
->size_of() > 0) {
1761 for (int i
= 0; i
< parameter_names
->size_of(); ++i
) {
1763 ttcn3_debugger
.add_to_result(", ");
1765 const TTCN3_Debugger::variable_t
* parameter
= find_variable((*parameter_names
)[i
]);
1766 ttcn3_debugger
.add_to_result("[%s] %s := %s", (const char*)(*parameter_types
)[i
],
1767 (const char*)(*parameter_names
)[i
], (const char*)parameter
->print_function(*parameter
));
1770 ttcn3_debugger
.add_to_result(")");
1773 void TTCN3_Debug_Function::list_variables(const char* p_scope
, const char* p_filter
) const
1776 bool list_local
= false;
1777 bool list_global
= false;
1778 bool list_comp
= false;
1779 if (!strcmp(p_scope
, "local")) {
1782 else if (!strcmp(p_scope
, "global")) {
1785 else if (!strcmp(p_scope
, "comp")) {
1788 else if (!strcmp(p_scope
, "all")) {
1794 ttcn3_debugger
.print(DRET_NOTIFICATION
, "Argument 1 is invalid. "
1795 "Expected 'local', 'global', 'comp' or 'all'.");
1798 regex_t
* posix_regexp
= NULL
;
1799 if (p_filter
!= NULL
) {
1800 char* posix_str
= TTCN_pattern_to_regexp(p_filter
);
1801 if (posix_str
== NULL
) {
1802 ttcn3_debugger
.print(DRET_NOTIFICATION
, "Argument 2 is invalid. "
1803 "Expected a valid TTCN-3 character pattern.");
1806 posix_regexp
= new regex_t
;
1807 int ret_val
= regcomp(posix_regexp
, posix_str
, REG_EXTENDED
| REG_NOSUB
);
1811 regerror(ret_val
, posix_regexp
, msg
, sizeof(msg
));
1812 regfree(posix_regexp
);
1813 delete posix_regexp
;
1814 ttcn3_debugger
.print(DRET_NOTIFICATION
, "Compilation of POSIX regular "
1815 "expression failed.");
1820 for (size_t i
= 0; i
< variables
.size(); ++i
) {
1821 if (posix_regexp
== NULL
||
1822 regexec(posix_regexp
, variables
[i
]->name
, 0, NULL
, 0) == 0) {
1823 ttcn3_debugger
.add_to_result("%s%s", first
? "" : " ", variables
[i
]->name
);
1828 if (list_global
&& global_scope
!= NULL
&& global_scope
->has_variables()) {
1829 global_scope
->list_variables(posix_regexp
, first
, module_name
);
1831 if (list_comp
&& component_scope
!= NULL
&& component_scope
->has_variables()) {
1832 component_scope
->list_variables(posix_regexp
, first
, NULL
);
1835 ttcn3_debugger
.print(DRET_NOTIFICATION
, "No variables found.");
1837 if (posix_regexp
!= NULL
) {
1838 regfree(posix_regexp
);
1839 delete posix_regexp
;
1843 bool TTCN3_Debug_Function::is_control_part() const
1845 return !strcmp(function_type
, "control");
1848 bool TTCN3_Debug_Function::is_test_case() const
1850 return !strcmp(function_type
, "testcase");