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>|<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
}
91 #ifdef ADVANCED_DEBUGGER_UI
92 char* TTCN_Debugger_UI::ttcn3_history_filename
= NULL
;
95 /** local function for extracting the command name and its arguments from an
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
)
105 while (start
< len
&& isspace(arguments
[start
])) {
109 while (end
< len
&& !isspace(arguments
[end
])) {
114 void TTCN_Debugger_UI::process_command(const char* p_line_read
)
116 // locate the command text
117 size_t len
= strlen(p_line_read
);
120 get_next_argument_loc(p_line_read
, len
, start
, end
);
125 #ifdef ADVANCED_DEBUGGER_UI
126 add_history(p_line_read
+ start
);
128 for (const command_t
*command
= debug_command_list
; command
->name
!= NULL
;
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
) {
137 get_next_argument_loc(p_line_read
, len
, start_tmp
, end_tmp
);
138 if (start_tmp
< len
) {
142 // extract the arguments into a string array
144 if (argument_count
> 0) {
145 arguments
= new char*[argument_count
];
146 for (int i
= 0; i
< argument_count
; ++i
) {
148 get_next_argument_loc(p_line_read
, len
, start
, end
);
149 arguments
[i
] = mcopystrn(p_line_read
+ start
, end
- start
);
155 ttcn3_debugger
.execute_command(command
->commandID
, argument_count
, arguments
);
156 if (argument_count
> 0) {
157 for (int i
= 0; i
< argument_count
; ++i
) {
165 if (!strncmp(p_line_read
+ start
, BATCH_TEXT
, end
- start
)) {
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
);
172 if (!strncmp(p_line_read
+ start
, HELP_TEXT
, end
- start
)) {
174 get_next_argument_loc(p_line_read
, len
, start
, end
); // just to skip to the argument
175 help(p_line_read
+ start
);
178 puts("Unknown command, try again...");
181 void TTCN_Debugger_UI::help(const char* p_argument
)
183 if (*p_argument
== 0) {
184 puts("Help is available for the following commands:");
186 for (const command_t
*command
= debug_command_list
;
187 command
->name
!= NULL
; command
++) {
188 printf(" %s", command
->name
);
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
);
201 if (!strcmp(p_argument
, BATCH_TEXT
)) {
202 puts(BATCH_TEXT
" usage: " BATCH_TEXT
"\nRun commands from batch file.");
205 printf("No help for %s.\n", p_argument
);
210 void TTCN_Debugger_UI::init()
212 #ifdef ADVANCED_DEBUGGER_UI
213 // initialize history library
215 // calculate history file name
216 const char *home_directory
= getenv("HOME");
217 if (home_directory
== NULL
) {
218 home_directory
= ".";
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
;
228 void TTCN_Debugger_UI::clean_up()
230 #ifdef ADVANCED_DEBUGGER_UI
231 if (write_history(ttcn3_history_filename
)) {
232 puts("Could not save debugger command history.");
234 Free(ttcn3_history_filename
);
238 void TTCN_Debugger_UI::read_loop()
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
);
247 // simplified debugger UI: use a simple fgets
250 char* res
= fgets(line
, sizeof(line
), stdin
);
253 process_command(line
);
254 #ifdef ADVANCED_DEBUGGER_UI
259 // EOF was received -> exit all
261 char** args
= new char*[1];
262 args
[0] = (char*)"all";
263 ttcn3_debugger
.execute_command(D_EXIT
, 1, args
);
269 void TTCN_Debugger_UI::execute_batch_file(const char* p_file_name
)
271 FILE* fp
= fopen(p_file_name
, "r");
273 printf("Failed to open file '%s' for reading.\n", p_file_name
);
277 printf("Executing batch file '%s'.\n", p_file_name
);
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';
287 printf("%s\n", line
);
288 process_command(line
);
292 printf("Error occurred while reading batch file '%s' (error code: %d).\n",
293 p_file_name
, ferror(fp
));
298 void TTCN_Debugger_UI::print(const char* p_str
)
303 #ifdef ADVANCED_DEBUGGER_UI
304 char* TTCN_Debugger_UI::complete_command(const char* p_prefix
, int p_state
)
306 static int command_index
;
307 static size_t prefix_len
;
308 const char *command_name
;
312 prefix_len
= strlen(p_prefix
);
315 while ((command_name
= debug_command_list
[command_index
].name
)) {
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
);