debugger: added function breakpoints and added a setting for handling function call...
[deliverable/titan.core.git] / core / DebuggerUI.cc
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 "DebuggerUI.hh"
15 #include "DebugCommands.hh"
16 #include "Debugger.hh"
17 #include <stdio.h>
18 #include <ctype.h>
19
20 #ifdef ADVANCED_DEBUGGER_UI
21 #include "../mctr2/editline/libedit/src/editline/readline.h"
22 // use a different file, than the MCTR CLI, since not all commands are the same
23 #define TTCN3_HISTORY_FILENAME ".ttcn3_history_single"
24 #endif
25
26 #define PROMPT_TEXT "DEBUG> "
27 #define BATCH_TEXT "batch"
28 #define HELP_TEXT "help"
29
30 const TTCN_Debugger_UI::command_t TTCN_Debugger_UI::debug_command_list[] = {
31 { D_SWITCH_TEXT, D_SWITCH, D_SWITCH_TEXT " on|off",
32 "Switch the debugger on or off." },
33 { D_SET_BREAKPOINT_TEXT, D_SET_BREAKPOINT,
34 D_SET_BREAKPOINT_TEXT " <module> <line>|<function> [<batch_file>]",
35 "Add a breakpoint at the specified location, or change the batch file of "
36 "an existing breakpoint." },
37 { D_REMOVE_BREAKPOINT_TEXT, D_REMOVE_BREAKPOINT,
38 D_REMOVE_BREAKPOINT_TEXT " all|<module> [all|<line>|<function>]",
39 "Remove a breakpoint, or all breakpoints from a module, or all breakpoints "
40 "from all modules." },
41 { D_SET_AUTOMATIC_BREAKPOINT_TEXT, D_SET_AUTOMATIC_BREAKPOINT,
42 D_SET_AUTOMATIC_BREAKPOINT_TEXT " error|fail on|off [<batch_file>]",
43 "Switch an automatic breakpoint (truggered by an event) on or off, and/or "
44 "change its batch file." },
45 { D_SET_OUTPUT_TEXT, D_SET_OUTPUT,
46 D_SET_OUTPUT_TEXT " console|file|both [file_name]",
47 "Set the output of the debugger." },
48 { D_SET_GLOBAL_BATCH_FILE_TEXT, D_SET_GLOBAL_BATCH_FILE,
49 D_SET_GLOBAL_BATCH_FILE_TEXT " on|off [batch_file_name]",
50 "Set whether a batch file should be executed automatically when test execution "
51 "is halted (breakpoint-specific batch files override this setting)." },
52 { D_FUNCTION_CALL_CONFIG_TEXT, D_FUNCTION_CALL_CONFIG,
53 D_FUNCTION_CALL_CONFIG_TEXT " file|<limit>|all [<file_name>]",
54 "Configure the storing of function call data." },
55 { D_PRINT_SETTINGS_TEXT, D_PRINT_SETTINGS, D_PRINT_SETTINGS_TEXT,
56 "Prints the debugger's settings." },
57 { D_PRINT_CALL_STACK_TEXT, D_PRINT_CALL_STACK, D_PRINT_CALL_STACK_TEXT,
58 "Print call stack." },
59 { D_SET_STACK_LEVEL_TEXT, D_SET_STACK_LEVEL, D_SET_STACK_LEVEL_TEXT " <level>",
60 "Set the stack level to print debug information from." },
61 { D_LIST_VARIABLES_TEXT, D_LIST_VARIABLES,
62 D_LIST_VARIABLES_TEXT " [local|global|comp|all] [pattern]",
63 "List variable names." },
64 { D_PRINT_VARIABLE_TEXT, D_PRINT_VARIABLE,
65 D_PRINT_VARIABLE_TEXT " <variable_name>|$ [{ <variable_name>|$}]",
66 "Print current value of one or more variables ('$' is substituted with the "
67 "result of the last " D_LIST_VARIABLES_TEXT " command)." },
68 { D_OVERWRITE_VARIABLE_TEXT, D_OVERWRITE_VARIABLE,
69 D_OVERWRITE_VARIABLE_TEXT " <variable_name> <value>",
70 "Overwrite the current value of a variable." },
71 { D_PRINT_FUNCTION_CALLS_TEXT, D_PRINT_FUNCTION_CALLS,
72 D_PRINT_FUNCTION_CALLS_TEXT " [all|<amount>]",
73 "Print function call data." },
74 { D_STEP_OVER_TEXT, D_STEP_OVER, D_STEP_OVER_TEXT,
75 "Resume test execution until the next line of code (in this function or the "
76 "caller function)." },
77 { D_STEP_INTO_TEXT, D_STEP_INTO, D_STEP_INTO_TEXT,
78 "Resume test execution until the next line of code (on any stack level)." },
79 { D_STEP_OUT_TEXT, D_STEP_OUT, D_STEP_OUT_TEXT,
80 "Resume test execution until the next line of code in the caller function." },
81 { D_RUN_TO_CURSOR_TEXT, D_RUN_TO_CURSOR,
82 D_RUN_TO_CURSOR_TEXT " <module> <line>|<function>",
83 "Resume test execution until the specified location." },
84 { D_HALT_TEXT, D_HALT, D_HALT_TEXT, "Halt test execution." },
85 { D_CONTINUE_TEXT, D_CONTINUE, D_CONTINUE_TEXT, "Resume halted test execution." },
86 { D_EXIT_TEXT, D_EXIT, D_EXIT_TEXT " test|all",
87 "Exit the current test or the execution of all tests." },
88 { NULL, D_ERROR, NULL, NULL }
89 };
90
91 #ifdef ADVANCED_DEBUGGER_UI
92 char* TTCN_Debugger_UI::ttcn3_history_filename = NULL;
93 #endif
94
95 /** local function for extracting the command name and its arguments from an
96 * input line
97 * @param arguments [in] input line
98 * @param len [in] length of the input line
99 * @param start [in] indicates the position to start searching from
100 * @param start [out] the next argument's start position (set to len if no further
101 * arguments were found)
102 * @param end [out] the position of the first character after the next argument */
103 static void get_next_argument_loc(const char* arguments, size_t len, size_t& start, size_t& end)
104 {
105 while (start < len && isspace(arguments[start])) {
106 ++start;
107 }
108 end = start;
109 while (end < len && !isspace(arguments[end])) {
110 ++end;
111 }
112 }
113
114 void TTCN_Debugger_UI::process_command(const char* p_line_read)
115 {
116 // locate the command text
117 size_t len = strlen(p_line_read);
118 size_t start = 0;
119 size_t end = 0;
120 get_next_argument_loc(p_line_read, len, start, end);
121 if (start == len) {
122 // empty command
123 return;
124 }
125 #ifdef ADVANCED_DEBUGGER_UI
126 add_history(p_line_read + start);
127 #endif
128 for (const command_t *command = debug_command_list; command->name != NULL;
129 ++command) {
130 if (!strncmp(p_line_read + start, command->name, end - start)) {
131 // count the arguments
132 int argument_count = 0;
133 size_t start_tmp = start;
134 size_t end_tmp = end;
135 while (start_tmp < len) {
136 start_tmp = end_tmp;
137 get_next_argument_loc(p_line_read, len, start_tmp, end_tmp);
138 if (start_tmp < len) {
139 ++argument_count;
140 }
141 }
142 // extract the arguments into a string array
143 char** arguments;
144 if (argument_count > 0) {
145 arguments = new char*[argument_count];
146 for (int i = 0; i < argument_count; ++i) {
147 start = end;
148 get_next_argument_loc(p_line_read, len, start, end);
149 arguments[i] = mcopystrn(p_line_read + start, end - start);
150 }
151 }
152 else {
153 arguments = NULL;
154 }
155 ttcn3_debugger.execute_command(command->commandID, argument_count, arguments);
156 if (argument_count > 0) {
157 for (int i = 0; i < argument_count; ++i) {
158 Free(arguments[i]);
159 }
160 delete [] arguments;
161 }
162 return;
163 }
164 }
165 if (!strncmp(p_line_read + start, BATCH_TEXT, end - start)) {
166 start = end;
167 get_next_argument_loc(p_line_read, len, start, end); // just to skip to the argument
168 // the entire argument list is treated as one file name (even if it contains spaces)
169 execute_batch_file(p_line_read + start);
170 return;
171 }
172 if (!strncmp(p_line_read + start, HELP_TEXT, end - start)) {
173 start = end;
174 get_next_argument_loc(p_line_read, len, start, end); // just to skip to the argument
175 help(p_line_read + start);
176 return;
177 }
178 puts("Unknown command, try again...");
179 }
180
181 void TTCN_Debugger_UI::help(const char* p_argument)
182 {
183 if (*p_argument == 0) {
184 puts("Help is available for the following commands:");
185 printf(BATCH_TEXT);
186 for (const command_t *command = debug_command_list;
187 command->name != NULL; command++) {
188 printf(" %s", command->name);
189 }
190 putchar('\n');
191 }
192 else {
193 for (const command_t *command = debug_command_list;
194 command->name != NULL; command++) {
195 if (!strncmp(p_argument, command->name, strlen(command->name))) {
196 printf("%s usage: %s\n%s\n", command->name, command->synopsis,
197 command->description);
198 return;
199 }
200 }
201 if (!strcmp(p_argument, BATCH_TEXT)) {
202 puts(BATCH_TEXT " usage: " BATCH_TEXT "\nRun commands from batch file.");
203 }
204 else {
205 printf("No help for %s.\n", p_argument);
206 }
207 }
208 }
209
210 void TTCN_Debugger_UI::init()
211 {
212 #ifdef ADVANCED_DEBUGGER_UI
213 // initialize history library
214 using_history();
215 // calculate history file name
216 const char *home_directory = getenv("HOME");
217 if (home_directory == NULL) {
218 home_directory = ".";
219 }
220 ttcn3_history_filename = mprintf("%s/%s", home_directory, TTCN3_HISTORY_FILENAME);
221 // read history from file, don't bother if it does not exist
222 read_history(ttcn3_history_filename);
223 // set our own command completion function
224 rl_completion_entry_function = (Function*)complete_command;
225 #endif
226 }
227
228 void TTCN_Debugger_UI::clean_up()
229 {
230 #ifdef ADVANCED_DEBUGGER_UI
231 if (write_history(ttcn3_history_filename)) {
232 puts("Could not save debugger command history.");
233 }
234 Free(ttcn3_history_filename);
235 #endif
236 }
237
238 void TTCN_Debugger_UI::read_loop()
239 {
240 while (ttcn3_debugger.is_halted()) {
241 #ifdef ADVANCED_DEBUGGER_UI
242 // print the prompt and read a line using the readline(), which
243 // automatically handles command completion and command history
244 char* line = readline(PROMPT_TEXT);
245 if (line != NULL) {
246 #else
247 // simplified debugger UI: use a simple fgets
248 printf(PROMPT_TEXT);
249 char line[1024];
250 char* res = fgets(line, sizeof(line), stdin);
251 if (res != NULL) {
252 #endif
253 process_command(line);
254 #ifdef ADVANCED_DEBUGGER_UI
255 free(line);
256 #endif
257 }
258 else {
259 // EOF was received -> exit all
260 puts("exit all");
261 char** args = new char*[1];
262 args[0] = (char*)"all";
263 ttcn3_debugger.execute_command(D_EXIT, 1, args);
264 delete [] args;
265 }
266 }
267 }
268
269 void TTCN_Debugger_UI::execute_batch_file(const char* p_file_name)
270 {
271 FILE* fp = fopen(p_file_name, "r");
272 if (fp == NULL) {
273 printf("Failed to open file '%s' for reading.\n", p_file_name);
274 return;
275 }
276 else {
277 printf("Executing batch file '%s'.\n", p_file_name);
278 }
279 char line[1024];
280 while (fgets(line, sizeof(line), fp) != NULL) {
281 size_t len = strlen(line);
282 if (line[len - 1] == '\n') {
283 line[len - 1] = '\0';
284 --len;
285 }
286 if (len != 0) {
287 printf("%s\n", line);
288 process_command(line);
289 }
290 }
291 if (!feof(fp)) {
292 printf("Error occurred while reading batch file '%s' (error code: %d).\n",
293 p_file_name, ferror(fp));
294 }
295 fclose(fp);
296 }
297
298 void TTCN_Debugger_UI::print(const char* p_str)
299 {
300 puts(p_str);
301 }
302
303 #ifdef ADVANCED_DEBUGGER_UI
304 char* TTCN_Debugger_UI::complete_command(const char* p_prefix, int p_state)
305 {
306 static int command_index;
307 static size_t prefix_len;
308 const char *command_name;
309
310 if (p_state == 0) {
311 command_index = 0;
312 prefix_len = strlen(p_prefix);
313 }
314
315 while ((command_name = debug_command_list[command_index].name)) {
316 ++command_index;
317 if (strncmp(p_prefix, command_name, prefix_len) == 0) {
318 // must allocate buffer for returned string (readline() frees it)
319 return strdup(command_name);
320 }
321 }
322 // no match found
323 return NULL;
324 }
325 #endif
This page took 0.075239 seconds and 5 git commands to generate.