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
10 * Baranyi, Botond – initial implementation
12 ******************************************************************************/
14 #include "DebuggerUI.hh"
15 #include "DebugCommands.hh"
16 #include "Debugger.hh"
17 #include "../mctr2/editline/libedit/src/editline/readline.h"
21 #define PROMPT_TEXT "DEBUG> "
22 #define BATCH_TEXT "batch"
23 #define HELP_TEXT "help"
25 // use a different file, than the MCTR CLI, since not all commands are the same
26 #define TTCN3_HISTORY_FILENAME ".ttcn3_history_single"
28 const TTCN_Debugger_UI::command_t
TTCN_Debugger_UI::debug_command_list
[] = {
29 { D_SWITCH_TEXT
, D_SWITCH
, D_SWITCH_TEXT
" on|off",
30 "Switch the debugger on or off." },
31 { D_SET_BREAKPOINT_TEXT
, D_SET_BREAKPOINT
,
32 D_SET_BREAKPOINT_TEXT
" <module> <line> [<batch_file>]",
33 "Add a breakpoint at the specified location, or change the batch file of "
34 "an existing breakpoint." },
35 { D_REMOVE_BREAKPOINT_TEXT
, D_REMOVE_BREAKPOINT
,
36 D_REMOVE_BREAKPOINT_TEXT
" all|<module> [all|<line>]", "Remove a breakpoint, "
37 "or all breakpoints from a module, or all breakpoints from all modules." },
38 { D_SET_AUTOMATIC_BREAKPOINT_TEXT
, D_SET_AUTOMATIC_BREAKPOINT
,
39 D_SET_AUTOMATIC_BREAKPOINT_TEXT
" error|fail on|off [<batch_file>]",
40 "Switch an automatic breakpoint (truggered by an event) on or off, and/or "
41 "change its batch file." },
42 { D_SET_OUTPUT_TEXT
, D_SET_OUTPUT
,
43 D_SET_OUTPUT_TEXT
" console|file|both [file_name]",
44 "Set the output of the debugger." },
45 { D_SET_GLOBAL_BATCH_FILE_TEXT
, D_SET_GLOBAL_BATCH_FILE
,
46 D_SET_GLOBAL_BATCH_FILE_TEXT
" on|off [batch_file_name]",
47 "Set whether a batch file should be executed automatically when test execution "
48 "is halted (breakpoint-specific batch files override this setting)." },
49 { D_SET_COMPONENT_TEXT
, D_SET_COMPONENT
,
50 D_SET_COMPONENT_TEXT
" mtc|<component_reference>", "Set the test component "
51 "to print debug information from (not available in single mode)." },
52 { D_PRINT_CALL_STACK_TEXT
, D_PRINT_CALL_STACK
, D_PRINT_CALL_STACK_TEXT
,
53 "Print call stack." },
54 { D_SET_STACK_LEVEL_TEXT
, D_SET_STACK_LEVEL
, D_SET_STACK_LEVEL_TEXT
" <level>",
55 "Set the stack level to print debug information from." },
56 { D_LIST_VARIABLES_TEXT
, D_LIST_VARIABLES
,
57 D_LIST_VARIABLES_TEXT
" local|global|comp|all [pattern]",
58 "List variable names." },
59 { D_PRINT_VARIABLE_TEXT
, D_PRINT_VARIABLE
,
60 D_PRINT_VARIABLE_TEXT
" <variable_name>|$ [{ <variable_name>|$}]",
61 "Print current value of one or more variables ('$' is substituted with the "
62 "result of the last " D_LIST_VARIABLES_TEXT
" command)." },
63 { D_OVERWRITE_VARIABLE_TEXT
, D_OVERWRITE_VARIABLE
,
64 D_OVERWRITE_VARIABLE_TEXT
" <variable_name> <value>",
65 "Overwrite the current value of a variable." },
66 { D_PRINT_SNAPSHOTS_TEXT
, D_PRINT_SNAPSHOTS
, D_PRINT_SNAPSHOTS_TEXT
,
67 "Print snapshots of function calls until this point." },
68 // D_SET_SNAPSHOT_BEHAVIOR_TEXT
69 { D_STEP_OVER_TEXT
, D_STEP_OVER
, D_STEP_OVER_TEXT
,
70 "Resume test execution until the next line of code (in this function or the "
71 "caller function)." },
72 { D_STEP_INTO_TEXT
, D_STEP_INTO
, D_STEP_INTO_TEXT
,
73 "Resume test execution until the next line of code (on any stack level)." },
74 { D_STEP_OUT_TEXT
, D_STEP_OUT
, D_STEP_OUT_TEXT
,
75 "Resume test execution until the next line of code in the caller function." },
76 { D_RUN_TO_CURSOR_TEXT
, D_RUN_TO_CURSOR
, D_RUN_TO_CURSOR_TEXT
" <module> <line>",
77 "Resume test execution until the specified location." },
78 { D_HALT_TEXT
, D_HALT
, D_HALT_TEXT
, "Halt test execution." },
79 { D_CONTINUE_TEXT
, D_CONTINUE
, D_CONTINUE_TEXT
, "Resume halted test execution." },
80 { D_EXIT_TEXT
, D_EXIT
, D_EXIT_TEXT
" test|all",
81 "Exit the current test or the execution of all tests." },
82 { NULL
, D_ERROR
, NULL
, NULL
}
85 char* TTCN_Debugger_UI::ttcn3_history_filename
= NULL
;
87 /** local function for extracting the command name and its arguments from an
89 * @param arguments [in] input line
90 * @param len [in] length of the input line
91 * @param start [in] indicates the position to start searching from
92 * @param start [out] the next argument's start position (set to len if no further
93 * arguments were found)
94 * @param end [out] the position of the first character after the next argument */
95 static void get_next_argument_loc(const char* arguments
, size_t len
, size_t& start
, size_t& end
)
97 while (start
< len
&& isspace(arguments
[start
])) {
101 while (end
< len
&& !isspace(arguments
[end
])) {
106 void TTCN_Debugger_UI::process_command(const char* p_line_read
)
108 // locate the command text
109 size_t len
= strlen(p_line_read
);
112 get_next_argument_loc(p_line_read
, len
, start
, end
);
117 add_history(p_line_read
+ start
);
118 for (const command_t
*command
= debug_command_list
; command
->name
!= NULL
;
120 if (!strncmp(p_line_read
+ start
, command
->name
, end
- start
)) {
121 // count the arguments
122 int argument_count
= 0;
123 size_t start_tmp
= start
;
124 size_t end_tmp
= end
;
125 while (start_tmp
< len
) {
127 get_next_argument_loc(p_line_read
, len
, start_tmp
, end_tmp
);
128 if (start_tmp
< len
) {
132 // extract the arguments into a string array
134 if (argument_count
> 0) {
135 arguments
= new char*[argument_count
];
136 for (int i
= 0; i
< argument_count
; ++i
) {
138 get_next_argument_loc(p_line_read
, len
, start
, end
);
139 arguments
[i
] = mcopystrn(p_line_read
+ start
, end
- start
);
145 ttcn3_debugger
.execute_command(command
->commandID
, argument_count
, arguments
);
146 if (argument_count
> 0) {
147 for (int i
= 0; i
< argument_count
; ++i
) {
155 if (!strncmp(p_line_read
+ start
, BATCH_TEXT
, end
- start
)) {
157 get_next_argument_loc(p_line_read
, len
, start
, end
); // just to skip to the argument
158 // the entire argument list is treated as one file name (even if it contains spaces)
159 execute_batch_file(p_line_read
+ start
);
162 if (!strncmp(p_line_read
+ start
, HELP_TEXT
, end
- start
)) {
164 get_next_argument_loc(p_line_read
, len
, start
, end
); // just to skip to the argument
165 help(p_line_read
+ start
);
168 puts("Unknown command, try again...");
171 void TTCN_Debugger_UI::help(const char* p_argument
)
173 if (*p_argument
== 0) {
174 puts("Help is available for the following commands:");
176 for (const command_t
*command
= debug_command_list
;
177 command
->name
!= NULL
; command
++) {
178 printf(" %s", command
->name
);
183 for (const command_t
*command
= debug_command_list
;
184 command
->name
!= NULL
; command
++) {
185 if (!strncmp(p_argument
, command
->name
, strlen(command
->name
))) {
186 printf("%s usage: %s\n%s\n", command
->name
, command
->synopsis
,
187 command
->description
);
191 if (!strcmp(p_argument
, BATCH_TEXT
)) {
192 puts(BATCH_TEXT
" usage: " BATCH_TEXT
"\nRun commands from batch file.");
195 printf("No help for %s.\n", p_argument
);
200 void TTCN_Debugger_UI::init()
202 // initialize history library
204 // calculate history file name
205 const char *home_directory
= getenv("HOME");
206 if (home_directory
== NULL
) {
207 home_directory
= ".";
209 ttcn3_history_filename
= mprintf("%s/%s", home_directory
, TTCN3_HISTORY_FILENAME
);
210 // read history from file, don't bother if it does not exist
211 read_history(ttcn3_history_filename
);
212 // set our own command completion function
213 rl_completion_entry_function
= (Function
*)complete_command
;
216 void TTCN_Debugger_UI::clean_up()
218 if (write_history(ttcn3_history_filename
)) {
219 puts("Could not save debugger command history.");
221 Free(ttcn3_history_filename
);
224 void TTCN_Debugger_UI::read_loop()
226 while (ttcn3_debugger
.is_halted()) {
227 // print the prompt and read a line using the readline(), which
228 // automatically handles command completion and command history
229 char* line
= readline(PROMPT_TEXT
);
231 process_command(line
);
235 // EOF was received -> exit all
237 char** args
= new char*[1];
238 args
[0] = (char*)"all";
239 ttcn3_debugger
.execute_command(D_EXIT
, 1, args
);
245 void TTCN_Debugger_UI::execute_batch_file(const char* p_file_name
)
247 FILE* fp
= fopen(p_file_name
, "r");
249 printf("Failed to open file '%s' for reading.\n", p_file_name
);
253 printf("Executing batch file '%s'.\n", p_file_name
);
256 while (fgets(line
, sizeof(line
), fp
) != NULL
) {
257 size_t len
= strlen(line
);
258 if (line
[len
- 1] == '\n') {
259 line
[len
- 1] = '\0';
263 printf("%s\n", line
);
264 process_command(line
);
268 printf("Error occurred while reading batch file '%s' (error code: %d).\n",
269 p_file_name
, ferror(fp
));
274 void TTCN_Debugger_UI::print(const char* p_str
)
279 char* TTCN_Debugger_UI::complete_command(const char* p_prefix
, int p_state
)
281 static int command_index
;
282 static size_t prefix_len
;
283 const char *command_name
;
287 prefix_len
= strlen(p_prefix
);
290 while ((command_name
= debug_command_list
[command_index
].name
)) {
292 if (strncmp(p_prefix
, command_name
, prefix_len
) == 0) {
293 // must allocate buffer for returned string (readline() frees it)
294 return strdup(command_name
);