debugger: added help in single mode UI, plus minor fixes
[deliverable/titan.core.git] / core / Debugger.cc
CommitLineData
7329404e
BB
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"
016a1a93
BB
15#include "DebugCommands.hh"
16#include "Communication.hh"
17#include "../common/pattern.hh"
f08ff9ca
BB
18#include "Param_Types.hh"
19#include "DebuggerUI.hh"
016a1a93
BB
20#include <unistd.h>
21#include <pwd.h>
7329404e
BB
22
23//////////////////////////////////////////////////////
24////////////////// TTCN3_Debugger ////////////////////
25//////////////////////////////////////////////////////
26
27TTCN3_Debugger ttcn3_debugger;
28
016a1a93 29void TTCN3_Debugger::switch_state(const char* p_state_str)
7329404e 30{
016a1a93
BB
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 }
7329404e 39 }
016a1a93
BB
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 }
7329404e
BB
48 }
49 else {
f08ff9ca 50 print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'on' or 'off'.");
7329404e 51 }
7329404e
BB
52}
53
f08ff9ca
BB
54void TTCN3_Debugger::set_breakpoint(const char* p_module, int p_line,
55 const char* batch_file)
7329404e 56{
f08ff9ca
BB
57 size_t pos = find_breakpoint(p_module, p_line);
58 if (pos == breakpoints.size()) {
7329404e
BB
59 breakpoint_t bp;
60 bp.module = mcopystr(p_module);
61 bp.line = p_line;
f08ff9ca 62 bp.batch_file = batch_file != NULL ? mcopystr(batch_file) : NULL;
7329404e 63 breakpoints.push_back(bp);
f08ff9ca
BB
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 ? "'" : "");
7329404e
BB
68 }
69 else {
f08ff9ca
BB
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;
7329404e
BB
100 }
101}
102
f08ff9ca 103void TTCN3_Debugger::remove_breakpoint(const char* p_module, const char* p_line)
7329404e 104{
f08ff9ca
BB
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.");
7329404e
BB
163 }
164 else {
f08ff9ca
BB
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.");
7329404e
BB
171 }
172}
173
f08ff9ca
BB
174void TTCN3_Debugger::set_automatic_breakpoint(const char* p_event_str,
175 const char* p_state_str,
176 const char* p_batch_file)
7329404e
BB
177{
178 bool new_state;
f08ff9ca 179 if (!strcmp(p_state_str, "on")) {
7329404e
BB
180 new_state = true;
181 }
f08ff9ca 182 else if(!strcmp(p_state_str, "off")) {
7329404e
BB
183 new_state = false;
184 }
7329404e 185 else {
f08ff9ca 186 print(DRET_NOTIFICATION, "Argument 2 is invalid. Expected 'on' or 'off'.");
7329404e
BB
187 return;
188 }
189 const char* sbp_type_str;
190 bool state_changed;
f08ff9ca
BB
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'.");
7329404e
BB
206 return;
207 }
f08ff9ca
BB
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;
7329404e
BB
251}
252
253void TTCN3_Debugger::print_call_stack()
254{
255 for (size_t i = call_stack.size(); i != 0; --i) {
016a1a93 256 add_to_result("%d.\t", (int)call_stack.size() - (int)i + 1);
f08ff9ca 257 call_stack[i - 1].function->print_function();
016a1a93
BB
258 if (i != 1) {
259 add_to_result("\n");
260 }
7329404e
BB
261 }
262}
263
264void TTCN3_Debugger::set_stack_level(int new_level)
265{
016a1a93
BB
266 if (!halted) {
267 print(DRET_NOTIFICATION, "Stack level can only be set if test execution is halted.");
268 }
269 else if (new_level <= 0 || (size_t)new_level > call_stack.size()) {
270 print(DRET_NOTIFICATION, "Invalid new stack level. Expected 1 - %d.",
271 (int)call_stack.size());
7329404e
BB
272 }
273 else {
016a1a93 274 stack_level = (int)call_stack.size() - new_level;
f08ff9ca 275 call_stack[stack_level].function->print_function();
016a1a93 276 print(DRET_NOTIFICATION, "Stack level set to:\n%d.\t%s", new_level, command_result);
f08ff9ca
BB
277 Free(command_result);
278 command_result = NULL;
279 }
280}
281
282#define STACK_LEVEL (stack_level >= 0) ? (size_t)stack_level : (call_stack.size() - 1)
283
284void TTCN3_Debugger::print_variable(const char* p_var_name)
285{
286 const variable_t* var = call_stack[STACK_LEVEL].function->find_variable(p_var_name);
287 if (var != NULL) {
288 add_to_result("[%s] %s := %s", var->type_name, var->name,
289 (const char*)var->print_function(*var));
290 }
291 else {
292 add_to_result("Variable '%s' not found.", p_var_name);
7329404e
BB
293 }
294}
295
f08ff9ca
BB
296void TTCN3_Debugger::overwrite_variable(const char* p_var_name,
297 int p_value_element_count,
298 char** p_value_elements)
7329404e 299{
f08ff9ca
BB
300 variable_t* var = call_stack[STACK_LEVEL].function->find_variable(p_var_name);
301 if (var != NULL) {
302 if (var->set_function == NULL) {
303 print(DRET_NOTIFICATION, "Constant variables cannot be overwritten.");
304 }
305 else {
306 char* new_value_str = NULL;
307 for (int i = 0; i < p_value_element_count; ++i) {
308 if (i != 0) {
309 new_value_str = mputc(new_value_str, ' ');
310 }
311 new_value_str = mputstr(new_value_str, p_value_elements[i]);
312 }
313 Module_Param* parsed_mp = process_config_debugger_value(new_value_str);
314 // an error message has already been displayed if parsed_mp is NULL
315 if (parsed_mp != NULL) {
316 try {
317 Debugger_Value_Parsing debug_value_parsing;
318 boolean handled = var->set_function(*var, *parsed_mp);
319 if (!handled) {
320 print(DRET_NOTIFICATION, "Variables of type '%s' cannot be overwritten.",
321 var->type_name);
322 }
323 else {
324 add_to_result("[%s] %s := %s", var->type_name, var->name,
325 (const char*)var->print_function(*var));
326 }
327 }
328 catch (const TC_Error&) {
329 // do nothing, an error message has already been displayed in this case
330 }
331 delete parsed_mp;
332 }
333 }
334 }
335 else {
336 print(DRET_NOTIFICATION, "Variable '%s' not found.", p_var_name);
337 }
7329404e
BB
338}
339
340void TTCN3_Debugger::set_output(const char* p_output_type, const char* p_file_name)
341{
016a1a93
BB
342 FILE* new_fp = NULL;
343 bool file, console;
344 bool same_file = false;
345 char* final_file_name = NULL;
346 // check the command's parameters before actually changing anything
347 if (!strcmp(p_output_type, "console")) {
348 file = false;
349 console = true;
7329404e
BB
350 }
351 else if (!strcmp(p_output_type, "file")) {
016a1a93
BB
352 file = true;
353 console = false;
354 }
355 else if (!strcmp(p_output_type, "both")) {
356 file = true;
357 console = true;
358 }
359 else {
360 print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'console', 'file' or 'both'.");
361 return;
362 }
363 if (file) {
7329404e 364 if (p_file_name == NULL) {
016a1a93 365 print(DRET_NOTIFICATION, "Argument 2 (output file name) is missing.");
7329404e
BB
366 return;
367 }
016a1a93
BB
368 if (output_file_name != NULL && !strcmp(p_file_name, output_file_name)) {
369 // don't reopen it if it's the same file as before
370 same_file = true;
371 }
372 else if (!TTCN_Runtime::is_hc()) {
f08ff9ca 373 // don't open any files on HCs, just store settings for future PTCs
016a1a93 374 final_file_name = finalize_file_name(p_file_name);
f08ff9ca 375 new_fp = fopen(final_file_name, TTCN_Runtime::is_mtc() ? "w" : "a");
016a1a93
BB
376 if (new_fp == NULL) {
377 print(DRET_NOTIFICATION, "Failed to open file '%s' for writing.", final_file_name);
f08ff9ca 378 Free(final_file_name);
016a1a93
BB
379 return;
380 }
7329404e
BB
381 }
382 }
016a1a93 383 // print the change notification to the old output
f08ff9ca 384 char* file_str = file ? mprintf("file '%s'", final_file_name) : NULL;
016a1a93
BB
385 Free(final_file_name);
386 print(DRET_SETTING_CHANGE, "Debugger set to print its output to %s%s%s.",
387 console ? "the console" : "", (console && file) ? " and to " : "",
388 file ? file_str : "");
389 if (file) {
390 Free(file_str);
391 }
392 if (!same_file && !TTCN_Runtime::is_hc()) {
393 if (output_file != NULL) {
394 fclose(output_file);
395 }
396 output_file = new_fp;
397 }
398 send_to_console = console;
399 Free(output_file_name);
400 if (file) {
401 output_file_name = mcopystr(p_file_name);
402 }
403}
404
f08ff9ca
BB
405void TTCN3_Debugger::set_global_batch_file(const char* p_state_str,
406 const char* p_file_name)
407{
408 bool delete_old = false;
409 bool copy_new = false;
410 if (!strcmp(p_state_str, "on")) {
411 if (p_file_name != NULL) {
412 if (global_batch_file != NULL) {
413 if (!strcmp(p_file_name, global_batch_file)) {
414 print(DRET_NOTIFICATION, "Global batch file was already switched on "
415 "and set to '%s'.", p_file_name);
416 }
417 else {
418 print(DRET_SETTING_CHANGE, "Global batch file changed from '%s' to '%s'.",
419 global_batch_file, p_file_name);
420 delete_old = true;
421 copy_new = true;
422 }
423 }
424 else {
425 print(DRET_SETTING_CHANGE, "Global batch file switched on and set to '%s'.",
426 p_file_name);
427 copy_new = true;
428 }
429 }
430 else {
431 print(DRET_NOTIFICATION, "Missing batch file name argument.");
432 }
433 }
434 else if (!strcmp(p_state_str, "off")) {
435 if (global_batch_file != NULL) {
436 print(DRET_SETTING_CHANGE, "Global batch file switched off.");
437 delete_old = true;
438 }
439 else {
440 print(DRET_NOTIFICATION, "Global batch file was already switched off.");
441 }
442 }
443 else {
444 print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'on' or 'off'.");
445 }
446 if (delete_old) {
447 Free(global_batch_file);
448 global_batch_file = NULL;
449 }
450 if (copy_new) {
451 global_batch_file = mcopystr(p_file_name);
452 }
453}
454
455void TTCN3_Debugger::step(stepping_t p_stepping_type)
456{
457 if (!halted) {
458 print(DRET_NOTIFICATION, "Stepping commands can only be used when test "
459 "execution is halted.");
460 return;
461 }
462 stepping_type = p_stepping_type;
463 stepping_stack_size = call_stack.size();
464 if (!TTCN_Runtime::is_single()) {
465 TTCN_Communication::send_debug_continue_req();
466 }
467 resume();
468}
469
470void TTCN3_Debugger::run_to_cursor(const char* p_module, int p_line)
471{
472 // all processes receive this command, since the specified location might be
473 // reached in any process, even a PTC that hasn't been created yet
474 if (!halted) {
475 print(DRET_NOTIFICATION, "The 'run to' command can only be used when test "
476 "execution is halted.");
477 return;
478 }
479 temporary_breakpoint.module = mcopystr(p_module);
480 temporary_breakpoint.line = p_line;
481 resume();
482}
483
484void TTCN3_Debugger::halt(const char* p_batch_file, bool p_run_global_batch)
016a1a93
BB
485{
486 if (!halted) {
487 halted = true;
f08ff9ca
BB
488 Free(temporary_breakpoint.module);
489 temporary_breakpoint.module = NULL;
490 temporary_breakpoint.line = 0;
491 if (!TTCN_Runtime::is_hc()) {
492 stepping_type = NOT_STEPPING;
493 stack_level = call_stack.size() - 1;
494 print(DRET_NOTIFICATION, "Test execution halted.");
495 if (p_batch_file != NULL) {
496 if (TTCN_Runtime::is_single()) {
497 TTCN_Debugger_UI::execute_batch_file(p_batch_file);
498 }
499 else {
500 TTCN_Communication::send_debug_batch(p_batch_file);
501 }
502 }
503 else if (p_run_global_batch && global_batch_file != NULL) {
504 if (TTCN_Runtime::is_single()) {
505 TTCN_Debugger_UI::execute_batch_file(global_batch_file);
506 }
507 else {
508 TTCN_Communication::send_debug_batch(global_batch_file);
509 }
510 }
511 if (TTCN_Runtime::is_single()) {
512 if (halted && !halt_at_start) {
513 resume();
514 }
515 else {
516 TTCN_Debugger_UI::read_loop();
517 }
518 }
519 else {
520 TTCN_Communication::process_debug_messages();
521 }
522 }
016a1a93 523 }
7329404e 524 else {
016a1a93
BB
525 print(DRET_NOTIFICATION, "Test execution is already halted.");
526 }
527}
528
529void TTCN3_Debugger::resume()
530{
531 if (halted) {
532 halted = false;
533 stack_level = -1;
534 print(DRET_NOTIFICATION, "Test execution resumed.");
535 }
536 else {
537 print(DRET_NOTIFICATION, "Test execution is not halted.");
7329404e 538 }
016a1a93
BB
539}
540
541void TTCN3_Debugger::exit_(const char* p_what)
542{
016a1a93 543 if (!strcmp(p_what, "test")) {
f08ff9ca 544 exiting = false;
016a1a93
BB
545 }
546 else if (!strcmp(p_what, "all")) {
f08ff9ca 547 exiting = true;
016a1a93
BB
548 }
549 else {
550 print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'test' or 'all'.");
551 return;
7329404e 552 }
016a1a93 553 halted = false;
f08ff9ca
BB
554 if (!TTCN_Runtime::is_hc()) {
555 print((exiting && TTCN_Runtime::is_mtc()) ? DRET_EXIT_ALL : DRET_NOTIFICATION,
556 "Exiting %s.", exiting ? "test execution" : "current test");
557 TTCN_Runtime::stop_execution();
558 }
7329404e
BB
559}
560
561size_t TTCN3_Debugger::find_breakpoint(const char* p_module, int p_line) const
562{
563 for (size_t i = 0; i < breakpoints.size(); ++i) {
564 if (!strcmp(breakpoints[i].module, p_module) && breakpoints[i].line == p_line) {
565 return i;
566 }
567 }
568 return breakpoints.size();
569}
570
571TTCN3_Debugger::variable_t* TTCN3_Debugger::find_variable(const void* p_value) const
572{
573 for (size_t i = 0; i < variables.size(); ++i) {
574 if (variables[i]->value == p_value) {
575 return variables[i];
576 }
577 }
578 return NULL;
579}
580
016a1a93
BB
581char* TTCN3_Debugger::finalize_file_name(const char* p_file_name_skeleton)
582{
583 if (p_file_name_skeleton == NULL) {
584 return NULL;
585 }
586 size_t len = strlen(p_file_name_skeleton);
587 size_t next_idx = 0;
588 char* ret_val = NULL;
589 for (size_t i = 0; i < len - 1; ++i) {
590 if (p_file_name_skeleton[i] == '%') {
591 ret_val = mputstrn(ret_val, p_file_name_skeleton + next_idx, i - next_idx);
592 switch (p_file_name_skeleton[i + 1]) {
593 case 'e': // %e -> executable name
594 ret_val = mputstr(ret_val, TTCN_Logger::get_executable_name());
595 break;
596 case 'h': // %h -> host name
597 ret_val = mputstr(ret_val, TTCN_Runtime::get_host_name());
598 break;
599 case 'p': // %p -> process ID
600 ret_val = mputprintf(ret_val, "%ld", (long)getpid());
601 break;
602 case 'l': { // %l -> login name
603 setpwent();
604 struct passwd *p = getpwuid(getuid());
605 if (NULL != p) {
606 ret_val = mputstr(ret_val, p->pw_name);
607 }
608 endpwent();
609 break; }
610 case 'r': // %r -> component reference
611 if (TTCN_Runtime::is_single()) {
612 ret_val = mputstr(ret_val, "single");
613 }
614 else if (TTCN_Runtime::is_mtc()) {
615 ret_val = mputstr(ret_val, "mtc");
616 }
617 else if (TTCN_Runtime::is_ptc()) {
618 ret_val = mputprintf(ret_val, "%d", (component)self);
619 }
620 break;
621 case 'n': // %n -> component name
622 if (TTCN_Runtime::is_mtc()) {
623 ret_val = mputstr(ret_val, "MTC");
624 }
625 else if (TTCN_Runtime::is_ptc()) {
626 ret_val = mputstr(ret_val, TTCN_Runtime::get_component_name());
627 }
628 break;
629 case '%': // %% -> single %
630 ret_val = mputc(ret_val, '%');
631 break;
632 default: // unknown sequence -> leave it as it is
633 ret_val = mputstrn(ret_val, p_file_name_skeleton + i, 2);
634 break;
635 }
636 next_idx = i + 2;
637 ++i;
638 }
639 }
640 if (next_idx < len) {
641 ret_val = mputstr(ret_val, p_file_name_skeleton + next_idx);
642 }
643 return ret_val;
644}
645
f08ff9ca
BB
646void TTCN3_Debugger::test_execution_started()
647{
648 Free(snapshots);
649 snapshots = NULL;
650 exiting = false;
651 if (TTCN_Runtime::is_single()) {
652 TTCN_Debugger_UI::init();
653 if (initial_batch_file) {
654 halt(initial_batch_file, false);
655 }
656 else if (halt_at_start) {
657 halt(NULL, false);
658 }
659 }
f57971fe 660 halt_at_start = true;
f08ff9ca
BB
661}
662
663void TTCN3_Debugger::test_execution_finished()
664{
665 stepping_type = NOT_STEPPING;
666 Free(temporary_breakpoint.module);
667 temporary_breakpoint.module = NULL;
668 temporary_breakpoint.line = 0;
669 last_breakpoint_entry.module = NULL;
670 last_breakpoint_entry.line = 0;
671 if (TTCN_Runtime::is_single()) {
672 TTCN_Debugger_UI::clean_up();
673 }
674}
675
016a1a93
BB
676void TTCN3_Debugger::print(int return_type, const char* fmt, ...) const
677{
f08ff9ca
BB
678 if (TTCN_Runtime::is_hc()) {
679 // don't display anything while on the HC process
680 return;
681 }
016a1a93
BB
682 va_list parameters;
683 va_start(parameters, fmt);
684 char* str = mprintf_va_list(fmt, parameters);
685 va_end(parameters);
f08ff9ca
BB
686 if (TTCN_Runtime::is_single()) {
687 if (send_to_console) {
688 TTCN_Debugger_UI::print(str);
689 }
690 }
691 else {
692 TTCN_Communication::send_debug_return_value(return_type, send_to_console ? str : NULL);
693 }
016a1a93 694 if (output_file != NULL) {
f08ff9ca
BB
695 fseek(output_file, 0, SEEK_END); // in case multiple processes are writing the same file
696 fputs(str, output_file);
697 fputc('\n', output_file);
016a1a93
BB
698 fflush(output_file);
699 }
700 Free(str);
701}
702
7329404e
BB
703TTCN3_Debugger::TTCN3_Debugger()
704{
016a1a93 705 enabled = false;
7329404e 706 active = false;
016a1a93
BB
707 halted = false;
708 output_file = NULL;
709 output_file_name = NULL;
710 send_to_console = true;
7329404e
BB
711 snapshots = NULL;
712 last_breakpoint_entry.module = NULL;
713 last_breakpoint_entry.line = 0;
f08ff9ca 714 last_breakpoint_entry.batch_file = NULL; // not used
7329404e 715 stack_level = -1;
f08ff9ca
BB
716 fail_behavior.trigger = false;
717 fail_behavior.batch_file = NULL;
718 error_behavior.trigger = false;
719 error_behavior.batch_file = NULL;
720 global_batch_file = NULL;
016a1a93 721 command_result = NULL;
f08ff9ca
BB
722 last_variable_list = NULL;
723 stepping_type = NOT_STEPPING;
724 stepping_stack_size = 0;
725 temporary_breakpoint.module = NULL;
726 temporary_breakpoint.line = 0;
727 temporary_breakpoint.batch_file = NULL; // not used
728 exiting = false;
729 halt_at_start = false;
730 initial_batch_file = NULL;
7329404e
BB
731}
732
733TTCN3_Debugger::~TTCN3_Debugger()
734{
016a1a93
BB
735 if (output_file != NULL) {
736 fclose(output_file);
737 Free(output_file_name);
7329404e
BB
738 }
739 for (size_t i = 0; i < breakpoints.size(); ++i) {
740 Free(breakpoints[i].module);
f08ff9ca 741 Free(breakpoints[i].batch_file);
7329404e
BB
742 }
743 for (size_t i = 0; i < global_scopes.size(); ++i) {
744 delete global_scopes[i].scope;
745 }
746 for (size_t i = 0; i < component_scopes.size(); ++i) {
747 delete component_scopes[i].scope;
748 }
749 for (size_t i = 0; i < variables.size(); ++i) {
750 delete variables[i];
751 }
f08ff9ca
BB
752 Free(fail_behavior.batch_file);
753 Free(error_behavior.batch_file);
754 Free(global_batch_file);
7329404e 755 Free(snapshots);
f08ff9ca 756 Free(last_variable_list);
7329404e
BB
757}
758
759TTCN3_Debug_Scope* TTCN3_Debugger::add_global_scope(const char* p_module)
760{
761 named_scope_t global_scope;
762 global_scope.name = p_module;
763 global_scope.scope = new TTCN3_Debug_Scope();
764 global_scopes.push_back(global_scope);
765 return global_scope.scope;
766}
767
768TTCN3_Debug_Scope* TTCN3_Debugger::add_component_scope(const char* p_component)
769{
770 named_scope_t component_scope;
771 component_scope.name = p_component;
772 component_scope.scope = new TTCN3_Debug_Scope();
773 component_scopes.push_back(component_scope);
774 return component_scope.scope;
775}
776
777void TTCN3_Debugger::set_return_value(const CHARSTRING& p_value)
778{
016a1a93 779 if (active && !call_stack.empty()) {
f08ff9ca 780 call_stack[call_stack.size() - 1].function->set_return_value(p_value);
7329404e
BB
781 }
782}
783
f08ff9ca 784void TTCN3_Debugger::breakpoint_entry(int p_line)
7329404e
BB
785{
786 if (active && !call_stack.empty()) {
f08ff9ca 787 const char* module_name = call_stack[call_stack.size() - 1].function->get_module_name();
7329404e
BB
788 bool trigger = false;
789 const char* trigger_type;
790 int actual_line;
f08ff9ca 791 const char* batch_file = NULL;
7329404e
BB
792 switch (p_line) {
793 case SBP_FAIL_VERDICT:
f08ff9ca
BB
794 trigger = fail_behavior.trigger;
795 trigger_type = "Automatic breakpoint (fail verdict) reached at";
796 actual_line = TTCN_Location::get_line_number();
797 batch_file = fail_behavior.batch_file;
7329404e
BB
798 break;
799 case SBP_ERROR_VERDICT:
f08ff9ca
BB
800 trigger = error_behavior.trigger;
801 trigger_type = "Automatic breakpoint (error verdict) reached at";
802 actual_line = TTCN_Location::get_line_number();
803 batch_file = error_behavior.batch_file;
7329404e 804 break;
016a1a93 805 default: // code lines
f08ff9ca
BB
806 // first: make sure it's not the same breakpoint entry as last time
807 if (p_line == last_breakpoint_entry.line &&
808 module_name == last_breakpoint_entry.module) {
809 break;
810 }
7329404e 811 actual_line = p_line;
f08ff9ca
BB
812 // second: check if a stepping operation ends here
813 if (stepping_type == STEP_INTO ||
814 (stepping_type == STEP_OVER && call_stack.size() <= stepping_stack_size) ||
815 (stepping_type == STEP_OUT && call_stack.size() < stepping_stack_size)) {
816 trigger = true;
817 trigger_type = "Stepped to";
818 break;
819 }
820 // third: check if this is the destination of a 'run to cursor' operation
821 if (p_line == temporary_breakpoint.line &&
822 !strcmp(module_name, temporary_breakpoint.module)) {
823 trigger = true;
824 trigger_type = "Ran to";
825 break;
826 }
827 // fourth: check if the location matches one of the breakpoints in the list
828 size_t pos = find_breakpoint(module_name, p_line);
829 if (pos != breakpoints.size()) {
830 trigger = true;
831 batch_file = breakpoints[pos].batch_file;
832 }
833 trigger_type = "User breakpoint reached at";
7329404e
BB
834 break;
835 }
7329404e 836 if (trigger) {
f08ff9ca
BB
837 print(DRET_NOTIFICATION, "%s line %d in module '%s'.",
838 trigger_type, actual_line, module_name);
839 if (!TTCN_Runtime::is_single()) {
840 TTCN_Communication::send_debug_halt_req();
841 }
842 halt(batch_file, true);
7329404e
BB
843 }
844 last_breakpoint_entry.module = (char*)module_name;
845 last_breakpoint_entry.line = p_line;
846 }
847}
848
849CHARSTRING TTCN3_Debugger::print_base_var(const TTCN3_Debugger::variable_t& p_var)
850{
f08ff9ca 851 const void* ptr = p_var.set_function != NULL ? p_var.value : p_var.cvalue;
7329404e
BB
852 TTCN_Logger::begin_event_log2str();
853 if (!strcmp(p_var.type_name, "bitstring")) {
f08ff9ca 854 ((const BITSTRING*)ptr)->log();
7329404e
BB
855 }
856 else if (!strcmp(p_var.type_name, "bitstring template")) {
f08ff9ca 857 ((const BITSTRING_template*)ptr)->log();
7329404e
BB
858 }
859 else if (!strcmp(p_var.type_name, "boolean")) {
f08ff9ca 860 ((const BOOLEAN*)ptr)->log();
7329404e
BB
861 }
862 else if (!strcmp(p_var.type_name, "boolean template")) {
f08ff9ca 863 ((const BOOLEAN_template*)ptr)->log();
7329404e
BB
864 }
865 else if (!strcmp(p_var.type_name, "charstring")) {
f08ff9ca 866 ((const CHARSTRING*)ptr)->log();
7329404e
BB
867 }
868 else if (!strcmp(p_var.type_name, "charstring template")) {
f08ff9ca 869 ((const CHARSTRING_template*)ptr)->log();
7329404e
BB
870 }
871 else if (!strcmp(p_var.type_name, "float")) {
f08ff9ca 872 ((const FLOAT*)ptr)->log();
7329404e
BB
873 }
874 else if (!strcmp(p_var.type_name, "float template")) {
f08ff9ca 875 ((const FLOAT_template*)ptr)->log();
7329404e
BB
876 }
877 else if (!strcmp(p_var.type_name, "hexstring")) {
f08ff9ca 878 ((const HEXSTRING*)ptr)->log();
7329404e
BB
879 }
880 else if (!strcmp(p_var.type_name, "hexstring template")) {
f08ff9ca 881 ((const HEXSTRING_template*)ptr)->log();
7329404e
BB
882 }
883 else if (!strcmp(p_var.type_name, "integer")) {
f08ff9ca 884 ((const INTEGER*)ptr)->log();
7329404e
BB
885 }
886 else if (!strcmp(p_var.type_name, "integer template")) {
f08ff9ca 887 ((const INTEGER_template*)ptr)->log();
7329404e
BB
888 }
889 else if (!strcmp(p_var.type_name, "objid")) {
f08ff9ca 890 ((const OBJID*)ptr)->log();
7329404e
BB
891 }
892 else if (!strcmp(p_var.type_name, "objid template")) {
f08ff9ca 893 ((const OBJID_template*)ptr)->log();
7329404e
BB
894 }
895 else if (!strcmp(p_var.type_name, "octetstring")) {
f08ff9ca 896 ((const OCTETSTRING*)ptr)->log();
7329404e
BB
897 }
898 else if (!strcmp(p_var.type_name, "octetstring template")) {
f08ff9ca 899 ((const OCTETSTRING_template*)ptr)->log();
7329404e
BB
900 }
901 else if (!strcmp(p_var.type_name, "universal charstring")) {
f08ff9ca 902 ((const UNIVERSAL_CHARSTRING*)ptr)->log();
7329404e
BB
903 }
904 else if (!strcmp(p_var.type_name, "universal charstring template")) {
f08ff9ca 905 ((const UNIVERSAL_CHARSTRING_template*)ptr)->log();
7329404e
BB
906 }
907 else if (!strcmp(p_var.type_name, "verdicttype")) {
f08ff9ca 908 ((const VERDICTTYPE*)ptr)->log();
7329404e
BB
909 }
910 else if (!strcmp(p_var.type_name, "verdicttype template")) {
f08ff9ca 911 ((const VERDICTTYPE_template*)ptr)->log();
7329404e
BB
912 }
913 else if (!strcmp(p_var.type_name, "component")) {
f08ff9ca 914 ((const COMPONENT*)ptr)->log();
7329404e
BB
915 }
916 else if (!strcmp(p_var.type_name, "component template")) {
f08ff9ca 917 ((const COMPONENT_template*)ptr)->log();
7329404e
BB
918 }
919 else if (!strcmp(p_var.type_name, "default")) {
f08ff9ca 920 ((const DEFAULT*)ptr)->log();
7329404e
BB
921 }
922 else if (!strcmp(p_var.type_name, "default template")) {
f08ff9ca 923 ((const DEFAULT_template*)ptr)->log();
7329404e
BB
924 }
925 else if (!strcmp(p_var.type_name, "timer")) {
f08ff9ca 926 ((const TIMER*)ptr)->log();
7329404e
BB
927 }
928 else if (!strcmp(p_var.type_name, "NULL")) {
f08ff9ca 929 ((const ASN_NULL*)ptr)->log();
7329404e
BB
930 }
931 else if (!strcmp(p_var.type_name, "NULL template")) {
f08ff9ca 932 ((const ASN_NULL_template*)ptr)->log();
7329404e
BB
933 }
934 else if (!strcmp(p_var.type_name, "CHARACTER STRING")) {
f08ff9ca 935 ((const CHARACTER_STRING*)ptr)->log();
7329404e
BB
936 }
937 else if (!strcmp(p_var.type_name, "CHARACTER STRING template")) {
f08ff9ca 938 ((const CHARACTER_STRING_template*)ptr)->log();
7329404e
BB
939 }
940 else if (!strcmp(p_var.type_name, "EMBEDDED PDV")) {
f08ff9ca 941 ((const EMBEDDED_PDV*)ptr)->log();
7329404e
BB
942 }
943 else if (!strcmp(p_var.type_name, "EMBEDDED PDV template")) {
f08ff9ca 944 ((const EMBEDDED_PDV_template*)ptr)->log();
7329404e
BB
945 }
946 else if (!strcmp(p_var.type_name, "EXTERNAL")) {
f08ff9ca 947 ((const EXTERNAL*)ptr)->log();
7329404e
BB
948 }
949 else if (!strcmp(p_var.type_name, "EXTERNAL template")) {
f08ff9ca 950 ((const EXTERNAL_template*)ptr)->log();
7329404e
BB
951 }
952 else {
953 TTCN_Logger::log_event_str("<unrecognized value or template>");
954 }
955 return TTCN_Logger::end_event_log2str();
956}
957
f08ff9ca
BB
958boolean TTCN3_Debugger::set_base_var(variable_t& p_var, Module_Param& p_new_value)
959{
960 if (!strcmp(p_var.type_name, "bitstring")) {
961 ((BITSTRING*)p_var.value)->set_param(p_new_value);
962 }
963 else if (!strcmp(p_var.type_name, "bitstring template")) {
964 ((BITSTRING_template*)p_var.value)->set_param(p_new_value);
965 }
966 else if (!strcmp(p_var.type_name, "boolean")) {
967 ((BOOLEAN*)p_var.value)->set_param(p_new_value);
968 }
969 else if (!strcmp(p_var.type_name, "boolean template")) {
970 ((BOOLEAN_template*)p_var.value)->set_param(p_new_value);
971 }
972 else if (!strcmp(p_var.type_name, "charstring")) {
973 ((CHARSTRING*)p_var.value)->set_param(p_new_value);
974 }
975 else if (!strcmp(p_var.type_name, "charstring template")) {
976 ((CHARSTRING_template*)p_var.value)->set_param(p_new_value);
977 }
978 else if (!strcmp(p_var.type_name, "float")) {
979 ((FLOAT*)p_var.value)->set_param(p_new_value);
980 }
981 else if (!strcmp(p_var.type_name, "float template")) {
982 ((FLOAT_template*)p_var.value)->set_param(p_new_value);
983 }
984 else if (!strcmp(p_var.type_name, "hexstring")) {
985 ((HEXSTRING*)p_var.value)->set_param(p_new_value);
986 }
987 else if (!strcmp(p_var.type_name, "hexstring template")) {
988 ((HEXSTRING_template*)p_var.value)->set_param(p_new_value);
989 }
990 else if (!strcmp(p_var.type_name, "integer")) {
991 ((INTEGER*)p_var.value)->set_param(p_new_value);
992 }
993 else if (!strcmp(p_var.type_name, "integer template")) {
994 ((INTEGER_template*)p_var.value)->set_param(p_new_value);
995 }
996 else if (!strcmp(p_var.type_name, "objid")) {
997 ((OBJID*)p_var.value)->set_param(p_new_value);
998 }
999 else if (!strcmp(p_var.type_name, "objid template")) {
1000 ((OBJID_template*)p_var.value)->set_param(p_new_value);
1001 }
1002 else if (!strcmp(p_var.type_name, "octetstring")) {
1003 ((OCTETSTRING*)p_var.value)->set_param(p_new_value);
1004 }
1005 else if (!strcmp(p_var.type_name, "octetstring template")) {
1006 ((OCTETSTRING_template*)p_var.value)->set_param(p_new_value);
1007 }
1008 else if (!strcmp(p_var.type_name, "universal charstring")) {
1009 ((UNIVERSAL_CHARSTRING*)p_var.value)->set_param(p_new_value);
1010 }
1011 else if (!strcmp(p_var.type_name, "universal charstring template")) {
1012 ((UNIVERSAL_CHARSTRING_template*)p_var.value)->set_param(p_new_value);
1013 }
1014 else if (!strcmp(p_var.type_name, "verdicttype")) {
1015 ((VERDICTTYPE*)p_var.value)->set_param(p_new_value);
1016 }
1017 else if (!strcmp(p_var.type_name, "verdicttype template")) {
1018 ((VERDICTTYPE_template*)p_var.value)->set_param(p_new_value);
1019 }
1020 else if (!strcmp(p_var.type_name, "component")) {
1021 ((COMPONENT*)p_var.value)->set_param(p_new_value);
1022 }
1023 else if (!strcmp(p_var.type_name, "component template")) {
1024 ((COMPONENT_template*)p_var.value)->set_param(p_new_value);
1025 }
1026 else if (!strcmp(p_var.type_name, "default")) {
1027 ((DEFAULT*)p_var.value)->set_param(p_new_value);
1028 }
1029 else if (!strcmp(p_var.type_name, "default template")) {
1030 ((DEFAULT_template*)p_var.value)->set_param(p_new_value);
1031 }
1032 else if (!strcmp(p_var.type_name, "NULL")) {
1033 ((ASN_NULL*)p_var.value)->set_param(p_new_value);
1034 }
1035 else if (!strcmp(p_var.type_name, "NULL template")) {
1036 ((ASN_NULL_template*)p_var.value)->set_param(p_new_value);
1037 }
1038 else if (!strcmp(p_var.type_name, "CHARACTER STRING")) {
1039 ((CHARACTER_STRING*)p_var.value)->set_param(p_new_value);
1040 }
1041 else if (!strcmp(p_var.type_name, "CHARACTER STRING template")) {
1042 ((CHARACTER_STRING_template*)p_var.value)->set_param(p_new_value);
1043 }
1044 else if (!strcmp(p_var.type_name, "EMBEDDED PDV")) {
1045 ((EMBEDDED_PDV*)p_var.value)->set_param(p_new_value);
1046 }
1047 else if (!strcmp(p_var.type_name, "EMBEDDED PDV template")) {
1048 ((EMBEDDED_PDV_template*)p_var.value)->set_param(p_new_value);
1049 }
1050 else if (!strcmp(p_var.type_name, "EXTERNAL")) {
1051 ((EXTERNAL*)p_var.value)->set_param(p_new_value);
1052 }
1053 else if (!strcmp(p_var.type_name, "EXTERNAL template")) {
1054 ((EXTERNAL_template*)p_var.value)->set_param(p_new_value);
1055 }
1056 else {
1057 return FALSE;
1058 }
1059 return TRUE;
1060}
1061
016a1a93 1062void TTCN3_Debugger::add_to_result(const char* fmt, ...)
7329404e
BB
1063{
1064 va_list parameters;
1065 va_start(parameters, fmt);
016a1a93 1066 command_result = mputprintf_va_list(command_result, fmt, parameters);
7329404e 1067 va_end(parameters);
7329404e
BB
1068}
1069
1070void TTCN3_Debugger::add_function(TTCN3_Debug_Function* p_function)
1071{
f08ff9ca
BB
1072 function_call_t function_call;
1073 if (call_stack.empty()) {
1074 test_execution_started();
1075 function_call.caller_line = 0;
1076 }
1077 else {
1078 function_call.caller_line = last_breakpoint_entry.line;
7329404e 1079 }
f08ff9ca
BB
1080 function_call.function = p_function;
1081 call_stack.push_back(function_call);
7329404e
BB
1082}
1083
1084void TTCN3_Debugger::add_scope(TTCN3_Debug_Scope* p_scope)
1085{
1086 if (active && !call_stack.empty()) {
f08ff9ca 1087 call_stack[call_stack.size() - 1].function->add_scope(p_scope);
7329404e
BB
1088 }
1089}
1090
1091void TTCN3_Debugger::remove_function(TTCN3_Debug_Function* p_function)
1092{
f08ff9ca
BB
1093 if (!call_stack.empty() && call_stack[call_stack.size() - 1].function == p_function) {
1094 bool removing_test_case = call_stack[call_stack.size() - 1].function->is_test_case();
1095 int caller_line = call_stack[call_stack.size() - 1].caller_line;
7329404e 1096 call_stack.erase_at(call_stack.size() - 1);
f08ff9ca
BB
1097 if (call_stack.empty()) {
1098 test_execution_finished();
1099 }
1100 if (caller_line != 0 && (stepping_type == STEP_INTO || stepping_type == STEP_OUT ||
1101 (stepping_type == STEP_OVER && call_stack.size() != stepping_stack_size))) {
1102 breakpoint_entry(caller_line);
1103 }
1104 if (exiting && TTCN_Runtime::is_single() && !call_stack.empty() && removing_test_case &&
1105 call_stack[call_stack.size() - 1].function->is_control_part()) {
1106 // 'exit all' was requested while executing a test case called by a control
1107 // part, which means the test case caught the original TC_End exception;
1108 // another exception must be thrown to stop the control part, too
1109 throw TC_End();
1110 }
7329404e
BB
1111 }
1112}
1113
1114void TTCN3_Debugger::remove_scope(TTCN3_Debug_Scope* p_scope)
1115{
1116 if (!call_stack.empty()) {
f08ff9ca 1117 call_stack[call_stack.size() - 1].function->remove_scope(p_scope);
7329404e
BB
1118 }
1119}
1120
f08ff9ca
BB
1121TTCN3_Debugger::variable_t* TTCN3_Debugger::add_variable(const void* p_value,
1122 const char* p_name,
1123 const char* p_type,
1124 TTCN3_Debugger::print_function_t p_print_function)
1125{
1126 if (call_stack.empty()) {
1127 // no call stack yet, so this is a global or component variable
1128 variable_t* var = find_variable(p_value);
1129 if (var == NULL) {
1130 var = new TTCN3_Debugger::variable_t;
1131 var->cvalue = p_value;
1132 var->name = p_name;
1133 var->type_name = p_type;
1134 var->print_function = p_print_function;
1135 var->set_function = NULL;
1136 variables.push_back(var);
1137 }
1138 return var;
1139 }
1140 else if (active) {
1141 // it's a local variable for the top-most function
1142 return call_stack[call_stack.size() - 1].function->add_variable(
1143 p_value, p_name, p_type, p_print_function);
1144 }
1145 return NULL;
1146}
1147
1148TTCN3_Debugger::variable_t* TTCN3_Debugger::add_variable(void* p_value,
1149 const char* p_name,
1150 const char* p_type,
1151 TTCN3_Debugger::print_function_t p_print_function,
1152 TTCN3_Debugger::set_function_t p_set_function)
7329404e 1153{
7329404e
BB
1154 if (call_stack.empty()) {
1155 // no call stack yet, so this is a global or component variable
1156 variable_t* var = find_variable(p_value);
1157 if (var == NULL) {
1158 var = new TTCN3_Debugger::variable_t;
1159 var->value = p_value;
1160 var->name = p_name;
1161 var->type_name = p_type;
1162 var->print_function = p_print_function;
f08ff9ca 1163 var->set_function = p_set_function;
7329404e
BB
1164 variables.push_back(var);
1165 }
1166 return var;
1167 }
1168 else if (active) {
1169 // it's a local variable for the top-most function
f08ff9ca
BB
1170 return call_stack[call_stack.size() - 1].function->add_variable(
1171 p_value, p_name, p_type, p_print_function);
7329404e
BB
1172 }
1173 return NULL;
1174}
1175
1176void TTCN3_Debugger::remove_variable(const variable_t* p_var)
1177{
1178 if (active && !call_stack.empty()) {
f08ff9ca 1179 call_stack[call_stack.size() - 1].function->remove_variable(p_var);
7329404e
BB
1180 }
1181}
1182
1183const TTCN3_Debug_Scope* TTCN3_Debugger::get_global_scope(const char* p_module) const
1184{
1185 for (size_t i = 0; i < global_scopes.size(); ++i) {
1186 if (strcmp(global_scopes[i].name, p_module) == 0) {
1187 return global_scopes[i].scope;
1188 }
1189 }
1190 return NULL;
1191}
1192
1193const TTCN3_Debug_Scope* TTCN3_Debugger::get_component_scope(const char* p_component) const
1194{
1195 for (size_t i = 0; i < component_scopes.size(); ++i) {
1196 if (strcmp(component_scopes[i].name, p_component) == 0) {
1197 return component_scopes[i].scope;
1198 }
1199 }
1200 return NULL;
1201}
1202
1203void TTCN3_Debugger::add_snapshot(const char* p_snapshot)
1204{
016a1a93
BB
1205 if (snapshots != NULL) {
1206 snapshots = mputc(snapshots, '\n');
1207 }
7329404e
BB
1208 snapshots = mputstr(snapshots, p_snapshot);
1209}
1210
1211#define CHECK_NOF_ARGUMENTS(exp_num) \
016a1a93
BB
1212 if (exp_num != p_argument_count) { \
1213 print(DRET_NOTIFICATION, "Invalid number of arguments. Expected %d, got %d.", \
1214 (int)exp_num, (int)p_argument_count); \
7329404e
BB
1215 return; \
1216 }
1217
1218#define CHECK_NOF_ARGUMENTS_RANGE(min, max) \
016a1a93
BB
1219 if ((int)min > p_argument_count || (int)max < p_argument_count) { \
1220 print(DRET_NOTIFICATION, "Invalid number of arguments. Expected at least %d " \
1221 "and at most %d, got %d.", (int)min, (int)max, p_argument_count); \
7329404e
BB
1222 return; \
1223 }
1224
1225#define CHECK_NOF_ARGUMENTS_MIN(min) \
016a1a93
BB
1226 if ((int)min > p_argument_count) { \
1227 print(DRET_NOTIFICATION, "Invalid number of arguments. Expected at least %d, got %d.", \
1228 (int)min, p_argument_count); \
7329404e
BB
1229 return; \
1230 }
1231
1232#define CHECK_INT_ARGUMENT(arg_idx) \
1233 { \
016a1a93
BB
1234 size_t len = strlen(p_arguments[arg_idx]); \
1235 for (size_t i = 0; i < len; ++i) { \
1236 if (p_arguments[arg_idx][i] < '0' || p_arguments[arg_idx][i] > '9') { \
1237 print(DRET_NOTIFICATION, "Argument %d is not an integer.", (int)(arg_idx + 1)); \
7329404e
BB
1238 return; \
1239 } \
1240 } \
1241 }
1242
f08ff9ca
BB
1243#define CHECK_CALL_STACK(print_msg) \
1244 if (!active) { \
1245 if (print_msg) { \
1246 print(DRET_NOTIFICATION, "This command can only be used if the debugger " \
1247 "is switched on."); \
1248 } \
1249 return; \
1250 } \
7329404e 1251 if (call_stack.empty()) { \
f08ff9ca
BB
1252 if (print_msg) { \
1253 print(DRET_NOTIFICATION, "This command can only be used if the debugger's " \
1254 "call stack is not empty."); \
1255 } \
7329404e
BB
1256 return; \
1257 }
1258
016a1a93
BB
1259void TTCN3_Debugger::execute_command(int p_command, int p_argument_count,
1260 char** p_arguments)
7329404e 1261{
016a1a93 1262 if (!enabled) {
7329404e
BB
1263 return;
1264 }
016a1a93
BB
1265 for (int i = 0; i < p_argument_count; ++i) {
1266 if (p_arguments[i] == NULL) {
1267 print(DRET_NOTIFICATION, "Argument %d is a null pointer.", i + 1);
7329404e
BB
1268 return;
1269 }
1270 }
1271 switch (p_command) {
016a1a93
BB
1272 case D_SWITCH:
1273 CHECK_NOF_ARGUMENTS(1)
1274 switch_state(p_arguments[0]);
7329404e 1275 break;
f08ff9ca
BB
1276 case D_SET_BREAKPOINT:
1277 CHECK_NOF_ARGUMENTS_RANGE(2, 3)
7329404e 1278 CHECK_INT_ARGUMENT(1)
f08ff9ca
BB
1279 set_breakpoint(p_arguments[0], str2int(p_arguments[1]),
1280 (p_argument_count == 3) ? p_arguments[2] : NULL);
7329404e
BB
1281 break;
1282 case D_REMOVE_BREAKPOINT:
f08ff9ca
BB
1283 CHECK_NOF_ARGUMENTS_RANGE(1, 2)
1284 remove_breakpoint(p_arguments[0], (p_argument_count == 2) ? p_arguments[1] : NULL);
7329404e 1285 break;
f08ff9ca
BB
1286 case D_SET_AUTOMATIC_BREAKPOINT:
1287 CHECK_NOF_ARGUMENTS_RANGE(2, 3)
1288 set_automatic_breakpoint(p_arguments[0], p_arguments[1],
1289 (p_argument_count == 3) ? p_arguments[2] : NULL);
7329404e 1290 break;
7329404e
BB
1291 case D_SET_OUTPUT:
1292 CHECK_NOF_ARGUMENTS_RANGE(1, 2)
016a1a93
BB
1293 set_output(p_arguments[0], (p_argument_count == 2) ? p_arguments[1] : NULL);
1294 break;
f08ff9ca
BB
1295 case D_SET_GLOBAL_BATCH_FILE:
1296 CHECK_NOF_ARGUMENTS_RANGE(1, 2)
1297 set_global_batch_file(p_arguments[0], (p_argument_count == 2) ? p_arguments[1] : NULL);
1298 break;
016a1a93 1299 case D_SET_COMPONENT:
f08ff9ca
BB
1300 print(DRET_NOTIFICATION, "Command " D_SET_COMPONENT_TEXT " %s.",
1301 TTCN_Runtime::is_single() ? "is not available in single mode" :
1302 "should have been sent to the Main Controller");
7329404e 1303 break;
7329404e 1304 case D_PRINT_CALL_STACK:
f08ff9ca 1305 CHECK_CALL_STACK(true)
7329404e
BB
1306 CHECK_NOF_ARGUMENTS(0)
1307 print_call_stack();
1308 break;
1309 case D_SET_STACK_LEVEL:
f08ff9ca 1310 CHECK_CALL_STACK(true)
7329404e
BB
1311 CHECK_NOF_ARGUMENTS(1)
1312 CHECK_INT_ARGUMENT(0)
1313 set_stack_level(str2int(p_arguments[0]));
f08ff9ca 1314 break;
7329404e 1315 case D_LIST_VARIABLES:
f08ff9ca 1316 CHECK_CALL_STACK(true)
7329404e 1317 CHECK_NOF_ARGUMENTS_RANGE(1, 2)
f08ff9ca 1318 call_stack[STACK_LEVEL].function->list_variables(p_arguments[0],
016a1a93 1319 (p_argument_count == 2) ? p_arguments[1] : NULL);
7329404e
BB
1320 break;
1321 case D_PRINT_VARIABLE:
f08ff9ca 1322 CHECK_CALL_STACK(true)
7329404e 1323 CHECK_NOF_ARGUMENTS_MIN(1)
016a1a93 1324 for (int i = 0; i < p_argument_count; ++i) {
f08ff9ca
BB
1325 if (i != 0) {
1326 add_to_result("\n");
7329404e 1327 }
f08ff9ca
BB
1328 if (!strcmp(p_arguments[i], "$")) {
1329 // '$' refers to the result of the last D_LIST_VARIABLES command
1330 // these variable names are separated by spaces
1331 if (last_variable_list != NULL) {
1332 size_t len = mstrlen(last_variable_list);
1333 size_t start = 0;
1334 for (size_t j = 0; j < len; ++j) {
1335 if (last_variable_list[j] == ' ') {
1336 // extract the variable name before this space
1337 char* var_name = mcopystrn(last_variable_list + start, j - start);
1338 print_variable(var_name);
1339 Free(var_name);
1340 add_to_result("\n");
1341 start = j + 1;
1342 }
1343 }
1344 // extract the last (or only) variable name
1345 char* var_name = mcopystrn(last_variable_list + start, len - start);
1346 print_variable(var_name);
1347 Free(var_name);
1348 }
1349 else {
1350 add_to_result("No previous " D_LIST_VARIABLES_TEXT " result.");
1351 }
016a1a93 1352 }
f08ff9ca
BB
1353 else {
1354 print_variable(p_arguments[i]);
7329404e
BB
1355 }
1356 }
1357 break;
f08ff9ca
BB
1358 case D_OVERWRITE_VARIABLE:
1359 CHECK_CALL_STACK(true)
1360 CHECK_NOF_ARGUMENTS_MIN(2)
1361 overwrite_variable(p_arguments[0], p_argument_count - 1, p_arguments + 1);
1362 break;
7329404e
BB
1363 case D_PRINT_SNAPSHOTS:
1364 CHECK_NOF_ARGUMENTS(0)
016a1a93 1365 add_to_result("%s", snapshots);
7329404e 1366 break;
f08ff9ca
BB
1367 case D_STEP_OVER:
1368 CHECK_CALL_STACK(true)
1369 CHECK_NOF_ARGUMENTS(0)
1370 step(STEP_OVER);
1371 break;
1372 case D_STEP_INTO:
1373 CHECK_CALL_STACK(true)
1374 CHECK_NOF_ARGUMENTS(0)
1375 step(STEP_INTO);
1376 break;
1377 case D_STEP_OUT:
1378 CHECK_CALL_STACK(true)
1379 CHECK_NOF_ARGUMENTS(0)
1380 step(STEP_OUT);
1381 break;
1382 case D_RUN_TO_CURSOR:
1383 if (!TTCN_Runtime::is_hc() && !TTCN_Runtime::is_single()) {
1384 CHECK_CALL_STACK(TTCN_Runtime::is_mtc())
1385 }
1386 CHECK_NOF_ARGUMENTS(2)
1387 CHECK_INT_ARGUMENT(1)
1388 run_to_cursor(p_arguments[0], str2int(p_arguments[1]));
1389 break;
016a1a93 1390 case D_HALT:
f08ff9ca
BB
1391 if (!TTCN_Runtime::is_hc() && !TTCN_Runtime::is_single()) {
1392 CHECK_CALL_STACK(TTCN_Runtime::is_mtc())
016a1a93
BB
1393 }
1394 CHECK_NOF_ARGUMENTS(0)
f08ff9ca 1395 halt(NULL, false);
016a1a93
BB
1396 break;
1397 case D_CONTINUE:
1398 CHECK_NOF_ARGUMENTS(0)
1399 resume();
1400 break;
1401 case D_EXIT:
f08ff9ca
BB
1402 if (!TTCN_Runtime::is_hc() && !TTCN_Runtime::is_single()) {
1403 CHECK_CALL_STACK(TTCN_Runtime::is_mtc())
016a1a93 1404 }
f08ff9ca 1405 CHECK_NOF_ARGUMENTS(1)
016a1a93
BB
1406 exit_(p_arguments[0]);
1407 break;
1408 case D_SETUP:
1409 CHECK_NOF_ARGUMENTS_MIN(5)
1410 if (strlen(p_arguments[0]) > 0) {
1411 switch_state(p_arguments[0]);
1412 }
1413 if (strlen(p_arguments[1]) > 0) {
1414 set_output(p_arguments[1], p_arguments[2]);
1415 }
1416 if (strlen(p_arguments[3]) > 0) {
f08ff9ca
BB
1417 set_automatic_breakpoint("error", p_arguments[3],
1418 strlen(p_arguments[4]) > 0 ? p_arguments[4] : NULL);
1419 }
1420 if (strlen(p_arguments[5]) > 0) {
1421 set_automatic_breakpoint("fail", p_arguments[5],
1422 strlen(p_arguments[6]) > 0 ? p_arguments[6] : NULL);
016a1a93 1423 }
f08ff9ca
BB
1424 if (strlen(p_arguments[7]) > 0) {
1425 set_global_batch_file(p_arguments[7],
1426 strlen(p_arguments[8]) > 0 ? p_arguments[8] : NULL);
016a1a93 1427 }
f08ff9ca
BB
1428 for (int i = 9; i < p_argument_count; i += 3) {
1429 set_breakpoint(p_arguments[i], str2int(p_arguments[i + 1]),
1430 strlen(p_arguments[i + 2]) > 0 ? p_arguments[i + 2] : NULL);
016a1a93 1431 }
7329404e 1432 break;
016a1a93 1433 default:
f08ff9ca 1434 print(DRET_NOTIFICATION, "Invalid command received (ID: %d).", p_command);
016a1a93
BB
1435 return;
1436 }
1437 if (command_result != NULL) {
1438 print(DRET_DATA, command_result);
f08ff9ca
BB
1439 if (p_command == D_LIST_VARIABLES) {
1440 Free(last_variable_list);
1441 last_variable_list = command_result;
1442 }
1443 else {
1444 Free(command_result);
1445 }
1446 command_result = NULL;
016a1a93
BB
1447 }
1448}
1449
1450void TTCN3_Debugger::open_output_file()
1451{
1452 if (output_file == NULL && output_file_name != NULL) {
1453 char* final_file_name = finalize_file_name(output_file_name);
f08ff9ca 1454 output_file = fopen(final_file_name, TTCN_Runtime::is_mtc() ? "w" : "a");
016a1a93
BB
1455 if (output_file == NULL) {
1456 print(DRET_NOTIFICATION, "Failed to open file '%s' for writing.", final_file_name);
1457 }
1458 Free(final_file_name);
7329404e
BB
1459 }
1460}
1461
1462//////////////////////////////////////////////////////
1463//////////////// TTCN3_Debug_Scope ///////////////////
1464//////////////////////////////////////////////////////
1465
1466TTCN3_Debug_Scope::TTCN3_Debug_Scope()
1467{
1468 ttcn3_debugger.add_scope(this);
1469}
1470
1471TTCN3_Debug_Scope::~TTCN3_Debug_Scope()
1472{
1473 for (size_t i = 0; i < variables.size(); ++i) {
1474 ttcn3_debugger.remove_variable(variables[i]);
1475 }
1476 ttcn3_debugger.remove_scope(this);
1477}
1478
1479void TTCN3_Debug_Scope::add_variable(const void* p_value,
1480 const char* p_name,
1481 const char* p_type,
f08ff9ca
BB
1482 TTCN3_Debugger::print_function_t p_print_function)
1483{
1484 TTCN3_Debugger::variable_t* var = ttcn3_debugger.add_variable(p_value, p_name, p_type, p_print_function);
1485 if (var != NULL) {
1486 variables.push_back(var);
1487 }
1488}
1489
1490void TTCN3_Debug_Scope::add_variable(void* p_value,
1491 const char* p_name,
1492 const char* p_type,
1493 TTCN3_Debugger::print_function_t p_print_function,
1494 TTCN3_Debugger::set_function_t p_set_function)
7329404e 1495{
f08ff9ca
BB
1496 TTCN3_Debugger::variable_t* var = ttcn3_debugger.add_variable(p_value, p_name,
1497 p_type, p_print_function, p_set_function);
7329404e
BB
1498 if (var != NULL) {
1499 variables.push_back(var);
1500 }
1501}
1502
f08ff9ca 1503TTCN3_Debugger::variable_t* TTCN3_Debug_Scope::find_variable(const char* p_name) const
7329404e
BB
1504{
1505 for (size_t i = 0; i < variables.size(); ++i) {
1506 if (strcmp(variables[i]->name, p_name) == 0) {
1507 return variables[i];
1508 }
1509 }
1510 return NULL;
1511}
1512
016a1a93 1513void TTCN3_Debug_Scope::list_variables(regex_t* p_posix_regexp, bool& p_first) const
7329404e
BB
1514{
1515 for (size_t i = 0; i < variables.size(); ++i) {
016a1a93
BB
1516 if (p_posix_regexp == NULL ||
1517 regexec(p_posix_regexp, variables[i]->name, 0, NULL, 0) == 0) {
1518 ttcn3_debugger.add_to_result("%s%s", p_first ? "" : " ", variables[i]->name);
1519 p_first = false;
1520 }
7329404e
BB
1521 }
1522}
1523
1524//////////////////////////////////////////////////////
1525/////////////// TTCN3_Debug_Function /////////////////
1526//////////////////////////////////////////////////////
1527
1528TTCN3_Debug_Function::TTCN3_Debug_Function(const char* p_name,
1529 const char* p_type,
1530 const char* p_module,
1531 const charstring_list& p_parameter_names,
1532 const charstring_list& p_parameter_types,
1533 const char* p_component_name)
1534: function_name(p_name), function_type(p_type), module_name(p_module)
1535, parameter_names(new charstring_list(p_parameter_names))
1536, parameter_types(new charstring_list(p_parameter_types))
1537{
1538 ttcn3_debugger.add_function(this);
1539 global_scope = ttcn3_debugger.get_global_scope(p_module);
1540 component_scope = (p_component_name != NULL) ?
1541 ttcn3_debugger.get_component_scope(p_component_name) : NULL;
1542 if (function_name == NULL) {
1543 function_name = p_module; // for control parts
1544 }
1545}
1546
1547TTCN3_Debug_Function::~TTCN3_Debug_Function()
1548{
1549 if (ttcn3_debugger.is_on()) {
1550 char* snapshot = mprintf("[%s]\tfinished\t%s(", function_type, function_name);
1551 if (parameter_names->size_of() > 0) {
1552 for (int i = 0; i < parameter_names->size_of(); ++i) {
1553 if (i > 0) {
1554 snapshot = mputstr(snapshot, ", ");
1555 }
1556 snapshot = mputprintf(snapshot, "[%s] %s := ", (const char*)((*parameter_types)[i]),
1557 (const char*)((*parameter_names)[i]));
1558 if ((*parameter_types)[i] == "out" || (*parameter_types)[i] == "inout") {
1559 const TTCN3_Debugger::variable_t* parameter = find_variable((*parameter_names)[i]);
1560 snapshot = mputstr(snapshot, parameter->print_function(*parameter));
1561 }
1562 else {
1563 snapshot = mputc(snapshot, '-');
1564 }
1565 }
1566 }
1567 snapshot = mputc(snapshot, ')');
1568 if (return_value.is_bound()) {
1569 snapshot = mputprintf(snapshot, " returned %s", (const char*)return_value);
1570 }
7329404e
BB
1571 ttcn3_debugger.add_snapshot(snapshot);
1572 Free(snapshot);
1573 }
1574 for (size_t i = 0; i < variables.size(); ++i) {
1575 delete variables[i];
1576 }
1577 delete parameter_names;
1578 delete parameter_types;
1579 ttcn3_debugger.remove_function(this);
1580}
1581
f08ff9ca
BB
1582TTCN3_Debugger::variable_t* TTCN3_Debug_Function::add_variable(const void* p_value,
1583 const char* p_name,
1584 const char* p_type,
1585 TTCN3_Debugger::print_function_t p_print_function)
1586{
1587 if (ttcn3_debugger.is_on()) {
1588 TTCN3_Debugger::variable_t* var = new TTCN3_Debugger::variable_t;
1589 var->cvalue = p_value;
1590 var->name = p_name;
1591 var->type_name = p_type;
1592 var->print_function = p_print_function;
1593 var->set_function = NULL;
1594 variables.push_back(var);
1595 return var;
1596 }
1597 return NULL;
1598}
1599
1600TTCN3_Debugger::variable_t* TTCN3_Debug_Function::add_variable(void* p_value,
1601 const char* p_name,
1602 const char* p_type,
1603 TTCN3_Debugger::print_function_t p_print_function,
1604 TTCN3_Debugger::set_function_t p_set_function)
7329404e
BB
1605{
1606 if (ttcn3_debugger.is_on()) {
1607 TTCN3_Debugger::variable_t* var = new TTCN3_Debugger::variable_t;
1608 var->value = p_value;
1609 var->name = p_name;
1610 var->type_name = p_type;
1611 var->print_function = p_print_function;
f08ff9ca 1612 var->set_function = p_set_function;
7329404e
BB
1613 variables.push_back(var);
1614 return var;
1615 }
1616 return NULL;
1617}
1618
1619void TTCN3_Debug_Function::set_return_value(const CHARSTRING& p_value)
1620{
1621 return_value = p_value;
1622}
1623
1624void TTCN3_Debug_Function::initial_snapshot() const
1625{
1626 if (ttcn3_debugger.is_on()) {
1627 char* snapshot = mprintf("[%s]\tstarted \t%s(", function_type, function_name);
1628 if (parameter_names->size_of() > 0) {
1629 for (int i = 0; i < parameter_names->size_of(); ++i) {
1630 if (i > 0) {
1631 snapshot = mputstr(snapshot, ", ");
1632 }
1633 snapshot = mputprintf(snapshot, "[%s] %s := ", (const char*)((*parameter_types)[i]),
1634 (const char*)((*parameter_names)[i]));
1635 if ((*parameter_types)[i] == "in" || (*parameter_types)[i] == "inout") {
1636 const TTCN3_Debugger::variable_t* parameter = find_variable((*parameter_names)[i]);
1637 snapshot = mputstr(snapshot, parameter->print_function(*parameter));
1638 }
1639 else {
1640 snapshot = mputc(snapshot, '-');
1641 }
1642 }
1643 }
016a1a93 1644 snapshot = mputstr(snapshot, ")");
7329404e
BB
1645 ttcn3_debugger.add_snapshot(snapshot);
1646 Free(snapshot);
1647 }
1648}
1649
1650void TTCN3_Debug_Function::add_scope(TTCN3_Debug_Scope* p_scope)
1651{
1652 scopes.push_back(p_scope);
1653}
1654
1655void TTCN3_Debug_Function::remove_scope(TTCN3_Debug_Scope* p_scope)
1656{
016a1a93 1657 if (!scopes.empty() && scopes[scopes.size() - 1] == p_scope) {
7329404e
BB
1658 scopes.erase_at(scopes.size() - 1);
1659 }
1660}
1661
1662void TTCN3_Debug_Function::remove_variable(const TTCN3_Debugger::variable_t* p_var)
1663{
1664 for (size_t i = 0; i < variables.size(); ++i) {
1665 if (variables[i] == p_var) {
1666 variables.erase_at(i);
1667 delete p_var;
1668 break;
1669 }
1670 }
1671}
1672
f08ff9ca 1673TTCN3_Debugger::variable_t* TTCN3_Debug_Function::find_variable(const char* p_name) const
7329404e
BB
1674{
1675 for (size_t i = 0; i < variables.size(); ++i) {
1676 if (strcmp(variables[i]->name, p_name) == 0) {
1677 return variables[i];
1678 }
1679 }
1680 // it's not a local variable, it might still be a global or component variable
1681 if (component_scope != NULL) {
f08ff9ca 1682 TTCN3_Debugger::variable_t* res = component_scope->find_variable(p_name);
7329404e
BB
1683 if (res != NULL) {
1684 return res;
1685 }
1686 }
1687 return (global_scope != NULL) ? global_scope->find_variable(p_name) : NULL;
1688}
1689
1690void TTCN3_Debug_Function::print_function() const
1691{
016a1a93 1692 ttcn3_debugger.add_to_result("[%s]\t%s(", function_type, function_name);
7329404e
BB
1693 if (parameter_names->size_of() > 0) {
1694 for (int i = 0; i < parameter_names->size_of(); ++i) {
1695 if (i > 0) {
016a1a93 1696 ttcn3_debugger.add_to_result(", ");
7329404e
BB
1697 }
1698 const TTCN3_Debugger::variable_t* parameter = find_variable((*parameter_names)[i]);
016a1a93 1699 ttcn3_debugger.add_to_result("[%s] %s := %s", (const char*)(*parameter_types)[i],
7329404e
BB
1700 (const char*)(*parameter_names)[i], (const char*)parameter->print_function(*parameter));
1701 }
1702 }
016a1a93 1703 ttcn3_debugger.add_to_result(")");
7329404e
BB
1704}
1705
1706void TTCN3_Debug_Function::list_variables(const char* p_scope, const char* p_filter) const
1707{
1708 bool first = true;
1709 bool list_local = false;
1710 bool list_global = false;
1711 bool list_comp = false;
1712 if (!strcmp(p_scope, "local")) {
1713 list_local = true;
1714 }
1715 else if (!strcmp(p_scope, "global")) {
1716 list_global = true;
1717 }
016a1a93 1718 else if (!strcmp(p_scope, "comp")) {
7329404e
BB
1719 list_comp = true;
1720 }
016a1a93 1721 else if (!strcmp(p_scope, "all")) {
7329404e
BB
1722 list_local = true;
1723 list_global = true;
1724 list_comp = true;
1725 }
016a1a93
BB
1726 else {
1727 ttcn3_debugger.print(DRET_NOTIFICATION, "Argument 1 is invalid. "
1728 "Expected 'local', 'global', 'comp' or 'all'.");
1729 return;
1730 }
1731 regex_t* posix_regexp = NULL;
1732 if (p_filter != NULL) {
1733 char* posix_str = TTCN_pattern_to_regexp(p_filter);
1734 if (posix_str == NULL) {
1735 ttcn3_debugger.print(DRET_NOTIFICATION, "Argument 2 is invalid. "
1736 "Expected a valid TTCN-3 character pattern.");
1737 return;
1738 }
1739 posix_regexp = new regex_t;
1740 int ret_val = regcomp(posix_regexp, posix_str, REG_EXTENDED | REG_NOSUB);
1741 Free(posix_str);
1742 if (ret_val != 0) {
1743 char msg[512];
1744 regerror(ret_val, posix_regexp, msg, sizeof(msg));
1745 regfree(posix_regexp);
1746 delete posix_regexp;
1747 ttcn3_debugger.print(DRET_NOTIFICATION, "Compilation of POSIX regular "
1748 "expression failed.");
1749 return;
1750 }
1751 }
7329404e
BB
1752 if (list_local) {
1753 for (size_t i = 0; i < variables.size(); ++i) {
016a1a93
BB
1754 if (posix_regexp == NULL ||
1755 regexec(posix_regexp, variables[i]->name, 0, NULL, 0) == 0) {
1756 ttcn3_debugger.add_to_result("%s%s", first ? "" : " ", variables[i]->name);
1757 first = false;
1758 }
7329404e
BB
1759 }
1760 }
1761 if (list_global && global_scope != NULL && global_scope->has_variables()) {
016a1a93 1762 global_scope->list_variables(posix_regexp, first);
7329404e
BB
1763 }
1764 if (list_comp && component_scope != NULL && component_scope->has_variables()) {
016a1a93 1765 component_scope->list_variables(posix_regexp, first);
7329404e
BB
1766 }
1767 if (first) {
016a1a93
BB
1768 ttcn3_debugger.print(DRET_NOTIFICATION, "No variables found.");
1769 }
1770 if (posix_regexp != NULL) {
1771 regfree(posix_regexp);
1772 delete posix_regexp;
7329404e 1773 }
7329404e
BB
1774}
1775
f08ff9ca
BB
1776bool TTCN3_Debug_Function::is_control_part() const
1777{
1778 return !strcmp(function_type, "control");
1779}
1780
1781bool TTCN3_Debug_Function::is_test_case() const
1782{
1783 return !strcmp(function_type, "testcase");
1784}
This page took 0.097016 seconds and 5 git commands to generate.