debugger: added help in single mode UI, plus minor fixes
[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 "../mctr2/editline/libedit/src/editline/readline.h"
18 #include <stdio.h>
19 #include <ctype.h>
20
21 #define PROMPT_TEXT "DEBUG> "
22 #define BATCH_TEXT "batch"
23 #define HELP_TEXT "help"
24
25 // use a different file, than the MCTR CLI, since not all commands are the same
26 #define TTCN3_HISTORY_FILENAME ".ttcn3_history_single"
27
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 }
83 };
84
85 char* TTCN_Debugger_UI::ttcn3_history_filename = NULL;
86
87 /** local function for extracting the command name and its arguments from an
88 * input line
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)
96 {
97 while (start < len && isspace(arguments[start])) {
98 ++start;
99 }
100 end = start;
101 while (end < len && !isspace(arguments[end])) {
102 ++end;
103 }
104 }
105
106 void TTCN_Debugger_UI::process_command(const char* p_line_read)
107 {
108 // locate the command text
109 size_t len = strlen(p_line_read);
110 size_t start = 0;
111 size_t end = 0;
112 get_next_argument_loc(p_line_read, len, start, end);
113 if (start == len) {
114 // empty command
115 return;
116 }
117 add_history(p_line_read + start);
118 for (const command_t *command = debug_command_list; command->name != NULL;
119 ++command) {
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) {
126 start_tmp = end_tmp;
127 get_next_argument_loc(p_line_read, len, start_tmp, end_tmp);
128 if (start_tmp < len) {
129 ++argument_count;
130 }
131 }
132 // extract the arguments into a string array
133 char** arguments;
134 if (argument_count > 0) {
135 arguments = new char*[argument_count];
136 for (int i = 0; i < argument_count; ++i) {
137 start = end;
138 get_next_argument_loc(p_line_read, len, start, end);
139 arguments[i] = mcopystrn(p_line_read + start, end - start);
140 }
141 }
142 else {
143 arguments = NULL;
144 }
145 ttcn3_debugger.execute_command(command->commandID, argument_count, arguments);
146 if (argument_count > 0) {
147 for (int i = 0; i < argument_count; ++i) {
148 Free(arguments[i]);
149 }
150 delete [] arguments;
151 }
152 return;
153 }
154 }
155 if (!strncmp(p_line_read + start, BATCH_TEXT, end - start)) {
156 start = end;
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);
160 return;
161 }
162 if (!strncmp(p_line_read + start, HELP_TEXT, end - start)) {
163 start = end;
164 get_next_argument_loc(p_line_read, len, start, end); // just to skip to the argument
165 help(p_line_read + start);
166 return;
167 }
168 puts("Unknown command, try again...");
169 }
170
171 void TTCN_Debugger_UI::help(const char* p_argument)
172 {
173 if (*p_argument == 0) {
174 puts("Help is available for the following commands:");
175 printf(BATCH_TEXT);
176 for (const command_t *command = debug_command_list;
177 command->name != NULL; command++) {
178 printf(" %s", command->name);
179 }
180 putchar('\n');
181 }
182 else {
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);
188 return;
189 }
190 }
191 if (!strcmp(p_argument, BATCH_TEXT)) {
192 puts(BATCH_TEXT " usage: " BATCH_TEXT "\nRun commands from batch file.");
193 }
194 else {
195 printf("No help for %s.\n", p_argument);
196 }
197 }
198 }
199
200 void TTCN_Debugger_UI::init()
201 {
202 // initialize history library
203 using_history();
204 // calculate history file name
205 const char *home_directory = getenv("HOME");
206 if (home_directory == NULL) {
207 home_directory = ".";
208 }
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;
214 }
215
216 void TTCN_Debugger_UI::clean_up()
217 {
218 if (write_history(ttcn3_history_filename)) {
219 puts("Could not save debugger command history.");
220 }
221 Free(ttcn3_history_filename);
222 }
223
224 void TTCN_Debugger_UI::read_loop()
225 {
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);
230 if (line != NULL) {
231 process_command(line);
232 free(line);
233 }
234 else {
235 // EOF was received -> exit all
236 puts("exit all");
237 char** args = new char*[1];
238 args[0] = (char*)"all";
239 ttcn3_debugger.execute_command(D_EXIT, 1, args);
240 delete [] args;
241 }
242 }
243 }
244
245 void TTCN_Debugger_UI::execute_batch_file(const char* p_file_name)
246 {
247 FILE* fp = fopen(p_file_name, "r");
248 if (fp == NULL) {
249 printf("Failed to open file '%s' for reading.\n", p_file_name);
250 return;
251 }
252 else {
253 printf("Executing batch file '%s'.\n", p_file_name);
254 }
255 char line[1024];
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';
260 --len;
261 }
262 if (len != 0) {
263 printf("%s\n", line);
264 process_command(line);
265 }
266 }
267 if (!feof(fp)) {
268 printf("Error occurred while reading batch file '%s' (error code: %d).\n",
269 p_file_name, ferror(fp));
270 }
271 fclose(fp);
272 }
273
274 void TTCN_Debugger_UI::print(const char* p_str)
275 {
276 puts(p_str);
277 }
278
279 char* TTCN_Debugger_UI::complete_command(const char* p_prefix, int p_state)
280 {
281 static int command_index;
282 static size_t prefix_len;
283 const char *command_name;
284
285 if (p_state == 0) {
286 command_index = 0;
287 prefix_len = strlen(p_prefix);
288 }
289
290 while ((command_name = debug_command_list[command_index].name)) {
291 ++command_index;
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);
295 }
296 }
297 // no match found
298 return NULL;
299 }
This page took 0.037971 seconds and 6 git commands to generate.