clang specific define in Runtime.cc
[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"
18#include <unistd.h>
19#include <pwd.h>
7329404e
BB
20
21//////////////////////////////////////////////////////
22////////////////// TTCN3_Debugger ////////////////////
23//////////////////////////////////////////////////////
24
25TTCN3_Debugger ttcn3_debugger;
26
016a1a93 27void TTCN3_Debugger::switch_state(const char* p_state_str)
7329404e 28{
016a1a93
BB
29 if (!strcmp(p_state_str, "on")) {
30 if (active) {
31 print(DRET_NOTIFICATION, "The debugger is already switched on.");
32 }
33 else {
34 active = true;
35 print(DRET_SETTING_CHANGE, "Debugger switched on.");
36 }
7329404e 37 }
016a1a93
BB
38 else if(!strcmp(p_state_str, "off")) {
39 if (!active) {
40 print(DRET_NOTIFICATION, "The debugger is already switched off.");
41 }
42 else {
43 active = false;
44 print(DRET_SETTING_CHANGE, "Debugger switched off.");
45 }
7329404e
BB
46 }
47 else {
016a1a93 48 print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'yes' or 'no'.");
7329404e 49 }
7329404e
BB
50}
51
52void TTCN3_Debugger::add_breakpoint(const char* p_module, int p_line /*const char* batch_file*/)
53{
54 if (find_breakpoint(p_module, p_line) == breakpoints.size()) {
55 breakpoint_t bp;
56 bp.module = mcopystr(p_module);
57 bp.line = p_line;
58 breakpoints.push_back(bp);
016a1a93
BB
59 print(DRET_SETTING_CHANGE, "Breakpoint added in module '%s' at line %d.",
60 p_module, p_line);
7329404e
BB
61 }
62 else {
016a1a93
BB
63 print(DRET_NOTIFICATION, "Breakpoint already set in module '%s' at line %d.",
64 p_module, p_line);
7329404e
BB
65 }
66}
67
68void TTCN3_Debugger::remove_breakpoint(const char* p_module, int p_line)
69{
70 size_t pos = find_breakpoint(p_module, p_line);
71 if (pos != breakpoints.size()) {
72 Free(breakpoints[pos].module);
73 breakpoints.erase_at(pos);
016a1a93
BB
74 print(DRET_SETTING_CHANGE, "Breakpoint removed in module '%s' from line %d.",
75 p_module, p_line);
7329404e
BB
76 }
77 else {
016a1a93
BB
78 print(DRET_NOTIFICATION, "No breakpoint found in module '%s' at line %d.",
79 p_module, p_line);
7329404e
BB
80 }
81}
82
83void TTCN3_Debugger::set_special_breakpoint(special_breakpoint_t p_type, const char* p_state_str)
84{
85 bool new_state;
86 if (!strcmp(p_state_str, "yes")) {
87 new_state = true;
88 }
89 else if(!strcmp(p_state_str, "no")) {
90 new_state = false;
91 }
92 // else if "batch"
93 else {
016a1a93 94 print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'yes' or 'no'.");
7329404e
BB
95 return;
96 }
97 const char* sbp_type_str;
98 bool state_changed;
99 switch (p_type) {
100 case SBP_FAIL_VERDICT:
101 state_changed = (fail_behavior != new_state);
102 fail_behavior = new_state;
103 sbp_type_str = "Fail";
104 break;
105 case SBP_ERROR_VERDICT:
106 state_changed = (error_behavior != new_state);
107 error_behavior = new_state;
108 sbp_type_str = "Error";
109 break;
110 default:
111 // should never happen
112 return;
113 }
016a1a93 114 print(DRET_SETTING_CHANGE, "%s verdict behavior %sset to %s.", sbp_type_str,
7329404e 115 state_changed ? "" : "was already ",
016a1a93 116 new_state ? "halt test execution" : "do nothing");
7329404e
BB
117}
118
119void TTCN3_Debugger::print_call_stack()
120{
121 for (size_t i = call_stack.size(); i != 0; --i) {
016a1a93 122 add_to_result("%d.\t", (int)call_stack.size() - (int)i + 1);
7329404e 123 call_stack[i - 1]->print_function();
016a1a93
BB
124 if (i != 1) {
125 add_to_result("\n");
126 }
7329404e
BB
127 }
128}
129
130void TTCN3_Debugger::set_stack_level(int new_level)
131{
016a1a93
BB
132 if (!halted) {
133 print(DRET_NOTIFICATION, "Stack level can only be set if test execution is halted.");
134 }
135 else if (new_level <= 0 || (size_t)new_level > call_stack.size()) {
136 print(DRET_NOTIFICATION, "Invalid new stack level. Expected 1 - %d.",
137 (int)call_stack.size());
7329404e
BB
138 }
139 else {
016a1a93
BB
140 stack_level = (int)call_stack.size() - new_level;
141 call_stack[stack_level]->print_function();
142 print(DRET_NOTIFICATION, "Stack level set to:\n%d.\t%s", new_level, command_result);
7329404e
BB
143 }
144}
145
016a1a93 146void TTCN3_Debugger::print_variable(const TTCN3_Debugger::variable_t* p_var)
7329404e 147{
016a1a93
BB
148 add_to_result("[%s] %s := %s", p_var->type_name, p_var->name,
149 (const char*)p_var->print_function(*p_var));
7329404e
BB
150}
151
152void TTCN3_Debugger::set_output(const char* p_output_type, const char* p_file_name)
153{
016a1a93
BB
154 FILE* new_fp = NULL;
155 bool file, console;
156 bool same_file = false;
157 char* final_file_name = NULL;
158 // check the command's parameters before actually changing anything
159 if (!strcmp(p_output_type, "console")) {
160 file = false;
161 console = true;
7329404e
BB
162 }
163 else if (!strcmp(p_output_type, "file")) {
016a1a93
BB
164 file = true;
165 console = false;
166 }
167 else if (!strcmp(p_output_type, "both")) {
168 file = true;
169 console = true;
170 }
171 else {
172 print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'console', 'file' or 'both'.");
173 return;
174 }
175 if (file) {
7329404e 176 if (p_file_name == NULL) {
016a1a93 177 print(DRET_NOTIFICATION, "Argument 2 (output file name) is missing.");
7329404e
BB
178 return;
179 }
016a1a93
BB
180 if (output_file_name != NULL && !strcmp(p_file_name, output_file_name)) {
181 // don't reopen it if it's the same file as before
182 same_file = true;
183 }
184 else if (!TTCN_Runtime::is_hc()) {
185 // don't open any files on HCs, just store settings for future PTCs
186 final_file_name = finalize_file_name(p_file_name);
187 new_fp = fopen(final_file_name, "w");
188 if (new_fp == NULL) {
189 print(DRET_NOTIFICATION, "Failed to open file '%s' for writing.", final_file_name);
190 return;
191 }
7329404e
BB
192 }
193 }
016a1a93
BB
194 // print the change notification to the old output
195 char* file_str = file ? mprintf("file '%s'", TTCN_Runtime::is_hc() ? p_file_name
196 : final_file_name) : NULL;
197 Free(final_file_name);
198 print(DRET_SETTING_CHANGE, "Debugger set to print its output to %s%s%s.",
199 console ? "the console" : "", (console && file) ? " and to " : "",
200 file ? file_str : "");
201 if (file) {
202 Free(file_str);
203 }
204 if (!same_file && !TTCN_Runtime::is_hc()) {
205 if (output_file != NULL) {
206 fclose(output_file);
207 }
208 output_file = new_fp;
209 }
210 send_to_console = console;
211 Free(output_file_name);
212 if (file) {
213 output_file_name = mcopystr(p_file_name);
214 }
215}
216
217void TTCN3_Debugger::halt()
218{
219 if (!halted) {
220 halted = true;
221 stack_level = call_stack.size() - 1;
222 print(DRET_NOTIFICATION, "Test execution halted.");
223 TTCN_Communication::process_debug_messages();
224 }
7329404e 225 else {
016a1a93
BB
226 print(DRET_NOTIFICATION, "Test execution is already halted.");
227 }
228}
229
230void TTCN3_Debugger::resume()
231{
232 if (halted) {
233 halted = false;
234 stack_level = -1;
235 print(DRET_NOTIFICATION, "Test execution resumed.");
236 }
237 else {
238 print(DRET_NOTIFICATION, "Test execution is not halted.");
7329404e 239 }
016a1a93
BB
240}
241
242void TTCN3_Debugger::exit_(const char* p_what)
243{
244 bool exit_all;
245 if (!strcmp(p_what, "test")) {
246 exit_all = false;
247 }
248 else if (!strcmp(p_what, "all")) {
249 exit_all = true;
250 }
251 else {
252 print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'test' or 'all'.");
253 return;
7329404e 254 }
016a1a93
BB
255 halted = false;
256 print((exit_all && TTCN_Runtime::is_mtc()) ? DRET_EXIT_ALL : DRET_NOTIFICATION,
257 "Exiting %s.", exit_all ? "test execution" : "current test");
258 TTCN_Runtime::stop_execution();
7329404e
BB
259}
260
261size_t TTCN3_Debugger::find_breakpoint(const char* p_module, int p_line) const
262{
263 for (size_t i = 0; i < breakpoints.size(); ++i) {
264 if (!strcmp(breakpoints[i].module, p_module) && breakpoints[i].line == p_line) {
265 return i;
266 }
267 }
268 return breakpoints.size();
269}
270
271TTCN3_Debugger::variable_t* TTCN3_Debugger::find_variable(const void* p_value) const
272{
273 for (size_t i = 0; i < variables.size(); ++i) {
274 if (variables[i]->value == p_value) {
275 return variables[i];
276 }
277 }
278 return NULL;
279}
280
016a1a93
BB
281char* TTCN3_Debugger::finalize_file_name(const char* p_file_name_skeleton)
282{
283 if (p_file_name_skeleton == NULL) {
284 return NULL;
285 }
286 size_t len = strlen(p_file_name_skeleton);
287 size_t next_idx = 0;
288 char* ret_val = NULL;
289 for (size_t i = 0; i < len - 1; ++i) {
290 if (p_file_name_skeleton[i] == '%') {
291 ret_val = mputstrn(ret_val, p_file_name_skeleton + next_idx, i - next_idx);
292 switch (p_file_name_skeleton[i + 1]) {
293 case 'e': // %e -> executable name
294 ret_val = mputstr(ret_val, TTCN_Logger::get_executable_name());
295 break;
296 case 'h': // %h -> host name
297 ret_val = mputstr(ret_val, TTCN_Runtime::get_host_name());
298 break;
299 case 'p': // %p -> process ID
300 ret_val = mputprintf(ret_val, "%ld", (long)getpid());
301 break;
302 case 'l': { // %l -> login name
303 setpwent();
304 struct passwd *p = getpwuid(getuid());
305 if (NULL != p) {
306 ret_val = mputstr(ret_val, p->pw_name);
307 }
308 endpwent();
309 break; }
310 case 'r': // %r -> component reference
311 if (TTCN_Runtime::is_single()) {
312 ret_val = mputstr(ret_val, "single");
313 }
314 else if (TTCN_Runtime::is_mtc()) {
315 ret_val = mputstr(ret_val, "mtc");
316 }
317 else if (TTCN_Runtime::is_ptc()) {
318 ret_val = mputprintf(ret_val, "%d", (component)self);
319 }
320 break;
321 case 'n': // %n -> component name
322 if (TTCN_Runtime::is_mtc()) {
323 ret_val = mputstr(ret_val, "MTC");
324 }
325 else if (TTCN_Runtime::is_ptc()) {
326 ret_val = mputstr(ret_val, TTCN_Runtime::get_component_name());
327 }
328 break;
329 case '%': // %% -> single %
330 ret_val = mputc(ret_val, '%');
331 break;
332 default: // unknown sequence -> leave it as it is
333 ret_val = mputstrn(ret_val, p_file_name_skeleton + i, 2);
334 break;
335 }
336 next_idx = i + 2;
337 ++i;
338 }
339 }
340 if (next_idx < len) {
341 ret_val = mputstr(ret_val, p_file_name_skeleton + next_idx);
342 }
343 return ret_val;
344}
345
346void TTCN3_Debugger::print(int return_type, const char* fmt, ...) const
347{
348 va_list parameters;
349 va_start(parameters, fmt);
350 char* str = mprintf_va_list(fmt, parameters);
351 va_end(parameters);
352 TTCN_Communication::send_debug_return_value(return_type, send_to_console ? str : NULL);
353 if (output_file != NULL) {
354 fprintf(output_file, "%s\n", str);
355 fflush(output_file);
356 }
357 Free(str);
358}
359
7329404e
BB
360TTCN3_Debugger::TTCN3_Debugger()
361{
016a1a93 362 enabled = false;
7329404e 363 active = false;
016a1a93
BB
364 halted = false;
365 output_file = NULL;
366 output_file_name = NULL;
367 send_to_console = true;
7329404e
BB
368 snapshots = NULL;
369 last_breakpoint_entry.module = NULL;
370 last_breakpoint_entry.line = 0;
371 stack_level = -1;
372 fail_behavior = false;
373 error_behavior = false;
016a1a93 374 command_result = NULL;
7329404e
BB
375}
376
377TTCN3_Debugger::~TTCN3_Debugger()
378{
016a1a93
BB
379 if (output_file != NULL) {
380 fclose(output_file);
381 Free(output_file_name);
7329404e
BB
382 }
383 for (size_t i = 0; i < breakpoints.size(); ++i) {
384 Free(breakpoints[i].module);
385 }
386 for (size_t i = 0; i < global_scopes.size(); ++i) {
387 delete global_scopes[i].scope;
388 }
389 for (size_t i = 0; i < component_scopes.size(); ++i) {
390 delete component_scopes[i].scope;
391 }
392 for (size_t i = 0; i < variables.size(); ++i) {
393 delete variables[i];
394 }
395 Free(snapshots);
016a1a93 396 Free(command_result);
7329404e
BB
397}
398
399TTCN3_Debug_Scope* TTCN3_Debugger::add_global_scope(const char* p_module)
400{
401 named_scope_t global_scope;
402 global_scope.name = p_module;
403 global_scope.scope = new TTCN3_Debug_Scope();
404 global_scopes.push_back(global_scope);
405 return global_scope.scope;
406}
407
408TTCN3_Debug_Scope* TTCN3_Debugger::add_component_scope(const char* p_component)
409{
410 named_scope_t component_scope;
411 component_scope.name = p_component;
412 component_scope.scope = new TTCN3_Debug_Scope();
413 component_scopes.push_back(component_scope);
414 return component_scope.scope;
415}
416
417void TTCN3_Debugger::set_return_value(const CHARSTRING& p_value)
418{
016a1a93 419 if (active && !call_stack.empty()) {
7329404e
BB
420 call_stack[call_stack.size() - 1]->set_return_value(p_value);
421 }
422}
423
424void TTCN3_Debugger::breakpoint_entry(int p_line /*bool p_stepping_helper*/)
425{
426 if (active && !call_stack.empty()) {
427 const char* module_name = call_stack[call_stack.size() - 1]->get_module_name();
428 bool trigger = false;
429 const char* trigger_type;
430 int actual_line;
431 switch (p_line) {
432 case SBP_FAIL_VERDICT:
433 trigger = fail_behavior;
016a1a93 434 trigger_type = "Automatic breakpoint (fail verdict)";
7329404e
BB
435 actual_line = last_breakpoint_entry.line;
436 break;
437 case SBP_ERROR_VERDICT:
438 trigger = error_behavior;
016a1a93 439 trigger_type = "Automatic breakpoint (error verdict)";
7329404e
BB
440 actual_line = last_breakpoint_entry.line;
441 break;
016a1a93
BB
442 default: // code lines
443 // make sure it's not the same breakpoint entry as last time
7329404e
BB
444 trigger = (last_breakpoint_entry.line == 0 || p_line != last_breakpoint_entry.line ||
445 module_name != last_breakpoint_entry.module) &&
446 find_breakpoint(module_name, p_line) != breakpoints.size();
016a1a93 447 trigger_type = "User breakpoint";
7329404e
BB
448 actual_line = p_line;
449 break;
450 }
7329404e 451 if (trigger) {
016a1a93
BB
452 print(DRET_NOTIFICATION, "%s reached in module '%s' at line %d.",
453 trigger_type, module_name, actual_line);
454 TTCN_Communication::send_debug_halt_req();
455 halt();
7329404e
BB
456 }
457 last_breakpoint_entry.module = (char*)module_name;
458 last_breakpoint_entry.line = p_line;
459 }
460}
461
462CHARSTRING TTCN3_Debugger::print_base_var(const TTCN3_Debugger::variable_t& p_var)
463{
464 TTCN_Logger::begin_event_log2str();
465 if (!strcmp(p_var.type_name, "bitstring")) {
466 ((const BITSTRING*)p_var.value)->log();
467 }
468 else if (!strcmp(p_var.type_name, "bitstring template")) {
469 ((const BITSTRING_template*)p_var.value)->log();
470 }
471 else if (!strcmp(p_var.type_name, "boolean")) {
472 ((const BOOLEAN*)p_var.value)->log();
473 }
474 else if (!strcmp(p_var.type_name, "boolean template")) {
475 ((const BOOLEAN_template*)p_var.value)->log();
476 }
477 else if (!strcmp(p_var.type_name, "charstring")) {
478 ((const CHARSTRING*)p_var.value)->log();
479 }
480 else if (!strcmp(p_var.type_name, "charstring template")) {
481 ((const CHARSTRING_template*)p_var.value)->log();
482 }
483 else if (!strcmp(p_var.type_name, "float")) {
484 ((const FLOAT*)p_var.value)->log();
485 }
486 else if (!strcmp(p_var.type_name, "float template")) {
487 ((const FLOAT_template*)p_var.value)->log();
488 }
489 else if (!strcmp(p_var.type_name, "hexstring")) {
490 ((const HEXSTRING*)p_var.value)->log();
491 }
492 else if (!strcmp(p_var.type_name, "hexstring template")) {
493 ((const HEXSTRING_template*)p_var.value)->log();
494 }
495 else if (!strcmp(p_var.type_name, "integer")) {
496 ((const INTEGER*)p_var.value)->log();
497 }
498 else if (!strcmp(p_var.type_name, "integer template")) {
499 ((const INTEGER_template*)p_var.value)->log();
500 }
501 else if (!strcmp(p_var.type_name, "objid")) {
502 ((const OBJID*)p_var.value)->log();
503 }
504 else if (!strcmp(p_var.type_name, "objid template")) {
505 ((const OBJID_template*)p_var.value)->log();
506 }
507 else if (!strcmp(p_var.type_name, "octetstring")) {
508 ((const OCTETSTRING*)p_var.value)->log();
509 }
510 else if (!strcmp(p_var.type_name, "octetstring template")) {
511 ((const OCTETSTRING_template*)p_var.value)->log();
512 }
513 else if (!strcmp(p_var.type_name, "universal charstring")) {
514 ((const UNIVERSAL_CHARSTRING*)p_var.value)->log();
515 }
516 else if (!strcmp(p_var.type_name, "universal charstring template")) {
517 ((const UNIVERSAL_CHARSTRING_template*)p_var.value)->log();
518 }
519 else if (!strcmp(p_var.type_name, "verdicttype")) {
520 ((const VERDICTTYPE*)p_var.value)->log();
521 }
522 else if (!strcmp(p_var.type_name, "verdicttype template")) {
523 ((const VERDICTTYPE_template*)p_var.value)->log();
524 }
525 else if (!strcmp(p_var.type_name, "component")) {
526 ((const COMPONENT*)p_var.value)->log();
527 }
528 else if (!strcmp(p_var.type_name, "component template")) {
529 ((const COMPONENT_template*)p_var.value)->log();
530 }
531 else if (!strcmp(p_var.type_name, "default")) {
532 ((const DEFAULT*)p_var.value)->log();
533 }
534 else if (!strcmp(p_var.type_name, "default template")) {
535 ((const DEFAULT_template*)p_var.value)->log();
536 }
537 else if (!strcmp(p_var.type_name, "timer")) {
538 ((const TIMER*)p_var.value)->log();
539 }
540 else if (!strcmp(p_var.type_name, "NULL")) {
541 ((const ASN_NULL*)p_var.value)->log();
542 }
543 else if (!strcmp(p_var.type_name, "NULL template")) {
544 ((const ASN_NULL_template*)p_var.value)->log();
545 }
546 else if (!strcmp(p_var.type_name, "CHARACTER STRING")) {
547 ((const CHARACTER_STRING*)p_var.value)->log();
548 }
549 else if (!strcmp(p_var.type_name, "CHARACTER STRING template")) {
550 ((const CHARACTER_STRING_template*)p_var.value)->log();
551 }
552 else if (!strcmp(p_var.type_name, "EMBEDDED PDV")) {
553 ((const EMBEDDED_PDV*)p_var.value)->log();
554 }
555 else if (!strcmp(p_var.type_name, "EMBEDDED PDV template")) {
556 ((const EMBEDDED_PDV_template*)p_var.value)->log();
557 }
558 else if (!strcmp(p_var.type_name, "EXTERNAL")) {
559 ((const EXTERNAL*)p_var.value)->log();
560 }
561 else if (!strcmp(p_var.type_name, "EXTERNAL template")) {
562 ((const EXTERNAL_template*)p_var.value)->log();
563 }
564 else {
565 TTCN_Logger::log_event_str("<unrecognized value or template>");
566 }
567 return TTCN_Logger::end_event_log2str();
568}
569
016a1a93 570void TTCN3_Debugger::add_to_result(const char* fmt, ...)
7329404e
BB
571{
572 va_list parameters;
573 va_start(parameters, fmt);
016a1a93 574 command_result = mputprintf_va_list(command_result, fmt, parameters);
7329404e 575 va_end(parameters);
7329404e
BB
576}
577
578void TTCN3_Debugger::add_function(TTCN3_Debug_Function* p_function)
579{
580 if (active) {
581 call_stack.push_back(p_function);
582 }
583}
584
585void TTCN3_Debugger::add_scope(TTCN3_Debug_Scope* p_scope)
586{
587 if (active && !call_stack.empty()) {
588 call_stack[call_stack.size() - 1]->add_scope(p_scope);
589 }
590}
591
592void TTCN3_Debugger::remove_function(TTCN3_Debug_Function* p_function)
593{
594 if (!call_stack.empty() && call_stack[call_stack.size() - 1] == p_function) {
595 call_stack.erase_at(call_stack.size() - 1);
596 }
597}
598
599void TTCN3_Debugger::remove_scope(TTCN3_Debug_Scope* p_scope)
600{
601 if (!call_stack.empty()) {
602 call_stack[call_stack.size() - 1]->remove_scope(p_scope);
603 }
604}
605
606const TTCN3_Debugger::variable_t* TTCN3_Debugger::add_variable(const void* p_value,
607 const char* p_name,
608 const char* p_type,
609 CHARSTRING (*p_print_function)(const TTCN3_Debugger::variable_t&))
610{
7329404e
BB
611 if (call_stack.empty()) {
612 // no call stack yet, so this is a global or component variable
613 variable_t* var = find_variable(p_value);
614 if (var == NULL) {
615 var = new TTCN3_Debugger::variable_t;
616 var->value = p_value;
617 var->name = p_name;
618 var->type_name = p_type;
619 var->print_function = p_print_function;
620 variables.push_back(var);
621 }
622 return var;
623 }
624 else if (active) {
625 // it's a local variable for the top-most function
626 return call_stack[call_stack.size() - 1]->add_variable(p_value, p_name, p_type, p_print_function);
627 }
628 return NULL;
629}
630
631void TTCN3_Debugger::remove_variable(const variable_t* p_var)
632{
633 if (active && !call_stack.empty()) {
634 call_stack[call_stack.size() - 1]->remove_variable(p_var);
635 }
636}
637
638const TTCN3_Debug_Scope* TTCN3_Debugger::get_global_scope(const char* p_module) const
639{
640 for (size_t i = 0; i < global_scopes.size(); ++i) {
641 if (strcmp(global_scopes[i].name, p_module) == 0) {
642 return global_scopes[i].scope;
643 }
644 }
645 return NULL;
646}
647
648const TTCN3_Debug_Scope* TTCN3_Debugger::get_component_scope(const char* p_component) const
649{
650 for (size_t i = 0; i < component_scopes.size(); ++i) {
651 if (strcmp(component_scopes[i].name, p_component) == 0) {
652 return component_scopes[i].scope;
653 }
654 }
655 return NULL;
656}
657
658void TTCN3_Debugger::add_snapshot(const char* p_snapshot)
659{
016a1a93
BB
660 if (snapshots != NULL) {
661 snapshots = mputc(snapshots, '\n');
662 }
7329404e
BB
663 snapshots = mputstr(snapshots, p_snapshot);
664}
665
666#define CHECK_NOF_ARGUMENTS(exp_num) \
016a1a93
BB
667 if (exp_num != p_argument_count) { \
668 print(DRET_NOTIFICATION, "Invalid number of arguments. Expected %d, got %d.", \
669 (int)exp_num, (int)p_argument_count); \
7329404e
BB
670 return; \
671 }
672
673#define CHECK_NOF_ARGUMENTS_RANGE(min, max) \
016a1a93
BB
674 if ((int)min > p_argument_count || (int)max < p_argument_count) { \
675 print(DRET_NOTIFICATION, "Invalid number of arguments. Expected at least %d " \
676 "and at most %d, got %d.", (int)min, (int)max, p_argument_count); \
7329404e
BB
677 return; \
678 }
679
680#define CHECK_NOF_ARGUMENTS_MIN(min) \
016a1a93
BB
681 if ((int)min > p_argument_count) { \
682 print(DRET_NOTIFICATION, "Invalid number of arguments. Expected at least %d, got %d.", \
683 (int)min, p_argument_count); \
7329404e
BB
684 return; \
685 }
686
687#define CHECK_INT_ARGUMENT(arg_idx) \
688 { \
016a1a93
BB
689 size_t len = strlen(p_arguments[arg_idx]); \
690 for (size_t i = 0; i < len; ++i) { \
691 if (p_arguments[arg_idx][i] < '0' || p_arguments[arg_idx][i] > '9') { \
692 print(DRET_NOTIFICATION, "Argument %d is not an integer.", (int)(arg_idx + 1)); \
7329404e
BB
693 return; \
694 } \
695 } \
696 }
697
698#define CHECK_CALL_STACK \
699 if (call_stack.empty()) { \
016a1a93 700 print(DRET_NOTIFICATION, "This command can only be used during test execution."); \
7329404e
BB
701 return; \
702 }
703
016a1a93
BB
704#define STACK_LEVEL (stack_level >= 0) ? (size_t)stack_level : (call_stack.size() - 1)
705
706void TTCN3_Debugger::execute_command(int p_command, int p_argument_count,
707 char** p_arguments)
7329404e 708{
016a1a93 709 if (!enabled) {
7329404e
BB
710 return;
711 }
016a1a93
BB
712 Free(command_result);
713 command_result = NULL;
714 for (int i = 0; i < p_argument_count; ++i) {
715 if (p_arguments[i] == NULL) {
716 print(DRET_NOTIFICATION, "Argument %d is a null pointer.", i + 1);
7329404e
BB
717 return;
718 }
719 }
720 switch (p_command) {
016a1a93
BB
721 case D_SWITCH:
722 CHECK_NOF_ARGUMENTS(1)
723 switch_state(p_arguments[0]);
7329404e
BB
724 break;
725 case D_ADD_BREAKPOINT:
726 CHECK_NOF_ARGUMENTS(2)
727 CHECK_INT_ARGUMENT(1)
728 add_breakpoint(p_arguments[0], str2int(p_arguments[1]));
729 break;
730 case D_REMOVE_BREAKPOINT:
731 CHECK_NOF_ARGUMENTS(2)
732 CHECK_INT_ARGUMENT(1)
733 remove_breakpoint(p_arguments[0], str2int(p_arguments[1]));
734 break;
735 case D_SET_ERROR_BEHAVIOR:
736 CHECK_NOF_ARGUMENTS(1)
737 set_special_breakpoint(SBP_ERROR_VERDICT, p_arguments[0]);
738 break;
739 case D_SET_FAIL_BEHAVIOR:
740 CHECK_NOF_ARGUMENTS(1)
741 set_special_breakpoint(SBP_FAIL_VERDICT, p_arguments[0]);
742 break;
7329404e
BB
743 case D_SET_OUTPUT:
744 CHECK_NOF_ARGUMENTS_RANGE(1, 2)
016a1a93
BB
745 set_output(p_arguments[0], (p_argument_count == 2) ? p_arguments[1] : NULL);
746 break;
747 case D_SET_COMPONENT:
748 print(DRET_NOTIFICATION, "Command " D_SET_COMPONENT_TEXT " should have been "
749 "sent to the Main Controller.");
7329404e 750 break;
7329404e
BB
751 case D_PRINT_CALL_STACK:
752 CHECK_CALL_STACK
753 CHECK_NOF_ARGUMENTS(0)
754 print_call_stack();
755 break;
756 case D_SET_STACK_LEVEL:
757 CHECK_CALL_STACK
758 CHECK_NOF_ARGUMENTS(1)
759 CHECK_INT_ARGUMENT(0)
760 set_stack_level(str2int(p_arguments[0]));
016a1a93 761 return; // don't print the command result in this case
7329404e
BB
762 case D_LIST_VARIABLES:
763 CHECK_CALL_STACK
764 CHECK_NOF_ARGUMENTS_RANGE(1, 2)
016a1a93
BB
765 call_stack[STACK_LEVEL]->list_variables(p_arguments[0],
766 (p_argument_count == 2) ? p_arguments[1] : NULL);
7329404e
BB
767 break;
768 case D_PRINT_VARIABLE:
769 CHECK_CALL_STACK
770 CHECK_NOF_ARGUMENTS_MIN(1)
016a1a93
BB
771 for (int i = 0; i < p_argument_count; ++i) {
772 const variable_t* var = call_stack[STACK_LEVEL]->find_variable(p_arguments[i]);
7329404e
BB
773 if (var != NULL) {
774 print_variable(var);
775 }
776 else {
016a1a93
BB
777 add_to_result("Variable '%s' not found.", p_arguments[i]);
778 }
779 if (i != p_argument_count - 1) {
780 add_to_result("\n");
7329404e
BB
781 }
782 }
783 break;
784 // ...
785 case D_PRINT_SNAPSHOTS:
786 CHECK_NOF_ARGUMENTS(0)
016a1a93 787 add_to_result("%s", snapshots);
7329404e
BB
788 break;
789 // ...
016a1a93
BB
790 case D_HALT:
791 if (TTCN_Runtime::is_mtc()) {
792 CHECK_CALL_STACK
793 }
794 CHECK_NOF_ARGUMENTS(0)
795 halt();
796 break;
797 case D_CONTINUE:
798 CHECK_NOF_ARGUMENTS(0)
799 resume();
800 break;
801 case D_EXIT:
802 CHECK_NOF_ARGUMENTS(1)
803 if (TTCN_Runtime::is_mtc()) {
804 CHECK_CALL_STACK
805 }
806 exit_(p_arguments[0]);
807 break;
808 case D_SETUP:
809 CHECK_NOF_ARGUMENTS_MIN(5)
810 if (strlen(p_arguments[0]) > 0) {
811 switch_state(p_arguments[0]);
812 }
813 if (strlen(p_arguments[1]) > 0) {
814 set_output(p_arguments[1], p_arguments[2]);
815 }
816 if (strlen(p_arguments[3]) > 0) {
817 set_special_breakpoint(SBP_ERROR_VERDICT, p_arguments[3]);
818 }
819 if (strlen(p_arguments[4]) > 0) {
820 set_special_breakpoint(SBP_FAIL_VERDICT, p_arguments[4]);
821 }
822 for (int i = 5; i < p_argument_count; i += 2) {
823 add_breakpoint(p_arguments[i], str2int(p_arguments[i + 1]));
824 }
7329404e 825 break;
016a1a93
BB
826 default:
827 print(DRET_NOTIFICATION, "Command not implemented.");
828 return;
829 }
830 if (command_result != NULL) {
831 print(DRET_DATA, command_result);
832 }
833}
834
835void TTCN3_Debugger::open_output_file()
836{
837 if (output_file == NULL && output_file_name != NULL) {
838 char* final_file_name = finalize_file_name(output_file_name);
839 output_file = fopen(final_file_name, "w");
840 if (output_file == NULL) {
841 print(DRET_NOTIFICATION, "Failed to open file '%s' for writing.", final_file_name);
842 }
843 Free(final_file_name);
7329404e
BB
844 }
845}
846
847//////////////////////////////////////////////////////
848//////////////// TTCN3_Debug_Scope ///////////////////
849//////////////////////////////////////////////////////
850
851TTCN3_Debug_Scope::TTCN3_Debug_Scope()
852{
853 ttcn3_debugger.add_scope(this);
854}
855
856TTCN3_Debug_Scope::~TTCN3_Debug_Scope()
857{
858 for (size_t i = 0; i < variables.size(); ++i) {
859 ttcn3_debugger.remove_variable(variables[i]);
860 }
861 ttcn3_debugger.remove_scope(this);
862}
863
864void TTCN3_Debug_Scope::add_variable(const void* p_value,
865 const char* p_name,
866 const char* p_type,
867 CHARSTRING (*p_print_function)(const TTCN3_Debugger::variable_t&))
868{
869 const TTCN3_Debugger::variable_t* var = ttcn3_debugger.add_variable(p_value, p_name, p_type, p_print_function);
870 if (var != NULL) {
871 variables.push_back(var);
872 }
873}
874
875const TTCN3_Debugger::variable_t* TTCN3_Debug_Scope::find_variable(const char* p_name) const
876{
877 for (size_t i = 0; i < variables.size(); ++i) {
878 if (strcmp(variables[i]->name, p_name) == 0) {
879 return variables[i];
880 }
881 }
882 return NULL;
883}
884
016a1a93 885void TTCN3_Debug_Scope::list_variables(regex_t* p_posix_regexp, bool& p_first) const
7329404e
BB
886{
887 for (size_t i = 0; i < variables.size(); ++i) {
016a1a93
BB
888 if (p_posix_regexp == NULL ||
889 regexec(p_posix_regexp, variables[i]->name, 0, NULL, 0) == 0) {
890 ttcn3_debugger.add_to_result("%s%s", p_first ? "" : " ", variables[i]->name);
891 p_first = false;
892 }
7329404e
BB
893 }
894}
895
896//////////////////////////////////////////////////////
897/////////////// TTCN3_Debug_Function /////////////////
898//////////////////////////////////////////////////////
899
900TTCN3_Debug_Function::TTCN3_Debug_Function(const char* p_name,
901 const char* p_type,
902 const char* p_module,
903 const charstring_list& p_parameter_names,
904 const charstring_list& p_parameter_types,
905 const char* p_component_name)
906: function_name(p_name), function_type(p_type), module_name(p_module)
907, parameter_names(new charstring_list(p_parameter_names))
908, parameter_types(new charstring_list(p_parameter_types))
909{
910 ttcn3_debugger.add_function(this);
911 global_scope = ttcn3_debugger.get_global_scope(p_module);
912 component_scope = (p_component_name != NULL) ?
913 ttcn3_debugger.get_component_scope(p_component_name) : NULL;
914 if (function_name == NULL) {
915 function_name = p_module; // for control parts
916 }
917}
918
919TTCN3_Debug_Function::~TTCN3_Debug_Function()
920{
921 if (ttcn3_debugger.is_on()) {
922 char* snapshot = mprintf("[%s]\tfinished\t%s(", function_type, function_name);
923 if (parameter_names->size_of() > 0) {
924 for (int i = 0; i < parameter_names->size_of(); ++i) {
925 if (i > 0) {
926 snapshot = mputstr(snapshot, ", ");
927 }
928 snapshot = mputprintf(snapshot, "[%s] %s := ", (const char*)((*parameter_types)[i]),
929 (const char*)((*parameter_names)[i]));
930 if ((*parameter_types)[i] == "out" || (*parameter_types)[i] == "inout") {
931 const TTCN3_Debugger::variable_t* parameter = find_variable((*parameter_names)[i]);
932 snapshot = mputstr(snapshot, parameter->print_function(*parameter));
933 }
934 else {
935 snapshot = mputc(snapshot, '-');
936 }
937 }
938 }
939 snapshot = mputc(snapshot, ')');
940 if (return_value.is_bound()) {
941 snapshot = mputprintf(snapshot, " returned %s", (const char*)return_value);
942 }
7329404e
BB
943 ttcn3_debugger.add_snapshot(snapshot);
944 Free(snapshot);
945 }
946 for (size_t i = 0; i < variables.size(); ++i) {
947 delete variables[i];
948 }
949 delete parameter_names;
950 delete parameter_types;
951 ttcn3_debugger.remove_function(this);
952}
953
954const TTCN3_Debugger::variable_t* TTCN3_Debug_Function::add_variable(const void* p_value,
955 const char* p_name,
956 const char* p_type,
957 CHARSTRING (*p_print_function)(const TTCN3_Debugger::variable_t&))
958{
959 if (ttcn3_debugger.is_on()) {
960 TTCN3_Debugger::variable_t* var = new TTCN3_Debugger::variable_t;
961 var->value = p_value;
962 var->name = p_name;
963 var->type_name = p_type;
964 var->print_function = p_print_function;
965 variables.push_back(var);
966 return var;
967 }
968 return NULL;
969}
970
971void TTCN3_Debug_Function::set_return_value(const CHARSTRING& p_value)
972{
973 return_value = p_value;
974}
975
976void TTCN3_Debug_Function::initial_snapshot() const
977{
978 if (ttcn3_debugger.is_on()) {
979 char* snapshot = mprintf("[%s]\tstarted \t%s(", function_type, function_name);
980 if (parameter_names->size_of() > 0) {
981 for (int i = 0; i < parameter_names->size_of(); ++i) {
982 if (i > 0) {
983 snapshot = mputstr(snapshot, ", ");
984 }
985 snapshot = mputprintf(snapshot, "[%s] %s := ", (const char*)((*parameter_types)[i]),
986 (const char*)((*parameter_names)[i]));
987 if ((*parameter_types)[i] == "in" || (*parameter_types)[i] == "inout") {
988 const TTCN3_Debugger::variable_t* parameter = find_variable((*parameter_names)[i]);
989 snapshot = mputstr(snapshot, parameter->print_function(*parameter));
990 }
991 else {
992 snapshot = mputc(snapshot, '-');
993 }
994 }
995 }
016a1a93 996 snapshot = mputstr(snapshot, ")");
7329404e
BB
997 ttcn3_debugger.add_snapshot(snapshot);
998 Free(snapshot);
999 }
1000}
1001
1002void TTCN3_Debug_Function::add_scope(TTCN3_Debug_Scope* p_scope)
1003{
1004 scopes.push_back(p_scope);
1005}
1006
1007void TTCN3_Debug_Function::remove_scope(TTCN3_Debug_Scope* p_scope)
1008{
016a1a93 1009 if (!scopes.empty() && scopes[scopes.size() - 1] == p_scope) {
7329404e
BB
1010 scopes.erase_at(scopes.size() - 1);
1011 }
1012}
1013
1014void TTCN3_Debug_Function::remove_variable(const TTCN3_Debugger::variable_t* p_var)
1015{
1016 for (size_t i = 0; i < variables.size(); ++i) {
1017 if (variables[i] == p_var) {
1018 variables.erase_at(i);
1019 delete p_var;
1020 break;
1021 }
1022 }
1023}
1024
1025const TTCN3_Debugger::variable_t* TTCN3_Debug_Function::find_variable(const char* p_name) const
1026{
1027 for (size_t i = 0; i < variables.size(); ++i) {
1028 if (strcmp(variables[i]->name, p_name) == 0) {
1029 return variables[i];
1030 }
1031 }
1032 // it's not a local variable, it might still be a global or component variable
1033 if (component_scope != NULL) {
1034 const TTCN3_Debugger::variable_t* res = component_scope->find_variable(p_name);
1035 if (res != NULL) {
1036 return res;
1037 }
1038 }
1039 return (global_scope != NULL) ? global_scope->find_variable(p_name) : NULL;
1040}
1041
1042void TTCN3_Debug_Function::print_function() const
1043{
016a1a93 1044 ttcn3_debugger.add_to_result("[%s]\t%s(", function_type, function_name);
7329404e
BB
1045 if (parameter_names->size_of() > 0) {
1046 for (int i = 0; i < parameter_names->size_of(); ++i) {
1047 if (i > 0) {
016a1a93 1048 ttcn3_debugger.add_to_result(", ");
7329404e
BB
1049 }
1050 const TTCN3_Debugger::variable_t* parameter = find_variable((*parameter_names)[i]);
016a1a93 1051 ttcn3_debugger.add_to_result("[%s] %s := %s", (const char*)(*parameter_types)[i],
7329404e
BB
1052 (const char*)(*parameter_names)[i], (const char*)parameter->print_function(*parameter));
1053 }
1054 }
016a1a93 1055 ttcn3_debugger.add_to_result(")");
7329404e
BB
1056}
1057
1058void TTCN3_Debug_Function::list_variables(const char* p_scope, const char* p_filter) const
1059{
1060 bool first = true;
1061 bool list_local = false;
1062 bool list_global = false;
1063 bool list_comp = false;
1064 if (!strcmp(p_scope, "local")) {
1065 list_local = true;
1066 }
1067 else if (!strcmp(p_scope, "global")) {
1068 list_global = true;
1069 }
016a1a93 1070 else if (!strcmp(p_scope, "comp")) {
7329404e
BB
1071 list_comp = true;
1072 }
016a1a93 1073 else if (!strcmp(p_scope, "all")) {
7329404e
BB
1074 list_local = true;
1075 list_global = true;
1076 list_comp = true;
1077 }
016a1a93
BB
1078 else {
1079 ttcn3_debugger.print(DRET_NOTIFICATION, "Argument 1 is invalid. "
1080 "Expected 'local', 'global', 'comp' or 'all'.");
1081 return;
1082 }
1083 regex_t* posix_regexp = NULL;
1084 if (p_filter != NULL) {
1085 char* posix_str = TTCN_pattern_to_regexp(p_filter);
1086 if (posix_str == NULL) {
1087 ttcn3_debugger.print(DRET_NOTIFICATION, "Argument 2 is invalid. "
1088 "Expected a valid TTCN-3 character pattern.");
1089 return;
1090 }
1091 posix_regexp = new regex_t;
1092 int ret_val = regcomp(posix_regexp, posix_str, REG_EXTENDED | REG_NOSUB);
1093 Free(posix_str);
1094 if (ret_val != 0) {
1095 char msg[512];
1096 regerror(ret_val, posix_regexp, msg, sizeof(msg));
1097 regfree(posix_regexp);
1098 delete posix_regexp;
1099 ttcn3_debugger.print(DRET_NOTIFICATION, "Compilation of POSIX regular "
1100 "expression failed.");
1101 return;
1102 }
1103 }
7329404e
BB
1104 if (list_local) {
1105 for (size_t i = 0; i < variables.size(); ++i) {
016a1a93
BB
1106 if (posix_regexp == NULL ||
1107 regexec(posix_regexp, variables[i]->name, 0, NULL, 0) == 0) {
1108 ttcn3_debugger.add_to_result("%s%s", first ? "" : " ", variables[i]->name);
1109 first = false;
1110 }
7329404e
BB
1111 }
1112 }
1113 if (list_global && global_scope != NULL && global_scope->has_variables()) {
016a1a93 1114 global_scope->list_variables(posix_regexp, first);
7329404e
BB
1115 }
1116 if (list_comp && component_scope != NULL && component_scope->has_variables()) {
016a1a93 1117 component_scope->list_variables(posix_regexp, first);
7329404e
BB
1118 }
1119 if (first) {
016a1a93
BB
1120 ttcn3_debugger.print(DRET_NOTIFICATION, "No variables found.");
1121 }
1122 if (posix_regexp != NULL) {
1123 regfree(posix_regexp);
1124 delete posix_regexp;
7329404e 1125 }
7329404e
BB
1126}
1127
This page took 0.088385 seconds and 5 git commands to generate.