Debugger - Stage 3 (artf511247)
authorBotond Baranyi <botond.baranyi@ericsson.com>
Wed, 1 Jun 2016 10:29:47 +0000 (12:29 +0200)
committerBotond Baranyi <botond.baranyi@ericsson.com>
Wed, 1 Jun 2016 15:47:57 +0000 (17:47 +0200)
Change-Id: Id1c3432bf8b314655b84ee653959dda2cd299c41
Signed-off-by: Botond Baranyi <botond.baranyi@ericsson.com>
42 files changed:
Makefile
compiler2/DebuggerStuff.cc
compiler2/Value.cc
compiler2/asn1/AST_asn1.cc
compiler2/compiler.1
compiler2/main.cc
compiler2/makefile.c
compiler2/ttcn3/AST_ttcn3.cc
compiler2/ttcn3_makefilegen.1
compiler2/xpather.cc
compiler2/xpather.h
core/Communication.cc
core/Communication.hh
core/DebugCommands.hh
core/Debugger.cc
core/Debugger.hh
core/DebuggerUI.cc [new file with mode: 0644]
core/DebuggerUI.hh [new file with mode: 0644]
core/Logger.cc
core/Logger.hh
core/Makefile
core/Message_types.hh
core/Module_list.cc
core/Param_Types.cc
core/Param_Types.hh
core/Single_main.cc
core/config_process.l
core/config_process.y
etc/xsd/TPD.xsd
function_test/BER_EncDec/Makefile
function_test/RAW_EncDec/Makefile
function_test/Text_EncDec/Makefile
function_test/XER_EncDec/Makefile
mctr2/cli/Cli.cc
mctr2/cli/Cli.h
mctr2/mctr/MainController.cc
mctr2/mctr/MainController.h
mctr2/mctr/UserInterface.cc
mctr2/mctr/UserInterface.h
regression_test/Makefile.regression
regression_test/XML/XMLqualif/Makefile
regression_test/XML/xsdConverter/Makefile.converter

index 357f933afbebc7b0a84c253050bd1bd22770e806..bc81ca306bac3bd4ddcea476b229e65b484bbcaf 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -37,7 +37,7 @@ include $(TOP)/Makefile.cfg
 ALLDIRS := common compiler2 repgen xsdconvert
 
 ifndef MINGW
-       ALLDIRS += core core2 mctr2 loggerplugins
+       ALLDIRS += mctr2 core core2 loggerplugins
 endif
 
 # JNI not supported on Cygwin or Mingw
index c34bd6ebc89b9196123ba9a10995231cd96498f9..f379c09730fc43b848ed30aa1e7b386f79778567 100644 (file)
@@ -110,11 +110,12 @@ string array_dimensions_to_string(Ttcn::ArrayDimensions* p_dims)
   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)
