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"
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"
26 #define PROMPT_TEXT "DEBUG> "
27 #define BATCH_TEXT "batch"
28 #define HELP_TEXT "help"
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> [<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>]", "Remove a breakpoint, "
39 "or all breakpoints from a module, or all breakpoints from all modules." },
40 { D_SET_AUTOMATIC_BREAKPOINT_TEXT
, D_SET_AUTOMATIC_BREAKPOINT
,
41 D_SET_AUTOMATIC_BREAKPOINT_TEXT
" error|fail on|off [<batch_file>]",
42 "Switch an automatic breakpoint (truggered by an event) on or off, and/or "
43 "change its batch file." },
44 { D_SET_OUTPUT_TEXT
, D_SET_OUTPUT
,
45 D_SET_OUTPUT_TEXT
" console|file|both [file_name]",
46 "Set the output of the debugger." },
47 { D_SET_GLOBAL_BATCH_FILE_TEXT
, D_SET_GLOBAL_BATCH_FILE
,
48 D_SET_GLOBAL_BATCH_FILE_TEXT
" on|off [batch_file_name]",
49 "Set whether a batch file should be executed automatically when test execution "
50 "is halted (breakpoint-specific batch files override this setting)." },
51 { D_PRINT_SETTINGS_TEXT
, D_PRINT_SETTINGS
, D_PRINT_SETTINGS_TEXT
,
52 "Prints the debugger's settings." },
53 { D_PRINT_CALL_STACK_TEXT
, D_PRINT_CALL_STACK
, D_PRINT_CALL_STACK_TEXT
,
54 "Print call stack." },
55 { D_SET_STACK_LEVEL_TEXT
, D_SET_STACK_LEVEL
, D_SET_STACK_LEVEL_TEXT
" <level>",
56 "Set the stack level to print debug information from." },
57 { D_LIST_VARIABLES_TEXT
, D_LIST_VARIABLES
,
58 D_LIST_VARIABLES_TEXT
" local|global|comp|all [pattern]",
59 "List variable names." },
60 { D_PRINT_VARIABLE_TEXT
, D_PRINT_VARIABLE
,
61 D_PRINT_VARIABLE_TEXT
" <variable_name>|$ [{ <variable_name>|$}]",
62 "Print current value of one or more variables ('$' is substituted with the "
63 "result of the last " D_LIST_VARIABLES_TEXT
" command)." },
64 { D_OVERWRITE_VARIABLE_TEXT
, D_OVERWRITE_VARIABLE
,
65 D_OVERWRITE_VARIABLE_TEXT
" <variable_name> <value>",
66 "Overwrite the current value of a variable." },
67 { D_PRINT_SNAPSHOTS_TEXT
, D_PRINT_SNAPSHOTS
, D_PRINT_SNAPSHOTS_TEXT
,
68 "Print snapshots of function calls until this point." },
69 // D_SET_SNAPSHOT_BEHAVIOR_TEXT
70 { D_STEP_OVER_TEXT
, D_STEP_OVER
, D_STEP_OVER_TEXT
,
71 "Resume test execution until the next line of code (in this function or the "
72 "caller function)." },
73 { D_STEP_INTO_TEXT
, D_STEP_INTO
, D_STEP_INTO_TEXT
,
74 "Resume test execution until the next line of code (on any stack level)." },
75 { D_STEP_OUT_TEXT
, D_STEP_OUT
, D_STEP_OUT_TEXT
,
76 "Resume test execution until the next line of code in the caller function." },
77 { D_RUN_TO_CURSOR_TEXT
, D_RUN_TO_CURSOR
, D_RUN_TO_CURSOR_TEXT
" <module> <line>",
78 "Resume test execution until the specified location." },
79 { D_HALT_TEXT
, D_HALT
, D_HALT_TEXT
, "Halt test execution." },
80 { D_CONTINUE_TEXT
, D_CONTINUE
, D_CONTINUE_TEXT
, "Resume halted test execution." },
81 { D_EXIT_TEXT
, D_EXIT
, D_EXIT_TEXT
" test|all",
82 "Exit the current test or the execution of all tests." },
83 { NULL
, D_ERROR
, NULL
, NULL
}
86 #ifdef ADVANCED_DEBUGGER_UI
87 char* TTCN_Debugger_UI::ttcn3_history_filename
= NULL
;
90 /** local function for extracting the command name and its arguments from an
92 * @param arguments [in] input line
93 * @param len [in] length of the input line
94 * @param start [in] indicates the position to start searching from
95 * @param start [out] the next argument's start position (set to len if no further
96 * arguments were found)
97 * @param end [out] the position of the first character after the next argument */
98 static void get_next_argument_loc(const char* arguments
, size_t len
, size_t& start
, size_t& end
)
100 while (start
< len
&& isspace(arguments
[start
])) {
104 while (end
< len
&& !isspace(arguments
[end
])) {
109 void TTCN_Debugger_UI::process_command(const char* p_line_read
)
111 // locate the command text
112 size_t len
= strlen(p_line_read
);
115 get_next_argument_loc(p_line_read
, len
, start
, end
);
120 #ifdef ADVANCED_DEBUGGER_UI
121 add_history(p_line_read
+ start
);
123 for (const command_t
*command
= debug_command_list
; command
->name
!= NULL
;
125 if (!strncmp(p_line_read
+ start
, command
->name
, end
- start
)) {
126 // count the arguments
127 int argument_count
= 0;
128 size_t start_tmp
= start
;
129 size_t end_tmp
= end
;
130 while (start_tmp
< len
) {
132 get_next_argument_loc(p_line_read
, len
, start_tmp
, end_tmp
);
133 if (start_tmp
< len
) {
137 // extract the arguments into a string array
139 if (argument_count
> 0) {
140 arguments
= new char*[argument_count
];
141 for (int i
= 0; i
< argument_count
; ++i
) {
143 get_next_argument_loc(p_line_read
, len
, start
, end
);
144 arguments
[i
] = mcopystrn(p_line_read
+ start
, end
- start
);
150 ttcn3_debugger
.execute_command(command
->commandID
, argument_count
, arguments
);
151 if (argument_count
> 0) {
152 for (int i
= 0; i
< argument_count
; ++i
) {
160 if (!strncmp(p_line_read
+ start
, BATCH_TEXT
, end
- start
)) {
162 get_next_argument_loc(p_line_read
, len
, start
, end
); // just to skip to the argument
163 // the entire argument list is treated as one file name (even if it contains spaces)
164 execute_batch_file(p_line_read
+ start
);
167 if (!strncmp(p_line_read
+ start
, HELP_TEXT
, end
- start
)) {
169 get_next_argument_loc(p_line_read
, len
, start
, end
); // just to skip to the argument
170 help(p_line_read
+ start
);
173 puts("Unknown command, try again...");
176 void TTCN_Debugger_UI::help(const char* p_argument
)
178 if (*p_argument
== 0) {
179 puts("Help is available for the following commands:");
181 for (const command_t
*command
= debug_command_list
;
182 command
->name
!= NULL
; command
++) {
183 printf(" %s", command
->name
);
188 for (const command_t
*command
= debug_command_list
;
189 command
->name
!= NULL
; command
++) {
190 if (!strncmp(p_argument
, command
->name
, strlen(command
->name
))) {
191 printf("%s usage: %s\n%s\n", command
->name
, command
->synopsis
,
192 command
->description
);
196 if (!strcmp(p_argument
, BATCH_TEXT
)) {
197 puts(BATCH_TEXT
" usage: " BATCH_TEXT
"\nRun commands from batch file.");
200 printf("No help for %s.\n", p_argument
);
205 void TTCN_Debugger_UI::init()
207 #ifdef ADVANCED_DEBUGGER_UI
208 // initialize history library
210 // calculate history file name
211 const char *home_directory
= getenv("HOME");
212 if (home_directory
== NULL
) {
213 home_directory
= ".";
215 ttcn3_history_filename
= mprintf("%s/%s", home_directory
, TTCN3_HISTORY_FILENAME
);
216 // read history from file, don't bother if it does not exist
217 read_history(ttcn3_history_filename
);
218 // set our own command completion function
219 rl_completion_entry_function
= (Function
*)complete_command
;
223 void TTCN_Debugger_UI::clean_up()
225 #ifdef ADVANCED_DEBUGGER_UI
226 if (write_history(ttcn3_history_filename
)) {
227 puts("Could not save debugger command history.");
229 Free(ttcn3_history_filename
);
233 void TTCN_Debugger_UI::read_loop()
235 while (ttcn3_debugger
.is_halted()) {
236 #ifdef ADVANCED_DEBUGGER_UI
237 // print the prompt and read a line using the readline(), which
238 // automatically handles command completion and command history
239 char* line
= readline(PROMPT_TEXT
);
242 // simplified debugger UI: use a simple fgets
245 char* res
= fgets(line
, sizeof(line
), stdin
);
248 process_command(line
);
249 #ifdef ADVANCED_DEBUGGER_UI
254 // EOF was received -> exit all
256 char** args
= new char*[1];
257 args
[0] = (char*)"all";
258 ttcn3_debugger
.execute_command(D_EXIT
, 1, args
);
264 void TTCN_Debugger_UI::execute_batch_file(const char* p_file_name
)
266 FILE* fp
= fopen(p_file_name
, "r");
268 printf("Failed to open file '%s' for reading.\n", p_file_name
);
272 printf("Executing batch file '%s'.\n", p_file_name
);
275 while (fgets(line
, sizeof(line
), fp
) != NULL
) {
276 size_t len
= strlen(line
);
277 if (line
[len
- 1] == '\n') {
278 line
[len
- 1] = '\0';
282 printf("%s\n", line
);
283 process_command(line
);
287 printf("Error occurred while reading batch file '%s' (error code: %d).\n",
288 p_file_name
, ferror(fp
));
293 void TTCN_Debugger_UI::print(const char* p_str
)
298 #ifdef ADVANCED_DEBUGGER_UI
299 char* TTCN_Debugger_UI::complete_command(const char* p_prefix
, int p_state
)
301 static int command_index
;
302 static size_t prefix_len
;
303 const char *command_name
;
307 prefix_len
= strlen(p_prefix
);
310 while ((command_name
= debug_command_list
[command_index
].name
)) {
312 if (strncmp(p_prefix
, command_name
, prefix_len
) == 0) {
313 // must allocate buffer for returned string (readline() frees it)
314 return strdup(command_name
);