From: Botond Baranyi Date: Fri, 24 Jun 2016 11:46:26 +0000 (+0200) Subject: debugger: added function breakpoints and added a setting for handling function call... X-Git-Url: http://git.efficios.com/?p=deliverable%2Ftitan.core.git;a=commitdiff_plain;h=cf2b605667d9367755dadd6f540b97e0d6f5af98 debugger: added function breakpoints and added a setting for handling function call data Change-Id: I78833e8462686c4385cd540a41a1abd0d1b5162b Signed-off-by: Botond Baranyi --- diff --git a/core/DebugCommands.hh b/core/DebugCommands.hh index 96bc6ec..96c7218 100644 --- a/core/DebugCommands.hh +++ b/core/DebugCommands.hh @@ -18,38 +18,39 @@ // settings #define D_SWITCH 1 // 1, "on" or "off" -#define D_SET_BREAKPOINT 2 // 2-3, module name and line number + optional batch file name -#define D_REMOVE_BREAKPOINT 3 // 1-2, 'all', or module name + 'all' or line number +#define D_SET_BREAKPOINT 2 // 2-3, module name, and line number or function name, + optional batch file name +#define D_REMOVE_BREAKPOINT 3 // 1-2, 'all', or module name + 'all' or line number or function name #define D_SET_AUTOMATIC_BREAKPOINT 4 // 2-3, "error" or "fail", + "off", or "on" + optional batch file name #define D_SET_OUTPUT 5 // 1-2, "console", or "file" or "both", + file name #define D_SET_GLOBAL_BATCH_FILE 6 // 1-2, "off", or "on" + batch file name -#define D_PRINT_SETTINGS 7 // 0 +#define D_FUNCTION_CALL_CONFIG 7 // 1-2, ring buffer size or "all", or "file" + file name +#define D_PRINT_SETTINGS 8 // 0 // printing and overwriting data -#define D_LIST_COMPONENTS 8 // 0 -#define D_SET_COMPONENT 9 // 1, "mtc" or component reference -#define D_PRINT_CALL_STACK 10 // 0 -#define D_SET_STACK_LEVEL 11 // 1, stack level -#define D_LIST_VARIABLES 12 // 1-2, "local", "global", "comp" or "all", + optional filter (pattern) -#define D_PRINT_VARIABLE 13 // 1+, list of variable names -#define D_OVERWRITE_VARIABLE 14 // 2, variable name, new value (in module parameter syntax) -#define D_PRINT_SNAPSHOTS 15 // 0 -#define D_SET_SNAPSHOT_BEHAVIOR 16 // TBD +#define D_LIST_COMPONENTS 9 // 0 +#define D_SET_COMPONENT 10 // 1, component name or component reference +#define D_PRINT_CALL_STACK 11 // 0 +#define D_SET_STACK_LEVEL 12 // 1, stack level +#define D_LIST_VARIABLES 13 // 0-2, optional "local", "global", "comp" or "all", + optional filter (pattern) +#define D_PRINT_VARIABLE 14 // 1+, list of variable names +#define D_OVERWRITE_VARIABLE 15 // 2, variable name, new value (in module parameter syntax) +#define D_PRINT_FUNCTION_CALLS 16 // 0-1, optional "all" or number of calls // stepping #define D_STEP_OVER 17 // 0 #define D_STEP_INTO 18 // 0 #define D_STEP_OUT 19 // 0 -#define D_RUN_TO_CURSOR 20 // 2, module name and line number +#define D_RUN_TO_CURSOR 20 // 2, module name, and line number or function name // the halted state #define D_HALT 21 // 0 #define D_CONTINUE 22 // 0 #define D_EXIT 23 // 1, "test" or "all" // initialization -#define D_SETUP 24 // 9+: +#define D_SETUP 24 // 11+: // 1 argument for D_SWITCH, // 2 arguments for D_SET_OUTPUT, // 2 arguments (2nd and 3rd) for D_SET_AUTOMATIC_BREAKPOINT, where the first argument is "error", // 2 arguments (2nd and 3rd) for D_SET_AUTOMATIC_BREAKPOINT, where the first argument is "fail", // 2 arguments for D_SET_GLOBAL_BATCH_FILE, + // 2 arguments for D_FUNCTION_CALL_CONFIG, // + arguments for any number of D_SET_BREAKPOINT commands (optional) #define D_ERROR 0 // any @@ -62,6 +63,7 @@ #define D_SET_AUTOMATIC_BREAKPOINT_TEXT "dautobp" #define D_SET_OUTPUT_TEXT "doutput" #define D_SET_GLOBAL_BATCH_FILE_TEXT "dglobbatch" +#define D_FUNCTION_CALL_CONFIG_TEXT "dcallcfg" #define D_PRINT_SETTINGS_TEXT "dsettings" #define D_LIST_COMPONENTS_TEXT "dlistcomp" #define D_SET_COMPONENT_TEXT "dsetcomp" @@ -70,8 +72,7 @@ #define D_LIST_VARIABLES_TEXT "dlistvar" #define D_PRINT_VARIABLE_TEXT "dprintvar" #define D_OVERWRITE_VARIABLE_TEXT "dsetvar" -#define D_PRINT_SNAPSHOTS_TEXT "dprintss" -#define D_SET_SNAPSHOT_BEHAVIOR_TEXT "dsscfg" +#define D_PRINT_FUNCTION_CALLS_TEXT "dprintcalls" #define D_STEP_OVER_TEXT "dstepover" #define D_STEP_INTO_TEXT "dstepinto" #define D_STEP_OUT_TEXT "dstepout" diff --git a/core/Debugger.cc b/core/Debugger.cc index fb5da15..d9b3a96 100644 --- a/core/Debugger.cc +++ b/core/Debugger.cc @@ -24,6 +24,8 @@ ////////////////// TTCN3_Debugger //////////////////// ////////////////////////////////////////////////////// +#define BUFFER_INCREASE 100 + TTCN3_Debugger ttcn3_debugger; void TTCN3_Debugger::switch_state(const char* p_state_str) @@ -51,19 +53,43 @@ void TTCN3_Debugger::switch_state(const char* p_state_str) } } -void TTCN3_Debugger::set_breakpoint(const char* p_module, int p_line, +static bool is_numeric(const char* p_str) +{ + size_t len = strlen(p_str); + for (size_t i = 0; i < len; ++i) { + if (p_str[i] < '0' || p_str[i] > '9') { + return false; + } + } + return true; +} + +void TTCN3_Debugger::set_breakpoint(const char* p_module, const char* p_location, const char* batch_file) { - size_t pos = find_breakpoint(p_module, p_line); + int line = 0; + char* function = NULL; + if (is_numeric(p_location)) { + // it's a line breakpoint + line = strtol(p_location, NULL, 10); + } + else { + // it's a function breakpoint + function = mcopystr(p_location); + } + char* loc_str = function != NULL ? mprintf("function '%s'", function) : + mprintf("line %d", line); + size_t pos = find_breakpoint(p_module, line, function); if (pos == breakpoints.size()) { breakpoint_t bp; bp.module = mcopystr(p_module); - bp.line = p_line; + bp.line = line; + bp.function = function; bp.batch_file = batch_file != NULL ? mcopystr(batch_file) : NULL; breakpoints.push_back(bp); - print(DRET_SETTING_CHANGE, "Breakpoint added in module '%s' at line %d%s%s%s.", - p_module, p_line, - batch_file != NULL ? " with batch file '" : " with no batch file", + print(DRET_SETTING_CHANGE, "Breakpoint added in module '%s' at %s %s%s%s.", + p_module, loc_str, + batch_file != NULL ? "with batch file '" : "with no batch file", batch_file != NULL ? batch_file : "", batch_file != NULL ? "'" : ""); } else { @@ -71,40 +97,41 @@ void TTCN3_Debugger::set_breakpoint(const char* p_module, int p_line, if (batch_file != NULL) { if (!strcmp(batch_file, breakpoints[pos].batch_file)) { print(DRET_NOTIFICATION, "Breakpoint already set in module '%s' at " - "line %d with batch file '%s'.", - p_module, p_line, batch_file); + "%s with batch file '%s'.", + p_module, loc_str, batch_file); } else { print(DRET_SETTING_CHANGE, "Batch file was changed from '%s' to '%s' for " - "breakpoint in module '%s' at line %d.", breakpoints[pos].batch_file, - batch_file, p_module, p_line); + "breakpoint in module '%s' at %s.", breakpoints[pos].batch_file, + batch_file, p_module, loc_str); } } else { print(DRET_SETTING_CHANGE, "Batch file '%s' removed from breakpoint in " - "module '%s' at line %d.", breakpoints[pos].batch_file, p_module, p_line); + "module '%s' at %s.", breakpoints[pos].batch_file, p_module, loc_str); } Free(breakpoints[pos].batch_file); } else { if (batch_file != NULL) { print(DRET_SETTING_CHANGE, "Batch file '%s' added to breakpoint in module " - "'%s' at line %d.", batch_file, p_module, p_line); + "'%s' at %s.", batch_file, p_module, loc_str); } else { - print(DRET_NOTIFICATION, "Breakpoint already set in module '%s' at line " - "%d with no batch file.", p_module, p_line); + print(DRET_NOTIFICATION, "Breakpoint already set in module '%s' at %s " + "with no batch file.", p_module, loc_str); } } breakpoints[pos].batch_file = batch_file != NULL ? mcopystr(batch_file) : NULL; } + Free(loc_str); } -void TTCN3_Debugger::remove_breakpoint(const char* p_module, const char* p_line) +void TTCN3_Debugger::remove_breakpoint(const char* p_module, const char* p_location) { bool all_breakpoints = !strcmp(p_module, "all"); - if (p_line != NULL) { - if (!strcmp(p_line, "all")) { + if (p_location != NULL) { + if (!strcmp(p_location, "all")) { bool found = false; for (size_t i = breakpoints.size(); i > 0; --i) { if (!strcmp(breakpoints[i - 1].module, p_module)) { @@ -128,27 +155,32 @@ void TTCN3_Debugger::remove_breakpoint(const char* p_module, const char* p_line) "argument is 'all'."); return; } - size_t len = strlen(p_line); - for (size_t i = 0; i < len; ++i) { - if (p_line[i] < '0' || p_line[i] > '9') { - print(DRET_NOTIFICATION, "Argument 2 is invalid. Expected 'all' or " - "integer value (line number)."); - return; - } + int line = 0; + char* function = NULL; + if (is_numeric(p_location)) { + // it's a line breakpoint + line = strtol(p_location, NULL, 10); } - long line = strtol(p_line, NULL, 10); - size_t pos = find_breakpoint(p_module, line); + else { + // it's a function breakpoint + function = mcopystr(p_location); + } + char* loc_str = function != NULL ? mprintf("function '%s'", function) : + mprintf("line %d", line); + size_t pos = find_breakpoint(p_module, line, function); if (pos != breakpoints.size()) { Free(breakpoints[pos].module); + Free(breakpoints[pos].function); Free(breakpoints[pos].batch_file); breakpoints.erase_at(pos); - print(DRET_SETTING_CHANGE, "Breakpoint removed in module '%s' from line %d.", - p_module, line); + print(DRET_SETTING_CHANGE, "Breakpoint removed in module '%s' from %s.", + p_module, loc_str); } else { - print(DRET_NOTIFICATION, "No breakpoint found in module '%s' at line %d.", - p_module, line); + print(DRET_NOTIFICATION, "No breakpoint found in module '%s' at %s.", + p_module, loc_str); } + Free(loc_str); return; } } @@ -164,6 +196,7 @@ void TTCN3_Debugger::remove_breakpoint(const char* p_module, const char* p_line) else { for (size_t i = 0; i < breakpoints.size(); ++i) { Free(breakpoints[i].module); + Free(breakpoints[i].function); Free(breakpoints[i].batch_file); } breakpoints.clear(); @@ -266,6 +299,22 @@ void TTCN3_Debugger::print_settings() // global batch file add_to_result("Global batch file%s%s.\n", global_batch_file != NULL ? ": " : "", global_batch_file != NULL ? global_batch_file : " not set"); + // function call data configuration + add_to_result("Function call data "); + if (function_calls.cfg == CALLS_TO_FILE) { + char* final_file_name2 = finalize_file_name(function_calls.file.name); + add_to_result("sent to file '%s'.\n", final_file_name2); + Free(final_file_name2); + } + else { + add_to_result("buffer size: "); + if (function_calls.cfg == CALLS_STORE_ALL) { + add_to_result("infinite.\n"); + } + else { + add_to_result("%d.\n", function_calls.buffer.size); + } + } // user breakpoints if (breakpoints.empty()) { add_to_result("No user breakpoints.\n"); @@ -274,8 +323,17 @@ void TTCN3_Debugger::print_settings() add_to_result("User breakpoints:\n"); for (size_t i = 0; i < breakpoints.size(); ++i) { const breakpoint_t& bp = breakpoints[i]; - add_to_result("%s %d %s\n", bp.module, bp.line, - bp.batch_file != NULL ? bp.batch_file : ""); + add_to_result("%s ", bp.module); + if (bp.function == NULL) { + add_to_result("%d", bp.line); + } + else { + add_to_result("%s", bp.function); + } + if (bp.batch_file != NULL) { + add_to_result(" %s", bp.batch_file); + } + add_to_result("\n"); } } // automatic breakpoints @@ -378,6 +436,172 @@ void TTCN3_Debugger::overwrite_variable(const char* p_var_name, } } +void TTCN3_Debugger::clean_up_function_calls() +{ + if (function_calls.cfg == CALLS_TO_FILE) { + if (!TTCN_Runtime::is_hc()) { + fclose(function_calls.file.ptr); + } + Free(function_calls.file.name); + } + else if (!TTCN_Runtime::is_hc()) { + if (function_calls.buffer.size != 0) { + if (function_calls.buffer.end != -1) { + for (int i = function_calls.buffer.start; + i != function_calls.buffer.end; + i = (i + 1) % function_calls.buffer.size) { + Free(function_calls.buffer.ptr[i]); + } + Free(function_calls.buffer.ptr[function_calls.buffer.end]); + } + Free(function_calls.buffer.ptr); + } + } +} + +void TTCN3_Debugger::configure_function_calls(const char* p_config, + const char* p_file_name) +{ + function_call_data_config_t cfg; + // check the command's parameters before actually changing anything + if (strcmp(p_config, "file") == 0) { + cfg = CALLS_TO_FILE; + } + else if (strcmp(p_config, "all") == 0) { + cfg = CALLS_STORE_ALL; + } + else if (is_numeric(p_config)) { + cfg = CALLS_RING_BUFFER; + } + else { + print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'file', 'all' or " + "ring buffer size."); + return; + } + FILE* new_fp = NULL; + char* final_file_name = NULL; + bool same_setting = false; + int new_size = 0; + switch (cfg) { + case CALLS_TO_FILE: + if (p_file_name != NULL) { + if (function_calls.cfg == CALLS_TO_FILE && + strcmp(p_file_name, function_calls.file.name) == 0) { + // don't reopen it if it's the same file as before + same_setting = true; + } + else if (!TTCN_Runtime::is_hc()) { + // don't open any files on HCs, just store settings for future PTCs + final_file_name = finalize_file_name(p_file_name); + new_fp = fopen(final_file_name, TTCN_Runtime::is_mtc() ? "w" : "a"); + if (new_fp == NULL) { + print(DRET_NOTIFICATION, "Failed to open file '%s' for writing.", final_file_name); + Free(final_file_name); + return; + } + } + } + else { + print(DRET_NOTIFICATION, "Argument 2 (file name) is missing."); + return; + } + break; + case CALLS_RING_BUFFER: + new_size = strtol(p_config, NULL, 10); + same_setting = function_calls.cfg == CALLS_RING_BUFFER && + function_calls.buffer.size == new_size; + break; + case CALLS_STORE_ALL: + same_setting = function_calls.cfg == CALLS_STORE_ALL; + break; + } + if (!same_setting) { + clean_up_function_calls(); + function_calls.cfg = cfg; + switch (cfg) { + case CALLS_TO_FILE: + function_calls.file.name = mcopystr(p_file_name); + if (!TTCN_Runtime::is_hc()) { + function_calls.file.ptr = new_fp; + } + break; + case CALLS_RING_BUFFER: + function_calls.buffer.size = new_size; + // no break + case CALLS_STORE_ALL: + function_calls.buffer.start = 0; + function_calls.buffer.end = -1; + if (new_size != 0 && !TTCN_Runtime::is_hc()) { + // don't allocate buffers on HCs, just store settings for future PTCs + function_calls.buffer.ptr = (char**)Malloc(new_size * sizeof(char*)); + } + else { + function_calls.buffer.ptr = NULL; + } + break; + } + } + switch (cfg) { + case CALLS_TO_FILE: + print(DRET_SETTING_CHANGE, "Debugger %sset to not store function call data, but to " + "send them to file '%s'.", same_setting ? "was already " : "", final_file_name); + Free(final_file_name); + break; + case CALLS_RING_BUFFER: + if (new_size == 0) { + print(DRET_SETTING_CHANGE, "Debugger %sset to not store function call data.", + same_setting ? "was already " : ""); + } + else { + print(DRET_SETTING_CHANGE, "Debugger %sset to store only the last %d function calls.", + same_setting ? "was already " : "", new_size); + } + break; + case CALLS_STORE_ALL: + print(DRET_SETTING_CHANGE, "Debugger %sset to store all function call data.", + same_setting ? "was already " : ""); + break; + } +} + +void TTCN3_Debugger::print_function_calls(const char* p_amount) +{ + if (function_calls.cfg == CALLS_TO_FILE || function_calls.buffer.size == 0 || + function_calls.buffer.end == -1) { + print(DRET_NOTIFICATION, "No function calls are stored."); + return; + } + int amount; + int limit = (function_calls.cfg == CALLS_RING_BUFFER && + function_calls.buffer.start == (function_calls.buffer.end + 1) % + function_calls.buffer.size) ? + function_calls.buffer.size : function_calls.buffer.end + 1; + if (p_amount == NULL || strcmp(p_amount, "all") == 0) { + amount = limit; + } + else if (is_numeric(p_amount)) { + amount = strtol(p_amount, NULL, 10); + if (amount == 0 || amount > limit) { + print(DRET_NOTIFICATION, "Invalid number of function calls. Expected 1 - %d.", + limit); + return; + } + } + else { + print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'all' or integer " + "value (number of calls)."); + return; + } + for (int i = (function_calls.buffer.end - amount + function_calls.buffer.size + 1) % + function_calls.buffer.size; amount > 0; + i = (i + 1) % function_calls.buffer.size, --amount) { + add_to_result(function_calls.buffer.ptr[i]); + if (amount > 1) { + add_to_result("\n"); + } + } +} + void TTCN3_Debugger::set_output(const char* p_output_type, const char* p_file_name) { FILE* new_fp = NULL; @@ -508,7 +732,7 @@ void TTCN3_Debugger::step(stepping_t p_stepping_type) resume(); } -void TTCN3_Debugger::run_to_cursor(const char* p_module, int p_line) +void TTCN3_Debugger::run_to_cursor(const char* p_module, const char* p_location) { // all processes receive this command, since the specified location might be // reached in any process, even a PTC that hasn't been created yet @@ -518,7 +742,14 @@ void TTCN3_Debugger::run_to_cursor(const char* p_module, int p_line) return; } temporary_breakpoint.module = mcopystr(p_module); - temporary_breakpoint.line = p_line; + if (is_numeric(p_location)) { + temporary_breakpoint.line = strtol(p_location, NULL, 10); + temporary_breakpoint.function = NULL; + } + else { + temporary_breakpoint.line = 0; + temporary_breakpoint.function = mcopystr(p_location); + } resume(); } @@ -529,6 +760,8 @@ void TTCN3_Debugger::halt(const char* p_batch_file, bool p_run_global_batch) Free(temporary_breakpoint.module); temporary_breakpoint.module = NULL; temporary_breakpoint.line = 0; + Free(temporary_breakpoint.function); + temporary_breakpoint.function = NULL; if (!TTCN_Runtime::is_hc()) { stepping_type = NOT_STEPPING; stack_level = call_stack.size() - 1; @@ -599,10 +832,14 @@ void TTCN3_Debugger::exit_(const char* p_what) } } -size_t TTCN3_Debugger::find_breakpoint(const char* p_module, int p_line) const +size_t TTCN3_Debugger::find_breakpoint(const char* p_module, int p_line, + const char* p_function) const { for (size_t i = 0; i < breakpoints.size(); ++i) { - if (!strcmp(breakpoints[i].module, p_module) && breakpoints[i].line == p_line) { + if (!strcmp(breakpoints[i].module, p_module) && + ((p_line != 0 && breakpoints[i].line == p_line) || + (p_function != NULL && breakpoints[i].function != NULL && + strcmp(breakpoints[i].function, p_function) == 0))) { return i; } } @@ -686,8 +923,22 @@ char* TTCN3_Debugger::finalize_file_name(const char* p_file_name_skeleton) void TTCN3_Debugger::test_execution_started() { - Free(snapshots); - snapshots = NULL; + if (function_calls.cfg != CALLS_TO_FILE) { + if (function_calls.buffer.size != 0 && function_calls.buffer.end != -1) { + for (int i = function_calls.buffer.start; + i != function_calls.buffer.end; + i = (i + 1) % function_calls.buffer.size) { + Free(function_calls.buffer.ptr[i]); + } + Free(function_calls.buffer.ptr[function_calls.buffer.end]); + } + if (function_calls.cfg == CALLS_STORE_ALL) { + Free(function_calls.buffer.ptr); + function_calls.buffer.ptr = NULL; + } + function_calls.buffer.start = 0; + function_calls.buffer.end = -1; + } exiting = false; if (TTCN_Runtime::is_single()) { TTCN_Debugger_UI::init(); @@ -707,8 +958,11 @@ void TTCN3_Debugger::test_execution_finished() Free(temporary_breakpoint.module); temporary_breakpoint.module = NULL; temporary_breakpoint.line = 0; + Free(temporary_breakpoint.function); + temporary_breakpoint.function = NULL; last_breakpoint_entry.module = NULL; last_breakpoint_entry.line = 0; + last_breakpoint_entry.stack_size = 0; if (TTCN_Runtime::is_single()) { TTCN_Debugger_UI::clean_up(); } @@ -749,10 +1003,14 @@ TTCN3_Debugger::TTCN3_Debugger() output_file = NULL; output_file_name = NULL; send_to_console = true; - snapshots = NULL; + function_calls.cfg = CALLS_STORE_ALL; + function_calls.buffer.size = 0; + function_calls.buffer.start = 0; + function_calls.buffer.end = -1; + function_calls.buffer.ptr = NULL; last_breakpoint_entry.module = NULL; last_breakpoint_entry.line = 0; - last_breakpoint_entry.batch_file = NULL; // not used + last_breakpoint_entry.stack_size = 0; stack_level = -1; fail_behavior.trigger = false; fail_behavior.batch_file = NULL; @@ -765,6 +1023,7 @@ TTCN3_Debugger::TTCN3_Debugger() stepping_stack_size = 0; temporary_breakpoint.module = NULL; temporary_breakpoint.line = 0; + temporary_breakpoint.function = NULL; temporary_breakpoint.batch_file = NULL; // not used exiting = false; halt_at_start = false; @@ -779,6 +1038,7 @@ TTCN3_Debugger::~TTCN3_Debugger() } for (size_t i = 0; i < breakpoints.size(); ++i) { Free(breakpoints[i].module); + Free(breakpoints[i].function); Free(breakpoints[i].batch_file); } for (size_t i = 0; i < global_scopes.size(); ++i) { @@ -793,7 +1053,7 @@ TTCN3_Debugger::~TTCN3_Debugger() Free(fail_behavior.batch_file); Free(error_behavior.batch_file); Free(global_batch_file); - Free(snapshots); + clean_up_function_calls(); Free(last_variable_list); } @@ -858,15 +1118,27 @@ void TTCN3_Debugger::breakpoint_entry(int p_line) trigger_type = "Stepped to"; break; } + const char* function_name = + call_stack[call_stack.size() - 1].function->get_function_name(); // third: check if this is the destination of a 'run to cursor' operation - if (p_line == temporary_breakpoint.line && - !strcmp(module_name, temporary_breakpoint.module)) { + if (temporary_breakpoint.module != NULL && + strcmp(module_name, temporary_breakpoint.module) == 0 && + (p_line == temporary_breakpoint.line || + (temporary_breakpoint.function != NULL && + last_breakpoint_entry.stack_size == call_stack.size() - 1 && + strcmp(temporary_breakpoint.function, function_name) == 0))) { trigger = true; trigger_type = "Ran to"; break; } // fourth: check if the location matches one of the breakpoints in the list - size_t pos = find_breakpoint(module_name, p_line); + size_t pos = find_breakpoint(module_name, p_line, NULL); + if (pos == breakpoints.size() && + last_breakpoint_entry.stack_size == call_stack.size() - 1) { + // this is the first breakpoint entry in the function, + // check for a matching function breakpoint + pos = find_breakpoint(module_name, 0, function_name); + } if (pos != breakpoints.size()) { trigger = true; batch_file = breakpoints[pos].batch_file; @@ -882,8 +1154,9 @@ void TTCN3_Debugger::breakpoint_entry(int p_line) } halt(batch_file, true); } - last_breakpoint_entry.module = (char*)module_name; + last_breakpoint_entry.module = module_name; last_breakpoint_entry.line = p_line; + last_breakpoint_entry.stack_size = call_stack.size(); } } @@ -1245,12 +1518,50 @@ const TTCN3_Debug_Scope* TTCN3_Debugger::get_component_scope(const char* p_compo return NULL; } -void TTCN3_Debugger::add_snapshot(const char* p_snapshot) +void TTCN3_Debugger::store_function_call(char* p_snapshot) { - if (snapshots != NULL) { - snapshots = mputc(snapshots, '\n'); + if (function_calls.cfg == CALLS_RING_BUFFER && function_calls.buffer.size == 0) { + Free(p_snapshot); + return; + } + // append timestamp to the beginning of the snapshot + timeval tv; + gettimeofday(&tv, NULL); + struct tm *lt = localtime(&tv.tv_sec); + if (lt != NULL) { + char* temp = mprintf("%02d:%02d:%02d.%06ld\t%s", lt->tm_hour, lt->tm_min, + lt->tm_sec, tv.tv_usec, p_snapshot); + Free(p_snapshot); + p_snapshot = temp; + } + switch (function_calls.cfg) { + case CALLS_TO_FILE: + fseek(function_calls.file.ptr, 0, SEEK_END); // in case multiple processes are writing the same file + fputs(p_snapshot, function_calls.file.ptr); + Free(p_snapshot); + fputc('\n', function_calls.file.ptr); + fflush(function_calls.file.ptr); + break; + case CALLS_RING_BUFFER: { + bool first = function_calls.buffer.end == -1; + function_calls.buffer.end = (function_calls.buffer.end + 1) % + function_calls.buffer.size; + function_calls.buffer.ptr[function_calls.buffer.end] = p_snapshot; + if (!first && function_calls.buffer.start == function_calls.buffer.end) { + function_calls.buffer.start = (function_calls.buffer.start + 1) % + function_calls.buffer.size; + } + break; } + case CALLS_STORE_ALL: + if (function_calls.buffer.end == function_calls.buffer.size - 1) { + function_calls.buffer.size += BUFFER_INCREASE; + function_calls.buffer.ptr = (char**)Realloc(function_calls.buffer.ptr, + function_calls.buffer.size * sizeof(char*)); + } + ++function_calls.buffer.end; + function_calls.buffer.ptr[function_calls.buffer.end] = p_snapshot; + break; } - snapshots = mputstr(snapshots, p_snapshot); } #define CHECK_NOF_ARGUMENTS(exp_num) \ @@ -1276,12 +1587,9 @@ void TTCN3_Debugger::add_snapshot(const char* p_snapshot) #define CHECK_INT_ARGUMENT(arg_idx) \ { \ - size_t len = strlen(p_arguments[arg_idx]); \ - for (size_t i = 0; i < len; ++i) { \ - if (p_arguments[arg_idx][i] < '0' || p_arguments[arg_idx][i] > '9') { \ - print(DRET_NOTIFICATION, "Argument %d is not an integer.", (int)(arg_idx + 1)); \ - return; \ - } \ + if (!is_numeric(p_arguments[arg_idx])) { \ + print(DRET_NOTIFICATION, "Argument %d is not an integer.", (int)(arg_idx + 1)); \ + return; \ } \ } @@ -1320,8 +1628,7 @@ void TTCN3_Debugger::execute_command(int p_command, int p_argument_count, break; case D_SET_BREAKPOINT: CHECK_NOF_ARGUMENTS_RANGE(2, 3) - CHECK_INT_ARGUMENT(1) - set_breakpoint(p_arguments[0], str2int(p_arguments[1]), + set_breakpoint(p_arguments[0], p_arguments[1], (p_argument_count == 3) ? p_arguments[2] : NULL); break; case D_REMOVE_BREAKPOINT: @@ -1341,6 +1648,10 @@ void TTCN3_Debugger::execute_command(int p_command, int p_argument_count, CHECK_NOF_ARGUMENTS_RANGE(1, 2) set_global_batch_file(p_arguments[0], (p_argument_count == 2) ? p_arguments[1] : NULL); break; + case D_FUNCTION_CALL_CONFIG: + CHECK_NOF_ARGUMENTS_RANGE(1, 2) + configure_function_calls(p_arguments[0], (p_argument_count == 2) ? p_arguments[1] : NULL); + break; case D_PRINT_SETTINGS: CHECK_NOF_ARGUMENTS(0) print_settings(); @@ -1358,8 +1669,9 @@ void TTCN3_Debugger::execute_command(int p_command, int p_argument_count, break; case D_LIST_VARIABLES: CHECK_CALL_STACK(true) - CHECK_NOF_ARGUMENTS_RANGE(1, 2) - call_stack[STACK_LEVEL].function->list_variables(p_arguments[0], + CHECK_NOF_ARGUMENTS_RANGE(0, 2) + call_stack[STACK_LEVEL].function->list_variables( + (p_argument_count > 0) ? p_arguments[0] : NULL, (p_argument_count == 2) ? p_arguments[1] : NULL); break; case D_PRINT_VARIABLE: @@ -1404,9 +1716,9 @@ void TTCN3_Debugger::execute_command(int p_command, int p_argument_count, CHECK_NOF_ARGUMENTS_MIN(2) overwrite_variable(p_arguments[0], p_argument_count - 1, p_arguments + 1); break; - case D_PRINT_SNAPSHOTS: - CHECK_NOF_ARGUMENTS(0) - add_to_result("%s", snapshots); + case D_PRINT_FUNCTION_CALLS: + CHECK_NOF_ARGUMENTS_RANGE(0, 1) + print_function_calls((p_argument_count > 0) ? p_arguments[0] : NULL); break; case D_STEP_OVER: CHECK_CALL_STACK(true) @@ -1428,8 +1740,7 @@ void TTCN3_Debugger::execute_command(int p_command, int p_argument_count, CHECK_CALL_STACK(TTCN_Runtime::is_mtc()) } CHECK_NOF_ARGUMENTS(2) - CHECK_INT_ARGUMENT(1) - run_to_cursor(p_arguments[0], str2int(p_arguments[1])); + run_to_cursor(p_arguments[0], p_arguments[1]); break; case D_HALT: if (!TTCN_Runtime::is_hc() && !TTCN_Runtime::is_single()) { @@ -1450,7 +1761,7 @@ void TTCN3_Debugger::execute_command(int p_command, int p_argument_count, exit_(p_arguments[0]); break; case D_SETUP: - CHECK_NOF_ARGUMENTS_MIN(5) + CHECK_NOF_ARGUMENTS_MIN(11) if (strlen(p_arguments[0]) > 0) { switch_state(p_arguments[0]); } @@ -1469,8 +1780,12 @@ void TTCN3_Debugger::execute_command(int p_command, int p_argument_count, set_global_batch_file(p_arguments[7], strlen(p_arguments[8]) > 0 ? p_arguments[8] : NULL); } - for (int i = 9; i < p_argument_count; i += 3) { - set_breakpoint(p_arguments[i], str2int(p_arguments[i + 1]), + if (strlen(p_arguments[9]) > 0) { + configure_function_calls(p_arguments[9], + strlen(p_arguments[10]) > 0 ? p_arguments[10] : NULL); + } + for (int i = 11; i < p_argument_count; i += 3) { + set_breakpoint(p_arguments[i], p_arguments[i + 1], strlen(p_arguments[i + 2]) > 0 ? p_arguments[i + 2] : NULL); } break; @@ -1491,16 +1806,27 @@ void TTCN3_Debugger::execute_command(int p_command, int p_argument_count, } } -void TTCN3_Debugger::open_output_file() +void TTCN3_Debugger::init_PTC_settings() { if (output_file == NULL && output_file_name != NULL) { char* final_file_name = finalize_file_name(output_file_name); - output_file = fopen(final_file_name, TTCN_Runtime::is_mtc() ? "w" : "a"); + output_file = fopen(final_file_name, "a"); if (output_file == NULL) { print(DRET_NOTIFICATION, "Failed to open file '%s' for writing.", final_file_name); } Free(final_file_name); } + if (function_calls.cfg == CALLS_TO_FILE) { + char* final_file_name = finalize_file_name(function_calls.file.name); + function_calls.file.ptr = fopen(final_file_name, "a"); + if (function_calls.file.ptr == NULL) { + print(DRET_NOTIFICATION, "Failed to open file '%s' for writing.", final_file_name); + } + Free(final_file_name); + } + else if (function_calls.cfg == CALLS_RING_BUFFER && function_calls.buffer.size != 0) { + function_calls.buffer.ptr = (char**)Malloc(function_calls.buffer.size * sizeof(char*)); + } } ////////////////////////////////////////////////////// @@ -1631,8 +1957,7 @@ TTCN3_Debug_Function::~TTCN3_Debug_Function() if (return_value.is_bound()) { snapshot = mputprintf(snapshot, " returned %s", (const char*)return_value); } - ttcn3_debugger.add_snapshot(snapshot); - Free(snapshot); + ttcn3_debugger.store_function_call(snapshot); } for (size_t i = 0; i < variables.size(); ++i) { delete variables[i]; @@ -1709,8 +2034,7 @@ void TTCN3_Debug_Function::initial_snapshot() const } } snapshot = mputstr(snapshot, ")"); - ttcn3_debugger.add_snapshot(snapshot); - Free(snapshot); + ttcn3_debugger.store_function_call(snapshot); } } @@ -1776,18 +2100,18 @@ void TTCN3_Debug_Function::list_variables(const char* p_scope, const char* p_fil bool list_local = false; bool list_global = false; bool list_comp = false; - if (!strcmp(p_scope, "local")) { + if (p_scope == NULL || !strcmp(p_scope, "all")) { list_local = true; - } - else if (!strcmp(p_scope, "global")) { list_global = true; - } - else if (!strcmp(p_scope, "comp")) { list_comp = true; } - else if (!strcmp(p_scope, "all")) { + else if (!strcmp(p_scope, "local")) { list_local = true; + } + else if (!strcmp(p_scope, "global")) { list_global = true; + } + else if (!strcmp(p_scope, "comp")) { list_comp = true; } else { diff --git a/core/Debugger.hh b/core/Debugger.hh index 6b253b6..8abfa0c 100644 --- a/core/Debugger.hh +++ b/core/Debugger.hh @@ -96,12 +96,24 @@ public: struct breakpoint_t { /** module name, owned */ char* module; - /** line number */ + /** line number (if it's a line breakpoint, otherwise 0) */ int line; - /** batch file to be executed when the breakpoint is reached (optional) */ + /** function name (if it's a function breakpoint, otherwise NULL), owned */ + char* function; + /** batch file to be executed when the breakpoint is reached (optional), owned */ char* batch_file; }; + /** type for storing data related to a breakpoint entry */ + struct breakpoint_entry_t { + /** module name, not owned */ + const char* module; + /** line number */ + int line; + /** size of the call stack */ + size_t stack_size; + }; + /** special breakpoint types, passed to breakpoint_entry() as the line parameter, * so these need to be 0 or negative, to never conflict with any line number */ enum special_breakpoint_t { @@ -115,10 +127,58 @@ public: struct automatic_breakpoint_behavior_t { /** indicates whether the breakpoint should be triggered by the associated event */ bool trigger; - /** batch file to be executed if the breakpoint is triggered (optional) */ + /** batch file to be executed if the breakpoint is triggered (optional), owned */ char* batch_file; }; + /** possible function call data handling configurations */ + enum function_call_data_config_t { + /** function call data is printed directly to file and not stored by the + * debugger */ + CALLS_TO_FILE, + /** function call data is stored in a ring buffer of a set size + * (i.e. when the buffer is full, adding a new function call automatically + * deletes the oldest function call) + * the buffer size can be zero, in which case no calls are stored */ + CALLS_RING_BUFFER, + /** function call data is stored in a buffer of variable length (i.e. when + * the buffer is full, its size is increased) */ + CALLS_STORE_ALL + }; + + /** structure containing the function call data and information related to + * their handling */ + struct function_call_data_t { + /** current function call data configuration (this also indicates which + * field of the following union is selected) */ + function_call_data_config_t cfg; + union { + /** information related to the file, that function calls are printed to + * (in case of CALLS_TO_FILE) */ + struct { + /** name of the target file (may contain metacharacters), owned */ + char* name; + /** the target file's handler, owned */ + FILE* ptr; + } file; + /** information related to the storing of function calls + * (in case of CALLS_RING_BUFFER or CALLS_STORE_ALL) */ + struct { + /** size of the buffer used for storing function calls (this value is + * fixed in case of CALLS_RING_BUFFER and dynamic in case of CALLS_STORE_ALL) */ + int size; + /** stores the index of the first function call in the buffer + * (is always 0 in case of CALLS_STORE_ALL) */ + int start; + /** stores the index of the last function call in the buffer + * (its value is -1 if no function calls are currently stored) */ + int end; + /** buffer containing the function call data, owned */ + char** ptr; + } buffer; + }; + }; + /** stepping type */ enum stepping_t { NOT_STEPPING, @@ -141,11 +201,11 @@ private: bool halted; /** the debugger's output file handler (NULL if the debugger's output is only - * sent to the console) */ + * sent to the console); owned */ FILE* output_file; /** name of the debugger's output file (NULL if the debugger's output is only - * sent to the console) */ + * sent to the console); may contain metacharacters; owned */ char* output_file_name; /** indicates whether the debugger's output should be sent to the console */ @@ -168,11 +228,11 @@ private: /** list of breakpoints */ Vector breakpoints; - /** string containing function call snapshots, owned */ - char* snapshots; + /** structure containing function call data */ + function_call_data_t function_calls; /** stores the last line hit by breakpoint_entry() */ - breakpoint_t last_breakpoint_entry; + breakpoint_entry_t last_breakpoint_entry; /** current stack level (reset when test execution is halted or resumed) */ int stack_level; @@ -184,14 +244,14 @@ private: automatic_breakpoint_behavior_t error_behavior; /** batch file executed automatically when test execution is halted - * NULL if switched off + * NULL if switched off (owned) * is overridden by breakpoint-specific batch files */ char* global_batch_file; - /** result of the currently executing command */ + /** result of the currently executing command, owned */ char* command_result; - /** result of the last D_LIST_VARIABLES command */ + /** result of the last D_LIST_VARIABLES command, owned */ char* last_variable_list; /** stores which stepping option was requested by the user (if any) */ @@ -223,17 +283,18 @@ private: * handles the D_SWITCH command */ void switch_state(const char* p_state_str); - /** adds a new breakpoint at the specified module and line with the specified - * batch file (if not NULL), or changes the batch file of an existing + /** adds a new breakpoint at the specified location (line or function) with the + * specified batch file (if not NULL), or changes the batch file of an existing * breakpoint * handles the D_SET_BREAKPOINT command */ - void set_breakpoint(const char* p_module, int p_line, const char* batch_file); + void set_breakpoint(const char* p_module, const char* p_location, + const char* batch_file); - /** removes the breakpoint from the specified module/line, if it exists + /** removes the breakpoint from the specified location, if it exists * can also be used to remove all breakpoints from the specified module or * all breakpoints in all modules * handles the D_REMOVE_BREAKPOINT command */ - void remove_breakpoint(const char* p_module, const char* p_line); + void remove_breakpoint(const char* p_module, const char* p_location); /** switches an automatic breakpoint related to the specified event on or off * and/or sets the batch file to run when the breakpoint is triggered @@ -276,6 +337,18 @@ private: void overwrite_variable(const char* p_var_name, int p_value_element_count, char** p_value_elements); + /** frees all resources related to the handling of function call data */ + void clean_up_function_calls(); + + /** changes the method of handling function call data + * handles the D_FUNCTION_CALL_CONFIG command */ + void configure_function_calls(const char* p_config, const char* p_file_name); + + /** prints the last stored function calls + * handles the D_PRINT_FUNCTION_CALLS command + * @param p_amount amount of function calls to print or "all" */ + void print_function_calls(const char* p_amount); + /** sets the debugger's output to the console and/or a text file * handles the D_SET_OUTPUT command * @param p_output_type "console", "file" or "both" @@ -292,10 +365,10 @@ private: * handles the D_STEP_OVER, D_STEP_INTO and D_STEP_OUT commands */ void step(stepping_t p_stepping_type); - /** resumes execution until the specified location is reached + /** resumes execution until the specified location (line or function) is reached * (or until test execution is halted for any other reason) * handles the D_RUN_TO_CURSOR command */ - void run_to_cursor(const char* p_module, int p_line); + void run_to_cursor(const char* p_module, const char* p_location); /** halts test execution, processing only debug commands * @param p_batch_file batch file executed after the halt (if not NULL) @@ -314,7 +387,8 @@ private: /** returns the index of the specified breakpoint, if found, * otherwise returns breakpoints.size() */ - size_t find_breakpoint(const char* p_module, int p_line) const; + size_t find_breakpoint(const char* p_module, int p_line, + const char* p_function) const; /** returns the specified variable, if found, otherwise returns NULL */ TTCN3_Debugger::variable_t* find_variable(const void* p_value) const; @@ -501,15 +575,18 @@ public: /** returns the component scope object associated with the specified component type */ const TTCN3_Debug_Scope* get_component_scope(const char* p_component) const; - /** appends the specified function call snapshot to the end of the snapshot string */ - void add_snapshot(const char* p_snapshot); + /** stores the specified snapshot of a function call, together with a time stamp + * (the debugger is responsible for freeing the string parameter) */ + void store_function_call(char* p_snapshot); /** executes a command received from the user interface */ void execute_command(int p_command, int p_argument_count, char** p_arguments); - /** opens the debugger's output file for writing (if one has been set, but not - * opened, in the HC process) */ - void open_output_file(); + /** called when a PTC is forked from the HC process + * contains supplementary initializations (i.e. opening of file pointers and + * allocations of buffers) that the HC's debugger does not perform, but are + * needed by the PTC's debugger */ + void init_PTC_settings(); /** indicates whether an 'exit all' command has been issued * (this causes the execution of tests in the current queue to stop) */ @@ -706,6 +783,9 @@ public: /** prints the function's type, name and current values of parameters */ void print_function() const; + /** returns the name of the function */ + const char* get_function_name() const { return function_name; } + /** returns the name of the module the function was defined in */ const char* get_module_name() const { return module_name; } diff --git a/core/DebuggerUI.cc b/core/DebuggerUI.cc index aa583b2..42675f5 100644 --- a/core/DebuggerUI.cc +++ b/core/DebuggerUI.cc @@ -31,12 +31,13 @@ const TTCN_Debugger_UI::command_t TTCN_Debugger_UI::debug_command_list[] = { { D_SWITCH_TEXT, D_SWITCH, D_SWITCH_TEXT " on|off", "Switch the debugger on or off." }, { D_SET_BREAKPOINT_TEXT, D_SET_BREAKPOINT, - D_SET_BREAKPOINT_TEXT " []", + D_SET_BREAKPOINT_TEXT " | []", "Add a breakpoint at the specified location, or change the batch file of " "an existing breakpoint." }, { D_REMOVE_BREAKPOINT_TEXT, D_REMOVE_BREAKPOINT, - D_REMOVE_BREAKPOINT_TEXT " all| [all|]", "Remove a breakpoint, " - "or all breakpoints from a module, or all breakpoints from all modules." }, + D_REMOVE_BREAKPOINT_TEXT " all| [all||]", + "Remove a breakpoint, or all breakpoints from a module, or all breakpoints " + "from all modules." }, { D_SET_AUTOMATIC_BREAKPOINT_TEXT, D_SET_AUTOMATIC_BREAKPOINT, D_SET_AUTOMATIC_BREAKPOINT_TEXT " error|fail on|off []", "Switch an automatic breakpoint (truggered by an event) on or off, and/or " @@ -48,6 +49,9 @@ const TTCN_Debugger_UI::command_t TTCN_Debugger_UI::debug_command_list[] = { D_SET_GLOBAL_BATCH_FILE_TEXT " on|off [batch_file_name]", "Set whether a batch file should be executed automatically when test execution " "is halted (breakpoint-specific batch files override this setting)." }, + { D_FUNCTION_CALL_CONFIG_TEXT, D_FUNCTION_CALL_CONFIG, + D_FUNCTION_CALL_CONFIG_TEXT " file||all []", + "Configure the storing of function call data." }, { D_PRINT_SETTINGS_TEXT, D_PRINT_SETTINGS, D_PRINT_SETTINGS_TEXT, "Prints the debugger's settings." }, { D_PRINT_CALL_STACK_TEXT, D_PRINT_CALL_STACK, D_PRINT_CALL_STACK_TEXT, @@ -55,7 +59,7 @@ const TTCN_Debugger_UI::command_t TTCN_Debugger_UI::debug_command_list[] = { { D_SET_STACK_LEVEL_TEXT, D_SET_STACK_LEVEL, D_SET_STACK_LEVEL_TEXT " ", "Set the stack level to print debug information from." }, { D_LIST_VARIABLES_TEXT, D_LIST_VARIABLES, - D_LIST_VARIABLES_TEXT " local|global|comp|all [pattern]", + D_LIST_VARIABLES_TEXT " [local|global|comp|all] [pattern]", "List variable names." }, { D_PRINT_VARIABLE_TEXT, D_PRINT_VARIABLE, D_PRINT_VARIABLE_TEXT " |$ [{ |$}]", @@ -64,9 +68,9 @@ const TTCN_Debugger_UI::command_t TTCN_Debugger_UI::debug_command_list[] = { { D_OVERWRITE_VARIABLE_TEXT, D_OVERWRITE_VARIABLE, D_OVERWRITE_VARIABLE_TEXT " ", "Overwrite the current value of a variable." }, - { D_PRINT_SNAPSHOTS_TEXT, D_PRINT_SNAPSHOTS, D_PRINT_SNAPSHOTS_TEXT, - "Print snapshots of function calls until this point." }, - // D_SET_SNAPSHOT_BEHAVIOR_TEXT + { D_PRINT_FUNCTION_CALLS_TEXT, D_PRINT_FUNCTION_CALLS, + D_PRINT_FUNCTION_CALLS_TEXT " [all|]", + "Print function call data." }, { D_STEP_OVER_TEXT, D_STEP_OVER, D_STEP_OVER_TEXT, "Resume test execution until the next line of code (in this function or the " "caller function)." }, @@ -74,7 +78,8 @@ const TTCN_Debugger_UI::command_t TTCN_Debugger_UI::debug_command_list[] = { "Resume test execution until the next line of code (on any stack level)." }, { D_STEP_OUT_TEXT, D_STEP_OUT, D_STEP_OUT_TEXT, "Resume test execution until the next line of code in the caller function." }, - { D_RUN_TO_CURSOR_TEXT, D_RUN_TO_CURSOR, D_RUN_TO_CURSOR_TEXT " ", + { D_RUN_TO_CURSOR_TEXT, D_RUN_TO_CURSOR, + D_RUN_TO_CURSOR_TEXT " |", "Resume test execution until the specified location." }, { D_HALT_TEXT, D_HALT, D_HALT_TEXT, "Halt test execution." }, { D_CONTINUE_TEXT, D_CONTINUE, D_CONTINUE_TEXT, "Resume halted test execution." }, diff --git a/core/Runtime.cc b/core/Runtime.cc index 164bc85..19b053b 100644 --- a/core/Runtime.cc +++ b/core/Runtime.cc @@ -535,7 +535,7 @@ int TTCN_Runtime::ptc_main() } if (ret_val == EXIT_SUCCESS) { if (ttcn3_debugger.is_activated()) { - ttcn3_debugger.open_output_file(); + ttcn3_debugger.init_PTC_settings(); } try { do { diff --git a/mctr2/cli/Cli.cc b/mctr2/cli/Cli.cc index d76a83c..b4818ac 100644 --- a/mctr2/cli/Cli.cc +++ b/mctr2/cli/Cli.cc @@ -118,12 +118,13 @@ static const DebugCommand debug_command_list[] = { { D_SWITCH_TEXT, D_SWITCH, D_SWITCH_TEXT " on|off", "Switch the debugger on or off." }, { D_SET_BREAKPOINT_TEXT, D_SET_BREAKPOINT, - D_SET_BREAKPOINT_TEXT " []", + D_SET_BREAKPOINT_TEXT " | []", "Add a breakpoint at the specified location, or change the batch file of " "an existing breakpoint." }, { D_REMOVE_BREAKPOINT_TEXT, D_REMOVE_BREAKPOINT, - D_REMOVE_BREAKPOINT_TEXT " all| [all|]", "Remove a breakpoint, " - "or all breakpoints from a module, or all breakpoints from all modules." }, + D_REMOVE_BREAKPOINT_TEXT " all| [all||]", + "Remove a breakpoint, or all breakpoints from a module, or all breakpoints " + "from all modules." }, { D_SET_AUTOMATIC_BREAKPOINT_TEXT, D_SET_AUTOMATIC_BREAKPOINT, D_SET_AUTOMATIC_BREAKPOINT_TEXT " error|fail on|off []", "Switch an automatic breakpoint (truggered by an event) on or off, and/or " @@ -135,6 +136,9 @@ static const DebugCommand debug_command_list[] = { D_SET_GLOBAL_BATCH_FILE_TEXT " on|off [batch_file_name]", "Set whether a batch file should be executed automatically when test execution " "is halted (breakpoint-specific batch files override this setting)." }, + { D_FUNCTION_CALL_CONFIG_TEXT, D_FUNCTION_CALL_CONFIG, + D_FUNCTION_CALL_CONFIG_TEXT " file||all []", + "Configure the storing of function call data." }, { D_PRINT_SETTINGS_TEXT, D_PRINT_SETTINGS, D_PRINT_SETTINGS_TEXT, "Prints the debugger's settings." }, { D_LIST_COMPONENTS_TEXT, D_LIST_COMPONENTS, D_LIST_COMPONENTS_TEXT, @@ -147,7 +151,7 @@ static const DebugCommand debug_command_list[] = { { D_SET_STACK_LEVEL_TEXT, D_SET_STACK_LEVEL, D_SET_STACK_LEVEL_TEXT " ", "Set the stack level to print debug information from." }, { D_LIST_VARIABLES_TEXT, D_LIST_VARIABLES, - D_LIST_VARIABLES_TEXT " local|global|comp|all [pattern]", + D_LIST_VARIABLES_TEXT " [local|global|comp|all] [pattern]", "List variable names." }, { D_PRINT_VARIABLE_TEXT, D_PRINT_VARIABLE, D_PRINT_VARIABLE_TEXT " |$ [{ |$}]", @@ -156,9 +160,9 @@ static const DebugCommand debug_command_list[] = { { D_OVERWRITE_VARIABLE_TEXT, D_OVERWRITE_VARIABLE, D_OVERWRITE_VARIABLE_TEXT " ", "Overwrite the current value of a variable." }, - { D_PRINT_SNAPSHOTS_TEXT, D_PRINT_SNAPSHOTS, D_PRINT_SNAPSHOTS_TEXT, - "Print snapshots of function calls until this point." }, - // D_SET_SNAPSHOT_BEHAVIOR_TEXT + { D_PRINT_FUNCTION_CALLS_TEXT, D_PRINT_FUNCTION_CALLS, + D_PRINT_FUNCTION_CALLS_TEXT " [all|]", + "Print function call data." }, { D_STEP_OVER_TEXT, D_STEP_OVER, D_STEP_OVER_TEXT, "Resume test execution until the next line of code (in this function or the " "caller function)." }, @@ -166,7 +170,8 @@ static const DebugCommand debug_command_list[] = { "Resume test execution until the next line of code (on any stack level)." }, { D_STEP_OUT_TEXT, D_STEP_OUT, D_STEP_OUT_TEXT, "Resume test execution until the next line of code in the caller function." }, - { D_RUN_TO_CURSOR_TEXT, D_RUN_TO_CURSOR, D_RUN_TO_CURSOR_TEXT " ", + { D_RUN_TO_CURSOR_TEXT, D_RUN_TO_CURSOR, + D_RUN_TO_CURSOR_TEXT " |", "Resume test execution until the specified location." }, { D_HALT_TEXT, D_HALT, D_HALT_TEXT, "Halt test execution." }, { D_CONTINUE_TEXT, D_CONTINUE, D_CONTINUE_TEXT, "Resume halted test execution." }, diff --git a/mctr2/mctr/MainController.cc b/mctr2/mctr/MainController.cc index c869353..0af6531 100644 --- a/mctr2/mctr/MainController.cc +++ b/mctr2/mctr/MainController.cc @@ -3117,6 +3117,10 @@ void MainController::clean_up() debugger_settings.global_batch_state = NULL; Free(debugger_settings.global_batch_file); debugger_settings.global_batch_file = NULL; + Free(debugger_settings.function_calls_cfg); + debugger_settings.function_calls_cfg = NULL; + Free(debugger_settings.function_calls_file); + debugger_settings.function_calls_file = NULL; for (int i = 0; i < debugger_settings.nof_breakpoints; ++i) { Free(debugger_settings.breakpoints[i].module); Free(debugger_settings.breakpoints[i].line); @@ -3430,7 +3434,7 @@ void MainController::send_debug_setup(host_struct *hc) Text_Buf text_buf; text_buf.push_int(MSG_DEBUG_COMMAND); text_buf.push_int(D_SETUP); - text_buf.push_int(9 + 3 * debugger_settings.nof_breakpoints); + text_buf.push_int(11 + 3 * debugger_settings.nof_breakpoints); text_buf.push_string(debugger_settings.on_switch); text_buf.push_string(debugger_settings.output_file); text_buf.push_string(debugger_settings.output_type); @@ -3440,6 +3444,8 @@ void MainController::send_debug_setup(host_struct *hc) text_buf.push_string(debugger_settings.fail_batch_file); text_buf.push_string(debugger_settings.global_batch_state); text_buf.push_string(debugger_settings.global_batch_file); + text_buf.push_string(debugger_settings.function_calls_cfg); + text_buf.push_string(debugger_settings.function_calls_file); for (int i = 0; i < debugger_settings.nof_breakpoints; ++i) { text_buf.push_string(debugger_settings.breakpoints[i].module); text_buf.push_string(debugger_settings.breakpoints[i].line); @@ -5608,6 +5614,21 @@ void MainController::process_debug_return_value(Text_Buf& text_buf, char* log_so Free(line); } break; + case D_FUNCTION_CALL_CONFIG: { + Free(debugger_settings.function_calls_cfg); + Free(debugger_settings.function_calls_file); + debugger_settings.function_calls_file = NULL; + size_t args_len = mstrlen(last_debug_command.arguments); + size_t start = 0; + size_t end = 0; + get_next_argument_loc(last_debug_command.arguments, args_len, start, end); + debugger_settings.function_calls_cfg = mcopystrn(last_debug_command.arguments + start, end - start); + if (end < args_len) { + start = end; + get_next_argument_loc(last_debug_command.arguments, args_len, start, end); + debugger_settings.function_calls_file = mcopystrn(last_debug_command.arguments + start, end - start); + } + break; } default: break; } @@ -5883,6 +5904,8 @@ void MainController::initialize(UserInterface& par_ui, int par_max_ptcs) debugger_settings.fail_batch_file = NULL; debugger_settings.global_batch_state = NULL; debugger_settings.global_batch_file = NULL; + debugger_settings.function_calls_cfg = NULL; + debugger_settings.function_calls_file = NULL; debugger_settings.nof_breakpoints = 0; debugger_settings.breakpoints = NULL; last_debug_command.command = D_ERROR; @@ -6576,7 +6599,7 @@ void MainController::debug_command(int commandID, char* arguments) case D_LIST_VARIABLES: case D_PRINT_VARIABLE: case D_OVERWRITE_VARIABLE: - case D_PRINT_SNAPSHOTS: + case D_PRINT_FUNCTION_CALLS: case D_STEP_OVER: case D_STEP_INTO: case D_STEP_OUT: @@ -6594,6 +6617,7 @@ void MainController::debug_command(int commandID, char* arguments) case D_SET_GLOBAL_BATCH_FILE: case D_SET_BREAKPOINT: case D_REMOVE_BREAKPOINT: + case D_FUNCTION_CALL_CONFIG: // it's a global setting, store it, the next MSG_DEBUG_RETURN_VALUE message // might need it last_debug_command.command = commandID; diff --git a/mctr2/mctr/MainController.h b/mctr2/mctr/MainController.h index 1f0f0a0..561c665 100644 --- a/mctr2/mctr/MainController.h +++ b/mctr2/mctr/MainController.h @@ -253,6 +253,8 @@ struct debugger_settings_struct { char* fail_batch_file; char* global_batch_state; char* global_batch_file; + char* function_calls_cfg; + char* function_calls_file; int nof_breakpoints; struct breakpoint_struct { char* module;