+void calculate_type_name_and_debug_functions_from_type(Type* p_type,
+                                                       Type* p_type_last,
+                                                       Module* p_module,
+                                                       string& p_type_name,
+                                                       string& p_print_function,
+                                                       string& p_set_function)
 {
   if (p_type_last->get_typetype() == Type::T_COMPONENT) {
     p_type_name = "component";
@@ -142,13 +143,17 @@ void calculate_type_name_and_print_function_from_type(Type* p_type,
       p_type_name = p_type_last->get_dispname();
     }
     const Module* var_type_mod = p_type_last->get_my_scope()->get_scope_mod();
+    string module_prefix;
     if (var_type_mod != p_module) {
-      p_print_function = var_type_mod->get_modid().get_name() + "::";
+      module_prefix = var_type_mod->get_modid().get_name() + "::";
     }
-    else {
-      p_print_function.clear();
+    p_print_function = module_prefix + "print_var_" +
+      var_type_mod->get_modid().get_ttcnname();
+    if (p_type_last->get_typetype() != Type::T_SIGNATURE &&
+        p_type_last->get_typetype() != Type::T_PORT) {
+      p_set_function = module_prefix + "set_var_" +
+        var_type_mod->get_modid().get_ttcnname();
     }
-    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
@@ -182,23 +187,34 @@ char* generate_code_debugger_add_var(char* str, Common::Assignment* var_ass,
   }
   
   bool is_lazy_param = false;
+  bool is_constant = 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:
+  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;
+    Ttcn::FormalPar* fpar = dynamic_cast<Ttcn::FormalPar*>(var_ass);
+    is_constant = fpar == NULL || !fpar->get_used_as_lvalue();
+    break; }
+  case Common::Assignment::A_CONST:
+  case Common::Assignment::A_EXT_CONST:
+  case Common::Assignment::A_MODULEPAR:
+  case Common::Assignment::A_MODULEPAR_TEMP:
+  case Common::Assignment::A_TEMPLATE:
+    is_constant = scope_name != NULL;
   default:
     break;
   }
   
-  // recreate the TTCN-3 version of the type name and determine the type's printing function
-  string type_name, print_function;
+  // recreate the TTCN-3 version of the type name and determine the type's 
+  // printing and overwriting functions
+  string type_name, print_function, set_function;
   print_function = is_lazy_param ? "TTCN3_Debugger::print_lazy_param<" :
     "TTCN3_Debugger::print_base_var";
+  set_function = "TTCN3_Debugger::set_base_var";
   if (var_ass->get_asstype() == Common::Assignment::A_TIMER ||
       var_ass->get_asstype() == Common::Assignment::A_PAR_TIMER) {
     type_name = "timer";
@@ -242,8 +258,9 @@ char* generate_code_debugger_add_var(char* str, Common::Assignment* var_ass,
         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);
+      string dummy1, dummy2;
+      calculate_type_name_and_debug_functions_from_type(t, t, current_mod,
+        type_name, dummy1, dummy2);
       type_name += dims_str;
       if (!is_lazy_param) {
         switch (var_ass->get_asstype()) {
@@ -257,20 +274,27 @@ char* generate_code_debugger_add_var(char* str, Common::Assignment* var_ass,
           print_function = string("TTCN3_Debugger::print_template_array<") +
             function_params_for_array_type(var_type, current_mod, true) +
             string(">");
+          set_function = string("TTCN3_Debugger::set_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(">");
+          set_function = string("TTCN3_Debugger::set_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);
+      calculate_type_name_and_debug_functions_from_type(var_ass->get_Type(),
+        var_type, current_mod, type_name, is_lazy_param ? dummy : print_function,
+        set_function);
     }
   }
   
@@ -295,7 +319,7 @@ char* generate_code_debugger_add_var(char* str, Common::Assignment* var_ass,
     print_function += ">";
   }
   
-  return mputprintf(str, "%s%s_scope%sadd_variable(&%s, \"%s\", \"%s\", %s);\n",
+  return mputprintf(str, "%s%s_scope%sadd_variable(&%s, \"%s\", \"%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,
@@ -307,7 +331,9 @@ char* generate_code_debugger_add_var(char* str, Common::Assignment* var_ass,
     // 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
+    print_function.c_str(), // variable printing function
+    is_constant ? "" : ", ", is_constant ? "" : set_function.c_str());
+    // variable overwriting function (not generated for constants)
 }
 
 char* generate_code_debugger_function_init(char* str, Common::Assignment* func_ass)
index af36e4ef7d0c8fc179a41af45b293ec11d2583f4..e6cf738b27d5264e879c4869842a69aa9c9f5f7b 100644 (file)
@@ -11521,11 +11521,9 @@ error:
         break;
       case OPTYPE_DECODE: {
         Ttcn::ActualParList *parlist = u.expr.r1->get_parlist();
-        Common::Assignment *ass = u.expr.r1->get_refd_assignment();
         if (parlist) str = parlist->rearrange_init_code(str, usage_mod);
 
         parlist = u.expr.r2->get_parlist();
-        ass = u.expr.r2->get_refd_assignment();
         if (parlist) str = parlist->rearrange_init_code(str, usage_mod);
         break; }
       case OPTYPE_HOSTID:    
index cd87a28f38d2866c34e67334e9ab120aee811a77..4503ef0dcc9a84f926b09cae669888c6f45df1bf 100644 (file)
@@ -578,7 +578,8 @@ namespace Asn {
   
   void Module::generate_debugger_functions(output_struct *output)
   {
-    char* str = NULL;
+    char* print_str = NULL;
+    char* overwrite_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) {
@@ -594,36 +595,58 @@ namespace Asn {
           // 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, 
+          print_str = mputprintf(print_str, 
             "  %sif (!strcmp(p_var.type_name, \"%s\")) {\n"
-            "    ((const %s*)p_var.value)->log();\n"
+            "    ((const %s*)ptr)->log();\n"
             "  }\n"
             "  else if (!strcmp(p_var.type_name, \"%s template\")) {\n"
-            "    ((const %s_template*)p_var.value)->log();\n"
+            "    ((const %s_template*)ptr)->log();\n"
             "  }\n"
-            , (str != NULL) ? "else " : ""
+            , (print_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());
+          overwrite_str = mputprintf(overwrite_str, 
+            "  %sif (!strcmp(p_var.type_name, \"%s\")) {\n"
+            "    ((%s*)p_var.value)->set_param(p_new_value);\n"
+            "  }\n"
+            "  else if (!strcmp(p_var.type_name, \"%s template\")) {\n"
+            "    ((%s_template*)p_var.value)->set_param(p_new_value);\n"
+            "  }\n"
+            , (overwrite_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) {
+    if (print_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());
+        "/* Debugger printing and overwriting functions for types declared in this module */\n\n"
+        "extern CHARSTRING print_var_%s(const TTCN3_Debugger::variable_t& p_var);\n"
+        "extern boolean set_var_%s(TTCN3_Debugger::variable_t& p_var, Module_Param& p_new_value);\n",
+        get_modid().get_ttcnname().c_str(), 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"
+        "  const void* ptr = p_var.set_function != NULL ? p_var.value : p_var.cvalue;\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);
+        "}\n\n"
+        "/* Debugger overwriting function for types declared in this module */\n"
+        "boolean set_var_%s(TTCN3_Debugger::variable_t& p_var, Module_Param& p_new_value)\n"
+        "{\n"
+        "%s"
+        "  else {\n"
+        "    return FALSE;\n"
+        "  }\n"
+        "  return TRUE;\n"
+        "}\n", get_modid().get_ttcnname().c_str(), print_str,
+        get_modid().get_ttcnname().c_str(), overwrite_str);
     }
   }
 
index d64cc88fbcbea83bc59f522c82734370aa489637..8c0e3467b9d1e86810be556aea974ddad06171a6 100644 (file)
@@ -3,7 +3,7 @@
 compiler \- TTCN-3 and ASN.1 to C++ translator
 .SH SYNOPSIS
 .B compiler
-.RB "[\| " \-abcdEfgijlLMpqrRsStuwxXyY " \|]"
+.RB "[\| " \-abcdEfgijlLMnpqrRsStuwxXyY " \|]"
 .RB "[\| " \-V
 .IR " verb_level" " \|]"
 .RB "[\| " \-K
@@ -167,6 +167,9 @@ operation and the
 .B present
 template restriction accordingly.
 .TP
+.B \-n
+Activates the debugger and generates extra code for storing debug information.
+.TP
 .BI \-o " dir"
 The output files (including Test Port skeletons) will be placed into
 the directory specified by
index 5e6cfd5bc3d2e8c2fa4eae7970a320f619a12edf..f01d98e2e466dc4dbce5c8bd91ab3db43c30645a 100644 (file)
@@ -384,7 +384,7 @@ static boolean is_valid_asn1_filename(const char* file_name)
 static void usage()
 {
   fprintf(stderr, "\n"
-    "usage: %s [-abcdEfgijlLOpqrRsStuwxXyY] [-K file] [-z file] [-V verb_level]\n"
+    "usage: %s [-abcdEfgijlLMnOpqrRsStuwxXyY] [-K file] [-z file] [-V verb_level]\n"
     "  [-o dir] [-U none|type] [-P modulename.top_level_pdu_name] [-Q number] ...\n"
     "  [-T] module.ttcn [-A] module.asn ...\n"
     "  or  %s -v\n"
@@ -405,6 +405,7 @@ static void usage()
     "  -l:             include source line info in C++ code\n"
     "  -L:             add source line info for logging\n"
     "  -M:             allow 'omit' in template value lists (legacy behavior)\n"
+    "  -n:             activate debugger (generates extra code for debugging)\n"
     "  -o dir:         output files will be placed into dir\n"
     "  -p:             parse only (no semantic check or code generation)\n"
     "  -P pduname:     define top-level pdu\n"
@@ -791,7 +792,7 @@ int main(int argc, char *argv[])
         break;
       }
     }
-
+    
     /* Checking incompatible options */
     if (vflag) {
       if (Aflag || Lflag || Pflag || Tflag || Vflag || Yflag ||
index d77f45226ff9027531ba951fb3ff764c16c046ff..0c5639d28e03710aa389e1b8b80485fd6d2e3111 100644 (file)
@@ -262,6 +262,7 @@ struct makefile_struct {
   boolean outparamboundness;
   boolean omit_in_value_list;
   boolean warnings_for_bad_variants;
+  boolean activate_debugger;
   boolean disable_predef_ext_folder;
   struct string_list* solspeclibraries; /* not owned */
   struct string_list* sol8speclibraries; /* not owned */
@@ -327,6 +328,7 @@ static void init_makefile_struct(struct makefile_struct *makefile)
   makefile->outparamboundness = FALSE;
   makefile->omit_in_value_list = FALSE;
   makefile->warnings_for_bad_variants = FALSE;
+  makefile->activate_debugger = FALSE;
   makefile->solspeclibraries = NULL;
   makefile->sol8speclibraries = NULL;
   makefile->linuxspeclibraries = NULL;
@@ -2054,6 +2056,7 @@ static void print_makefile(struct makefile_struct *makefile)
           (makefile->outparamboundness ? " -Y" : ""),
           (makefile->omit_in_value_list ? " -M" : ""),
           (makefile->warnings_for_bad_variants ? " -E" : ""),
+          (makefile->activate_debugger ? " -n" : ""),
           (makefile->tcov_file_name ? makefile->tcov_file_name : ""),
           (makefile->profiled_file_list ? " -z $(PROFILED_FILE_LIST)" : ""),
           /* end of COMPILER FLAGS */
@@ -2939,7 +2942,7 @@ static void print_makefile(struct makefile_struct *makefile)
       "# Platform specific additional libraries:\n"
       "#\n", fp);
 
-    fputs("SOLARIS_LIBS = -lsocket -lnsl -lxml2", fp);
+    fputs("SOLARIS_LIBS = -lsocket -lnsl -lxml2 -lcurses", fp);
 #ifdef USAGE_STATS
     fputs(" -lresolv", fp);
 #endif
@@ -2954,7 +2957,7 @@ static void print_makefile(struct makefile_struct *makefile)
     }
     fputs("\n", fp);
 
-    fputs("SOLARIS8_LIBS = -lsocket -lnsl -lxml2", fp);
+    fputs("SOLARIS8_LIBS = -lsocket -lnsl -lxml2 -lcurses", fp);
 #ifdef USAGE_STATS
     fputs(" -lresolv", fp);
 #endif
@@ -2969,7 +2972,7 @@ static void print_makefile(struct makefile_struct *makefile)
     }
     fputs("\n", fp);
 
-    fputs("LINUX_LIBS = -lxml2", fp);
+    fputs("LINUX_LIBS = -lxml2 -lncurses", fp);
 #ifdef USAGE_STATS
     fputs(" -lpthread -lrt", fp);
 #endif
@@ -2984,7 +2987,7 @@ static void print_makefile(struct makefile_struct *makefile)
     }
     fputs("\n", fp);
 
-    fputs("FREEBSD_LIBS = -lxml2", fp);
+    fputs("FREEBSD_LIBS = -lxml2 -lncurses", fp);
     if (makefile->freebsdspeclibraries) {
       struct string_list* act_elem = makefile->freebsdspeclibraries;
       while (act_elem) {
@@ -2996,7 +2999,7 @@ static void print_makefile(struct makefile_struct *makefile)
     }
     fputs("\n", fp);
 
-    fputs("WIN32_LIBS = -lxml2", fp);
+    fputs("WIN32_LIBS = -lxml2 -lncurses", fp);
     if (makefile->win32speclibraries) {
       struct string_list* act_elem = makefile->win32speclibraries;
       while (act_elem) {
@@ -3816,7 +3819,8 @@ static void generate_makefile(size_t n_arguments, char *arguments[],
   const char* cxxcompiler, const char* optlevel, const char* optflags, boolean disableber, boolean disableraw, boolean disabletext,
   boolean disablexer, boolean disablejson, boolean forcexerinasn, boolean defaultasomit, boolean gccmsgformat,
   boolean linenumbersonlymsg, boolean includesourceinfo, boolean addsourcelineinfo, boolean suppresswarnings,
-  boolean outparamboundness, boolean omit_in_value_list, boolean warnings_for_bad_variants, boolean disable_predef_ext_folder, struct string_list* solspeclibraries,
+  boolean outparamboundness, boolean omit_in_value_list, boolean warnings_for_bad_variants, boolean activate_debugger,
+  boolean disable_predef_ext_folder, struct string_list* solspeclibraries,
   struct string_list* sol8speclibraries, struct string_list* linuxspeclibraries, struct string_list* freebsdspeclibraries,
   struct string_list* win32speclibraries, const char* ttcn3preprocessor, struct string_list* linkerlibraries,
   struct string_list* additionalObjects, struct string_list* linkerlibsearchpath, char* generatorCommandOutput,
@@ -3868,6 +3872,7 @@ static void generate_makefile(size_t n_arguments, char *arguments[],
   makefile.outparamboundness = outparamboundness;
   makefile.omit_in_value_list = omit_in_value_list;
   makefile.warnings_for_bad_variants = warnings_for_bad_variants;
+  makefile.activate_debugger = activate_debugger;
   makefile.disable_predef_ext_folder = disable_predef_ext_folder;
   makefile.solspeclibraries = solspeclibraries;
   makefile.sol8speclibraries = sol8speclibraries;
@@ -4003,7 +4008,7 @@ static void generate_makefile(size_t n_arguments, char *arguments[],
 static void usage(void)
 {
   fprintf(stderr, "\n"
-    "usage: %s [-abc" C_flag "dDEfFglLmMprRstTVwWXZ] [-K file] [-z file ] [-P dir]"
+    "usage: %s [-abc" C_flag "dDEfFglLmMnprRstTVwWXZ] [-K file] [-z file ] [-P dir]"
     " [-U none|type] [-e ets_name] [-o dir|file]\n"
     "        [-t project_descriptor.tpd [-b buildconfig]]\n"
     "        [-O file] ... module_name ... testport_name ...\n"
@@ -4026,6 +4031,7 @@ static void usage(void)
     "  -L:             create makefile with library archive as the default target\n"
     "  -m:             always use makedepend for dependencies\n"
     "  -M:             allow 'omit' in template value lists (legacy behavior)\n"
+    "  -n:             activate debugger (generates extra code for debugging)\n"
     "  -o dir|file:    write the Makefile to the given directory or file\n"
     "  -O file:        add the given file to the Makefile as other file\n"
     "  -p:             generate Makefile with TTCN-3 preprocessing\n"
@@ -4093,7 +4099,7 @@ int main(int argc, char *argv[])
     gfflag = FALSE, lnflag = FALSE, isflag = FALSE, asflag = FALSE,
     swflag = FALSE, Vflag = FALSE, Dflag = FALSE, Wflag = FALSE,
     djflag = FALSE, Zflag = FALSE, Hflag = FALSE, Mflag = FALSE,
-    diflag = FALSE, zflag = FALSE, Eflag = FALSE;
+    diflag = FALSE, zflag = FALSE, Eflag = FALSE, nflag = FALSE;
   boolean error_flag = FALSE;
   char *output_file = NULL;
   char *ets_name = NULL;
@@ -4150,7 +4156,7 @@ int main(int argc, char *argv[])
   }
 
   for ( ; ; ) {
-    int c = getopt(argc, argv, "O:ab:c" C_flag "dDe:EfFgI:K:o:lLmMpP:rRst:TU:vVwWXYz:ZH");
+    int c = getopt(argc, argv, "O:ab:c" C_flag "dDe:EfFgI:K:o:lLmMnpP:rRst:TU:vVwWXYz:ZH");
     if (c == -1) break;
     switch (c) {
     case 'O':
@@ -4225,6 +4231,9 @@ int main(int argc, char *argv[])
     case 'M':
       SET_FLAG(M);
       break;
+    case 'n':
+      SET_FLAG(n);
+      break;
     case 'p':
       SET_FLAG(p);
       break;
@@ -4301,7 +4310,7 @@ int main(int argc, char *argv[])
     if ( aflag || bflag || cflag || Cflag || dflag || eflag || fflag || Fflag || gflag
       || mflag || oflag || lflag || pflag || Pflag || rflag || Rflag || sflag
       || tflag || Tflag || Vflag || wflag || Xflag || Kflag || Dflag || Wflag || Yflag
-      || Zflag || Hflag || Mflag || zflag || Eflag || n_other_files > 0 || n_search_paths > 0)
+      || Zflag || Hflag || Mflag || zflag || Eflag || nflag || n_other_files > 0 || n_search_paths > 0)
       error_flag = TRUE;
   }
 
@@ -4487,7 +4496,7 @@ int main(int argc, char *argv[])
       &Rflag, &lflag, &mflag, &Pflag, &Lflag, rflag, Fflag, Tflag, output_file, &abs_work_dir, sub_project_dirs, program_name, prj_graph_fp,
       create_symlink_list,ttcn3_prep_includes, ttcn3_prep_defines,ttcn3_prep_undefines, prep_includes, prep_defines, prep_undefines, &csflag, 
       &quflag, &dsflag, &cxxcompiler, &optlevel, &optflags, &dbflag, &drflag, &dtflag, &dxflag, &djflag, &fxflag, &doflag, &gfflag, &lnflag, &isflag,
-      &asflag, &swflag, &Yflag, &Mflag, &Eflag, &diflag, solspeclibraries, sol8speclibraries, linuxspeclibraries, freebsdspeclibraries, win32speclibraries, &ttcn3prep,
+      &asflag, &swflag, &Yflag, &Mflag, &Eflag, &nflag, &diflag, solspeclibraries, sol8speclibraries, linuxspeclibraries, freebsdspeclibraries, win32speclibraries, &ttcn3prep,
       linkerlibraries, additionalObjects, linkerlibsearchpath, Vflag, Dflag, &Zflag, &Hflag,
       &generatorCommandOutput, target_placement_list, Wflag, run_command_list, required_configs, &profiled_file_list, search_paths, n_search_paths);
 
@@ -4526,7 +4535,7 @@ int main(int argc, char *argv[])
       Rflag, lflag, mflag, Cflag, code_splitting_mode, tcov_file_name, profiled_file_list,
       Lflag, Zflag, Hflag, rflag ? sub_project_dirs : NULL, ttcn3_prep_includes,
       ttcn3_prep_defines, ttcn3_prep_undefines, prep_includes, prep_defines, prep_undefines, csflag, quflag, dsflag, cxxcompiler, optlevel, optflags, dbflag,
-      drflag, dtflag, dxflag, djflag, fxflag, doflag, gfflag, lnflag, isflag, asflag, swflag, Yflag, Mflag, Eflag, diflag, solspeclibraries,
+      drflag, dtflag, dxflag, djflag, fxflag, doflag, gfflag, lnflag, isflag, asflag, swflag, Yflag, Mflag, Eflag, nflag, diflag, solspeclibraries,
       sol8speclibraries, linuxspeclibraries, freebsdspeclibraries, win32speclibraries, ttcn3prep, linkerlibraries, additionalObjects,
       linkerlibsearchpath, generatorCommandOutput, target_placement_list);
   }
index 85568e2b974cae248384114f4fa2a41d63ffb1a7..6859e36cc11c7b90c97f61bc1d2e7e2f15091a43 100644 (file)
@@ -2912,7 +2912,8 @@ namespace Ttcn {
   
   void Module::generate_debugger_functions(output_struct *output)
   {
-    char* str = NULL;
+    char* print_str = NULL;
+    char* overwrite_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) {
@@ -2920,41 +2921,74 @@ namespace Ttcn {
         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, 
+            print_str = mputprintf(print_str, 
               "  %sif (!strcmp(p_var.type_name, \"%s\")) {\n"
-              "    ((const %s*)p_var.value)->log();\n"
+              "    ((const %s*)ptr)->log();\n"
               "  }\n"
-              , (str != NULL) ? "else " : ""
+              , (print_str != NULL) ? "else " : ""
               , t->get_dispname().c_str(), t->get_genname_value(this).c_str());
+            if (t->get_typetype() != Type::T_PORT) {
+              overwrite_str = mputprintf(overwrite_str,
+                "  %sif (!strcmp(p_var.type_name, \"%s\")) {\n"
+                "    ((%s*)p_var.value)->set_param(p_new_value);\n"
+                "  }\n"
+                , (overwrite_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,
+            print_str = mputprintf(print_str,
               "  %sif (!strcmp(p_var.type_name, \"%s template\")) {\n"
-              "    ((const %s_template*)p_var.value)->log();\n"
+              "    ((const %s_template*)ptr)->log();\n"
               "  }\n"
-              , (str != NULL) ? "else " : ""
+              , (print_str != NULL) ? "else " : ""
               , t->get_dispname().c_str(), t->get_genname_value(this).c_str());
+            if (t->get_typetype() != Type::T_SIGNATURE) {
+              overwrite_str = mputprintf(overwrite_str,
+                "  %sif (!strcmp(p_var.type_name, \"%s template\")) {\n"
+                "    ((%s_template*)p_var.value)->set_param(p_new_value);\n"
+                "  }\n"
+                , (overwrite_str != NULL) ? "else " : ""
+                , t->get_dispname().c_str(), t->get_genname_value(this).c_str());
+            }
           }
         }
       }
     }
-    if (str != NULL) {
+    if (print_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"
+        "/* Debugger printing and overwriting functions 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"
+        "  const void* ptr = p_var.set_function != NULL ? p_var.value : p_var.cvalue;\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);
+        "}\n", get_modid().get_ttcnname().c_str(), print_str);
+    }
+    if (overwrite_str != NULL) {
+      // don't generate an empty overwriting function
+      output->header.class_defs = mputprintf(output->header.class_defs,
+        "extern boolean set_var_%s(TTCN3_Debugger::variable_t& p_var, Module_Param& p_new_value);\n",
+        get_modid().get_ttcnname().c_str());
+      output->source.global_vars = mputprintf(output->source.global_vars,
+        "\n/* Debugger overwriting function for types declared in this module */\n"
+        "boolean set_var_%s(TTCN3_Debugger::variable_t& p_var, Module_Param& p_new_value)\n"
+        "{\n"
+        "%s"
+        "  else {\n"
+        "    return FALSE;\n"
+        "  }\n"
+        "  return TRUE;\n"
+        "}\n", get_modid().get_ttcnname().c_str(), overwrite_str);
     }
   }
 
@@ -7542,6 +7576,9 @@ namespace Ttcn {
         "timer_value);\n");
     body = create_location_object(body, "TESTCASE", dispname_str);
     body = fp_list->generate_shadow_objects(body);
+    if (debugger_active) {
+      body = generate_code_debugger_function_init(body, this);
+    }
     body = mputprintf(body, "try {\n"
       "TTCN_Runtime::begin_testcase(\"%s\", \"%s\", ",
       my_scope->get_scope_mod()->get_modid().get_dispname().c_str(),
@@ -7553,9 +7590,6 @@ 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"
index fb1c164f9c2124eb1840c69e922ef2453619bca2..a99b01d834fc80900bba0a939bb437ef0ca1af17 100644 (file)
@@ -3,7 +3,7 @@
 ttcn3_makefilegen \- Makefile Generator
 .SH SYNOPSIS
 .B ttcn3_makefilegen
-.RB "[\| " \-acdEfglMpRsw " \|]"
+.RB "[\| " \-acdEfglMnpRsw " \|]"
 .RB "[\| " \-e
 .IR " ETS_name" " \|]"
 .RB "[\| " \-o
@@ -126,6 +126,9 @@ operation and the
 .B present
 template restriction accordingly.
 .TP
+.B \-n
+Activates the debugger and generates extra code for storing debug information.
+.TP
 .B \-p
 Generate Makefile with
 .I TTCN-3 preprocessing.
index 59e14e97e5dd1b9562d147b3e3c5a52fa8ae4d1e..2c6cf1e3273b3bce835b5c9f1c4595d73d8281f8 100644 (file)
@@ -633,7 +633,8 @@ static tpd_result process_tpd_internal(const char *p_tpd_name, char* tpdName, co
   struct string_list* prep_defines, struct string_list* prep_undefines, boolean *p_csflag, boolean *p_quflag, boolean* p_dsflag,
   char** cxxcompiler, char** optlevel, char** optflags, boolean* p_dbflag, boolean* p_drflag, boolean* p_dtflag, boolean* p_dxflag,
   boolean* p_djflag, boolean* p_fxflag, boolean* p_doflag, boolean* p_gfflag, boolean* p_lnflag, boolean* p_isflag,
-  boolean* p_asflag, boolean* p_swflag, boolean* p_Yflag, boolean* p_Mflag, boolean *p_Eflag, boolean* p_diflag, struct string_list* solspeclibs, struct string_list* sol8speclibs,
+  boolean* p_asflag, boolean* p_swflag, boolean* p_Yflag, boolean* p_Mflag, boolean *p_Eflag, boolean* p_nflag,
+  boolean* p_diflag, struct string_list* solspeclibs, struct string_list* sol8speclibs,
   struct string_list* linuxspeclibs, struct string_list* freebsdspeclibs, struct string_list* win32speclibs, char** ttcn3prep,
   struct string_list* linkerlibs, struct string_list* additionalObjects, struct string_list* linkerlibsearchp, boolean Vflag, boolean Dflag,
   boolean *p_Zflag, boolean *p_Hflag, char** generatorCommandOutput, struct string2_list* target_placement_list, boolean prefix_workdir, 
@@ -652,7 +653,8 @@ extern "C" tpd_result process_tpd(const char *p_tpd_name, const char *actcfg,
   struct string_list* prep_defines, struct string_list* prep_undefines, boolean *p_csflag, boolean *p_quflag, boolean* p_dsflag,
   char** cxxcompiler, char** optlevel, char** optflags, boolean* p_dbflag, boolean* p_drflag, boolean* p_dtflag, boolean* p_dxflag, 
   boolean* p_djflag, boolean* p_fxflag, boolean* p_doflag, boolean* p_gfflag, boolean* p_lnflag, boolean* p_isflag,
-  boolean* p_asflag, boolean* p_swflag, boolean* p_Yflag, boolean* p_Mflag, boolean* p_Eflag, boolean* p_diflag, struct string_list* solspeclibs, struct string_list* sol8speclibs,
+  boolean* p_asflag, boolean* p_swflag, boolean* p_Yflag, boolean* p_Mflag, boolean* p_Eflag, boolean* p_nflag,
+  boolean* p_diflag, struct string_list* solspeclibs, struct string_list* sol8speclibs,
   struct string_list* linuxspeclibs, struct string_list* freebsdspeclibs, struct string_list* win32speclibs, char** ttcn3prep,
   string_list* linkerlibs, string_list* additionalObjects, string_list* linkerlibsearchp, boolean Vflag, boolean Dflag, boolean *p_Zflag,
   boolean *p_Hflag, char** generatorCommandOutput, struct string2_list* target_placement_list, boolean prefix_workdir,
@@ -675,7 +677,7 @@ extern "C" tpd_result process_tpd(const char *p_tpd_name, const char *actcfg,
       prep_undefines, p_csflag, p_quflag, p_dsflag, cxxcompiler,
       optlevel, optflags, p_dbflag, p_drflag, p_dtflag, p_dxflag, p_djflag,
       p_fxflag, p_doflag, p_gfflag, p_lnflag, p_isflag,
-      p_asflag, p_swflag, p_Yflag, p_Mflag, p_Eflag, p_diflag, solspeclibs, sol8speclibs,
+      p_asflag, p_swflag, p_Yflag, p_Mflag, p_Eflag, p_nflag, p_diflag, solspeclibs, sol8speclibs,
       linuxspeclibs, freebsdspeclibs, win32speclibs, ttcn3prep,
       linkerlibs, additionalObjects, linkerlibsearchp, Vflag, Dflag, p_Zflag,
       p_Hflag, generatorCommandOutput, target_placement_list, prefix_workdir, 
@@ -724,7 +726,8 @@ static tpd_result process_tpd_internal(const char *p_tpd_name, char *tpdName, co
   struct string_list* prep_defines, struct string_list* prep_undefines, boolean *p_csflag, boolean *p_quflag, boolean* p_dsflag,
   char** cxxcompiler, char** optlevel, char** optflags, boolean* p_dbflag, boolean* p_drflag, boolean* p_dtflag, boolean* p_dxflag,
   boolean* p_djflag, boolean* p_fxflag, boolean* p_doflag, boolean* p_gfflag, boolean* p_lnflag, boolean* p_isflag,
-  boolean* p_asflag, boolean* p_swflag, boolean* p_Yflag, boolean* p_Mflag, boolean* p_Eflag, boolean* p_diflag, struct string_list* solspeclibs, struct string_list* sol8speclibs,
+  boolean* p_asflag, boolean* p_swflag, boolean* p_Yflag, boolean* p_Mflag, boolean* p_Eflag, boolean* p_nflag,
+  boolean* p_diflag, struct string_list* solspeclibs, struct string_list* sol8speclibs,
   struct string_list* linuxspeclibs, struct string_list* freebsdspeclibs, struct string_list* win32speclibs, char** ttcn3prep,
   string_list* linkerlibs, string_list* additionalObjects, string_list* linkerlibsearchp, boolean Vflag, boolean Dflag, boolean *p_Zflag,
   boolean *p_Hflag, char** generatorCommandOutput, struct string2_list* target_placement_list, boolean prefix_workdir,
@@ -1245,6 +1248,7 @@ static tpd_result process_tpd_internal(const char *p_tpd_name, char *tpdName, co
   xsdbool2boolean(xpathCtx, actcfg, "forceOldFuncOutParHandling", p_Yflag);
   xsdbool2boolean(xpathCtx, actcfg, "omitInValueList", p_Mflag);
   xsdbool2boolean(xpathCtx, actcfg, "warningsForBadVariants", p_Eflag);
+  xsdbool2boolean(xpathCtx, actcfg, "activateDebugger", p_nflag);
   xsdbool2boolean(xpathCtx, actcfg, "disablePredefinedExternalFolder", p_diflag);
 
   projDesc = projGenHelper.getTargetOfProject(*p_project_name);
@@ -2085,7 +2089,8 @@ static tpd_result process_tpd_internal(const char *p_tpd_name, char *tpdName, co
           my_quflag = 0, my_dsflag = 0, my_dbflag = 0, my_drflag = 0,
           my_dtflag = 0, my_dxflag = 0, my_djflag = 0, my_fxflag = 0, my_doflag = 0, 
           my_gfflag = 0, my_lnflag = 0, my_isflag = 0, my_asflag = 0, 
-          my_swflag = 0, my_Yflag = 0, my_Mflag = *p_Mflag, my_Eflag = 0, my_diflag = *p_diflag;
+          my_swflag = 0, my_Yflag = 0, my_Mflag = *p_Mflag, my_Eflag = 0, my_nflag = *p_nflag,
+          my_diflag = *p_diflag;
 
         char *my_ets = NULL;
         char *my_proj_name = NULL;
@@ -2107,7 +2112,7 @@ static tpd_result process_tpd_internal(const char *p_tpd_name, char *tpdName, co
           prep_includes, prep_defines, prep_undefines, &my_csflag,
           &my_quflag, &my_dsflag, cxxcompiler, optlevel, optflags, &my_dbflag, &my_drflag,
           &my_dtflag, &my_dxflag, &my_djflag, &my_fxflag, &my_doflag,
-          &my_gfflag, &my_lnflag, &my_isflag, &my_asflag, &my_swflag, &my_Yflag, &my_Mflag, &my_Eflag, &my_diflag,
+          &my_gfflag, &my_lnflag, &my_isflag, &my_asflag, &my_swflag, &my_Yflag, &my_Mflag, &my_Eflag, &my_nflag, &my_diflag,
           solspeclibs, sol8speclibs, linuxspeclibs, freebsdspeclibs, win32speclibs,
           ttcn3prep, linkerlibs, additionalObjects, linkerlibsearchp, Vflag, FALSE, &my_Zflag, 
           &my_Hflag, NULL, NULL, prefix_workdir, run_command_list, seen_tpd_files, required_configs, profiled_file_list,
index 8cfd183de4bd217e26b6fe9402ef48cb3d943527..de3f9c91a5e47d6015955781a7abeb2b59376baa 100644 (file)
@@ -197,6 +197,7 @@ boolean buildObjects(const char* projName, boolean add_referenced);
  * @param outparamboundness  outParamBoundness -Y
  * @param omit_in_value_list omitInValueList -M
  * @param warnings_for_bad_variants warningsForBadVariants -E
+ * @param activate_debugger activateDebugger -n
  * @param disable_predef_exp_folder disablePredefinedExternalFolder
  * @param solspeclibs SolarisSpecificLibraries
  * @param sol8speclibs Solaris8SpecificLibraries
@@ -238,7 +239,8 @@ tpd_result process_tpd(const char *p_tpd_name, const char *actcfg,
   char** cxxcompiler, char** optlevel, char** optflags, boolean *disableber, boolean *disableraw, boolean *disabletext, boolean *disablexer,
   boolean *disablejson, boolean *forcexerinasn, boolean *defaultasomit, boolean *gccmessageformat,
   boolean *linenumber, boolean *includesourceinfo, boolean *addsourcelineinfo, boolean *suppresswarnings,
-  boolean *outparamboundness, boolean *omit_in_value_list, boolean *warnings_for_bad_variants, boolean *disable_predef_exp_folder, struct string_list* solspeclibs, struct string_list* sol8speclibs,
+  boolean *outparamboundness, boolean *omit_in_value_list, boolean *warnings_for_bad_variants, boolean *activate_debugger,
+  boolean *disable_predef_exp_folder, struct string_list* solspeclibs, struct string_list* sol8speclibs,
   struct string_list* linuxspeclibs, struct string_list* freebsdspeclibs, struct string_list* win32speclibs,
   char** ttcn3preprocessor, struct string_list* linkerlibs, struct string_list* additionalObjects, struct string_list* linkerlibsearchpath, boolean Vflag, boolean Dflag,
   boolean *Zflag, boolean *Hflag, char** generatorCommandOutput, struct string2_list* target_placement_list, boolean prefix_workdir, struct string2_list* run_command_list,
index db8fb8d35a8da6d9c9636e965a65467647354226..44352b934da5fda883bff31a369032e3957f7b3d 100644 (file)
@@ -8,6 +8,7 @@
  * Contributors:
  *   Baji, Laszlo
  *   Balasko, Jeno
+ *   Baranyi, Botond
  *   Beres, Szabolcs
  *   Delic, Adam
  *   Feher, Csaba
@@ -1158,11 +1159,13 @@ void TTCN_Communication::send_debug_return_value(int return_type, const char* me
   Text_Buf text_buf;
   text_buf.push_int(MSG_DEBUG_RETURN_VALUE);
   text_buf.push_int(return_type);
-  timeval tv;
-  gettimeofday(&tv, NULL);
-  text_buf.push_int(tv.tv_sec);
-  text_buf.push_int(tv.tv_usec);
-  text_buf.push_string(message);
+  if (message != NULL) {
+    timeval tv;
+    gettimeofday(&tv, NULL);
+    text_buf.push_int(tv.tv_sec);
+    text_buf.push_int(tv.tv_usec);
+    text_buf.push_string(message);
+  }
   send_message(text_buf);
 }
 
@@ -1173,6 +1176,21 @@ void TTCN_Communication::send_debug_halt_req()
   send_message(text_buf);
 }
 
+void TTCN_Communication::send_debug_continue_req()
+{
+  Text_Buf text_buf;
+  text_buf.push_int(MSG_DEBUG_CONTINUE_REQ);
+  send_message(text_buf);
+}
+
+void TTCN_Communication::send_debug_batch(const char* batch_file)
+{
+  Text_Buf text_buf;
+  text_buf.push_int(MSG_DEBUG_BATCH);
+  text_buf.push_string(batch_file);
+  send_message(text_buf);
+}
+
 boolean TTCN_Communication::send_log(time_t timestamp_sec, long timestamp_usec,
   unsigned int event_severity, size_t message_text_len,
   const char *message_text)
index 1147128f8e09dc8b72b83e09e9d08e34ab26b0c4..3cf058ebff0e035ffca12d0c5b92b0c201246cbc 100644 (file)
@@ -8,6 +8,7 @@
  * Contributors:
  *   Baji, Laszlo
  *   Balasko, Jeno
+ *   Baranyi, Botond
  *   Beres, Szabolcs
  *   Feher, Csaba
  *   Forstner, Matyas
@@ -149,6 +150,8 @@ public:
   
   static void send_debug_return_value(int return_type, const char* message);
   static void send_debug_halt_req();
+  static void send_debug_continue_req();
+  static void send_debug_batch(const char* batch_file);
 
   /** @brief Send a log message to the MC.
 
index e5d569c559b840ca9bf18d5fd382c8dbd4d4f146..a2ed895dede3199370f9549814e16638a7da260b 100644 (file)
 /** list of commands coming from the user interface to the debugger (parameters listed in comments) */
 
 // settings
-#define D_SWITCH                  1 // 1, "on" or "off"
-#define D_ADD_BREAKPOINT          2 // 2, module name and line number
-#define D_REMOVE_BREAKPOINT       3 // 2, module name and line number
-#define D_SET_ERROR_BEHAVIOR      4 // 1, "yes" or "no"
-#define D_SET_FAIL_BEHAVIOR       5 // 1, "yes" or "no"
-#define D_SET_OUTPUT              6 // 1-2, "console", or "file" or "both" + file name
+#define D_SWITCH                   1 // 1, "on" or "off"
+#define D_SET_BREAKPOINT           2 // 2-3, module name and line number + optional batch file name
+#define D_REMOVE_BREAKPOINT        3 // 1-2, 'all', or module name + 'all' or line number
+#define D_SET_AUTOMATIC_BREAKPOINT 4 // 2-3, "error" or "fail", + "off", or "on" + optional batch file name
+#define D_SET_OUTPUT               5 // 1-2, "console", or "file" or "both", + file name
+#define D_SET_GLOBAL_BATCH_FILE    6 // 1-2, "off", or "on" + batch file name
 // printing and overwriting data
-#define D_SET_COMPONENT           7 // 1, "mtc" or component reference
-#define D_PRINT_CALL_STACK        8 // 0
-#define D_SET_STACK_LEVEL         9 // 1, stack level
-#define D_LIST_VARIABLES         10 // 1-2, "local", "global", "comp" or "all", + optional filter (pattern)
-#define D_PRINT_VARIABLE         11 // 1+, list of variable names
-#define D_OVERWRITE_VARIABLE     12 // 2, variable name, new value (in module parameter syntax)
-#define D_PRINT_SNAPSHOTS        13 // 0
-#define D_SET_SNAPSHOT_BEHAVIOR  14 // TBD
+#define D_SET_COMPONENT            7 // 1, "mtc" or component reference
+#define D_PRINT_CALL_STACK         8 // 0
+#define D_SET_STACK_LEVEL          9 // 1, stack level
+#define D_LIST_VARIABLES          10 // 1-2, "local", "global", "comp" or "all", + optional filter (pattern)
+#define D_PRINT_VARIABLE          11 // 1+, list of variable names
+#define D_OVERWRITE_VARIABLE      12 // 2, variable name, new value (in module parameter syntax)
+#define D_PRINT_SNAPSHOTS         13 // 0
+#define D_SET_SNAPSHOT_BEHAVIOR   14 // TBD
 // stepping
-#define D_STEP_OVER              15 // 0
-#define D_STEP_INTO              16 // 0
-#define D_STEP_OUT               17 // 0
-#define D_RUN_TO_CURSOR          18 // 2, module name and line number
+#define D_STEP_OVER               15 // 0
+#define D_STEP_INTO               16 // 0
+#define D_STEP_OUT                17 // 0
+#define D_RUN_TO_CURSOR           18 // 2, module name and line number
 // the halted state
-#define D_HALT                   19 // 0
-#define D_CONTINUE               20 // 0
-#define D_EXIT                   21 // 1, "test" or "all"
-// batch files
-#define D_BATCH                  22 // 1, batch file name
-#define D_SET_HALTING_BATCH_FILE 23 // 1-2, "no", or "yes" + batch file name
+#define D_HALT                    19 // 0
+#define D_CONTINUE                20 // 0
+#define D_EXIT                    21 // 1, "test" or "all"
 // initialization
-#define D_SETUP                  24 // 5+, arguments for D_SWITCH, D_SET_OUTPUT, D_ERROR_BEHAVIOR, D_FAIL_BEHAVIOR + any number of D_ADD_BREAKPOINT arguments
+#define D_SETUP                   22 // 9+:
+                                     // 1 argument for D_SWITCH,
+                                     // 2 arguments for D_SET_OUTPUT,
+                                     // 2 arguments (2nd and 3rd) for D_SET_AUTOMATIC_BREAKPOINT, where the first argument is "error",
+                                     // 2 arguments (2nd and 3rd) for D_SET_AUTOMATIC_BREAKPOINT, where the first argument is "fail",
+                                     // 2 arguments for D_SET_GLOBAL_BATCH_FILE,
+                                     // + arguments for any number of D_SET_BREAKPOINT commands (optional)
 
-#define D_ERROR                   0 // any
+#define D_ERROR                    0 // any
 
 /** names of commands in the user interface */
 
 #define D_SWITCH_TEXT "debug"
-#define D_ADD_BREAKPOINT_TEXT "daddbp"
+#define D_SET_BREAKPOINT_TEXT "dsetbp"
 #define D_REMOVE_BREAKPOINT_TEXT "drembp"
-#define D_SET_ERROR_BEHAVIOR_TEXT "derrcfg"
-#define D_SET_FAIL_BEHAVIOR_TEXT "dfailcfg"
+#define D_SET_AUTOMATIC_BREAKPOINT_TEXT "dautobp"
 #define D_SET_OUTPUT_TEXT "doutput"
+#define D_SET_GLOBAL_BATCH_FILE_TEXT "dglobbatch"
 #define D_SET_COMPONENT_TEXT "dcomp"
 #define D_PRINT_CALL_STACK_TEXT "dprintstack"
 #define D_SET_STACK_LEVEL_TEXT "dstacklevel"
@@ -72,8 +75,6 @@
 #define D_HALT_TEXT "dhalt"
 #define D_CONTINUE_TEXT "dcont"
 #define D_EXIT_TEXT "dexit"
-#define D_BATCH_TEXT "dbatch"
-#define D_SET_HALTING_BATCH_FILE_TEXT "dbatchcfg"
 
 /** debugger return value types */
 
index a5331cab477ff40bf6f13f508a70a23dc718a19e..5d5642508fbef1dd0c98f54c9d7156d2b788a703 100644 (file)
@@ -15,6 +15,8 @@
 #include "DebugCommands.hh"
 #include "Communication.hh"
 #include "../common/pattern.hh"
+#include "Param_Types.hh"
+#include "DebuggerUI.hh"
 #include <unistd.h>
 #include <pwd.h>
 
@@ -45,82 +47,214 @@ void TTCN3_Debugger::switch_state(const char* p_state_str)
     }
   }
   else {
-    print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'yes' or 'no'.");
+    print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'on' or 'off'.");
   }
 }
 
-void TTCN3_Debugger::add_breakpoint(const char* p_module, int p_line /*const char* batch_file*/)
+void TTCN3_Debugger::set_breakpoint(const char* p_module, int p_line,
+                                    const char* batch_file)
 {
-  if (find_breakpoint(p_module, p_line) == breakpoints.size()) {
+  size_t pos = find_breakpoint(p_module, p_line);
+  if (pos == breakpoints.size()) {
     breakpoint_t bp;
     bp.module = mcopystr(p_module);
     bp.line = p_line;
+    bp.batch_file = batch_file != NULL ? mcopystr(batch_file) : NULL;
     breakpoints.push_back(bp);
-    print(DRET_SETTING_CHANGE, "Breakpoint added in module '%s' at line %d.",
-      p_module, p_line);
+    print(DRET_SETTING_CHANGE, "Breakpoint added in module '%s' at line %d%s%s%s.",
+      p_module, p_line,
+      batch_file != NULL ? " with batch file '" : " with no batch file",
+      batch_file != NULL ? batch_file : "", batch_file != NULL ? "'" : "");
   }
   else {
-    print(DRET_NOTIFICATION, "Breakpoint already set in module '%s' at line %d.",
-      p_module, p_line);
+    if (breakpoints[pos].batch_file != NULL) {
+      if (batch_file != NULL) {
+        if (!strcmp(batch_file, breakpoints[pos].batch_file)) {
+          print(DRET_NOTIFICATION, "Breakpoint already set in module '%s' at "
+            "line %d with batch file '%s'.",
+            p_module, p_line, batch_file);
+        }
+        else {
+          print(DRET_SETTING_CHANGE, "Batch file was changed from '%s' to '%s' for "
+            "breakpoint in module '%s' at line %d.", breakpoints[pos].batch_file,
+            batch_file, p_module, p_line);
+        }
+      }
+      else {
+        print(DRET_SETTING_CHANGE, "Batch file '%s' removed from breakpoint in "
+          "module '%s' at line %d.", breakpoints[pos].batch_file, p_module, p_line);
+      }
+      Free(breakpoints[pos].batch_file);
+    }
+    else {
+      if (batch_file != NULL) {
+        print(DRET_SETTING_CHANGE, "Batch file '%s' added to breakpoint in module "
+          "'%s' at line %d.", batch_file, p_module, p_line);
+      }
+      else {
+        print(DRET_NOTIFICATION, "Breakpoint already set in module '%s' at line "
+          "%d with no batch file.", p_module, p_line);
+      }
+    }
+    breakpoints[pos].batch_file = batch_file != NULL ? mcopystr(batch_file) : NULL;
   }
 }
 
-void TTCN3_Debugger::remove_breakpoint(const char* p_module, int p_line)
+void TTCN3_Debugger::remove_breakpoint(const char* p_module, const char* p_line)
 {
-  size_t pos = find_breakpoint(p_module, p_line);
-  if (pos != breakpoints.size()) {
-    Free(breakpoints[pos].module);
-    breakpoints.erase_at(pos);
-    print(DRET_SETTING_CHANGE, "Breakpoint removed in module '%s' from line %d.",
-      p_module, p_line);
+  bool all_breakpoints = !strcmp(p_module, "all");
+  if (p_line != NULL) {
+    if (!strcmp(p_line, "all")) {
+      bool found = false;
+      for (size_t i = breakpoints.size(); i > 0; --i) {
+        if (!strcmp(breakpoints[i - 1].module, p_module)) {
+          found = true;
+          Free(breakpoints[i - 1].module);
+          Free(breakpoints[i - 1].batch_file);
+          breakpoints.erase_at(i - 1);
+        }
+      }
+      if (found) {
+        print(DRET_SETTING_CHANGE, "Removed all breakpoints in module '%s'.", p_module);
+      }
+      else {
+        print(DRET_NOTIFICATION, "No breakpoints found in module '%s'.", p_module);
+      }
+      return;
+    }
+    else {
+      if (all_breakpoints) {
+        print(DRET_NOTIFICATION, "Unexpected 2nd argument, when the first "
+          "argument is 'all'.");
+        return;
+      }
+      size_t len = strlen(p_line);
+      for (size_t i = 0; i < len; ++i) {
+        if (p_line[i] < '0' || p_line[i] > '9') {
+          print(DRET_NOTIFICATION, "Argument 2 is invalid. Expected 'all' or "
+            "integer value (line number).");
+          return;
+        }
+      }
+      long line = strtol(p_line, NULL, 10);
+      size_t pos = find_breakpoint(p_module, line);
+      if (pos != breakpoints.size()) {
+        Free(breakpoints[pos].module);
+        Free(breakpoints[pos].batch_file);
+        breakpoints.erase_at(pos);
+        print(DRET_SETTING_CHANGE, "Breakpoint removed in module '%s' from line %d.",
+          p_module, line);
+      }
+      else {
+        print(DRET_NOTIFICATION, "No breakpoint found in module '%s' at line %d.",
+          p_module, line);
+      }
+      return;
+    }
+  }
+  else if (!all_breakpoints) {
+    print(DRET_NOTIFICATION, "2 arguments expected, when the first argument is "
+      "not 'all'.");
+    return;
+  }
+  // delete all breakpoints if we got this far
+  if (breakpoints.empty()) {
+    print(DRET_NOTIFICATION, "No breakpoints found.");
   }
   else {
-    print(DRET_NOTIFICATION, "No breakpoint found in module '%s' at line %d.",
-      p_module, p_line);
+    for (size_t i = 0; i < breakpoints.size(); ++i) {
+      Free(breakpoints[i].module);
+      Free(breakpoints[i].batch_file);
+    }
+    breakpoints.clear();
+    print(DRET_SETTING_CHANGE, "Removed all breakpoints.");
   }
 }
 
-void TTCN3_Debugger::set_special_breakpoint(special_breakpoint_t p_type, const char* p_state_str)
+void TTCN3_Debugger::set_automatic_breakpoint(const char* p_event_str,
+                                              const char* p_state_str,
+                                              const char* p_batch_file)
 {
   bool new_state;
-  if (!strcmp(p_state_str, "yes")) {
+  if (!strcmp(p_state_str, "on")) {
     new_state = true;
   }
-  else if(!strcmp(p_state_str, "no")) {
+  else if(!strcmp(p_state_str, "off")) {
     new_state = false;
   }
-  // else if "batch"
   else {
-    print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'yes' or 'no'.");
+    print(DRET_NOTIFICATION, "Argument 2 is invalid. Expected 'on' or 'off'.");
     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
+  char** old_batch_file_ptr;
+  if (!strcmp(p_event_str, "fail")) {
+    state_changed = (fail_behavior.trigger != new_state);
+    fail_behavior.trigger = new_state;
+    old_batch_file_ptr = &fail_behavior.batch_file;
+    sbp_type_str = "fail verdict";
+  }
+  else if (!strcmp(p_event_str, "error")) {
+    state_changed = (error_behavior.trigger != new_state);
+    error_behavior.trigger = new_state;
+    old_batch_file_ptr = &error_behavior.batch_file;
+    sbp_type_str = "error verdict";
+  }
+  else {
+    print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'error' or 'fail'.");
     return;
   }
-  print(DRET_SETTING_CHANGE, "%s verdict behavior %sset to %s.", sbp_type_str,
-    state_changed ? "" : "was already ",
-    new_state ? "halt test execution" : "do nothing");
+  if (state_changed) {
+    print(DRET_SETTING_CHANGE, "Automatic breakpoint at %s switched %s%s%s%s.",
+      sbp_type_str, new_state ? "on" : "off",
+      new_state ? (p_batch_file != NULL ? " with batch file '" : " with no batch file") : "",
+      (p_batch_file != NULL && new_state) ? p_batch_file : "",
+      (p_batch_file != NULL && new_state) ? "'" : "");
+  }
+  else {
+    if (new_state) {
+      if (*old_batch_file_ptr != NULL) {
+        if (p_batch_file != NULL) {
+          if (!strcmp(p_batch_file, *old_batch_file_ptr)) {
+            print(DRET_NOTIFICATION, "Automatic breakpoint at %s was already "
+              "switched on with batch file '%s'.", sbp_type_str, p_batch_file);
+          }
+          else {
+            print(DRET_SETTING_CHANGE, "Batch file was changed from '%s' to '%s' "
+              "for automatic breakpoint at %s.", *old_batch_file_ptr, p_batch_file,
+              sbp_type_str);
+          }
+        }
+        else {
+          print(DRET_SETTING_CHANGE, "Batch file '%s' removed from automatic "
+            "breakpoint at %s.", *old_batch_file_ptr, sbp_type_str);
+        }
+      }
+      else {
+        if (p_batch_file != NULL) {
+          print(DRET_SETTING_CHANGE, "Batch file '%s' added to automatic breakpoint "
+            "at %s.", p_batch_file, sbp_type_str);
+        }
+        else {
+          print(DRET_NOTIFICATION, "Automatic breakpoint at %s was already "
+              "switched on with no batch file.", sbp_type_str);
+        }
+      }
+    }
+    else {
+      print(DRET_NOTIFICATION, "Automatic breakpoint at %s was already switched off.");
+    }
+  }
+  Free(*old_batch_file_ptr);
+  *old_batch_file_ptr = p_batch_file != NULL ? mcopystr(p_batch_file) : NULL;
 }
 
 void TTCN3_Debugger::print_call_stack()
 {
   for (size_t i = call_stack.size(); i != 0; --i) {
     add_to_result("%d.\t", (int)call_stack.size() - (int)i + 1);
-    call_stack[i - 1]->print_function();
+    call_stack[i - 1].function->print_function();
     if (i != 1) {
       add_to_result("\n");
     }
@@ -138,15 +272,69 @@ void TTCN3_Debugger::set_stack_level(int new_level)
   }
   else {
     stack_level = (int)call_stack.size() - new_level;
-    call_stack[stack_level]->print_function();
+    call_stack[stack_level].function->print_function();
     print(DRET_NOTIFICATION, "Stack level set to:\n%d.\t%s", new_level, command_result);
+    Free(command_result);
+    command_result = NULL;
+  }
+}
+
+#define STACK_LEVEL (stack_level >= 0) ? (size_t)stack_level : (call_stack.size() - 1)
+
+void TTCN3_Debugger::print_variable(const char* p_var_name)
+{
+  const variable_t* var = call_stack[STACK_LEVEL].function->find_variable(p_var_name);
+  if (var != NULL) {
+    add_to_result("[%s] %s := %s", var->type_name, var->name,
+      (const char*)var->print_function(*var));
+  }
+  else {
+    add_to_result("Variable '%s' not found.", p_var_name);
   }
 }
 
-void TTCN3_Debugger::print_variable(const TTCN3_Debugger::variable_t* p_var)
+void TTCN3_Debugger::overwrite_variable(const char* p_var_name,
+                                        int p_value_element_count,
+                                        char** p_value_elements)
 {
-  add_to_result("[%s] %s := %s", p_var->type_name, p_var->name,
-    (const char*)p_var->print_function(*p_var));
+  variable_t* var = call_stack[STACK_LEVEL].function->find_variable(p_var_name);
+  if (var != NULL) {
+    if (var->set_function == NULL) {
+      print(DRET_NOTIFICATION, "Constant variables cannot be overwritten.");
+    }
+    else {
+      char* new_value_str = NULL;
+      for (int i = 0; i < p_value_element_count; ++i) {
+        if (i != 0) {
+          new_value_str = mputc(new_value_str, ' ');
+        }
+        new_value_str = mputstr(new_value_str, p_value_elements[i]);
+      }
+      Module_Param* parsed_mp = process_config_debugger_value(new_value_str);
+      // an error message has already been displayed if parsed_mp is NULL
+      if (parsed_mp != NULL) {
+        try {
+          Debugger_Value_Parsing debug_value_parsing;
+          boolean handled = var->set_function(*var, *parsed_mp);
+          if (!handled) {
+            print(DRET_NOTIFICATION, "Variables of type '%s' cannot be overwritten.",
+              var->type_name);
+          }
+          else {
+            add_to_result("[%s] %s := %s", var->type_name, var->name,
+              (const char*)var->print_function(*var));
+          }
+        }
+        catch (const TC_Error&) {
+          // do nothing, an error message has already been displayed in this case
+        }
+        delete parsed_mp;
+      }
+    }
+  }
+  else {
+    print(DRET_NOTIFICATION, "Variable '%s' not found.", p_var_name);
+  }
 }
 
 void TTCN3_Debugger::set_output(const char* p_output_type, const char* p_file_name)
@@ -182,18 +370,18 @@ void TTCN3_Debugger::set_output(const char* p_output_type, const char* p_file_na
       same_file = true;
     }
     else if (!TTCN_Runtime::is_hc()) {
-        // don't open any files on HCs, just store settings for future PTCs
+      // don't open any files on HCs, just store settings for future PTCs
       final_file_name = finalize_file_name(p_file_name);
-      new_fp = fopen(final_file_name, "w");
+      new_fp = fopen(final_file_name, TTCN_Runtime::is_mtc() ? "w" : "a");
       if (new_fp == NULL) {
         print(DRET_NOTIFICATION, "Failed to open file '%s' for writing.", final_file_name);
+        Free(final_file_name);
         return;
       }
     }
   }
   // print the change notification to the old output
-  char* file_str = file ? mprintf("file '%s'", TTCN_Runtime::is_hc() ? p_file_name
-    : final_file_name) : NULL;
+  char* file_str = file ? mprintf("file '%s'", final_file_name) : NULL;
   Free(final_file_name);
   print(DRET_SETTING_CHANGE, "Debugger set to print its output to %s%s%s.",
     console ? "the console" : "", (console && file) ? " and to " : "",
@@ -214,13 +402,124 @@ void TTCN3_Debugger::set_output(const char* p_output_type, const char* p_file_na
   }
 }
 
-void TTCN3_Debugger::halt()
+void TTCN3_Debugger::set_global_batch_file(const char* p_state_str,
+                                           const char* p_file_name)
+{
+  bool delete_old = false;
+  bool copy_new = false;
+  if (!strcmp(p_state_str, "on")) {
+    if (p_file_name != NULL) {
+      if (global_batch_file != NULL) {
+        if (!strcmp(p_file_name, global_batch_file)) {
+          print(DRET_NOTIFICATION, "Global batch file was already switched on "
+            "and set to '%s'.", p_file_name);
+        }
+        else {
+          print(DRET_SETTING_CHANGE, "Global batch file changed from '%s' to '%s'.",
+            global_batch_file, p_file_name);
+          delete_old = true;
+          copy_new = true;
+        }
+      }
+      else {
+        print(DRET_SETTING_CHANGE, "Global batch file switched on and set to '%s'.",
+          p_file_name);
+        copy_new = true;
+      }
+    }
+    else {
+      print(DRET_NOTIFICATION, "Missing batch file name argument.");
+    }
+  }
+  else if (!strcmp(p_state_str, "off")) {
+    if (global_batch_file != NULL) {
+      print(DRET_SETTING_CHANGE, "Global batch file switched off.");
+      delete_old = true;
+    }
+    else {
+      print(DRET_NOTIFICATION, "Global batch file was already switched off.");
+    }
+  }
+  else {
+    print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'on' or 'off'.");
+  }
+  if (delete_old) {
+    Free(global_batch_file);
+    global_batch_file = NULL;
+  }
+  if (copy_new) {
+    global_batch_file = mcopystr(p_file_name);
+  }
+}
+
+void TTCN3_Debugger::step(stepping_t p_stepping_type)
+{
+  if (!halted) {
+    print(DRET_NOTIFICATION, "Stepping commands can only be used when test "
+      "execution is halted.");
+    return;
+  }
+  stepping_type = p_stepping_type;
+  stepping_stack_size = call_stack.size();
+  if (!TTCN_Runtime::is_single()) {
+    TTCN_Communication::send_debug_continue_req();
+  }
+  resume();
+}
+
+void TTCN3_Debugger::run_to_cursor(const char* p_module, int p_line)
+{
+  // all processes receive this command, since the specified location might be
+  // reached in any process, even a PTC that hasn't been created yet
+  if (!halted) {
+    print(DRET_NOTIFICATION, "The 'run to' command can only be used when test "
+      "execution is halted.");
+    return;
+  }
+  temporary_breakpoint.module = mcopystr(p_module);
+  temporary_breakpoint.line = p_line;
+  resume();
+}
+
+void TTCN3_Debugger::halt(const char* p_batch_file, bool p_run_global_batch)
 {
   if (!halted) {
     halted = true;
-    stack_level = call_stack.size() - 1;
-    print(DRET_NOTIFICATION, "Test execution halted.");
-    TTCN_Communication::process_debug_messages();
+    Free(temporary_breakpoint.module);
+    temporary_breakpoint.module = NULL;
+    temporary_breakpoint.line = 0;
+    if (!TTCN_Runtime::is_hc()) {
+      stepping_type = NOT_STEPPING;
+      stack_level = call_stack.size() - 1;
+      print(DRET_NOTIFICATION, "Test execution halted.");
+      if (p_batch_file != NULL) {
+        if (TTCN_Runtime::is_single()) {
+          TTCN_Debugger_UI::execute_batch_file(p_batch_file);
+        }
+        else {
+          TTCN_Communication::send_debug_batch(p_batch_file);
+        }
+      }
+      else if (p_run_global_batch && global_batch_file != NULL) {
+        if (TTCN_Runtime::is_single()) {
+          TTCN_Debugger_UI::execute_batch_file(global_batch_file);
+        }
+        else {
+          TTCN_Communication::send_debug_batch(global_batch_file);
+        }
+      }
+      if (TTCN_Runtime::is_single()) {
+        if (halted && !halt_at_start) {
+          resume();
+        }
+        else {
+          TTCN_Debugger_UI::read_loop();
+        }
+      }
+      else {
+        TTCN_Communication::process_debug_messages();
+      }
+    }
   }
   else {
     print(DRET_NOTIFICATION, "Test execution is already halted.");
@@ -241,21 +540,22 @@ void TTCN3_Debugger::resume()
 
 void TTCN3_Debugger::exit_(const char* p_what)
 {
-  bool exit_all;
   if (!strcmp(p_what, "test")) {
-    exit_all = false;
+    exiting = false;
   }
   else if (!strcmp(p_what, "all")) {
-    exit_all = true;
+    exiting = true;
   }
   else {
     print(DRET_NOTIFICATION, "Argument 1 is invalid. Expected 'test' or 'all'.");
     return;
   }
   halted = false;
-  print((exit_all && TTCN_Runtime::is_mtc()) ? DRET_EXIT_ALL : DRET_NOTIFICATION,
-    "Exiting %s.", exit_all ? "test execution" : "current test");
-  TTCN_Runtime::stop_execution();
+  if (!TTCN_Runtime::is_hc()) {
+    print((exiting && TTCN_Runtime::is_mtc()) ? DRET_EXIT_ALL : DRET_NOTIFICATION,
+      "Exiting %s.", exiting ? "test execution" : "current test");
+    TTCN_Runtime::stop_execution();
+  }
 }
 
 size_t TTCN3_Debugger::find_breakpoint(const char* p_module, int p_line) const
@@ -343,15 +643,58 @@ char* TTCN3_Debugger::finalize_file_name(const char* p_file_name_skeleton)
   return ret_val;
 }
 
+void TTCN3_Debugger::test_execution_started()
+{
+  Free(snapshots);
+  snapshots = NULL;
+  exiting = false;
+  if (TTCN_Runtime::is_single()) {
+    TTCN_Debugger_UI::init();
+    if (initial_batch_file) {
+      halt(initial_batch_file, false);
+    }
+    else if (halt_at_start) {
+      halt(NULL, false);
+    }
+  }
+  halt_at_start = false;
+}
+
+void TTCN3_Debugger::test_execution_finished()
+{
+  stepping_type = NOT_STEPPING;
+  Free(temporary_breakpoint.module);
+  temporary_breakpoint.module = NULL;
+  temporary_breakpoint.line = 0;
+  last_breakpoint_entry.module = NULL;
+  last_breakpoint_entry.line = 0;
+  if (TTCN_Runtime::is_single()) {
+    TTCN_Debugger_UI::clean_up();
+  }
+}
+
 void TTCN3_Debugger::print(int return_type, const char* fmt, ...) const
 {
+  if (TTCN_Runtime::is_hc()) {
+    // don't display anything while on the HC process
+    return;
+  }
   va_list parameters;
   va_start(parameters, fmt);
   char* str = mprintf_va_list(fmt, parameters);
   va_end(parameters);
-  TTCN_Communication::send_debug_return_value(return_type, send_to_console ? str : NULL);
+  if (TTCN_Runtime::is_single()) {
+    if (send_to_console) {
+      TTCN_Debugger_UI::print(str);
+    }
+  }
+  else {
+    TTCN_Communication::send_debug_return_value(return_type, send_to_console ? str : NULL);
+  }
   if (output_file != NULL) {
-    fprintf(output_file, "%s\n", str);
+    fseek(output_file, 0, SEEK_END); // in case multiple processes are writing the same file
+    fputs(str, output_file);
+    fputc('\n', output_file);
     fflush(output_file);
   }
   Free(str);
@@ -368,10 +711,23 @@ TTCN3_Debugger::TTCN3_Debugger()
   snapshots = NULL;
   last_breakpoint_entry.module = NULL;
   last_breakpoint_entry.line = 0;
+  last_breakpoint_entry.batch_file = NULL; // not used
   stack_level = -1;
-  fail_behavior = false;
-  error_behavior = false;
+  fail_behavior.trigger = false;
+  fail_behavior.batch_file = NULL;
+  error_behavior.trigger = false;
+  error_behavior.batch_file = NULL;
+  global_batch_file = NULL;
   command_result = NULL;
+  last_variable_list = NULL;
+  stepping_type = NOT_STEPPING;
+  stepping_stack_size = 0;
+  temporary_breakpoint.module = NULL;
+  temporary_breakpoint.line = 0;
+  temporary_breakpoint.batch_file = NULL; // not used
+  exiting = false;
+  halt_at_start = false;
+  initial_batch_file = NULL;
 }
 
 TTCN3_Debugger::~TTCN3_Debugger()
@@ -382,6 +738,7 @@ TTCN3_Debugger::~TTCN3_Debugger()
   }
   for (size_t i = 0; i < breakpoints.size(); ++i) {
     Free(breakpoints[i].module);
+    Free(breakpoints[i].batch_file);
   }
   for (size_t i = 0; i < global_scopes.size(); ++i) {
     delete global_scopes[i].scope;
@@ -392,8 +749,11 @@ TTCN3_Debugger::~TTCN3_Debugger()
   for (size_t i = 0; i < variables.size(); ++i) {
     delete variables[i];
   }
+  Free(fail_behavior.batch_file);
+  Free(error_behavior.batch_file);
+  Free(global_batch_file);
   Free(snapshots);
-  Free(command_result);
+  Free(last_variable_list);
 }
 
 TTCN3_Debug_Scope* TTCN3_Debugger::add_global_scope(const char* p_module)
@@ -417,42 +777,69 @@ TTCN3_Debug_Scope* TTCN3_Debugger::add_component_scope(const char* p_component)
 void TTCN3_Debugger::set_return_value(const CHARSTRING& p_value)
 {
   if (active && !call_stack.empty()) {
-    call_stack[call_stack.size() - 1]->set_return_value(p_value);
+    call_stack[call_stack.size() - 1].function->set_return_value(p_value);
   }
 }
 
-void TTCN3_Debugger::breakpoint_entry(int p_line /*bool p_stepping_helper*/)
+void TTCN3_Debugger::breakpoint_entry(int p_line)
 {
   if (active && !call_stack.empty()) {
-    const char* module_name = call_stack[call_stack.size() - 1]->get_module_name();
+    const char* module_name = call_stack[call_stack.size() - 1].function->get_module_name();
     bool trigger = false;
     const char* trigger_type;
     int actual_line;
+    const char* batch_file = NULL;
     switch (p_line) {
     case SBP_FAIL_VERDICT:
-      trigger = fail_behavior;
-      trigger_type = "Automatic breakpoint (fail verdict)";
-      actual_line = last_breakpoint_entry.line;
+      trigger = fail_behavior.trigger;
+      trigger_type = "Automatic breakpoint (fail verdict) reached at";
+      actual_line = TTCN_Location::get_line_number();
+      batch_file = fail_behavior.batch_file;
       break;
     case SBP_ERROR_VERDICT:
-      trigger = error_behavior;
-      trigger_type = "Automatic breakpoint (error verdict)";
-      actual_line = last_breakpoint_entry.line;
+      trigger = error_behavior.trigger;
+      trigger_type = "Automatic breakpoint (error verdict) reached at";
+      actual_line = TTCN_Location::get_line_number();
+      batch_file = error_behavior.batch_file;
       break;
     default: // code lines
-      // make sure it's not the same breakpoint entry as last time
-      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 = "User breakpoint";
+      // first: make sure it's not the same breakpoint entry as last time
+      if (p_line == last_breakpoint_entry.line &&
+          module_name == last_breakpoint_entry.module) {
+        break;
+      }
       actual_line = p_line;
+      // second: check if a stepping operation ends here
+      if (stepping_type == STEP_INTO ||
+          (stepping_type == STEP_OVER && call_stack.size() <= stepping_stack_size) ||
+          (stepping_type == STEP_OUT && call_stack.size() < stepping_stack_size)) {
+        trigger = true;
+        trigger_type = "Stepped to";
+        break;
+      }
+      // third: check if this is the destination of a 'run to cursor' operation
+      if (p_line == temporary_breakpoint.line &&
+          !strcmp(module_name, temporary_breakpoint.module)) {
+        trigger = true;
+        trigger_type = "Ran to";
+        break;
+      }
+      // fourth: check if the location matches one of the breakpoints in the list
+      size_t pos = find_breakpoint(module_name, p_line);
+      if (pos != breakpoints.size()) {
+        trigger = true;
+        batch_file = breakpoints[pos].batch_file;
+      }
+      trigger_type = "User breakpoint reached at";
       break;
     }
     if (trigger) {
-      print(DRET_NOTIFICATION, "%s reached in module '%s' at line %d.",
-        trigger_type, module_name, actual_line);
-      TTCN_Communication::send_debug_halt_req();
-      halt();
+      print(DRET_NOTIFICATION, "%s line %d in module '%s'.",
+        trigger_type, actual_line, module_name);
+      if (!TTCN_Runtime::is_single()) {
+        TTCN_Communication::send_debug_halt_req();
+      }
+      halt(batch_file, true);
     }
     last_breakpoint_entry.module = (char*)module_name;
     last_breakpoint_entry.line = p_line;
@@ -461,105 +848,106 @@ void TTCN3_Debugger::breakpoint_entry(int p_line /*bool p_stepping_helper*/)
 
 CHARSTRING TTCN3_Debugger::print_base_var(const TTCN3_Debugger::variable_t& p_var)
 {
+  const void* ptr = p_var.set_function != NULL ? p_var.value : p_var.cvalue;
   TTCN_Logger::begin_event_log2str();
   if (!strcmp(p_var.type_name, "bitstring")) {
-    ((const BITSTRING*)p_var.value)->log();
+    ((const BITSTRING*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "bitstring template")) {
-    ((const BITSTRING_template*)p_var.value)->log();
+    ((const BITSTRING_template*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "boolean")) {
-    ((const BOOLEAN*)p_var.value)->log();
+    ((const BOOLEAN*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "boolean template")) {
-    ((const BOOLEAN_template*)p_var.value)->log();
+    ((const BOOLEAN_template*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "charstring")) {
-    ((const CHARSTRING*)p_var.value)->log();
+    ((const CHARSTRING*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "charstring template")) {
-    ((const CHARSTRING_template*)p_var.value)->log();
+    ((const CHARSTRING_template*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "float")) {
-    ((const FLOAT*)p_var.value)->log();
+    ((const FLOAT*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "float template")) {
-    ((const FLOAT_template*)p_var.value)->log();
+    ((const FLOAT_template*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "hexstring")) {
-    ((const HEXSTRING*)p_var.value)->log();
+    ((const HEXSTRING*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "hexstring template")) {
-    ((const HEXSTRING_template*)p_var.value)->log();
+    ((const HEXSTRING_template*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "integer")) {
-    ((const INTEGER*)p_var.value)->log();
+    ((const INTEGER*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "integer template")) {
-    ((const INTEGER_template*)p_var.value)->log();
+    ((const INTEGER_template*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "objid")) {
-    ((const OBJID*)p_var.value)->log();
+    ((const OBJID*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "objid template")) {
-    ((const OBJID_template*)p_var.value)->log();
+    ((const OBJID_template*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "octetstring")) {
-    ((const OCTETSTRING*)p_var.value)->log();
+    ((const OCTETSTRING*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "octetstring template")) {
-    ((const OCTETSTRING_template*)p_var.value)->log();
+    ((const OCTETSTRING_template*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "universal charstring")) {
-    ((const UNIVERSAL_CHARSTRING*)p_var.value)->log();
+    ((const UNIVERSAL_CHARSTRING*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "universal charstring template")) {
-    ((const UNIVERSAL_CHARSTRING_template*)p_var.value)->log();
+    ((const UNIVERSAL_CHARSTRING_template*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "verdicttype")) {
-    ((const VERDICTTYPE*)p_var.value)->log();
+    ((const VERDICTTYPE*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "verdicttype template")) {
-    ((const VERDICTTYPE_template*)p_var.value)->log();
+    ((const VERDICTTYPE_template*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "component")) {
-    ((const COMPONENT*)p_var.value)->log();
+    ((const COMPONENT*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "component template")) {
-    ((const COMPONENT_template*)p_var.value)->log();
+    ((const COMPONENT_template*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "default")) {
-    ((const DEFAULT*)p_var.value)->log();
+    ((const DEFAULT*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "default template")) {
-    ((const DEFAULT_template*)p_var.value)->log();
+    ((const DEFAULT_template*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "timer")) {
-    ((const TIMER*)p_var.value)->log();
+    ((const TIMER*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "NULL")) {
-    ((const ASN_NULL*)p_var.value)->log();
+    ((const ASN_NULL*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "NULL template")) {
-    ((const ASN_NULL_template*)p_var.value)->log();
+    ((const ASN_NULL_template*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "CHARACTER STRING")) {
-    ((const CHARACTER_STRING*)p_var.value)->log();
+    ((const CHARACTER_STRING*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "CHARACTER STRING template")) {
-    ((const CHARACTER_STRING_template*)p_var.value)->log();
+    ((const CHARACTER_STRING_template*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "EMBEDDED PDV")) {
-    ((const EMBEDDED_PDV*)p_var.value)->log();
+    ((const EMBEDDED_PDV*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "EMBEDDED PDV template")) {
-    ((const EMBEDDED_PDV_template*)p_var.value)->log();
+    ((const EMBEDDED_PDV_template*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "EXTERNAL")) {
-    ((const EXTERNAL*)p_var.value)->log();
+    ((const EXTERNAL*)ptr)->log();
   }
   else if (!strcmp(p_var.type_name, "EXTERNAL template")) {
-    ((const EXTERNAL_template*)p_var.value)->log();
+    ((const EXTERNAL_template*)ptr)->log();
   }
   else {
     TTCN_Logger::log_event_str("<unrecognized value or template>");
@@ -567,6 +955,110 @@ CHARSTRING TTCN3_Debugger::print_base_var(const TTCN3_Debugger::variable_t& p_va
   return TTCN_Logger::end_event_log2str();
 }
 
+boolean TTCN3_Debugger::set_base_var(variable_t& p_var, Module_Param& p_new_value)
+{
+  if (!strcmp(p_var.type_name, "bitstring")) {
+    ((BITSTRING*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "bitstring template")) {
+    ((BITSTRING_template*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "boolean")) {
+    ((BOOLEAN*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "boolean template")) {
+    ((BOOLEAN_template*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "charstring")) {
+    ((CHARSTRING*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "charstring template")) {
+    ((CHARSTRING_template*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "float")) {
+    ((FLOAT*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "float template")) {
+    ((FLOAT_template*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "hexstring")) {
+    ((HEXSTRING*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "hexstring template")) {
+    ((HEXSTRING_template*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "integer")) {
+    ((INTEGER*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "integer template")) {
+    ((INTEGER_template*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "objid")) {
+    ((OBJID*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "objid template")) {
+    ((OBJID_template*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "octetstring")) {
+    ((OCTETSTRING*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "octetstring template")) {
+    ((OCTETSTRING_template*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "universal charstring")) {
+    ((UNIVERSAL_CHARSTRING*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "universal charstring template")) {
+    ((UNIVERSAL_CHARSTRING_template*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "verdicttype")) {
+    ((VERDICTTYPE*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "verdicttype template")) {
+    ((VERDICTTYPE_template*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "component")) {
+    ((COMPONENT*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "component template")) {
+    ((COMPONENT_template*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "default")) {
+    ((DEFAULT*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "default template")) {
+    ((DEFAULT_template*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "NULL")) {
+    ((ASN_NULL*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "NULL template")) {
+    ((ASN_NULL_template*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "CHARACTER STRING")) {
+    ((CHARACTER_STRING*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "CHARACTER STRING template")) {
+    ((CHARACTER_STRING_template*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "EMBEDDED PDV")) {
+    ((EMBEDDED_PDV*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "EMBEDDED PDV template")) {
+    ((EMBEDDED_PDV_template*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "EXTERNAL")) {
+    ((EXTERNAL*)p_var.value)->set_param(p_new_value);
+  }
+  else if (!strcmp(p_var.type_name, "EXTERNAL template")) {
+    ((EXTERNAL_template*)p_var.value)->set_param(p_new_value);
+  }
+  else {
+    return FALSE;
+  }
+  return TRUE;
+}
+
 void TTCN3_Debugger::add_to_result(const char* fmt, ...)
 {
   va_list parameters;
@@ -577,36 +1069,87 @@ void TTCN3_Debugger::add_to_result(const char* fmt, ...)
 
 void TTCN3_Debugger::add_function(TTCN3_Debug_Function* p_function)
 {
-  if (active) {
-    call_stack.push_back(p_function);
+  function_call_t function_call;
+  if (call_stack.empty()) {
+    test_execution_started();
+    function_call.caller_line = 0;
+  }
+  else {
+    function_call.caller_line = last_breakpoint_entry.line;
   }
+  function_call.function = p_function;
+  call_stack.push_back(function_call);
 }
 
 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);
+    call_stack[call_stack.size() - 1].function->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) {
+  if (!call_stack.empty() && call_stack[call_stack.size() - 1].function == p_function) {
+    bool removing_test_case = call_stack[call_stack.size() - 1].function->is_test_case();
+    int caller_line = call_stack[call_stack.size() - 1].caller_line;
     call_stack.erase_at(call_stack.size() - 1);
+    if (call_stack.empty()) {
+      test_execution_finished();
+    }
+    if (caller_line != 0 && (stepping_type == STEP_INTO || stepping_type == STEP_OUT ||
+        (stepping_type == STEP_OVER && call_stack.size() != stepping_stack_size))) {
+      breakpoint_entry(caller_line);
+    }
+    if (exiting && TTCN_Runtime::is_single() && !call_stack.empty() && removing_test_case &&
+        call_stack[call_stack.size() - 1].function->is_control_part()) {
+      // 'exit all' was requested while executing a test case called by a control
+      // part, which means the test case caught the original TC_End exception;
+      // another exception must be thrown to stop the control part, too
+      throw TC_End();
+    }
   }
 }
 
 void TTCN3_Debugger::remove_scope(TTCN3_Debug_Scope* p_scope)
 {
   if (!call_stack.empty()) {
-    call_stack[call_stack.size() - 1]->remove_scope(p_scope);
+    call_stack[call_stack.size() - 1].function->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&))
+TTCN3_Debugger::variable_t* TTCN3_Debugger::add_variable(const void* p_value,
+                                                         const char* p_name,
+                                                         const char* p_type,
+                                                         TTCN3_Debugger::print_function_t p_print_function)
+{
+  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->cvalue = p_value;
+      var->name = p_name;
+      var->type_name = p_type;
+      var->print_function = p_print_function;
+      var->set_function = NULL;
+      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].function->add_variable(
+      p_value, p_name, p_type, p_print_function);
+  }
+  return NULL;
+}
+
+TTCN3_Debugger::variable_t* TTCN3_Debugger::add_variable(void* p_value,
+                                                         const char* p_name,
+                                                         const char* p_type,
+                                                         TTCN3_Debugger::print_function_t p_print_function,
+                                                         TTCN3_Debugger::set_function_t p_set_function)
 {
   if (call_stack.empty()) {
     // no call stack yet, so this is a global or component variable
@@ -617,13 +1160,15 @@ const TTCN3_Debugger::variable_t* TTCN3_Debugger::add_variable(const void* p_val
       var->name = p_name;
       var->type_name = p_type;
       var->print_function = p_print_function;
+      var->set_function = p_set_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 call_stack[call_stack.size() - 1].function->add_variable(
+      p_value, p_name, p_type, p_print_function);
   }
   return NULL;
 }
@@ -631,7 +1176,7 @@ const TTCN3_Debugger::variable_t* TTCN3_Debugger::add_variable(const void* p_val
 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);
+    call_stack[call_stack.size() - 1].function->remove_variable(p_var);
   }
 }
 
@@ -695,22 +1240,28 @@ void TTCN3_Debugger::add_snapshot(const char* p_snapshot)
     } \
   }
 
-#define CHECK_CALL_STACK \
+#define CHECK_CALL_STACK(print_msg) \
+  if (!active) { \
+    if (print_msg) { \
+      print(DRET_NOTIFICATION, "This command can only be used if the debugger " \
+        "is switched on."); \
+    } \
+    return; \
+  } \
   if (call_stack.empty()) { \
-    print(DRET_NOTIFICATION, "This command can only be used during test execution."); \
+    if (print_msg) { \
+      print(DRET_NOTIFICATION, "This command can only be used if the debugger's " \
+        "call stack is not empty."); \
+    } \
     return; \
   }
 
-#define STACK_LEVEL (stack_level >= 0) ? (size_t)stack_level : (call_stack.size() - 1)
-
 void TTCN3_Debugger::execute_command(int p_command, int p_argument_count,
                                      char** p_arguments)
 {
   if (!enabled) {
     return;
   }
-  Free(command_result);
-  command_result = NULL;
   for (int i = 0; i < p_argument_count; ++i) {
     if (p_arguments[i] == NULL) {
       print(DRET_NOTIFICATION, "Argument %d is a null pointer.", i + 1);
@@ -722,87 +1273,136 @@ void TTCN3_Debugger::execute_command(int p_command, int p_argument_count,
     CHECK_NOF_ARGUMENTS(1)
     switch_state(p_arguments[0]);
     break;
-  case D_ADD_BREAKPOINT:
-    CHECK_NOF_ARGUMENTS(2)
+  case D_SET_BREAKPOINT:
+    CHECK_NOF_ARGUMENTS_RANGE(2, 3)
     CHECK_INT_ARGUMENT(1)
-    add_breakpoint(p_arguments[0], str2int(p_arguments[1]));
+    set_breakpoint(p_arguments[0], str2int(p_arguments[1]),
+      (p_argument_count == 3) ? p_arguments[2] : NULL);
     break;
   case D_REMOVE_BREAKPOINT:
-    CHECK_NOF_ARGUMENTS(2)
-    CHECK_INT_ARGUMENT(1)
-    remove_breakpoint(p_arguments[0], str2int(p_arguments[1]));
+    CHECK_NOF_ARGUMENTS_RANGE(1, 2)
+    remove_breakpoint(p_arguments[0], (p_argument_count == 2) ? p_arguments[1] : NULL);
     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]);
+  case D_SET_AUTOMATIC_BREAKPOINT:
+    CHECK_NOF_ARGUMENTS_RANGE(2, 3)
+    set_automatic_breakpoint(p_arguments[0], p_arguments[1],
+      (p_argument_count == 3) ? p_arguments[2] : NULL);
     break;
   case D_SET_OUTPUT:
     CHECK_NOF_ARGUMENTS_RANGE(1, 2)
     set_output(p_arguments[0], (p_argument_count == 2) ? p_arguments[1] : NULL);
     break;
+  case D_SET_GLOBAL_BATCH_FILE:
+    CHECK_NOF_ARGUMENTS_RANGE(1, 2)
+    set_global_batch_file(p_arguments[0], (p_argument_count == 2) ? p_arguments[1] : NULL);
+    break;
   case D_SET_COMPONENT:
-    print(DRET_NOTIFICATION, "Command " D_SET_COMPONENT_TEXT " should have been "
-      "sent to the Main Controller.");
+    print(DRET_NOTIFICATION, "Command " D_SET_COMPONENT_TEXT " %s.",
+      TTCN_Runtime::is_single() ? "is not available in single mode" :
+      "should have been sent to the Main Controller");
     break;
   case D_PRINT_CALL_STACK:
-    CHECK_CALL_STACK
+    CHECK_CALL_STACK(true)
     CHECK_NOF_ARGUMENTS(0)
     print_call_stack();
     break;
   case D_SET_STACK_LEVEL:
-    CHECK_CALL_STACK
+    CHECK_CALL_STACK(true)
     CHECK_NOF_ARGUMENTS(1)
     CHECK_INT_ARGUMENT(0)
     set_stack_level(str2int(p_arguments[0]));
-    return; // don't print the command result in this case
+    break;
   case D_LIST_VARIABLES:
-    CHECK_CALL_STACK
+    CHECK_CALL_STACK(true)
     CHECK_NOF_ARGUMENTS_RANGE(1, 2)
-    call_stack[STACK_LEVEL]->list_variables(p_arguments[0],
+    call_stack[STACK_LEVEL].function->list_variables(p_arguments[0],
       (p_argument_count == 2) ? p_arguments[1] : NULL);
     break;
   case D_PRINT_VARIABLE:
-    CHECK_CALL_STACK
+    CHECK_CALL_STACK(true)
     CHECK_NOF_ARGUMENTS_MIN(1)
     for (int i = 0; i < p_argument_count; ++i) {
-      const variable_t* var = call_stack[STACK_LEVEL]->find_variable(p_arguments[i]);
-      if (var != NULL) {
-        print_variable(var);
+      if (i != 0) {
+        add_to_result("\n");
       }
-      else {
-        add_to_result("Variable '%s' not found.", p_arguments[i]);
+      if (!strcmp(p_arguments[i], "$")) {
+        // '$' refers to the result of the last D_LIST_VARIABLES command
+        // these variable names are separated by spaces
+        if (last_variable_list != NULL) {
+          size_t len = mstrlen(last_variable_list);
+          size_t start = 0;
+          for (size_t j = 0; j < len; ++j) {
+            if (last_variable_list[j] == ' ') {
+              // extract the variable name before this space
+              char* var_name = mcopystrn(last_variable_list + start, j - start);
+              print_variable(var_name);
+              Free(var_name);
+              add_to_result("\n");
+              start = j + 1;
+            }
+          }
+          // extract the last (or only) variable name
+          char* var_name = mcopystrn(last_variable_list + start, len - start);
+          print_variable(var_name);
+          Free(var_name);
+        }
+        else {
+          add_to_result("No previous " D_LIST_VARIABLES_TEXT " result.");
+        }
       }
-      if (i != p_argument_count - 1) {
-        add_to_result("\n");
+      else {
+        print_variable(p_arguments[i]);
       }
     }
     break;
-  // ...
+  case D_OVERWRITE_VARIABLE:
+    CHECK_CALL_STACK(true)
+    CHECK_NOF_ARGUMENTS_MIN(2)
+    overwrite_variable(p_arguments[0], p_argument_count - 1, p_arguments + 1);
+    break;
   case D_PRINT_SNAPSHOTS:
     CHECK_NOF_ARGUMENTS(0)
     add_to_result("%s", snapshots);
     break;
-  // ...
+  case D_STEP_OVER:
+    CHECK_CALL_STACK(true)
+    CHECK_NOF_ARGUMENTS(0)
+    step(STEP_OVER);
+    break;
+  case D_STEP_INTO:
+    CHECK_CALL_STACK(true)
+    CHECK_NOF_ARGUMENTS(0)
+    step(STEP_INTO);
+    break;
+  case D_STEP_OUT:
+    CHECK_CALL_STACK(true)
+    CHECK_NOF_ARGUMENTS(0)
+    step(STEP_OUT);
+    break;
+  case D_RUN_TO_CURSOR:
+    if (!TTCN_Runtime::is_hc() && !TTCN_Runtime::is_single()) {
+      CHECK_CALL_STACK(TTCN_Runtime::is_mtc())
+    }
+    CHECK_NOF_ARGUMENTS(2)
+    CHECK_INT_ARGUMENT(1)
+    run_to_cursor(p_arguments[0], str2int(p_arguments[1]));
+    break;
   case D_HALT:
-    if (TTCN_Runtime::is_mtc()) {
-      CHECK_CALL_STACK
+    if (!TTCN_Runtime::is_hc() && !TTCN_Runtime::is_single()) {
+      CHECK_CALL_STACK(TTCN_Runtime::is_mtc())
     }
     CHECK_NOF_ARGUMENTS(0)
-    halt();
+    halt(NULL, false);
     break;
   case D_CONTINUE:
     CHECK_NOF_ARGUMENTS(0)
     resume();
     break;
   case D_EXIT:
-    CHECK_NOF_ARGUMENTS(1)
-    if (TTCN_Runtime::is_mtc()) {
-      CHECK_CALL_STACK
+    if (!TTCN_Runtime::is_hc() && !TTCN_Runtime::is_single()) {
+      CHECK_CALL_STACK(TTCN_Runtime::is_mtc())
     }
+    CHECK_NOF_ARGUMENTS(1)
     exit_(p_arguments[0]);
     break;
   case D_SETUP:
@@ -814,21 +1414,36 @@ void TTCN3_Debugger::execute_command(int p_command, int p_argument_count,
       set_output(p_arguments[1], p_arguments[2]);
     }
     if (strlen(p_arguments[3]) > 0) {
-      set_special_breakpoint(SBP_ERROR_VERDICT, p_arguments[3]);
+      set_automatic_breakpoint("error", p_arguments[3],
+        strlen(p_arguments[4]) > 0 ? p_arguments[4] : NULL);
+    }
+    if (strlen(p_arguments[5]) > 0) {
+      set_automatic_breakpoint("fail", p_arguments[5],
+        strlen(p_arguments[6]) > 0 ? p_arguments[6] : NULL);
     }
-    if (strlen(p_arguments[4]) > 0) {
-      set_special_breakpoint(SBP_FAIL_VERDICT, p_arguments[4]);
+    if (strlen(p_arguments[7]) > 0) {
+      set_global_batch_file(p_arguments[7],
+        strlen(p_arguments[8]) > 0 ? p_arguments[8] : NULL);
     }
-    for (int i = 5; i < p_argument_count; i += 2) {
-      add_breakpoint(p_arguments[i], str2int(p_arguments[i + 1]));
+    for (int i = 9; i < p_argument_count; i += 3) {
+      set_breakpoint(p_arguments[i], str2int(p_arguments[i + 1]),
+        strlen(p_arguments[i + 2]) > 0 ? p_arguments[i + 2] : NULL);
     }
     break;
   default:
-    print(DRET_NOTIFICATION, "Command not implemented.");
+    print(DRET_NOTIFICATION, "Invalid command received (ID: %d).", p_command);
     return;
   }
   if (command_result != NULL) {
     print(DRET_DATA, command_result);
+    if (p_command == D_LIST_VARIABLES) {
+      Free(last_variable_list);
+      last_variable_list = command_result;
+    }
+    else {
+      Free(command_result);
+    }
+    command_result = NULL;
   }
 }
 
@@ -836,7 +1451,7 @@ void TTCN3_Debugger::open_output_file()
 {
   if (output_file == NULL && output_file_name != NULL) {
     char* final_file_name = finalize_file_name(output_file_name);
-    output_file = fopen(final_file_name, "w");
+    output_file = fopen(final_file_name, TTCN_Runtime::is_mtc() ? "w" : "a");
     if (output_file == NULL) {
       print(DRET_NOTIFICATION, "Failed to open file '%s' for writing.", final_file_name);
     }
@@ -864,15 +1479,28 @@ TTCN3_Debug_Scope::~TTCN3_Debug_Scope()
 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&))
+                                     TTCN3_Debugger::print_function_t p_print_function)
+{
+  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);
+  }
+}
+
+void TTCN3_Debug_Scope::add_variable(void* p_value,
+                                     const char* p_name,
+                                     const char* p_type,
+                                     TTCN3_Debugger::print_function_t p_print_function,
+                                     TTCN3_Debugger::set_function_t p_set_function)
 {
-  const TTCN3_Debugger::variable_t* var = ttcn3_debugger.add_variable(p_value, p_name, p_type, p_print_function);
+  TTCN3_Debugger::variable_t* var = ttcn3_debugger.add_variable(p_value, p_name,
+    p_type, p_print_function, p_set_function);
   if (var != NULL) {
     variables.push_back(var);
   }
 }
 
-const TTCN3_Debugger::variable_t* TTCN3_Debug_Scope::find_variable(const char* p_name) 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) {
@@ -951,10 +1579,29 @@ TTCN3_Debug_Function::~TTCN3_Debug_Function()
   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&))
+TTCN3_Debugger::variable_t* TTCN3_Debug_Function::add_variable(const void* p_value,
+                                                               const char* p_name,
+                                                               const char* p_type,
+                                                               TTCN3_Debugger::print_function_t p_print_function)
+{
+  if (ttcn3_debugger.is_on()) {
+    TTCN3_Debugger::variable_t* var = new TTCN3_Debugger::variable_t;
+    var->cvalue = p_value;
+    var->name = p_name;
+    var->type_name = p_type;
+    var->print_function = p_print_function;
+    var->set_function = NULL;
+    variables.push_back(var);
+    return var;
+  }
+  return NULL;
+}
+
+TTCN3_Debugger::variable_t* TTCN3_Debug_Function::add_variable(void* p_value,
+                                                               const char* p_name,
+                                                               const char* p_type,
+                                                               TTCN3_Debugger::print_function_t p_print_function,
+                                                               TTCN3_Debugger::set_function_t p_set_function)
 {
   if (ttcn3_debugger.is_on()) {
     TTCN3_Debugger::variable_t* var = new TTCN3_Debugger::variable_t;
@@ -962,6 +1609,7 @@ const TTCN3_Debugger::variable_t* TTCN3_Debug_Function::add_variable(const void*
     var->name = p_name;
     var->type_name = p_type;
     var->print_function = p_print_function;
+    var->set_function = p_set_function;
     variables.push_back(var);
     return var;
   }
@@ -1022,7 +1670,7 @@ void TTCN3_Debug_Function::remove_variable(const TTCN3_Debugger::variable_t* p_v
   }
 }
 
-const TTCN3_Debugger::variable_t* TTCN3_Debug_Function::find_variable(const char* p_name) 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) {
@@ -1031,7 +1679,7 @@ const TTCN3_Debugger::variable_t* TTCN3_Debug_Function::find_variable(const char
   }
   // 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);
+    TTCN3_Debugger::variable_t* res = component_scope->find_variable(p_name);
     if (res != NULL) {
       return res;
     }
@@ -1125,3 +1773,12 @@ void TTCN3_Debug_Function::list_variables(const char* p_scope, const char* p_fil
   }
 }
 
+bool TTCN3_Debug_Function::is_control_part() const
+{
+  return !strcmp(function_type, "control");
+}
+
+bool TTCN3_Debug_Function::is_test_case() const
+{
+  return !strcmp(function_type, "testcase");
+}
index 370c0344919e7f6b2c1c3c01adf980463427fd35..de6eac1d09ae473af184f7f4682a0acfd8ecb19c 100644 (file)
 /** alias for record of charstring */
 typedef PreGenRecordOf::PREGEN__RECORD__OF__CHARSTRING charstring_list;
 
-// forward declarations
+// debugger forward declarations
 class TTCN3_Debug_Scope;
 class TTCN3_Debug_Function;
 
+// other forward declarations
+class Module_Param;
+
 
 //////////////////////////////////////////////////////
 ////////////////// TTCN3_Debugger ////////////////////
@@ -43,16 +46,29 @@ class TTCN3_Debug_Function;
 class TTCN3_Debugger {
 public:
   
+  struct variable_t;
+  
+  typedef CHARSTRING (*print_function_t)(const variable_t&);
+  typedef boolean (*set_function_t)(variable_t&, Module_Param&);
+  
   /** type for keeping track of a variable */
   struct variable_t {
     /** pointer to the variable object, not owned */
-    const void* value;
+    union {
+      /** container for non-constant variable objects */
+      void* value;
+      /** container for constant variable objects */
+      const void* cvalue;
+    };
     /** 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&);
+    print_function_t print_function;
+    /** variable setting (overwriting) function (using the variable object's
+      * set_param() function) - set to NULL for constant variables */
+    set_function_t set_function;
   };
   
   /** this type pairs a debug scope with a name, used for storing global and
@@ -65,13 +81,22 @@ public:
     TTCN3_Debug_Scope* scope;
   };
   
+  /** type for storing a function call in the call stack */
+  struct function_call_t {
+    /** pointer to the debug function object */
+    TTCN3_Debug_Function* function;
+    /** TTCN-3 line number, where the function was called from */
+    int caller_line;
+  };
+  
   /** type for storing breakpoints */
   struct breakpoint_t {
     /** module name, owned */
     char* module;
     /** line number */
     int line;
-    // const char* batch_file;
+    /** batch file to be executed when the breakpoint is reached (optional) */
+    char* batch_file;
   };
   
   /** special breakpoint types, passed to breakpoint_entry() as the line parameter,
@@ -83,6 +108,22 @@ public:
     SBP_FAIL_VERDICT = -1
   };
   
+  /** type for storing the settings of automatic breakpoints */
+  struct automatic_breakpoint_behavior_t {
+    /** indicates whether the breakpoint should be triggered by the associated event */
+    bool trigger;
+    /** batch file to be executed if the breakpoint is triggered (optional) */
+    char* batch_file;
+  };
+  
+  /** stepping type */
+  enum stepping_t {
+    NOT_STEPPING,
+    STEP_OVER,
+    STEP_INTO,
+    STEP_OUT
+  };
+  
 private:
   
   /** indicates whether the debugger has been activated, meaning that the debugger's
@@ -116,9 +157,10 @@ private:
   /** 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;
+  /** pointers to debug function objects and the lines they were called from
+    * (resembling a call stack), the current function is always the last element
+    * in the array (the top element in the stack) */
+  Vector<function_call_t> call_stack;
   
   /** list of breakpoints */
   Vector<breakpoint_t> breakpoints;
@@ -132,17 +174,44 @@ private:
   /** current stack level (reset when test execution is halted or resumed) */
   int stack_level;
   
-  /** behavior triggered by setting the local verdict to FAIL
-    * (a breakpoint is activated if set to true) */
-  bool fail_behavior;
+  /** automatic breakpoint triggered by setting the local verdict to FAIL */
+  automatic_breakpoint_behavior_t fail_behavior;
   
-  /** behavior triggered by setting the local verdict to ERROR
-    * (a breakpoint is activated if set to true) */
-  bool error_behavior;
+  /** automatic breakpoint triggered by setting the local verdict to ERROR */
+  automatic_breakpoint_behavior_t error_behavior;
   
-  /** result of the last executed or currently executing command */
+  /** batch file executed automatically when test execution is halted
+    * NULL if switched off
+    * is overridden by breakpoint-specific batch files */
+  char* global_batch_file;
+  
+  /** result of the currently executing command */
   char* command_result;
   
+  /** result of the last D_LIST_VARIABLES command */
+  char* last_variable_list;
+  
+  /** stores which stepping option was requested by the user (if any) */
+  stepping_t stepping_type;
+  
+  /** stores the size of the call stack when the last stepping operation was
+    * initiated */
+  size_t stepping_stack_size;
+  
+  /** temporary breakpoint set by the 'run to cursor' operation */
+  breakpoint_t temporary_breakpoint;
+  
+  /** true if an 'exit all' command was issued
+    * switched to false when test execution is restarted */
+  bool exiting;
+  
+  /** test execution is automatically halted at start if set to true */
+  bool halt_at_start;
+  
+  /** batch file executed automatically at the start of test execution
+    * if not set to NULL (not owned) */
+  const char* initial_batch_file;
+  
   //////////////////////////////////////////////////////
   ///////////////// internal functions /////////////////
   //////////////////////////////////////////////////////
@@ -151,19 +220,28 @@ private:
     * handles the D_SWITCH command */
   void switch_state(const char* p_state_str);
   
-  /** adds a new breakpoint at the specified module and line 
+  /** adds a new breakpoint at the specified module and line with the specified
+    * batch file (if not NULL), or changes the batch file of an existing
+    * breakpoint
     * handles the D_ADD_BREAKPOINT command */
-  void add_breakpoint(const char* p_module, int p_line /*const char* batch_file*/);
+  void set_breakpoint(const char* p_module, int p_line, const char* batch_file);
   
   /** removes the breakpoint from the specified module/line, if it exists
+    * can also be used to remove all breakpoints from the specified module or
+    * all breakpoints in all modules
     * handles the D_REMOVE_BREAKPOINT command  */
-  void remove_breakpoint(const char* p_module, 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);
+  void remove_breakpoint(const char* p_module, const char* p_line);
+  
+  /** switches an automatic breakpoint related to the specified event on or off
+    * and/or sets the batch file to run when the breakpoint is triggered
+    * @param p_event_str string representation of the event that triggers the
+    * breakpoint (either "error" or "fail")
+    * @param p_state_str "on" or "off", indicates the new state of the breakpoint
+    * @param p_batch_file name of the batch file to execute (NULL means don't
+    * execute anything)
+    * handles the D_SET_AUTOMATIC_BREAKPOINT command */
+  void set_automatic_breakpoint(const char* p_event_str, const char* p_state_str,
+    const char* p_batch_file);
   
   /** prints the current call stack
     * handles the D_PRINT_CALL_STACK command */
@@ -173,9 +251,13 @@ private:
     * handles the D_SET_STACK_LEVEL command */
   void set_stack_level(int new_level);
   
-  /** prints the specified variable
+  /** finds the variable with the specified name, and prints its value or an
+    * error message
     * handles (one parameter of) the D_PRINT_VARIABLE command */
-  void print_variable(const variable_t* p_var);
+  void print_variable(const char* p_var_name);
+  
+  void overwrite_variable(const char* p_var_name, int p_value_element_count,
+    char** p_value_elements);
   
   /** sets the debugger's output to the console and/or a text file
     * handles the D_SET_OUTPUT command
@@ -183,9 +265,27 @@ private:
     * @param p_file_name output file name or NULL */
   void set_output(const char* p_output_type, const char* p_file_name);
   
+  /** switches the global batch file on or off
+    * handles the D_SET_GLOBAL_BATCH_FILE command */
+  void set_global_batch_file(const char* p_state_str, const char* p_file_name);
+  
+  /** resumes execution until the next breakpoint entry (in the stack levels
+    * specified by the stepping type arguments) is reached (or until test execution
+    * is halted for any other reason)
+    * handles the D_STEP_OVER, D_STEP_INTO and D_STEP_OUT commands */
+  void step(stepping_t p_stepping_type);
+  
+  /** resumes execution until the specified location is reached 
+    * (or until test execution is halted for any other reason)
+    * handles the D_RUN_TO_CURSOR command */
+  void run_to_cursor(const char* p_module, int p_line);
+  
   /** halts test execution, processing only debug commands
+    * @param p_batch_file batch file executed after the halt (if not NULL)
+    * @param p_run_global_batch indicates whether the global batch file should
+    * be executed after the halt (only if p_batch_file is NULL)
     * handles the D_HALT command */
-  void halt();
+  void halt(const char* p_batch_file, bool p_run_global_batch);
   
   /** resumes the halted test execution
     * handles the D_CONTINUE command */
@@ -205,6 +305,14 @@ private:
   /** handles metacharacters in the specified file name skeleton
     * @return final file name (must be freed by caller) */
   static char* finalize_file_name(const char* p_file_name_skeleton);
+  
+  /** initialization function, called when the first function is added to the
+    * call stack */
+  void test_execution_started();
+  
+  /** clean up function, called when the last function is removed from the
+    * call stack */
+  void test_execution_finished();
 
 public:
   /** constructor - called once per process (at the beginning) */
@@ -237,37 +345,61 @@ public:
     * 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*/);
+  void breakpoint_entry(int p_line);
   
   /** variable printing function for base types */
   static CHARSTRING print_base_var(const variable_t& p_var);
   
+  /** variable setting function for base types */
+  static boolean set_base_var(variable_t& p_var, Module_Param& p_new_value);
+  
   /** 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)
   {
+    const void* ptr = p_var.set_function != NULL ? p_var.value : p_var.cvalue;
     TTCN_Logger::begin_event_log2str();
-    ((VALUE_ARRAY<T_type, array_size, index_offset>*)p_var.value)->log();
+    ((VALUE_ARRAY<T_type, array_size, index_offset>*)ptr)->log();
     return TTCN_Logger::end_event_log2str();
   }
   
+  /** variable setting function for value arrays */
+  template <typename T_type, unsigned int array_size, int index_offset>
+  static boolean set_value_array(variable_t& p_var, Module_Param& p_new_value)
+  {
+    ((VALUE_ARRAY<T_type, array_size, index_offset>*)p_var.value)->set_param(p_new_value);
+    return TRUE;
+  }
+  
   /** 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)
   {
+    const void* ptr = p_var.set_function != NULL ? p_var.value : p_var.cvalue;
     TTCN_Logger::begin_event_log2str();
     ((TEMPLATE_ARRAY<T_value_type, T_template_type, array_size,
-      index_offset>*)p_var.value)->log();
+      index_offset>*)ptr)->log();
     return TTCN_Logger::end_event_log2str();
   }
   
+  /** variable setting function for template arrays */
+  template <typename T_value_type, typename T_template_type,
+    unsigned int array_size, int index_offset>
+  static boolean set_template_array(variable_t& p_var, Module_Param& p_new_value)
+  {
+    ((TEMPLATE_ARRAY<T_value_type, T_template_type, array_size,
+      index_offset>*)p_var.value)->set_param(p_new_value);
+    return TRUE;
+  }
+  
   /** 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)
   {
+    const void* ptr = p_var.set_function != NULL ? p_var.value : p_var.cvalue;
     TTCN_Logger::begin_event_log2str();
-    ((PORT_ARRAY<T_type, array_size, index_offset>*)p_var.value)->log();
+    ((PORT_ARRAY<T_type, array_size, index_offset>*)ptr)->log();
     return TTCN_Logger::end_event_log2str();
   }
   
@@ -275,8 +407,9 @@ public:
   template <typename T_type, unsigned int array_size, int index_offset>
   static CHARSTRING print_timer_array(const variable_t& p_var)
   {
+    const void* ptr = p_var.set_function != NULL ? p_var.value : p_var.cvalue;
     TTCN_Logger::begin_event_log2str();
-    ((TIMER_ARRAY<T_type, array_size, index_offset>*)p_var.value)->log();
+    ((TIMER_ARRAY<T_type, array_size, index_offset>*)ptr)->log();
     return TTCN_Logger::end_event_log2str();
   }
   
@@ -284,8 +417,9 @@ public:
   template <typename EXPR_TYPE>
   static CHARSTRING print_lazy_param(const variable_t& p_var)
   {
+    const void* ptr = p_var.set_function != NULL ? p_var.value : p_var.cvalue;
     TTCN_Logger::begin_event_log2str();
-    ((Lazy_Param<EXPR_TYPE>*)p_var.value)->log();
+    ((Lazy_Param<EXPR_TYPE>*)ptr)->log();
     return TTCN_Logger::end_event_log2str();
   }
   
@@ -325,14 +459,19 @@ public:
     * (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
+  /** finds or creates, and returns the constant 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&));
+  variable_t* add_variable(const void* p_value, const char* p_name, const char* p_type,
+    print_function_t p_print_function);
+  
+  /** same as before, but for non-constant variables */
+  variable_t* add_variable(void* p_value, const char* p_name, const char* p_type,
+    print_function_t p_print_function, set_function_t p_set_function);
   
   /** removes the variable entry for the specified local variable in the current
     * function (only if the call stack is not empty) */
@@ -353,6 +492,19 @@ public:
   /** opens the debugger's output file for writing (if one has been set, but not
     * opened, in the HC process) */
   void open_output_file();
+  
+  /** indicates whether an 'exit all' command has been issued 
+    * (this causes the execution of tests in the current queue to stop) */
+  bool is_exiting() const { return exiting; }
+  
+  /** sets the debugger to halt test execution at start (only in single mode) */
+  void set_halt_at_start() { halt_at_start = true; }
+  
+  /** sets the specified batch file to be executed at the start of test execution
+    * (only in single mode) */
+  void set_initial_batch_file(const char* p_batch_file) {
+    initial_batch_file = p_batch_file;
+  }
 };
 
 /** the main debugger object */
@@ -377,7 +529,7 @@ 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;
+  Vector<TTCN3_Debugger::variable_t*> variables;
   
 public:
   
@@ -393,11 +545,16 @@ public:
   //////////////////////////////////////////////////////
   
   /** 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
+    * create and store a (constant) 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&));
+    TTCN3_Debugger::print_function_t p_print_function);
+  
+  /** same as before, but for non-constant variables */
+  void add_variable(void* p_value, const char* p_name, const char* p_type,
+    TTCN3_Debugger::print_function_t p_print_function,
+    TTCN3_Debugger::set_function_t p_set_function);
   
   //////////////////////////////////////////////////////
   ////// methods called by other debugger classes //////
@@ -407,7 +564,7 @@ public:
   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;
+  TTCN3_Debugger::variable_t* find_variable(const char* p_name) const;
   
   /** prints the names of variables in this scope that match the specified
     * @param p_posix_regexp the pattern converted into a POSIX regex structure
@@ -483,10 +640,15 @@ public:
   ////// methods called from TITAN generated code //////
   //////////////////////////////////////////////////////
   
-  /** creates, stores and returns the variable entry of the local variable
+  /** creates, stores and returns the variable entry of the local (constant) 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&));
+  TTCN3_Debugger::variable_t* add_variable(const void* p_value, const char* p_name,
+    const char* p_type, TTCN3_Debugger::print_function_t p_print_function);
+  
+  /** same as before, but for non-constant variables */
+  TTCN3_Debugger::variable_t* add_variable(void* p_value, const char* p_name,
+    const char* p_type, TTCN3_Debugger::print_function_t p_print_function,
+    TTCN3_Debugger::set_function_t p_set_function);
   
   /** stores the string representation of the value returned by the function */
   void set_return_value(const CHARSTRING& p_value);
@@ -513,7 +675,7 @@ public:
   /** 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;
+  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;
@@ -530,6 +692,12 @@ public:
     * - "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;
+  
+  /** returns true if this instance belongs to a control part */
+  bool is_control_part() const;
+  
+  /** returns true if this instance belongs to a test case */
+  bool is_test_case() const;
 };
 
 /** This macro stores a function's return value in the current function.
diff --git a/core/DebuggerUI.cc b/core/DebuggerUI.cc
new file mode 100644 (file)
index 0000000..4ca6e5b
--- /dev/null
@@ -0,0 +1,263 @@
+/******************************************************************************
+ * 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 "DebuggerUI.hh"
+#include "DebugCommands.hh"
+#include "Debugger.hh"
+#include "../mctr2/editline/libedit/src/editline/readline.h"
+#include <stdio.h>
+#include <ctype.h>
+
+#define PROMPT_TEXT "DEBUG> "
+#define BATCH_TEXT "batch"
+
+// use a different file, than the MCTR CLI, since not all commands are the same
+#define TTCN3_HISTORY_FILENAME ".ttcn3_history_single"
+
+const TTCN_Debugger_UI::command_t TTCN_Debugger_UI::debug_command_list[] = {
+  { D_SWITCH_TEXT, D_SWITCH, D_SWITCH_TEXT " on|off",
+    "Switch the debugger on or off." },
+  { D_SET_BREAKPOINT_TEXT, D_SET_BREAKPOINT,
+    D_SET_BREAKPOINT_TEXT " <module> <line> [<batch_file>]",
+    "Add a breakpoint at the specified location, or change the batch file of "
+    " an existing breakpoint." },
+  { D_REMOVE_BREAKPOINT_TEXT, D_REMOVE_BREAKPOINT,
+    D_REMOVE_BREAKPOINT_TEXT " all|<module> [all|<line>]", "Remove a breakpoint, "
+    "or all breakpoints from a module, or all breakpoints from all modules." },
+  { D_SET_AUTOMATIC_BREAKPOINT_TEXT, D_SET_AUTOMATIC_BREAKPOINT,
+    D_SET_AUTOMATIC_BREAKPOINT_TEXT " error|fail on|off [<batch_file>]",
+    "Switch an automatic breakpoint (truggered by an event) on or off, and/or "
+    "change its batch file." },
+  { D_SET_OUTPUT_TEXT, D_SET_OUTPUT,
+    D_SET_OUTPUT_TEXT " console|file|both [file_name]",
+    "Set the output of the debugger." },
+  { D_SET_GLOBAL_BATCH_FILE_TEXT, D_SET_GLOBAL_BATCH_FILE,
+    D_SET_GLOBAL_BATCH_FILE_TEXT " on|off [batch_file_name]",
+    "Set whether a batch file should be executed automatically when test execution "
+    "is halted (breakpoint-specific batch files override this setting)." },
+  { D_SET_COMPONENT_TEXT, D_SET_COMPONENT,
+    D_SET_COMPONENT_TEXT " mtc|<component_reference>",
+    "Set the test component to print debug information from." },
+  { D_PRINT_CALL_STACK_TEXT, D_PRINT_CALL_STACK, D_PRINT_CALL_STACK_TEXT,
+    "Print call stack." },
+  { D_SET_STACK_LEVEL_TEXT, D_SET_STACK_LEVEL, D_SET_STACK_LEVEL_TEXT " <level>",
+    "Set the stack level to print debug information from." },
+  { D_LIST_VARIABLES_TEXT, D_LIST_VARIABLES,
+    D_LIST_VARIABLES_TEXT " local|global|comp|all [pattern]",
+    "List variable names." },
+  { D_PRINT_VARIABLE_TEXT, D_PRINT_VARIABLE,
+    D_PRINT_VARIABLE_TEXT " <variable_name>|$ [{ <variable_name>|$}]",
+    "Print current value of one or more variables ('$' is substituted with the "
+    "result of the last " D_LIST_VARIABLES_TEXT " command)." },
+  { D_OVERWRITE_VARIABLE_TEXT, D_OVERWRITE_VARIABLE,
+    D_OVERWRITE_VARIABLE_TEXT " <variable_name> <value>",
+    "Overwrite the current value of a variable." },
+  { D_PRINT_SNAPSHOTS_TEXT, D_PRINT_SNAPSHOTS, D_PRINT_SNAPSHOTS_TEXT,
+    "Print snapshots of function calls until this point." },
+  // D_SET_SNAPSHOT_BEHAVIOR_TEXT
+  { D_STEP_OVER_TEXT, D_STEP_OVER, D_STEP_OVER_TEXT,
+    "Resume test execution until the next line of code (in this function or the "
+    "caller function)." },
+  { D_STEP_INTO_TEXT, D_STEP_INTO, D_STEP_INTO_TEXT,
+    "Resume test execution until the next line of code (on any stack level)." },
+  { D_STEP_OUT_TEXT, D_STEP_OUT, D_STEP_OUT_TEXT,
+    "Resume test execution until the next line of code in the caller function." },
+  { D_RUN_TO_CURSOR_TEXT, D_RUN_TO_CURSOR, D_RUN_TO_CURSOR_TEXT " <module> <line>",
+    "Resume test execution until the specified location." },
+  { D_HALT_TEXT, D_HALT, D_HALT_TEXT, "Halt test execution." },
+  { D_CONTINUE_TEXT, D_CONTINUE, D_CONTINUE_TEXT, "Resume halted test execution." },
+  { D_EXIT_TEXT, D_EXIT, D_EXIT_TEXT " test|all",
+    "Exit the current test or the execution of all tests." },
+  { NULL, D_ERROR, NULL, NULL }
+};
+
+char* TTCN_Debugger_UI::ttcn3_history_filename = NULL;
+
+/** local function for extracting the command name and its arguments from an
+  * input line
+  * @param arguments [in] input line
+  * @param len [in] length of the input line
+  * @param start [in] indicates the position to start searching from
+  * @param start [out] the next argument's start position (set to len if no further
+  * arguments were found)
+  * @param end [out] the position of the first character after the next argument */
+static void get_next_argument_loc(const char* arguments, size_t len, size_t& start, size_t& end)
+{
+  while (start < len && isspace(arguments[start])) {
+    ++start;
+  }
+  end = start;
+  while (end < len && !isspace(arguments[end])) {
+    ++end;
+  }
+}
+
+void TTCN_Debugger_UI::process_command(const char* p_line_read)
+{
+  // locate the command text
+  size_t len = strlen(p_line_read);
+  size_t start = 0;
+  size_t end = 0;
+  get_next_argument_loc(p_line_read, len, start, end);
+  if (start == len) {
+    // empty command
+    return;
+  }
+  add_history(p_line_read + start);
+  for (const command_t *command = debug_command_list; command->name != NULL;
+       ++command) {
+    if (!strncmp(p_line_read + start, command->name, end - start)) {
+      // count the arguments
+      int argument_count = 0;
+      size_t start_tmp = start;
+      size_t end_tmp = end;
+      while (start_tmp < len) {
+        start_tmp = end_tmp;
+        get_next_argument_loc(p_line_read, len, start_tmp, end_tmp);
+        if (start_tmp < len) {
+          ++argument_count;
+        }
+      }
+      // extract the arguments into a string array
+      char** arguments;
+      if (argument_count > 0) {
+        arguments = new char*[argument_count];
+        for (int i = 0; i < argument_count; ++i) {
+          start = end;
+          get_next_argument_loc(p_line_read, len, start, end);
+          arguments[i] = mcopystrn(p_line_read + start, end - start);
+        }
+      }
+      else {
+        arguments = NULL;
+      }
+      ttcn3_debugger.execute_command(command->commandID, argument_count, arguments);
+      if (argument_count > 0) {
+        for (int i = 0; i < argument_count; ++i) {
+          Free(arguments[i]);
+        }
+        delete [] arguments;
+      }
+      return;
+    }
+  }
+  if (!strncmp(p_line_read + start, BATCH_TEXT, end - start)) {
+    start = end;
+    get_next_argument_loc(p_line_read, len, start, end); // just to skip to the argument
+    // the entire argument list is treated as one file name (even if it contains spaces)
+    execute_batch_file(p_line_read + start);
+    return;
+  }
+  puts("Unknown command, try again...");
+}
+
+void TTCN_Debugger_UI::init()
+{
+  // initialize history library
+  using_history();
+  // calculate history file name
+  const char *home_directory = getenv("HOME");
+  if (home_directory == NULL) {
+    home_directory = ".";
+  }
+  ttcn3_history_filename = mprintf("%s/%s", home_directory, TTCN3_HISTORY_FILENAME);
+  // read history from file, don't bother if it does not exist
+  read_history(ttcn3_history_filename);
+  // set our own command completion function
+  rl_completion_entry_function = (Function*)complete_command;
+}
+
+void TTCN_Debugger_UI::clean_up()
+{
+  if (write_history(ttcn3_history_filename)) {
+    puts("Could not save debugger command history.");
+  }
+  Free(ttcn3_history_filename);
+}
+
+void TTCN_Debugger_UI::read_loop()
+{
+  while (ttcn3_debugger.is_halted()) {
+    // print the prompt and read a line using the readline(), which
+    // automatically handles command completion and command history
+    char* line = readline(PROMPT_TEXT);
+    if (line != NULL) {
+      process_command(line);
+      free(line);
+    }
+    else {
+      // EOF was received -> exit all
+      puts("exit all");
+      char** args = new char*[1];
+      args[0] = (char*)"all";
+      ttcn3_debugger.execute_command(D_EXIT, 1, args);
+      delete [] args;
+    }
+  }
+}
+
+void TTCN_Debugger_UI::execute_batch_file(const char* p_file_name)
+{
+  FILE* fp = fopen(p_file_name, "r");
+  if (fp == NULL) {
+    printf("Failed to open file '%s' for reading.\n", p_file_name);
+    return;
+  }
+  else {
+    printf("Executing batch file '%s'.\n", p_file_name);
+  }
+  char line[1024];
+  while (fgets(line, sizeof(line), fp) != NULL) {
+    size_t len = strlen(line);
+    if (line[len - 1] == '\n') {
+      line[len - 1] = '\0';
+      --len;
+    }
+    if (len != 0) {
+      printf("%s\n", line);
+      process_command(line);
+    }
+  }
+  if (!feof(fp)) {
+    printf("Error occurred while reading batch file '%s' (error code: %d).\n",
+      p_file_name, ferror(fp));
+  }
+  fclose(fp);
+}
+
+void TTCN_Debugger_UI::print(const char* p_str)
+{
+  puts(p_str);
+}
+
+char* TTCN_Debugger_UI::complete_command(const char* p_prefix, int p_state)
+{
+  static int command_index;
+  static size_t prefix_len;
+  const char *command_name;
+
+  if (p_state == 0) {
+    command_index = 0;
+    prefix_len = strlen(p_prefix);
+  }
+  
+  while ((command_name = debug_command_list[command_index].name)) {
+    ++command_index;
+    if (strncmp(p_prefix, command_name, prefix_len) == 0) {
+      // must allocate buffer for returned string (readline() frees it)
+      return strdup(command_name);
+    }
+  }
+  // no match found
+  return NULL;
+}
\ No newline at end of file
diff --git a/core/DebuggerUI.hh b/core/DebuggerUI.hh
new file mode 100644 (file)
index 0000000..b3da252
--- /dev/null
@@ -0,0 +1,71 @@
+/******************************************************************************
+ * 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_UI_HH
+#define DEBUGGER_UI_HH
+
+/** Command line interface class for the TTCN-3 debugger in single mode
+  * Mimics the functionality of the Main Controller CLI in most cases
+  * Uses the editline package for reading commands (which provides command
+  * completion and command history tracking) */
+class TTCN_Debugger_UI {
+  
+  /** structure for storing a command */
+  struct command_t {
+    /** command name */
+    const char *name;
+    /** debugger command ID */
+    int commandID;
+    /** command usage text */
+    const char *synopsis;
+    /** command description text */
+    const char *description;
+  };
+  
+  /** list of commands */
+  static const command_t debug_command_list[];
+  
+  /** name of the file, where the command history is stored */
+  static char* ttcn3_history_filename;
+  
+  /** processes the command in the specified input line
+    * if it's a valid command, then it is added to the command history and 
+    * passed to the debugger 
+    * if it's not valid, an error message is displayed */
+  static void process_command(const char* p_line_read);
+  
+public:
+  
+  /** initializes the UI */
+  static void init();
+  
+  /** cleans up the UI's resources */
+  static void clean_up();
+  
+  /** reads commands from the standard input and passes them on for processing,
+    * until test execution is no longer halted */
+  static void read_loop();
+  
+  /** executes the commands in the specified batch file
+    * each line is treated as a separate command */
+  static void execute_batch_file(const char* p_file_name);
+  
+  /** prints the specified text to the standard output */
+  static void print(const char* p_str);
+  
+  /** command completion function for editline */
+  static char* complete_command(const char* p_prefix, int p_state);
+};
+
+#endif /* DEBUGGER_UI_HH */
+
index bb0f3e0b07856700dadf993173060f57f343195d..9b7d92657660ab8f7a40f6a22b752c12e81d3af0 100644 (file)
@@ -1510,6 +1510,14 @@ void TTCN_Location::strip_entity_name(char*& par_str)
   par_str = new_str;
 }
 
+unsigned int TTCN_Location::get_line_number()
+{
+  if (innermost_location != NULL) {
+    return innermost_location->line_number;
+  }
+  return 0;
+}
+
 char *TTCN_Location::append_contents(char *par_str,
   boolean print_entity_name) const
 {
index c394c95d199685c4f3a2c0f1bf5ffd5d7dd79b13..96d1b2d71a62de1f76c70ad6769611d8796f47b6 100644 (file)
@@ -836,6 +836,10 @@ public:
    *
    **/
   static void strip_entity_name(char*& par_str);
+  
+  /** @brief Return the innermost location's line number 
+   **/
+  static unsigned int get_line_number();
 protected:
   char *append_contents(char *par_str, boolean print_entity_name) const;
 };
index a01209d85d9a7901a84cd8033546efa06866a45b..ea118e12aa206413c3f1ef79ae2c5a8c814d055a 100644 (file)
@@ -75,12 +75,12 @@ TTCN_COMPILER_FLAGS += -o $(APIDIR)
 # -I. is needed because TitanLoggerApi.hh (generated) does #include <TTCN3.hh>
 CPPFLAGS += -I. -I$(APIDIR)
 
-SOLARIS_LIBS   := -lsocket -lnsl -lxml2
-SOLARIS8_LIBS  := -lsocket -lnsl -lxml2
-LINUX_LIBS     := -lxml2
-FREEBSD_LIBS   := -lxml2
-WIN32_LIBS     := -lxml2
-INTERIX_LIBS   := -lxml2
+SOLARIS_LIBS   := -lsocket -lnsl -lxml2 -lcurses
+SOLARIS8_LIBS  := -lsocket -lnsl -lxml2 -lcurses
+LINUX_LIBS     := -lxml2 -lncurses
+FREEBSD_LIBS   := -lxml2 -lncurses
+WIN32_LIBS     := -lxml2 -lncurses
+INTERIX_LIBS   := -lxml2 -lncurses
 
 ifeq ($(USAGE_STATS), yes)
   SOLARIS_LIBS  += -lresolv
@@ -104,7 +104,8 @@ 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 Debugger.cc $(RT2_SOURCES)
+Profiler.cc ProfilerTools.cc ProfMerge_main.cc Debugger.cc DebuggerUI.cc \
+$(RT2_SOURCES)
 
 # Keep GENERATED_SOURCES at the beginning. This may speed up parallel builds
 # by starting early the compilation of the largest files.
@@ -169,8 +170,13 @@ ifeq ($(USAGE_STATS), yes)
   COMMON_OBJECTS += ../common/usage_stats.o
 endif
 
+EDITLINE_OBJECTS := $(addprefix ../mctr2/editline/build/src/.libs/, chared.o common.o \
+emacs.o fgetln.o help.o history.o map.o prompt.o read.o search.o strlcat.o term.o tty.o \
+vi.o wcsdup.o chartype.o el.o fcns.o filecomplete.o hist.o key.o parse.o readline.o \
+refresh.o sig.o strlcpy.o tokenizer.o unvis.o vis.o)
+
 LIBRARY_OBJECTS_NOMAIN := $(filter-out Single_main.o Parallel_main.o ProfMerge_main.o, \
-  $(OBJECTS)) $(COMMON_OBJECTS)
+  $(OBJECTS)) $(COMMON_OBJECTS) $(EDITLINE_OBJECTS)
 
 TOBECLEANED := LoggerPlugin_static.o LoggerPlugin_dynamic.o ProfilerTools.profmerge.o
 
@@ -186,7 +192,8 @@ 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 Debugger.hh DebugCommands.hh
+JSON.hh Profiler.hh RefdIndex.hh ProfilerTools.hh Debugger.hh DebugCommands.hh \
+DebuggerUI.hh
 # Copied during "make install"
 
 ifdef REGEX_DIR
index b47fa1526e0271892ee745121342f107f3a4240d..b8873d663237de51200cd3b6c376881c3978a3dd 100644 (file)
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *   Balasko, Jeno
+ *   Baranyi, Botond
  *   Forstner, Matyas
  *   Szabo, Janos Zoltan â€“ initial implementation
  *
 #define MSG_UNMAP_REQ          19
 #define MSG_UNMAPPED           20
 #define MSG_DEBUG_HALT_REQ     101
+#define MSG_DEBUG_CONTINUE_REQ 102
+#define MSG_DEBUG_BATCH                103
 
 /* Messages from MTC to MC (up) */
 
index 48705eeaf9bc54775fab961b1233cddde64d9ec5..f3a90fead19e76f9b57a23ace596426c6f5eafbc 100644 (file)
@@ -37,6 +37,7 @@
 #endif
 
 #include "../common/dbgnew.hh"
+#include "Debugger.hh"
 
 #include <stdio.h>
 #include <string.h>
@@ -985,6 +986,9 @@ void TTCN_Module::execute_all_testcases()
   boolean found = FALSE;
   for (testcase_list_item *list_iter = testcase_head; list_iter != NULL;
     list_iter = list_iter->next_testcase) {
+    if (ttcn3_debugger.is_exiting()) {
+      break;
+    }
     if (!list_iter->is_pard) {
       list_iter->testcase_function(FALSE, 0.0);
       found = TRUE;
index 1da214635eb7e0fc4ed3316751741a327954875e..8926f32baa14a1d6e61edee7c71fb33b23f0aa45 100644 (file)
@@ -33,6 +33,8 @@
 #include "Universal_charstring.hh"
 #include "Logger.hh"
 #include "Module_list.hh"
+#include "Debugger.hh"
+#include "DebugCommands.hh"
 
 
 size_t Module_Param_Id::get_index() const {
@@ -317,6 +319,9 @@ Module_Param_Reference::Module_Param_Reference(Module_Param_Name* p): mp_ref(p)
 }
 
 Module_Param_Ptr Module_Param_Reference::get_referenced_param() const {
+  if (Debugger_Value_Parsing::happening()) {
+    error("References to other variables are not allowed.");
+  }
   mp_ref->reset();
   Module_Param_Ptr ptr = Module_List::get_param(*mp_ref);
   ptr.set_temporary();
@@ -635,6 +640,31 @@ void Module_Param::error(const char* err_msg, ...) const {
     Free(exception_str);
     TTCN_error_end();
   }
+  else if (Debugger_Value_Parsing::happening()) {
+    char* exception_str = mcopystr("Error while overwriting ");
+    char* param_ctx;
+    if (id && id->is_custom()) {
+      param_ctx = mputstr(id->get_str(), " in the variable");
+    }
+    else {
+      char* tmp = get_param_context();
+      param_ctx = tmp != NULL ? mprintf("variable field '%s'", tmp) : 
+        mcopystr("the variable");
+      Free(tmp);
+    }
+    exception_str = mputstr(exception_str, param_ctx);
+    Free(param_ctx);
+    exception_str = mputstr(exception_str, ": ");
+    va_list p_var;
+    va_start(p_var, err_msg);
+    char* error_msg_str = mprintf_va_list(err_msg, p_var);
+    va_end(p_var);
+    exception_str = mputstr(exception_str, error_msg_str);
+    Free(error_msg_str);
+    ttcn3_debugger.print(DRET_NOTIFICATION, "%s", exception_str);
+    Free(exception_str);
+    throw TC_Error(); // don't log anything in this case
+  }
   TTCN_Logger::begin_event(TTCN_Logger::ERROR_UNQUALIFIED);
   TTCN_Logger::log_event_str("Error while ");
   switch (operation_type) {
@@ -673,19 +703,26 @@ void Module_Param::error(const char* err_msg, ...) const {
 }
 
 void Module_Param::type_error(const char* expected, const char* type_name /* = NULL */) const {
-  const Module_Param* reporter = this;
-  // if it's an expression, find its head and use that to report the error
-  // (since that's the only parameter with a valid name)
-  while (reporter->parent != NULL && reporter->parent->get_type() == MP_Expression) {
-    reporter = reporter->parent;
+  if (Debugger_Value_Parsing::happening()) {
+    // no references when overwriting a variable through the debugger
+    error("Type mismatch: %s was expected instead of %s.", expected, get_type_str());
+  }
+  else {
+    const Module_Param* reporter = this;
+    // if it's an expression, find its head and use that to report the error
+    // (since that's the only parameter with a valid name)
+    while (reporter->parent != NULL && reporter->parent->get_type() == MP_Expression) {
+      reporter = reporter->parent;
+    }
+    // either use this parameter's or the referenced parameter's type string
+    // (but never the head's type string)
+    reporter->error("Type mismatch: %s or reference to %s was expected%s%s instead of %s%s.",
+      expected, expected,
+      (type_name != NULL) ? " for type " : "", (type_name != NULL) ? type_name : "",
+      (get_type() == MP_Reference) ? "reference to " : "",
+      (get_type() == MP_Reference) ? get_referenced_param()->get_type_str() : get_type_str());
   }
-  // either use this parameter's or the referenced parameter's type string
-  // (but never the head's type string)
-  reporter->error("Type mismatch: %s or reference to %s was expected%s%s instead of %s%s.",
-    expected, expected,
-    (type_name != NULL) ? " for type " : "", (type_name != NULL) ? type_name : "",
-    (get_type() == MP_Reference) ? "reference to " : "",
-    (get_type() == MP_Reference) ? get_referenced_param()->get_type_str() : get_type_str());
 }
 
 bool Ttcn_String_Parsing::string_parsing = false;
+bool Debugger_Value_Parsing::is_happening = false;
index adc3ff779c63171c3aa3b18d710673a6aff23ad6..d85ab420294881afc6d7afdc496421183f860cac 100644 (file)
@@ -685,6 +685,15 @@ public:
   static bool happening() { return string_parsing; }
 };
 
+class Debugger_Value_Parsing {
+private: // only instantiation can set it to true and destruction set it back to false
+  static bool is_happening;
+public:
+  Debugger_Value_Parsing() { is_happening = true; }
+  ~Debugger_Value_Parsing() { is_happening = false; }
+  static bool happening() { return is_happening; }
+};
+
 /** Use the configuration file parser to convert a string into a TTCN-3 value.
   * @param mp_str the converted string (used as if it were a module parameter in a
   * config file)
@@ -695,4 +704,6 @@ public:
   * be applied to components. */
 extern Module_Param* process_config_string2ttcn(const char* mp_str, bool is_component);
 
+extern Module_Param* process_config_debugger_value(const char* mp_str);
+
 #endif
index f78d7adc58b02e159042ef6dbcf7cad339d6a6b3..b0a615e974f483f0067a28373e6e957024490e8c 100644 (file)
@@ -80,11 +80,13 @@ void signal_handler(int signum)
 static void usage(const char* program_name)
 {
   fprintf(stderr, "\n"
-    "usage: %s configuration_file\n"
+    "usage: %s [-h] [-b file] configuration_file\n"
     "   or  %s -l\n"
     "   or  %s -v\n"
     "\n"
     "OPTIONS:\n"
+    "  -b file:        run specified batch file at start (debugger must be activated)\n"
+    "  -h:             automatically halt execution at start (debugger must be activated)\n"
     "  -l:             list startable test cases and control parts\n"
     "  -v:             show version and module information\n",
     program_name, program_name, program_name);
@@ -105,12 +107,26 @@ int main(int argc, char *argv[])
 #endif
   errno = 0;
   int c, i, ret_val = EXIT_SUCCESS;
-  boolean lflag = FALSE, vflag = FALSE, errflag = FALSE;
+  boolean bflag = FALSE, hflag = FALSE, lflag = FALSE, vflag = FALSE, errflag = FALSE;
   const char *config_file = NULL;
   TTCN_Module *only_runnable = Module_List::single_control_part();
 
-  while ((c = getopt(argc, argv, "lv")) != -1) {
+  while ((c = getopt(argc, argv, "b:hlv")) != -1) {
     switch (c) {
+    case 'b':
+      if (bflag || lflag || vflag) errflag = TRUE; // duplicate or conflicting
+      else {
+        bflag = TRUE;
+        ttcn3_debugger.set_initial_batch_file(optarg);
+      }
+      break;
+    case 'h':
+      if (hflag || lflag || vflag) errflag = TRUE; // duplicate or conflicting
+      else {
+        hflag = TRUE;
+        ttcn3_debugger.set_halt_at_start();
+      }
+      break;
     case 'l':
       if (lflag || vflag) errflag = TRUE; // duplicate or conflicting
       else lflag = TRUE;
@@ -124,13 +140,15 @@ int main(int argc, char *argv[])
     }
   }
 
-  if (lflag || vflag) {
-    if (optind != argc) errflag = TRUE; // -l or -v and non-option arg
-  } else {
-    if (optind > argc - 1) { // no config file argument
-      errflag = (only_runnable == 0);
+  if (!errflag) {
+    if (lflag || vflag) {
+      if (optind != argc) errflag = TRUE; // -l or -v and non-option arg
+    } else {
+      if (optind > argc - 1) { // no config file argument
+        errflag = (only_runnable == 0);
+      }
+      else config_file = argv[optind];
     }
-    else config_file = argv[optind];
   }
 
   if (errflag) {
@@ -272,6 +290,9 @@ int main(int argc, char *argv[])
       Module_List::post_init_modules();
 
       for (i = 0; i < execute_list_len; i++) {
+        if (ttcn3_debugger.is_exiting()) {
+          break;
+        }
         if (execute_list[i].testcase_name == NULL)
           Module_List::execute_control(execute_list[i].module_name);
         else if (!strcmp(execute_list[i].testcase_name, "*"))
index 3e111129da3eee89f2c08f5c5eafabf3d200f721..a0396e971df43aabccb45920e189158761201dbf 100644 (file)
@@ -173,7 +173,7 @@ TTCNSTRINGPARSING_COMPONENT "$#&&&(#TTCNSTRINGPARSING_COMPONENT$#&&^#% "
 {LINECOMMENT}  current_line++;
 
 {TTCNSTRINGPARSING} {
-  if (Ttcn_String_Parsing::happening()) {
+  if (Ttcn_String_Parsing::happening() || Debugger_Value_Parsing::happening()) {
     BEGIN(SC_MODULE_PARAMETERS);
     return TtcnStringParsingKeyword;
   } else {
index cd36e38249bdcfda6262c2dffd2e913888151c26..f9bc7948882e7f8d46b2b5d6a895d162953c93ac 100644 (file)
@@ -56,6 +56,8 @@
 #include "LoggingParam.hh"
 
 #include "Profiler.hh"
+#include "Debugger.hh"
+#include "DebugCommands.hh"
 
 #define YYERROR_VERBOSE
 
@@ -71,7 +73,7 @@ static void check_duplicate_option(const char *section_name,
     const char *option_name, boolean& option_flag);
 static void check_ignored_section(const char *section_name);
 static void set_param(Module_Param& module_param);
-static unsigned char char_to_hexdigit(char c);
+static unsigned char char_to_hexdigit_(char c);
 
 static boolean error_flag = FALSE;
 
@@ -397,7 +399,7 @@ all modules in the next module parameter (reduce).
 GrammarRoot:
   ConfigFile
   {
-    if (Ttcn_String_Parsing::happening()) {
+    if (Ttcn_String_Parsing::happening() || Debugger_Value_Parsing::happening()) {
       config_process_error("Config file cannot be parsed as ttcn string");
     }
   }
@@ -692,7 +694,7 @@ SimpleParameterValue:
     for (int i=0; i<n_chars; i++) {
       if ($1[i]=='?') chars_ptr[i] = 16;
       else if ($1[i]=='*') chars_ptr[i] = 17;
-      else chars_ptr[i] = char_to_hexdigit($1[i]);
+      else chars_ptr[i] = char_to_hexdigit_($1[i]);
     }
     Free($1);
     $$ = new Module_Param_Hexstring_Template(n_chars, chars_ptr);
@@ -707,11 +709,11 @@ SimpleParameterValue:
       else if ($1[i]=='*') num = 257;
       else {
         // first digit
-        num = 16 * char_to_hexdigit($1[i]);
+        num = 16 * char_to_hexdigit_($1[i]);
         i++;
         if (i>=str_len) config_process_error("Unexpected end of octetstring pattern");
         // second digit
-        num += char_to_hexdigit($1[i]);
+        num += char_to_hexdigit_($1[i]);
       }
       octet_vec.push_back(num);
     }
@@ -2167,6 +2169,59 @@ Module_Param* process_config_string2ttcn(const char* mp_str, bool is_component)
   }
 }
 
+Module_Param* process_config_debugger_value(const char* mp_str)
+{
+  if (parsed_module_param != NULL || parsing_error_messages != NULL) {
+    ttcn3_debugger.print(DRET_NOTIFICATION,
+      "Internal error: previously parsed TTCN string was not cleared.");
+    return NULL;
+  }
+  // add the hidden keyword
+  std::string mp_string = std::string("$#&&&(#TTCNSTRINGPARSING$#&&^#% ") + mp_str;
+  struct yy_buffer_state *flex_buffer = config_process__scan_bytes(mp_string.c_str(), (int)mp_string.size());
+  if (flex_buffer == NULL) {
+    ttcn3_debugger.print(DRET_NOTIFICATION, "Internal error: flex buffer creation failed.");
+    return NULL;
+  }
+  reset_config_process_lex(NULL);
+  error_flag = FALSE;
+  try {
+    Debugger_Value_Parsing debugger_value_parsing;
+    if (config_process_parse()) {
+      error_flag = TRUE;
+    }
+  }
+  catch (const TC_Error& TC_error) {
+    if (parsed_module_param != NULL) {
+      delete parsed_module_param;
+      parsed_module_param = NULL;
+    }
+    error_flag = TRUE;
+  }
+  config_process_close();
+  config_process_lex_destroy();
+
+  if (error_flag || parsing_error_messages != NULL) {
+    delete parsed_module_param;
+    parsed_module_param = NULL;
+    char* pem = parsing_error_messages != NULL ? parsing_error_messages :
+      mcopystr("Unknown parsing error");
+    parsing_error_messages = NULL;
+    ttcn3_debugger.print(DRET_NOTIFICATION, "%s", pem);
+    Free(pem);
+    return NULL;
+  }
+  else {
+    if (parsed_module_param == NULL) {
+      ttcn3_debugger.print(DRET_NOTIFICATION, "Internal error: could not parse TTCN string.");
+      return NULL;
+    }
+    Module_Param* ret_val = parsed_module_param;
+    parsed_module_param = NULL;
+    return ret_val;
+  }
+}
+
 boolean process_config_string(const char *config_string, int string_len)
 {
   error_flag = FALSE;
@@ -2245,14 +2300,20 @@ boolean process_config_file(const char *file_name)
 
 void config_process_error_f(const char *error_str, ...)
 {
-  if (Ttcn_String_Parsing::happening()) {
+  if (Ttcn_String_Parsing::happening() || Debugger_Value_Parsing::happening()) {
     va_list p_var;
     va_start(p_var, error_str);
     char* error_msg_str = mprintf_va_list(error_str, p_var);
     va_end(p_var);
     if (parsing_error_messages!=NULL) parsing_error_messages = mputc(parsing_error_messages, '\n');
-    parsing_error_messages = mputprintf(parsing_error_messages,
-      "Parse error in line %d, at or before token `%s': %s", config_process_get_current_line(), config_process_text, error_msg_str);
+    if (Debugger_Value_Parsing::happening()) {
+      parsing_error_messages = mputprintf(parsing_error_messages,
+        "Parse error at or before token `%s': %s", config_process_text, error_msg_str);
+    }
+    else { // Ttcn_String_Parsing::happening()
+      parsing_error_messages = mputprintf(parsing_error_messages,
+        "Parse error in line %d, at or before token `%s': %s", config_process_get_current_line(), config_process_text, error_msg_str);
+    }
     Free(error_msg_str);
     error_flag = TRUE;
     return;
@@ -2334,13 +2395,13 @@ static void set_param(Module_Param& param)
   }
 }
 
-unsigned char char_to_hexdigit(char c)
+unsigned char char_to_hexdigit_(char c)
 {
   if (c >= '0' && c <= '9') return c - '0';
   else if (c >= 'A' && c <= 'F') return c - 'A' + 10;
   else if (c >= 'a' && c <= 'f') return c - 'a' + 10;
   else {
-    config_process_error_f("char_to_hexdigit(): invalid argument: %c", c);
+    config_process_error_f("char_to_hexdigit_(): invalid argument: %c", c);
     return 0; // to avoid warning
   }
 }
index 200dbd202fef065c9a080e2369048debee380ed8..7812e8f45712d0419f88def2def9920d68791650 100644 (file)
                     <xs:element name="outParamBoundness" minOccurs="0" maxOccurs="1" type="xs:boolean" />
                     <xs:element name="omitInValueList" minOccurs="0" maxOccurs="1" type="xs:boolean" />
                     <xs:element name="warningsForBadVariants" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+                    <xs:element name="activateDebugger" minOccurs="0" maxOccurs="1" type="xs:boolean" />
                     <xs:element name="quietly" minOccurs="0" maxOccurs="1" type="xs:boolean" />
                     <xs:element name="namingRules" minOccurs="0" maxOccurs="1" type="xs:normalizedString" />
                     <xs:element name="disableSubtypeChecking" minOccurs="0" maxOccurs="1" type="xs:boolean" />
index 6dbb2c13d24fa789eb30ac9fc72f6bda43d8b78c..5989dd54e61dbe35ef5ecdc0fe5d30cc539bf83f 100644 (file)
@@ -119,11 +119,11 @@ endif
 #
 # Do not modify these unless you know what you are doing...
 #
-SOLARIS_LIBS = -lxnet -L$(XMLDIR)/lib -lxml2 -lresolv
-SOLARIS8_LIBS = -lxnet -L$(XMLDIR)/lib -lxml2 -lresolv -lnsl -lsocket
-LINUX_LIBS = -L$(XMLDIR)/lib -lxml2 -lpthread -lrt
-FREEBSD_LIBS = -L$(XMLDIR)/lib -lxml2
-WIN32_LIBS = -L$(XMLDIR)/lib -lxml2
+SOLARIS_LIBS = -lxnet -L$(XMLDIR)/lib -lxml2 -lresolv -lcurses
+SOLARIS8_LIBS = -lxnet -L$(XMLDIR)/lib -lxml2 -lresolv -lnsl -lsocket -lcurses
+LINUX_LIBS = -L$(XMLDIR)/lib -lxml2 -lpthread -lrt -lncurses
+FREEBSD_LIBS = -L$(XMLDIR)/lib -lxml2 -lncurses
+WIN32_LIBS = -L$(XMLDIR)/lib -lxml2 -lncurses
 
 #
 # Rules for building the executable...
index 8b7f6f3598206c1d283fa99bef07a283311b9526..56b44142e4787de83534ad22a8cc9dea77a92313 100644 (file)
@@ -113,11 +113,11 @@ endif
 #
 # Do not modify these unless you know what you are doing...
 #
-SOLARIS_LIBS = -lxnet -L$(XMLDIR)/lib -lxml2 
-SOLARIS8_LIBS = -lxnet -L$(XMLDIR)/lib -lxml2 -lnsl -lsocket -lresolv
-LINUX_LIBS = -L$(XMLDIR)/lib -lxml2 -lpthread -lrt
-FREEBSD_LIBS = -L$(XMLDIR)/lib -lxml2
-WIN32_LIBS = -L$(XMLDIR)/lib -lxml2
+SOLARIS_LIBS = -lxnet -L$(XMLDIR)/lib -lxml2 -lcurses
+SOLARIS8_LIBS = -lxnet -L$(XMLDIR)/lib -lxml2 -lnsl -lsocket -lresolv -lcurses
+LINUX_LIBS = -L$(XMLDIR)/lib -lxml2 -lpthread -lrt -lncurses
+FREEBSD_LIBS = -L$(XMLDIR)/lib -lxml2 -lncurses
+WIN32_LIBS = -L$(XMLDIR)/lib -lxml2 -lncurses
 
 #
 # Rules for building the executable...
index c276d7e747c70d73930af9382e9aa614435bd452..f3d50efeb23bf99f436f868cc0e6aa8f56f63a9b 100644 (file)
@@ -109,11 +109,11 @@ endif
 #
 # Do not modify these unless you know what you are doing...
 #
-SOLARIS_LIBS = -lxnet -L$(XMLDIR)/lib -lxml2 -lresolv
-SOLARIS8_LIBS = -lxnet -L$(XMLDIR)/lib -lxml2 -lresolv -lnsl -lsocket
-LINUX_LIBS = -L$(XMLDIR)/lib -lxml2 -lpthread -lrt
-FREEBSD_LIBS = -L$(XMLDIR)/lib -lxml2
-WIN32_LIBS = -L$(XMLDIR)/lib -lxml2
+SOLARIS_LIBS = -lxnet -L$(XMLDIR)/lib -lxml2 -lresolv -lcurses
+SOLARIS8_LIBS = -lxnet -L$(XMLDIR)/lib -lxml2 -lresolv -lnsl -lsocket -lcurses
+LINUX_LIBS = -L$(XMLDIR)/lib -lxml2 -lpthread -lrt -lncurses
+FREEBSD_LIBS = -L$(XMLDIR)/lib -lxml2 -lncurses
+WIN32_LIBS = -L$(XMLDIR)/lib -lxml2 -lncurses
 
 #
 # Rules for building the executable...
index 94ee3ccf76521bdcc66d8dfe575932c4b577aa92..3a31f3cba349400c25ad491d827090c8f5be4ed4 100644 (file)
@@ -109,11 +109,11 @@ endif
 #
 # Do not modify these unless you know what you are doing...
 #
-SOLARIS_LIBS = -lxnet -L$(XMLDIR)/lib -lxml2 
-SOLARIS8_LIBS = -lxnet -L$(XMLDIR)/lib -lxml2 -lnsl -lsocket -lresolv
-LINUX_LIBS = -L$(XMLDIR)/lib -lxml2 -lpthread -lrt
-FREEBSD_LIBS = -L$(XMLDIR)/lib -lxml2
-WIN32_LIBS = -L$(XMLDIR)/lib -lxml2
+SOLARIS_LIBS = -lxnet -L$(XMLDIR)/lib -lxml2 -lcurses
+SOLARIS8_LIBS = -lxnet -L$(XMLDIR)/lib -lxml2 -lnsl -lsocket -lresolv -lcurses
+LINUX_LIBS = -L$(XMLDIR)/lib -lxml2 -lpthread -lrt -lncurses
+FREEBSD_LIBS = -L$(XMLDIR)/lib -lxml2 -lncurses
+WIN32_LIBS = -L$(XMLDIR)/lib -lxml2 -lncurses
 
 #
 # Rules for building the executable...
index a217c39d0f6756c892137a0c2f6680c3e666a618..3216e558a95b2c45c50b0943a232e89d8b98e28b 100644 (file)
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *   Balasko, Jeno
+ *   Baranyi, Botond
  *   Bene, Tamas
  *   Beres, Szabolcs
  *   Delic, Adam
@@ -56,6 +57,7 @@
 #define SHELL_TEXT "!"
 #define EXIT_TEXT "quit"
 #define EXIT_TEXT2 "exit"
+#define BATCH_TEXT "batch"
 #define SHELL_ESCAPE '!'
 #define TTCN3_HISTORY_FILENAME ".ttcn3_history"
 
@@ -100,6 +102,8 @@ static const Command command_list[] = {
     "Execute commands in subshell." },
   { EXIT_TEXT, &Cli::exitCallback, EXIT_TEXT, "Exit Main Controller." },
   { EXIT_TEXT2, &Cli::exitCallback, EXIT_TEXT2, "Exit Main Controller." },
+  { BATCH_TEXT, &Cli::executeBatchFile, BATCH_TEXT " <batch_file>",
+    "Run commands from batch file." },
   { NULL, NULL, NULL, NULL }
 };
 
@@ -113,21 +117,24 @@ struct DebugCommand {
 static const DebugCommand debug_command_list[] = {
   { D_SWITCH_TEXT, D_SWITCH, D_SWITCH_TEXT " on|off",
     "Switch the debugger on or off." },
-  { D_ADD_BREAKPOINT_TEXT, D_ADD_BREAKPOINT,
-    D_ADD_BREAKPOINT_TEXT " <module> <line>",
-    "Add breakpoint at specified location." },
+  { D_SET_BREAKPOINT_TEXT, D_SET_BREAKPOINT,
+    D_SET_BREAKPOINT_TEXT " <module> <line> [<batch_file>]",
+    "Add a breakpoint at the specified location, or change the batch file of "
+    " an existing breakpoint." },
   { D_REMOVE_BREAKPOINT_TEXT, D_REMOVE_BREAKPOINT,
-    D_REMOVE_BREAKPOINT_TEXT " <module> <line>",
-    "Remove breakpoint from specified location." },
-  { D_SET_ERROR_BEHAVIOR_TEXT, D_SET_ERROR_BEHAVIOR,
-    D_SET_ERROR_BEHAVIOR_TEXT " yes|no",
-    "Set whether to halt test execution when component verdict is set to 'error'." },
-  { D_SET_FAIL_BEHAVIOR_TEXT, D_SET_FAIL_BEHAVIOR,
-    D_SET_FAIL_BEHAVIOR_TEXT " yes|no",
-    "Set whether to halt test execution when component verdict is set to 'fail'." },
+    D_REMOVE_BREAKPOINT_TEXT " all|<module> [all|<line>]", "Remove a breakpoint, "
+    "or all breakpoints from a module, or all breakpoints from all modules." },
+  { D_SET_AUTOMATIC_BREAKPOINT_TEXT, D_SET_AUTOMATIC_BREAKPOINT,
+    D_SET_AUTOMATIC_BREAKPOINT_TEXT " error|fail on|off [<batch_file>]",
+    "Switch an automatic breakpoint (truggered by an event) on or off, and/or "
+    "change its batch file." },
   { D_SET_OUTPUT_TEXT, D_SET_OUTPUT,
     D_SET_OUTPUT_TEXT " console|file|both [file_name]",
     "Set the output of the debugger." },
+  { D_SET_GLOBAL_BATCH_FILE_TEXT, D_SET_GLOBAL_BATCH_FILE,
+    D_SET_GLOBAL_BATCH_FILE_TEXT " on|off [batch_file_name]",
+    "Set whether a batch file should be executed automatically when test execution "
+    "is halted (breakpoint-specific batch files override this setting)." },
   { D_SET_COMPONENT_TEXT, D_SET_COMPONENT,
     D_SET_COMPONENT_TEXT " mtc|<component_reference>",
     "Set the test component to print debug information from." },
@@ -139,8 +146,9 @@ static const DebugCommand debug_command_list[] = {
     D_LIST_VARIABLES_TEXT " local|global|comp|all [pattern]",
     "List variable names." },
   { D_PRINT_VARIABLE_TEXT, D_PRINT_VARIABLE,
-    D_PRINT_VARIABLE_TEXT " <variable_name>[{ <variable_name>}]",
-    "Print current value of one or more variables." },
+    D_PRINT_VARIABLE_TEXT " <variable_name>|$ [{ <variable_name>|$}]",
+    "Print current value of one or more variables ('$' is substituted with the "
+    "result of the last " D_LIST_VARIABLES_TEXT " command)." },
   { D_OVERWRITE_VARIABLE_TEXT, D_OVERWRITE_VARIABLE,
     D_OVERWRITE_VARIABLE_TEXT " <variable_name> <value>",
     "Overwrite the current value of a variable." },
@@ -160,12 +168,6 @@ static const DebugCommand debug_command_list[] = {
   { D_CONTINUE_TEXT, D_CONTINUE, D_CONTINUE_TEXT, "Resume halted test execution." },
   { D_EXIT_TEXT, D_EXIT, D_EXIT_TEXT " test|all",
     "Exit the current test or the execution of all tests." },
-  { D_BATCH_TEXT, D_BATCH, D_BATCH_TEXT " <batch_file_name>",
-    "Run commands from batch file." },
-  { D_SET_HALTING_BATCH_FILE_TEXT, D_SET_HALTING_BATCH_FILE,
-    D_SET_HALTING_BATCH_FILE_TEXT " yes|no [batch_file_name]",
-    "Set whether a batch file should be executed automatically when test execution "
-    "is halted by the debugger." },
   { NULL, D_ERROR, NULL, NULL }
 };
 
@@ -802,6 +804,35 @@ void Cli::exitCallback(const char *arguments)
   }
 }
 
+void Cli::executeBatchFile(const char* filename)
+{
+  FILE* fp = fopen(filename, "r");
+  if (fp == NULL) {
+    printf("Failed to open file '%s' for reading.\n", filename);
+    return;
+  }
+  else {
+    printf("Executing batch file '%s'.\n", filename);
+  }
+  char line[1024];
+  while (fgets(line, sizeof(line), fp) != NULL) {
+    size_t len = strlen(line);
+    if (line[len - 1] == '\n') {
+      line[len - 1] = '\0';
+      --len;
+    }
+    if (len != 0) {
+      printf("%s\n", line);
+      processCommand(line);
+    }
+  }
+  if (!feof(fp)) {
+    printf("Error occurred while reading batch file '%s' (error code: %d).\n",
+      filename, ferror(fp));
+  }
+  fclose(fp);
+}
+
 //----------------------------------------------------------------------------
 // PRIVATE
 // Command completion function implementation for readline() library.
index a552efa2084d93dbe0356d5e2cadfb4a0183d2f1..190de8f7edbb9c54631f900051d7d221f93a8ef8 100644 (file)
@@ -90,6 +90,8 @@ public:
   void helpCallback(const char *arguments);
   void shellCallback(const char *arguments);
   void exitCallback(const char *arguments);
+  
+  virtual void executeBatchFile(const char* filename);
 
 private:
   /**
index f4258e16fbfb8cd3fe59a65bd5d02d1144c80c22..64f0dfe6fb6c0b7520b8b02ad61c86c25167e4bc 100644 (file)
@@ -8,6 +8,7 @@
  * Contributors:
  *   Baji, Laszlo
  *   Balasko, Jeno
+ *   Baranyi, Botond
  *   Bene, Tamas
  *   Feher, Csaba
  *   Forstner, Matyas
@@ -2711,7 +2712,8 @@ void MainController::handle_hc_data(host_struct *hc, boolean recv_from_socket)
   if (recv_len > 0) {
     try {
       while (text_buf.is_message()) {
-        text_buf.pull_int(); // message length
+        int msg_len = text_buf.pull_int().get_val();
+        int msg_end = text_buf.get_pos() + msg_len;
         int message_type = text_buf.pull_int().get_val();
         switch (message_type) {
         case MSG_ERROR:
@@ -2733,7 +2735,7 @@ void MainController::handle_hc_data(host_struct *hc, boolean recv_from_socket)
           process_hc_ready(hc);
           break;
         case MSG_DEBUG_RETURN_VALUE:
-          process_debug_return_value(*hc->text_buf, hc->log_source, false);
+          process_debug_return_value(*hc->text_buf, hc->log_source, msg_end, false);
           break;
         default:
           error("Invalid message type (%d) was received on HC "
@@ -2874,10 +2876,17 @@ void MainController::handle_tc_data(component_struct *tc,
           process_unmapped(tc);
           break;
         case MSG_DEBUG_RETURN_VALUE:
-          process_debug_return_value(*tc->text_buf, tc->log_source, tc == mtc);
+          process_debug_return_value(*tc->text_buf, tc->log_source, message_end,
+            tc == mtc);
           break;
         case MSG_DEBUG_HALT_REQ:
-          process_debug_halt_req(tc);
+          process_debug_broadcast_req(tc, D_HALT);
+          break;
+        case MSG_DEBUG_CONTINUE_REQ:
+          process_debug_broadcast_req(tc, D_CONTINUE);
+          break;
+        case MSG_DEBUG_BATCH:
+          process_debug_batch(tc);
           break;
         default:
           if (tc == mtc) {
@@ -3098,11 +3107,20 @@ void MainController::clean_up()
   debugger_settings.output_file = NULL;
   Free(debugger_settings.error_behavior);
   debugger_settings.error_behavior = NULL;
+  Free(debugger_settings.error_batch_file);
+  debugger_settings.error_batch_file = NULL;
   Free(debugger_settings.fail_behavior);
   debugger_settings.fail_behavior = NULL;
+  Free(debugger_settings.fail_batch_file);
+  debugger_settings.fail_batch_file = NULL;
+  Free(debugger_settings.global_batch_state);
+  debugger_settings.global_batch_state = NULL;
+  Free(debugger_settings.global_batch_file);
+  debugger_settings.global_batch_file = NULL;
   for (int i = 0; i < debugger_settings.nof_breakpoints; ++i) {
     Free(debugger_settings.breakpoints[i].module);
     Free(debugger_settings.breakpoints[i].line);
+    Free(debugger_settings.breakpoints[i].batch_file);
   }
   debugger_settings.nof_breakpoints = 0;
   Free(debugger_settings.breakpoints);
@@ -3412,15 +3430,20 @@ void MainController::send_debug_setup(host_struct *hc)
   Text_Buf text_buf;
   text_buf.push_int(MSG_DEBUG_COMMAND);
   text_buf.push_int(D_SETUP);
-  text_buf.push_int(5 + 2 * debugger_settings.nof_breakpoints);
+  text_buf.push_int(9 + 3 * debugger_settings.nof_breakpoints);
   text_buf.push_string(debugger_settings.on_switch);
   text_buf.push_string(debugger_settings.output_file);
   text_buf.push_string(debugger_settings.output_type);
   text_buf.push_string(debugger_settings.error_behavior);
+  text_buf.push_string(debugger_settings.error_batch_file);
   text_buf.push_string(debugger_settings.fail_behavior);
+  text_buf.push_string(debugger_settings.fail_batch_file);
+  text_buf.push_string(debugger_settings.global_batch_state);
+  text_buf.push_string(debugger_settings.global_batch_file);
   for (int i = 0; i < debugger_settings.nof_breakpoints; ++i) {
     text_buf.push_string(debugger_settings.breakpoints[i].module);
     text_buf.push_string(debugger_settings.breakpoints[i].line);
+    text_buf.push_string(debugger_settings.breakpoints[i].batch_file);
   }
   send_message(hc->hc_fd, text_buf);
 }
@@ -5415,66 +5438,145 @@ void MainController::process_unmapped(component_struct *tc)
   status_change();
 }
 
-void MainController::process_debug_return_value(Text_Buf& text_buf, char* log_source, bool from_mtc)
+void MainController::process_debug_return_value(Text_Buf& text_buf, char* log_source,
+                                                int msg_end, bool from_mtc)
 {
   int return_type = text_buf.pull_int().get_val();
-  timeval tv;
-  tv.tv_sec = text_buf.pull_int().get_val();
-  tv.tv_usec = text_buf.pull_int().get_val();
-  char* message = text_buf.pull_string();
-  if (return_type == DRET_DATA) {
-    char* result = mprintf("\n%s", message);
-    notify(&tv, log_source, TTCN_Logger::DEBUG_UNQUALIFIED, result);
-    Free(result);
-  }
-  else {
-    if (from_mtc) {
-      if (return_type == DRET_SETTING_CHANGE) {
-        switch (last_debug_command.command) {
-        case D_SWITCH:
-          Free(debugger_settings.on_switch);
-          debugger_settings.on_switch = mcopystr(last_debug_command.arguments);
+  if (text_buf.get_pos() != msg_end) {
+    timeval tv;
+    tv.tv_sec = text_buf.pull_int().get_val();
+    tv.tv_usec = text_buf.pull_int().get_val();
+    char* message = text_buf.pull_string();
+    if (return_type == DRET_DATA) {
+      char* result = mprintf("\n%s", message);
+      notify(&tv, log_source, TTCN_Logger::DEBUG_UNQUALIFIED, result);
+      Free(result);
+    }
+    else {
+      notify(&tv, log_source, TTCN_Logger::DEBUG_UNQUALIFIED, message);
+    }
+    delete [] message;
+  }
+  if (from_mtc) {
+    if (return_type == DRET_SETTING_CHANGE) {
+      switch (last_debug_command.command) {
+      case D_SWITCH:
+        Free(debugger_settings.on_switch);
+        debugger_settings.on_switch = mcopystr(last_debug_command.arguments);
+        break;
+      case D_SET_OUTPUT: {
+        Free(debugger_settings.output_type);
+        Free(debugger_settings.output_file);
+        debugger_settings.output_file = NULL;
+        size_t args_len = mstrlen(last_debug_command.arguments);
+        size_t start = 0;
+        size_t end = 0;
+        get_next_argument_loc(last_debug_command.arguments, args_len, start, end);
+        debugger_settings.output_type = mcopystrn(last_debug_command.arguments + start, end - start);
+        if (end < args_len) {
+          start = end;
+          get_next_argument_loc(last_debug_command.arguments, args_len, start, end);
+          debugger_settings.output_file = mcopystrn(last_debug_command.arguments + start, end - start);
+        }
+        break; }
+      case D_SET_AUTOMATIC_BREAKPOINT: {
+        size_t args_len = mstrlen(last_debug_command.arguments);
+        size_t start = 0;
+        size_t end = 0;
+        get_next_argument_loc(last_debug_command.arguments, args_len, start, end);
+        char* event_str = mcopystrn(last_debug_command.arguments + start, end - start);
+        char** event_behavior;
+        char** event_batch_file;
+        if (!strcmp(event_str, "error")) {
+          event_behavior = &debugger_settings.error_behavior;
+          event_batch_file = &debugger_settings.error_batch_file;
+        }
+        else if (!strcmp(event_str, "fail")) {
+          event_behavior = &debugger_settings.fail_behavior;
+          event_batch_file = &debugger_settings.fail_batch_file;
+        }
+        else { // should never happen
+          Free(event_str);
           break;
-        case D_SET_OUTPUT: {
-          Free(debugger_settings.output_type);
-          Free(debugger_settings.output_file);
-          debugger_settings.output_file = NULL;
-          size_t args_len = mstrlen(last_debug_command.arguments);
-          size_t start = 0;
-          size_t end = 0;
+        }
+        Free(event_str);
+        Free(*event_behavior);
+        Free(*event_batch_file);
+        *event_batch_file = NULL;
+        start = end;
+        get_next_argument_loc(last_debug_command.arguments, args_len, start, end);
+        *event_behavior = mcopystrn(last_debug_command.arguments + start, end - start);
+        if (end < args_len) {
+          start = end;
+          get_next_argument_loc(last_debug_command.arguments, args_len, start, end);
+          *event_batch_file = mcopystrn(last_debug_command.arguments + start, end - start);
+        }
+        break; }
+      case D_SET_GLOBAL_BATCH_FILE: {
+        Free(debugger_settings.global_batch_state);
+        Free(debugger_settings.global_batch_file);
+        debugger_settings.global_batch_file = NULL;
+        size_t args_len = mstrlen(last_debug_command.arguments);
+        size_t start = 0;
+        size_t end = 0;
+        get_next_argument_loc(last_debug_command.arguments, args_len, start, end);
+        debugger_settings.global_batch_state = mcopystrn(last_debug_command.arguments + start, end - start);
+        if (end < args_len) {
+          start = end;
           get_next_argument_loc(last_debug_command.arguments, args_len, start, end);
-          debugger_settings.output_type = mcopystrn(last_debug_command.arguments + start, end - start);
-          if (end < args_len) {
-            start = end;
-            get_next_argument_loc(last_debug_command.arguments, args_len, start, end);
-            debugger_settings.output_file = mcopystrn(last_debug_command.arguments + start, end - start);
+          debugger_settings.global_batch_file = mcopystrn(last_debug_command.arguments + start, end - start);
+        }
+        break; }
+      case D_SET_BREAKPOINT: {
+        size_t args_len = mstrlen(last_debug_command.arguments);
+        size_t start = 0;
+        size_t end = 0;
+        get_next_argument_loc(last_debug_command.arguments, args_len, start, end);
+        char* module = mcopystrn(last_debug_command.arguments + start, end - start);
+        start = end;
+        get_next_argument_loc(last_debug_command.arguments, args_len, start, end);
+        char* line = mcopystrn(last_debug_command.arguments + start, end - start);
+        char* batch_file = NULL;
+        if (end < args_len) {
+          start = end;
+          get_next_argument_loc(last_debug_command.arguments, args_len, start, end);
+          batch_file = mcopystrn(last_debug_command.arguments + start, end - start);
+        }
+        int pos;
+        for (pos = 0; pos < debugger_settings.nof_breakpoints; ++pos) {
+          if (!strcmp(debugger_settings.breakpoints[pos].module, module) &&
+              !strcmp(debugger_settings.breakpoints[pos].line, line)) {
+            break;
           }
-          break; }
-        case D_SET_ERROR_BEHAVIOR:
-          Free(debugger_settings.error_behavior);
-          debugger_settings.error_behavior = mcopystr(last_debug_command.arguments);
-          break;
-        case D_SET_FAIL_BEHAVIOR:
-          Free(debugger_settings.fail_behavior);
-          debugger_settings.fail_behavior = mcopystr(last_debug_command.arguments);
-          break;
-        case D_ADD_BREAKPOINT: {
+        }
+        if (pos == debugger_settings.nof_breakpoints) {
+          // not found, add a new one
           debugger_settings.breakpoints = (debugger_settings_struct::breakpoint_struct*)
             Realloc(debugger_settings.breakpoints, (debugger_settings.nof_breakpoints + 1) *
             sizeof(debugger_settings_struct::breakpoint_struct));
-          size_t args_len = mstrlen(last_debug_command.arguments);
-          size_t start = 0;
-          size_t end = 0;
-          get_next_argument_loc(last_debug_command.arguments, args_len, start, end);
-          debugger_settings.breakpoints[debugger_settings.nof_breakpoints].module =
-            mcopystrn(last_debug_command.arguments + start, end - start);
-          start = end;
-          get_next_argument_loc(last_debug_command.arguments, args_len, start, end);
-          debugger_settings.breakpoints[debugger_settings.nof_breakpoints].line =
-            mcopystrn(last_debug_command.arguments + start, end - start);
           ++debugger_settings.nof_breakpoints;
-          break; }
-        case D_REMOVE_BREAKPOINT: {
+          debugger_settings.breakpoints[pos].module = module;
+          debugger_settings.breakpoints[pos].line = line;
+        }
+        else {
+          Free(debugger_settings.breakpoints[pos].batch_file);
+          Free(module);
+          Free(line);
+        }
+        debugger_settings.breakpoints[pos].batch_file = batch_file;
+        break; }
+      case D_REMOVE_BREAKPOINT:
+        if (!strcmp(last_debug_command.arguments, "all")) {
+          for (int i = 0; i < debugger_settings.nof_breakpoints; ++i) {
+            Free(debugger_settings.breakpoints[i].module);
+            Free(debugger_settings.breakpoints[i].line);
+            Free(debugger_settings.breakpoints[i].batch_file);
+          }
+          Free(debugger_settings.breakpoints);
+          debugger_settings.breakpoints = NULL;
+          debugger_settings.nof_breakpoints = 0;
+        }
+        else {
           size_t args_len = mstrlen(last_debug_command.arguments);
           size_t start = 0;
           size_t end = 0;
@@ -5483,53 +5585,68 @@ void MainController::process_debug_return_value(Text_Buf& text_buf, char* log_so
           start = end;
           get_next_argument_loc(last_debug_command.arguments, args_len, start, end);
           char* line = mcopystrn(last_debug_command.arguments + start, end - start);
+          bool all_in_module = !strcmp(line, "all");
           for (int i = 0; i < debugger_settings.nof_breakpoints; ++i) {
             if (!strcmp(debugger_settings.breakpoints[i].module, module) &&
-                !strcmp(debugger_settings.breakpoints[i].line, line)) {
+                (all_in_module || !strcmp(debugger_settings.breakpoints[i].line, line))) {
               Free(debugger_settings.breakpoints[i].module);
               Free(debugger_settings.breakpoints[i].line);
+              Free(debugger_settings.breakpoints[i].batch_file);
               for (int j = i; j < debugger_settings.nof_breakpoints - 1; ++j) {
                 debugger_settings.breakpoints[j] = debugger_settings.breakpoints[j + 1];
               }
-              debugger_settings.breakpoints = (debugger_settings_struct::breakpoint_struct*)
-                Realloc(debugger_settings.breakpoints, (debugger_settings.nof_breakpoints - 1) *
-                sizeof(debugger_settings_struct::breakpoint_struct));
               --debugger_settings.nof_breakpoints;
-              break;
+              if (!all_in_module) {
+                break;
+              }
             }
           }
+          debugger_settings.breakpoints = (debugger_settings_struct::breakpoint_struct*)
+            Realloc(debugger_settings.breakpoints, debugger_settings.nof_breakpoints *
+            sizeof(debugger_settings_struct::breakpoint_struct));
           Free(module);
           Free(line);
-          break; }
-        default:
-          break;
         }
+        break;
+      default:
+        break;
       }
-      else if (return_type == DRET_EXIT_ALL) {
-        stop_requested = TRUE;
-      }
     }
-    notify(&tv, log_source, TTCN_Logger::DEBUG_UNQUALIFIED, message);
+    else if (return_type == DRET_EXIT_ALL) {
+      stop_requested = TRUE;
+    }
   }
-  delete [] message;
 }
 
-void MainController::process_debug_halt_req(component_struct* tc)
+void MainController::process_debug_broadcast_req(component_struct* tc, int commandID)
 {
-  //lock();
-  // don't send the halt command back to the requesting component
+  // don't send the command back to the requesting component
   if (tc != mtc) {
-    send_debug_command(mtc->tc_fd, D_HALT, "");
+    send_debug_command(mtc->tc_fd, commandID, "");
   }
   for (component i = tc_first_comp_ref; i < n_components; ++i) {
     component_struct* comp = components[i];
     if (tc != comp && comp->tc_state != PTC_STALE && comp->tc_state != TC_EXITED) {
-      send_debug_command(comp->tc_fd, D_HALT, "");
+      send_debug_command(comp->tc_fd, commandID, "");
     }
   }
   debugger_active_tc = tc;
-  //status_change();
-  //unlock();
+  for (int i = 0; i < n_hosts; i++) {
+    host_struct* host = hosts[i];
+    if (host->hc_state != HC_DOWN) {
+      send_debug_command(hosts[i]->hc_fd, commandID, "");
+    }
+  }
+}
+
+void MainController::process_debug_batch(component_struct* tc)
+{
+  Text_Buf& text_buf = *tc->text_buf;
+  const char* batch_file = text_buf.pull_string();
+  unlock();
+  ui->executeBatchFile(batch_file);
+  lock();
+  delete [] batch_file;
 }
 
 void MainController::process_testcase_started()
@@ -5739,7 +5856,11 @@ void MainController::initialize(UserInterface& par_ui, int par_max_ptcs)
   debugger_settings.output_type = NULL;
   debugger_settings.output_file = NULL;
   debugger_settings.error_behavior = NULL;
+  debugger_settings.error_batch_file = NULL;
   debugger_settings.fail_behavior = NULL;
+  debugger_settings.fail_batch_file = NULL;
+  debugger_settings.global_batch_state = NULL;
+  debugger_settings.global_batch_file = NULL;
   debugger_settings.nof_breakpoints = 0;
   debugger_settings.breakpoints = NULL;
   last_debug_command.command = D_ERROR;
@@ -6401,43 +6522,48 @@ void MainController::debug_command(int commandID, char* arguments)
     case D_STEP_OVER:
     case D_STEP_INTO:
     case D_STEP_OUT:
-    case D_RUN_TO_CURSOR:
       // it's a data printing or stepping command, needs to be sent to the
       // active component
-      if (debugger_active_tc == NULL) {
-        // set the MTC as active if test execution hasn't halted and no
-        // D_SET_COMPONENT command has been issued
+      if (debugger_active_tc == NULL ||
+          debugger_active_tc->tc_state == PTC_STALE ||
+          debugger_active_tc->tc_state == TC_EXITED) {
+        // set the MTC as active in the beginning or if the active PTC has
+        // finished executing
         debugger_active_tc = mtc;
       }
       send_debug_command(debugger_active_tc->tc_fd, commandID, arguments);
       break;
     case D_SWITCH:
     case D_SET_OUTPUT:
-    case D_SET_ERROR_BEHAVIOR:
-    case D_SET_FAIL_BEHAVIOR:
-    case D_ADD_BREAKPOINT:
+    case D_SET_AUTOMATIC_BREAKPOINT:
+    case D_SET_GLOBAL_BATCH_FILE:
+    case D_SET_BREAKPOINT:
     case D_REMOVE_BREAKPOINT:
-      // it's a global setting, needs to be sent to all HCs and TCs
-      for (int i = 0; i < n_hosts; i++) {
-        send_debug_command(hosts[i]->hc_fd, commandID, arguments);
-      }
-      // store this command, the next MSG_DEBUG_RETURN_VALUE message might need it
+      // it's a global setting, store it, the next MSG_DEBUG_RETURN_VALUE message
+      // might need it
       last_debug_command.command = commandID;
       Free(last_debug_command.arguments);
       last_debug_command.arguments = mcopystr(arguments);
-      // no break, send it to TCs, too
+      // no break
+    case D_RUN_TO_CURSOR:
     case D_HALT:
     case D_CONTINUE:
     case D_EXIT:
-      // it's a global setting or a command related to the halted state,
-      // needs to be sent to all TCs
+      // it's a global setting, a 'run to' command or a command related to the
+      // halted state, needs to be sent to all HCs and TCs
       send_debug_command(mtc->tc_fd, commandID, arguments);
       for (component i = FIRST_PTC_COMPREF; i < n_components; ++i) {
-        component_struct *comp = components[i];
+        component_structcomp = components[i];
         if (comp != NULL && comp->tc_state != PTC_STALE && comp->tc_state != TC_EXITED) {
           send_debug_command(comp->tc_fd, commandID, arguments);
         }
       }
+      for (int i = 0; i < n_hosts; i++) {
+        host_struct* host = hosts[i];
+        if (host->hc_state != HC_DOWN) {
+          send_debug_command(host->hc_fd, commandID, arguments);
+        }
+      }
       break;
     default:
       break;
@@ -6446,7 +6572,6 @@ void MainController::debug_command(int commandID, char* arguments)
   else {
     notify("Cannot execute debug commands before the MTC is created.");
   }
-  status_change();
   unlock();
 }
 
index 4993bffadcbcf62b0b3854dd594d2002164b29b8..1f0f0a00bb7c45f6399734068a41763bb676ffb6 100644 (file)
@@ -8,6 +8,7 @@
  * Contributors:
  *   Baji, Laszlo
  *   Balasko, Jeno
+ *   Baranyi, Botond
  *   Bene, Tamas
  *   Czimbalmos, Eduard
  *   Feher, Csaba
@@ -247,11 +248,16 @@ struct debugger_settings_struct {
   char* output_type;
   char* output_file;
   char* error_behavior;
+  char* error_batch_file;
   char* fail_behavior;
+  char* fail_batch_file;
+  char* global_batch_state;
+  char* global_batch_file;
   int nof_breakpoints;
   struct breakpoint_struct {
     char* module;
     char* line;
+    char* batch_file;
   }* breakpoints;
 };
 
@@ -436,6 +442,8 @@ class MainController {
   static struct sigaction new_action, old_action;
   static void register_termination_handlers();
   static void termination_handler(int signum);
+  
+  static void execute_batch_file(const char* file_name);
 
 public:
   static void error(const char *fmt, ...)
@@ -588,8 +596,10 @@ private:
   static void process_mapped(component_struct *tc);
   static void process_unmap_req(component_struct *tc);
   static void process_unmapped(component_struct *tc);
-  static void process_debug_return_value(Text_Buf& text_buf, char* log_source, bool from_mtc);
-  static void process_debug_halt_req(component_struct *tc);
+  static void process_debug_return_value(Text_Buf& text_buf, char* log_source,
+    int msg_end, bool from_mtc);
+  static void process_debug_broadcast_req(component_struct *tc, int commandID);
+  static void process_debug_batch(component_struct *tc);
 
   /* Incoming messages from MTC */
   static void process_testcase_started();
index e645ad8cdda10adff28051887c0334bc092148c4..ead29cf819fda1360b4441b669375e86d340137d 100644 (file)
@@ -34,6 +34,11 @@ void UserInterface::initialize()
 
 }
 
+void UserInterface::executeBatchFile(const char* /* filename */)
+{
+  error(/* severity */ 0, "This user interface does not support batch files.");
+}
+
 //----------------------------------------------------------------------------
 // Local Variables:
 // mode: C++
index 0f534f1467ceef673495d4feb33bd0f243e24056..c3f2267a2484380ead89701a9e0bbcd8859d9267 100644 (file)
@@ -68,6 +68,8 @@ public:
      */
     virtual void notify(const struct timeval* timestamp, const char* source,
                         int severity, const char* message) = 0;
+    
+    virtual void executeBatchFile(const char* filename);
 
 };
 
index 9357b044986dedba10c16effb973e69846e5e236..71957f99dddb2b679a8d6863fa8c211ad3caf89d 100644 (file)
@@ -175,12 +175,12 @@ else
   EXESUFFIX :=
 endif # WIN32
 
-SOLARIS_LIBS = -lxnet -L${XMLDIR}/lib -lxml2 -lresolv
-SOLARIS8_LIBS = $(SOLARIS_LIBS) -lresolv
-LINUX_LIBS = -L${XMLDIR}/lib -lxml2 -lpthread -lrt
-FREEBSD_LIBS =
-WIN32_LIBS = -L${XMLDIR}/lib -lxml2
-INTERIX_LIBS = -L${XMLDIR}/lib -lxml2 -liconv
+SOLARIS_LIBS = -lxnet -L${XMLDIR}/lib -lxml2 -lresolv -lcurses
+SOLARIS8_LIBS = $(SOLARIS_LIBS) -lresolv -lcurses
+LINUX_LIBS = -L${XMLDIR}/lib -lxml2 -lpthread -lrt -lncurses
+FREEBSD_LIBS = -lncurses
+WIN32_LIBS = -L${XMLDIR}/lib -lxml2 -lncurses
+INTERIX_LIBS = -L${XMLDIR}/lib -lxml2 -liconv -lncurses
 
 
 #
index 360c2b8b4d14dac90d73c0d09b028e17a56ecdf0..a0c69c23f31f45e42d6aba1a2aa1b0448ab568e6 100644 (file)
@@ -121,11 +121,11 @@ TARGET = POtest$(EXESUFFIX)
 # Do not modify these unless you know what you are doing...
 # Platform specific additional libraries:
 #
-SOLARIS_LIBS  = -lsocket -lnsl -lxml2 -lresolv
-SOLARIS8_LIBS = -lsocket -lnsl -lxml2 -lresolv
-LINUX_LIBS = -lxml2 -lpthread -lrt
-FREEBSD_LIBS = -lxml2 -liconv
-WIN32_LIBS = -lxml2 -liconv
+SOLARIS_LIBS  = -lsocket -lnsl -lxml2 -lresolv -lcurses
+SOLARIS8_LIBS = -lsocket -lnsl -lxml2 -lresolv -lcurses
+LINUX_LIBS = -lxml2 -lpthread -lrt -lncurses
+FREEBSD_LIBS = -lxml2 -liconv -lncurses
+WIN32_LIBS = -lxml2 -liconv -lncurses
 
 #
 # Rules for building the executable...
index 26fc5065bca7418af832b75a23e5f0eb63af9761..b5ff34d4f04a06e4a53e085e9377804f39f00752 100644 (file)
@@ -29,11 +29,11 @@ OBJECTS = $(GENERATED_SOURCES:.cc=.o)
 
 OTHER_FILES = Makefile
 
-SOLARIS_LIBS = -lsocket -lnsl -lxml2 -lresolv
-SOLARIS8_LIBS = -lsocket -lnsl -lxml2 -lresolv
-LINUX_LIBS = -lxml2 -lpthread -lrt
-FREEBSD_LIBS = -lxml2
-WIN32_LIBS = -lxml2
+SOLARIS_LIBS = -lsocket -lnsl -lxml2 -lresolv -lcurses
+SOLARIS8_LIBS = -lsocket -lnsl -lxml2 -lresolv -lcurses
+LINUX_LIBS = -lxml2 -lpthread -lrt -lncurses
+FREEBSD_LIBS = -lxml2 -lncurses
+WIN32_LIBS = -lxml2 -lncurses
 
 
 all: $(TARGET) ;
This page took 0.115437 seconds and 5 git commands to generate.