Use LTTngUSTLogger logger plugin in logtest regression test
[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
cf2b6056
BB
27#define BUFFER_INCREASE 100
28
7329404e
BB
29TTCN3_Debugger ttcn3_debugger;
30
016a1a93 31void TTCN3_Debugger::switch_state(const char* p_state_str)
7329404e 32{
016a1a93
BB
33 if (!strcmp(p_state_str, "on")) {
34 if (active) {
35 print(DRET_NOTIFICATION, "The debugger is already switched on.");
36 }
37 else {
38 active = true;
39 print(DRET_SETTING_CHANGE, "Debugger switched on.");
40 }
7329404e 41 }
016a1a93
BB
42 else if(!strcmp(p_state_str, "off")) {
43 if (!active) {
44 print(DRET_NOTIFICATION, "The debugger is already switched off.");
45 }
46 else {
47 active = false;
48 print(DRET_SETTING_CHANGE, "Debugger switched off.");
49 }
7329404e
BB
50 }
51 else {
f08ff9ca 52 print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'on' or 'off'.");
7329404e 53 }
7329404e
BB
54}
55
cf2b6056
BB
56static bool is_numeric(const char* p_str)
57{
58 size_t len = strlen(p_str);
59 for (size_t i = 0; i < len; ++i) {
60 if (p_str[i] < '0' || p_str[i] > '9') {
61 return false;
62 }
63 }
64 return true;
65}
66
67void TTCN3_Debugger::set_breakpoint(const char* p_module, const char* p_location,
f08ff9ca 68 const char* batch_file)
7329404e 69{
cf2b6056
BB
70 int line = 0;
71 char* function = NULL;
72 if (is_numeric(p_location)) {
73 // it's a line breakpoint
74 line = strtol(p_location, NULL, 10);
75 }
76 else {
77 // it's a function breakpoint
78 function = mcopystr(p_location);
79 }
80 char* loc_str = function != NULL ? mprintf("function '%s'", function) :
81 mprintf("line %d", line);
82 size_t pos = find_breakpoint(p_module, line, function);
f08ff9ca 83 if (pos == breakpoints.size()) {
7329404e
BB
84 breakpoint_t bp;
85 bp.module = mcopystr(p_module);
cf2b6056
BB
86 bp.line = line;
87 bp.function = function;
f08ff9ca 88 bp.batch_file = batch_file != NULL ? mcopystr(batch_file) : NULL;
7329404e 89 breakpoints.push_back(bp);
cf2b6056
BB
90 print(DRET_SETTING_CHANGE, "Breakpoint added in module '%s' at %s %s%s%s.",
91 p_module, loc_str,
92 batch_file != NULL ? "with batch file '" : "with no batch file",
f08ff9ca 93 batch_file != NULL ? batch_file : "", batch_file != NULL ? "'" : "");
7329404e
BB
94 }
95 else {
f08ff9ca
BB
96 if (breakpoints[pos].batch_file != NULL) {
97 if (batch_file != NULL) {
98 if (!strcmp(batch_file, breakpoints[pos].batch_file)) {
99 print(DRET_NOTIFICATION, "Breakpoint already set in module '%s' at "
cf2b6056
BB
100 "%s with batch file '%s'.",
101 p_module, loc_str, batch_file);
f08ff9ca
BB
102 }
103 else {
104 print(DRET_SETTING_CHANGE, "Batch file was changed from '%s' to '%s' for "
cf2b6056
BB
105 "breakpoint in module '%s' at %s.", breakpoints[pos].batch_file,
106 batch_file, p_module, loc_str);
f08ff9ca
BB
107 }
108 }
109 else {
110 print(DRET_SETTING_CHANGE, "Batch file '%s' removed from breakpoint in "
cf2b6056 111 "module '%s' at %s.", breakpoints[pos].batch_file, p_module, loc_str);
f08ff9ca
BB
112 }
113 Free(breakpoints[pos].batch_file);
114 }
115 else {
116 if (batch_file != NULL) {
117 print(DRET_SETTING_CHANGE, "Batch file '%s' added to breakpoint in module "
cf2b6056 118 "'%s' at %s.", batch_file, p_module, loc_str);
f08ff9ca
BB
119 }
120 else {
cf2b6056
BB
121 print(DRET_NOTIFICATION, "Breakpoint already set in module '%s' at %s "
122 "with no batch file.", p_module, loc_str);
f08ff9ca
BB
123 }
124 }
125 breakpoints[pos].batch_file = batch_file != NULL ? mcopystr(batch_file) : NULL;
7329404e 126 }
cf2b6056 127 Free(loc_str);
7329404e
BB
128}
129
cf2b6056 130void TTCN3_Debugger::remove_breakpoint(const char* p_module, const char* p_location)
7329404e 131{
f08ff9ca 132 bool all_breakpoints = !strcmp(p_module, "all");
cf2b6056
BB
133 if (p_location != NULL) {
134 if (!strcmp(p_location, "all")) {
f08ff9ca
BB
135 bool found = false;
136 for (size_t i = breakpoints.size(); i > 0; --i) {
137 if (!strcmp(breakpoints[i - 1].module, p_module)) {
138 found = true;
139 Free(breakpoints[i - 1].module);
140 Free(breakpoints[i - 1].batch_file);
141 breakpoints.erase_at(i - 1);
142 }
143 }
144 if (found) {
145 print(DRET_SETTING_CHANGE, "Removed all breakpoints in module '%s'.", p_module);
146 }
147 else {
148 print(DRET_NOTIFICATION, "No breakpoints found in module '%s'.", p_module);
149 }
150 return;
151 }
152 else {
153 if (all_breakpoints) {
154 print(DRET_NOTIFICATION, "Unexpected 2nd argument, when the first "
155 "argument is 'all'.");
156 return;
157 }
cf2b6056
BB
158 int line = 0;
159 char* function = NULL;
160 if (is_numeric(p_location)) {
161 // it's a line breakpoint
162 line = strtol(p_location, NULL, 10);
f08ff9ca 163 }
cf2b6056
BB
164 else {
165 // it's a function breakpoint
166 function = mcopystr(p_location);
167 }
168 char* loc_str = function != NULL ? mprintf("function '%s'", function) :
169 mprintf("line %d", line);
170 size_t pos = find_breakpoint(p_module, line, function);
f08ff9ca
BB
171 if (pos != breakpoints.size()) {
172 Free(breakpoints[pos].module);
cf2b6056 173 Free(breakpoints[pos].function);
f08ff9ca
BB
174 Free(breakpoints[pos].batch_file);
175 breakpoints.erase_at(pos);
cf2b6056
BB
176 print(DRET_SETTING_CHANGE, "Breakpoint removed in module '%s' from %s.",
177 p_module, loc_str);
f08ff9ca
BB
178 }
179 else {
cf2b6056
BB
180 print(DRET_NOTIFICATION, "No breakpoint found in module '%s' at %s.",
181 p_module, loc_str);
f08ff9ca 182 }
cf2b6056 183 Free(loc_str);
f08ff9ca
BB
184 return;
185 }
186 }
187 else if (!all_breakpoints) {
188 print(DRET_NOTIFICATION, "2 arguments expected, when the first argument is "
189 "not 'all'.");
190 return;
191 }
192 // delete all breakpoints if we got this far
193 if (breakpoints.empty()) {
194 print(DRET_NOTIFICATION, "No breakpoints found.");
7329404e
BB
195 }
196 else {
f08ff9ca
BB
197 for (size_t i = 0; i < breakpoints.size(); ++i) {
198 Free(breakpoints[i].module);
cf2b6056 199 Free(breakpoints[i].function);
f08ff9ca
BB
200 Free(breakpoints[i].batch_file);
201 }
202 breakpoints.clear();
203 print(DRET_SETTING_CHANGE, "Removed all breakpoints.");
7329404e
BB
204 }
205}
206
f08ff9ca
BB
207void TTCN3_Debugger::set_automatic_breakpoint(const char* p_event_str,
208 const char* p_state_str,
209 const char* p_batch_file)
7329404e
BB
210{
211 bool new_state;
f08ff9ca 212 if (!strcmp(p_state_str, "on")) {
7329404e
BB
213 new_state = true;
214 }
f08ff9ca 215 else if(!strcmp(p_state_str, "off")) {
7329404e
BB
216 new_state = false;
217 }
7329404e 218 else {
f08ff9ca 219 print(DRET_NOTIFICATION, "Argument 2 is invalid. Expected 'on' or 'off'.");
7329404e
BB
220 return;
221 }
222 const char* sbp_type_str;
223 bool state_changed;
f08ff9ca
BB
224 char** old_batch_file_ptr;
225 if (!strcmp(p_event_str, "fail")) {
226 state_changed = (fail_behavior.trigger != new_state);
227 fail_behavior.trigger = new_state;
228 old_batch_file_ptr = &fail_behavior.batch_file;
229 sbp_type_str = "fail verdict";
230 }
231 else if (!strcmp(p_event_str, "error")) {
232 state_changed = (error_behavior.trigger != new_state);
233 error_behavior.trigger = new_state;
234 old_batch_file_ptr = &error_behavior.batch_file;
235 sbp_type_str = "error verdict";
236 }
237 else {
238 print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'error' or 'fail'.");
7329404e
BB
239 return;
240 }
f08ff9ca
BB
241 if (state_changed) {
242 print(DRET_SETTING_CHANGE, "Automatic breakpoint at %s switched %s%s%s%s.",
243 sbp_type_str, new_state ? "on" : "off",
244 new_state ? (p_batch_file != NULL ? " with batch file '" : " with no batch file") : "",
245 (p_batch_file != NULL && new_state) ? p_batch_file : "",
246 (p_batch_file != NULL && new_state) ? "'" : "");
247 }
248 else {
249 if (new_state) {
250 if (*old_batch_file_ptr != NULL) {
251 if (p_batch_file != NULL) {
252 if (!strcmp(p_batch_file, *old_batch_file_ptr)) {
253 print(DRET_NOTIFICATION, "Automatic breakpoint at %s was already "
254 "switched on with batch file '%s'.", sbp_type_str, p_batch_file);
255 }
256 else {
257 print(DRET_SETTING_CHANGE, "Batch file was changed from '%s' to '%s' "
258 "for automatic breakpoint at %s.", *old_batch_file_ptr, p_batch_file,
259 sbp_type_str);
260 }
261 }
262 else {
263 print(DRET_SETTING_CHANGE, "Batch file '%s' removed from automatic "
264 "breakpoint at %s.", *old_batch_file_ptr, sbp_type_str);
265 }
266 }
267 else {
268 if (p_batch_file != NULL) {
269 print(DRET_SETTING_CHANGE, "Batch file '%s' added to automatic breakpoint "
270 "at %s.", p_batch_file, sbp_type_str);
271 }
272 else {
273 print(DRET_NOTIFICATION, "Automatic breakpoint at %s was already "
274 "switched on with no batch file.", sbp_type_str);
275 }
276 }
277 }
278 else {
279 print(DRET_NOTIFICATION, "Automatic breakpoint at %s was already switched off.");
280 }
281 }
282 Free(*old_batch_file_ptr);
283 *old_batch_file_ptr = p_batch_file != NULL ? mcopystr(p_batch_file) : NULL;
7329404e
BB
284}
285
1cb5b229
BB
286void TTCN3_Debugger::print_settings()
287{
288 // on/off switch
289 add_to_result("Debugger is switched %s.\n", active ? "on" : "off");
290 // output
291 char* final_file_name = finalize_file_name(output_file_name);
292 char* file_str = output_file != NULL ? mprintf("file '%s'", final_file_name) : NULL;
293 Free(final_file_name);
294 add_to_result("Output is printed to %s%s%s.\n",
295 send_to_console ? "the console" : "",
296 (send_to_console && output_file != NULL) ? " and to " : "",
297 output_file != NULL ? file_str : "");
298 Free(file_str);
299 // global batch file
300 add_to_result("Global batch file%s%s.\n", global_batch_file != NULL ? ": " : "",
301 global_batch_file != NULL ? global_batch_file : " not set");
cf2b6056
BB
302 // function call data configuration
303 add_to_result("Function call data ");
304 if (function_calls.cfg == CALLS_TO_FILE) {
305 char* final_file_name2 = finalize_file_name(function_calls.file.name);
306 add_to_result("sent to file '%s'.\n", final_file_name2);
307 Free(final_file_name2);
308 }
309 else {
310 add_to_result("buffer size: ");
311 if (function_calls.cfg == CALLS_STORE_ALL) {
312 add_to_result("infinite.\n");
313 }
314 else {
315 add_to_result("%d.\n", function_calls.buffer.size);
316 }
317 }
1cb5b229
BB
318 // user breakpoints
319 if (breakpoints.empty()) {
320 add_to_result("No user breakpoints.\n");
321 }
322 else {
323 add_to_result("User breakpoints:\n");
324 for (size_t i = 0; i < breakpoints.size(); ++i) {
325 const breakpoint_t& bp = breakpoints[i];
cf2b6056
BB
326 add_to_result("%s ", bp.module);
327 if (bp.function == NULL) {
328 add_to_result("%d", bp.line);
329 }
330 else {
331 add_to_result("%s", bp.function);
332 }
333 if (bp.batch_file != NULL) {
334 add_to_result(" %s", bp.batch_file);
335 }
336 add_to_result("\n");
1cb5b229
BB
337 }
338 }
339 // automatic breakpoints
340 add_to_result("Automatic breakpoints:\nerror %s %s\nfail %s %s",
341 error_behavior.trigger ? "on" : "off",
342 error_behavior.batch_file != NULL ? error_behavior.batch_file : "",
343 fail_behavior.trigger ? "on" : "off",
344 fail_behavior.batch_file != NULL ? fail_behavior.batch_file : "");
345}
346
347#define STACK_LEVEL (stack_level >= 0) ? (size_t)stack_level : (call_stack.size() - 1)
348
7329404e
BB
349void TTCN3_Debugger::print_call_stack()
350{
351 for (size_t i = call_stack.size(); i != 0; --i) {
016a1a93 352 add_to_result("%d.\t", (int)call_stack.size() - (int)i + 1);
f08ff9ca 353 call_stack[i - 1].function->print_function();
1cb5b229
BB
354 if (i - 1 == (STACK_LEVEL)) {
355 // mark the active stack level with an asterisk
356 add_to_result("*");
357 }
016a1a93
BB
358 if (i != 1) {
359 add_to_result("\n");
360 }
7329404e
BB
361 }
362}
363
364void TTCN3_Debugger::set_stack_level(int new_level)
365{
016a1a93
BB
366 if (!halted) {
367 print(DRET_NOTIFICATION, "Stack level can only be set if test execution is halted.");
368 }
369 else if (new_level <= 0 || (size_t)new_level > call_stack.size()) {
370 print(DRET_NOTIFICATION, "Invalid new stack level. Expected 1 - %d.",
371 (int)call_stack.size());
7329404e
BB
372 }
373 else {
016a1a93 374 stack_level = (int)call_stack.size() - new_level;
f08ff9ca 375 call_stack[stack_level].function->print_function();
016a1a93 376 print(DRET_NOTIFICATION, "Stack level set to:\n%d.\t%s", new_level, command_result);
f08ff9ca
BB
377 Free(command_result);
378 command_result = NULL;
379 }
380}
381
f08ff9ca
BB
382void TTCN3_Debugger::print_variable(const char* p_var_name)
383{
384 const variable_t* var = call_stack[STACK_LEVEL].function->find_variable(p_var_name);
385 if (var != NULL) {
1cb5b229
BB
386 add_to_result("[%s] %s%s%s := %s", var->type_name,
387 var->module != NULL ? var->module : "", var->module != NULL ? "." : "",
388 var->name, (const char*)var->print_function(*var));
f08ff9ca
BB
389 }
390 else {
391 add_to_result("Variable '%s' not found.", p_var_name);
7329404e
BB
392 }
393}
394
f08ff9ca
BB
395void TTCN3_Debugger::overwrite_variable(const char* p_var_name,
396 int p_value_element_count,
397 char** p_value_elements)
7329404e 398{
f08ff9ca
BB
399 variable_t* var = call_stack[STACK_LEVEL].function->find_variable(p_var_name);
400 if (var != NULL) {
401 if (var->set_function == NULL) {
402 print(DRET_NOTIFICATION, "Constant variables cannot be overwritten.");
403 }
404 else {
405 char* new_value_str = NULL;
406 for (int i = 0; i < p_value_element_count; ++i) {
407 if (i != 0) {
408 new_value_str = mputc(new_value_str, ' ');
409 }
410 new_value_str = mputstr(new_value_str, p_value_elements[i]);
411 }
412 Module_Param* parsed_mp = process_config_debugger_value(new_value_str);
413 // an error message has already been displayed if parsed_mp is NULL
414 if (parsed_mp != NULL) {
415 try {
416 Debugger_Value_Parsing debug_value_parsing;
417 boolean handled = var->set_function(*var, *parsed_mp);
418 if (!handled) {
419 print(DRET_NOTIFICATION, "Variables of type '%s' cannot be overwritten.",
420 var->type_name);
421 }
422 else {
423 add_to_result("[%s] %s := %s", var->type_name, var->name,
424 (const char*)var->print_function(*var));
425 }
426 }
427 catch (const TC_Error&) {
428 // do nothing, an error message has already been displayed in this case
429 }
430 delete parsed_mp;
431 }
432 }
433 }
434 else {
435 print(DRET_NOTIFICATION, "Variable '%s' not found.", p_var_name);
436 }
7329404e
BB
437}
438
cf2b6056
BB
439void TTCN3_Debugger::clean_up_function_calls()
440{
441 if (function_calls.cfg == CALLS_TO_FILE) {
442 if (!TTCN_Runtime::is_hc()) {
443 fclose(function_calls.file.ptr);
444 }
445 Free(function_calls.file.name);
446 }
447 else if (!TTCN_Runtime::is_hc()) {
448 if (function_calls.buffer.size != 0) {
449 if (function_calls.buffer.end != -1) {
450 for (int i = function_calls.buffer.start;
451 i != function_calls.buffer.end;
452 i = (i + 1) % function_calls.buffer.size) {
453 Free(function_calls.buffer.ptr[i]);
454 }
455 Free(function_calls.buffer.ptr[function_calls.buffer.end]);
456 }
457 Free(function_calls.buffer.ptr);
458 }
459 }
460}
461
462void TTCN3_Debugger::configure_function_calls(const char* p_config,
463 const char* p_file_name)
464{
465 function_call_data_config_t cfg;
466 // check the command's parameters before actually changing anything
467 if (strcmp(p_config, "file") == 0) {
468 cfg = CALLS_TO_FILE;
469 }
470 else if (strcmp(p_config, "all") == 0) {
471 cfg = CALLS_STORE_ALL;
472 }
473 else if (is_numeric(p_config)) {
474 cfg = CALLS_RING_BUFFER;
475 }
476 else {
477 print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'file', 'all' or "
478 "ring buffer size.");
479 return;
480 }
481 FILE* new_fp = NULL;
482 char* final_file_name = NULL;
483 bool same_setting = false;
484 int new_size = 0;
485 switch (cfg) {
486 case CALLS_TO_FILE:
487 if (p_file_name != NULL) {
488 if (function_calls.cfg == CALLS_TO_FILE &&
489 strcmp(p_file_name, function_calls.file.name) == 0) {
490 // don't reopen it if it's the same file as before
491 same_setting = true;
492 }
493 else if (!TTCN_Runtime::is_hc()) {
494 // don't open any files on HCs, just store settings for future PTCs
495 final_file_name = finalize_file_name(p_file_name);
496 new_fp = fopen(final_file_name, TTCN_Runtime::is_mtc() ? "w" : "a");
497 if (new_fp == NULL) {
498 print(DRET_NOTIFICATION, "Failed to open file '%s' for writing.", final_file_name);
499 Free(final_file_name);
500 return;
501 }
502 }
503 }
504 else {
505 print(DRET_NOTIFICATION, "Argument 2 (file name) is missing.");
506 return;
507 }
508 break;
509 case CALLS_RING_BUFFER:
510 new_size = strtol(p_config, NULL, 10);
511 same_setting = function_calls.cfg == CALLS_RING_BUFFER &&
512 function_calls.buffer.size == new_size;
513 break;
514 case CALLS_STORE_ALL:
515 same_setting = function_calls.cfg == CALLS_STORE_ALL;
516 break;
517 }
518 if (!same_setting) {
519 clean_up_function_calls();
520 function_calls.cfg = cfg;
521 switch (cfg) {
522 case CALLS_TO_FILE:
523 function_calls.file.name = mcopystr(p_file_name);
524 if (!TTCN_Runtime::is_hc()) {
525 function_calls.file.ptr = new_fp;
526 }
527 break;
528 case CALLS_RING_BUFFER:
529 function_calls.buffer.size = new_size;
530 // no break
531 case CALLS_STORE_ALL:
532 function_calls.buffer.start = 0;
533 function_calls.buffer.end = -1;
534 if (new_size != 0 && !TTCN_Runtime::is_hc()) {
535 // don't allocate buffers on HCs, just store settings for future PTCs
536 function_calls.buffer.ptr = (char**)Malloc(new_size * sizeof(char*));
537 }
538 else {
539 function_calls.buffer.ptr = NULL;
540 }
541 break;
542 }
543 }
544 switch (cfg) {
545 case CALLS_TO_FILE:
546 print(DRET_SETTING_CHANGE, "Debugger %sset to not store function call data, but to "
547 "send them to file '%s'.", same_setting ? "was already " : "", final_file_name);
548 Free(final_file_name);
549 break;
550 case CALLS_RING_BUFFER:
551 if (new_size == 0) {
552 print(DRET_SETTING_CHANGE, "Debugger %sset to not store function call data.",
553 same_setting ? "was already " : "");
554 }
555 else {
556 print(DRET_SETTING_CHANGE, "Debugger %sset to store only the last %d function calls.",
557 same_setting ? "was already " : "", new_size);
558 }
559 break;
560 case CALLS_STORE_ALL:
561 print(DRET_SETTING_CHANGE, "Debugger %sset to store all function call data.",
562 same_setting ? "was already " : "");
563 break;
564 }
565}
566
567void TTCN3_Debugger::print_function_calls(const char* p_amount)
568{
569 if (function_calls.cfg == CALLS_TO_FILE || function_calls.buffer.size == 0 ||
570 function_calls.buffer.end == -1) {
571 print(DRET_NOTIFICATION, "No function calls are stored.");
572 return;
573 }
574 int amount;
575 int limit = (function_calls.cfg == CALLS_RING_BUFFER &&
576 function_calls.buffer.start == (function_calls.buffer.end + 1) %
577 function_calls.buffer.size) ?
578 function_calls.buffer.size : function_calls.buffer.end + 1;
788b5893 579 bool invalid_arg = false;
cf2b6056
BB
580 if (p_amount == NULL || strcmp(p_amount, "all") == 0) {
581 amount = limit;
582 }
583 else if (is_numeric(p_amount)) {
584 amount = strtol(p_amount, NULL, 10);
788b5893
BB
585 if (amount == 0) {
586 invalid_arg = true;
587 }
588 else if (amount > limit) {
589 amount = limit;
cf2b6056
BB
590 }
591 }
592 else {
788b5893
BB
593 invalid_arg = true;
594 }
595 if (invalid_arg) {
596 print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'all' or non-zero "
597 "integer value (number of calls).");
cf2b6056
BB
598 return;
599 }
600 for (int i = (function_calls.buffer.end - amount + function_calls.buffer.size + 1) %
601 function_calls.buffer.size; amount > 0;
602 i = (i + 1) % function_calls.buffer.size, --amount) {
603 add_to_result(function_calls.buffer.ptr[i]);
604 if (amount > 1) {
605 add_to_result("\n");
606 }
607 }
608}
609
7329404e
BB
610void TTCN3_Debugger::set_output(const char* p_output_type, const char* p_file_name)
611{
016a1a93
BB
612 FILE* new_fp = NULL;
613 bool file, console;
614 bool same_file = false;
615 char* final_file_name = NULL;
616 // check the command's parameters before actually changing anything
617 if (!strcmp(p_output_type, "console")) {
618 file = false;
619 console = true;
7329404e
BB
620 }
621 else if (!strcmp(p_output_type, "file")) {
016a1a93
BB
622 file = true;
623 console = false;
624 }
625 else if (!strcmp(p_output_type, "both")) {
626 file = true;
627 console = true;
628 }
629 else {
630 print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'console', 'file' or 'both'.");
631 return;
632 }
633 if (file) {
7329404e 634 if (p_file_name == NULL) {
016a1a93 635 print(DRET_NOTIFICATION, "Argument 2 (output file name) is missing.");
7329404e
BB
636 return;
637 }
016a1a93
BB
638 if (output_file_name != NULL && !strcmp(p_file_name, output_file_name)) {
639 // don't reopen it if it's the same file as before
640 same_file = true;
641 }
642 else if (!TTCN_Runtime::is_hc()) {
f08ff9ca 643 // don't open any files on HCs, just store settings for future PTCs
016a1a93 644 final_file_name = finalize_file_name(p_file_name);
f08ff9ca 645 new_fp = fopen(final_file_name, TTCN_Runtime::is_mtc() ? "w" : "a");
016a1a93
BB
646 if (new_fp == NULL) {
647 print(DRET_NOTIFICATION, "Failed to open file '%s' for writing.", final_file_name);
f08ff9ca 648 Free(final_file_name);
016a1a93
BB
649 return;
650 }
7329404e
BB
651 }
652 }
016a1a93 653 // print the change notification to the old output
f08ff9ca 654 char* file_str = file ? mprintf("file '%s'", final_file_name) : NULL;
016a1a93
BB
655 Free(final_file_name);
656 print(DRET_SETTING_CHANGE, "Debugger set to print its output to %s%s%s.",
657 console ? "the console" : "", (console && file) ? " and to " : "",
658 file ? file_str : "");
659 if (file) {
660 Free(file_str);
661 }
662 if (!same_file && !TTCN_Runtime::is_hc()) {
663 if (output_file != NULL) {
664 fclose(output_file);
665 }
666 output_file = new_fp;
667 }
668 send_to_console = console;
669 Free(output_file_name);
670 if (file) {
671 output_file_name = mcopystr(p_file_name);
672 }
673}
674
f08ff9ca
BB
675void TTCN3_Debugger::set_global_batch_file(const char* p_state_str,
676 const char* p_file_name)
677{
678 bool delete_old = false;
679 bool copy_new = false;
680 if (!strcmp(p_state_str, "on")) {
681 if (p_file_name != NULL) {
682 if (global_batch_file != NULL) {
683 if (!strcmp(p_file_name, global_batch_file)) {
684 print(DRET_NOTIFICATION, "Global batch file was already switched on "
685 "and set to '%s'.", p_file_name);
686 }
687 else {
688 print(DRET_SETTING_CHANGE, "Global batch file changed from '%s' to '%s'.",
689 global_batch_file, p_file_name);
690 delete_old = true;
691 copy_new = true;
692 }
693 }
694 else {
695 print(DRET_SETTING_CHANGE, "Global batch file switched on and set to '%s'.",
696 p_file_name);
697 copy_new = true;
698 }
699 }
700 else {
701 print(DRET_NOTIFICATION, "Missing batch file name argument.");
702 }
703 }
704 else if (!strcmp(p_state_str, "off")) {
705 if (global_batch_file != NULL) {
706 print(DRET_SETTING_CHANGE, "Global batch file switched off.");
707 delete_old = true;
708 }
709 else {
710 print(DRET_NOTIFICATION, "Global batch file was already switched off.");
711 }
712 }
713 else {
714 print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'on' or 'off'.");
715 }
716 if (delete_old) {
717 Free(global_batch_file);
718 global_batch_file = NULL;
719 }
720 if (copy_new) {
721 global_batch_file = mcopystr(p_file_name);
722 }
723}
724
725void TTCN3_Debugger::step(stepping_t p_stepping_type)
726{
727 if (!halted) {
728 print(DRET_NOTIFICATION, "Stepping commands can only be used when test "
729 "execution is halted.");
730 return;
731 }
732 stepping_type = p_stepping_type;
733 stepping_stack_size = call_stack.size();
734 if (!TTCN_Runtime::is_single()) {
735 TTCN_Communication::send_debug_continue_req();
736 }
737 resume();
738}
739
cf2b6056 740void TTCN3_Debugger::run_to_cursor(const char* p_module, const char* p_location)
f08ff9ca
BB
741{
742 // all processes receive this command, since the specified location might be
743 // reached in any process, even a PTC that hasn't been created yet
744 if (!halted) {
745 print(DRET_NOTIFICATION, "The 'run to' command can only be used when test "
746 "execution is halted.");
747 return;
748 }
749 temporary_breakpoint.module = mcopystr(p_module);
cf2b6056
BB
750 if (is_numeric(p_location)) {
751 temporary_breakpoint.line = strtol(p_location, NULL, 10);
752 temporary_breakpoint.function = NULL;
753 }
754 else {
755 temporary_breakpoint.line = 0;
756 temporary_breakpoint.function = mcopystr(p_location);
757 }
f08ff9ca
BB
758 resume();
759}
760
761void TTCN3_Debugger::halt(const char* p_batch_file, bool p_run_global_batch)
016a1a93
BB
762{
763 if (!halted) {
764 halted = true;
f08ff9ca
BB
765 Free(temporary_breakpoint.module);
766 temporary_breakpoint.module = NULL;
767 temporary_breakpoint.line = 0;
cf2b6056
BB
768 Free(temporary_breakpoint.function);
769 temporary_breakpoint.function = NULL;
f08ff9ca
BB
770 if (!TTCN_Runtime::is_hc()) {
771 stepping_type = NOT_STEPPING;
772 stack_level = call_stack.size() - 1;
773 print(DRET_NOTIFICATION, "Test execution halted.");
774 if (p_batch_file != NULL) {
775 if (TTCN_Runtime::is_single()) {
776 TTCN_Debugger_UI::execute_batch_file(p_batch_file);
777 }
778 else {
779 TTCN_Communication::send_debug_batch(p_batch_file);
780 }
781 }
782 else if (p_run_global_batch && global_batch_file != NULL) {
783 if (TTCN_Runtime::is_single()) {
784 TTCN_Debugger_UI::execute_batch_file(global_batch_file);
785 }
786 else {
787 TTCN_Communication::send_debug_batch(global_batch_file);
788 }
789 }
790 if (TTCN_Runtime::is_single()) {
791 if (halted && !halt_at_start) {
792 resume();
793 }
794 else {
795 TTCN_Debugger_UI::read_loop();
796 }
797 }
798 else {
799 TTCN_Communication::process_debug_messages();
800 }
801 }
016a1a93 802 }
7329404e 803 else {
016a1a93
BB
804 print(DRET_NOTIFICATION, "Test execution is already halted.");
805 }
806}
807
808void TTCN3_Debugger::resume()
809{
810 if (halted) {
811 halted = false;
812 stack_level = -1;
813 print(DRET_NOTIFICATION, "Test execution resumed.");
814 }
815 else {
816 print(DRET_NOTIFICATION, "Test execution is not halted.");
7329404e 817 }
016a1a93
BB
818}
819
820void TTCN3_Debugger::exit_(const char* p_what)
821{
016a1a93 822 if (!strcmp(p_what, "test")) {
f08ff9ca 823 exiting = false;
016a1a93
BB
824 }
825 else if (!strcmp(p_what, "all")) {
f08ff9ca 826 exiting = true;
016a1a93
BB
827 }
828 else {
829 print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'test' or 'all'.");
830 return;
7329404e 831 }
016a1a93 832 halted = false;
f08ff9ca
BB
833 if (!TTCN_Runtime::is_hc()) {
834 print((exiting && TTCN_Runtime::is_mtc()) ? DRET_EXIT_ALL : DRET_NOTIFICATION,
835 "Exiting %s.", exiting ? "test execution" : "current test");
836 TTCN_Runtime::stop_execution();
837 }
7329404e
BB
838}
839
cf2b6056
BB
840size_t TTCN3_Debugger::find_breakpoint(const char* p_module, int p_line,
841 const char* p_function) const
7329404e
BB
842{
843 for (size_t i = 0; i < breakpoints.size(); ++i) {
cf2b6056
BB
844 if (!strcmp(breakpoints[i].module, p_module) &&
845 ((p_line != 0 && breakpoints[i].line == p_line) ||
846 (p_function != NULL && breakpoints[i].function != NULL &&
847 strcmp(breakpoints[i].function, p_function) == 0))) {
7329404e
BB
848 return i;
849 }
850 }
851 return breakpoints.size();
852}
853
854TTCN3_Debugger::variable_t* TTCN3_Debugger::find_variable(const void* p_value) const
855{
856 for (size_t i = 0; i < variables.size(); ++i) {
857 if (variables[i]->value == p_value) {
858 return variables[i];
859 }
860 }
861 return NULL;
862}
863
016a1a93
BB
864char* TTCN3_Debugger::finalize_file_name(const char* p_file_name_skeleton)
865{
866 if (p_file_name_skeleton == NULL) {
867 return NULL;
868 }
869 size_t len = strlen(p_file_name_skeleton);
870 size_t next_idx = 0;
871 char* ret_val = NULL;
872 for (size_t i = 0; i < len - 1; ++i) {
873 if (p_file_name_skeleton[i] == '%') {
874 ret_val = mputstrn(ret_val, p_file_name_skeleton + next_idx, i - next_idx);
875 switch (p_file_name_skeleton[i + 1]) {
876 case 'e': // %e -> executable name
877 ret_val = mputstr(ret_val, TTCN_Logger::get_executable_name());
878 break;
879 case 'h': // %h -> host name
880 ret_val = mputstr(ret_val, TTCN_Runtime::get_host_name());
881 break;
882 case 'p': // %p -> process ID
883 ret_val = mputprintf(ret_val, "%ld", (long)getpid());
884 break;
885 case 'l': { // %l -> login name
886 setpwent();
887 struct passwd *p = getpwuid(getuid());
888 if (NULL != p) {
889 ret_val = mputstr(ret_val, p->pw_name);
890 }
891 endpwent();
892 break; }
893 case 'r': // %r -> component reference
894 if (TTCN_Runtime::is_single()) {
895 ret_val = mputstr(ret_val, "single");
896 }
897 else if (TTCN_Runtime::is_mtc()) {
898 ret_val = mputstr(ret_val, "mtc");
899 }
900 else if (TTCN_Runtime::is_ptc()) {
901 ret_val = mputprintf(ret_val, "%d", (component)self);
902 }
903 break;
904 case 'n': // %n -> component name
905 if (TTCN_Runtime::is_mtc()) {
906 ret_val = mputstr(ret_val, "MTC");
907 }
908 else if (TTCN_Runtime::is_ptc()) {
909 ret_val = mputstr(ret_val, TTCN_Runtime::get_component_name());
910 }
911 break;
912 case '%': // %% -> single %
913 ret_val = mputc(ret_val, '%');
914 break;
915 default: // unknown sequence -> leave it as it is
916 ret_val = mputstrn(ret_val, p_file_name_skeleton + i, 2);
917 break;
918 }
919 next_idx = i + 2;
920 ++i;
921 }
922 }
923 if (next_idx < len) {
924 ret_val = mputstr(ret_val, p_file_name_skeleton + next_idx);
925 }
926 return ret_val;
927}
928
f08ff9ca
BB
929void TTCN3_Debugger::test_execution_started()
930{
cf2b6056
BB
931 if (function_calls.cfg != CALLS_TO_FILE) {
932 if (function_calls.buffer.size != 0 && function_calls.buffer.end != -1) {
933 for (int i = function_calls.buffer.start;
934 i != function_calls.buffer.end;
935 i = (i + 1) % function_calls.buffer.size) {
936 Free(function_calls.buffer.ptr[i]);
937 }
938 Free(function_calls.buffer.ptr[function_calls.buffer.end]);
939 }
940 if (function_calls.cfg == CALLS_STORE_ALL) {
941 Free(function_calls.buffer.ptr);
942 function_calls.buffer.ptr = NULL;
943 }
944 function_calls.buffer.start = 0;
945 function_calls.buffer.end = -1;
946 }
f08ff9ca
BB
947 exiting = false;
948 if (TTCN_Runtime::is_single()) {
949 TTCN_Debugger_UI::init();
950 if (initial_batch_file) {
951 halt(initial_batch_file, false);
952 }
953 else if (halt_at_start) {
954 halt(NULL, false);
955 }
956 }
f57971fe 957 halt_at_start = true;
f08ff9ca
BB
958}
959
960void TTCN3_Debugger::test_execution_finished()
961{
962 stepping_type = NOT_STEPPING;
963 Free(temporary_breakpoint.module);
964 temporary_breakpoint.module = NULL;
965 temporary_breakpoint.line = 0;
cf2b6056
BB
966 Free(temporary_breakpoint.function);
967 temporary_breakpoint.function = NULL;
f08ff9ca
BB
968 last_breakpoint_entry.module = NULL;
969 last_breakpoint_entry.line = 0;
cf2b6056 970 last_breakpoint_entry.stack_size = 0;
f08ff9ca
BB
971 if (TTCN_Runtime::is_single()) {
972 TTCN_Debugger_UI::clean_up();
973 }
974}
975
016a1a93
BB
976void TTCN3_Debugger::print(int return_type, const char* fmt, ...) const
977{
f08ff9ca
BB
978 if (TTCN_Runtime::is_hc()) {
979 // don't display anything while on the HC process
980 return;
981 }
016a1a93
BB
982 va_list parameters;
983 va_start(parameters, fmt);
984 char* str = mprintf_va_list(fmt, parameters);
985 va_end(parameters);
f08ff9ca
BB
986 if (TTCN_Runtime::is_single()) {
987 if (send_to_console) {
988 TTCN_Debugger_UI::print(str);
989 }
990 }
991 else {
992 TTCN_Communication::send_debug_return_value(return_type, send_to_console ? str : NULL);
993 }
016a1a93 994 if (output_file != NULL) {
f08ff9ca
BB
995 fseek(output_file, 0, SEEK_END); // in case multiple processes are writing the same file
996 fputs(str, output_file);
997 fputc('\n', output_file);
016a1a93
BB
998 fflush(output_file);
999 }
1000 Free(str);
1001}
1002
7329404e
BB
1003TTCN3_Debugger::TTCN3_Debugger()
1004{
016a1a93 1005 enabled = false;
7329404e 1006 active = false;
016a1a93
BB
1007 halted = false;
1008 output_file = NULL;
1009 output_file_name = NULL;
1010 send_to_console = true;
cf2b6056
BB
1011 function_calls.cfg = CALLS_STORE_ALL;
1012 function_calls.buffer.size = 0;
1013 function_calls.buffer.start = 0;
1014 function_calls.buffer.end = -1;
1015 function_calls.buffer.ptr = NULL;
7329404e
BB
1016 last_breakpoint_entry.module = NULL;
1017 last_breakpoint_entry.line = 0;
cf2b6056 1018 last_breakpoint_entry.stack_size = 0;
7329404e 1019 stack_level = -1;
f08ff9ca
BB
1020 fail_behavior.trigger = false;
1021 fail_behavior.batch_file = NULL;
1022 error_behavior.trigger = false;
1023 error_behavior.batch_file = NULL;
1024 global_batch_file = NULL;
016a1a93 1025 command_result = NULL;
f08ff9ca
BB
1026 last_variable_list = NULL;
1027 stepping_type = NOT_STEPPING;
1028 stepping_stack_size = 0;
1029 temporary_breakpoint.module = NULL;
1030 temporary_breakpoint.line = 0;
cf2b6056 1031 temporary_breakpoint.function = NULL;
f08ff9ca
BB
1032 temporary_breakpoint.batch_file = NULL; // not used
1033 exiting = false;
1034 halt_at_start = false;
1035 initial_batch_file = NULL;
7329404e
BB
1036}
1037
1038TTCN3_Debugger::~TTCN3_Debugger()
1039{
016a1a93
BB
1040 if (output_file != NULL) {
1041 fclose(output_file);
1042 Free(output_file_name);
7329404e
BB
1043 }
1044 for (size_t i = 0; i < breakpoints.size(); ++i) {
1045 Free(breakpoints[i].module);
cf2b6056 1046 Free(breakpoints[i].function);
f08ff9ca 1047 Free(breakpoints[i].batch_file);
7329404e
BB
1048 }
1049 for (size_t i = 0; i < global_scopes.size(); ++i) {
1050 delete global_scopes[i].scope;
1051 }
1052 for (size_t i = 0; i < component_scopes.size(); ++i) {
1053 delete component_scopes[i].scope;
1054 }
1055 for (size_t i = 0; i < variables.size(); ++i) {
1056 delete variables[i];
1057 }
f08ff9ca
BB
1058 Free(fail_behavior.batch_file);
1059 Free(error_behavior.batch_file);
1060 Free(global_batch_file);
cf2b6056 1061 clean_up_function_calls();
f08ff9ca 1062 Free(last_variable_list);
7329404e
BB
1063}
1064
1065TTCN3_Debug_Scope* TTCN3_Debugger::add_global_scope(const char* p_module)
1066{
1067 named_scope_t global_scope;
1068 global_scope.name = p_module;
1069 global_scope.scope = new TTCN3_Debug_Scope();
1070 global_scopes.push_back(global_scope);
1071 return global_scope.scope;
1072}
1073
1074TTCN3_Debug_Scope* TTCN3_Debugger::add_component_scope(const char* p_component)
1075{
1076 named_scope_t component_scope;
1077 component_scope.name = p_component;
1078 component_scope.scope = new TTCN3_Debug_Scope();
1079 component_scopes.push_back(component_scope);
1080 return component_scope.scope;
1081}
1082
1083void TTCN3_Debugger::set_return_value(const CHARSTRING& p_value)
1084{
016a1a93 1085 if (active && !call_stack.empty()) {
f08ff9ca 1086 call_stack[call_stack.size() - 1].function->set_return_value(p_value);
7329404e
BB
1087 }
1088}
1089
f08ff9ca 1090void TTCN3_Debugger::breakpoint_entry(int p_line)
7329404e
BB
1091{
1092 if (active && !call_stack.empty()) {
f08ff9ca 1093 const char* module_name = call_stack[call_stack.size() - 1].function->get_module_name();
7329404e
BB
1094 bool trigger = false;
1095 const char* trigger_type;
1096 int actual_line;
f08ff9ca 1097 const char* batch_file = NULL;
7329404e
BB
1098 switch (p_line) {
1099 case SBP_FAIL_VERDICT:
f08ff9ca
BB
1100 trigger = fail_behavior.trigger;
1101 trigger_type = "Automatic breakpoint (fail verdict) reached at";
1102 actual_line = TTCN_Location::get_line_number();
1103 batch_file = fail_behavior.batch_file;
7329404e
BB
1104 break;
1105 case SBP_ERROR_VERDICT:
f08ff9ca
BB
1106 trigger = error_behavior.trigger;
1107 trigger_type = "Automatic breakpoint (error verdict) reached at";
1108 actual_line = TTCN_Location::get_line_number();
1109 batch_file = error_behavior.batch_file;
7329404e 1110 break;
016a1a93 1111 default: // code lines
f08ff9ca
BB
1112 // first: make sure it's not the same breakpoint entry as last time
1113 if (p_line == last_breakpoint_entry.line &&
1114 module_name == last_breakpoint_entry.module) {
1115 break;
1116 }
7329404e 1117 actual_line = p_line;
f08ff9ca
BB
1118 // second: check if a stepping operation ends here
1119 if (stepping_type == STEP_INTO ||
1120 (stepping_type == STEP_OVER && call_stack.size() <= stepping_stack_size) ||
1121 (stepping_type == STEP_OUT && call_stack.size() < stepping_stack_size)) {
1122 trigger = true;
1123 trigger_type = "Stepped to";
1124 break;
1125 }
cf2b6056
BB
1126 const char* function_name =
1127 call_stack[call_stack.size() - 1].function->get_function_name();
f08ff9ca 1128 // third: check if this is the destination of a 'run to cursor' operation
cf2b6056
BB
1129 if (temporary_breakpoint.module != NULL &&
1130 strcmp(module_name, temporary_breakpoint.module) == 0 &&
1131 (p_line == temporary_breakpoint.line ||
1132 (temporary_breakpoint.function != NULL &&
1133 last_breakpoint_entry.stack_size == call_stack.size() - 1 &&
1134 strcmp(temporary_breakpoint.function, function_name) == 0))) {
f08ff9ca
BB
1135 trigger = true;
1136 trigger_type = "Ran to";
1137 break;
1138 }
1139 // fourth: check if the location matches one of the breakpoints in the list
cf2b6056
BB
1140 size_t pos = find_breakpoint(module_name, p_line, NULL);
1141 if (pos == breakpoints.size() &&
1142 last_breakpoint_entry.stack_size == call_stack.size() - 1) {
1143 // this is the first breakpoint entry in the function,
1144 // check for a matching function breakpoint
1145 pos = find_breakpoint(module_name, 0, function_name);
1146 }
f08ff9ca
BB
1147 if (pos != breakpoints.size()) {
1148 trigger = true;
1149 batch_file = breakpoints[pos].batch_file;
1150 }
1151 trigger_type = "User breakpoint reached at";
7329404e
BB
1152 break;
1153 }
7329404e 1154 if (trigger) {
f08ff9ca
BB
1155 print(DRET_NOTIFICATION, "%s line %d in module '%s'.",
1156 trigger_type, actual_line, module_name);
1157 if (!TTCN_Runtime::is_single()) {
1158 TTCN_Communication::send_debug_halt_req();
1159 }
1160 halt(batch_file, true);
7329404e 1161 }
cf2b6056 1162 last_breakpoint_entry.module = module_name;
7329404e 1163 last_breakpoint_entry.line = p_line;
cf2b6056 1164 last_breakpoint_entry.stack_size = call_stack.size();
7329404e
BB
1165 }
1166}
1167
1168CHARSTRING TTCN3_Debugger::print_base_var(const TTCN3_Debugger::variable_t& p_var)
1169{
f08ff9ca 1170 const void* ptr = p_var.set_function != NULL ? p_var.value : p_var.cvalue;
7329404e
BB
1171 TTCN_Logger::begin_event_log2str();
1172 if (!strcmp(p_var.type_name, "bitstring")) {
f08ff9ca 1173 ((const BITSTRING*)ptr)->log();
7329404e
BB
1174 }
1175 else if (!strcmp(p_var.type_name, "bitstring template")) {
f08ff9ca 1176 ((const BITSTRING_template*)ptr)->log();
7329404e
BB
1177 }
1178 else if (!strcmp(p_var.type_name, "boolean")) {
f08ff9ca 1179 ((const BOOLEAN*)ptr)->log();
7329404e
BB
1180 }
1181 else if (!strcmp(p_var.type_name, "boolean template")) {
f08ff9ca 1182 ((const BOOLEAN_template*)ptr)->log();
7329404e
BB
1183 }
1184 else if (!strcmp(p_var.type_name, "charstring")) {
f08ff9ca 1185 ((const CHARSTRING*)ptr)->log();
7329404e
BB
1186 }
1187 else if (!strcmp(p_var.type_name, "charstring template")) {
f08ff9ca 1188 ((const CHARSTRING_template*)ptr)->log();
7329404e
BB
1189 }
1190 else if (!strcmp(p_var.type_name, "float")) {
f08ff9ca 1191 ((const FLOAT*)ptr)->log();
7329404e
BB
1192 }
1193 else if (!strcmp(p_var.type_name, "float template")) {
f08ff9ca 1194 ((const FLOAT_template*)ptr)->log();
7329404e
BB
1195 }
1196 else if (!strcmp(p_var.type_name, "hexstring")) {
f08ff9ca 1197 ((const HEXSTRING*)ptr)->log();
7329404e
BB
1198 }
1199 else if (!strcmp(p_var.type_name, "hexstring template")) {
f08ff9ca 1200 ((const HEXSTRING_template*)ptr)->log();
7329404e
BB
1201 }
1202 else if (!strcmp(p_var.type_name, "integer")) {
f08ff9ca 1203 ((const INTEGER*)ptr)->log();
7329404e
BB
1204 }
1205 else if (!strcmp(p_var.type_name, "integer template")) {
f08ff9ca 1206 ((const INTEGER_template*)ptr)->log();
7329404e
BB
1207 }
1208 else if (!strcmp(p_var.type_name, "objid")) {
f08ff9ca 1209 ((const OBJID*)ptr)->log();
7329404e
BB
1210 }
1211 else if (!strcmp(p_var.type_name, "objid template")) {
f08ff9ca 1212 ((const OBJID_template*)ptr)->log();
7329404e
BB
1213 }
1214 else if (!strcmp(p_var.type_name, "octetstring")) {
f08ff9ca 1215 ((const OCTETSTRING*)ptr)->log();
7329404e
BB
1216 }
1217 else if (!strcmp(p_var.type_name, "octetstring template")) {
f08ff9ca 1218 ((const OCTETSTRING_template*)ptr)->log();
7329404e
BB
1219 }
1220 else if (!strcmp(p_var.type_name, "universal charstring")) {
f08ff9ca 1221 ((const UNIVERSAL_CHARSTRING*)ptr)->log();
7329404e
BB
1222 }
1223 else if (!strcmp(p_var.type_name, "universal charstring template")) {
f08ff9ca 1224 ((const UNIVERSAL_CHARSTRING_template*)ptr)->log();
7329404e
BB
1225 }
1226 else if (!strcmp(p_var.type_name, "verdicttype")) {
f08ff9ca 1227 ((const VERDICTTYPE*)ptr)->log();
7329404e
BB
1228 }
1229 else if (!strcmp(p_var.type_name, "verdicttype template")) {
f08ff9ca 1230 ((const VERDICTTYPE_template*)ptr)->log();
7329404e
BB
1231 }
1232 else if (!strcmp(p_var.type_name, "component")) {
f08ff9ca 1233 ((const COMPONENT*)ptr)->log();
7329404e
BB
1234 }
1235 else if (!strcmp(p_var.type_name, "component template")) {
f08ff9ca 1236 ((const COMPONENT_template*)ptr)->log();
7329404e 1237 }
788b5893
BB
1238 else if (!strcmp(p_var.type_name, "port")) {
1239 ((const PORT*)ptr)->log(); // virtual
1240 }
7329404e 1241 else if (!strcmp(p_var.type_name, "default")) {
f08ff9ca 1242 ((const DEFAULT*)ptr)->log();
7329404e
BB
1243 }
1244 else if (!strcmp(p_var.type_name, "default template")) {
f08ff9ca 1245 ((const DEFAULT_template*)ptr)->log();
7329404e
BB
1246 }
1247 else if (!strcmp(p_var.type_name, "timer")) {
f08ff9ca 1248 ((const TIMER*)ptr)->log();
7329404e
BB
1249 }
1250 else if (!strcmp(p_var.type_name, "NULL")) {
f08ff9ca 1251 ((const ASN_NULL*)ptr)->log();
7329404e
BB
1252 }
1253 else if (!strcmp(p_var.type_name, "NULL template")) {
f08ff9ca 1254 ((const ASN_NULL_template*)ptr)->log();
7329404e
BB
1255 }
1256 else if (!strcmp(p_var.type_name, "CHARACTER STRING")) {
f08ff9ca 1257 ((const CHARACTER_STRING*)ptr)->log();
7329404e
BB
1258 }
1259 else if (!strcmp(p_var.type_name, "CHARACTER STRING template")) {
f08ff9ca 1260 ((const CHARACTER_STRING_template*)ptr)->log();
7329404e
BB
1261 }
1262 else if (!strcmp(p_var.type_name, "EMBEDDED PDV")) {
f08ff9ca 1263 ((const EMBEDDED_PDV*)ptr)->log();
7329404e
BB
1264 }
1265 else if (!strcmp(p_var.type_name, "EMBEDDED PDV template")) {
f08ff9ca 1266 ((const EMBEDDED_PDV_template*)ptr)->log();
7329404e
BB
1267 }
1268 else if (!strcmp(p_var.type_name, "EXTERNAL")) {
f08ff9ca 1269 ((const EXTERNAL*)ptr)->log();
7329404e
BB
1270 }
1271 else if (!strcmp(p_var.type_name, "EXTERNAL template")) {
f08ff9ca 1272 ((const EXTERNAL_template*)ptr)->log();
7329404e
BB
1273 }
1274 else {
1275 TTCN_Logger::log_event_str("<unrecognized value or template>");
1276 }
1277 return TTCN_Logger::end_event_log2str();
1278}
1279
f08ff9ca
BB
1280boolean TTCN3_Debugger::set_base_var(variable_t& p_var, Module_Param& p_new_value)
1281{
1282 if (!strcmp(p_var.type_name, "bitstring")) {
1283 ((BITSTRING*)p_var.value)->set_param(p_new_value);
1284 }
1285 else if (!strcmp(p_var.type_name, "bitstring template")) {
1286 ((BITSTRING_template*)p_var.value)->set_param(p_new_value);
1287 }
1288 else if (!strcmp(p_var.type_name, "boolean")) {
1289 ((BOOLEAN*)p_var.value)->set_param(p_new_value);
1290 }
1291 else if (!strcmp(p_var.type_name, "boolean template")) {
1292 ((BOOLEAN_template*)p_var.value)->set_param(p_new_value);
1293 }
1294 else if (!strcmp(p_var.type_name, "charstring")) {
1295 ((CHARSTRING*)p_var.value)->set_param(p_new_value);
1296 }
1297 else if (!strcmp(p_var.type_name, "charstring template")) {
1298 ((CHARSTRING_template*)p_var.value)->set_param(p_new_value);
1299 }
1300 else if (!strcmp(p_var.type_name, "float")) {
1301 ((FLOAT*)p_var.value)->set_param(p_new_value);
1302 }
1303 else if (!strcmp(p_var.type_name, "float template")) {
1304 ((FLOAT_template*)p_var.value)->set_param(p_new_value);
1305 }
1306 else if (!strcmp(p_var.type_name, "hexstring")) {
1307 ((HEXSTRING*)p_var.value)->set_param(p_new_value);
1308 }
1309 else if (!strcmp(p_var.type_name, "hexstring template")) {
1310 ((HEXSTRING_template*)p_var.value)->set_param(p_new_value);
1311 }
1312 else if (!strcmp(p_var.type_name, "integer")) {
1313 ((INTEGER*)p_var.value)->set_param(p_new_value);
1314 }
1315 else if (!strcmp(p_var.type_name, "integer template")) {
1316 ((INTEGER_template*)p_var.value)->set_param(p_new_value);
1317 }
1318 else if (!strcmp(p_var.type_name, "objid")) {
1319 ((OBJID*)p_var.value)->set_param(p_new_value);
1320 }
1321 else if (!strcmp(p_var.type_name, "objid template")) {
1322 ((OBJID_template*)p_var.value)->set_param(p_new_value);
1323 }
1324 else if (!strcmp(p_var.type_name, "octetstring")) {
1325 ((OCTETSTRING*)p_var.value)->set_param(p_new_value);
1326 }
1327 else if (!strcmp(p_var.type_name, "octetstring template")) {
1328 ((OCTETSTRING_template*)p_var.value)->set_param(p_new_value);
1329 }
1330 else if (!strcmp(p_var.type_name, "universal charstring")) {
1331 ((UNIVERSAL_CHARSTRING*)p_var.value)->set_param(p_new_value);
1332 }
1333 else if (!strcmp(p_var.type_name, "universal charstring template")) {
1334 ((UNIVERSAL_CHARSTRING_template*)p_var.value)->set_param(p_new_value);
1335 }
1336 else if (!strcmp(p_var.type_name, "verdicttype")) {
1337 ((VERDICTTYPE*)p_var.value)->set_param(p_new_value);
1338 }
1339 else if (!strcmp(p_var.type_name, "verdicttype template")) {
1340 ((VERDICTTYPE_template*)p_var.value)->set_param(p_new_value);
1341 }
1342 else if (!strcmp(p_var.type_name, "component")) {
1343 ((COMPONENT*)p_var.value)->set_param(p_new_value);
1344 }
1345 else if (!strcmp(p_var.type_name, "component template")) {
1346 ((COMPONENT_template*)p_var.value)->set_param(p_new_value);
1347 }
1348 else if (!strcmp(p_var.type_name, "default")) {
1349 ((DEFAULT*)p_var.value)->set_param(p_new_value);
1350 }
1351 else if (!strcmp(p_var.type_name, "default template")) {
1352 ((DEFAULT_template*)p_var.value)->set_param(p_new_value);
1353 }
1354 else if (!strcmp(p_var.type_name, "NULL")) {
1355 ((ASN_NULL*)p_var.value)->set_param(p_new_value);
1356 }
1357 else if (!strcmp(p_var.type_name, "NULL template")) {
1358 ((ASN_NULL_template*)p_var.value)->set_param(p_new_value);
1359 }
1360 else if (!strcmp(p_var.type_name, "CHARACTER STRING")) {
1361 ((CHARACTER_STRING*)p_var.value)->set_param(p_new_value);
1362 }
1363 else if (!strcmp(p_var.type_name, "CHARACTER STRING template")) {
1364 ((CHARACTER_STRING_template*)p_var.value)->set_param(p_new_value);
1365 }
1366 else if (!strcmp(p_var.type_name, "EMBEDDED PDV")) {
1367 ((EMBEDDED_PDV*)p_var.value)->set_param(p_new_value);
1368 }
1369 else if (!strcmp(p_var.type_name, "EMBEDDED PDV template")) {
1370 ((EMBEDDED_PDV_template*)p_var.value)->set_param(p_new_value);
1371 }
1372 else if (!strcmp(p_var.type_name, "EXTERNAL")) {
1373 ((EXTERNAL*)p_var.value)->set_param(p_new_value);
1374 }
1375 else if (!strcmp(p_var.type_name, "EXTERNAL template")) {
1376 ((EXTERNAL_template*)p_var.value)->set_param(p_new_value);
1377 }
1378 else {
1379 return FALSE;
1380 }
1381 return TRUE;
1382}
1383
016a1a93 1384void TTCN3_Debugger::add_to_result(const char* fmt, ...)
7329404e
BB
1385{
1386 va_list parameters;
1387 va_start(parameters, fmt);
016a1a93 1388 command_result = mputprintf_va_list(command_result, fmt, parameters);
7329404e 1389 va_end(parameters);
7329404e
BB
1390}
1391
1392void TTCN3_Debugger::add_function(TTCN3_Debug_Function* p_function)
1393{
f08ff9ca
BB
1394 function_call_t function_call;
1395 if (call_stack.empty()) {
1396 test_execution_started();
1397 function_call.caller_line = 0;
1398 }
1399 else {
1400 function_call.caller_line = last_breakpoint_entry.line;
7329404e 1401 }
f08ff9ca
BB
1402 function_call.function = p_function;
1403 call_stack.push_back(function_call);
7329404e
BB
1404}
1405
1406void TTCN3_Debugger::add_scope(TTCN3_Debug_Scope* p_scope)
1407{
1408 if (active && !call_stack.empty()) {
f08ff9ca 1409 call_stack[call_stack.size() - 1].function->add_scope(p_scope);
7329404e
BB
1410 }
1411}
1412
1413void TTCN3_Debugger::remove_function(TTCN3_Debug_Function* p_function)
1414{
f08ff9ca
BB
1415 if (!call_stack.empty() && call_stack[call_stack.size() - 1].function == p_function) {
1416 bool removing_test_case = call_stack[call_stack.size() - 1].function->is_test_case();
1417 int caller_line = call_stack[call_stack.size() - 1].caller_line;
7329404e 1418 call_stack.erase_at(call_stack.size() - 1);
f08ff9ca
BB
1419 if (call_stack.empty()) {
1420 test_execution_finished();
1421 }
1422 if (caller_line != 0 && (stepping_type == STEP_INTO || stepping_type == STEP_OUT ||
1423 (stepping_type == STEP_OVER && call_stack.size() != stepping_stack_size))) {
1424 breakpoint_entry(caller_line);
1425 }
1426 if (exiting && TTCN_Runtime::is_single() && !call_stack.empty() && removing_test_case &&
1427 call_stack[call_stack.size() - 1].function->is_control_part()) {
1428 // 'exit all' was requested while executing a test case called by a control
1429 // part, which means the test case caught the original TC_End exception;
1430 // another exception must be thrown to stop the control part, too
1431 throw TC_End();
1432 }
7329404e
BB
1433 }
1434}
1435
1436void TTCN3_Debugger::remove_scope(TTCN3_Debug_Scope* p_scope)
1437{
1438 if (!call_stack.empty()) {
f08ff9ca 1439 call_stack[call_stack.size() - 1].function->remove_scope(p_scope);
7329404e
BB
1440 }
1441}
1442
f08ff9ca
BB
1443TTCN3_Debugger::variable_t* TTCN3_Debugger::add_variable(const void* p_value,
1444 const char* p_name,
1445 const char* p_type,
1cb5b229 1446 const char* p_module,
f08ff9ca
BB
1447 TTCN3_Debugger::print_function_t p_print_function)
1448{
1449 if (call_stack.empty()) {
1450 // no call stack yet, so this is a global or component variable
1451 variable_t* var = find_variable(p_value);
1452 if (var == NULL) {
1453 var = new TTCN3_Debugger::variable_t;
1454 var->cvalue = p_value;
1455 var->name = p_name;
1456 var->type_name = p_type;
1cb5b229 1457 var->module = p_module;
f08ff9ca
BB
1458 var->print_function = p_print_function;
1459 var->set_function = NULL;
1460 variables.push_back(var);
1461 }
1462 return var;
1463 }
1464 else if (active) {
1465 // it's a local variable for the top-most function
1466 return call_stack[call_stack.size() - 1].function->add_variable(
1cb5b229 1467 p_value, p_name, p_type, p_module, p_print_function);
f08ff9ca
BB
1468 }
1469 return NULL;
1470}
1471
1472TTCN3_Debugger::variable_t* TTCN3_Debugger::add_variable(void* p_value,
1473 const char* p_name,
1474 const char* p_type,
1cb5b229 1475 const char* p_module,
f08ff9ca
BB
1476 TTCN3_Debugger::print_function_t p_print_function,
1477 TTCN3_Debugger::set_function_t p_set_function)
7329404e 1478{
7329404e
BB
1479 if (call_stack.empty()) {
1480 // no call stack yet, so this is a global or component variable
1481 variable_t* var = find_variable(p_value);
1482 if (var == NULL) {
1483 var = new TTCN3_Debugger::variable_t;
1484 var->value = p_value;
1485 var->name = p_name;
1486 var->type_name = p_type;
1cb5b229 1487 var->module = p_module;
7329404e 1488 var->print_function = p_print_function;
f08ff9ca 1489 var->set_function = p_set_function;
7329404e
BB
1490 variables.push_back(var);
1491 }
1492 return var;
1493 }
1494 else if (active) {
1495 // it's a local variable for the top-most function
f08ff9ca 1496 return call_stack[call_stack.size() - 1].function->add_variable(
1cb5b229 1497 p_value, p_name, p_type, p_module, p_print_function, p_set_function);
7329404e
BB
1498 }
1499 return NULL;
1500}
1501
1502void TTCN3_Debugger::remove_variable(const variable_t* p_var)
1503{
1504 if (active && !call_stack.empty()) {
f08ff9ca 1505 call_stack[call_stack.size() - 1].function->remove_variable(p_var);
7329404e
BB
1506 }
1507}
1508
1509const TTCN3_Debug_Scope* TTCN3_Debugger::get_global_scope(const char* p_module) const
1510{
1511 for (size_t i = 0; i < global_scopes.size(); ++i) {
1512 if (strcmp(global_scopes[i].name, p_module) == 0) {
1513 return global_scopes[i].scope;
1514 }
1515 }
1516 return NULL;
1517}
1518
1519const TTCN3_Debug_Scope* TTCN3_Debugger::get_component_scope(const char* p_component) const
1520{
1521 for (size_t i = 0; i < component_scopes.size(); ++i) {
1522 if (strcmp(component_scopes[i].name, p_component) == 0) {
1523 return component_scopes[i].scope;
1524 }
1525 }
1526 return NULL;
1527}
1528
cf2b6056 1529void TTCN3_Debugger::store_function_call(char* p_snapshot)
7329404e 1530{
cf2b6056
BB
1531 if (function_calls.cfg == CALLS_RING_BUFFER && function_calls.buffer.size == 0) {
1532 Free(p_snapshot);
1533 return;
1534 }
1535 // append timestamp to the beginning of the snapshot
1536 timeval tv;
1537 gettimeofday(&tv, NULL);
1538 struct tm *lt = localtime(&tv.tv_sec);
1539 if (lt != NULL) {
1540 char* temp = mprintf("%02d:%02d:%02d.%06ld\t%s", lt->tm_hour, lt->tm_min,
1541 lt->tm_sec, tv.tv_usec, p_snapshot);
1542 Free(p_snapshot);
1543 p_snapshot = temp;
1544 }
1545 switch (function_calls.cfg) {
1546 case CALLS_TO_FILE:
1547 fseek(function_calls.file.ptr, 0, SEEK_END); // in case multiple processes are writing the same file
1548 fputs(p_snapshot, function_calls.file.ptr);
1549 Free(p_snapshot);
1550 fputc('\n', function_calls.file.ptr);
1551 fflush(function_calls.file.ptr);
1552 break;
1553 case CALLS_RING_BUFFER: {
1554 bool first = function_calls.buffer.end == -1;
1555 function_calls.buffer.end = (function_calls.buffer.end + 1) %
1556 function_calls.buffer.size;
1557 function_calls.buffer.ptr[function_calls.buffer.end] = p_snapshot;
1558 if (!first && function_calls.buffer.start == function_calls.buffer.end) {
1559 function_calls.buffer.start = (function_calls.buffer.start + 1) %
1560 function_calls.buffer.size;
1561 }
1562 break; }
1563 case CALLS_STORE_ALL:
1564 if (function_calls.buffer.end == function_calls.buffer.size - 1) {
1565 function_calls.buffer.size += BUFFER_INCREASE;
1566 function_calls.buffer.ptr = (char**)Realloc(function_calls.buffer.ptr,
1567 function_calls.buffer.size * sizeof(char*));
1568 }
1569 ++function_calls.buffer.end;
1570 function_calls.buffer.ptr[function_calls.buffer.end] = p_snapshot;
1571 break;
016a1a93 1572 }
7329404e
BB
1573}
1574
1575#define CHECK_NOF_ARGUMENTS(exp_num) \
016a1a93
BB
1576 if (exp_num != p_argument_count) { \
1577 print(DRET_NOTIFICATION, "Invalid number of arguments. Expected %d, got %d.", \
1578 (int)exp_num, (int)p_argument_count); \
7329404e
BB
1579 return; \
1580 }
1581
1582#define CHECK_NOF_ARGUMENTS_RANGE(min, max) \
016a1a93
BB
1583 if ((int)min > p_argument_count || (int)max < p_argument_count) { \
1584 print(DRET_NOTIFICATION, "Invalid number of arguments. Expected at least %d " \
1585 "and at most %d, got %d.", (int)min, (int)max, p_argument_count); \
7329404e
BB
1586 return; \
1587 }
1588
1589#define CHECK_NOF_ARGUMENTS_MIN(min) \
016a1a93
BB
1590 if ((int)min > p_argument_count) { \
1591 print(DRET_NOTIFICATION, "Invalid number of arguments. Expected at least %d, got %d.", \
1592 (int)min, p_argument_count); \
7329404e
BB
1593 return; \
1594 }
1595
1596#define CHECK_INT_ARGUMENT(arg_idx) \
1597 { \
cf2b6056
BB
1598 if (!is_numeric(p_arguments[arg_idx])) { \
1599 print(DRET_NOTIFICATION, "Argument %d is not an integer.", (int)(arg_idx + 1)); \
1600 return; \
7329404e
BB
1601 } \
1602 }
1603
f08ff9ca
BB
1604#define CHECK_CALL_STACK(print_msg) \
1605 if (!active) { \
1606 if (print_msg) { \
1607 print(DRET_NOTIFICATION, "This command can only be used if the debugger " \
1608 "is switched on."); \
1609 } \
1610 return; \
1611 } \
7329404e 1612 if (call_stack.empty()) { \
f08ff9ca
BB
1613 if (print_msg) { \
1614 print(DRET_NOTIFICATION, "This command can only be used if the debugger's " \
1615 "call stack is not empty."); \
1616 } \
7329404e
BB
1617 return; \
1618 }
1619
016a1a93
BB
1620void TTCN3_Debugger::execute_command(int p_command, int p_argument_count,
1621 char** p_arguments)
7329404e 1622{
016a1a93 1623 if (!enabled) {
7329404e
BB
1624 return;
1625 }
016a1a93
BB
1626 for (int i = 0; i < p_argument_count; ++i) {
1627 if (p_arguments[i] == NULL) {
1628 print(DRET_NOTIFICATION, "Argument %d is a null pointer.", i + 1);
7329404e
BB
1629 return;
1630 }
1631 }
1632 switch (p_command) {
016a1a93
BB
1633 case D_SWITCH:
1634 CHECK_NOF_ARGUMENTS(1)
1635 switch_state(p_arguments[0]);
7329404e 1636 break;
f08ff9ca
BB
1637 case D_SET_BREAKPOINT:
1638 CHECK_NOF_ARGUMENTS_RANGE(2, 3)
cf2b6056 1639 set_breakpoint(p_arguments[0], p_arguments[1],
f08ff9ca 1640 (p_argument_count == 3) ? p_arguments[2] : NULL);
7329404e
BB
1641 break;
1642 case D_REMOVE_BREAKPOINT:
f08ff9ca
BB
1643 CHECK_NOF_ARGUMENTS_RANGE(1, 2)
1644 remove_breakpoint(p_arguments[0], (p_argument_count == 2) ? p_arguments[1] : NULL);
7329404e 1645 break;
f08ff9ca
BB
1646 case D_SET_AUTOMATIC_BREAKPOINT:
1647 CHECK_NOF_ARGUMENTS_RANGE(2, 3)
1648 set_automatic_breakpoint(p_arguments[0], p_arguments[1],
1649 (p_argument_count == 3) ? p_arguments[2] : NULL);
7329404e 1650 break;
7329404e
BB
1651 case D_SET_OUTPUT:
1652 CHECK_NOF_ARGUMENTS_RANGE(1, 2)
016a1a93
BB
1653 set_output(p_arguments[0], (p_argument_count == 2) ? p_arguments[1] : NULL);
1654 break;
f08ff9ca
BB
1655 case D_SET_GLOBAL_BATCH_FILE:
1656 CHECK_NOF_ARGUMENTS_RANGE(1, 2)
1657 set_global_batch_file(p_arguments[0], (p_argument_count == 2) ? p_arguments[1] : NULL);
1658 break;
cf2b6056
BB
1659 case D_FUNCTION_CALL_CONFIG:
1660 CHECK_NOF_ARGUMENTS_RANGE(1, 2)
1661 configure_function_calls(p_arguments[0], (p_argument_count == 2) ? p_arguments[1] : NULL);
1662 break;
1cb5b229
BB
1663 case D_PRINT_SETTINGS:
1664 CHECK_NOF_ARGUMENTS(0)
1665 print_settings();
7329404e 1666 break;
7329404e 1667 case D_PRINT_CALL_STACK:
f08ff9ca 1668 CHECK_CALL_STACK(true)
7329404e
BB
1669 CHECK_NOF_ARGUMENTS(0)
1670 print_call_stack();
1671 break;
1672 case D_SET_STACK_LEVEL:
f08ff9ca 1673 CHECK_CALL_STACK(true)
7329404e
BB
1674 CHECK_NOF_ARGUMENTS(1)
1675 CHECK_INT_ARGUMENT(0)
1676 set_stack_level(str2int(p_arguments[0]));
f08ff9ca 1677 break;
7329404e 1678 case D_LIST_VARIABLES:
f08ff9ca 1679 CHECK_CALL_STACK(true)
cf2b6056
BB
1680 CHECK_NOF_ARGUMENTS_RANGE(0, 2)
1681 call_stack[STACK_LEVEL].function->list_variables(
1682 (p_argument_count > 0) ? p_arguments[0] : NULL,
016a1a93 1683 (p_argument_count == 2) ? p_arguments[1] : NULL);
7329404e
BB
1684 break;
1685 case D_PRINT_VARIABLE:
f08ff9ca 1686 CHECK_CALL_STACK(true)
7329404e 1687 CHECK_NOF_ARGUMENTS_MIN(1)
016a1a93 1688 for (int i = 0; i < p_argument_count; ++i) {
f08ff9ca
BB
1689 if (i != 0) {
1690 add_to_result("\n");
7329404e 1691 }
f08ff9ca
BB
1692 if (!strcmp(p_arguments[i], "$")) {
1693 // '$' refers to the result of the last D_LIST_VARIABLES command
1694 // these variable names are separated by spaces
1695 if (last_variable_list != NULL) {
1696 size_t len = mstrlen(last_variable_list);
1697 size_t start = 0;
1698 for (size_t j = 0; j < len; ++j) {
1699 if (last_variable_list[j] == ' ') {
1700 // extract the variable name before this space
1701 char* var_name = mcopystrn(last_variable_list + start, j - start);
1702 print_variable(var_name);
1703 Free(var_name);
1704 add_to_result("\n");
1705 start = j + 1;
1706 }
1707 }
1708 // extract the last (or only) variable name
1709 char* var_name = mcopystrn(last_variable_list + start, len - start);
1710 print_variable(var_name);
1711 Free(var_name);
1712 }
1713 else {
1714 add_to_result("No previous " D_LIST_VARIABLES_TEXT " result.");
1715 }
016a1a93 1716 }
f08ff9ca
BB
1717 else {
1718 print_variable(p_arguments[i]);
7329404e
BB
1719 }
1720 }
1721 break;
f08ff9ca
BB
1722 case D_OVERWRITE_VARIABLE:
1723 CHECK_CALL_STACK(true)
1724 CHECK_NOF_ARGUMENTS_MIN(2)
1725 overwrite_variable(p_arguments[0], p_argument_count - 1, p_arguments + 1);
1726 break;
cf2b6056
BB
1727 case D_PRINT_FUNCTION_CALLS:
1728 CHECK_NOF_ARGUMENTS_RANGE(0, 1)
1729 print_function_calls((p_argument_count > 0) ? p_arguments[0] : NULL);
7329404e 1730 break;
f08ff9ca
BB
1731 case D_STEP_OVER:
1732 CHECK_CALL_STACK(true)
1733 CHECK_NOF_ARGUMENTS(0)
1734 step(STEP_OVER);
1735 break;
1736 case D_STEP_INTO:
1737 CHECK_CALL_STACK(true)
1738 CHECK_NOF_ARGUMENTS(0)
1739 step(STEP_INTO);
1740 break;
1741 case D_STEP_OUT:
1742 CHECK_CALL_STACK(true)
1743 CHECK_NOF_ARGUMENTS(0)
1744 step(STEP_OUT);
1745 break;
1746 case D_RUN_TO_CURSOR:
1747 if (!TTCN_Runtime::is_hc() && !TTCN_Runtime::is_single()) {
1748 CHECK_CALL_STACK(TTCN_Runtime::is_mtc())
1749 }
1750 CHECK_NOF_ARGUMENTS(2)
cf2b6056 1751 run_to_cursor(p_arguments[0], p_arguments[1]);
f08ff9ca 1752 break;
016a1a93 1753 case D_HALT:
f08ff9ca
BB
1754 if (!TTCN_Runtime::is_hc() && !TTCN_Runtime::is_single()) {
1755 CHECK_CALL_STACK(TTCN_Runtime::is_mtc())
016a1a93
BB
1756 }
1757 CHECK_NOF_ARGUMENTS(0)
f08ff9ca 1758 halt(NULL, false);
016a1a93
BB
1759 break;
1760 case D_CONTINUE:
1761 CHECK_NOF_ARGUMENTS(0)
1762 resume();
1763 break;
1764 case D_EXIT:
f08ff9ca
BB
1765 if (!TTCN_Runtime::is_hc() && !TTCN_Runtime::is_single()) {
1766 CHECK_CALL_STACK(TTCN_Runtime::is_mtc())
016a1a93 1767 }
f08ff9ca 1768 CHECK_NOF_ARGUMENTS(1)
016a1a93
BB
1769 exit_(p_arguments[0]);
1770 break;
1771 case D_SETUP:
cf2b6056 1772 CHECK_NOF_ARGUMENTS_MIN(11)
016a1a93
BB
1773 if (strlen(p_arguments[0]) > 0) {
1774 switch_state(p_arguments[0]);
1775 }
1776 if (strlen(p_arguments[1]) > 0) {
1777 set_output(p_arguments[1], p_arguments[2]);
1778 }
1779 if (strlen(p_arguments[3]) > 0) {
f08ff9ca
BB
1780 set_automatic_breakpoint("error", p_arguments[3],
1781 strlen(p_arguments[4]) > 0 ? p_arguments[4] : NULL);
1782 }
1783 if (strlen(p_arguments[5]) > 0) {
1784 set_automatic_breakpoint("fail", p_arguments[5],
1785 strlen(p_arguments[6]) > 0 ? p_arguments[6] : NULL);
016a1a93 1786 }
f08ff9ca
BB
1787 if (strlen(p_arguments[7]) > 0) {
1788 set_global_batch_file(p_arguments[7],
1789 strlen(p_arguments[8]) > 0 ? p_arguments[8] : NULL);
016a1a93 1790 }
cf2b6056
BB
1791 if (strlen(p_arguments[9]) > 0) {
1792 configure_function_calls(p_arguments[9],
1793 strlen(p_arguments[10]) > 0 ? p_arguments[10] : NULL);
1794 }
1795 for (int i = 11; i < p_argument_count; i += 3) {
1796 set_breakpoint(p_arguments[i], p_arguments[i + 1],
f08ff9ca 1797 strlen(p_arguments[i + 2]) > 0 ? p_arguments[i + 2] : NULL);
016a1a93 1798 }
7329404e 1799 break;
016a1a93 1800 default:
f08ff9ca 1801 print(DRET_NOTIFICATION, "Invalid command received (ID: %d).", p_command);
016a1a93
BB
1802 return;
1803 }
1804 if (command_result != NULL) {
1805 print(DRET_DATA, command_result);
f08ff9ca
BB
1806 if (p_command == D_LIST_VARIABLES) {
1807 Free(last_variable_list);
1808 last_variable_list = command_result;
1809 }
1810 else {
1811 Free(command_result);
1812 }
1813 command_result = NULL;
016a1a93
BB
1814 }
1815}
1816
cf2b6056 1817void TTCN3_Debugger::init_PTC_settings()
016a1a93
BB
1818{
1819 if (output_file == NULL && output_file_name != NULL) {
1820 char* final_file_name = finalize_file_name(output_file_name);
cf2b6056 1821 output_file = fopen(final_file_name, "a");
016a1a93
BB
1822 if (output_file == NULL) {
1823 print(DRET_NOTIFICATION, "Failed to open file '%s' for writing.", final_file_name);
1824 }
1825 Free(final_file_name);
7329404e 1826 }
cf2b6056
BB
1827 if (function_calls.cfg == CALLS_TO_FILE) {
1828 char* final_file_name = finalize_file_name(function_calls.file.name);
1829 function_calls.file.ptr = fopen(final_file_name, "a");
1830 if (function_calls.file.ptr == NULL) {
1831 print(DRET_NOTIFICATION, "Failed to open file '%s' for writing.", final_file_name);
1832 }
1833 Free(final_file_name);
1834 }
1835 else if (function_calls.cfg == CALLS_RING_BUFFER && function_calls.buffer.size != 0) {
1836 function_calls.buffer.ptr = (char**)Malloc(function_calls.buffer.size * sizeof(char*));
1837 }
7329404e
BB
1838}
1839
1840//////////////////////////////////////////////////////
1841//////////////// TTCN3_Debug_Scope ///////////////////
1842//////////////////////////////////////////////////////
1843
1844TTCN3_Debug_Scope::TTCN3_Debug_Scope()
1845{
1846 ttcn3_debugger.add_scope(this);
1847}
1848
1849TTCN3_Debug_Scope::~TTCN3_Debug_Scope()
1850{
1851 for (size_t i = 0; i < variables.size(); ++i) {
1852 ttcn3_debugger.remove_variable(variables[i]);
1853 }
1854 ttcn3_debugger.remove_scope(this);
1855}
1856
1857void TTCN3_Debug_Scope::add_variable(const void* p_value,
1858 const char* p_name,
1859 const char* p_type,
1cb5b229 1860 const char* p_module,
f08ff9ca
BB
1861 TTCN3_Debugger::print_function_t p_print_function)
1862{
1cb5b229
BB
1863 TTCN3_Debugger::variable_t* var = ttcn3_debugger.add_variable(p_value, p_name,
1864 p_type, p_module, p_print_function);
f08ff9ca
BB
1865 if (var != NULL) {
1866 variables.push_back(var);
1867 }
1868}
1869
1870void TTCN3_Debug_Scope::add_variable(void* p_value,
1871 const char* p_name,
1872 const char* p_type,
1cb5b229 1873 const char* p_module,
f08ff9ca
BB
1874 TTCN3_Debugger::print_function_t p_print_function,
1875 TTCN3_Debugger::set_function_t p_set_function)
7329404e 1876{
f08ff9ca 1877 TTCN3_Debugger::variable_t* var = ttcn3_debugger.add_variable(p_value, p_name,
1cb5b229 1878 p_type, p_module, p_print_function, p_set_function);
7329404e
BB
1879 if (var != NULL) {
1880 variables.push_back(var);
1881 }
1882}
1883
f08ff9ca 1884TTCN3_Debugger::variable_t* TTCN3_Debug_Scope::find_variable(const char* p_name) const
7329404e
BB
1885{
1886 for (size_t i = 0; i < variables.size(); ++i) {
1cb5b229
BB
1887 TTCN3_Debugger::variable_t* var = variables[i];
1888 if (strcmp(var->name, p_name) == 0) {
1889 // the string matches the variable's name
1890 return var;
1891 }
1892 else if (var->module != NULL) {
1893 size_t name_len = strlen(var->name);
1894 size_t mod_len = strlen(var->module);
1895 size_t len = strlen(p_name);
1896 if (len == mod_len + name_len + 1 && p_name[mod_len] == '.' &&
1897 strncmp(p_name, var->module, mod_len) == 0 &&
1898 strncmp(p_name + mod_len + 1, var->name, name_len) == 0) {
1899 // the string matches the variable's name prefixed by its module name
1900 return var;
1901 }
7329404e
BB
1902 }
1903 }
1904 return NULL;
1905}
1906
1cb5b229
BB
1907void TTCN3_Debug_Scope::list_variables(regex_t* p_posix_regexp, bool& p_first,
1908 const char* p_module) const
7329404e
BB
1909{
1910 for (size_t i = 0; i < variables.size(); ++i) {
016a1a93
BB
1911 if (p_posix_regexp == NULL ||
1912 regexec(p_posix_regexp, variables[i]->name, 0, NULL, 0) == 0) {
1cb5b229
BB
1913 bool imported = p_module != NULL && strcmp(p_module, variables[i]->module) != 0;
1914 ttcn3_debugger.add_to_result("%s%s%s%s", p_first ? "" : " ",
1915 imported ? variables[i]->module : "", imported ? "." : "", variables[i]->name);
016a1a93
BB
1916 p_first = false;
1917 }
7329404e
BB
1918 }
1919}
1920
1921//////////////////////////////////////////////////////
1922/////////////// TTCN3_Debug_Function /////////////////
1923//////////////////////////////////////////////////////
1924
1925TTCN3_Debug_Function::TTCN3_Debug_Function(const char* p_name,
1926 const char* p_type,
1927 const char* p_module,
1928 const charstring_list& p_parameter_names,
1929 const charstring_list& p_parameter_types,
1930 const char* p_component_name)
1931: function_name(p_name), function_type(p_type), module_name(p_module)
1932, parameter_names(new charstring_list(p_parameter_names))
1933, parameter_types(new charstring_list(p_parameter_types))
1934{
1935 ttcn3_debugger.add_function(this);
1936 global_scope = ttcn3_debugger.get_global_scope(p_module);
1937 component_scope = (p_component_name != NULL) ?
1938 ttcn3_debugger.get_component_scope(p_component_name) : NULL;
1939 if (function_name == NULL) {
1940 function_name = p_module; // for control parts
1941 }
1942}
1943
1944TTCN3_Debug_Function::~TTCN3_Debug_Function()
1945{
1946 if (ttcn3_debugger.is_on()) {
1947 char* snapshot = mprintf("[%s]\tfinished\t%s(", function_type, function_name);
1948 if (parameter_names->size_of() > 0) {
1949 for (int i = 0; i < parameter_names->size_of(); ++i) {
1950 if (i > 0) {
1951 snapshot = mputstr(snapshot, ", ");
1952 }
1953 snapshot = mputprintf(snapshot, "[%s] %s := ", (const char*)((*parameter_types)[i]),
1954 (const char*)((*parameter_names)[i]));
1955 if ((*parameter_types)[i] == "out" || (*parameter_types)[i] == "inout") {
1956 const TTCN3_Debugger::variable_t* parameter = find_variable((*parameter_names)[i]);
1957 snapshot = mputstr(snapshot, parameter->print_function(*parameter));
1958 }
1959 else {
1960 snapshot = mputc(snapshot, '-');
1961 }
1962 }
1963 }
1964 snapshot = mputc(snapshot, ')');
1965 if (return_value.is_bound()) {
1966 snapshot = mputprintf(snapshot, " returned %s", (const char*)return_value);
1967 }
cf2b6056 1968 ttcn3_debugger.store_function_call(snapshot);
7329404e
BB
1969 }
1970 for (size_t i = 0; i < variables.size(); ++i) {
1971 delete variables[i];
1972 }
1973 delete parameter_names;
1974 delete parameter_types;
1975 ttcn3_debugger.remove_function(this);
1976}
1977
f08ff9ca
BB
1978TTCN3_Debugger::variable_t* TTCN3_Debug_Function::add_variable(const void* p_value,
1979 const char* p_name,
1980 const char* p_type,
1cb5b229 1981 const char* p_module,
f08ff9ca
BB
1982 TTCN3_Debugger::print_function_t p_print_function)
1983{
1984 if (ttcn3_debugger.is_on()) {
1985 TTCN3_Debugger::variable_t* var = new TTCN3_Debugger::variable_t;
1986 var->cvalue = p_value;
1987 var->name = p_name;
1988 var->type_name = p_type;
1cb5b229 1989 var->module = p_module;
f08ff9ca
BB
1990 var->print_function = p_print_function;
1991 var->set_function = NULL;
1992 variables.push_back(var);
1993 return var;
1994 }
1995 return NULL;
1996}
1997
1998TTCN3_Debugger::variable_t* TTCN3_Debug_Function::add_variable(void* p_value,
1999 const char* p_name,
2000 const char* p_type,
1cb5b229 2001 const char* p_module,
f08ff9ca
BB
2002 TTCN3_Debugger::print_function_t p_print_function,
2003 TTCN3_Debugger::set_function_t p_set_function)
7329404e
BB
2004{
2005 if (ttcn3_debugger.is_on()) {
2006 TTCN3_Debugger::variable_t* var = new TTCN3_Debugger::variable_t;
2007 var->value = p_value;
2008 var->name = p_name;
2009 var->type_name = p_type;
1cb5b229 2010 var->module = p_module;
7329404e 2011 var->print_function = p_print_function;
f08ff9ca 2012 var->set_function = p_set_function;
7329404e
BB
2013 variables.push_back(var);
2014 return var;
2015 }
2016 return NULL;
2017}
2018
2019void TTCN3_Debug_Function::set_return_value(const CHARSTRING& p_value)
2020{
2021 return_value = p_value;
2022}
2023
2024void TTCN3_Debug_Function::initial_snapshot() const
2025{
2026 if (ttcn3_debugger.is_on()) {
2027 char* snapshot = mprintf("[%s]\tstarted \t%s(", function_type, function_name);
2028 if (parameter_names->size_of() > 0) {
2029 for (int i = 0; i < parameter_names->size_of(); ++i) {
2030 if (i > 0) {
2031 snapshot = mputstr(snapshot, ", ");
2032 }
2033 snapshot = mputprintf(snapshot, "[%s] %s := ", (const char*)((*parameter_types)[i]),
2034 (const char*)((*parameter_names)[i]));
2035 if ((*parameter_types)[i] == "in" || (*parameter_types)[i] == "inout") {
2036 const TTCN3_Debugger::variable_t* parameter = find_variable((*parameter_names)[i]);
2037 snapshot = mputstr(snapshot, parameter->print_function(*parameter));
2038 }
2039 else {
2040 snapshot = mputc(snapshot, '-');
2041 }
2042 }
2043 }
016a1a93 2044 snapshot = mputstr(snapshot, ")");
cf2b6056 2045 ttcn3_debugger.store_function_call(snapshot);
7329404e
BB
2046 }
2047}
2048
2049void TTCN3_Debug_Function::add_scope(TTCN3_Debug_Scope* p_scope)
2050{
2051 scopes.push_back(p_scope);
2052}
2053
2054void TTCN3_Debug_Function::remove_scope(TTCN3_Debug_Scope* p_scope)
2055{
016a1a93 2056 if (!scopes.empty() && scopes[scopes.size() - 1] == p_scope) {
7329404e
BB
2057 scopes.erase_at(scopes.size() - 1);
2058 }
2059}
2060
2061void TTCN3_Debug_Function::remove_variable(const TTCN3_Debugger::variable_t* p_var)
2062{
2063 for (size_t i = 0; i < variables.size(); ++i) {
2064 if (variables[i] == p_var) {
2065 variables.erase_at(i);
2066 delete p_var;
2067 break;
2068 }
2069 }
2070}
2071
f08ff9ca 2072TTCN3_Debugger::variable_t* TTCN3_Debug_Function::find_variable(const char* p_name) const
7329404e
BB
2073{
2074 for (size_t i = 0; i < variables.size(); ++i) {
2075 if (strcmp(variables[i]->name, p_name) == 0) {
2076 return variables[i];
2077 }
2078 }
2079 // it's not a local variable, it might still be a global or component variable
2080 if (component_scope != NULL) {
f08ff9ca 2081 TTCN3_Debugger::variable_t* res = component_scope->find_variable(p_name);
7329404e
BB
2082 if (res != NULL) {
2083 return res;
2084 }
2085 }
2086 return (global_scope != NULL) ? global_scope->find_variable(p_name) : NULL;
2087}
2088
2089void TTCN3_Debug_Function::print_function() const
2090{
016a1a93 2091 ttcn3_debugger.add_to_result("[%s]\t%s(", function_type, function_name);
7329404e
BB
2092 if (parameter_names->size_of() > 0) {
2093 for (int i = 0; i < parameter_names->size_of(); ++i) {
2094 if (i > 0) {
016a1a93 2095 ttcn3_debugger.add_to_result(", ");
7329404e
BB
2096 }
2097 const TTCN3_Debugger::variable_t* parameter = find_variable((*parameter_names)[i]);
016a1a93 2098 ttcn3_debugger.add_to_result("[%s] %s := %s", (const char*)(*parameter_types)[i],
7329404e
BB
2099 (const char*)(*parameter_names)[i], (const char*)parameter->print_function(*parameter));
2100 }
2101 }
016a1a93 2102 ttcn3_debugger.add_to_result(")");
7329404e
BB
2103}
2104
2105void TTCN3_Debug_Function::list_variables(const char* p_scope, const char* p_filter) const
2106{
2107 bool first = true;
2108 bool list_local = false;
2109 bool list_global = false;
2110 bool list_comp = false;
cf2b6056 2111 if (p_scope == NULL || !strcmp(p_scope, "all")) {
7329404e 2112 list_local = true;
7329404e 2113 list_global = true;
7329404e
BB
2114 list_comp = true;
2115 }
cf2b6056 2116 else if (!strcmp(p_scope, "local")) {
7329404e 2117 list_local = true;
cf2b6056
BB
2118 }
2119 else if (!strcmp(p_scope, "global")) {
7329404e 2120 list_global = true;
cf2b6056
BB
2121 }
2122 else if (!strcmp(p_scope, "comp")) {
7329404e
BB
2123 list_comp = true;
2124 }
016a1a93
BB
2125 else {
2126 ttcn3_debugger.print(DRET_NOTIFICATION, "Argument 1 is invalid. "
2127 "Expected 'local', 'global', 'comp' or 'all'.");
2128 return;
2129 }
2130 regex_t* posix_regexp = NULL;
2131 if (p_filter != NULL) {
2132 char* posix_str = TTCN_pattern_to_regexp(p_filter);
2133 if (posix_str == NULL) {
2134 ttcn3_debugger.print(DRET_NOTIFICATION, "Argument 2 is invalid. "
2135 "Expected a valid TTCN-3 character pattern.");
2136 return;
2137 }
2138 posix_regexp = new regex_t;
2139 int ret_val = regcomp(posix_regexp, posix_str, REG_EXTENDED | REG_NOSUB);
2140 Free(posix_str);
2141 if (ret_val != 0) {
2142 char msg[512];
2143 regerror(ret_val, posix_regexp, msg, sizeof(msg));
2144 regfree(posix_regexp);
2145 delete posix_regexp;
2146 ttcn3_debugger.print(DRET_NOTIFICATION, "Compilation of POSIX regular "
2147 "expression failed.");
2148 return;
2149 }
2150 }
7329404e
BB
2151 if (list_local) {
2152 for (size_t i = 0; i < variables.size(); ++i) {
016a1a93
BB
2153 if (posix_regexp == NULL ||
2154 regexec(posix_regexp, variables[i]->name, 0, NULL, 0) == 0) {
2155 ttcn3_debugger.add_to_result("%s%s", first ? "" : " ", variables[i]->name);
2156 first = false;
2157 }
7329404e
BB
2158 }
2159 }
2160 if (list_global && global_scope != NULL && global_scope->has_variables()) {
1cb5b229 2161 global_scope->list_variables(posix_regexp, first, module_name);
7329404e
BB
2162 }
2163 if (list_comp && component_scope != NULL && component_scope->has_variables()) {
1cb5b229 2164 component_scope->list_variables(posix_regexp, first, NULL);
7329404e
BB
2165 }
2166 if (first) {
016a1a93
BB
2167 ttcn3_debugger.print(DRET_NOTIFICATION, "No variables found.");
2168 }
2169 if (posix_regexp != NULL) {
2170 regfree(posix_regexp);
2171 delete posix_regexp;
7329404e 2172 }
7329404e
BB
2173}
2174
f08ff9ca
BB
2175bool TTCN3_Debug_Function::is_control_part() const
2176{
2177 return !strcmp(function_type, "control");
2178}
2179
2180bool TTCN3_Debug_Function::is_test_case() const
2181{
2182 return !strcmp(function_type, "testcase");
2183}
This page took 0.117771 seconds and 5 git commands to generate.