Debugger - Stage 1 (artf511247)
authorBotond Baranyi <botond.baranyi@ericsson.com>
Tue, 5 Apr 2016 11:14:32 +0000 (13:14 +0200)
committerBotond Baranyi <botond.baranyi@ericsson.com>
Tue, 5 Apr 2016 11:50:54 +0000 (13:50 +0200)
Signed-off-by: Botond Baranyi <botond.baranyi@ericsson.com>
20 files changed:
compiler2/AST.cc
compiler2/AST.hh
compiler2/DebuggerStuff.cc [new file with mode: 0644]
compiler2/DebuggerStuff.hh [new file with mode: 0644]
compiler2/Makefile
compiler2/Setting.cc
compiler2/Type.hh
compiler2/asn1/AST_asn1.cc
compiler2/asn1/AST_asn1.hh
compiler2/main.cc
compiler2/main.hh
compiler2/ttcn3/AST_ttcn3.cc
compiler2/ttcn3/AST_ttcn3.hh
compiler2/ttcn3/Statement.cc
core/Basetype.hh
core/Debugger.cc [new file with mode: 0644]
core/Debugger.hh [new file with mode: 0644]
core/Makefile
core/Runtime.cc
core/TTCN3.hh

index da8696b515b231f9316405abc92fe19291a929f2..4c74e61dd1d7f987bdd819e16f393d3eb57b25e8 100644 (file)
@@ -815,8 +815,9 @@ namespace Common {
     // pre_init function
     bool has_pre_init = false;
     bool profiled = MOD_TTCN == get_moduletype() && is_file_profiled(get_filename());
+    bool debugged = debugger_active && MOD_TTCN == get_moduletype();
     // always generate pre_init_module if the file is profiled
-    if (output->functions.pre_init || profiled) {
+    if (output->functions.pre_init || profiled || debugged) {
       output->source.static_function_prototypes =
        mputstr(output->source.static_function_prototypes,
          "static void pre_init_module();\n");
@@ -848,6 +849,10 @@ namespace Common {
             "TTCN3_Stack_Depth stack_depth;\n"
             "ttcn3_prof.execute_line(\"%s\", 0);\n", get_modid().get_name().c_str(), get_filename());
         }
+        if (debugged) {
+          output->source.static_function_bodies = mputprintf(output->source.static_function_bodies,
+            "%s::init_ttcn3_debugger();\n", get_modid().get_name().c_str());
+        }
       }
       output->source.static_function_bodies =
         mputstr(output->source.static_function_bodies, output->functions.pre_init);
@@ -1564,7 +1569,7 @@ namespace Common {
         "{\n");
       char* function_name = 0;
       int line_no = -1;
-      while(get_profiler_code_line(get_filename(), &function_name, &line_no)) {
+      while (get_profiler_code_line(get_filename(), &function_name, &line_no)) {
         output->source.global_vars = mputprintf(output->source.global_vars,
           "  ttcn3_prof.create_line(ttcn3_prof.get_element(\"%s\"), %d);\n",
           get_filename(), line_no);
@@ -1574,7 +1579,15 @@ namespace Common {
             get_filename(), line_no, function_name);
         }
       }
-      output->source.global_vars = mputstr(output->source.global_vars, "}\n\n");
+      output->source.global_vars = mputstr(output->source.global_vars, "}\n");
+    }
+    /* TTCN-3 debugger:
+       generate the printing function for the types defined in this module
+       and initialize the debugger with this module's global variables,
+       component types and the components' variables */
+    if (debugger_active) {
+      generate_debugger_functions(output);
+      generate_debugger_init(output);
     }
   }
 
index 894d20571586b1955dbc08520ed8b3e218f61645..f92c90a625345134f996cae6efc8ea5d90994ff4 100644 (file)
@@ -268,6 +268,24 @@ namespace Common {
      * module object to output->source.global_vars */
     void generate_functions(output_struct *output);
     void generate_conversion_functions(output_struct *output);
+    
+    /** Generates the debugger initialization function for this module.
+      * The function creates the global debug scope associated with this module,
+      * and initializes it with all the global variables visible in the module
+      * (including imported variables).
+      * The debug scopes of all component types defined in the module are also
+      * created and initialized with their variables. */
+    virtual void generate_debugger_init(output_struct *output) = 0;
+    
+    /** Generates the variable adding code for all global variables defined
+      * in this module. This function is called by generate_debugger_init()
+      * for both the current module and all imported modules. */
+    virtual char* generate_debugger_global_vars(char* str, Common::Module* current_mod) = 0;
+    
+    /** Generates the debugger variable printing function, which can print values
+      * and templates of all types defined in this module (excluding subtypes). */
+    virtual void generate_debugger_functions(output_struct *output) = 0;
+    
   private:
     /** Copy constructor not implemented */
     Module(const Module& p);
diff --git a/compiler2/DebuggerStuff.cc b/compiler2/DebuggerStuff.cc
new file mode 100644 (file)
index 0000000..c34bd6e
--- /dev/null
@@ -0,0 +1,405 @@
+/******************************************************************************
+ * Copyright (c) 2000-2016 Ericsson Telecom AB
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *   
+ *   Baranyi, Botond – initial implementation
+ *
+ ******************************************************************************/
+
+#include "DebuggerStuff.hh"
+#include "AST.hh"
+#include "Type.hh"
+#include "ttcn3/AST_ttcn3.hh"
+#include "ttcn3/ArrayDimensions.hh"
+
+namespace Common {
+/** Returns a string, that contains the template parameters of the debugger's
+  * port array or timer array printing function.
+  * 
+  * Recursive (handles one array dimension per call).
+  * 
+  * @param p_dims the array's dimensions
+  * @param p_element_type name of the array's element C++ class
+  * @param p_array_type name of the array's C++ class ("PORT_ARRAY" or "TIMER_ARRAY")
+  * @param index the index of the currently handled dimension in \a p_dims
+  */
+string function_params_for_array_dims(Ttcn::ArrayDimensions* p_dims,
+                                      string p_element_type,
+                                      string p_array_type,
+                                      size_t index = 0)
+{
+  if (index == p_dims->get_nof_dims()) {
+    return p_element_type;
+  }
+  string ret_val;
+  if (index != 0) {
+    ret_val = p_array_type + string("<");
+  }
+  ret_val += function_params_for_array_dims(p_dims, p_element_type, p_array_type, index + 1);
+  Ttcn::ArrayDimension* dim = p_dims->get_dim_byIndex(index);
+  ret_val += string(", ") + Int2string(dim->get_size()) +
+    string(", ") + Int2string(dim->get_offset());
+  if (index != 0) {
+    ret_val += string(">");
+  }
+  return ret_val;
+}
+
+/** Returns a string, that contains the template parameters of the debugger's
+  * value array or template array printing function.
+  * 
+  * Recursive (handles one array dimension per call).
+  * 
+  * @param p_type the current type (either an array or the array element)
+  * @param p_scope the current scope
+  * @param p_templ indicates whether it's a template array or value array
+  * @param p_first indicates whether this is the first call or a recursive call
+  */
+string function_params_for_array_type(Type* p_type,
+                                      Scope* p_scope,
+                                      bool p_templ,
+                                      bool p_first = true)
+{
+  string ret_val;
+  if (p_type->get_typetype() != Type::T_ARRAY) {
+    ret_val = p_type->get_genname_value(p_scope);
+    if (p_templ) {
+      ret_val += "_template";
+    }
+  }
+  else {
+    if (!p_first) {
+      if (p_templ) {
+        ret_val = "TEMPLATE_ARRAY<";
+      }
+      else {
+        ret_val = "VALUE_ARRAY<";
+      }
+    }
+    Type* elem_type = p_type->get_ofType()->get_type_refd_last();
+    if (p_templ) {
+      ret_val += function_params_for_array_type(elem_type, p_scope, false, false) +
+        ", " + function_params_for_array_type(elem_type, p_scope, true, false);
+    }
+    else {
+      ret_val += function_params_for_array_type(elem_type, p_scope, false, false);
+    }
+    Ttcn::ArrayDimension* dim = p_type->get_dimension();
+    ret_val += string(", ") + Int2string(dim->get_size()) +
+      string(", ") + Int2string(dim->get_offset());
+    if (!p_first) {
+      ret_val += string(">");
+    }
+  }
+  return ret_val;
+}
+
+/** Appends the string representations of the specified array dimensions. */
+string array_dimensions_to_string(Ttcn::ArrayDimensions* p_dims)
+{
+  string ret_val;
+  for (size_t i = 0; i < p_dims->get_nof_dims(); ++i) {
+    ret_val += p_dims->get_dim_byIndex(i)->get_stringRepr();
+  }
+  return ret_val;
+}
+
+void calculate_type_name_and_print_function_from_type(Type* p_type,
+                                                      Type* p_type_last,
+                                                      Module* p_module,
+                                                      string& p_type_name,
+                                                      string& p_print_function)
+{
+  if (p_type_last->get_typetype() == Type::T_COMPONENT) {
+    p_type_name = "component";
+  }
+  else if (p_type_last->is_structured_type() ||
+           p_type_last->get_typetype() == Type::T_ENUM_A ||
+           p_type_last->get_typetype() == Type::T_ENUM_T ||
+           p_type_last->get_typetype() == Type::T_PORT ||
+           p_type_last->get_typetype() == Type::T_SIGNATURE ||
+           p_type_last->get_typetype() == Type::T_FUNCTION ||
+           p_type_last->get_typetype() == Type::T_ALTSTEP ||
+           p_type_last->get_typetype() == Type::T_TESTCASE) {
+    // user-defined type
+    if (p_type_last->is_pard_type_instance()) {
+      // if the referenced type is an instance of an ASN.1 parameterized type,
+      // then use the last non-parameterized type in the reference chain to
+      // calculate the type name
+      Type* t = p_type;
+      while (t->is_ref() && !t->is_pard_type_instance()) {
+        p_type_name = t->get_dispname();
+        t = t->get_type_refd();
+      }
+    }
+    else {
+      p_type_name = p_type_last->get_dispname();
+    }
+    const Module* var_type_mod = p_type_last->get_my_scope()->get_scope_mod();
+    if (var_type_mod != p_module) {
+      p_print_function = var_type_mod->get_modid().get_name() + "::";
+    }
+    else {
+      p_print_function.clear();
+    }
+    p_print_function += "print_var_" + var_type_mod->get_modid().get_ttcnname();
+  }
+  else {
+    // built-in type, get the TTCN-3 version of the type if possible
+    switch (p_type_last->get_typetype()) {
+    case Type::T_GENERALSTRING:
+    case Type::T_GRAPHICSTRING:
+    case Type::T_TELETEXSTRING:
+    case Type::T_VIDEOTEXSTRING:
+      // these ASN.1 string types are not converted right by Type::get_typetype_ttcn3()
+      p_type_name = "universal charstring";
+      break;
+    case Type::T_UNRESTRICTEDSTRING:
+    case Type::T_EMBEDDED_PDV:
+    case Type::T_EXTERNAL:
+      // these are converted to T_SEQ_T by Type::get_typetype_ttcn3()
+      p_type_name = Type::get_typename_builtin(p_type_last->get_typetype());
+      break;
+    default:
+      p_type_name = Type::get_typename_builtin(p_type_last->get_typetype_ttcn3());
+      break;
+    }
+  }
+}
+
+char* generate_code_debugger_add_var(char* str, Common::Assignment* var_ass,
+                                     Module* current_mod /* = NULL */,
+                                     const char* scope_name /* = NULL */)
+{
+  if (current_mod == NULL) {
+    current_mod = var_ass->get_my_scope()->get_scope_mod();
+  }
+  
+  bool is_lazy_param = false;
+  switch (var_ass->get_asstype()) {
+  case Common::Assignment::A_PAR_VAL:
+  case Common::Assignment::A_PAR_VAL_IN:
+  case Common::Assignment::A_PAR_TEMPL_IN:
+    if (var_ass->get_lazy_eval()) {
+      // lazy parameters have their own printing function
+      is_lazy_param = true;
+    }
+    break;
+  default:
+    break;
+  }
+  
+  // recreate the TTCN-3 version of the type name and determine the type's printing function
+  string type_name, print_function;
+  print_function = is_lazy_param ? "TTCN3_Debugger::print_lazy_param<" :
+    "TTCN3_Debugger::print_base_var";
+  if (var_ass->get_asstype() == Common::Assignment::A_TIMER ||
+      var_ass->get_asstype() == Common::Assignment::A_PAR_TIMER) {
+    type_name = "timer";
+    if (var_ass->get_Dimensions() != NULL) {
+      // timer array
+      type_name += array_dimensions_to_string(var_ass->get_Dimensions());
+      print_function = string("TTCN3_Debugger::print_timer_array<") +
+        function_params_for_array_dims(var_ass->get_Dimensions(),
+                                       string("TIMER"), string("TIMER_ARRAY")) +
+        string(">");
+    }
+  }
+  else {
+    Common::Type* var_type = var_ass->get_Type();
+    // get the type at the end of the reference chain, but don't go through
+    // CHARACTER STRINGs, EMBEDDED PDVs and EXTERNALs
+    while (var_type->is_ref() && var_type->get_typetype() != Type::T_EXTERNAL &&
+           var_type->get_typetype() != Type::T_EMBEDDED_PDV &&
+           var_type->get_typetype() != Type::T_UNRESTRICTEDSTRING) {
+      var_type = var_type->get_type_refd();
+    }
+    if (is_lazy_param) {
+      print_function += var_type->get_genname_value(current_mod);
+    }
+    if (var_type->get_typetype() == Type::T_PORT && var_ass->get_Dimensions() != NULL) {
+      // port array
+      type_name = var_type->get_dispname() +
+        array_dimensions_to_string(var_ass->get_Dimensions());
+      if (!is_lazy_param) {
+        print_function = string("TTCN3_Debugger::print_port_array<") +
+          function_params_for_array_dims(var_ass->get_Dimensions(),
+                                    var_type->get_genname_value(current_mod),
+                                    string("PORT_ARRAY")) +
+          string(">");
+      }
+    }
+    else if (var_type->get_typetype() == Type::T_ARRAY) {
+      string dims_str;
+      Type* t = var_type;
+      while (t->get_typetype() == Type::T_ARRAY) {
+        dims_str += t->get_dimension()->get_stringRepr();
+        t = t->get_ofType()->get_type_refd_last();
+      }
+      string dummy;
+      calculate_type_name_and_print_function_from_type(t, t, current_mod, type_name, dummy);
+      type_name += dims_str;
+      if (!is_lazy_param) {
+        switch (var_ass->get_asstype()) {
+        case Common::Assignment::A_MODULEPAR_TEMP:
+        case Common::Assignment::A_TEMPLATE:
+        case Common::Assignment::A_VAR_TEMPLATE:
+        case Common::Assignment::A_PAR_TEMPL_IN:
+        case Common::Assignment::A_PAR_TEMPL_OUT:
+        case Common::Assignment::A_PAR_TEMPL_INOUT:
+          // template array
+          print_function = string("TTCN3_Debugger::print_template_array<") +
+            function_params_for_array_type(var_type, current_mod, true) +
+            string(">");
+          break;
+        default:
+          // value array
+          print_function = string("TTCN3_Debugger::print_value_array<") +
+            function_params_for_array_type(var_type, current_mod, false) +
+            string(">");
+          break;
+        }
+      }
+    }
+    else {
+      string dummy;
+      calculate_type_name_and_print_function_from_type(var_ass->get_Type(),
+        var_type, current_mod, type_name, is_lazy_param ? dummy : print_function);
+    }
+  }
+  
+  switch (var_ass->get_asstype()) {
+  case Common::Assignment::A_MODULEPAR_TEMP:
+  case Common::Assignment::A_TEMPLATE:
+  case Common::Assignment::A_VAR_TEMPLATE:
+  case Common::Assignment::A_PAR_TEMPL_IN:
+  case Common::Assignment::A_PAR_TEMPL_OUT:
+  case Common::Assignment::A_PAR_TEMPL_INOUT:
+    // add a suffix, if it's a template
+    type_name += " template";
+    if (is_lazy_param) {
+      print_function += "_template";
+    }
+    break;
+  default:
+    break;
+  }
+  
+  if (is_lazy_param) {
+    print_function += ">";
+  }
+  
+  return mputprintf(str, "%s%s_scope%sadd_variable(&%s, \"%s\", \"%s\", %s);\n",
+    scope_name != NULL ? "  " : "", // add indenting for global variables
+    scope_name != NULL ? scope_name : "debug", // the prefix of the debugger scope:
+    // ("global" for global variables, "debug" for local variables,
+    // or the component name for component variables)
+    scope_name != NULL ? "->" : ".", // global scopes are pointers, local scopes
+    // are local variables
+    var_ass->get_genname_from_scope(current_mod, "").c_str(), // variable name in C++
+    // (HACK: an empty string is passed as the prefix parameter to get_genname_from_scope,
+    // so the lazy parameter evaluation code is not generated)
+    var_ass->get_id().get_ttcnname().c_str(), // variable name in TTCN-3
+    type_name.c_str(), // variable type in TTCN-3, with a suffix if it's a template
+    print_function.c_str()); // variable printing function
+}
+
+char* generate_code_debugger_function_init(char* str, Common::Assignment* func_ass)
+{
+  string comp_str = func_ass->get_RunsOnType() == NULL ? string("NULL") :
+    string("\"") + func_ass->get_RunsOnType()->get_dispname() + string("\"");
+  const char* func_type_str = NULL;
+  switch (func_ass->get_asstype()) {
+  case Common::Assignment::A_FUNCTION:
+  case Common::Assignment::A_FUNCTION_RVAL:
+  case Common::Assignment::A_FUNCTION_RTEMP:
+    func_type_str = "function";
+    break;
+  case Common::Assignment::A_EXT_FUNCTION:
+  case Common::Assignment::A_EXT_FUNCTION_RVAL:
+  case Common::Assignment::A_EXT_FUNCTION_RTEMP:
+    func_type_str = "external function";
+    break;
+  case Common::Assignment::A_TESTCASE:
+    func_type_str = "testcase";
+    break;
+  case Common::Assignment::A_ALTSTEP:
+    func_type_str = "altstep";
+    break;
+  case Common::Assignment::A_TEMPLATE: // parameterized template
+    func_type_str = "template";
+    break;
+  default:
+    break;
+  }
+  Ttcn::FormalParList* fp_list = func_ass != NULL ? func_ass->get_FormalParList() : NULL;
+  if (fp_list != NULL && fp_list->get_nof_fps() != 0) {
+    // has parameters
+    char* fp_names_str = NULL;
+    char* fp_types_str = NULL;
+    char* fp_add_var_str = NULL;
+    for (size_t i = 0; i < fp_list->get_nof_fps(); ++i) {
+      // gather everything needed for this parameter in sub-strings
+      Ttcn::FormalPar* fp = fp_list->get_fp_byIndex(i);
+      const char* fp_type_str = NULL;
+      switch (fp->get_asstype()) {
+      case Common::Assignment::A_PAR_VAL:
+      case Common::Assignment::A_PAR_VAL_IN:
+      case Common::Assignment::A_PAR_TEMPL_IN:
+        fp_type_str = "in";
+        break;
+      case Common::Assignment::A_PAR_VAL_INOUT:
+      case Common::Assignment::A_PAR_TEMPL_INOUT:
+      case Common::Assignment::A_PAR_TIMER: // treat timers and ports as 'inout' parameters
+      case Common::Assignment::A_PAR_PORT:
+        fp_type_str = "inout";
+        break;
+      case Common::Assignment::A_PAR_VAL_OUT:
+      case Common::Assignment::A_PAR_TEMPL_OUT:
+        fp_type_str = "out";
+        break;
+      default:
+        break;
+      }
+      fp_names_str = mputprintf(fp_names_str,
+        "param_names[%d] = \"%s\";\n", (int)i, fp->get_id().get_ttcnname().c_str());
+      fp_types_str = mputprintf(fp_types_str,
+        "param_types[%d] = \"%s\";\n", (int)i, fp_type_str);
+      fp_add_var_str = generate_code_debugger_add_var(fp_add_var_str, fp);
+    }
+    str = mputprintf(str,
+      "charstring_list param_names;\n"
+      "%s"
+      "charstring_list param_types;\n"
+      "%s"
+      "TTCN3_Debug_Function debug_scope(\"%s\", \"%s\", \"%s\", param_names, param_types, %s);\n"
+      "%s"
+      "debug_scope.initial_snapshot();\n"
+      , fp_names_str, fp_types_str
+      , func_ass->get_id().get_dispname().c_str(), func_type_str
+      , func_ass->get_my_scope()->get_scope_mod()->get_modid().get_ttcnname().c_str()
+      , comp_str.c_str(), fp_add_var_str);
+    Free(fp_names_str);
+    Free(fp_types_str);
+    Free(fp_add_var_str);
+  }
+  else {
+    // no parameters
+    str = mputprintf(str,
+      "charstring_list no_params = NULL_VALUE;\n"
+      "TTCN3_Debug_Function debug_scope(\"%s\", \"%s\", \"%s\", no_params, no_params, %s);\n"
+      "debug_scope.initial_snapshot();\n"
+      , func_ass->get_id().get_dispname().c_str(), func_type_str
+      , func_ass->get_my_scope()->get_scope_mod()->get_modid().get_ttcnname().c_str()
+      , comp_str.c_str());
+  }
+  return str;
+}
+
+}
\ No newline at end of file
diff --git a/compiler2/DebuggerStuff.hh b/compiler2/DebuggerStuff.hh
new file mode 100644 (file)
index 0000000..55cc7c8
--- /dev/null
@@ -0,0 +1,44 @@
+/******************************************************************************
+ * Copyright (c) 2000-2016 Ericsson Telecom AB
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *   
+ *   Baranyi, Botond – initial implementation
+ *
+ ******************************************************************************/
+
+#ifndef DEBUGGERSTUFF_HH
+#define DEBUGGERSTUFF_HH
+
+#include <stddef.h>
+
+// forward declarations
+namespace Common {
+  class Assignment;
+  class Module;
+  
+/** Generates code, that adds a variable to a debugger scope object.
+  * @param str code generation buffer
+  * @param var_ass the variable's definition
+  * @param current_mod scope object's module (NULL means the module is the same,
+  * where the variable is defined)
+  * @param scope_name the prefix of the debugger scope object (NULL for local
+  * variables) */
+extern char* generate_code_debugger_add_var(char* str, Common::Assignment* var_ass,
+  Common::Module* current_mod = NULL, const char* scope_name = NULL);
+
+/** Generates code, that creates a debugger function object, adds its parameters
+  * to be tracked by the debugger, and takes the function's initial snapshot.
+  * @param str code generation buffer
+  * @param func_ass the function's definition */
+extern char* generate_code_debugger_function_init(char* str,
+   Common::Assignment* func_ass);
+
+}
+
+#endif /* DEBUGGERSTUFF_HH */
+
index 74c5e36138da4aea8a10a00606407b355817499e..a2789d57a5d100302f73a5b7bf6c3402518d95b7 100644 (file)
@@ -86,7 +86,8 @@ PredefFunc.cc AST.cc Code.cc Constraint.cc CompilerError.cc \
 CompField.cc CompType.cc EnumItem.cc Identifier.cc Int.cc \
 main.cc Real.cc Setting.cc SigParam.cc string.cc subtype.cc Stopwatch.cc \
 Type.cc Type_chk.cc Type_codegen.cc TypeCompat.cc \
-Typestuff.cc ustring.cc Value.cc Valuestuff.cc XerAttributes.cc subtypestuff.cc CodeGenHelper.cc
+Typestuff.cc ustring.cc Value.cc Valuestuff.cc XerAttributes.cc subtypestuff.cc \
+CodeGenHelper.cc DebuggerStuff.cc
 
 MFGEN_SOURCES := makefile.c xpather.cc ProjectGenHelper.cc
 
index 9f71f58eff3ceea2a80c2a152a0bb416989ac437..5f1add8b48e4982d21237cef6e28e195420bf5f7 100644 (file)
@@ -293,6 +293,9 @@ namespace Common {
               mputprintf(effective_module_lines, "%s%d",
                           (effective_module_lines ? ", " : ""), yyloc.first_line);
         }
+        if (debugger_active) {
+          str = mputprintf(str, "ttcn3_debugger.breakpoint_entry(%d);\n", yyloc.first_line);
+        }
       }
 
       if (include_line_info)
