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"
21 //////////////////////////////////////////////////////
22 ////////////////// TTCN3_Debugger ////////////////////
23 //////////////////////////////////////////////////////
25 TTCN3_Debugger ttcn3_debugger
;
27 void TTCN3_Debugger::switch_state(const char* p_state_str
)
29 if (!strcmp(p_state_str
, "on")) {
31 print(DRET_NOTIFICATION
, "The debugger is already switched on.");
35 print(DRET_SETTING_CHANGE
, "Debugger switched on.");
38 else if(!strcmp(p_state_str
, "off")) {
40 print(DRET_NOTIFICATION
, "The debugger is already switched off.");
44 print(DRET_SETTING_CHANGE
, "Debugger switched off.");
48 print(DRET_NOTIFICATION
, "Argument 1 is invalid. Expected 'yes' or 'no'.");
52 void TTCN3_Debugger::add_breakpoint(const char* p_module
, int p_line
/*const char* batch_file*/)
54 if (find_breakpoint(p_module
, p_line
) == breakpoints
.size()) {
56 bp
.module
= mcopystr(p_module
);
58 breakpoints
.push_back(bp
);
59 print(DRET_SETTING_CHANGE
, "Breakpoint added in module '%s' at line %d.",
63 print(DRET_NOTIFICATION
, "Breakpoint already set in module '%s' at line %d.",
68 void TTCN3_Debugger::remove_breakpoint(const char* p_module
, int p_line
)
70 size_t pos
= find_breakpoint(p_module
, p_line
);
71 if (pos
!= breakpoints
.size()) {
72 Free(breakpoints
[pos
].module
);
73 breakpoints
.erase_at(pos
);
74 print(DRET_SETTING_CHANGE
, "Breakpoint removed in module '%s' from line %d.",
78 print(DRET_NOTIFICATION
, "No breakpoint found in module '%s' at line %d.",
83 void TTCN3_Debugger::set_special_breakpoint(special_breakpoint_t p_type
, const char* p_state_str
)
86 if (!strcmp(p_state_str
, "yes")) {
89 else if(!strcmp(p_state_str
, "no")) {
94 print(DRET_NOTIFICATION
, "Argument 1 is invalid. Expected 'yes' or 'no'.");
97 const char* sbp_type_str
;
100 case SBP_FAIL_VERDICT
:
101 state_changed
= (fail_behavior
!= new_state
);
102 fail_behavior
= new_state
;
103 sbp_type_str
= "Fail";
105 case SBP_ERROR_VERDICT
:
106 state_changed
= (error_behavior
!= new_state
);
107 error_behavior
= new_state
;
108 sbp_type_str
= "Error";
111 // should never happen
114 print(DRET_SETTING_CHANGE
, "%s verdict behavior %sset to %s.", sbp_type_str
,
115 state_changed
? "" : "was already ",
116 new_state
? "halt test execution" : "do nothing");
119 void TTCN3_Debugger::print_call_stack()
121 for (size_t i
= call_stack
.size(); i
!= 0; --i
) {
122 add_to_result("%d.\t", (int)call_stack
.size() - (int)i
+ 1);
123 call_stack
[i
- 1]->print_function();
130 void TTCN3_Debugger::set_stack_level(int new_level
)
133 print(DRET_NOTIFICATION
, "Stack level can only be set if test execution is halted.");
135 else if (new_level
<= 0 || (size_t)new_level
> call_stack
.size()) {
136 print(DRET_NOTIFICATION
, "Invalid new stack level. Expected 1 - %d.",
137 (int)call_stack
.size());
140 stack_level
= (int)call_stack
.size() - new_level
;
141 call_stack
[stack_level
]->print_function();
142 print(DRET_NOTIFICATION
, "Stack level set to:\n%d.\t%s", new_level
, command_result
);
146 void TTCN3_Debugger::print_variable(const TTCN3_Debugger::variable_t
* p_var
)
148 add_to_result("[%s] %s := %s", p_var
->type_name
, p_var
->name
,
149 (const char*)p_var
->print_function(*p_var
));
152 void TTCN3_Debugger::set_output(const char* p_output_type
, const char* p_file_name
)
156 bool same_file
= false;
157 char* final_file_name
= NULL
;
158 // check the command's parameters before actually changing anything
159 if (!strcmp(p_output_type
, "console")) {
163 else if (!strcmp(p_output_type
, "file")) {
167 else if (!strcmp(p_output_type
, "both")) {
172 print(DRET_NOTIFICATION
, "Argument 1 is invalid. Expected 'console', 'file' or 'both'.");
176 if (p_file_name
== NULL
) {
177 print(DRET_NOTIFICATION
, "Argument 2 (output file name) is missing.");
180 if (output_file_name
!= NULL
&& !strcmp(p_file_name
, output_file_name
)) {
181 // don't reopen it if it's the same file as before
184 else if (!TTCN_Runtime::is_hc()) {
185 // don't open any files on HCs, just store settings for future PTCs
186 final_file_name
= finalize_file_name(p_file_name
);
187 new_fp
= fopen(final_file_name
, "w");
188 if (new_fp
== NULL
) {
189 print(DRET_NOTIFICATION
, "Failed to open file '%s' for writing.", final_file_name
);
194 // print the change notification to the old output
195 char* file_str
= file
? mprintf("file '%s'", TTCN_Runtime::is_hc() ? p_file_name
196 : final_file_name
) : NULL
;
197 Free(final_file_name
);
198 print(DRET_SETTING_CHANGE
, "Debugger set to print its output to %s%s%s.",
199 console
? "the console" : "", (console
&& file
) ? " and to " : "",
200 file
? file_str
: "");
204 if (!same_file
&& !TTCN_Runtime::is_hc()) {
205 if (output_file
!= NULL
) {
208 output_file
= new_fp
;
210 send_to_console
= console
;
211 Free(output_file_name
);
213 output_file_name
= mcopystr(p_file_name
);
217 void TTCN3_Debugger::halt()
221 stack_level
= call_stack
.size() - 1;
222 print(DRET_NOTIFICATION
, "Test execution halted.");
223 TTCN_Communication::process_debug_messages();
226 print(DRET_NOTIFICATION
, "Test execution is already halted.");
230 void TTCN3_Debugger::resume()
235 print(DRET_NOTIFICATION
, "Test execution resumed.");
238 print(DRET_NOTIFICATION
, "Test execution is not halted.");
242 void TTCN3_Debugger::exit_(const char* p_what
)
245 if (!strcmp(p_what
, "test")) {
248 else if (!strcmp(p_what
, "all")) {
252 print(DRET_NOTIFICATION
, "Argument 1 is invalid. Expected 'test' or 'all'.");
256 print((exit_all
&& TTCN_Runtime::is_mtc()) ? DRET_EXIT_ALL
: DRET_NOTIFICATION
,
257 "Exiting %s.", exit_all
? "test execution" : "current test");
258 TTCN_Runtime::stop_execution();
261 size_t TTCN3_Debugger::find_breakpoint(const char* p_module
, int p_line
) const
263 for (size_t i
= 0; i
< breakpoints
.size(); ++i
) {
264 if (!strcmp(breakpoints
[i
].module
, p_module
) && breakpoints
[i
].line
== p_line
) {
268 return breakpoints
.size();
271 TTCN3_Debugger::variable_t
* TTCN3_Debugger::find_variable(const void* p_value
) const
273 for (size_t i
= 0; i
< variables
.size(); ++i
) {
274 if (variables
[i
]->value
== p_value
) {
281 char* TTCN3_Debugger::finalize_file_name(const char* p_file_name_skeleton
)
283 if (p_file_name_skeleton
== NULL
) {
286 size_t len
= strlen(p_file_name_skeleton
);
288 char* ret_val
= NULL
;
289 for (size_t i
= 0; i
< len
- 1; ++i
) {
290 if (p_file_name_skeleton
[i
] == '%') {
291 ret_val
= mputstrn(ret_val
, p_file_name_skeleton
+ next_idx
, i
- next_idx
);
292 switch (p_file_name_skeleton
[i
+ 1]) {
293 case 'e': // %e -> executable name
294 ret_val
= mputstr(ret_val
, TTCN_Logger::get_executable_name());
296 case 'h': // %h -> host name
297 ret_val
= mputstr(ret_val
, TTCN_Runtime::get_host_name());
299 case 'p': // %p -> process ID
300 ret_val
= mputprintf(ret_val
, "%ld", (long)getpid());
302 case 'l': { // %l -> login name
304 struct passwd
*p
= getpwuid(getuid());
306 ret_val
= mputstr(ret_val
, p
->pw_name
);
310 case 'r': // %r -> component reference
311 if (TTCN_Runtime::is_single()) {
312 ret_val
= mputstr(ret_val
, "single");
314 else if (TTCN_Runtime::is_mtc()) {
315 ret_val
= mputstr(ret_val
, "mtc");
317 else if (TTCN_Runtime::is_ptc()) {
318 ret_val
= mputprintf(ret_val
, "%d", (component
)self
);
321 case 'n': // %n -> component name
322 if (TTCN_Runtime::is_mtc()) {
323 ret_val
= mputstr(ret_val
, "MTC");
325 else if (TTCN_Runtime::is_ptc()) {
326 ret_val
= mputstr(ret_val
, TTCN_Runtime::get_component_name());
329 case '%': // %% -> single %
330 ret_val
= mputc(ret_val
, '%');
332 default: // unknown sequence -> leave it as it is
333 ret_val
= mputstrn(ret_val
, p_file_name_skeleton
+ i
, 2);
340 if (next_idx
< len
) {
341 ret_val
= mputstr(ret_val
, p_file_name_skeleton
+ next_idx
);
346 void TTCN3_Debugger::print(int return_type
, const char* fmt
, ...) const
349 va_start(parameters
, fmt
);
350 char* str
= mprintf_va_list(fmt
, parameters
);
352 TTCN_Communication::send_debug_return_value(return_type
, send_to_console
? str
: NULL
);
353 if (output_file
!= NULL
) {
354 fprintf(output_file
, "%s\n", str
);
360 TTCN3_Debugger::TTCN3_Debugger()
366 output_file_name
= NULL
;
367 send_to_console
= true;
369 last_breakpoint_entry
.module
= NULL
;
370 last_breakpoint_entry
.line
= 0;
372 fail_behavior
= false;
373 error_behavior
= false;
374 command_result
= NULL
;
377 TTCN3_Debugger::~TTCN3_Debugger()
379 if (output_file
!= NULL
) {
381 Free(output_file_name
);
383 for (size_t i
= 0; i
< breakpoints
.size(); ++i
) {
384 Free(breakpoints
[i
].module
);
386 for (size_t i
= 0; i
< global_scopes
.size(); ++i
) {
387 delete global_scopes
[i
].scope
;
389 for (size_t i
= 0; i
< component_scopes
.size(); ++i
) {
390 delete component_scopes
[i
].scope
;
392 for (size_t i
= 0; i
< variables
.size(); ++i
) {
396 Free(command_result
);
399 TTCN3_Debug_Scope
* TTCN3_Debugger::add_global_scope(const char* p_module
)
401 named_scope_t global_scope
;
402 global_scope
.name
= p_module
;
403 global_scope
.scope
= new TTCN3_Debug_Scope();
404 global_scopes
.push_back(global_scope
);
405 return global_scope
.scope
;
408 TTCN3_Debug_Scope
* TTCN3_Debugger::add_component_scope(const char* p_component
)
410 named_scope_t component_scope
;
411 component_scope
.name
= p_component
;
412 component_scope
.scope
= new TTCN3_Debug_Scope();
413 component_scopes
.push_back(component_scope
);
414 return component_scope
.scope
;
417 void TTCN3_Debugger::set_return_value(const CHARSTRING
& p_value
)
419 if (active
&& !call_stack
.empty()) {
420 call_stack
[call_stack
.size() - 1]->set_return_value(p_value
);
424 void TTCN3_Debugger::breakpoint_entry(int p_line
/*bool p_stepping_helper*/)
426 if (active
&& !call_stack
.empty()) {
427 const char* module_name
= call_stack
[call_stack
.size() - 1]->get_module_name();
428 bool trigger
= false;
429 const char* trigger_type
;
432 case SBP_FAIL_VERDICT
:
433 trigger
= fail_behavior
;
434 trigger_type
= "Automatic breakpoint (fail verdict)";
435 actual_line
= last_breakpoint_entry
.line
;
437 case SBP_ERROR_VERDICT
:
438 trigger
= error_behavior
;
439 trigger_type
= "Automatic breakpoint (error verdict)";
440 actual_line
= last_breakpoint_entry
.line
;
442 default: // code lines
443 // make sure it's not the same breakpoint entry as last time
444 trigger
= (last_breakpoint_entry
.line
== 0 || p_line
!= last_breakpoint_entry
.line
||
445 module_name
!= last_breakpoint_entry
.module
) &&
446 find_breakpoint(module_name
, p_line
) != breakpoints
.size();
447 trigger_type
= "User breakpoint";
448 actual_line
= p_line
;
452 print(DRET_NOTIFICATION
, "%s reached in module '%s' at line %d.",
453 trigger_type
, module_name
, actual_line
);
454 TTCN_Communication::send_debug_halt_req();
457 last_breakpoint_entry
.module
= (char*)module_name
;
458 last_breakpoint_entry
.line
= p_line
;
462 CHARSTRING
TTCN3_Debugger::print_base_var(const TTCN3_Debugger::variable_t
& p_var
)
464 TTCN_Logger::begin_event_log2str();
465 if (!strcmp(p_var
.type_name
, "bitstring")) {
466 ((const BITSTRING
*)p_var
.value
)->log();
468 else if (!strcmp(p_var
.type_name
, "bitstring template")) {
469 ((const BITSTRING_template
*)p_var
.value
)->log();
471 else if (!strcmp(p_var
.type_name
, "boolean")) {
472 ((const BOOLEAN
*)p_var
.value
)->log();
474 else if (!strcmp(p_var
.type_name
, "boolean template")) {
475 ((const BOOLEAN_template
*)p_var
.value
)->log();
477 else if (!strcmp(p_var
.type_name
, "charstring")) {
478 ((const CHARSTRING
*)p_var
.value
)->log();
480 else if (!strcmp(p_var
.type_name
, "charstring template")) {
481 ((const CHARSTRING_template
*)p_var
.value
)->log();
483 else if (!strcmp(p_var
.type_name
, "float")) {
484 ((const FLOAT
*)p_var
.value
)->log();
486 else if (!strcmp(p_var
.type_name
, "float template")) {
487 ((const FLOAT_template
*)p_var
.value
)->log();
489 else if (!strcmp(p_var
.type_name
, "hexstring")) {
490 ((const HEXSTRING
*)p_var
.value
)->log();
492 else if (!strcmp(p_var
.type_name
, "hexstring template")) {
493 ((const HEXSTRING_template
*)p_var
.value
)->log();
495 else if (!strcmp(p_var
.type_name
, "integer")) {
496 ((const INTEGER
*)p_var
.value
)->log();
498 else if (!strcmp(p_var
.type_name
, "integer template")) {
499 ((const INTEGER_template
*)p_var
.value
)->log();
501 else if (!strcmp(p_var
.type_name
, "objid")) {
502 ((const OBJID
*)p_var
.value
)->log();
504 else if (!strcmp(p_var
.type_name
, "objid template")) {
505 ((const OBJID_template
*)p_var
.value
)->log();
507 else if (!strcmp(p_var
.type_name
, "octetstring")) {
508 ((const OCTETSTRING
*)p_var
.value
)->log();
510 else if (!strcmp(p_var
.type_name
, "octetstring template")) {
511 ((const OCTETSTRING_template
*)p_var
.value
)->log();
513 else if (!strcmp(p_var
.type_name
, "universal charstring")) {
514 ((const UNIVERSAL_CHARSTRING
*)p_var
.value
)->log();
516 else if (!strcmp(p_var
.type_name
, "universal charstring template")) {
517 ((const UNIVERSAL_CHARSTRING_template
*)p_var
.value
)->log();
519 else if (!strcmp(p_var
.type_name
, "verdicttype")) {
520 ((const VERDICTTYPE
*)p_var
.value
)->log();
522 else if (!strcmp(p_var
.type_name
, "verdicttype template")) {
523 ((const VERDICTTYPE_template
*)p_var
.value
)->log();
525 else if (!strcmp(p_var
.type_name
, "component")) {
526 ((const COMPONENT
*)p_var
.value
)->log();
528 else if (!strcmp(p_var
.type_name
, "component template")) {
529 ((const COMPONENT_template
*)p_var
.value
)->log();
531 else if (!strcmp(p_var
.type_name
, "default")) {
532 ((const DEFAULT
*)p_var
.value
)->log();
534 else if (!strcmp(p_var
.type_name
, "default template")) {
535 ((const DEFAULT_template
*)p_var
.value
)->log();
537 else if (!strcmp(p_var
.type_name
, "timer")) {
538 ((const TIMER
*)p_var
.value
)->log();
540 else if (!strcmp(p_var
.type_name
, "NULL")) {
541 ((const ASN_NULL
*)p_var
.value
)->log();
543 else if (!strcmp(p_var
.type_name
, "NULL template")) {
544 ((const ASN_NULL_template
*)p_var
.value
)->log();
546 else if (!strcmp(p_var
.type_name
, "CHARACTER STRING")) {
547 ((const CHARACTER_STRING
*)p_var
.value
)->log();
549 else if (!strcmp(p_var
.type_name
, "CHARACTER STRING template")) {
550 ((const CHARACTER_STRING_template
*)p_var
.value
)->log();
552 else if (!strcmp(p_var
.type_name
, "EMBEDDED PDV")) {
553 ((const EMBEDDED_PDV
*)p_var
.value
)->log();
555 else if (!strcmp(p_var
.type_name
, "EMBEDDED PDV template")) {
556 ((const EMBEDDED_PDV_template
*)p_var
.value
)->log();
558 else if (!strcmp(p_var
.type_name
, "EXTERNAL")) {
559 ((const EXTERNAL
*)p_var
.value
)->log();
561 else if (!strcmp(p_var
.type_name
, "EXTERNAL template")) {
562 ((const EXTERNAL_template
*)p_var
.value
)->log();
565 TTCN_Logger::log_event_str("<unrecognized value or template>");
567 return TTCN_Logger::end_event_log2str();
570 void TTCN3_Debugger::add_to_result(const char* fmt
, ...)
573 va_start(parameters
, fmt
);
574 command_result
= mputprintf_va_list(command_result
, fmt
, parameters
);
578 void TTCN3_Debugger::add_function(TTCN3_Debug_Function
* p_function
)
581 call_stack
.push_back(p_function
);
585 void TTCN3_Debugger::add_scope(TTCN3_Debug_Scope
* p_scope
)
587 if (active
&& !call_stack
.empty()) {
588 call_stack
[call_stack
.size() - 1]->add_scope(p_scope
);
592 void TTCN3_Debugger::remove_function(TTCN3_Debug_Function
* p_function
)
594 if (!call_stack
.empty() && call_stack
[call_stack
.size() - 1] == p_function
) {
595 call_stack
.erase_at(call_stack
.size() - 1);
599 void TTCN3_Debugger::remove_scope(TTCN3_Debug_Scope
* p_scope
)
601 if (!call_stack
.empty()) {
602 call_stack
[call_stack
.size() - 1]->remove_scope(p_scope
);
606 const TTCN3_Debugger::variable_t
* TTCN3_Debugger::add_variable(const void* p_value
,
609 CHARSTRING (*p_print_function
)(const TTCN3_Debugger::variable_t
&))
611 if (call_stack
.empty()) {
612 // no call stack yet, so this is a global or component variable
613 variable_t
* var
= find_variable(p_value
);
615 var
= new TTCN3_Debugger::variable_t
;
616 var
->value
= p_value
;
618 var
->type_name
= p_type
;
619 var
->print_function
= p_print_function
;
620 variables
.push_back(var
);
625 // it's a local variable for the top-most function
626 return call_stack
[call_stack
.size() - 1]->add_variable(p_value
, p_name
, p_type
, p_print_function
);
631 void TTCN3_Debugger::remove_variable(const variable_t
* p_var
)
633 if (active
&& !call_stack
.empty()) {
634 call_stack
[call_stack
.size() - 1]->remove_variable(p_var
);
638 const TTCN3_Debug_Scope
* TTCN3_Debugger::get_global_scope(const char* p_module
) const
640 for (size_t i
= 0; i
< global_scopes
.size(); ++i
) {
641 if (strcmp(global_scopes
[i
].name
, p_module
) == 0) {
642 return global_scopes
[i
].scope
;
648 const TTCN3_Debug_Scope
* TTCN3_Debugger::get_component_scope(const char* p_component
) const
650 for (size_t i
= 0; i
< component_scopes
.size(); ++i
) {
651 if (strcmp(component_scopes
[i
].name
, p_component
) == 0) {
652 return component_scopes
[i
].scope
;
658 void TTCN3_Debugger::add_snapshot(const char* p_snapshot
)
660 if (snapshots
!= NULL
) {
661 snapshots
= mputc(snapshots
, '\n');
663 snapshots
= mputstr(snapshots
, p_snapshot
);
666 #define CHECK_NOF_ARGUMENTS(exp_num) \
667 if (exp_num != p_argument_count) { \
668 print(DRET_NOTIFICATION, "Invalid number of arguments. Expected %d, got %d.", \
669 (int)exp_num, (int)p_argument_count); \
673 #define CHECK_NOF_ARGUMENTS_RANGE(min, max) \
674 if ((int)min > p_argument_count || (int)max < p_argument_count) { \
675 print(DRET_NOTIFICATION, "Invalid number of arguments. Expected at least %d " \
676 "and at most %d, got %d.", (int)min, (int)max, p_argument_count); \
680 #define CHECK_NOF_ARGUMENTS_MIN(min) \
681 if ((int)min > p_argument_count) { \
682 print(DRET_NOTIFICATION, "Invalid number of arguments. Expected at least %d, got %d.", \
683 (int)min, p_argument_count); \
687 #define CHECK_INT_ARGUMENT(arg_idx) \
689 size_t len = strlen(p_arguments[arg_idx]); \
690 for (size_t i = 0; i < len; ++i) { \
691 if (p_arguments[arg_idx][i] < '0' || p_arguments[arg_idx][i] > '9') { \
692 print(DRET_NOTIFICATION, "Argument %d is not an integer.", (int)(arg_idx + 1)); \
698 #define CHECK_CALL_STACK \
699 if (call_stack.empty()) { \
700 print(DRET_NOTIFICATION, "This command can only be used during test execution."); \
704 #define STACK_LEVEL (stack_level >= 0) ? (size_t)stack_level : (call_stack.size() - 1)
706 void TTCN3_Debugger::execute_command(int p_command
, int p_argument_count
,
712 Free(command_result
);
713 command_result
= NULL
;
714 for (int i
= 0; i
< p_argument_count
; ++i
) {
715 if (p_arguments
[i
] == NULL
) {
716 print(DRET_NOTIFICATION
, "Argument %d is a null pointer.", i
+ 1);
722 CHECK_NOF_ARGUMENTS(1)
723 switch_state(p_arguments
[0]);
725 case D_ADD_BREAKPOINT
:
726 CHECK_NOF_ARGUMENTS(2)
727 CHECK_INT_ARGUMENT(1)
728 add_breakpoint(p_arguments
[0], str2int(p_arguments
[1]));
730 case D_REMOVE_BREAKPOINT
:
731 CHECK_NOF_ARGUMENTS(2)
732 CHECK_INT_ARGUMENT(1)
733 remove_breakpoint(p_arguments
[0], str2int(p_arguments
[1]));
735 case D_SET_ERROR_BEHAVIOR
:
736 CHECK_NOF_ARGUMENTS(1)
737 set_special_breakpoint(SBP_ERROR_VERDICT
, p_arguments
[0]);
739 case D_SET_FAIL_BEHAVIOR
:
740 CHECK_NOF_ARGUMENTS(1)
741 set_special_breakpoint(SBP_FAIL_VERDICT
, p_arguments
[0]);
744 CHECK_NOF_ARGUMENTS_RANGE(1, 2)
745 set_output(p_arguments
[0], (p_argument_count
== 2) ? p_arguments
[1] : NULL
);
747 case D_SET_COMPONENT
:
748 print(DRET_NOTIFICATION
, "Command " D_SET_COMPONENT_TEXT
" should have been "
749 "sent to the Main Controller.");
751 case D_PRINT_CALL_STACK
:
753 CHECK_NOF_ARGUMENTS(0)
756 case D_SET_STACK_LEVEL
:
758 CHECK_NOF_ARGUMENTS(1)
759 CHECK_INT_ARGUMENT(0)
760 set_stack_level(str2int(p_arguments
[0]));
761 return; // don't print the command result in this case
762 case D_LIST_VARIABLES
:
764 CHECK_NOF_ARGUMENTS_RANGE(1, 2)
765 call_stack
[STACK_LEVEL
]->list_variables(p_arguments
[0],
766 (p_argument_count
== 2) ? p_arguments
[1] : NULL
);
768 case D_PRINT_VARIABLE
:
770 CHECK_NOF_ARGUMENTS_MIN(1)
771 for (int i
= 0; i
< p_argument_count
; ++i
) {
772 const variable_t
* var
= call_stack
[STACK_LEVEL
]->find_variable(p_arguments
[i
]);
777 add_to_result("Variable '%s' not found.", p_arguments
[i
]);
779 if (i
!= p_argument_count
- 1) {
785 case D_PRINT_SNAPSHOTS
:
786 CHECK_NOF_ARGUMENTS(0)
787 add_to_result("%s", snapshots
);
791 if (TTCN_Runtime::is_mtc()) {
794 CHECK_NOF_ARGUMENTS(0)
798 CHECK_NOF_ARGUMENTS(0)
802 CHECK_NOF_ARGUMENTS(1)
803 if (TTCN_Runtime::is_mtc()) {
806 exit_(p_arguments
[0]);
809 CHECK_NOF_ARGUMENTS_MIN(5)
810 if (strlen(p_arguments
[0]) > 0) {
811 switch_state(p_arguments
[0]);
813 if (strlen(p_arguments
[1]) > 0) {
814 set_output(p_arguments
[1], p_arguments
[2]);
816 if (strlen(p_arguments
[3]) > 0) {
817 set_special_breakpoint(SBP_ERROR_VERDICT
, p_arguments
[3]);
819 if (strlen(p_arguments
[4]) > 0) {
820 set_special_breakpoint(SBP_FAIL_VERDICT
, p_arguments
[4]);
822 for (int i
= 5; i
< p_argument_count
; i
+= 2) {
823 add_breakpoint(p_arguments
[i
], str2int(p_arguments
[i
+ 1]));
827 print(DRET_NOTIFICATION
, "Command not implemented.");
830 if (command_result
!= NULL
) {
831 print(DRET_DATA
, command_result
);
835 void TTCN3_Debugger::open_output_file()
837 if (output_file
== NULL
&& output_file_name
!= NULL
) {
838 char* final_file_name
= finalize_file_name(output_file_name
);
839 output_file
= fopen(final_file_name
, "w");
840 if (output_file
== NULL
) {
841 print(DRET_NOTIFICATION
, "Failed to open file '%s' for writing.", final_file_name
);
843 Free(final_file_name
);
847 //////////////////////////////////////////////////////
848 //////////////// TTCN3_Debug_Scope ///////////////////
849 //////////////////////////////////////////////////////
851 TTCN3_Debug_Scope::TTCN3_Debug_Scope()
853 ttcn3_debugger
.add_scope(this);
856 TTCN3_Debug_Scope::~TTCN3_Debug_Scope()
858 for (size_t i
= 0; i
< variables
.size(); ++i
) {
859 ttcn3_debugger
.remove_variable(variables
[i
]);
861 ttcn3_debugger
.remove_scope(this);
864 void TTCN3_Debug_Scope::add_variable(const void* p_value
,
867 CHARSTRING (*p_print_function
)(const TTCN3_Debugger::variable_t
&))
869 const TTCN3_Debugger::variable_t
* var
= ttcn3_debugger
.add_variable(p_value
, p_name
, p_type
, p_print_function
);
871 variables
.push_back(var
);
875 const TTCN3_Debugger::variable_t
* TTCN3_Debug_Scope::find_variable(const char* p_name
) const
877 for (size_t i
= 0; i
< variables
.size(); ++i
) {
878 if (strcmp(variables
[i
]->name
, p_name
) == 0) {
885 void TTCN3_Debug_Scope::list_variables(regex_t
* p_posix_regexp
, bool& p_first
) const
887 for (size_t i
= 0; i
< variables
.size(); ++i
) {
888 if (p_posix_regexp
== NULL
||
889 regexec(p_posix_regexp
, variables
[i
]->name
, 0, NULL
, 0) == 0) {
890 ttcn3_debugger
.add_to_result("%s%s", p_first
? "" : " ", variables
[i
]->name
);
896 //////////////////////////////////////////////////////
897 /////////////// TTCN3_Debug_Function /////////////////
898 //////////////////////////////////////////////////////
900 TTCN3_Debug_Function::TTCN3_Debug_Function(const char* p_name
,
902 const char* p_module
,
903 const charstring_list
& p_parameter_names
,
904 const charstring_list
& p_parameter_types
,
905 const char* p_component_name
)
906 : function_name(p_name
), function_type(p_type
), module_name(p_module
)
907 , parameter_names(new charstring_list(p_parameter_names
))
908 , parameter_types(new charstring_list(p_parameter_types
))
910 ttcn3_debugger
.add_function(this);
911 global_scope
= ttcn3_debugger
.get_global_scope(p_module
);
912 component_scope
= (p_component_name
!= NULL
) ?
913 ttcn3_debugger
.get_component_scope(p_component_name
) : NULL
;
914 if (function_name
== NULL
) {
915 function_name
= p_module
; // for control parts
919 TTCN3_Debug_Function::~TTCN3_Debug_Function()
921 if (ttcn3_debugger
.is_on()) {
922 char* snapshot
= mprintf("[%s]\tfinished\t%s(", function_type
, function_name
);
923 if (parameter_names
->size_of() > 0) {
924 for (int i
= 0; i
< parameter_names
->size_of(); ++i
) {
926 snapshot
= mputstr(snapshot
, ", ");
928 snapshot
= mputprintf(snapshot
, "[%s] %s := ", (const char*)((*parameter_types
)[i
]),
929 (const char*)((*parameter_names
)[i
]));
930 if ((*parameter_types
)[i
] == "out" || (*parameter_types
)[i
] == "inout") {
931 const TTCN3_Debugger::variable_t
* parameter
= find_variable((*parameter_names
)[i
]);
932 snapshot
= mputstr(snapshot
, parameter
->print_function(*parameter
));
935 snapshot
= mputc(snapshot
, '-');
939 snapshot
= mputc(snapshot
, ')');
940 if (return_value
.is_bound()) {
941 snapshot
= mputprintf(snapshot
, " returned %s", (const char*)return_value
);
943 ttcn3_debugger
.add_snapshot(snapshot
);
946 for (size_t i
= 0; i
< variables
.size(); ++i
) {
949 delete parameter_names
;
950 delete parameter_types
;
951 ttcn3_debugger
.remove_function(this);
954 const TTCN3_Debugger::variable_t
* TTCN3_Debug_Function::add_variable(const void* p_value
,
957 CHARSTRING (*p_print_function
)(const TTCN3_Debugger::variable_t
&))
959 if (ttcn3_debugger
.is_on()) {
960 TTCN3_Debugger::variable_t
* var
= new TTCN3_Debugger::variable_t
;
961 var
->value
= p_value
;
963 var
->type_name
= p_type
;
964 var
->print_function
= p_print_function
;
965 variables
.push_back(var
);
971 void TTCN3_Debug_Function::set_return_value(const CHARSTRING
& p_value
)
973 return_value
= p_value
;
976 void TTCN3_Debug_Function::initial_snapshot() const
978 if (ttcn3_debugger
.is_on()) {
979 char* snapshot
= mprintf("[%s]\tstarted \t%s(", function_type
, function_name
);
980 if (parameter_names
->size_of() > 0) {
981 for (int i
= 0; i
< parameter_names
->size_of(); ++i
) {
983 snapshot
= mputstr(snapshot
, ", ");
985 snapshot
= mputprintf(snapshot
, "[%s] %s := ", (const char*)((*parameter_types
)[i
]),
986 (const char*)((*parameter_names
)[i
]));
987 if ((*parameter_types
)[i
] == "in" || (*parameter_types
)[i
] == "inout") {
988 const TTCN3_Debugger::variable_t
* parameter
= find_variable((*parameter_names
)[i
]);
989 snapshot
= mputstr(snapshot
, parameter
->print_function(*parameter
));
992 snapshot
= mputc(snapshot
, '-');
996 snapshot
= mputstr(snapshot
, ")");
997 ttcn3_debugger
.add_snapshot(snapshot
);
1002 void TTCN3_Debug_Function::add_scope(TTCN3_Debug_Scope
* p_scope
)
1004 scopes
.push_back(p_scope
);
1007 void TTCN3_Debug_Function::remove_scope(TTCN3_Debug_Scope
* p_scope
)
1009 if (!scopes
.empty() && scopes
[scopes
.size() - 1] == p_scope
) {
1010 scopes
.erase_at(scopes
.size() - 1);
1014 void TTCN3_Debug_Function::remove_variable(const TTCN3_Debugger::variable_t
* p_var
)
1016 for (size_t i
= 0; i
< variables
.size(); ++i
) {
1017 if (variables
[i
] == p_var
) {
1018 variables
.erase_at(i
);
1025 const TTCN3_Debugger::variable_t
* TTCN3_Debug_Function::find_variable(const char* p_name
) const
1027 for (size_t i
= 0; i
< variables
.size(); ++i
) {
1028 if (strcmp(variables
[i
]->name
, p_name
) == 0) {
1029 return variables
[i
];
1032 // it's not a local variable, it might still be a global or component variable
1033 if (component_scope
!= NULL
) {
1034 const TTCN3_Debugger::variable_t
* res
= component_scope
->find_variable(p_name
);
1039 return (global_scope
!= NULL
) ? global_scope
->find_variable(p_name
) : NULL
;
1042 void TTCN3_Debug_Function::print_function() const
1044 ttcn3_debugger
.add_to_result("[%s]\t%s(", function_type
, function_name
);
1045 if (parameter_names
->size_of() > 0) {
1046 for (int i
= 0; i
< parameter_names
->size_of(); ++i
) {
1048 ttcn3_debugger
.add_to_result(", ");
1050 const TTCN3_Debugger::variable_t
* parameter
= find_variable((*parameter_names
)[i
]);
1051 ttcn3_debugger
.add_to_result("[%s] %s := %s", (const char*)(*parameter_types
)[i
],
1052 (const char*)(*parameter_names
)[i
], (const char*)parameter
->print_function(*parameter
));
1055 ttcn3_debugger
.add_to_result(")");
1058 void TTCN3_Debug_Function::list_variables(const char* p_scope
, const char* p_filter
) const
1061 bool list_local
= false;
1062 bool list_global
= false;
1063 bool list_comp
= false;
1064 if (!strcmp(p_scope
, "local")) {
1067 else if (!strcmp(p_scope
, "global")) {
1070 else if (!strcmp(p_scope
, "comp")) {
1073 else if (!strcmp(p_scope
, "all")) {
1079 ttcn3_debugger
.print(DRET_NOTIFICATION
, "Argument 1 is invalid. "
1080 "Expected 'local', 'global', 'comp' or 'all'.");
1083 regex_t
* posix_regexp
= NULL
;
1084 if (p_filter
!= NULL
) {
1085 char* posix_str
= TTCN_pattern_to_regexp(p_filter
);
1086 if (posix_str
== NULL
) {
1087 ttcn3_debugger
.print(DRET_NOTIFICATION
, "Argument 2 is invalid. "
1088 "Expected a valid TTCN-3 character pattern.");
1091 posix_regexp
= new regex_t
;
1092 int ret_val
= regcomp(posix_regexp
, posix_str
, REG_EXTENDED
| REG_NOSUB
);
1096 regerror(ret_val
, posix_regexp
, msg
, sizeof(msg
));
1097 regfree(posix_regexp
);
1098 delete posix_regexp
;
1099 ttcn3_debugger
.print(DRET_NOTIFICATION
, "Compilation of POSIX regular "
1100 "expression failed.");
1105 for (size_t i
= 0; i
< variables
.size(); ++i
) {
1106 if (posix_regexp
== NULL
||
1107 regexec(posix_regexp
, variables
[i
]->name
, 0, NULL
, 0) == 0) {
1108 ttcn3_debugger
.add_to_result("%s%s", first
? "" : " ", variables
[i
]->name
);
1113 if (list_global
&& global_scope
!= NULL
&& global_scope
->has_variables()) {
1114 global_scope
->list_variables(posix_regexp
, first
);
1116 if (list_comp
&& component_scope
!= NULL
&& component_scope
->has_variables()) {
1117 component_scope
->list_variables(posix_regexp
, first
);
1120 ttcn3_debugger
.print(DRET_NOTIFICATION
, "No variables found.");
1122 if (posix_regexp
!= NULL
) {
1123 regfree(posix_regexp
);
1124 delete posix_regexp
;