debugger: added function breakpoints and added a setting for handling function call...
authorBotond Baranyi <botond.baranyi@ericsson.com>
Fri, 24 Jun 2016 11:46:26 +0000 (13:46 +0200)
committerBotond Baranyi <botond.baranyi@ericsson.com>
Fri, 24 Jun 2016 15:57:12 +0000 (17:57 +0200)
Change-Id: I78833e8462686c4385cd540a41a1abd0d1b5162b
Signed-off-by: Botond Baranyi <botond.baranyi@ericsson.com>
core/DebugCommands.hh
core/Debugger.cc
core/Debugger.hh
core/DebuggerUI.cc
core/Runtime.cc
mctr2/cli/Cli.cc
mctr2/mctr/MainController.cc
mctr2/mctr/MainController.h

index 96bc6ec915093a024a2adbf4dc12d3c3cf55e2e5..96c7218d2dcc9dd1fe408f46e333e3e0c9fad8d7 100644 (file)
 
 // 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"
index fb5da1538237355c560e9f158444c9b8b9a8b962..d9b3a96f520a1e1e7394ac19f57c111654295b14 100644 (file)
@@ -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 {
index 6b253b65fea67b09f1e2dbcc1164ec097df89411..8abfa0c32dd204aaefd3954da074c701c01c86ef 100644 (file)
@@ -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<breakpoint_t> 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; }
   
index aa583b25fff3b8901854a5c24a09075fbf53345e..42675f5e17803c4b0cde0bd5d76c4440c1b417bf 100644 (file)
@@ -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 " <module> <line> [<batch_file>]",
+    D_SET_BREAKPOINT_TEXT " <module> <line>|<function> [<batch_file>]",
     "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|<module> [all|<line>]", "Remove a breakpoint, "
-    "or all breakpoints from a module, or all breakpoints from all modules." },
+    D_REMOVE_BREAKPOINT_TEXT " all|<module> [all|<line>|<function>]",
+    "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 [<batch_file>]",
     "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|<limit>|all [<file_name>]",
+    "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 " <level>",
     "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 " <variable_name>|$ [{ <variable_name>|$}]",
@@ -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 " <variable_name> <value>",
     "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|<amount>]",
+    "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 " <module> <line>",
+  { D_RUN_TO_CURSOR_TEXT, D_RUN_TO_CURSOR,
+    D_RUN_TO_CURSOR_TEXT " <module> <line>|<function>",
     "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." },
index 164bc858a02f08c2f70c8468e7684e0c0d91968a..19b053b7e7c4890f266607414ed16e48ab5ab417 100644 (file)
@@ -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 {
index d76a83c076e33cc7568255f0a8dd35190e034b62..b4818ac18d9af54fdf0f553b2ca27dd030a9eeb4 100644 (file)
@@ -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 " <module> <line> [<batch_file>]",
+    D_SET_BREAKPOINT_TEXT " <module> <line>|<function> [<batch_file>]",
     "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|<module> [all|<line>]", "Remove a breakpoint, "
-    "or all breakpoints from a module, or all breakpoints from all modules." },
+    D_REMOVE_BREAKPOINT_TEXT " all|<module> [all|<line>|<function>]",
+    "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 [<batch_file>]",
     "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|<limit>|all [<file_name>]",
+    "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 " <level>",
     "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 " <variable_name>|$ [{ <variable_name>|$}]",
@@ -156,9 +160,9 @@ static const DebugCommand debug_command_list[] = {
   { D_OVERWRITE_VARIABLE_TEXT, D_OVERWRITE_VARIABLE,
     D_OVERWRITE_VARIABLE_TEXT " <variable_name> <value>",
     "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|<amount>]",
+    "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 " <module> <line>",
+  { D_RUN_TO_CURSOR_TEXT, D_RUN_TO_CURSOR,
+    D_RUN_TO_CURSOR_TEXT " <module> <line>|<function>",
     "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." },
index c869353d5c6041961f93173f692fac4561ff7d4a..0af6531642aa4e99855486271f15aededa7eff1f 100644 (file)
@@ -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;
index 1f0f0a00bb7c45f6399734068a41763bb676ffb6..561c665a3314a94e42ef2f2c4cae8613091aae52 100644 (file)
@@ -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;
This page took 0.042997 seconds and 5 git commands to generate.