index de1e6022d076ceca5a0faec3548abc19b52132bc..96ea75acc58051a2be3860d5c9b53880ef9fe5ea 100644 (file)
@@ -24,6 +24,7 @@
  *   Zalanyi, Balazs Andor
  *
  ******************************************************************************/
+
 #ifndef _Common_Type_HH
 #define _Common_Type_HH
 
index 639e914e4ef685d15fc602a21b4e517fdad3170b..cd87a28f38d2866c34e67334e9ab120aee811a77 100644 (file)
@@ -28,6 +28,7 @@
 #include "../main.hh"
 #include "../CodeGenHelper.hh"
 #include "../../common/JSON_Tokenizer.hh"
+#include "../DebuggerStuff.hh"
 
 /* defined in asn1p.y */
 extern int asn1_parse_string(const char* p_str);
@@ -529,22 +530,23 @@ namespace Asn {
     // cycle through all type assignments, insert schema segments and references
     // when needed
     for (size_t i = 0; i < asss->get_nof_asss(); ++i) {
-      Common::Assignment* ass = asss->get_ass_byIndex(i);
-      if (Common::Assignment::A_TYPE == ass->get_asstype()) {
-        Asn::Assignment* asn_ass = dynamic_cast<Asn::Assignment*>(ass);
-        // skip parameterized types and their instances
-        if (NULL == asn_ass || NULL == asn_ass->get_ass_pard()) {
-          Type* t = ass->get_Type();
-          if (!t->is_pard_type_instance() && t->has_encoding(Type::CT_JSON)) {
-            // insert type's schema segment
-            t->generate_json_schema(json, false, false);
-            
-            if (json_refs_for_all_types && !json_refs.has_key(t)) {
-              // create JSON schema reference for the type
-              JSON_Tokenizer* json_ref = new JSON_Tokenizer;
-              json_refs.add(t, json_ref);
-              t->generate_json_schema_ref(*json_ref);
-            }
+      Asn::Assignment* asn_ass = dynamic_cast<Asn::Assignment*>(asss->get_ass_byIndex(i));
+      if (asn_ass == NULL || asn_ass->get_ass_pard() != NULL) {
+        // skip parameterized types
+        continue;
+      }
+      if (Common::Assignment::A_TYPE == asn_ass->get_asstype()) {
+        Type* t = asn_ass->get_Type();
+        // skip instances of parameterized types
+        if (!t->is_pard_type_instance() && t->has_encoding(Type::CT_JSON)) {
+          // insert type's schema segment
+          t->generate_json_schema(json, false, false);
+
+          if (json_refs_for_all_types && !json_refs.has_key(t)) {
+            // create JSON schema reference for the type
+            JSON_Tokenizer* json_ref = new JSON_Tokenizer;
+            json_refs.add(t, json_ref);
+            t->generate_json_schema_ref(*json_ref);
           }
         }
       }
@@ -553,6 +555,77 @@ namespace Asn {
     // end of type definitions
     json.put_next_token(JSON_TOKEN_OBJECT_END);
   }
+  
+  void Module::generate_debugger_init(output_struct *output)
+  {
+    // no debugging in ASN.1 modules
+  }
+  
+  char* Module::generate_debugger_global_vars(char* str, Common::Module* current_mod)
+  {
+    for (size_t i = 0; i < asss->get_nof_asss(); ++i) {
+      Asn::Assignment* asn_ass = dynamic_cast<Asn::Assignment*>(asss->get_ass_byIndex(i));
+      if (asn_ass->get_ass_pard() != NULL) {
+        // this check must be done before get_asstype() is called
+        continue;
+      }
+      if (asn_ass->get_asstype() == Common::Assignment::A_CONST) {
+        str = generate_code_debugger_add_var(str, asn_ass, current_mod, "global");
+      }
+    }
+    return str;
+  }
+  
+  void Module::generate_debugger_functions(output_struct *output)
+  {
+    char* str = NULL;
+    for (size_t i = 0; i < asss->get_nof_asss(); ++i) {
+      Asn::Assignment* asn_ass = dynamic_cast<Asn::Assignment*>(asss->get_ass_byIndex(i));
+      if (asn_ass->get_ass_pard() != NULL) {
+        // skip parameterized types
+        // this check must be done before get_asstype() is called
+        continue;
+      }
+      if (Common::Assignment::A_TYPE == asn_ass->get_asstype()) {
+        Type* t = asn_ass->get_Type();
+        if (!t->is_pard_type_instance() && (t->is_structured_type() ||
+            t->get_typetype() == Type::T_ENUM_A ||
+            (t->is_ref() && t->get_type_refd()->is_pard_type_instance()))) {
+          // only structured types and enums are needed
+          // for instances of parameterized types, the last reference, which is
+          // not itself an instance of a parameterized type, holds the type's display name
+          str = mputprintf(str, 
+            "  %sif (!strcmp(p_var.type_name, \"%s\")) {\n"
+            "    ((const %s*)p_var.value)->log();\n"
+            "  }\n"
+            "  else if (!strcmp(p_var.type_name, \"%s template\")) {\n"
+            "    ((const %s_template*)p_var.value)->log();\n"
+            "  }\n"
+            , (str != NULL) ? "else " : ""
+            , t->get_dispname().c_str(), t->get_genname_value(this).c_str()
+            , t->get_dispname().c_str(), t->get_genname_value(this).c_str());
+        }
+      }
+    }
+    if (str != NULL) {
+      // don't generate an empty printing function
+      output->header.class_defs = mputprintf(output->header.class_defs,
+        "/* Debugger printing function for types declared in this module */\n\n"
+        "extern CHARSTRING print_var_%s(const TTCN3_Debugger::variable_t& p_var);\n",
+        get_modid().get_ttcnname().c_str());
+      output->source.global_vars = mputprintf(output->source.global_vars,
+        "\n/* Debugger printing function for types declared in this module */\n"
+        "CHARSTRING print_var_%s(const TTCN3_Debugger::variable_t& p_var)\n"
+        "{\n"
+        "  TTCN_Logger::begin_event_log2str();\n"
+        "%s"
+        "  else {\n"
+        "    TTCN_Logger::log_event_str(\"<unrecognized value or template>\");\n"
+        "  }\n"
+        "  return TTCN_Logger::end_event_log2str();\n"
+        "}\n", get_modid().get_ttcnname().c_str(), str);
+    }
+  }
 
   // =================================
   // ===== Assignments
index 28a264c9bd424349ddf86cd73e52c6f3d130ddb9..5d115acac051b6198f5e87a5ef62bf53ec3780e3 100644 (file)
@@ -263,6 +263,19 @@ namespace Asn {
       * the types will be inserted here
       * @param json_refs map of JSON documents containing the references to each type */
     virtual void generate_json_schema(JSON_Tokenizer& json, map<Type*, JSON_Tokenizer>& json_refs);
+    
+    /** Does nothing. Debugger initialization functions are not generated for
+      * ASN.1 modules. */
+    virtual void generate_debugger_init(output_struct *output);
+    
+    /** Generates the variable adding code for all global variables defined
+      * in this module. This function is called by generate_debugger_init()
+      * for both the current module and all imported modules. */
+    virtual char* generate_debugger_global_vars(char* str, Common::Module* current_mod);
+    
+    /** Generates the debugger variable printing function, which can print values
+      * and templates of all types defined in this module (excluding subtypes). */
+    virtual void generate_debugger_functions(output_struct *output);
   };
 
   /**
index 41d6f4bad30a19562331cfbcb6dbdec5ca1463a0..46890454c902db3c42ff467f256e78ded52939e5 100644 (file)
@@ -94,7 +94,7 @@ boolean generate_skeleton = FALSE, force_overwrite = FALSE,
   check_subtype = TRUE, suppress_context = FALSE, display_up_to_date = FALSE,
   implicit_json_encoding = FALSE, json_refs_for_all_types = TRUE,
   force_gen_seof = FALSE, omit_in_value_list = FALSE,
-  warnings_for_bad_variants = FALSE;
+  warnings_for_bad_variants = FALSE, debugger_active = FALSE;
 
 // Default code splitting mode is set to 'no splitting'.
 CodeGenHelper::split_type code_splitting_mode = CodeGenHelper::SPLIT_NONE;
@@ -479,8 +479,8 @@ int main(int argc, char *argv[])
     dflag = false, Xflag = false, Rflag = false, gflag = false, aflag = false,
     s0flag = false, Cflag = false, yflag = false, Uflag = false, Qflag = false,
     Sflag = false, Kflag = false, jflag = false, zflag = false, Fflag = false,
-    Mflag = false, Eflag = false, errflag = false, print_usage = false,
-    ttcn2json = false;
+    Mflag = false, Eflag = false, nflag = false, errflag = false,
+    print_usage = false, ttcn2json = false;
 
   CodeGenHelper cgh;
 
@@ -572,7 +572,7 @@ int main(int argc, char *argv[])
 
   if (!ttcn2json) {
     for ( ; ; ) {
-      int c = getopt(argc, argv, "aA:bcC:dEfFgijK:lLMo:pP:qQ:rRsStT:uU:vV:wxXyYz:0-");
+      int c = getopt(argc, argv, "aA:bcC:dEfFgijK:lLMno:pP:qQ:rRsStT:uU:vV:wxXyYz:0-");
       if (c == -1) break;
       switch (c) {
       case 'a':
@@ -743,6 +743,10 @@ int main(int argc, char *argv[])
         SET_FLAG(E);
         warnings_for_bad_variants = TRUE;
         break;
+      case 'n':
+        SET_FLAG(n);
+        debugger_active = TRUE;
+        break;
 
       case 'Q': {
         long max_errs;
@@ -787,7 +791,8 @@ int main(int argc, char *argv[])
       if (Aflag || Lflag || Pflag || Tflag || Vflag || Yflag ||
         bflag || fflag || iflag || lflag || oflag || pflag || qflag ||
         rflag || sflag || tflag || uflag || wflag || xflag || Xflag || Rflag ||
-        Uflag || yflag || Kflag || jflag || zflag || Fflag || Mflag || Eflag) {
+        Uflag || yflag || Kflag || jflag || zflag || Fflag || Mflag || Eflag ||
+        nflag) {
         errflag = true;
         print_usage = true;
       }
@@ -807,6 +812,10 @@ int main(int argc, char *argv[])
         ERROR("Source line information `-L' is necessary for profiling `-z'.");
         errflag = true;
       }
+      if (nflag && !Lflag) {
+        ERROR("Source line information `-L' is necessary for debugging `-n'.");
+        errflag = true;
+      }
       if (iflag && gflag) {
         WARNING("Option `-g' overrides `-i'.");
         iflag = false; // -g gives more information
index 5565413e5f775dff90f72446431f889440ab48ca..2192b32dfc1d51df62e85c765567a017b90692e8 100644 (file)
@@ -49,7 +49,7 @@ extern boolean generate_skeleton, force_overwrite, include_line_info,
   output_only_linenum, default_as_optional, use_runtime_2, gcc_compat, asn1_xer,
   check_subtype, suppress_context, enable_set_bound_out_param, display_up_to_date,
   implicit_json_encoding, json_refs_for_all_types, force_gen_seof,
-  omit_in_value_list, warnings_for_bad_variants;
+  omit_in_value_list, warnings_for_bad_variants, debugger_active;
 
 extern const char *expected_platform;
 
index edb3e2d2989c2831f866457f8502d4fba97ab481..d1afdbcb167f1e7ffb0a9a4d94a8297f7b1d495f 100644 (file)
@@ -43,6 +43,7 @@
 #include "../../common/version_internal.h"
 #include "../CodeGenHelper.hh"
 #include "../../common/JSON_Tokenizer.hh"
+#include "../DebuggerStuff.hh"
 #include <limits.h>
 
 // implemented in coding_attrib_p.y
@@ -1673,6 +1674,10 @@ namespace Ttcn {
   {
     target->header.includes = mputstr(target->header.includes,
       "#include <TTCN3.hh>\n");
+    /*if (debugger_active) {
+      target->header.includes = mputstr(target->header.includes,
+        "#include \"init_debug.inc\"\n");
+    }*/
     for (size_t i = 0; i < impmods_v.size(); i++) {
       ImpMod *im = impmods_v[i];
       Common::Module *m = im->get_mod();
@@ -2091,6 +2096,12 @@ namespace Ttcn {
       module_dispname);
     target->functions.control = mputprintf(target->functions.control,
       "TTCN_Runtime::begin_controlpart(\"%s\");\n", module_dispname);
+    if (debugger_active) {
+      target->functions.control = mputprintf(target->functions.control,
+        "charstring_list no_params = NULL_VALUE;\n"
+        "TTCN3_Debug_Function debug_scope(NULL, \"control\", \"%s\", no_params, no_params, NULL);\n"
+        "debug_scope.initial_snapshot();\n", module_dispname);
+    }
     target->functions.control =
       block->generate_code(target->functions.control);
     target->functions.control = mputstr(target->functions.control,
@@ -2805,6 +2816,132 @@ namespace Ttcn {
       }
     }
   }
+  
+  void Module::generate_debugger_init(output_struct* output)
+  {
+    // create the initializer function
+    output->source.global_vars = mputstr(output->source.global_vars,
+      "\n/* Initializing TTCN-3 debugger */\n"
+      "void init_ttcn3_debugger()\n"
+      "{\n"
+      /*"  debugger_manual_init();\n"*/);
+    
+    // initialize global scope and variables (including imported variables)
+    char* str_glob = generate_debugger_global_vars(NULL, this);
+    for (int i = 0; i < imp->get_imports_size(); ++i) {
+      str_glob = imp->get_impmod(i)->get_mod()->generate_debugger_global_vars(str_glob, this);
+    }
+    if (str_glob != NULL) {
+      // only add the global scope if it actually has variables
+      output->source.global_vars = mputprintf(output->source.global_vars,
+        "  /* global variables */\n"
+        "  TTCN3_Debug_Scope* global_scope = ttcn3_debugger.add_global_scope(\"%s\");\n"
+        "%s",
+        get_modid().get_dispname().c_str(), str_glob);
+      Free(str_glob);
+    }
+    
+    // initialize components' scopes and their variables
+    for (size_t i = 0; i < asss->get_nof_asss(); ++i) {
+      Def_Type* def = dynamic_cast<Def_Type*>(asss->get_ass_byIndex(i));
+      if (def != NULL) {
+        Type* comp_type = def->get_Type();
+        if (comp_type->get_typetype() == Type::T_COMPONENT) {
+          char* str_comp = NULL;
+          ComponentTypeBody* comp_body = comp_type->get_CompBody();
+          for (size_t j = 0; j < comp_body->get_nof_asss(); ++j) {
+            str_comp = generate_code_debugger_add_var(str_comp, comp_body->get_ass_byIndex(j),
+              this, comp_type->get_dispname().c_str());
+          }
+          if (str_comp != NULL) {
+            // only add the component if it actually has variables
+            output->source.global_vars = mputprintf(output->source.global_vars,
+              "  /* variables of component %s */\n"
+              "  TTCN3_Debug_Scope* %s_scope = ttcn3_debugger.add_component_scope(\"%s\");\n"
+              "%s"
+              , comp_type->get_dispname().c_str(), comp_type->get_dispname().c_str()
+              , comp_type->get_dispname().c_str(), str_comp);
+            Free(str_comp);
+          }
+        }
+      }
+    }
+    
+    // close the initializer function
+    output->source.global_vars = mputstr(output->source.global_vars, "}\n");
+  }
+  
+  char* Module::generate_debugger_global_vars(char* str, Common::Module* current_mod)
+  {
+    for (size_t i = 0; i < asss->get_nof_asss(); ++i) {
+      Common::Assignment* ass = asss->get_ass_byIndex(i);
+      switch (ass->get_asstype()) {
+      case Common::Assignment::A_TEMPLATE:
+        if (ass->get_FormalParList() != NULL) {
+          // don't add parameterized templates, since they are functions in C++
+          break;
+        }
+        // else fall through
+      case Common::Assignment::A_CONST:
+      //case Common::Assignment::A_EXT_CONST: TODO: handle unused ext_const
+      case Common::Assignment::A_MODULEPAR:
+      case Common::Assignment::A_MODULEPAR_TEMP:
+        str = generate_code_debugger_add_var(str, ass, current_mod, "global");
+        break;
+      default:
+        break;
+      }
+    }
+    return str;
+  }
+  
+  void Module::generate_debugger_functions(output_struct *output)
+  {
+    char* str = NULL;
+    for (size_t i = 0; i < asss->get_nof_asss(); ++i) {
+      Def_Type* def = dynamic_cast<Def_Type*>(asss->get_ass_byIndex(i));
+      if (def != NULL) {
+        Type* t = def->get_Type();
+        if (!t->is_ref() && t->get_typetype() != Type::T_COMPONENT) {
+          // don't generate code for subtypes
+          if (t->get_typetype() != Type::T_SIGNATURE) {
+            str = mputprintf(str, 
+              "  %sif (!strcmp(p_var.type_name, \"%s\")) {\n"
+              "    ((const %s*)p_var.value)->log();\n"
+              "  }\n"
+              , (str != NULL) ? "else " : ""
+              , t->get_dispname().c_str(), t->get_genname_value(this).c_str());
+          }
+          if (t->get_typetype() != Type::T_PORT) {
+            str = mputprintf(str,
+              "  %sif (!strcmp(p_var.type_name, \"%s template\")) {\n"
+              "    ((const %s_template*)p_var.value)->log();\n"
+              "  }\n"
+              , (str != NULL) ? "else " : ""
+              , t->get_dispname().c_str(), t->get_genname_value(this).c_str());
+          }
+        }
+      }
+    }
+    if (str != NULL) {
+      // don't generate an empty printing function
+      output->header.class_defs = mputprintf(output->header.class_defs,
+        "/* Debugger printing function for types declared in this module */\n\n"
+        "extern CHARSTRING print_var_%s(const TTCN3_Debugger::variable_t& p_var);\n",
+        get_modid().get_ttcnname().c_str());
+      output->source.global_vars = mputprintf(output->source.global_vars,
+        "\n/* Debugger printing function for types declared in this module */\n"
+        "CHARSTRING print_var_%s(const TTCN3_Debugger::variable_t& p_var)\n"
+        "{\n"
+        "  TTCN_Logger::begin_event_log2str();\n"
+        "%s"
+        "  else {\n"
+        "    TTCN_Logger::log_event_str(\"<unrecognized value or template>\");\n"
+        "  }\n"
+        "  return TTCN_Logger::end_event_log2str();\n"
+        "}\n", get_modid().get_ttcnname().c_str(), str);
+    }
+  }
 
   // =================================
   // ===== Definition
@@ -3381,6 +3518,9 @@ namespace Ttcn {
       // the value is assigned using subsequent statements
       str = value->generate_code_init(str, genname_str);
     }
+    if (debugger_active) {
+      str = generate_code_debugger_add_var(str, this);
+    }
     return str;
   }
 
@@ -4230,6 +4370,9 @@ namespace Ttcn {
       size_t nof_base_pars = 0;
       char* function_body = create_location_object(memptystr(), "TEMPLATE",
         template_dispname);
+      if (debugger_active) {
+        function_body = generate_code_debugger_function_init(function_body, this);
+      }
       if (base_template) {
         // modified template
         function_body = mputprintf(function_body, "%s ret_val(%s",
@@ -4260,6 +4403,11 @@ namespace Ttcn {
       if (template_restriction!=TR_NONE && gen_restriction_check)
         function_body = Template::generate_restriction_check_code(function_body,
                           "ret_val", template_restriction);
+      if (debugger_active) {
+        function_body = mputstr(function_body,
+          "ttcn3_debugger.set_return_value((TTCN_Logger::begin_event_log2str(), "
+          "ret_val.log(), TTCN_Logger::end_event_log2str()));\n");
+      }
       function_body = mputstr(function_body, "return ret_val;\n");
       // if the template modifies a parameterized template, then the inherited
       // formal parameters must always be displayed, otherwise generate a smart
@@ -4403,6 +4551,9 @@ namespace Ttcn {
         str = Template::generate_restriction_check_code(str, genname_str,
           template_restriction);
     }
+    if (debugger_active) {
+      str = generate_code_debugger_add_var(str, this);
+    }
     return str;
   }
 
@@ -4626,6 +4777,9 @@ namespace Ttcn {
         str = initial_value->generate_code_init(str, genname_str);
       }
     }
+    if (debugger_active) {
+      str = generate_code_debugger_add_var(str, this);
+    }
     return str;
   }
 
@@ -4853,6 +5007,9 @@ namespace Ttcn {
         && gen_restriction_check)
       str = Template::generate_restriction_check_code(str, genname_str,
                                                       template_restriction);
+    if (debugger_active) {
+      str = generate_code_debugger_add_var(str, this);
+    }
     return str;
   }
 
@@ -5371,6 +5528,9 @@ namespace Ttcn {
         }
       }
     }
+    if (debugger_active) {
+      str = generate_code_debugger_add_var(str, this);
+    }
     return str;
   }
 
@@ -6072,6 +6232,9 @@ namespace Ttcn {
     if (!enable_set_bound_out_param)
       body = fp_list->generate_code_set_unbound(body); // conform the standard out parameter is unbound
     body = fp_list->generate_shadow_objects(body);
+    if (debugger_active) {
+      body = generate_code_debugger_function_init(body, this);
+    }
     body = block->generate_code(body);
     // smart formal parameter list (names of unused parameters are omitted)
     char *formal_par_list = fp_list->generate_code(memptystr());
@@ -6637,7 +6800,14 @@ namespace Ttcn {
       "TTCN_Logger::end_event();\n"
       "}\n", result_name, function_name, result_name);
     // returning the result stream if necessary
-    if (prototype == PROTOTYPE_CONVERT) str = mputstr(str, "return ret_val;\n");
+    if (prototype == PROTOTYPE_CONVERT) {
+      if (debugger_active) {
+        str = mputstr(str,
+          "ttcn3_debugger.set_return_value((TTCN_Logger::begin_event_log2str(), "
+          "ret_val.log(), TTCN_Logger::end_event_log2str()));\n");
+      }
+      str = mputstr(str, "return ret_val;\n");
+    }
     return str;
   }
 
@@ -6718,12 +6888,28 @@ namespace Ttcn {
         "}\n", input_type->get_genname_value(my_scope).c_str(), function_name);
       // closing the block and returning the appropriate result or status code
       if (prototype == PROTOTYPE_BACKTRACK) {
-        str = mputstr(str, "return 0;\n"
-          "} else return 1;\n");
+        if (debugger_active) {
+          str = mputstr(str, "ttcn3_debugger.set_return_value(\"0\");\n");
+        }
+        str = mputstr(str,
+          "return 0;\n"
+          "} else {\n");
+        if (debugger_active) {
+          str = mputstr(str, "ttcn3_debugger.set_return_value(\"1\");\n");
+        }
+        str = mputstr(str,  
+          "return 1;\n"
+          "}\n");
       } else {
         str = mputstr(str, "}\n");
-        if (prototype == PROTOTYPE_CONVERT)
+        if (prototype == PROTOTYPE_CONVERT) {
+          if (debugger_active) {
+            str = mputstr(str,
+              "ttcn3_debugger.set_return_value((TTCN_Logger::begin_event_log2str(), "
+              "ret_val.log(), TTCN_Logger::end_event_log2str()));\n");
+          }
           str = mputstr(str, "return ret_val;\n");
+        }
       }
     } else {
       // result handling and debug printout for sliding decoders
@@ -6738,13 +6924,16 @@ namespace Ttcn {
         "%s.log();\n"
         "TTCN_Logger::end_event();\n"
         "}\n"
-        "return 0;\n"
+        "%sreturn 0;\n"
         "case TTCN_EncDec::ET_INCOMPL_MSG:\n"
         "case TTCN_EncDec::ET_LEN_ERR:\n"
-        "return 2;\n"
+        "%sreturn 2;\n"
         "default:\n"
-        "return 1;\n"
-        "}\n", first_par_name, function_name, first_par_name);
+        "%sreturn 1;\n"
+        "}\n", first_par_name, function_name, first_par_name,
+        debugger_active ? "ttcn3_debugger.set_return_value(\"0\");\n" : "",
+        debugger_active ? "ttcn3_debugger.set_return_value(\"2\");\n" : "",
+        debugger_active ? "ttcn3_debugger.set_return_value(\"1\");\n" : "");
     }
     return str;
   }
