debugger: switched advanded UI off by default, and implemented certain changes asked...
[deliverable/titan.core.git] / core / Debugger.cc
1 /******************************************************************************
2 * Copyright (c) 2000-2016 Ericsson Telecom AB
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 *
10 * Baranyi, Botond – initial implementation
11 *
12 ******************************************************************************/
13
14 #include "Debugger.hh"
15 #include "DebugCommands.hh"
16 #include "Communication.hh"
17 #include "../common/pattern.hh"
18 #include "Param_Types.hh"
19 #include "DebuggerUI.hh"
20 #include <unistd.h>
21 #include <pwd.h>
22
23 //////////////////////////////////////////////////////
24 ////////////////// TTCN3_Debugger ////////////////////
25 //////////////////////////////////////////////////////
26
27 TTCN3_Debugger ttcn3_debugger;
28
29 void TTCN3_Debugger::switch_state(const char* p_state_str)
30 {
31 if (!strcmp(p_state_str, "on")) {
32 if (active) {
33 print(DRET_NOTIFICATION, "The debugger is already switched on.");
34 }
35 else {
36 active = true;
37 print(DRET_SETTING_CHANGE, "Debugger switched on.");
38 }
39 }
40 else if(!strcmp(p_state_str, "off")) {
41 if (!active) {
42 print(DRET_NOTIFICATION, "The debugger is already switched off.");
43 }
44 else {
45 active = false;
46 print(DRET_SETTING_CHANGE, "Debugger switched off.");
47 }
48 }
49 else {
50 print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'on' or 'off'.");
51 }
52 }
53
54 void TTCN3_Debugger::set_breakpoint(const char* p_module, int p_line,
55 const char* batch_file)
56 {
57 size_t pos = find_breakpoint(p_module, p_line);
58 if (pos == breakpoints.size()) {
59 breakpoint_t bp;
60 bp.module = mcopystr(p_module);
61 bp.line = p_line;
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.",
65 p_module, p_line,
66 batch_file != NULL ? " with batch file '" : " with no batch file",
67 batch_file != NULL ? batch_file : "", batch_file != NULL ? "'" : "");
68 }
69 else {
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);
76 }
77 else {
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);
81 }
82 }
83 else {
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);
86 }
87 Free(breakpoints[pos].batch_file);
88 }
89 else {
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);
93 }
94 else {
95 print(DRET_NOTIFICATION, "Breakpoint already set in module '%s' at line "
96 "%d with no batch file.", p_module, p_line);
97 }
98 }
99 breakpoints[pos].batch_file = batch_file != NULL ? mcopystr(batch_file) : NULL;
100 }
101 }
102
103 void TTCN3_Debugger::remove_breakpoint(const char* p_module, const char* p_line)
104 {
105 bool all_breakpoints = !strcmp(p_module, "all");
106 if (p_line != NULL) {
107 if (!strcmp(p_line, "all")) {
108 bool found = false;
109 for (size_t i = breakpoints.size(); i > 0; --i) {
110 if (!strcmp(breakpoints[i - 1].module, p_module)) {
111 found = true;
112 Free(breakpoints[i - 1].module);
113 Free(breakpoints[i - 1].batch_file);
114 breakpoints.erase_at(i - 1);
115 }
116 }
117 if (found) {
118 print(DRET_SETTING_CHANGE, "Removed all breakpoints in module '%s'.", p_module);
119 }
120 else {
121 print(DRET_NOTIFICATION, "No breakpoints found in module '%s'.", p_module);
122 }
123 return;
124 }
125 else {
126 if (all_breakpoints) {
127 print(DRET_NOTIFICATION, "Unexpected 2nd argument, when the first "
128 "argument is 'all'.");
129 return;
130 }
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).");
136 return;
137 }
138 }
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.",
146 p_module, line);
147 }
148 else {
149 print(DRET_NOTIFICATION, "No breakpoint found in module '%s' at line %d.",
150 p_module, line);
151 }
152 return;
153 }
154 }
155 else if (!all_breakpoints) {
156 print(DRET_NOTIFICATION, "2 arguments expected, when the first argument is "
157 "not 'all'.");
158 return;
159 }
160 // delete all breakpoints if we got this far
161 if (breakpoints.empty()) {
162 print(DRET_NOTIFICATION, "No breakpoints found.");
163 }
164 else {
165 for (size_t i = 0; i < breakpoints.size(); ++i) {
166 Free(breakpoints[i].module);
167 Free(breakpoints[i].batch_file);
168 }
169 breakpoints.clear();
170 print(DRET_SETTING_CHANGE, "Removed all breakpoints.");
171 }
172 }
173
174 void TTCN3_Debugger::set_automatic_breakpoint(const char* p_event_str,
175 const char* p_state_str,
176 const char* p_batch_file)
177 {
178 bool new_state;
179 if (!strcmp(p_state_str, "on")) {
180 new_state = true;
181 }
182 else if(!strcmp(p_state_str, "off")) {
183 new_state = false;
184 }
185 else {
186 print(DRET_NOTIFICATION, "Argument 2 is invalid. Expected 'on' or 'off'.");
187 return;
188 }
189 const char* sbp_type_str;
190 bool state_changed;
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";
197 }
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";
203 }
204 else {
205 print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'error' or 'fail'.");
206 return;
207 }
208 if (state_changed) {
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) ? "'" : "");
214 }
215 else {
216 if (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);
222 }
223 else {
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,
226 sbp_type_str);
227 }
228 }
229 else {
230 print(DRET_SETTING_CHANGE, "Batch file '%s' removed from automatic "
231 "breakpoint at %s.", *old_batch_file_ptr, sbp_type_str);
232 }
233 }
234 else {
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);
238 }
239 else {
240 print(DRET_NOTIFICATION, "Automatic breakpoint at %s was already "
241 "switched on with no batch file.", sbp_type_str);
242 }
243 }
244 }
245 else {
246 print(DRET_NOTIFICATION, "Automatic breakpoint at %s was already switched off.");
247 }
248 }
249 Free(*old_batch_file_ptr);
250 *old_batch_file_ptr = p_batch_file != NULL ? mcopystr(p_batch_file) : NULL;
251 }
252
253 void TTCN3_Debugger::print_settings()
254 {
255 // on/off switch
256 add_to_result("Debugger is switched %s.\n", active ? "on" : "off");
257 // output
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 : "");
265 Free(file_str);
266 // global batch file
267 add_to_result("Global batch file%s%s.\n", global_batch_file != NULL ? ": " : "",
268 global_batch_file != NULL ? global_batch_file : " not set");
269 // user breakpoints
270 if (breakpoints.empty()) {
271 add_to_result("No user breakpoints.\n");
272 }
273 else {
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 : "");
279 }
280 }
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 : "");
287 }
288
289 #define STACK_LEVEL (stack_level >= 0) ? (size_t)stack_level : (call_stack.size() - 1)
290
291 void TTCN3_Debugger::print_call_stack()
292 {
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
298 add_to_result("*");
299 }
300 if (i != 1) {
301 add_to_result("\n");
302 }
303 }
304 }
305
306 void TTCN3_Debugger::set_stack_level(int new_level)
307 {
308 if (!halted) {
309 print(DRET_NOTIFICATION, "Stack level can only be set if test execution is halted.");
310 }
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());
314 }
315 else {
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;
321 }
322 }
323
324 void TTCN3_Debugger::print_variable(const char* p_var_name)
325 {
326 const variable_t* var = call_stack[STACK_LEVEL].function->find_variable(p_var_name);
327 if (var != NULL) {
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));
331 }
332 else {
333 add_to_result("Variable '%s' not found.", p_var_name);
334 }
335 }
336
337 void TTCN3_Debugger::overwrite_variable(const char* p_var_name,
338 int p_value_element_count,
339 char** p_value_elements)
340 {
341 variable_t* var = call_stack[STACK_LEVEL].function->find_variable(p_var_name);
342 if (var != NULL) {
343 if (var->set_function == NULL) {
344 print(DRET_NOTIFICATION, "Constant variables cannot be overwritten.");
345 }
346 else {
347 char* new_value_str = NULL;
348 for (int i = 0; i < p_value_element_count; ++i) {
349 if (i != 0) {
350 new_value_str = mputc(new_value_str, ' ');
351 }
352 new_value_str = mputstr(new_value_str, p_value_elements[i]);
353 }
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) {
357 try {
358 Debugger_Value_Parsing debug_value_parsing;
359 boolean handled = var->set_function(*var, *parsed_mp);
360 if (!handled) {
361 print(DRET_NOTIFICATION, "Variables of type '%s' cannot be overwritten.",
362 var->type_name);
363 }
364 else {
365 add_to_result("[%s] %s := %s", var->type_name, var->name,
366 (const char*)var->print_function(*var));
367 }
368 }
369 catch (const TC_Error&) {
370 // do nothing, an error message has already been displayed in this case
371 }
372 delete parsed_mp;
373 }
374 }
375 }
376 else {
377 print(DRET_NOTIFICATION, "Variable '%s' not found.", p_var_name);
378 }
379 }
380
381 void TTCN3_Debugger::set_output(const char* p_output_type, const char* p_file_name)
382 {
383 FILE* new_fp = NULL;
384 bool file, console;
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")) {
389 file = false;
390 console = true;
391 }
392 else if (!strcmp(p_output_type, "file")) {
393 file = true;
394 console = false;
395 }
396 else if (!strcmp(p_output_type, "both")) {
397 file = true;
398 console = true;
399 }
400 else {
401 print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'console', 'file' or 'both'.");
402 return;
403 }
404 if (file) {
405 if (p_file_name == NULL) {
406 print(DRET_NOTIFICATION, "Argument 2 (output file name) is missing.");
407 return;
408 }
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
411 same_file = true;
412 }
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);
420 return;
421 }
422 }
423 }
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 : "");
430 if (file) {
431 Free(file_str);
432 }
433 if (!same_file && !TTCN_Runtime::is_hc()) {
434 if (output_file != NULL) {
435 fclose(output_file);
436 }
437 output_file = new_fp;
438 }
439 send_to_console = console;
440 Free(output_file_name);
441 if (file) {
442 output_file_name = mcopystr(p_file_name);
443 }
444 }
445
446 void TTCN3_Debugger::set_global_batch_file(const char* p_state_str,
447 const char* p_file_name)
448 {
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);
457 }
458 else {
459 print(DRET_SETTING_CHANGE, "Global batch file changed from '%s' to '%s'.",
460 global_batch_file, p_file_name);
461 delete_old = true;
462 copy_new = true;
463 }
464 }
465 else {
466 print(DRET_SETTING_CHANGE, "Global batch file switched on and set to '%s'.",
467 p_file_name);
468 copy_new = true;
469 }
470 }
471 else {
472 print(DRET_NOTIFICATION, "Missing batch file name argument.");
473 }
474 }
475 else if (!strcmp(p_state_str, "off")) {
476 if (global_batch_file != NULL) {
477 print(DRET_SETTING_CHANGE, "Global batch file switched off.");
478 delete_old = true;
479 }
480 else {
481 print(DRET_NOTIFICATION, "Global batch file was already switched off.");
482 }
483 }
484 else {
485 print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'on' or 'off'.");
486 }
487 if (delete_old) {
488 Free(global_batch_file);
489 global_batch_file = NULL;
490 }
491 if (copy_new) {
492 global_batch_file = mcopystr(p_file_name);
493 }
494 }
495
496 void TTCN3_Debugger::step(stepping_t p_stepping_type)
497 {
498 if (!halted) {
499 print(DRET_NOTIFICATION, "Stepping commands can only be used when test "
500 "execution is halted.");
501 return;
502 }
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();
507 }
508 resume();
509 }
510
511 void TTCN3_Debugger::run_to_cursor(const char* p_module, int p_line)
512 {
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
515 if (!halted) {
516 print(DRET_NOTIFICATION, "The 'run to' command can only be used when test "
517 "execution is halted.");
518 return;
519 }
520 temporary_breakpoint.module = mcopystr(p_module);
521 temporary_breakpoint.line = p_line;
522 resume();
523 }
524
525 void TTCN3_Debugger::halt(const char* p_batch_file, bool p_run_global_batch)
526 {
527 if (!halted) {
528 halted = true;
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);
539 }
540 else {
541 TTCN_Communication::send_debug_batch(p_batch_file);
542 }
543 }
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);
547 }
548 else {
549 TTCN_Communication::send_debug_batch(global_batch_file);
550 }
551 }
552 if (TTCN_Runtime::is_single()) {
553 if (halted && !halt_at_start) {
554 resume();
555 }
556 else {
557 TTCN_Debugger_UI::read_loop();
558 }
559 }
560 else {
561 TTCN_Communication::process_debug_messages();
562 }
563 }
564 }
565 else {
566 print(DRET_NOTIFICATION, "Test execution is already halted.");
567 }
568 }
569
570 void TTCN3_Debugger::resume()
571 {
572 if (halted) {
573 halted = false;
574 stack_level = -1;
575 print(DRET_NOTIFICATION, "Test execution resumed.");
576 }
577 else {
578 print(DRET_NOTIFICATION, "Test execution is not halted.");
579 }
580 }
581
582 void TTCN3_Debugger::exit_(const char* p_what)
583 {
584 if (!strcmp(p_what, "test")) {
585 exiting = false;
586 }
587 else if (!strcmp(p_what, "all")) {
588 exiting = true;
589 }
590 else {
591 print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'test' or 'all'.");
592 return;
593 }
594 halted = false;
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();
599 }
600 }
601
602 size_t TTCN3_Debugger::find_breakpoint(const char* p_module, int p_line) const
603 {
604 for (size_t i = 0; i < breakpoints.size(); ++i) {
605 if (!strcmp(breakpoints[i].module, p_module) && breakpoints[i].line == p_line) {
606 return i;
607 }
608 }
609 return breakpoints.size();
610 }
611
612 TTCN3_Debugger::variable_t* TTCN3_Debugger::find_variable(const void* p_value) const
613 {
614 for (size_t i = 0; i < variables.size(); ++i) {
615 if (variables[i]->value == p_value) {
616 return variables[i];
617 }
618 }
619 return NULL;
620 }
621
622 char* TTCN3_Debugger::finalize_file_name(const char* p_file_name_skeleton)
623 {
624 if (p_file_name_skeleton == NULL) {
625 return NULL;
626 }
627 size_t len = strlen(p_file_name_skeleton);
628 size_t next_idx = 0;
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());
636 break;
637 case 'h': // %h -> host name
638 ret_val = mputstr(ret_val, TTCN_Runtime::get_host_name());
639 break;
640 case 'p': // %p -> process ID
641 ret_val = mputprintf(ret_val, "%ld", (long)getpid());
642 break;
643 case 'l': { // %l -> login name
644 setpwent();
645 struct passwd *p = getpwuid(getuid());
646 if (NULL != p) {
647 ret_val = mputstr(ret_val, p->pw_name);
648 }
649 endpwent();
650 break; }
651 case 'r': // %r -> component reference
652 if (TTCN_Runtime::is_single()) {
653 ret_val = mputstr(ret_val, "single");
654 }
655 else if (TTCN_Runtime::is_mtc()) {
656 ret_val = mputstr(ret_val, "mtc");
657 }
658 else if (TTCN_Runtime::is_ptc()) {
659 ret_val = mputprintf(ret_val, "%d", (component)self);
660 }
661 break;
662 case 'n': // %n -> component name
663 if (TTCN_Runtime::is_mtc()) {
664 ret_val = mputstr(ret_val, "MTC");
665 }
666 else if (TTCN_Runtime::is_ptc()) {
667 ret_val = mputstr(ret_val, TTCN_Runtime::get_component_name());
668 }
669 break;
670 case '%': // %% -> single %
671 ret_val = mputc(ret_val, '%');
672 break;
673 default: // unknown sequence -> leave it as it is
674 ret_val = mputstrn(ret_val, p_file_name_skeleton + i, 2);
675 break;
676 }
677 next_idx = i + 2;
678 ++i;
679 }
680 }
681 if (next_idx < len) {
682 ret_val = mputstr(ret_val, p_file_name_skeleton + next_idx);
683 }
684 return ret_val;
685 }
686
687 void TTCN3_Debugger::test_execution_started()
688 {
689 Free(snapshots);
690 snapshots = NULL;
691 exiting = false;
692 if (TTCN_Runtime::is_single()) {
693 TTCN_Debugger_UI::init();
694 if (initial_batch_file) {
695 halt(initial_batch_file, false);
696 }
697 else if (halt_at_start) {
698 halt(NULL, false);
699 }
700 }
701 halt_at_start = true;
702 }
703
704 void TTCN3_Debugger::test_execution_finished()
705 {
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();
714 }
715 }
716
717 void TTCN3_Debugger::print(int return_type, const char* fmt, ...) const
718 {
719 if (TTCN_Runtime::is_hc()) {
720 // don't display anything while on the HC process
721 return;
722 }
723 va_list parameters;
724 va_start(parameters, fmt);
725 char* str = mprintf_va_list(fmt, parameters);
726 va_end(parameters);
727 if (TTCN_Runtime::is_single()) {
728 if (send_to_console) {
729 TTCN_Debugger_UI::print(str);
730 }
731 }
732 else {
733 TTCN_Communication::send_debug_return_value(return_type, send_to_console ? str : NULL);
734 }
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);
739 fflush(output_file);
740 }
741 Free(str);
742 }
743
744 TTCN3_Debugger::TTCN3_Debugger()
745 {
746 enabled = false;
747 active = false;
748 halted = false;
749 output_file = NULL;
750 output_file_name = NULL;
751 send_to_console = true;
752 snapshots = NULL;
753 last_breakpoint_entry.module = NULL;
754 last_breakpoint_entry.line = 0;
755 last_breakpoint_entry.batch_file = NULL; // not used
756 stack_level = -1;
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
769 exiting = false;
770 halt_at_start = false;
771 initial_batch_file = NULL;
772 }
773
774 TTCN3_Debugger::~TTCN3_Debugger()
775 {
776 if (output_file != NULL) {
777 fclose(output_file);
778 Free(output_file_name);
779 }
780 for (size_t i = 0; i < breakpoints.size(); ++i) {
781 Free(breakpoints[i].module);
782 Free(breakpoints[i].batch_file);
783 }
784 for (size_t i = 0; i < global_scopes.size(); ++i) {
785 delete global_scopes[i].scope;
786 }
787 for (size_t i = 0; i < component_scopes.size(); ++i) {
788 delete component_scopes[i].scope;
789 }
790 for (size_t i = 0; i < variables.size(); ++i) {
791 delete variables[i];
792 }
793 Free(fail_behavior.batch_file);
794 Free(error_behavior.batch_file);
795 Free(global_batch_file);
796 Free(snapshots);
797 Free(last_variable_list);
798 }
799
800 TTCN3_Debug_Scope* TTCN3_Debugger::add_global_scope(const char* p_module)
801 {
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;
807 }
808
809 TTCN3_Debug_Scope* TTCN3_Debugger::add_component_scope(const char* p_component)
810 {
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;
816 }
817
818 void TTCN3_Debugger::set_return_value(const CHARSTRING& p_value)
819 {
820 if (active && !call_stack.empty()) {
821 call_stack[call_stack.size() - 1].function->set_return_value(p_value);
822 }
823 }
824
825 void TTCN3_Debugger::breakpoint_entry(int p_line)
826 {
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;
831 int actual_line;
832 const char* batch_file = NULL;
833 switch (p_line) {
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;
839 break;
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;
845 break;
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) {
850 break;
851 }
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)) {
857 trigger = true;
858 trigger_type = "Stepped to";
859 break;
860 }
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)) {
864 trigger = true;
865 trigger_type = "Ran to";
866 break;
867 }
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()) {
871 trigger = true;
872 batch_file = breakpoints[pos].batch_file;
873 }
874 trigger_type = "User breakpoint reached at";
875 break;
876 }
877 if (trigger) {
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();
882 }
883 halt(batch_file, true);
884 }
885 last_breakpoint_entry.module = (char*)module_name;
886 last_breakpoint_entry.line = p_line;
887 }
888 }
889
890 CHARSTRING TTCN3_Debugger::print_base_var(const TTCN3_Debugger::variable_t& p_var)
891 {
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();
896 }
897 else if (!strcmp(p_var.type_name, "bitstring template")) {
898 ((const BITSTRING_template*)ptr)->log();
899 }
900 else if (!strcmp(p_var.type_name, "boolean")) {
901 ((const BOOLEAN*)ptr)->log();
902 }
903 else if (!strcmp(p_var.type_name, "boolean template")) {
904 ((const BOOLEAN_template*)ptr)->log();
905 }
906 else if (!strcmp(p_var.type_name, "charstring")) {
907 ((const CHARSTRING*)ptr)->log();
908 }
909 else if (!strcmp(p_var.type_name, "charstring template")) {
910 ((const CHARSTRING_template*)ptr)->log();
911 }
912 else if (!strcmp(p_var.type_name, "float")) {
913 ((const FLOAT*)ptr)->log();
914 }
915 else if (!strcmp(p_var.type_name, "float template")) {
916 ((const FLOAT_template*)ptr)->log();
917 }
918 else if (!strcmp(p_var.type_name, "hexstring")) {
919 ((const HEXSTRING*)ptr)->log();
920 }
921 else if (!strcmp(p_var.type_name, "hexstring template")) {
922 ((const HEXSTRING_template*)ptr)->log();
923 }
924 else if (!strcmp(p_var.type_name, "integer")) {
925 ((const INTEGER*)ptr)->log();
926 }
927 else if (!strcmp(p_var.type_name, "integer template")) {
928 ((const INTEGER_template*)ptr)->log();
929 }
930 else if (!strcmp(p_var.type_name, "objid")) {
931 ((const OBJID*)ptr)->log();
932 }
933 else if (!strcmp(p_var.type_name, "objid template")) {
934 ((const OBJID_template*)ptr)->log();
935 }
936 else if (!strcmp(p_var.type_name, "octetstring")) {
937 ((const OCTETSTRING*)ptr)->log();
938 }
939 else if (!strcmp(p_var.type_name, "octetstring template")) {
940 ((const OCTETSTRING_template*)ptr)->log();
941 }
942 else if (!strcmp(p_var.type_name, "universal charstring")) {
943 ((const UNIVERSAL_CHARSTRING*)ptr)->log();
944 }
945 else if (!strcmp(p_var.type_name, "universal charstring template")) {
946 ((const UNIVERSAL_CHARSTRING_template*)ptr)->log();
947 }
948 else if (!strcmp(p_var.type_name, "verdicttype")) {
949 ((const VERDICTTYPE*)ptr)->log();
950 }
951 else if (!strcmp(p_var.type_name, "verdicttype template")) {
952 ((const VERDICTTYPE_template*)ptr)->log();
953 }
954 else if (!strcmp(p_var.type_name, "component")) {
955 ((const COMPONENT*)ptr)->log();
956 }
957 else if (!strcmp(p_var.type_name, "component template")) {
958 ((const COMPONENT_template*)ptr)->log();
959 }
960 else if (!strcmp(p_var.type_name, "default")) {
961 ((const DEFAULT*)ptr)->log();
962 }
963 else if (!strcmp(p_var.type_name, "default template")) {
964 ((const DEFAULT_template*)ptr)->log();
965 }
966 else if (!strcmp(p_var.type_name, "timer")) {
967 ((const TIMER*)ptr)->log();
968 }
969 else if (!strcmp(p_var.type_name, "NULL")) {
970 ((const ASN_NULL*)ptr)->log();
971 }
972 else if (!strcmp(p_var.type_name, "NULL template")) {
973 ((const ASN_NULL_template*)ptr)->log();
974 }
975 else if (!strcmp(p_var.type_name, "CHARACTER STRING")) {
976 ((const CHARACTER_STRING*)ptr)->log();
977 }
978 else if (!strcmp(p_var.type_name, "CHARACTER STRING template")) {
979 ((const CHARACTER_STRING_template*)ptr)->log();
980 }
981 else if (!strcmp(p_var.type_name, "EMBEDDED PDV")) {
982 ((const EMBEDDED_PDV*)ptr)->log();
983 }
984 else if (!strcmp(p_var.type_name, "EMBEDDED PDV template")) {
985 ((const EMBEDDED_PDV_template*)ptr)->log();
986 }
987 else if (!strcmp(p_var.type_name, "EXTERNAL")) {
988 ((const EXTERNAL*)ptr)->log();
989 }
990 else if (!strcmp(p_var.type_name, "EXTERNAL template")) {
991 ((const EXTERNAL_template*)ptr)->log();
992 }
993 else {
994 TTCN_Logger::log_event_str("<unrecognized value or template>");
995 }
996 return TTCN_Logger::end_event_log2str();
997 }
998
999 boolean TTCN3_Debugger::set_base_var(variable_t& p_var, Module_Param& p_new_value)
1000 {
1001 if (!strcmp(p_var.type_name, "bitstring")) {
1002 ((BITSTRING*)p_var.value)->set_param(p_new_value);
1003 }
1004 else if (!strcmp(p_var.type_name, "bitstring template")) {
1005 ((BITSTRING_template*)p_var.value)->set_param(p_new_value);
1006 }
1007 else if (!strcmp(p_var.type_name, "boolean")) {
1008 ((BOOLEAN*)p_var.value)->set_param(p_new_value);
1009 }
1010 else if (!strcmp(p_var.type_name, "boolean template")) {
1011 ((BOOLEAN_template*)p_var.value)->set_param(p_new_value);
1012 }
1013 else if (!strcmp(p_var.type_name, "charstring")) {
1014 ((CHARSTRING*)p_var.value)->set_param(p_new_value);
1015 }
1016 else if (!strcmp(p_var.type_name, "charstring template")) {
1017 ((CHARSTRING_template*)p_var.value)->set_param(p_new_value);
1018 }
1019 else if (!strcmp(p_var.type_name, "float")) {
1020 ((FLOAT*)p_var.value)->set_param(p_new_value);
1021 }
1022 else if (!strcmp(p_var.type_name, "float template")) {
1023 ((FLOAT_template*)p_var.value)->set_param(p_new_value);
1024 }
1025 else if (!strcmp(p_var.type_name, "hexstring")) {
1026 ((HEXSTRING*)p_var.value)->set_param(p_new_value);
1027 }
1028 else if (!strcmp(p_var.type_name, "hexstring template")) {
1029 ((HEXSTRING_template*)p_var.value)->set_param(p_new_value);
1030 }
1031 else if (!strcmp(p_var.type_name, "integer")) {
1032 ((INTEGER*)p_var.value)->set_param(p_new_value);
1033 }
1034 else if (!strcmp(p_var.type_name, "integer template")) {
1035 ((INTEGER_template*)p_var.value)->set_param(p_new_value);
1036 }
1037 else if (!strcmp(p_var.type_name, "objid")) {
1038 ((OBJID*)p_var.value)->set_param(p_new_value);
1039 }
1040 else if (!strcmp(p_var.type_name, "objid template")) {
1041 ((OBJID_template*)p_var.value)->set_param(p_new_value);
1042 }
1043 else if (!strcmp(p_var.type_name, "octetstring")) {
1044 ((OCTETSTRING*)p_var.value)->set_param(p_new_value);
1045 }
1046 else if (!strcmp(p_var.type_name, "octetstring template")) {
1047 ((OCTETSTRING_template*)p_var.value)->set_param(p_new_value);
1048 }
1049 else if (!strcmp(p_var.type_name, "universal charstring")) {
1050 ((UNIVERSAL_CHARSTRING*)p_var.value)->set_param(p_new_value);
1051 }
1052 else if (!strcmp(p_var.type_name, "universal charstring template")) {
1053 ((UNIVERSAL_CHARSTRING_template*)p_var.value)->set_param(p_new_value);
1054 }
1055 else if (!strcmp(p_var.type_name, "verdicttype")) {
1056 ((VERDICTTYPE*)p_var.value)->set_param(p_new_value);
1057 }
1058 else if (!strcmp(p_var.type_name, "verdicttype template")) {
1059 ((VERDICTTYPE_template*)p_var.value)->set_param(p_new_value);
1060 }
1061 else if (!strcmp(p_var.type_name, "component")) {
1062 ((COMPONENT*)p_var.value)->set_param(p_new_value);
1063 }
1064 else if (!strcmp(p_var.type_name, "component template")) {
1065 ((COMPONENT_template*)p_var.value)->set_param(p_new_value);
1066 }
1067 else if (!strcmp(p_var.type_name, "default")) {
1068 ((DEFAULT*)p_var.value)->set_param(p_new_value);
1069 }
1070 else if (!strcmp(p_var.type_name, "default template")) {
1071 ((DEFAULT_template*)p_var.value)->set_param(p_new_value);
1072 }
1073 else if (!strcmp(p_var.type_name, "NULL")) {
1074 ((ASN_NULL*)p_var.value)->set_param(p_new_value);
1075 }
1076 else if (!strcmp(p_var.type_name, "NULL template")) {
1077 ((ASN_NULL_template*)p_var.value)->set_param(p_new_value);
1078 }
1079 else if (!strcmp(p_var.type_name, "CHARACTER STRING")) {
1080 ((CHARACTER_STRING*)p_var.value)->set_param(p_new_value);
1081 }
1082 else if (!strcmp(p_var.type_name, "CHARACTER STRING template")) {
1083 ((CHARACTER_STRING_template*)p_var.value)->set_param(p_new_value);
1084 }
1085 else if (!strcmp(p_var.type_name, "EMBEDDED PDV")) {
1086 ((EMBEDDED_PDV*)p_var.value)->set_param(p_new_value);
1087 }
1088 else if (!strcmp(p_var.type_name, "EMBEDDED PDV template")) {
1089 ((EMBEDDED_PDV_template*)p_var.value)->set_param(p_new_value);
1090 }
1091 else if (!strcmp(p_var.type_name, "EXTERNAL")) {
1092 ((EXTERNAL*)p_var.value)->set_param(p_new_value);
1093 }
1094 else if (!strcmp(p_var.type_name, "EXTERNAL template")) {
1095 ((EXTERNAL_template*)p_var.value)->set_param(p_new_value);
1096 }
1097 else {
1098 return FALSE;
1099 }
1100 return TRUE;
1101 }
1102
1103 void TTCN3_Debugger::add_to_result(const char* fmt, ...)
1104 {
1105 va_list parameters;
1106 va_start(parameters, fmt);
1107 command_result = mputprintf_va_list(command_result, fmt, parameters);
1108 va_end(parameters);
1109 }
1110
1111 void TTCN3_Debugger::add_function(TTCN3_Debug_Function* p_function)
1112 {
1113 function_call_t function_call;
1114 if (call_stack.empty()) {
1115 test_execution_started();
1116 function_call.caller_line = 0;
1117 }
1118 else {
1119 function_call.caller_line = last_breakpoint_entry.line;
1120 }
1121 function_call.function = p_function;
1122 call_stack.push_back(function_call);
1123 }
1124
1125 void TTCN3_Debugger::add_scope(TTCN3_Debug_Scope* p_scope)
1126 {
1127 if (active && !call_stack.empty()) {
1128 call_stack[call_stack.size() - 1].function->add_scope(p_scope);
1129 }
1130 }
1131
1132 void TTCN3_Debugger::remove_function(TTCN3_Debug_Function* p_function)
1133 {
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();
1140 }
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);
1144 }
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
1150 throw TC_End();
1151 }
1152 }
1153 }
1154
1155 void TTCN3_Debugger::remove_scope(TTCN3_Debug_Scope* p_scope)
1156 {
1157 if (!call_stack.empty()) {
1158 call_stack[call_stack.size() - 1].function->remove_scope(p_scope);
1159 }
1160 }
1161
1162 TTCN3_Debugger::variable_t* TTCN3_Debugger::add_variable(const void* p_value,
1163 const char* p_name,
1164 const char* p_type,
1165 const char* p_module,
1166 TTCN3_Debugger::print_function_t p_print_function)
1167 {
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);
1171 if (var == NULL) {
1172 var = new TTCN3_Debugger::variable_t;
1173 var->cvalue = p_value;
1174 var->name = p_name;
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);
1180 }
1181 return var;
1182 }
1183 else if (active) {
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);
1187 }
1188 return NULL;
1189 }
1190
1191 TTCN3_Debugger::variable_t* TTCN3_Debugger::add_variable(void* p_value,
1192 const char* p_name,
1193 const char* p_type,
1194 const char* p_module,
1195 TTCN3_Debugger::print_function_t p_print_function,
1196 TTCN3_Debugger::set_function_t p_set_function)
1197 {
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);
1201 if (var == NULL) {
1202 var = new TTCN3_Debugger::variable_t;
1203 var->value = p_value;
1204 var->name = p_name;
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);
1210 }
1211 return var;
1212 }
1213 else if (active) {
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);
1217 }
1218 return NULL;
1219 }
1220
1221 void TTCN3_Debugger::remove_variable(const variable_t* p_var)
1222 {
1223 if (active && !call_stack.empty()) {
1224 call_stack[call_stack.size() - 1].function->remove_variable(p_var);
1225 }
1226 }
1227
1228 const TTCN3_Debug_Scope* TTCN3_Debugger::get_global_scope(const char* p_module) const
1229 {
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;
1233 }
1234 }
1235 return NULL;
1236 }
1237
1238 const TTCN3_Debug_Scope* TTCN3_Debugger::get_component_scope(const char* p_component) const
1239 {
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;
1243 }
1244 }
1245 return NULL;
1246 }
1247
1248 void TTCN3_Debugger::add_snapshot(const char* p_snapshot)
1249 {
1250 if (snapshots != NULL) {
1251 snapshots = mputc(snapshots, '\n');
1252 }
1253 snapshots = mputstr(snapshots, p_snapshot);
1254 }
1255
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); \
1260 return; \
1261 }
1262
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); \
1267 return; \
1268 }
1269
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); \
1274 return; \
1275 }
1276
1277 #define CHECK_INT_ARGUMENT(arg_idx) \
1278 { \
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)); \
1283 return; \
1284 } \
1285 } \
1286 }
1287
1288 #define CHECK_CALL_STACK(print_msg) \
1289 if (!active) { \
1290 if (print_msg) { \
1291 print(DRET_NOTIFICATION, "This command can only be used if the debugger " \
1292 "is switched on."); \
1293 } \
1294 return; \
1295 } \
1296 if (call_stack.empty()) { \
1297 if (print_msg) { \
1298 print(DRET_NOTIFICATION, "This command can only be used if the debugger's " \
1299 "call stack is not empty."); \
1300 } \
1301 return; \
1302 }
1303
1304 void TTCN3_Debugger::execute_command(int p_command, int p_argument_count,
1305 char** p_arguments)
1306 {
1307 if (!enabled) {
1308 return;
1309 }
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);
1313 return;
1314 }
1315 }
1316 switch (p_command) {
1317 case D_SWITCH:
1318 CHECK_NOF_ARGUMENTS(1)
1319 switch_state(p_arguments[0]);
1320 break;
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);
1326 break;
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);
1330 break;
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);
1335 break;
1336 case D_SET_OUTPUT:
1337 CHECK_NOF_ARGUMENTS_RANGE(1, 2)
1338 set_output(p_arguments[0], (p_argument_count == 2) ? p_arguments[1] : NULL);
1339 break;
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);
1343 break;
1344 case D_PRINT_SETTINGS:
1345 CHECK_NOF_ARGUMENTS(0)
1346 print_settings();
1347 break;
1348 case D_PRINT_CALL_STACK:
1349 CHECK_CALL_STACK(true)
1350 CHECK_NOF_ARGUMENTS(0)
1351 print_call_stack();
1352 break;
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]));
1358 break;
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);
1364 break;
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) {
1369 if (i != 0) {
1370 add_to_result("\n");
1371 }
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);
1377 size_t start = 0;
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);
1383 Free(var_name);
1384 add_to_result("\n");
1385 start = j + 1;
1386 }
1387 }
1388 // extract the last (or only) variable name
1389 char* var_name = mcopystrn(last_variable_list + start, len - start);
1390 print_variable(var_name);
1391 Free(var_name);
1392 }
1393 else {
1394 add_to_result("No previous " D_LIST_VARIABLES_TEXT " result.");
1395 }
1396 }
1397 else {
1398 print_variable(p_arguments[i]);
1399 }
1400 }
1401 break;
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);
1406 break;
1407 case D_PRINT_SNAPSHOTS:
1408 CHECK_NOF_ARGUMENTS(0)
1409 add_to_result("%s", snapshots);
1410 break;
1411 case D_STEP_OVER:
1412 CHECK_CALL_STACK(true)
1413 CHECK_NOF_ARGUMENTS(0)
1414 step(STEP_OVER);
1415 break;
1416 case D_STEP_INTO:
1417 CHECK_CALL_STACK(true)
1418 CHECK_NOF_ARGUMENTS(0)
1419 step(STEP_INTO);
1420 break;
1421 case D_STEP_OUT:
1422 CHECK_CALL_STACK(true)
1423 CHECK_NOF_ARGUMENTS(0)
1424 step(STEP_OUT);
1425 break;
1426 case D_RUN_TO_CURSOR:
1427 if (!TTCN_Runtime::is_hc() && !TTCN_Runtime::is_single()) {
1428 CHECK_CALL_STACK(TTCN_Runtime::is_mtc())
1429 }
1430 CHECK_NOF_ARGUMENTS(2)
1431 CHECK_INT_ARGUMENT(1)
1432 run_to_cursor(p_arguments[0], str2int(p_arguments[1]));
1433 break;
1434 case D_HALT:
1435 if (!TTCN_Runtime::is_hc() && !TTCN_Runtime::is_single()) {
1436 CHECK_CALL_STACK(TTCN_Runtime::is_mtc())
1437 }
1438 CHECK_NOF_ARGUMENTS(0)
1439 halt(NULL, false);
1440 break;
1441 case D_CONTINUE:
1442 CHECK_NOF_ARGUMENTS(0)
1443 resume();
1444 break;
1445 case D_EXIT:
1446 if (!TTCN_Runtime::is_hc() && !TTCN_Runtime::is_single()) {
1447 CHECK_CALL_STACK(TTCN_Runtime::is_mtc())
1448 }
1449 CHECK_NOF_ARGUMENTS(1)
1450 exit_(p_arguments[0]);
1451 break;
1452 case D_SETUP:
1453 CHECK_NOF_ARGUMENTS_MIN(5)
1454 if (strlen(p_arguments[0]) > 0) {
1455 switch_state(p_arguments[0]);
1456 }
1457 if (strlen(p_arguments[1]) > 0) {
1458 set_output(p_arguments[1], p_arguments[2]);
1459 }
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);
1463 }
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);
1467 }
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);
1471 }
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);
1475 }
1476 break;
1477 default:
1478 print(DRET_NOTIFICATION, "Invalid command received (ID: %d).", p_command);
1479 return;
1480 }
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;
1486 }
1487 else {
1488 Free(command_result);
1489 }
1490 command_result = NULL;
1491 }
1492 }
1493
1494 void TTCN3_Debugger::open_output_file()
1495 {
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);
1501 }
1502 Free(final_file_name);
1503 }
1504 }
1505
1506 //////////////////////////////////////////////////////
1507 //////////////// TTCN3_Debug_Scope ///////////////////
1508 //////////////////////////////////////////////////////
1509
1510 TTCN3_Debug_Scope::TTCN3_Debug_Scope()
1511 {
1512 ttcn3_debugger.add_scope(this);
1513 }
1514
1515 TTCN3_Debug_Scope::~TTCN3_Debug_Scope()
1516 {
1517 for (size_t i = 0; i < variables.size(); ++i) {
1518 ttcn3_debugger.remove_variable(variables[i]);
1519 }
1520 ttcn3_debugger.remove_scope(this);
1521 }
1522
1523 void TTCN3_Debug_Scope::add_variable(const void* p_value,
1524 const char* p_name,
1525 const char* p_type,
1526 const char* p_module,
1527 TTCN3_Debugger::print_function_t p_print_function)
1528 {
1529 TTCN3_Debugger::variable_t* var = ttcn3_debugger.add_variable(p_value, p_name,
1530 p_type, p_module, p_print_function);
1531 if (var != NULL) {
1532 variables.push_back(var);
1533 }
1534 }
1535
1536 void TTCN3_Debug_Scope::add_variable(void* p_value,
1537 const char* p_name,
1538 const char* p_type,
1539 const char* p_module,
1540 TTCN3_Debugger::print_function_t p_print_function,
1541 TTCN3_Debugger::set_function_t p_set_function)
1542 {
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);
1545 if (var != NULL) {
1546 variables.push_back(var);
1547 }
1548 }
1549
1550 TTCN3_Debugger::variable_t* TTCN3_Debug_Scope::find_variable(const char* p_name) const
1551 {
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
1556 return var;
1557 }
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
1566 return var;
1567 }
1568 }
1569 }
1570 return NULL;
1571 }
1572
1573 void TTCN3_Debug_Scope::list_variables(regex_t* p_posix_regexp, bool& p_first,
1574 const char* p_module) const
1575 {
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);
1582 p_first = false;
1583 }
1584 }
1585 }
1586
1587 //////////////////////////////////////////////////////
1588 /////////////// TTCN3_Debug_Function /////////////////
1589 //////////////////////////////////////////////////////
1590
1591 TTCN3_Debug_Function::TTCN3_Debug_Function(const char* p_name,
1592 const char* p_type,
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))
1600 {
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
1607 }
1608 }
1609
1610 TTCN3_Debug_Function::~TTCN3_Debug_Function()
1611 {
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) {
1616 if (i > 0) {
1617 snapshot = mputstr(snapshot, ", ");
1618 }
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));
1624 }
1625 else {
1626 snapshot = mputc(snapshot, '-');
1627 }
1628 }
1629 }
1630 snapshot = mputc(snapshot, ')');
1631 if (return_value.is_bound()) {
1632 snapshot = mputprintf(snapshot, " returned %s", (const char*)return_value);
1633 }
1634 ttcn3_debugger.add_snapshot(snapshot);
1635 Free(snapshot);
1636 }
1637 for (size_t i = 0; i < variables.size(); ++i) {
1638 delete variables[i];
1639 }
1640 delete parameter_names;
1641 delete parameter_types;
1642 ttcn3_debugger.remove_function(this);
1643 }
1644
1645 TTCN3_Debugger::variable_t* TTCN3_Debug_Function::add_variable(const void* p_value,
1646 const char* p_name,
1647 const char* p_type,
1648 const char* p_module,
1649 TTCN3_Debugger::print_function_t p_print_function)
1650 {
1651 if (ttcn3_debugger.is_on()) {
1652 TTCN3_Debugger::variable_t* var = new TTCN3_Debugger::variable_t;
1653 var->cvalue = p_value;
1654 var->name = p_name;
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);
1660 return var;
1661 }
1662 return NULL;
1663 }
1664
1665 TTCN3_Debugger::variable_t* TTCN3_Debug_Function::add_variable(void* p_value,
1666 const char* p_name,
1667 const char* p_type,
1668 const char* p_module,
1669 TTCN3_Debugger::print_function_t p_print_function,
1670 TTCN3_Debugger::set_function_t p_set_function)
1671 {
1672 if (ttcn3_debugger.is_on()) {
1673 TTCN3_Debugger::variable_t* var = new TTCN3_Debugger::variable_t;
1674 var->value = p_value;
1675 var->name = p_name;
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);
1681 return var;
1682 }
1683 return NULL;
1684 }
1685
1686 void TTCN3_Debug_Function::set_return_value(const CHARSTRING& p_value)
1687 {
1688 return_value = p_value;
1689 }
1690
1691 void TTCN3_Debug_Function::initial_snapshot() const
1692 {
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) {
1697 if (i > 0) {
1698 snapshot = mputstr(snapshot, ", ");
1699 }
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));
1705 }
1706 else {
1707 snapshot = mputc(snapshot, '-');
1708 }
1709 }
1710 }
1711 snapshot = mputstr(snapshot, ")");
1712 ttcn3_debugger.add_snapshot(snapshot);
1713 Free(snapshot);
1714 }
1715 }
1716
1717 void TTCN3_Debug_Function::add_scope(TTCN3_Debug_Scope* p_scope)
1718 {
1719 scopes.push_back(p_scope);
1720 }
1721
1722 void TTCN3_Debug_Function::remove_scope(TTCN3_Debug_Scope* p_scope)
1723 {
1724 if (!scopes.empty() && scopes[scopes.size() - 1] == p_scope) {
1725 scopes.erase_at(scopes.size() - 1);
1726 }
1727 }
1728
1729 void TTCN3_Debug_Function::remove_variable(const TTCN3_Debugger::variable_t* p_var)
1730 {
1731 for (size_t i = 0; i < variables.size(); ++i) {
1732 if (variables[i] == p_var) {
1733 variables.erase_at(i);
1734 delete p_var;
1735 break;
1736 }
1737 }
1738 }
1739
1740 TTCN3_Debugger::variable_t* TTCN3_Debug_Function::find_variable(const char* p_name) const
1741 {
1742 for (size_t i = 0; i < variables.size(); ++i) {
1743 if (strcmp(variables[i]->name, p_name) == 0) {
1744 return variables[i];
1745 }
1746 }
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);
1750 if (res != NULL) {
1751 return res;
1752 }
1753 }
1754 return (global_scope != NULL) ? global_scope->find_variable(p_name) : NULL;
1755 }
1756
1757 void TTCN3_Debug_Function::print_function() const
1758 {
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) {
1762 if (i > 0) {
1763 ttcn3_debugger.add_to_result(", ");
1764 }
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));
1768 }
1769 }
1770 ttcn3_debugger.add_to_result(")");
1771 }
1772
1773 void TTCN3_Debug_Function::list_variables(const char* p_scope, const char* p_filter) const
1774 {
1775 bool first = true;
1776 bool list_local = false;
1777 bool list_global = false;
1778 bool list_comp = false;
1779 if (!strcmp(p_scope, "local")) {
1780 list_local = true;
1781 }
1782 else if (!strcmp(p_scope, "global")) {
1783 list_global = true;
1784 }
1785 else if (!strcmp(p_scope, "comp")) {
1786 list_comp = true;
1787 }
1788 else if (!strcmp(p_scope, "all")) {
1789 list_local = true;
1790 list_global = true;
1791 list_comp = true;
1792 }
1793 else {
1794 ttcn3_debugger.print(DRET_NOTIFICATION, "Argument 1 is invalid. "
1795 "Expected 'local', 'global', 'comp' or 'all'.");
1796 return;
1797 }
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.");
1804 return;
1805 }
1806 posix_regexp = new regex_t;
1807 int ret_val = regcomp(posix_regexp, posix_str, REG_EXTENDED | REG_NOSUB);
1808 Free(posix_str);
1809 if (ret_val != 0) {
1810 char msg[512];
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.");
1816 return;
1817 }
1818 }
1819 if (list_local) {
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);
1824 first = false;
1825 }
1826 }
1827 }
1828 if (list_global && global_scope != NULL && global_scope->has_variables()) {
1829 global_scope->list_variables(posix_regexp, first, module_name);
1830 }
1831 if (list_comp && component_scope != NULL && component_scope->has_variables()) {
1832 component_scope->list_variables(posix_regexp, first, NULL);
1833 }
1834 if (first) {
1835 ttcn3_debugger.print(DRET_NOTIFICATION, "No variables found.");
1836 }
1837 if (posix_regexp != NULL) {
1838 regfree(posix_regexp);
1839 delete posix_regexp;
1840 }
1841 }
1842
1843 bool TTCN3_Debug_Function::is_control_part() const
1844 {
1845 return !strcmp(function_type, "control");
1846 }
1847
1848 bool TTCN3_Debug_Function::is_test_case() const
1849 {
1850 return !strcmp(function_type, "testcase");
1851 }
This page took 0.069985 seconds and 5 git commands to generate.