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"
17 #include "../mctr2/editline/libedit/src/editline/readline.h"
21 #define PROMPT_TEXT "DEBUG> "
22 #define BATCH_TEXT "batch"
24 // use a different file, than the MCTR CLI, since not all commands are the same
25 #define TTCN3_HISTORY_FILENAME ".ttcn3_history_single"
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
}
84 char* TTCN_Debugger_UI::ttcn3_history_filename
= NULL
;
86 /** local function for extracting the command name and its arguments from an
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
)
96 while (start
< len
&& isspace(arguments
[start
])) {
100 while (end
< len
&& !isspace(arguments
[end
])) {
105 void TTCN_Debugger_UI::process_command(const char* p_line_read
)
107 // locate the command text
108 size_t len
= strlen(p_line_read
);
111 get_next_argument_loc(p_line_read
, len
, start
, end
);
116 add_history(p_line_read
+ start
);
117 for (const command_t
*command
= debug_command_list
; command
->name
!= NULL
;
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
) {
126 get_next_argument_loc(p_line_read
, len
, start_tmp
, end_tmp
);
127 if (start_tmp
< len
) {
131 // extract the arguments into a string array
133 if (argument_count
> 0) {
134 arguments
= new char*[argument_count
];
135 for (int i
= 0; i
< argument_count
; ++i
) {
137 get_next_argument_loc(p_line_read
, len
, start
, end
);
138 arguments
[i
] = mcopystrn(p_line_read
+ start
, end
- start
);
144 ttcn3_debugger
.execute_command(command
->commandID
, argument_count
, arguments
);
145 if (argument_count
> 0) {
146 for (int i
= 0; i
< argument_count
; ++i
) {
154 if (!strncmp(p_line_read
+ start
, BATCH_TEXT
, end
- start
)) {
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
);
161 puts("Unknown command, try again...");
164 void TTCN_Debugger_UI::init()
166 // initialize history library
168 // calculate history file name
169 const char *home_directory
= getenv("HOME");
170 if (home_directory
== NULL
) {
171 home_directory
= ".";
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
;
180 void TTCN_Debugger_UI::clean_up()
182 if (write_history(ttcn3_history_filename
)) {
183 puts("Could not save debugger command history.");
185 Free(ttcn3_history_filename
);
188 void TTCN_Debugger_UI::read_loop()
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
);
195 process_command(line
);
199 // EOF was received -> exit all
201 char** args
= new char*[1];
202 args
[0] = (char*)"all";
203 ttcn3_debugger
.execute_command(D_EXIT
, 1, args
);
209 void TTCN_Debugger_UI::execute_batch_file(const char* p_file_name
)
211 FILE* fp
= fopen(p_file_name
, "r");
213 printf("Failed to open file '%s' for reading.\n", p_file_name
);
217 printf("Executing batch file '%s'.\n", p_file_name
);
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';
227 printf("%s\n", line
);
228 process_command(line
);
232 printf("Error occurred while reading batch file '%s' (error code: %d).\n",
233 p_file_name
, ferror(fp
));
238 void TTCN_Debugger_UI::print(const char* p_str
)
243 char* TTCN_Debugger_UI::complete_command(const char* p_prefix
, int p_state
)
245 static int command_index
;
246 static size_t prefix_len
;
247 const char *command_name
;
251 prefix_len
= strlen(p_prefix
);
254 while ((command_name
= debug_command_list
[command_index
].name
)) {
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
);