@@ -6786,6 +6975,9 @@ namespace Ttcn {
         "%s %s(%s)\n"
         "{\n"
         , return_type_str, genname_str, formal_par_list);
+      if (debugger_active) {
+        body = generate_code_debugger_function_init(body, this);
+      }
       switch (function_type) {
       case EXTFUNC_ENCODE:
         body = generate_code_encode(body);
@@ -7067,6 +7259,9 @@ namespace Ttcn {
     // are never used)
     char* body = create_location_object(memptystr(), "ALTSTEP", dispname_str);
     body = fp_list->generate_shadow_objects(body);
+    if (debugger_active) {
+      body = generate_code_debugger_function_init(body, this);
+    }
     body = sb->generate_code(body);
     body = ags->generate_code_altstep(body);
     // generate a smart formal parameter list (omits unused parameter names)
@@ -7342,6 +7537,9 @@ namespace Ttcn {
       body = system_type->get_CompBody()->generate_code_comptype_name(body);
     else body = runs_on_body->generate_code_comptype_name(body);
     body = mputstr(body, ", has_timer, timer_value);\n");
+    if (debugger_active) {
+      body = generate_code_debugger_function_init(body, this);
+    }
     body = block->generate_code(body);
     body = mputprintf(body,
       "} catch (const TC_Error& tc_error) {\n"
@@ -8283,8 +8481,8 @@ namespace Ttcn {
   {
     // the name of the parameter should not be displayed if the parameter is not
     // used (to avoid a compiler warning)
-    bool display_name = (usage_found || display_unused || (!enable_set_bound_out_param &&
-      (asstype == A_PAR_VAL_OUT || asstype == A_PAR_TEMPL_OUT)));
+    bool display_name = (usage_found || display_unused || debugger_active ||
+      (!enable_set_bound_out_param && (asstype == A_PAR_VAL_OUT || asstype == A_PAR_TEMPL_OUT)));
     const char *name_str = display_name ? id->get_name().c_str() : "";
     switch (asstype) {
     case A_PAR_VAL_IN:
index 0ea723c316810c7b422234035192ec9c0d520039..e694c26b295bfad8639470b4ee4c754149c85ce6 100644 (file)
@@ -653,6 +653,23 @@ namespace Ttcn {
       * @param json_refs map of JSON documents containing the references and function
       * info related to each type */
     virtual void generate_json_schema(JSON_Tokenizer& json, map<Type*, JSON_Tokenizer>& json_refs);
+    
+    /** Generates the debugger initialization function for this module.
+      * The function creates the global debug scope associated with this module,
+      * and initializes it with all the global variables visible in the module
+      * (including imported variables).
+      * The debug scopes of all component types defined in the module are also
+      * created and initialized with their variables. */
+    virtual void generate_debugger_init(output_struct *output);
+    
+    /** Generates the variable adding code for all global variables defined
+      * in this module. This function is called by generate_debugger_init()
+      * for both the current module and all imported modules. */
+    virtual char* generate_debugger_global_vars(char* str, Common::Module* current_mod);
+    
+    /** Generates the debugger variable printing function, which can print values
+      * and templates of all types defined in this module (excluding subtypes). */
+    virtual void generate_debugger_functions(output_struct *output);
   };
 
   /**
index 81e5cab5077be5eeda2a1204abbb1e9194f172ca..8610a6520a67100da9b358c58a4f3d60af36e4e9 100644 (file)
@@ -5819,6 +5819,9 @@ error:
     }
     if (block->get_nof_stmts() > 0 || block->get_exception_handling()!=StatementBlock::EH_NONE) {
       str = mputstr(str, "{\n");
+      if (debugger_active) {
+        str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
+      }
       str = block->generate_code(str);
       str = mputstr(str, "}\n");
     } else str = mputstr(str, "/* empty block */;\n");
@@ -5905,6 +5908,9 @@ error:
       if(!eachfalse) str=mputstr(str, "else ");
       eachfalse=false;
       str=mputstr(str, "{\n");
+      if (debugger_active) {
+        str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
+      }
       blockcount++;
       str=if_stmt.elseblock->generate_code(str);
     }
@@ -5937,6 +5943,9 @@ error:
     // generate code for them anyway
     if (loop.for_stmt.varinst) {
       str = mputstr(str, "{\n");
+      if (debugger_active) {
+        str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
+      }
       str = loop.for_stmt.init_varinst->generate_code_str(str);
     } else {
       str = loop.for_stmt.init_ass->update_location_object(str);
@@ -5965,6 +5974,9 @@ error:
        while (blockcount-- > 0) str = mputstr(str, "}\n");
       }
       if (loop.label_next) str = mputstr(str, "{\n");
+      if (debugger_active) {
+        str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
+      }
       str = loop.block->generate_code(str);
       if (loop.label_next)
         str = mputprintf(str, "}\n"
@@ -6001,6 +6013,9 @@ error:
        str = mputstr(str, ") break;\n");
        while(blockcount-- > 0) str = mputstr(str, "}\n");
       }
+      if (debugger_active) {
+        str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
+      }
       str = loop.block->generate_code(str);
       str = mputstr(str, "}\n");
     }
@@ -6018,6 +6033,9 @@ error:
     }
     if (loop.iterate_once && !loop.has_brk && !loop.has_cnt) {
       str = mputstr(str, "{\n");
+      if (debugger_active) {
+        str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
+      }
       str = loop.block->generate_code(str);
     } else {
       str = mputstr(str, "for ( ; ; ) {\n");
@@ -6027,6 +6045,9 @@ error:
       if (loop.label_next && is_infinite_loop)
         str = mputprintf(str, "%s:\n", loop.label_next->c_str());
       if (loop.label_next && !is_infinite_loop) str = mputstr(str, "{\n");
+      if (debugger_active) {
+        str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
+      }
       str = loop.block->generate_code(str);
       // do not generate the exit condition for infinite loops
       if (!is_infinite_loop) {
@@ -6432,10 +6453,31 @@ error:
     Definition *my_def = my_sb->get_my_def();
     if (returnexpr.v) {
       expr.expr = mputc(expr.expr, ' ');
+      if (debugger_active) {
+        // the debugger's return value storing macro requires a temporary,
+        // so the returned expression isn't evaluated twice
+        string tmp_id = my_def->get_my_scope()->get_scope_mod_gen()->get_temporary_id();
+        expr.preamble = mputprintf(expr.preamble, "%s %s;\n",
+          returnexpr.v->get_expr_governor_last()->get_genname_value(my_def->get_my_scope()).c_str(),
+          tmp_id.c_str());
+        expr.expr = mputprintf(expr.expr, "DEBUGGER_STORE_RETURN_VALUE(%s, ", tmp_id.c_str());
+      }
       returnexpr.v->generate_code_expr_mandatory(&expr);
+      if (debugger_active) {
+        expr.expr = mputc(expr.expr, ')');
+      }
     } else if (returnexpr.t) {
       expr.expr = mputc(expr.expr, ' ');
       if (!my_def) FATAL_ERROR("Statement::generate_code_return()");
+      if (debugger_active) {
+        // the debugger's return value storing macro requires a temporary,
+        // so the returned expression isn't evaluated twice
+        string tmp_id = my_def->get_my_scope()->get_scope_mod_gen()->get_temporary_id();
+        expr.preamble = mputprintf(expr.preamble, "%s_template %s;\n",
+          returnexpr.t->get_my_governor()->get_genname_value(my_def->get_my_scope()).c_str(),
+          tmp_id.c_str());
+        expr.expr = mputprintf(expr.expr, "DEBUGGER_STORE_RETURN_VALUE(%s, ", tmp_id.c_str());
+      }
       Def_Function_Base* dfb = dynamic_cast<Def_Function_Base*>(my_def);
       if (!dfb) FATAL_ERROR("Statement::generate_code_return()");
       if (dfb->get_template_restriction() != TR_NONE &&
@@ -6445,6 +6487,9 @@ error:
       } else {
         returnexpr.t->generate_code_expr(&expr, TR_NONE);
       }
+      if (debugger_active) {
+        expr.expr = mputc(expr.expr, ')');
+      }
     } else {
       if (my_def && my_def->get_asstype() == Definition::A_ALTSTEP)
         expr.expr = mputstr(expr.expr, " ALT_YES");
@@ -8953,6 +8998,9 @@ error:
     }
     eachfalse = false;
     str=mputstr(str, "{\n");
+    if (debugger_active) {
+      str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
+    }
     str=block->generate_code(str);
     str=mputstr(str, "}\n");
     return str;
@@ -9282,6 +9330,9 @@ error:
     if(unreach) return str;
     if(!tis) unreach=true;
     str=mputprintf(str, "%s_%lu:\n{\n", tmp_prefix, (unsigned long) idx);
+    if (debugger_active) {
+      str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
+    }
     str=block->generate_code(str);
     str=mputprintf(str, "goto %s_end;\n}\n", tmp_prefix);
     return str;
@@ -10055,6 +10106,9 @@ error:
        StatementBlock *block = ag->get_block();
        if (block->get_nof_stmts() > 0) {
          str = mputstr(str, "{\n");
+         if (debugger_active) {
+           str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
+         }
          str = block->generate_code(str);
          str = mputstr(str, "}\n");
        }
@@ -10134,6 +10188,9 @@ error:
        StatementBlock *block = ag->get_block();
        if (block && block->get_nof_stmts() > 0) {
          str = mputstr(str, "{\n");
+         if (debugger_active) {
+           str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
+         }
          str = block->generate_code(str);
          if (block->has_return() != StatementBlock::RS_YES)
            str = mputstr(str, "break;\n");
@@ -10192,6 +10249,9 @@ error:
        StatementBlock *block = ag->get_block();
        if (block->get_nof_stmts() > 0) {
          str = mputstr(str, "{\n");
+         if (debugger_active) {
+           str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
+         }
          str = block->generate_code(str);
          str = mputstr(str, "}\n");
        }
@@ -10264,6 +10324,9 @@ error:
        StatementBlock *block = ag->get_block();
        if (block && block->get_nof_stmts() > 0) {
          str = mputstr(str, "{\n");
+         if (debugger_active) {
+           str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
+         }
          str = block->generate_code(str);
          str = mputstr(str, "}\n");
        }
index 9ea3e191cacb73ad0d782bd53cd03843ec6bf604..f18f4434c02a5a038b7243c7e02a773dbf17efca 100644 (file)
@@ -28,6 +28,7 @@
 #include "Encdec.hh"
 #include "RInt.hh"
 #include "JSON_Tokenizer.hh"
+#include "Logger.hh"
 #ifdef TITAN_RUNTIME_2
 #include "Struct_of.hh"
 #include "XER.hh"
@@ -1076,6 +1077,14 @@ public:
     return expr_cache;
   }
   virtual ~Lazy_Param() {}
+  void log() const {
+    if (!expr_evaluated) {
+      TTCN_Logger::log_event_str("<not evaluated>");
+    }
+    else {
+      expr_cache.log();
+    }
+  }
 };
 
 #endif
diff --git a/core/Debugger.cc b/core/Debugger.cc
new file mode 100644 (file)
index 0000000..7e86c0c
--- /dev/null
@@ -0,0 +1,878 @@
+/******************************************************************************
+ * Copyright (c) 2000-2016 Ericsson Telecom AB
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *   
+ *   Baranyi, Botond – initial implementation
+ *
+ ******************************************************************************/
+
+#include "Debugger.hh"
+
+//////////////////////////////////////////////////////
+////////////////// TTCN3_Debugger ////////////////////
+//////////////////////////////////////////////////////
+
+TTCN3_Debugger ttcn3_debugger;
+
+void TTCN3_Debugger::switch_off()
+{
+  if (!active) {
+    print("The debugger is already switched off.\n");
+  }
+  else {
+    print("Debugger switched off.\n");
+  }
+  active = false;
+}
+
+void TTCN3_Debugger::switch_on()
+{
+  if (active) {
+    print("The debugger is already switched on.\n");
+  }
+  else {
+    print("Debugger switched on.\n");
+  }
+  active = true;
+}
+
+void TTCN3_Debugger::add_breakpoint(const char* p_module, int p_line /*const char* batch_file*/)
+{
+  if (find_breakpoint(p_module, p_line) == breakpoints.size()) {
+    breakpoint_t bp;
+    bp.module = mcopystr(p_module);
+    bp.line = p_line;
+    breakpoints.push_back(bp);
+    print("Breakpoint added in module '%s' at line %d.\n", p_module, p_line);
+  }
+  else {
+    print("Breakpoint already set in module '%s' at line %d.\n", p_module, p_line);
+  }
+}
+
+void TTCN3_Debugger::remove_breakpoint(const char* p_module, int p_line)
+{
+  size_t pos = find_breakpoint(p_module, p_line);
+  if (pos != breakpoints.size()) {
+    Free(breakpoints[pos].module);
+    breakpoints.erase_at(pos);
+    print("Breakpoint removed in module '%s' from line %d.\n", p_module, p_line);
+  }
+  else {
+    print("No breakpoint found in module '%s' at line %d.\n", p_module, p_line);
+  }
+}
+
+void TTCN3_Debugger::set_special_breakpoint(special_breakpoint_t p_type, const char* p_state_str)
+{
+  bool new_state;
+  if (!strcmp(p_state_str, "yes")) {
+    new_state = true;
+  }
+  else if(!strcmp(p_state_str, "no")) {
+    new_state = false;
+  }
+  // else if "batch"
+  else {
+    print("Argument 1 is invalid.\n");
+    return;
+  }
+  const char* sbp_type_str;
+  bool state_changed;
+  switch (p_type) {
+  case SBP_FAIL_VERDICT:
+    state_changed = (fail_behavior != new_state);
+    fail_behavior = new_state;
+    sbp_type_str = "Fail";
+    break;
+  case SBP_ERROR_VERDICT:
+    state_changed = (error_behavior != new_state);
+    error_behavior = new_state;
+    sbp_type_str = "Error";
+    break;
+  default:
+    // should never happen
+    return;
+  }
+  print("%s verdict behavior %sset to %s.\n", sbp_type_str,
+    state_changed ? "" : "was already ",
+    new_state ? "halt the program" : "do nothing");
+}
+
+void TTCN3_Debugger::print_call_stack()
+{
+  for (size_t i = call_stack.size(); i != 0; --i) {
+    print("%d.\t", (int)call_stack.size() - (int)i + 1);
+    call_stack[i - 1]->print_function();
+  }
+}
+
+void TTCN3_Debugger::set_stack_level(int new_level)
+{
+  if (new_level < 0 || (size_t)new_level > call_stack.size()) {
+    print("Invalid new stack level.\n");
+  }
+  else {
+    stack_level = new_level;
+  }
+}
+
+void TTCN3_Debugger::print_variable(const TTCN3_Debugger::variable_t* p_var) const
+{
+  print("%s := %s\n", p_var->name, (const char*)p_var->print_function(*p_var));
+}
+
+void TTCN3_Debugger::set_output(const char* p_output_type, const char* p_file_name)
+{
+  FILE* new_fp; 
+  if (!strcmp(p_output_type, "stdout")) {
+    new_fp = stdout;
+  }
+  else if (!strcmp(p_output_type, "stderr")) {
+    new_fp = stderr;
+  }
+  else if (!strcmp(p_output_type, "file")) {
+    if (p_file_name == NULL) {
+      print("Missing output file name.\n");
+      return;
+    }
+    new_fp = fopen(p_file_name, "w");
+    if (new_fp == NULL) {
+      print("Failed to open file '%s' for writing.\n");
+      return;
+    }
+  }
+  else {
+    print("Argument 1 is invalid.\n");
+    return;
+  }
+  // don't close the previous file, if the command's parameters are invalid
+  if (output != stdout && output != stderr) {
+    fclose(output);
+  }
+  output = new_fp;
+}
+
+size_t TTCN3_Debugger::find_breakpoint(const char* p_module, int p_line) const
+{
+  for (size_t i = 0; i < breakpoints.size(); ++i) {
+    if (!strcmp(breakpoints[i].module, p_module) && breakpoints[i].line == p_line) {
+      return i;
+    }
+  }
+  return breakpoints.size();
+}
+
+TTCN3_Debugger::variable_t* TTCN3_Debugger::find_variable(const void* p_value) const
+{
+  for (size_t i = 0; i < variables.size(); ++i) {
+    if (variables[i]->value == p_value) {
+      return variables[i];
+    }
+  }
+  return NULL;
+}
+
+TTCN3_Debugger::TTCN3_Debugger()
+{
+  active = false;
+  output = stderr;
+  snapshots = NULL;
+  last_breakpoint_entry.module = NULL;
+  last_breakpoint_entry.line = 0;
+  stack_level = -1;
+  fail_behavior = false;
+  error_behavior = false;
+}
+
+TTCN3_Debugger::~TTCN3_Debugger()
+{
+  if (output != stdout && output != stderr) {
+    fclose(output);
+  }
+  for (size_t i = 0; i < breakpoints.size(); ++i) {
+    Free(breakpoints[i].module);
+  }
+  for (size_t i = 0; i < global_scopes.size(); ++i) {
+    delete global_scopes[i].scope;
+  }
+  for (size_t i = 0; i < component_scopes.size(); ++i) {
+    delete component_scopes[i].scope;
+  }
+  for (size_t i = 0; i < variables.size(); ++i) {
+    delete variables[i];
+  }
+  Free(snapshots);
+}
+
+TTCN3_Debug_Scope* TTCN3_Debugger::add_global_scope(const char* p_module)
+{
+  named_scope_t global_scope;
+  global_scope.name = p_module;
+  global_scope.scope = new TTCN3_Debug_Scope();
+  global_scopes.push_back(global_scope);
+  return global_scope.scope;
+}
+
+TTCN3_Debug_Scope* TTCN3_Debugger::add_component_scope(const char* p_component)
+{
+  named_scope_t component_scope;
+  component_scope.name = p_component;
+  component_scope.scope = new TTCN3_Debug_Scope();
+  component_scopes.push_back(component_scope);
+  return component_scope.scope;
+}
+
+void TTCN3_Debugger::set_return_value(const CHARSTRING& p_value)
+{
+  if (active) {
+    call_stack[call_stack.size() - 1]->set_return_value(p_value);
+  }
+}
+
+void TTCN3_Debugger::breakpoint_entry(int p_line /*bool p_stepping_helper*/)
+{
+  if (active && !call_stack.empty()) {
+    const char* module_name = call_stack[call_stack.size() - 1]->get_module_name();
+    bool trigger = false;
+    const char* trigger_type;
+    int actual_line;
+    switch (p_line) {
+    case SBP_FAIL_VERDICT:
+      trigger = fail_behavior;
+      trigger_type = "Fail verdict";
+      actual_line = last_breakpoint_entry.line;
+      break;
+    case SBP_ERROR_VERDICT:
+      trigger = error_behavior;
+      trigger_type = "Error verdict";
+      actual_line = last_breakpoint_entry.line;
+      break;
+    default:
+      // code lines
+      trigger = (last_breakpoint_entry.line == 0 || p_line != last_breakpoint_entry.line ||
+        module_name != last_breakpoint_entry.module) &&
+        find_breakpoint(module_name, p_line) != breakpoints.size();
+      trigger_type = "Breakpoint";
+      actual_line = p_line;
+      break;
+    }
+    // make sure it's not the same breakpoint entry as last time
+    if (trigger) {
+      stack_level = call_stack.size() - 1;
+      print("%s reached in module '%s' at line %d.\n", trigger_type,
+        module_name, actual_line);
+      ///////////////////////////////////////////////////////////////////////////////////
+      /*print("##################################################\n");
+      print("Call stack:\n");
+      charstring_list params = NULL_VALUE;
+      execute_command(D_PRINT_CALL_STACK, params);
+      print("##################################################\n");
+      print("Variables: ");
+      params[0] = "global";
+      execute_command(D_LIST_VARIABLES, params);
+      params.set_size(0);
+      size_t idx = 0;
+      const TTCN3_Debug_Scope* glob_scope = get_global_scope(module_name);
+      for (size_t i = 0; i < variables.size(); ++i) {
+        if (glob_scope->find_variable(variables[i]->name) != NULL) {
+          params[idx++] = variables[i]->name;
+        }
+      }
+      execute_command(D_PRINT_VARIABLE, params);
+      print("##################################################\n");
+      print("Function call snapshots:\n");
+      params.set_size(0);
+      execute_command(D_PRINT_SNAPSHOTS, params);*/
+      ///////////////////////////////////////////////////////////////////////////////////
+    }
+    last_breakpoint_entry.module = (char*)module_name;
+    last_breakpoint_entry.line = p_line;
+  }
+}
+
+CHARSTRING TTCN3_Debugger::print_base_var(const TTCN3_Debugger::variable_t& p_var)
+{
+  TTCN_Logger::begin_event_log2str();
+  if (!strcmp(p_var.type_name, "bitstring")) {
+    ((const BITSTRING*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "bitstring template")) {
+    ((const BITSTRING_template*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "boolean")) {
+    ((const BOOLEAN*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "boolean template")) {
+    ((const BOOLEAN_template*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "charstring")) {
+    ((const CHARSTRING*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "charstring template")) {
+    ((const CHARSTRING_template*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "float")) {
+    ((const FLOAT*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "float template")) {
+    ((const FLOAT_template*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "hexstring")) {
+    ((const HEXSTRING*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "hexstring template")) {
+    ((const HEXSTRING_template*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "integer")) {
+    ((const INTEGER*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "integer template")) {
+    ((const INTEGER_template*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "objid")) {
+    ((const OBJID*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "objid template")) {
+    ((const OBJID_template*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "octetstring")) {
+    ((const OCTETSTRING*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "octetstring template")) {
+    ((const OCTETSTRING_template*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "universal charstring")) {
+    ((const UNIVERSAL_CHARSTRING*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "universal charstring template")) {
+    ((const UNIVERSAL_CHARSTRING_template*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "verdicttype")) {
+    ((const VERDICTTYPE*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "verdicttype template")) {
+    ((const VERDICTTYPE_template*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "component")) {
+    ((const COMPONENT*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "component template")) {
+    ((const COMPONENT_template*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "default")) {
+    ((const DEFAULT*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "default template")) {
+    ((const DEFAULT_template*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "timer")) {
+    ((const TIMER*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "NULL")) {
+    ((const ASN_NULL*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "NULL template")) {
+    ((const ASN_NULL_template*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "CHARACTER STRING")) {
+    ((const CHARACTER_STRING*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "CHARACTER STRING template")) {
+    ((const CHARACTER_STRING_template*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "EMBEDDED PDV")) {
+    ((const EMBEDDED_PDV*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "EMBEDDED PDV template")) {
+    ((const EMBEDDED_PDV_template*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "EXTERNAL")) {
+    ((const EXTERNAL*)p_var.value)->log();
+  }
+  else if (!strcmp(p_var.type_name, "EXTERNAL template")) {
+    ((const EXTERNAL_template*)p_var.value)->log();
+  }
+  else {
+    TTCN_Logger::log_event_str("<unrecognized value or template>");
+  }
+  return TTCN_Logger::end_event_log2str();
+}
+
+void TTCN3_Debugger::print(const char* fmt, ...) const
+{
+  va_list parameters;
+  va_start(parameters, fmt);
+  vfprintf(output, fmt, parameters);
+  va_end(parameters);
+  fflush(output);
+}
+
+void TTCN3_Debugger::add_function(TTCN3_Debug_Function* p_function)
+{
+  if (active) {
+    call_stack.push_back(p_function);
+  }
+}
+
+void TTCN3_Debugger::add_scope(TTCN3_Debug_Scope* p_scope)
+{
+  if (active && !call_stack.empty()) {
+    call_stack[call_stack.size() - 1]->add_scope(p_scope);
+  }
+}
+
+void TTCN3_Debugger::remove_function(TTCN3_Debug_Function* p_function)
+{
+  if (!call_stack.empty() && call_stack[call_stack.size() - 1] == p_function) {
+    call_stack.erase_at(call_stack.size() - 1);
+  }
+}
+
+void TTCN3_Debugger::remove_scope(TTCN3_Debug_Scope* p_scope)
+{
+  if (!call_stack.empty()) {
+    call_stack[call_stack.size() - 1]->remove_scope(p_scope);
+  }
+}
+
+const TTCN3_Debugger::variable_t* TTCN3_Debugger::add_variable(const void* p_value,
+                                                               const char* p_name,
+                                                               const char* p_type,
+                                                               CHARSTRING (*p_print_function)(const TTCN3_Debugger::variable_t&))
+{
+   
+  if (call_stack.empty()) {
+    // no call stack yet, so this is a global or component variable
+    variable_t* var = find_variable(p_value);
+    if (var == NULL) {
+      var = new TTCN3_Debugger::variable_t;
+      var->value = p_value;
+      var->name = p_name;
+      var->type_name = p_type;
+      var->print_function = p_print_function;
+      variables.push_back(var);
+    }
+    return var;
+  }
+  else if (active) {
+    // it's a local variable for the top-most function
+    return call_stack[call_stack.size() - 1]->add_variable(p_value, p_name, p_type, p_print_function);
+  }
+  return NULL;
+}
+
+void TTCN3_Debugger::remove_variable(const variable_t* p_var)
+{
+  if (active && !call_stack.empty()) {
+    call_stack[call_stack.size() - 1]->remove_variable(p_var);
+  }
+}
+
+const TTCN3_Debug_Scope* TTCN3_Debugger::get_global_scope(const char* p_module) const
+{
+  for (size_t i = 0; i < global_scopes.size(); ++i) {
+    if (strcmp(global_scopes[i].name, p_module) == 0) {
+      return global_scopes[i].scope;
+    }
+  }
+  return NULL;
+}
+
+const TTCN3_Debug_Scope* TTCN3_Debugger::get_component_scope(const char* p_component) const
+{
+  for (size_t i = 0; i < component_scopes.size(); ++i) {
+    if (strcmp(component_scopes[i].name, p_component) == 0) {
+      return component_scopes[i].scope;
+    }
+  }
+  return NULL;
+}
+
+void TTCN3_Debugger::add_snapshot(const char* p_snapshot)
+{
+  snapshots = mputstr(snapshots, p_snapshot);
+}
+
+#define CHECK_NOF_ARGUMENTS(exp_num) \
+  if (exp_num != p_arguments.size_of()) { \
+    print("Invalid number of arguments. Expected %d, got %d.\n", \
+      (int)exp_num, (int)p_arguments.size_of()); \
+    return; \
+  }
+
+#define CHECK_NOF_ARGUMENTS_RANGE(min, max) \
+  if ((int)min > p_arguments.size_of() || (int)max < p_arguments.size_of()) { \
+    print("Invalid number of arguments. Expected at least %d and at most %d, got %d.\n", \
+      (int)min, (int)max, p_arguments.size_of()); \
+    return; \
+  }
+
+#define CHECK_NOF_ARGUMENTS_MIN(min) \
+  if ((int)min > p_arguments.size_of()) { \
+    print("Invalid number of arguments. Expected at least %d, got %d.\n", \
+      (int)min, p_arguments.size_of()); \
+    return; \
+  }
+
+#define CHECK_INT_ARGUMENT(arg_idx) \
+  { \
+    const char* str = (const char*)p_arguments[arg_idx]; \
+    for (int i = 0; i < p_arguments[arg_idx].lengthof(); ++i) { \
+      if (str[i] < '0' || str[i] > '9') { \
+        print("Argument %d is not an integer.\n", (int)(arg_idx + 1)); \
+        return; \
+      } \
+    } \
+  }
+
+#define CHECK_CALL_STACK \
+  if (call_stack.empty()) { \
+    print("This command can only be executed when the program is running.\n"); \
+    return; \
+  }
+
+void TTCN3_Debugger::execute_command(TTCN3_Debugger::debug_command_t p_command,
+                                     const charstring_list& p_arguments)
+{
+  if (!active && p_command != D_SWITCH_ON && p_command != D_SWITCH_OFF) {
+    print("Cannot run debug commands while the debugger is switched off.\n");
+    return;
+  }
+  for (int i = 0; i < p_arguments.size_of(); ++i) {
+    if (!p_arguments[i].is_bound()) {
+      print("Argument %d is unbound.\n", i + 1);
+      return;
+    }
+  }
+  switch (p_command) {
+  case D_SWITCH_OFF:
+    CHECK_NOF_ARGUMENTS(0)
+    switch_off();
+    break;
+  case D_SWITCH_ON:
+    CHECK_NOF_ARGUMENTS(0)
+    switch_on();
+    break;
+  case D_ADD_BREAKPOINT:
+    CHECK_NOF_ARGUMENTS(2)
+    CHECK_INT_ARGUMENT(1)
+    add_breakpoint(p_arguments[0], str2int(p_arguments[1]));
+    break;
+  case D_REMOVE_BREAKPOINT:
+    CHECK_NOF_ARGUMENTS(2)
+    CHECK_INT_ARGUMENT(1)
+    remove_breakpoint(p_arguments[0], str2int(p_arguments[1]));
+    break;
+  case D_SET_ERROR_BEHAVIOR:
+    CHECK_NOF_ARGUMENTS(1)
+    set_special_breakpoint(SBP_ERROR_VERDICT, p_arguments[0]);
+    break;
+  case D_SET_FAIL_BEHAVIOR:
+    CHECK_NOF_ARGUMENTS(1)
+    set_special_breakpoint(SBP_FAIL_VERDICT, p_arguments[0]);
+    break;
+  // ...
+  case D_SET_OUTPUT:
+    CHECK_NOF_ARGUMENTS_RANGE(1, 2)
+    set_output(p_arguments[0], (p_arguments.size_of() == 2) ? (const char*)p_arguments[1] : NULL);
+    break;
+  // ...
+  case D_PRINT_CALL_STACK:
+    CHECK_CALL_STACK
+    CHECK_NOF_ARGUMENTS(0)
+    print_call_stack();
+    break;
+  case D_SET_STACK_LEVEL:
+    CHECK_CALL_STACK
+    CHECK_NOF_ARGUMENTS(1)
+    CHECK_INT_ARGUMENT(0)
+    set_stack_level(str2int(p_arguments[0]));
+    break;
+  case D_LIST_VARIABLES:
+    CHECK_CALL_STACK
+    CHECK_NOF_ARGUMENTS_RANGE(1, 2)
+    call_stack[stack_level]->list_variables(p_arguments[0],
+      (p_arguments.size_of() == 2) ? (const char*)p_arguments[1] : NULL);
+    break;
+  case D_PRINT_VARIABLE:
+    CHECK_CALL_STACK
+    CHECK_NOF_ARGUMENTS_MIN(1)
+    for (int i = 0; i < p_arguments.size_of(); ++i) {
+      const variable_t* var = call_stack[stack_level]->find_variable(p_arguments[i]);
+      if (var != NULL) {
+        print_variable(var);
+      }
+      else {
+        print("Variable '%s' not found.\n", (const char*)p_arguments[i]);
+      }
+    }
+    break;
+  // ...
+  case D_PRINT_SNAPSHOTS:
+    CHECK_NOF_ARGUMENTS(0)
+    print("%s", snapshots);
+    break;
+  // ...
+  default:
+    print("Command not implemented.\n");
+    break;
+  }
+}
+
+//////////////////////////////////////////////////////
+//////////////// TTCN3_Debug_Scope ///////////////////
+//////////////////////////////////////////////////////
+
+TTCN3_Debug_Scope::TTCN3_Debug_Scope()
+{
+  ttcn3_debugger.add_scope(this);
+}
+
+TTCN3_Debug_Scope::~TTCN3_Debug_Scope()
+{
+  for (size_t i = 0; i < variables.size(); ++i) {
+    ttcn3_debugger.remove_variable(variables[i]);
+  }
+  ttcn3_debugger.remove_scope(this);
+}
+
+void TTCN3_Debug_Scope::add_variable(const void* p_value,
+                                     const char* p_name,
+                                     const char* p_type,
+                                     CHARSTRING (*p_print_function)(const TTCN3_Debugger::variable_t&))
+{
+  const TTCN3_Debugger::variable_t* var = ttcn3_debugger.add_variable(p_value, p_name, p_type, p_print_function);
+  if (var != NULL) {
+    variables.push_back(var);
+  }
+}
+
+const TTCN3_Debugger::variable_t* TTCN3_Debug_Scope::find_variable(const char* p_name) const
+{
+  for (size_t i = 0; i < variables.size(); ++i) {
+    if (strcmp(variables[i]->name, p_name) == 0) {
+      return variables[i];
+    }
+  }
+  return NULL;
+}
+
+void TTCN3_Debug_Scope::list_variables(const char* p_filter, bool& p_first) const
+{
+  for (size_t i = 0; i < variables.size(); ++i) {
+    // the filter is currently ignored
+    ttcn3_debugger.print("%s%s", p_first ? "" : " ", variables[i]->name);
+    p_first = false;
+  }
+}
+
+//////////////////////////////////////////////////////
+/////////////// TTCN3_Debug_Function /////////////////
+//////////////////////////////////////////////////////
+
+TTCN3_Debug_Function::TTCN3_Debug_Function(const char* p_name,
+                                           const char* p_type,
+                                           const char* p_module,
+                                           const charstring_list& p_parameter_names,
+                                           const charstring_list& p_parameter_types,
+                                           const char* p_component_name)
+: function_name(p_name), function_type(p_type), module_name(p_module)
+, parameter_names(new charstring_list(p_parameter_names))
+, parameter_types(new charstring_list(p_parameter_types))
+{
+  ttcn3_debugger.add_function(this);
+  global_scope = ttcn3_debugger.get_global_scope(p_module);
+  component_scope = (p_component_name != NULL) ?
+    ttcn3_debugger.get_component_scope(p_component_name) : NULL;
+  if (function_name == NULL) {
+    function_name = p_module; // for control parts
+  }
+}
+
+TTCN3_Debug_Function::~TTCN3_Debug_Function()
+{
+  if (ttcn3_debugger.is_on()) {
+    char* snapshot = mprintf("[%s]\tfinished\t%s(", function_type, function_name);
+    if (parameter_names->size_of() > 0) {
+      for (int i = 0; i < parameter_names->size_of(); ++i) {
+        if (i > 0) {
+          snapshot = mputstr(snapshot, ", ");
+        }
+        snapshot = mputprintf(snapshot, "[%s] %s := ", (const char*)((*parameter_types)[i]),
+          (const char*)((*parameter_names)[i]));
+        if ((*parameter_types)[i] == "out" || (*parameter_types)[i] == "inout") {
+          const TTCN3_Debugger::variable_t* parameter = find_variable((*parameter_names)[i]);
+          snapshot = mputstr(snapshot, parameter->print_function(*parameter));
+        }
+        else {
+          snapshot = mputc(snapshot, '-');
+        }
+      }
+    }
+    snapshot = mputc(snapshot, ')');
+    if (return_value.is_bound()) {
+      snapshot = mputprintf(snapshot, " returned %s", (const char*)return_value);
+    }
+    snapshot = mputc(snapshot, '\n');
+    ttcn3_debugger.add_snapshot(snapshot);
+    Free(snapshot);
+  }
+  for (size_t i = 0; i < variables.size(); ++i) {
+    delete variables[i];
+  }
+  delete parameter_names;
+  delete parameter_types;
+  ttcn3_debugger.remove_function(this);
+}
+
+const TTCN3_Debugger::variable_t* TTCN3_Debug_Function::add_variable(const void* p_value,
+                                                                     const char* p_name,
+                                                                     const char* p_type,
+                                                                     CHARSTRING (*p_print_function)(const TTCN3_Debugger::variable_t&))
+{
+  if (ttcn3_debugger.is_on()) {
+    TTCN3_Debugger::variable_t* var = new TTCN3_Debugger::variable_t;
+    var->value = p_value;
+    var->name = p_name;
+    var->type_name = p_type;
+    var->print_function = p_print_function;
+    variables.push_back(var);
+    return var;
+  }
+  return NULL;
+}
+
+void TTCN3_Debug_Function::set_return_value(const CHARSTRING& p_value)
+{
+  return_value = p_value;
+}
+
+void TTCN3_Debug_Function::initial_snapshot() const
+{
+  if (ttcn3_debugger.is_on()) {
+    char* snapshot = mprintf("[%s]\tstarted \t%s(", function_type, function_name);
+    if (parameter_names->size_of() > 0) {
+      for (int i = 0; i < parameter_names->size_of(); ++i) {
+        if (i > 0) {
+          snapshot = mputstr(snapshot, ", ");
+        }
+        snapshot = mputprintf(snapshot, "[%s] %s := ", (const char*)((*parameter_types)[i]),
+          (const char*)((*parameter_names)[i]));
+        if ((*parameter_types)[i] == "in" || (*parameter_types)[i] == "inout") {
+          const TTCN3_Debugger::variable_t* parameter = find_variable((*parameter_names)[i]);
+          snapshot = mputstr(snapshot, parameter->print_function(*parameter));
+        }
+        else {
+          snapshot = mputc(snapshot, '-');
+        }
+      }
+    }
+    snapshot = mputstr(snapshot, ")\n");
+    ttcn3_debugger.add_snapshot(snapshot);
+    Free(snapshot);
+  }
+}
+
+void TTCN3_Debug_Function::add_scope(TTCN3_Debug_Scope* p_scope)
+{
+  scopes.push_back(p_scope);
+}
+
+void TTCN3_Debug_Function::remove_scope(TTCN3_Debug_Scope* p_scope)
+{
+  if (scopes[scopes.size() - 1] == p_scope) {
+    scopes.erase_at(scopes.size() - 1);
+  }
+}
+
+void TTCN3_Debug_Function::remove_variable(const TTCN3_Debugger::variable_t* p_var)
+{
+  for (size_t i = 0; i < variables.size(); ++i) {
+    if (variables[i] == p_var) {
+      variables.erase_at(i);
+      delete p_var;
+      break;
+    }
+  }
+}
+
+const TTCN3_Debugger::variable_t* TTCN3_Debug_Function::find_variable(const char* p_name) const
+{
+  for (size_t i = 0; i < variables.size(); ++i) {
+    if (strcmp(variables[i]->name, p_name) == 0) {
+      return variables[i];
+    }
+  }
+  // it's not a local variable, it might still be a global or component variable
+  if (component_scope != NULL) {
+    const TTCN3_Debugger::variable_t* res = component_scope->find_variable(p_name);
+    if (res != NULL) {
+      return res;
+    }
+  }
+  return (global_scope != NULL) ? global_scope->find_variable(p_name) : NULL;
+}
+
+void TTCN3_Debug_Function::print_function() const
+{
+  ttcn3_debugger.print("[%s]\t%s(", function_type, function_name);
+  if (parameter_names->size_of() > 0) {
+    for (int i = 0; i < parameter_names->size_of(); ++i) {
+      if (i > 0) {
+        ttcn3_debugger.print(", ");
+      }
+      const TTCN3_Debugger::variable_t* parameter = find_variable((*parameter_names)[i]);
+      ttcn3_debugger.print("[%s] %s := %s", (const char*)(*parameter_types)[i],
+        (const char*)(*parameter_names)[i], (const char*)parameter->print_function(*parameter));
+    }
+  }
+  ttcn3_debugger.print(")\n");
+}
+
+void TTCN3_Debug_Function::list_variables(const char* p_scope, const char* p_filter) const
+{
+  bool first = true;
+  bool list_local = false;
+  bool list_global = false;
+  bool list_comp = false;
+  if (!strcmp(p_scope, "local")) {
+    list_local = true;
+  }
+  else if (!strcmp(p_scope, "global")) {
+    list_global = true;
+  }
+  else if (!strcmp(p_scope, "comp") || !strcmp(p_scope, "component")) {
+    list_comp = true;
+  }
+  else {
+    if (strcmp(p_scope, "all")) {
+      ttcn3_debugger.print("Invalid scope. Listing variables in all scopes.\n");
+    }
+    list_local = true;
+    list_global = true;
+    list_comp = true;
+  }
+  if (list_local) {
+    for (size_t i = 0; i < variables.size(); ++i) {
+      ttcn3_debugger.print("%s%s", first ? "" : " ", variables[i]->name);
+      first = false;
+    }
+  }
+  if (list_global && global_scope != NULL && global_scope->has_variables()) {
+    global_scope->list_variables(p_filter, first);
+  }
+  if (list_comp && component_scope != NULL && component_scope->has_variables()) {
+    component_scope->list_variables(p_filter, first);
+  }
+  if (first) {
+    ttcn3_debugger.print("No variables found.");
+  }
+  ttcn3_debugger.print("\n");
+}
+
diff --git a/core/Debugger.hh b/core/Debugger.hh
new file mode 100644 (file)
index 0000000..42271c6
--- /dev/null
@@ -0,0 +1,527 @@
+/******************************************************************************
+ * Copyright (c) 2000-2016 Ericsson Telecom AB
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *   
+ *   Baranyi, Botond – initial implementation
+ *
+ ******************************************************************************/
+
+#ifndef DEBUGGER_HH
+#define DEBUGGER_HH
+
+#include "Vector.hh"
+#include "Basetype.hh"
+#include "Charstring.hh"
+#ifdef TITAN_RUNTIME_2
+#include "RT2/PreGenRecordOf.hh"
+#else
+#include "RT1/PreGenRecordOf.hh"
+#endif
+
+/** alias for record of charstring */
+typedef PreGenRecordOf::PREGEN__RECORD__OF__CHARSTRING charstring_list;
+
+// forward declarations
+class TTCN3_Debug_Scope;
+class TTCN3_Debug_Function;
+
+
+//////////////////////////////////////////////////////
+////////////////// TTCN3_Debugger ////////////////////
+//////////////////////////////////////////////////////
+
+/** main debugger class
+  * 
+  * instantiated once per process at the beginning of the current process,
+  * destroyed at the end of the current process */
+class TTCN3_Debugger {
+public:
+  
+  /** type for keeping track of a variable */
+  struct variable_t {
+    /** pointer to the variable object, not owned */
+    const void* value;
+    /** variable name (used for looking up variables), not owned */
+    const char* name;
+    /** name of the variable's type, not owned */
+    const char* type_name;
+    /** variable printing function (using the variable object's log() function) */
+    CHARSTRING (*print_function)(const variable_t&);
+  };
+  
+  /** this type pairs a debug scope with a name, used for storing global and
+    * component scopes */
+  struct named_scope_t {
+    /** scope name (module name for global scopes, or component type name for
+      * component scopes), not owned*/
+    const char* name;
+    /** scope pointer, owned */
+    TTCN3_Debug_Scope* scope;
+  };
+  
+  /** type for storing breakpoints */
+  struct breakpoint_t {
+    /** module name, owned */
+    char* module;
+    /** line number */
+    int line;
+    // const char* batch_file;
+  };
+  
+  /** list of commands coming from the user interface (parameters listed in comments) */
+  enum debug_command_t {
+    // on/off switch
+    D_SWITCH_OFF, // 0
+    D_SWITCH_ON, // 0
+    // breakpoints
+    D_ADD_BREAKPOINT, // 2, module name and line number
+    D_REMOVE_BREAKPOINT, // 2, module name and line number
+    D_SET_ERROR_BEHAVIOR, // 1, "yes" or "no"
+    D_SET_FAIL_BEHAVIOR, // 1, "yes" or "no"
+    // printing and overwriting data
+    D_SET_OUTPUT, // 1-2, "stdout", "stderr" or "file" + file name
+    D_SET_PROCESS, // 1, 'mtc' or component reference
+    D_PRINT_CALL_STACK, // 0
+    D_SET_STACK_LEVEL, // 1, stack level
+    D_LIST_VARIABLES, // 1-2, "local", "global", "comp", "component" or "all", + optional filter (pattern)
+    D_PRINT_VARIABLE, // 1+, list of variable names
+    D_OVERWRITE_VARIABLE, // 2, variable name, new value (in module parameter syntax)
+    D_PRINT_SNAPSHOTS, // 0
+    D_SET_SNAPSHOT_BEHAVIOR, // TBD
+    // stepping
+    D_STEP_OVER, // 0
+    D_STEP_INTO, // 0
+    D_STEP_OUT, // 0
+    D_RUN_TO_CURSOR, // 2, module name and line number
+    // ending the halted state
+    D_CONTINUE, // 0
+    D_EXIT, // 0
+    // batch files
+    D_SET_HALTING_BATCH_FILE // TBD
+  };
+  
+  /** 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 {
+    /** triggered when the local verdict is set to ERROR (by dynamic test case errors) */
+    SBP_ERROR_VERDICT = 0,
+    /** triggered when the local verdict is set to FAIL (by the user) */
+    SBP_FAIL_VERDICT = -1
+  };
+  
+private:
+  
+  /** the debugger's on/off switch */
+  bool active;
+  
+  /** the debugger's output file handler */
+  FILE* output;
+  
+  /** list of all global and component variables, elements are owned */
+  Vector<variable_t*> variables;
+  
+  /** list of global scopes */
+  Vector<named_scope_t> global_scopes;
+  
+  /** list of component scopes */
+  Vector<named_scope_t> component_scopes;
+  
+  /** pointers to debug function objects (resembling a call stack), elements are not owned
+    * the current function is always the last element in the array (the top element in the stack) */
+  Vector<TTCN3_Debug_Function*> call_stack;
+  
+  /** list of breakpoints */
+  Vector<breakpoint_t> breakpoints;
+  
+  /** string containing function call snapshots, owned */
+  char* snapshots;
+  
+  /** stores the last line hit by breakpoint_entry() */
+  breakpoint_t last_breakpoint_entry;
+  
+  /** current stack level (reset whenever a breakpoint is reached) */
+  int stack_level;
+  
+  /** behavior triggered by setting the local verdict to FAIL
+    * (a breakpoint is activated if set to true) */
+  bool fail_behavior;
+  
+  /** behavior triggered by setting the local verdict to ERROR
+    * (a breakpoint is activated if set to true) */
+  bool error_behavior;
+  
+  //////////////////////////////////////////////////////
+  ///////////////// internal functions /////////////////
+  //////////////////////////////////////////////////////
+  
+  /** switches the debugger off
+    * handles the D_SWITCH_OFF command */
+  void switch_off();
+  
+  /** switches the debugger on 
+    * handles the D_SWITCH_ON command */
+  void switch_on();
+  
+  /** adds a new breakpoint at the specified module and line 
+    * handles the D_ADD_BREAKPOINT command */
+  void add_breakpoint(const char* p_module, int p_line /*const char* batch_file*/);
+  
+  /** removes the breakpoint from the specified module/line, if it exists
+    * handles the D_REMOVE_BREAKPOINT command  */
+  void remove_breakpoint(const char* p_module, int p_line);
+  
+  /** sets the behavior of a special breakpoint type 
+    * @param p_state_str "yes" turns the special breakpoint on (as if it was an
+    * actual breakpoint), "no" turns it off 
+    * handles the D_SET_ERROR_BEHAVIOR and D_SET_FAIL_BEHAVIOR commands */
+  void set_special_breakpoint(special_breakpoint_t p_type, const char* p_state_str);
+  
+  /** prints the current call stack
+    * handles the D_PRINT_CALL_STACK command */
+  void print_call_stack();
+  
+  /** sets the current stack level to the specified level
+    * handles the D_SET_STACK_LEVEL command */
+  void set_stack_level(int new_level);
+  
+  /** prints the specified variable
+    * handles (one parameter of) the D_PRINT_VARIABLE command */
+  void print_variable(const variable_t* p_var) const;
+  
+  /** sets the debugger's output to a different stream 
+    * handles the D_SET_OUTPUT command
+    * @param p_output_type "stdout", "stderr" or "file"
+    * @param p_file_name output file name, if the output is a file, or NULL */
+  void set_output(const char* p_output_type, const char* p_file_name);
+  
+  /** 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;
+  
+  /** returns the specified variable, if found, otherwise returns NULL */
+  TTCN3_Debugger::variable_t* find_variable(const void* p_value) const;
+
+public:
+  /** constructor - called once per process (at the beginning) */
+  TTCN3_Debugger();
+  
+  /** destructor - called once per process (at the end) */
+  ~TTCN3_Debugger();
+  
+  //////////////////////////////////////////////////////
+  ////// methods called from TITAN generated code //////
+  //////////////////////////////////////////////////////
+  
+  /** creates, stores and returns a new global scope for the specified module */
+  TTCN3_Debug_Scope* add_global_scope(const char* p_module);
+  
+  /** creates, stores and returns a new global scope for the specified module */
+  TTCN3_Debug_Scope* add_component_scope(const char* p_component);
+  
+  /** stores the string representation of the current function's return value
+    * (only if the debugger is switched on) */
+  void set_return_value(const CHARSTRING& p_value);
+  
+  /** activates a breakpoint if the specified line and the current function's module
+    * match any of the breakpoints stored in the debugger 
+    * the special parameter values (SBP_ERROR_VERDICT and SBP_FAIL_VERDICT) only
+    * trigger a breakpoint if their respective behaviors have been set to do so
+    * (does nothing if the debugger is switched off) */
+  void breakpoint_entry(int p_line /*bool p_stepping_helper*/);
+  
+  /** variable printing function for base types */
+  static CHARSTRING print_base_var(const variable_t& p_var);
+  
+  /** variable printing function for value arrays */
+  template <typename T_type, unsigned int array_size, int index_offset>
+  static CHARSTRING print_value_array(const variable_t& p_var)
+  {
+    TTCN_Logger::begin_event_log2str();
+    ((VALUE_ARRAY<T_type, array_size, index_offset>*)p_var.value)->log();
+    return TTCN_Logger::end_event_log2str();
+  }
+  
+  /** variable printing function for template arrays */
+  template <typename T_value_type, typename T_template_type,
+    unsigned int array_size, int index_offset>
+  static CHARSTRING print_template_array(const variable_t& p_var)
+  {
+    TTCN_Logger::begin_event_log2str();
+    ((TEMPLATE_ARRAY<T_value_type, T_template_type, array_size,
+      index_offset>*)p_var.value)->log();
+    return TTCN_Logger::end_event_log2str();
+  }
+  
+  /** variable printing function for port arrays */
+  template <typename T_type, unsigned int array_size, int index_offset>
+  static CHARSTRING print_port_array(const variable_t& p_var)
+  {
+    TTCN_Logger::begin_event_log2str();
+    ((PORT_ARRAY<T_type, array_size, index_offset>*)p_var.value)->log();
+    return TTCN_Logger::end_event_log2str();
+  }
+  
+  /** variable printing function for timer arrays */
+  template <typename T_type, unsigned int array_size, int index_offset>
+  static CHARSTRING print_timer_array(const variable_t& p_var)
+  {
+    TTCN_Logger::begin_event_log2str();
+    ((TIMER_ARRAY<T_type, array_size, index_offset>*)p_var.value)->log();
+    return TTCN_Logger::end_event_log2str();
+  }
+  
+  /** variable printing function for lazy parameters */
+  template <typename EXPR_TYPE>
+  static CHARSTRING print_lazy_param(const variable_t& p_var)
+  {
+    TTCN_Logger::begin_event_log2str();
+    ((Lazy_Param<EXPR_TYPE>*)p_var.value)->log();
+    return TTCN_Logger::end_event_log2str();
+  }
+  
+  //////////////////////////////////////////////////////
+  ////// methods called by other debugger classes //////
+  //////////////////////////////////////////////////////
+  
+  /** returns true if the debugger is switched on */
+  bool is_on() const { return active; }
+  
+  /** prints formatted string to the debugger's output stream */
+  void print(const char* fmt, ...) const;
+  
+  /** adds the specified function object pointer to the call stack
+    * (only if the debugger is switched on) */
+  void add_function(TTCN3_Debug_Function* p_function);
+  
+  /** adds the specified scope object pointer to the current function's scope list
+    * (only if the debugger is switched on and the call stack is not empty) */
+  void add_scope(TTCN3_Debug_Scope* p_scope);
+  
+  /** removes the specified function object pointer from the call stack, if it is
+    * the function at the top of the stack */
+  void remove_function(TTCN3_Debug_Function* p_function);
+  
+  /** removes the specified scope object pointer from the current function's scope list
+    * (only if the call stack is not empty) */
+  void remove_scope(TTCN3_Debug_Scope* p_scope);
+  
+  /** finds or creates, and returns the variable entry specified by the parameters
+    *
+    * if the call stack is empty, an entry for a global or component variable is
+    * created and stored in the main debugger object (if it doesn't already exist);
+    * if the call stack is not empty (and if the debugger is switched on), the 
+    * variable entry for a local variable is created and stored by the current function*/
+  const variable_t* add_variable(const void* p_value, const char* p_name, const char* p_type,
+    CHARSTRING (*p_print_function)(const variable_t&));
+  
+  /** removes the variable entry for the specified local variable in the current
+    * function (only if the call stack is not empty) */
+  void remove_variable(const variable_t* p_var);
+  
+  /** returns the global scope object associated with the specified module */
+  const TTCN3_Debug_Scope* get_global_scope(const char* p_module) const;
+  
+  /** 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);
+  
+  /** executes a command received from the user interface */
+  void execute_command(debug_command_t p_command, const charstring_list& p_arguments);
+};
+
+/** the main debugger object */
+extern TTCN3_Debugger ttcn3_debugger;
+
+
+//////////////////////////////////////////////////////
+//////////////// TTCN3_Debug_Scope ///////////////////
+//////////////////////////////////////////////////////
+
+/** debugger scope class
+  *
+  * instantiated at the beginning of every code block in the TTCN-3 code (except
+  * for the code blocks of functions), plus one (global scope) instance is created
+  * for every module and one (component scope) for every component type 
+  * 
+  * the class' main purpose is to track which local variables were created in the
+  * current code block or to track which of the main debugger object's variables
+  * belong to which global or component scope */
+class TTCN3_Debug_Scope {
+  
+  /** list of pointers to local variable entries from the current function object or
+    * global or component variable entries from the main debugger object
+    * (the elements are not owned)*/
+  Vector<const TTCN3_Debugger::variable_t*> variables;
+  
+public:
+  
+  /** constructor - lets the current function know of this new scope */
+  TTCN3_Debug_Scope();
+  
+  /** destructor - tells the current function to delete the variable entries listed
+    * in this instance */
+  ~TTCN3_Debug_Scope();
+  
+  //////////////////////////////////////////////////////
+  ////// methods called from TITAN generated code //////
+  //////////////////////////////////////////////////////
+  
+  /** passes the parameters to the main debugger or current function object to 
+    * create and store a variable entry from them, and tracks this new variable
+    * by storing a pointer to it
+    * (local variables are only created and stored if the debugger is switched on) */
+  void add_variable(const void* p_value, const char* p_name, const char* p_type,
+    CHARSTRING (*p_print_function)(const TTCN3_Debugger::variable_t&));
+  
+  //////////////////////////////////////////////////////
+  ////// methods called by other debugger classes //////
+  //////////////////////////////////////////////////////
+  
+  /** returns true if there is at least one variable in the scope object */
+  bool has_variables() const { return !variables.empty(); }
+  
+  /** returns the specified variable, if found, otherwise returns NULL */
+  const TTCN3_Debugger::variable_t* find_variable(const char* p_name) const;
+  
+  /** prints the names of variables in this scope that match the specified pattern
+    * @param p_filter the mentioned pattern
+    * @param p_first true if no variables have been printed yet */
+  void list_variables(const char* p_filter, bool& p_first) const;
+};
+
+
+//////////////////////////////////////////////////////
+/////////////// TTCN3_Debug_Function /////////////////
+//////////////////////////////////////////////////////
+
+/** debugger function class
+  *
+  * instantiated at the beginning of every function, destroyed when function execution ends
+  *
+  * tracks all variables created during the function's execution (local variables),
+  * including the function's parameters, and stores the function's return value */
+class TTCN3_Debug_Function {
+  
+  /** name of the function, not owned */
+  const char* function_name;
+  
+  /** the TTCN-3 keyword(s) used to define the function ("function", "testcase",
+    * "altstep", "template" or "external function"), not owned */
+  const char* function_type;
+  
+  /** name of the module this function is defined in, not owned */
+  const char* module_name;
+  
+  /** names of the function's parameters (in the order of their declaration), owned */
+  charstring_list* parameter_names;
+  
+  /** types (directions) of the function's parameters ("in", "inout" or "out"), owned */
+  charstring_list* parameter_types;
+  
+  /** list of local variables tracked by this object, the array elements are owned */
+  Vector<TTCN3_Debugger::variable_t*> variables;
+  
+  /** list of pointers to the scope objects of code blocks in the function,
+    * the elements are not owned 
+    * (currently not used for anything) */
+  Vector<TTCN3_Debug_Scope*> scopes;
+  
+  /** pointer to the global scope object, not owned
+    * (may be NULL, if the module's global scope is empty) */
+  const TTCN3_Debug_Scope* global_scope;
+  
+  /** pointer to the runs-on component's scope object, not owned
+    * (may be NULL, if the component's scope is empty or if the function has no runs-on clause) */
+  const TTCN3_Debug_Scope* component_scope;
+  
+  /** the function's return value (unbound if the return value has not been set yet,
+    * or if the function doesn't return anything)
+    * 
+    * since this is only set right before the function ends, it is only accessible
+    * from the destructor */
+  CHARSTRING return_value;
+  
+public:
+  
+  /** constructor - initializes the instance with the specified parameters,
+    * retrieves the global scope and component scope from the main debugger object */
+  TTCN3_Debug_Function(const char* p_name, const char* p_type, const char* p_module,
+    const charstring_list& p_parameter_names, const charstring_list& p_parameter_types, const char* p_component_name);
+  
+  /** destructor - frees resources and saves the function's ending snapshot
+    * (including the values of 'out' and 'inout' parameters and the return value)
+    * in the main debugger object (only if the debugger is switched on) */
+  ~TTCN3_Debug_Function();
+  
+  //////////////////////////////////////////////////////
+  ////// methods called from TITAN generated code //////
+  //////////////////////////////////////////////////////
+  
+  /** creates, stores and returns the variable entry of the local variable
+    * specified by the parameters (only if the debugger is switched on) */
+  const TTCN3_Debugger::variable_t* add_variable(const void* p_value, const char* p_name,
+    const char* p_type, CHARSTRING (*p_print_function)(const TTCN3_Debugger::variable_t&));
+  
+  /** stores the string representation of the value returned by the function */
+  void set_return_value(const CHARSTRING& p_value);
+  
+  /** saves the function's initial snapshot (including the values on 'in' and
+    * 'inout' parameters) in the main debugger object
+    * (only if the debugger is switched on) */
+  void initial_snapshot() const;
+  
+  //////////////////////////////////////////////////////
+  ////// methods called by other debugger classes //////
+  //////////////////////////////////////////////////////
+  
+  /** adds the specified scope object pointer to the function's scope list */
+  void add_scope(TTCN3_Debug_Scope* p_scope);
+  
+  /** removes the specified scope object pointer from the function's scope list,
+    * if it is the last scope in the list */
+  void remove_scope(TTCN3_Debug_Scope* p_scope);
+  
+  /** removes the specified variable from the variable list */
+  void remove_variable(const TTCN3_Debugger::variable_t* p_var);
+  
+  /** searches for the variable entry with the specified name in the function's
+    * local variable list, the global scope (if any) and the component scope (if any),
+    * returns NULL, if the variable was not found */
+  const TTCN3_Debugger::variable_t* find_variable(const char* p_name) const;
+  
+  /** prints the function's type, name and current values of parameters */
+  void print_function() const;
+  
+  /** returns the name of the module the function was defined in */
+  const char* get_module_name() const { return module_name; }
+  
+  /** prints the names of variables specified by the parameters (separated by spaces)
+    * handles the D_LIST_VARIABLES debugger command 
+    * @param p_scope specifies which scope to print variables from:
+    * - "local" - the function's local variables (including variables in code blocks)
+    * - "global" - variables in the module's global scope
+    * - "comp" or "component" - variables in the function's runs-on component scope
+    * - "all" - all variables visible in the function (i.e. all of the above)
+    * @param p_filter a pattern to filter variable names further */
+  void list_variables(const char* p_scope, const char* p_filter) const;
+};
+
+/** This macro stores a function's return value in the current function.
+  * The returned value might be an expression, so it is copied to a temporary first,
+  * to avoid being evaluated twice. */
+#define DEBUGGER_STORE_RETURN_VALUE(tmp, ret_val) \
+  (tmp = (ret_val), \
+   ttcn3_debugger.set_return_value((TTCN_Logger::begin_event_log2str(), \
+                                    tmp.log(), \
+                                    TTCN_Logger::end_event_log2str())), \
+   tmp)
+
+#endif /* DEBUGGER_HH */
+
index 4d5957bb34b0bf611454ced1bd7206315f9c7aa1..d4fcd8446269a6787a27991ddff8313ad41983c6 100644 (file)
@@ -104,7 +104,7 @@ Module_list.cc Objid.cc Octetstring.cc Parallel_main.cc Port.cc RAW.cc \
 Runtime.cc Single_main.cc Snapshot.cc Struct_of.cc Template.cc TEXT.cc \
 Textbuf.cc Timer.cc Param_Types.cc Universal_charstring.cc \
 Verdicttype.cc XER.cc XmlReader.cc TitanLoggerControlImpl.cc TCov.cc JSON.cc \
-Profiler.cc ProfilerTools.cc ProfMerge_main.cc $(RT2_SOURCES)
+Profiler.cc ProfilerTools.cc ProfMerge_main.cc Debugger.cc $(RT2_SOURCES)
 
 # Keep GENERATED_SOURCES at the beginning. This may speed up parallel builds
 # by starting early the compilation of the largest files.
@@ -186,7 +186,7 @@ Port.hh Event_Handler.hh Struct_of.hh Array.hh Optional.hh Textbuf.hh Encdec.hh
 Module_list.hh Parameters.h Addfunc.hh RAW.hh BER.hh TEXT.hh ASN_Null.hh \
 ASN_Any.hh ASN_External.hh ASN_EmbeddedPDV.hh ASN_CharacterString.hh XER.hh \
 XmlReader.hh cversion.h TitanLoggerControl.ttcn TitanLoggerApi.xsd Vector.hh \
-JSON.hh Profiler.hh RefdIndex.hh ProfilerTools.hh
+JSON.hh Profiler.hh RefdIndex.hh ProfilerTools.hh Debugger.hh
 # Copied during "make install"
 
 ifdef REGEX_DIR
index 087514e831f08a865e3f8ae5ea634abcb7a8de08..27ca3b3b28c021d65d08dec2ca960062654a0d1e 100644 (file)
@@ -2129,6 +2129,12 @@ void TTCN_Runtime::setverdict_internal(verdicttype new_value,
       TTCN_Logger::log_setverdict(new_value, old_verdict, local_verdict);
     else TTCN_Logger::log_setverdict(new_value, old_verdict, local_verdict, reason, reason);
   }
+  if (new_value == FAIL) {
+    ttcn3_debugger.breakpoint_entry(TTCN3_Debugger::SBP_FAIL_VERDICT);
+  }
+  else if (new_value == ERROR) {
+    ttcn3_debugger.breakpoint_entry(TTCN3_Debugger::SBP_ERROR_VERDICT);
+  }
 }
 
 void TTCN_Runtime::set_begin_controlpart_command(const char *new_command)
index 3d7e815d862bb5a2159e50f09a79fa4d0f636fb9..869d3fa8d8f8948d61f50feae032b68536b8dd8a 100644 (file)
@@ -85,5 +85,6 @@
 #include "Error.hh"
 #include "XmlReader.hh"
 #include "Profiler.hh"
+#include "Debugger.hh"
 
 #endif
This page took 0.05648 seconds and 5 git commands to generate.