From f08ff9caff70c6c4ab6ad6e5e22b2cb336e58484 Mon Sep 17 00:00:00 2001 From: Botond Baranyi Date: Wed, 1 Jun 2016 12:29:47 +0200 Subject: [PATCH] Debugger - Stage 3 (artf511247) Change-Id: Id1c3432bf8b314655b84ee653959dda2cd299c41 Signed-off-by: Botond Baranyi --- Makefile | 2 +- compiler2/DebuggerStuff.cc | 64 +- compiler2/Value.cc | 2 - compiler2/asn1/AST_asn1.cc | 43 +- compiler2/compiler.1 | 5 +- compiler2/main.cc | 5 +- compiler2/makefile.c | 33 +- compiler2/ttcn3/AST_ttcn3.cc | 60 +- compiler2/ttcn3_makefilegen.1 | 5 +- compiler2/xpather.cc | 17 +- compiler2/xpather.h | 4 +- core/Communication.cc | 28 +- core/Communication.hh | 3 + core/DebugCommands.hh | 63 +- core/Debugger.cc | 1019 ++++++++++++++--- core/Debugger.hh | 256 ++++- core/DebuggerUI.cc | 263 +++++ core/DebuggerUI.hh | 71 ++ core/Logger.cc | 8 + core/Logger.hh | 4 + core/Makefile | 25 +- core/Message_types.hh | 3 + core/Module_list.cc | 4 + core/Param_Types.cc | 61 +- core/Param_Types.hh | 11 + core/Single_main.cc | 39 +- core/config_process.l | 2 +- core/config_process.y | 81 +- etc/xsd/TPD.xsd | 1 + function_test/BER_EncDec/Makefile | 10 +- function_test/RAW_EncDec/Makefile | 10 +- function_test/Text_EncDec/Makefile | 10 +- function_test/XER_EncDec/Makefile | 10 +- mctr2/cli/Cli.cc | 69 +- mctr2/cli/Cli.h | 2 + mctr2/mctr/MainController.cc | 311 +++-- mctr2/mctr/MainController.h | 14 +- mctr2/mctr/UserInterface.cc | 5 + mctr2/mctr/UserInterface.h | 2 + regression_test/Makefile.regression | 12 +- regression_test/XML/XMLqualif/Makefile | 10 +- .../XML/xsdConverter/Makefile.converter | 10 +- 42 files changed, 2137 insertions(+), 520 deletions(-) create mode 100644 core/DebuggerUI.cc create mode 100644 core/DebuggerUI.hh diff --git a/Makefile b/Makefile index 357f933..bc81ca3 100644 --- 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 diff --git a/compiler2/DebuggerStuff.cc b/compiler2/DebuggerStuff.cc index c34bd6e..f379c09 100644 --- a/compiler2/DebuggerStuff.cc +++ b/compiler2/DebuggerStuff.cc @@ -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(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) diff --git a/compiler2/Value.cc b/compiler2/Value.cc index af36e4e..e6cf738 100644 --- a/compiler2/Value.cc +++ b/compiler2/Value.cc @@ -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: diff --git a/compiler2/asn1/AST_asn1.cc b/compiler2/asn1/AST_asn1.cc index cd87a28..4503ef0 100644 --- a/compiler2/asn1/AST_asn1.cc +++ b/compiler2/asn1/AST_asn1.cc @@ -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(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(\"\");\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); } } diff --git a/compiler2/compiler.1 b/compiler2/compiler.1 index d64cc88..8c0e346 100644 --- a/compiler2/compiler.1 +++ b/compiler2/compiler.1 @@ -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 diff --git a/compiler2/main.cc b/compiler2/main.cc index 5e6cfd5..f01d98e 100644 --- a/compiler2/main.cc +++ b/compiler2/main.cc @@ -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 || diff --git a/compiler2/makefile.c b/compiler2/makefile.c index d77f452..0c5639d 100644 --- a/compiler2/makefile.c +++ b/compiler2/makefile.c @@ -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); } diff --git a/compiler2/ttcn3/AST_ttcn3.cc b/compiler2/ttcn3/AST_ttcn3.cc index 85568e2..6859e36 100644 --- a/compiler2/ttcn3/AST_ttcn3.cc +++ b/compiler2/ttcn3/AST_ttcn3.cc @@ -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(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(\"\");\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" diff --git a/compiler2/ttcn3_makefilegen.1 b/compiler2/ttcn3_makefilegen.1 index fb1c164..a99b01d 100644 --- a/compiler2/ttcn3_makefilegen.1 +++ b/compiler2/ttcn3_makefilegen.1 @@ -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. diff --git a/compiler2/xpather.cc b/compiler2/xpather.cc index 59e14e9..2c6cf1e 100644 --- a/compiler2/xpather.cc +++ b/compiler2/xpather.cc @@ -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, diff --git a/compiler2/xpather.h b/compiler2/xpather.h index 8cfd183..de3f9c9 100644 --- a/compiler2/xpather.h +++ b/compiler2/xpather.h @@ -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, diff --git a/core/Communication.cc b/core/Communication.cc index db8fb8d..44352b9 100644 --- a/core/Communication.cc +++ b/core/Communication.cc @@ -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) diff --git a/core/Communication.hh b/core/Communication.hh index 1147128..3cf058e 100644 --- a/core/Communication.hh +++ b/core/Communication.hh @@ -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. diff --git a/core/DebugCommands.hh b/core/DebugCommands.hh index e5d569c..a2ed895 100644 --- a/core/DebugCommands.hh +++ b/core/DebugCommands.hh @@ -17,46 +17,49 @@ /** 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 */ diff --git a/core/Debugger.cc b/core/Debugger.cc index a5331ca..5d56425 100644 --- a/core/Debugger.cc +++ b/core/Debugger.cc @@ -15,6 +15,8 @@ #include "DebugCommands.hh" #include "Communication.hh" #include "../common/pattern.hh" +#include "Param_Types.hh" +#include "DebuggerUI.hh" #include #include @@ -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(""); @@ -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"); +} diff --git a/core/Debugger.hh b/core/Debugger.hh index 370c034..de6eac1 100644 --- a/core/Debugger.hh +++ b/core/Debugger.hh @@ -27,10 +27,13 @@ /** 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 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 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 call_stack; /** list of breakpoints */ Vector 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 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*)p_var.value)->log(); + ((VALUE_ARRAY*)ptr)->log(); return TTCN_Logger::end_event_log2str(); } + /** variable setting function for value arrays */ + template + static boolean set_value_array(variable_t& p_var, Module_Param& p_new_value) + { + ((VALUE_ARRAY*)p_var.value)->set_param(p_new_value); + return TRUE; + } + /** variable printing function for template arrays */ template 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*)p_var.value)->log(); + index_offset>*)ptr)->log(); return TTCN_Logger::end_event_log2str(); } + /** variable setting function for template arrays */ + template + static boolean set_template_array(variable_t& p_var, Module_Param& p_new_value) + { + ((TEMPLATE_ARRAY*)p_var.value)->set_param(p_new_value); + return TRUE; + } + /** variable printing function for port arrays */ template 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*)p_var.value)->log(); + ((PORT_ARRAY*)ptr)->log(); return TTCN_Logger::end_event_log2str(); } @@ -275,8 +407,9 @@ public: template 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*)p_var.value)->log(); + ((TIMER_ARRAY*)ptr)->log(); return TTCN_Logger::end_event_log2str(); } @@ -284,8 +417,9 @@ public: template 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*)p_var.value)->log(); + ((Lazy_Param*)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 variables; + Vector 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 index 0000000..4ca6e5b --- /dev/null +++ b/core/DebuggerUI.cc @@ -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 +#include + +#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 " []", + "Add a breakpoint at the specified location, or change the batch file of " + " an existing breakpoint." }, + { D_REMOVE_BREAKPOINT_TEXT, D_REMOVE_BREAKPOINT, + D_REMOVE_BREAKPOINT_TEXT " all| [all|]", "Remove a breakpoint, " + "or all breakpoints from a module, or all breakpoints from all modules." }, + { D_SET_AUTOMATIC_BREAKPOINT_TEXT, D_SET_AUTOMATIC_BREAKPOINT, + D_SET_AUTOMATIC_BREAKPOINT_TEXT " error|fail on|off []", + "Switch an automatic breakpoint (truggered by an event) on or off, and/or " + "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|", + "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 " ", + "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 " |$ [{ |$}]", + "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 " ", + "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 " ", + "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 index 0000000..b3da252 --- /dev/null +++ b/core/DebuggerUI.hh @@ -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 */ + diff --git a/core/Logger.cc b/core/Logger.cc index bb0f3e0..9b7d926 100644 --- a/core/Logger.cc +++ b/core/Logger.cc @@ -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 { diff --git a/core/Logger.hh b/core/Logger.hh index c394c95..96d1b2d 100644 --- a/core/Logger.hh +++ b/core/Logger.hh @@ -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; }; diff --git a/core/Makefile b/core/Makefile index a01209d..ea118e1 100644 --- a/core/Makefile +++ b/core/Makefile @@ -75,12 +75,12 @@ TTCN_COMPILER_FLAGS += -o $(APIDIR) # -I. is needed because TitanLoggerApi.hh (generated) does #include 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 diff --git a/core/Message_types.hh b/core/Message_types.hh index b47fa15..b8873d6 100644 --- a/core/Message_types.hh +++ b/core/Message_types.hh @@ -7,6 +7,7 @@ * * Contributors: * Balasko, Jeno + * Baranyi, Botond * Forstner, Matyas * Szabo, Janos Zoltan – initial implementation * @@ -104,6 +105,8 @@ #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) */ diff --git a/core/Module_list.cc b/core/Module_list.cc index 48705ee..f3a90fe 100644 --- a/core/Module_list.cc +++ b/core/Module_list.cc @@ -37,6 +37,7 @@ #endif #include "../common/dbgnew.hh" +#include "Debugger.hh" #include #include @@ -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; diff --git a/core/Param_Types.cc b/core/Param_Types.cc index 1da2146..8926f32 100644 --- a/core/Param_Types.cc +++ b/core/Param_Types.cc @@ -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; diff --git a/core/Param_Types.hh b/core/Param_Types.hh index adc3ff7..d85ab42 100644 --- a/core/Param_Types.hh +++ b/core/Param_Types.hh @@ -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 diff --git a/core/Single_main.cc b/core/Single_main.cc index f78d7ad..b0a615e 100644 --- a/core/Single_main.cc +++ b/core/Single_main.cc @@ -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, "*")) diff --git a/core/config_process.l b/core/config_process.l index 3e11112..a0396e9 100644 --- a/core/config_process.l +++ b/core/config_process.l @@ -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 { diff --git a/core/config_process.y b/core/config_process.y index cd36e38..f9bc794 100644 --- a/core/config_process.y +++ b/core/config_process.y @@ -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=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 } } diff --git a/etc/xsd/TPD.xsd b/etc/xsd/TPD.xsd index 200dbd2..7812e8f 100644 --- a/etc/xsd/TPD.xsd +++ b/etc/xsd/TPD.xsd @@ -113,6 +113,7 @@ + diff --git a/function_test/BER_EncDec/Makefile b/function_test/BER_EncDec/Makefile index 6dbb2c1..5989dd5 100644 --- a/function_test/BER_EncDec/Makefile +++ b/function_test/BER_EncDec/Makefile @@ -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... diff --git a/function_test/RAW_EncDec/Makefile b/function_test/RAW_EncDec/Makefile index 8b7f6f3..56b4414 100644 --- a/function_test/RAW_EncDec/Makefile +++ b/function_test/RAW_EncDec/Makefile @@ -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... diff --git a/function_test/Text_EncDec/Makefile b/function_test/Text_EncDec/Makefile index c276d7e..f3d50ef 100644 --- a/function_test/Text_EncDec/Makefile +++ b/function_test/Text_EncDec/Makefile @@ -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... diff --git a/function_test/XER_EncDec/Makefile b/function_test/XER_EncDec/Makefile index 94ee3cc..3a31f3c 100644 --- a/function_test/XER_EncDec/Makefile +++ b/function_test/XER_EncDec/Makefile @@ -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... diff --git a/mctr2/cli/Cli.cc b/mctr2/cli/Cli.cc index a217c39..3216e55 100644 --- a/mctr2/cli/Cli.cc +++ b/mctr2/cli/Cli.cc @@ -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 " ", + "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 " ", - "Add breakpoint at specified location." }, + { D_SET_BREAKPOINT_TEXT, D_SET_BREAKPOINT, + D_SET_BREAKPOINT_TEXT " []", + "Add a breakpoint at the specified location, or change the batch file of " + " an existing breakpoint." }, { D_REMOVE_BREAKPOINT_TEXT, D_REMOVE_BREAKPOINT, - D_REMOVE_BREAKPOINT_TEXT " ", - "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| [all|]", "Remove a breakpoint, " + "or all breakpoints from a module, or all breakpoints from all modules." }, + { D_SET_AUTOMATIC_BREAKPOINT_TEXT, D_SET_AUTOMATIC_BREAKPOINT, + D_SET_AUTOMATIC_BREAKPOINT_TEXT " error|fail on|off []", + "Switch an automatic breakpoint (truggered by an event) on or off, and/or " + "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|", "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 " [{ }]", - "Print current value of one or more variables." }, + D_PRINT_VARIABLE_TEXT " |$ [{ |$}]", + "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 " ", "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 " ", - "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. diff --git a/mctr2/cli/Cli.h b/mctr2/cli/Cli.h index a552efa..190de8f 100644 --- a/mctr2/cli/Cli.h +++ b/mctr2/cli/Cli.h @@ -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: /** diff --git a/mctr2/mctr/MainController.cc b/mctr2/mctr/MainController.cc index f4258e1..64f0dfe 100644 --- a/mctr2/mctr/MainController.cc +++ b/mctr2/mctr/MainController.cc @@ -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_struct* comp = 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(); } diff --git a/mctr2/mctr/MainController.h b/mctr2/mctr/MainController.h index 4993bff..1f0f0a0 100644 --- a/mctr2/mctr/MainController.h +++ b/mctr2/mctr/MainController.h @@ -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(); diff --git a/mctr2/mctr/UserInterface.cc b/mctr2/mctr/UserInterface.cc index e645ad8..ead29cf 100644 --- a/mctr2/mctr/UserInterface.cc +++ b/mctr2/mctr/UserInterface.cc @@ -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++ diff --git a/mctr2/mctr/UserInterface.h b/mctr2/mctr/UserInterface.h index 0f534f1..c3f2267 100644 --- a/mctr2/mctr/UserInterface.h +++ b/mctr2/mctr/UserInterface.h @@ -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); }; diff --git a/regression_test/Makefile.regression b/regression_test/Makefile.regression index 9357b04..71957f9 100644 --- a/regression_test/Makefile.regression +++ b/regression_test/Makefile.regression @@ -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 # diff --git a/regression_test/XML/XMLqualif/Makefile b/regression_test/XML/XMLqualif/Makefile index 360c2b8..a0c69c2 100644 --- a/regression_test/XML/XMLqualif/Makefile +++ b/regression_test/XML/XMLqualif/Makefile @@ -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... diff --git a/regression_test/XML/xsdConverter/Makefile.converter b/regression_test/XML/xsdConverter/Makefile.converter index 26fc506..b5ff34d 100644 --- a/regression_test/XML/xsdConverter/Makefile.converter +++ b/regression_test/XML/xsdConverter/Makefile.converter @@ -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) ; -- 2.34.1