Commit | Line | Data |
---|---|---|
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 | ||
25 | TTCN3_Debugger ttcn3_debugger; | |
26 | ||
016a1a93 | 27 | void 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 | ||
52 | void 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 | ||
68 | void 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 | ||
83 | void 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 | ||
119 | void 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 | ||
130 | void 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 | 146 | void 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 | ||
152 | void 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 | ||
217 | void 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 | ||
230 | void 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 | ||
242 | void 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 | ||
261 | size_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 | ||
271 | TTCN3_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 |
281 | char* 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 | ||
346 | void 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 |
360 | TTCN3_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 | ||
377 | TTCN3_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 | ||
399 | TTCN3_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 | ||
408 | TTCN3_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 | ||
417 | void 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 | ||
424 | void 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 | ||
462 | CHARSTRING 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 | 570 | void 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 | ||
578 | void TTCN3_Debugger::add_function(TTCN3_Debug_Function* p_function) | |
579 | { | |
580 | if (active) { | |
581 | call_stack.push_back(p_function); | |
582 | } | |
583 | } | |
584 | ||
585 | void 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 | ||
592 | void 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 | ||
599 | void 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 | ||
606 | const 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 | ||
631 | void 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 | ||
638 | const 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 | ||
648 | const 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 | ||
658 | void 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 | ||
706 | void 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 | ||
835 | void 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 | ||
851 | TTCN3_Debug_Scope::TTCN3_Debug_Scope() | |
852 | { | |
853 | ttcn3_debugger.add_scope(this); | |
854 | } | |
855 | ||
856 | TTCN3_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 | ||
864 | void 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 | ||
875 | const 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 | 885 | void 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 | ||
900 | TTCN3_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 | ||
919 | TTCN3_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 | ||
954 | const 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 | ||
971 | void TTCN3_Debug_Function::set_return_value(const CHARSTRING& p_value) | |
972 | { | |
973 | return_value = p_value; | |
974 | } | |
975 | ||
976 | void 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 | ||
1002 | void TTCN3_Debug_Function::add_scope(TTCN3_Debug_Scope* p_scope) | |
1003 | { | |
1004 | scopes.push_back(p_scope); | |
1005 | } | |
1006 | ||
1007 | void 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 | ||
1014 | void 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 | ||
1025 | const 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 | ||
1042 | void 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 | ||
1058 | void 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 |