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