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
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";
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
}
bool is_lazy_param = false;
+ bool is_constant = false;
switch (var_ass->get_asstype()) {
case Common::Assignment::A_PAR_VAL:
case Common::Assignment::A_PAR_VAL_IN:
- case Common::Assignment::A_PAR_TEMPL_IN:
+ case Common::Assignment::A_PAR_TEMPL_IN: {
if (var_ass->get_lazy_eval()) {
// lazy parameters have their own printing function
is_lazy_param = true;
}
- break;
+ Ttcn::FormalPar* fpar = dynamic_cast<Ttcn::FormalPar*>(var_ass);
+ is_constant = fpar == NULL || !fpar->get_used_as_lvalue();
+ break; }
+ case Common::Assignment::A_CONST:
+ case Common::Assignment::A_EXT_CONST:
+ case Common::Assignment::A_MODULEPAR:
+ case Common::Assignment::A_MODULEPAR_TEMP:
+ case Common::Assignment::A_TEMPLATE:
+ is_constant = scope_name != NULL;
default:
break;
}
- // recreate the TTCN-3 version of the type name and determine the type's printing function
- string type_name, print_function;
+ // recreate the TTCN-3 version of the type name and determine the type's
+ // printing and overwriting functions
+ string type_name, print_function, set_function;
print_function = is_lazy_param ? "TTCN3_Debugger::print_lazy_param<" :
"TTCN3_Debugger::print_base_var";
+ set_function = "TTCN3_Debugger::set_base_var";
if (var_ass->get_asstype() == Common::Assignment::A_TIMER ||
var_ass->get_asstype() == Common::Assignment::A_PAR_TIMER) {
type_name = "timer";
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()) {
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);
}
}
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,
// 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)
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:
void Module::generate_debugger_functions(output_struct *output)
{
- char* str = NULL;
+ char* print_str = NULL;
+ char* overwrite_str = NULL;
for (size_t i = 0; i < asss->get_nof_asss(); ++i) {
Asn::Assignment* asn_ass = dynamic_cast<Asn::Assignment*>(asss->get_ass_byIndex(i));
if (asn_ass->get_ass_pard() != NULL) {
// only structured types and enums are needed
// for instances of parameterized types, the last reference, which is
// not itself an instance of a parameterized type, holds the type's display name
- str = mputprintf(str,
+ print_str = mputprintf(print_str,
" %sif (!strcmp(p_var.type_name, \"%s\")) {\n"
- " ((const %s*)p_var.value)->log();\n"
+ " ((const %s*)ptr)->log();\n"
" }\n"
" else if (!strcmp(p_var.type_name, \"%s template\")) {\n"
- " ((const %s_template*)p_var.value)->log();\n"
+ " ((const %s_template*)ptr)->log();\n"
" }\n"
- , (str != NULL) ? "else " : ""
+ , (print_str != NULL) ? "else " : ""
+ , t->get_dispname().c_str(), t->get_genname_value(this).c_str()
+ , t->get_dispname().c_str(), t->get_genname_value(this).c_str());
+ overwrite_str = mputprintf(overwrite_str,
+ " %sif (!strcmp(p_var.type_name, \"%s\")) {\n"
+ " ((%s*)p_var.value)->set_param(p_new_value);\n"
+ " }\n"
+ " else if (!strcmp(p_var.type_name, \"%s template\")) {\n"
+ " ((%s_template*)p_var.value)->set_param(p_new_value);\n"
+ " }\n"
+ , (overwrite_str != NULL) ? "else " : ""
, t->get_dispname().c_str(), t->get_genname_value(this).c_str()
, t->get_dispname().c_str(), t->get_genname_value(this).c_str());
}
}
}
- if (str != NULL) {
+ if (print_str != NULL) {
// don't generate an empty printing function
output->header.class_defs = mputprintf(output->header.class_defs,
- "/* Debugger printing function for types declared in this module */\n\n"
- "extern CHARSTRING print_var_%s(const TTCN3_Debugger::variable_t& p_var);\n",
- get_modid().get_ttcnname().c_str());
+ "/* Debugger printing and overwriting functions for types declared in this module */\n\n"
+ "extern CHARSTRING print_var_%s(const TTCN3_Debugger::variable_t& p_var);\n"
+ "extern boolean set_var_%s(TTCN3_Debugger::variable_t& p_var, Module_Param& p_new_value);\n",
+ get_modid().get_ttcnname().c_str(), get_modid().get_ttcnname().c_str());
output->source.global_vars = mputprintf(output->source.global_vars,
"\n/* Debugger printing function for types declared in this module */\n"
"CHARSTRING print_var_%s(const TTCN3_Debugger::variable_t& p_var)\n"
"{\n"
+ " const void* ptr = p_var.set_function != NULL ? p_var.value : p_var.cvalue;\n"
" TTCN_Logger::begin_event_log2str();\n"
"%s"
" else {\n"
" TTCN_Logger::log_event_str(\"<unrecognized value or template>\");\n"
" }\n"
" return TTCN_Logger::end_event_log2str();\n"
- "}\n", get_modid().get_ttcnname().c_str(), str);
+ "}\n\n"
+ "/* Debugger overwriting function for types declared in this module */\n"
+ "boolean set_var_%s(TTCN3_Debugger::variable_t& p_var, Module_Param& p_new_value)\n"
+ "{\n"
+ "%s"
+ " else {\n"
+ " return FALSE;\n"
+ " }\n"
+ " return TRUE;\n"
+ "}\n", get_modid().get_ttcnname().c_str(), print_str,
+ get_modid().get_ttcnname().c_str(), overwrite_str);
}
}
compiler \- TTCN-3 and ASN.1 to C++ translator
.SH SYNOPSIS
.B compiler
-.RB "[\| " \-abcdEfgijlLMpqrRsStuwxXyY " \|]"
+.RB "[\| " \-abcdEfgijlLMnpqrRsStuwxXyY " \|]"
.RB "[\| " \-V
.IR " verb_level" " \|]"
.RB "[\| " \-K
.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
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"
" -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"
break;
}
}
-
+
/* Checking incompatible options */
if (vflag) {
if (Aflag || Lflag || Pflag || Tflag || Vflag || Yflag ||
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 */
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;
(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 */
"# 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
}
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
}
fputs("\n", fp);
- fputs("LINUX_LIBS = -lxml2", fp);
+ fputs("LINUX_LIBS = -lxml2 -lncurses", fp);
#ifdef USAGE_STATS
fputs(" -lpthread -lrt", fp);
#endif
}
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) {
}
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) {
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,
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;
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"
" -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"
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;
}
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':
case 'M':
SET_FLAG(M);
break;
+ case 'n':
+ SET_FLAG(n);
+ break;
case 'p':
SET_FLAG(p);
break;
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;
}
&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);
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);
}
void Module::generate_debugger_functions(output_struct *output)
{
- char* str = NULL;
+ char* print_str = NULL;
+ char* overwrite_str = NULL;
for (size_t i = 0; i < asss->get_nof_asss(); ++i) {
Def_Type* def = dynamic_cast<Def_Type*>(asss->get_ass_byIndex(i));
if (def != NULL) {
if (!t->is_ref() && t->get_typetype() != Type::T_COMPONENT) {
// don't generate code for subtypes
if (t->get_typetype() != Type::T_SIGNATURE) {
- str = mputprintf(str,
+ print_str = mputprintf(print_str,
" %sif (!strcmp(p_var.type_name, \"%s\")) {\n"
- " ((const %s*)p_var.value)->log();\n"
+ " ((const %s*)ptr)->log();\n"
" }\n"
- , (str != NULL) ? "else " : ""
+ , (print_str != NULL) ? "else " : ""
, t->get_dispname().c_str(), t->get_genname_value(this).c_str());
+ if (t->get_typetype() != Type::T_PORT) {
+ overwrite_str = mputprintf(overwrite_str,
+ " %sif (!strcmp(p_var.type_name, \"%s\")) {\n"
+ " ((%s*)p_var.value)->set_param(p_new_value);\n"
+ " }\n"
+ , (overwrite_str != NULL) ? "else " : ""
+ , t->get_dispname().c_str(), t->get_genname_value(this).c_str());
+ }
}
if (t->get_typetype() != Type::T_PORT) {
- str = mputprintf(str,
+ print_str = mputprintf(print_str,
" %sif (!strcmp(p_var.type_name, \"%s template\")) {\n"
- " ((const %s_template*)p_var.value)->log();\n"
+ " ((const %s_template*)ptr)->log();\n"
" }\n"
- , (str != NULL) ? "else " : ""
+ , (print_str != NULL) ? "else " : ""
, t->get_dispname().c_str(), t->get_genname_value(this).c_str());
+ if (t->get_typetype() != Type::T_SIGNATURE) {
+ overwrite_str = mputprintf(overwrite_str,
+ " %sif (!strcmp(p_var.type_name, \"%s template\")) {\n"
+ " ((%s_template*)p_var.value)->set_param(p_new_value);\n"
+ " }\n"
+ , (overwrite_str != NULL) ? "else " : ""
+ , t->get_dispname().c_str(), t->get_genname_value(this).c_str());
+ }
}
}
}
}
- if (str != NULL) {
+ if (print_str != NULL) {
// don't generate an empty printing function
output->header.class_defs = mputprintf(output->header.class_defs,
- "/* Debugger printing function for types declared in this module */\n\n"
+ "/* Debugger printing and overwriting functions for types declared in this module */\n\n"
"extern CHARSTRING print_var_%s(const TTCN3_Debugger::variable_t& p_var);\n",
get_modid().get_ttcnname().c_str());
output->source.global_vars = mputprintf(output->source.global_vars,
"\n/* Debugger printing function for types declared in this module */\n"
"CHARSTRING print_var_%s(const TTCN3_Debugger::variable_t& p_var)\n"
"{\n"
+ " const void* ptr = p_var.set_function != NULL ? p_var.value : p_var.cvalue;\n"
" TTCN_Logger::begin_event_log2str();\n"
"%s"
" else {\n"
" TTCN_Logger::log_event_str(\"<unrecognized value or template>\");\n"
" }\n"
" return TTCN_Logger::end_event_log2str();\n"
- "}\n", get_modid().get_ttcnname().c_str(), str);
+ "}\n", get_modid().get_ttcnname().c_str(), print_str);
+ }
+ if (overwrite_str != NULL) {
+ // don't generate an empty overwriting function
+ output->header.class_defs = mputprintf(output->header.class_defs,
+ "extern boolean set_var_%s(TTCN3_Debugger::variable_t& p_var, Module_Param& p_new_value);\n",
+ get_modid().get_ttcnname().c_str());
+ output->source.global_vars = mputprintf(output->source.global_vars,
+ "\n/* Debugger overwriting function for types declared in this module */\n"
+ "boolean set_var_%s(TTCN3_Debugger::variable_t& p_var, Module_Param& p_new_value)\n"
+ "{\n"
+ "%s"
+ " else {\n"
+ " return FALSE;\n"
+ " }\n"
+ " return TRUE;\n"
+ "}\n", get_modid().get_ttcnname().c_str(), overwrite_str);
}
}
"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(),
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"
ttcn3_makefilegen \- Makefile Generator
.SH SYNOPSIS
.B ttcn3_makefilegen
-.RB "[\| " \-acdEfglMpRsw " \|]"
+.RB "[\| " \-acdEfglMnpRsw " \|]"
.RB "[\| " \-e
.IR " ETS_name" " \|]"
.RB "[\| " \-o
.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.
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,
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,
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,
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,
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);
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;
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,
* @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
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,
* Contributors:
* Baji, Laszlo
* Balasko, Jeno
+ * Baranyi, Botond
* Beres, Szabolcs
* Delic, Adam
* Feher, Csaba
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);
}
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)
* Contributors:
* Baji, Laszlo
* Balasko, Jeno
+ * Baranyi, Botond
* Beres, Szabolcs
* Feher, Csaba
* Forstner, Matyas
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.
/** 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"
#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 */
#include "DebugCommands.hh"
#include "Communication.hh"
#include "../common/pattern.hh"
+#include "Param_Types.hh"
+#include "DebuggerUI.hh"
#include <unistd.h>
#include <pwd.h>
}
}
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");
}
}
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)
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 " : "",
}
}
-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.");
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
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);
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()
}
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;
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)
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;
CHARSTRING TTCN3_Debugger::print_base_var(const TTCN3_Debugger::variable_t& p_var)
{
+ const void* ptr = p_var.set_function != NULL ? p_var.value : p_var.cvalue;
TTCN_Logger::begin_event_log2str();
if (!strcmp(p_var.type_name, "bitstring")) {
- ((const BITSTRING*)p_var.value)->log();
+ ((const BITSTRING*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "bitstring template")) {
- ((const BITSTRING_template*)p_var.value)->log();
+ ((const BITSTRING_template*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "boolean")) {
- ((const BOOLEAN*)p_var.value)->log();
+ ((const BOOLEAN*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "boolean template")) {
- ((const BOOLEAN_template*)p_var.value)->log();
+ ((const BOOLEAN_template*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "charstring")) {
- ((const CHARSTRING*)p_var.value)->log();
+ ((const CHARSTRING*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "charstring template")) {
- ((const CHARSTRING_template*)p_var.value)->log();
+ ((const CHARSTRING_template*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "float")) {
- ((const FLOAT*)p_var.value)->log();
+ ((const FLOAT*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "float template")) {
- ((const FLOAT_template*)p_var.value)->log();
+ ((const FLOAT_template*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "hexstring")) {
- ((const HEXSTRING*)p_var.value)->log();
+ ((const HEXSTRING*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "hexstring template")) {
- ((const HEXSTRING_template*)p_var.value)->log();
+ ((const HEXSTRING_template*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "integer")) {
- ((const INTEGER*)p_var.value)->log();
+ ((const INTEGER*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "integer template")) {
- ((const INTEGER_template*)p_var.value)->log();
+ ((const INTEGER_template*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "objid")) {
- ((const OBJID*)p_var.value)->log();
+ ((const OBJID*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "objid template")) {
- ((const OBJID_template*)p_var.value)->log();
+ ((const OBJID_template*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "octetstring")) {
- ((const OCTETSTRING*)p_var.value)->log();
+ ((const OCTETSTRING*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "octetstring template")) {
- ((const OCTETSTRING_template*)p_var.value)->log();
+ ((const OCTETSTRING_template*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "universal charstring")) {
- ((const UNIVERSAL_CHARSTRING*)p_var.value)->log();
+ ((const UNIVERSAL_CHARSTRING*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "universal charstring template")) {
- ((const UNIVERSAL_CHARSTRING_template*)p_var.value)->log();
+ ((const UNIVERSAL_CHARSTRING_template*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "verdicttype")) {
- ((const VERDICTTYPE*)p_var.value)->log();
+ ((const VERDICTTYPE*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "verdicttype template")) {
- ((const VERDICTTYPE_template*)p_var.value)->log();
+ ((const VERDICTTYPE_template*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "component")) {
- ((const COMPONENT*)p_var.value)->log();
+ ((const COMPONENT*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "component template")) {
- ((const COMPONENT_template*)p_var.value)->log();
+ ((const COMPONENT_template*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "default")) {
- ((const DEFAULT*)p_var.value)->log();
+ ((const DEFAULT*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "default template")) {
- ((const DEFAULT_template*)p_var.value)->log();
+ ((const DEFAULT_template*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "timer")) {
- ((const TIMER*)p_var.value)->log();
+ ((const TIMER*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "NULL")) {
- ((const ASN_NULL*)p_var.value)->log();
+ ((const ASN_NULL*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "NULL template")) {
- ((const ASN_NULL_template*)p_var.value)->log();
+ ((const ASN_NULL_template*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "CHARACTER STRING")) {
- ((const CHARACTER_STRING*)p_var.value)->log();
+ ((const CHARACTER_STRING*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "CHARACTER STRING template")) {
- ((const CHARACTER_STRING_template*)p_var.value)->log();
+ ((const CHARACTER_STRING_template*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "EMBEDDED PDV")) {
- ((const EMBEDDED_PDV*)p_var.value)->log();
+ ((const EMBEDDED_PDV*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "EMBEDDED PDV template")) {
- ((const EMBEDDED_PDV_template*)p_var.value)->log();
+ ((const EMBEDDED_PDV_template*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "EXTERNAL")) {
- ((const EXTERNAL*)p_var.value)->log();
+ ((const EXTERNAL*)ptr)->log();
}
else if (!strcmp(p_var.type_name, "EXTERNAL template")) {
- ((const EXTERNAL_template*)p_var.value)->log();
+ ((const EXTERNAL_template*)ptr)->log();
}
else {
TTCN_Logger::log_event_str("<unrecognized value or template>");
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;
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
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;
}
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);
}
}
} \
}
-#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);
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:
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;
}
}
{
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);
}
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) {
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;
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;
}
}
}
-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) {
}
// 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;
}
}
}
+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");
+}
/** 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 ////////////////////
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
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,
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
/** list of component scopes */
Vector<named_scope_t> component_scopes;
- /** pointers to debug function objects (resembling a call stack), elements are not owned
- * the current function is always the last element in the array (the top element in the stack) */
- Vector<TTCN3_Debug_Function*> call_stack;
+ /** pointers to debug function objects and the lines they were called from
+ * (resembling a call stack), the current function is always the last element
+ * in the array (the top element in the stack) */
+ Vector<function_call_t> call_stack;
/** list of breakpoints */
Vector<breakpoint_t> breakpoints;
/** 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 /////////////////
//////////////////////////////////////////////////////
* 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 */
* 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
* @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 */
/** 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) */
* the special parameter values (SBP_ERROR_VERDICT and SBP_FAIL_VERDICT) only
* trigger a breakpoint if their respective behaviors have been set to do so
* (does nothing if the debugger is switched off) */
- void breakpoint_entry(int p_line /*bool p_stepping_helper*/);
+ void breakpoint_entry(int p_line);
/** variable printing function for base types */
static CHARSTRING print_base_var(const variable_t& p_var);
+ /** variable setting function for base types */
+ static boolean set_base_var(variable_t& p_var, Module_Param& p_new_value);
+
/** variable printing function for value arrays */
template <typename T_type, unsigned int array_size, int index_offset>
static CHARSTRING print_value_array(const variable_t& p_var)
{
+ const void* ptr = p_var.set_function != NULL ? p_var.value : p_var.cvalue;
TTCN_Logger::begin_event_log2str();
- ((VALUE_ARRAY<T_type, array_size, index_offset>*)p_var.value)->log();
+ ((VALUE_ARRAY<T_type, array_size, index_offset>*)ptr)->log();
return TTCN_Logger::end_event_log2str();
}
+ /** variable setting function for value arrays */
+ template <typename T_type, unsigned int array_size, int index_offset>
+ static boolean set_value_array(variable_t& p_var, Module_Param& p_new_value)
+ {
+ ((VALUE_ARRAY<T_type, array_size, index_offset>*)p_var.value)->set_param(p_new_value);
+ return TRUE;
+ }
+
/** variable printing function for template arrays */
template <typename T_value_type, typename T_template_type,
unsigned int array_size, int index_offset>
static CHARSTRING print_template_array(const variable_t& p_var)
{
+ const void* ptr = p_var.set_function != NULL ? p_var.value : p_var.cvalue;
TTCN_Logger::begin_event_log2str();
((TEMPLATE_ARRAY<T_value_type, T_template_type, array_size,
- index_offset>*)p_var.value)->log();
+ index_offset>*)ptr)->log();
return TTCN_Logger::end_event_log2str();
}
+ /** variable setting function for template arrays */
+ template <typename T_value_type, typename T_template_type,
+ unsigned int array_size, int index_offset>
+ static boolean set_template_array(variable_t& p_var, Module_Param& p_new_value)
+ {
+ ((TEMPLATE_ARRAY<T_value_type, T_template_type, array_size,
+ index_offset>*)p_var.value)->set_param(p_new_value);
+ return TRUE;
+ }
+
/** variable printing function for port arrays */
template <typename T_type, unsigned int array_size, int index_offset>
static CHARSTRING print_port_array(const variable_t& p_var)
{
+ const void* ptr = p_var.set_function != NULL ? p_var.value : p_var.cvalue;
TTCN_Logger::begin_event_log2str();
- ((PORT_ARRAY<T_type, array_size, index_offset>*)p_var.value)->log();
+ ((PORT_ARRAY<T_type, array_size, index_offset>*)ptr)->log();
return TTCN_Logger::end_event_log2str();
}
template <typename T_type, unsigned int array_size, int index_offset>
static CHARSTRING print_timer_array(const variable_t& p_var)
{
+ const void* ptr = p_var.set_function != NULL ? p_var.value : p_var.cvalue;
TTCN_Logger::begin_event_log2str();
- ((TIMER_ARRAY<T_type, array_size, index_offset>*)p_var.value)->log();
+ ((TIMER_ARRAY<T_type, array_size, index_offset>*)ptr)->log();
return TTCN_Logger::end_event_log2str();
}
template <typename EXPR_TYPE>
static CHARSTRING print_lazy_param(const variable_t& p_var)
{
+ const void* ptr = p_var.set_function != NULL ? p_var.value : p_var.cvalue;
TTCN_Logger::begin_event_log2str();
- ((Lazy_Param<EXPR_TYPE>*)p_var.value)->log();
+ ((Lazy_Param<EXPR_TYPE>*)ptr)->log();
return TTCN_Logger::end_event_log2str();
}
* (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) */
/** 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 */
/** list of pointers to local variable entries from the current function object or
* global or component variable entries from the main debugger object
* (the elements are not owned)*/
- Vector<const TTCN3_Debugger::variable_t*> variables;
+ Vector<TTCN3_Debugger::variable_t*> variables;
public:
//////////////////////////////////////////////////////
/** 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 //////
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
////// 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);
/** 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;
* - "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.
--- /dev/null
+/******************************************************************************
+ * Copyright (c) 2000-2016 Ericsson Telecom AB
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ * Baranyi, Botond – initial implementation
+ *
+ ******************************************************************************/
+
+#include "DebuggerUI.hh"
+#include "DebugCommands.hh"
+#include "Debugger.hh"
+#include "../mctr2/editline/libedit/src/editline/readline.h"
+#include <stdio.h>
+#include <ctype.h>
+
+#define PROMPT_TEXT "DEBUG> "
+#define BATCH_TEXT "batch"
+
+// use a different file, than the MCTR CLI, since not all commands are the same
+#define TTCN3_HISTORY_FILENAME ".ttcn3_history_single"
+
+const TTCN_Debugger_UI::command_t TTCN_Debugger_UI::debug_command_list[] = {
+ { D_SWITCH_TEXT, D_SWITCH, D_SWITCH_TEXT " on|off",
+ "Switch the debugger on or off." },
+ { D_SET_BREAKPOINT_TEXT, D_SET_BREAKPOINT,
+ D_SET_BREAKPOINT_TEXT " <module> <line> [<batch_file>]",
+ "Add a breakpoint at the specified location, or change the batch file of "
+ " an existing breakpoint." },
+ { D_REMOVE_BREAKPOINT_TEXT, D_REMOVE_BREAKPOINT,
+ D_REMOVE_BREAKPOINT_TEXT " all|<module> [all|<line>]", "Remove a breakpoint, "
+ "or all breakpoints from a module, or all breakpoints from all modules." },
+ { D_SET_AUTOMATIC_BREAKPOINT_TEXT, D_SET_AUTOMATIC_BREAKPOINT,
+ D_SET_AUTOMATIC_BREAKPOINT_TEXT " error|fail on|off [<batch_file>]",
+ "Switch an automatic breakpoint (truggered by an event) on or off, and/or "
+ "change its batch file." },
+ { D_SET_OUTPUT_TEXT, D_SET_OUTPUT,
+ D_SET_OUTPUT_TEXT " console|file|both [file_name]",
+ "Set the output of the debugger." },
+ { D_SET_GLOBAL_BATCH_FILE_TEXT, D_SET_GLOBAL_BATCH_FILE,
+ D_SET_GLOBAL_BATCH_FILE_TEXT " on|off [batch_file_name]",
+ "Set whether a batch file should be executed automatically when test execution "
+ "is halted (breakpoint-specific batch files override this setting)." },
+ { D_SET_COMPONENT_TEXT, D_SET_COMPONENT,
+ D_SET_COMPONENT_TEXT " mtc|<component_reference>",
+ "Set the test component to print debug information from." },
+ { D_PRINT_CALL_STACK_TEXT, D_PRINT_CALL_STACK, D_PRINT_CALL_STACK_TEXT,
+ "Print call stack." },
+ { D_SET_STACK_LEVEL_TEXT, D_SET_STACK_LEVEL, D_SET_STACK_LEVEL_TEXT " <level>",
+ "Set the stack level to print debug information from." },
+ { D_LIST_VARIABLES_TEXT, D_LIST_VARIABLES,
+ D_LIST_VARIABLES_TEXT " local|global|comp|all [pattern]",
+ "List variable names." },
+ { D_PRINT_VARIABLE_TEXT, D_PRINT_VARIABLE,
+ D_PRINT_VARIABLE_TEXT " <variable_name>|$ [{ <variable_name>|$}]",
+ "Print current value of one or more variables ('$' is substituted with the "
+ "result of the last " D_LIST_VARIABLES_TEXT " command)." },
+ { D_OVERWRITE_VARIABLE_TEXT, D_OVERWRITE_VARIABLE,
+ D_OVERWRITE_VARIABLE_TEXT " <variable_name> <value>",
+ "Overwrite the current value of a variable." },
+ { D_PRINT_SNAPSHOTS_TEXT, D_PRINT_SNAPSHOTS, D_PRINT_SNAPSHOTS_TEXT,
+ "Print snapshots of function calls until this point." },
+ // D_SET_SNAPSHOT_BEHAVIOR_TEXT
+ { D_STEP_OVER_TEXT, D_STEP_OVER, D_STEP_OVER_TEXT,
+ "Resume test execution until the next line of code (in this function or the "
+ "caller function)." },
+ { D_STEP_INTO_TEXT, D_STEP_INTO, D_STEP_INTO_TEXT,
+ "Resume test execution until the next line of code (on any stack level)." },
+ { D_STEP_OUT_TEXT, D_STEP_OUT, D_STEP_OUT_TEXT,
+ "Resume test execution until the next line of code in the caller function." },
+ { D_RUN_TO_CURSOR_TEXT, D_RUN_TO_CURSOR, D_RUN_TO_CURSOR_TEXT " <module> <line>",
+ "Resume test execution until the specified location." },
+ { D_HALT_TEXT, D_HALT, D_HALT_TEXT, "Halt test execution." },
+ { D_CONTINUE_TEXT, D_CONTINUE, D_CONTINUE_TEXT, "Resume halted test execution." },
+ { D_EXIT_TEXT, D_EXIT, D_EXIT_TEXT " test|all",
+ "Exit the current test or the execution of all tests." },
+ { NULL, D_ERROR, NULL, NULL }
+};
+
+char* TTCN_Debugger_UI::ttcn3_history_filename = NULL;
+
+/** local function for extracting the command name and its arguments from an
+ * input line
+ * @param arguments [in] input line
+ * @param len [in] length of the input line
+ * @param start [in] indicates the position to start searching from
+ * @param start [out] the next argument's start position (set to len if no further
+ * arguments were found)
+ * @param end [out] the position of the first character after the next argument */
+static void get_next_argument_loc(const char* arguments, size_t len, size_t& start, size_t& end)
+{
+ while (start < len && isspace(arguments[start])) {
+ ++start;
+ }
+ end = start;
+ while (end < len && !isspace(arguments[end])) {
+ ++end;
+ }
+}
+
+void TTCN_Debugger_UI::process_command(const char* p_line_read)
+{
+ // locate the command text
+ size_t len = strlen(p_line_read);
+ size_t start = 0;
+ size_t end = 0;
+ get_next_argument_loc(p_line_read, len, start, end);
+ if (start == len) {
+ // empty command
+ return;
+ }
+ add_history(p_line_read + start);
+ for (const command_t *command = debug_command_list; command->name != NULL;
+ ++command) {
+ if (!strncmp(p_line_read + start, command->name, end - start)) {
+ // count the arguments
+ int argument_count = 0;
+ size_t start_tmp = start;
+ size_t end_tmp = end;
+ while (start_tmp < len) {
+ start_tmp = end_tmp;
+ get_next_argument_loc(p_line_read, len, start_tmp, end_tmp);
+ if (start_tmp < len) {
+ ++argument_count;
+ }
+ }
+ // extract the arguments into a string array
+ char** arguments;
+ if (argument_count > 0) {
+ arguments = new char*[argument_count];
+ for (int i = 0; i < argument_count; ++i) {
+ start = end;
+ get_next_argument_loc(p_line_read, len, start, end);
+ arguments[i] = mcopystrn(p_line_read + start, end - start);
+ }
+ }
+ else {
+ arguments = NULL;
+ }
+ ttcn3_debugger.execute_command(command->commandID, argument_count, arguments);
+ if (argument_count > 0) {
+ for (int i = 0; i < argument_count; ++i) {
+ Free(arguments[i]);
+ }
+ delete [] arguments;
+ }
+ return;
+ }
+ }
+ if (!strncmp(p_line_read + start, BATCH_TEXT, end - start)) {
+ start = end;
+ get_next_argument_loc(p_line_read, len, start, end); // just to skip to the argument
+ // the entire argument list is treated as one file name (even if it contains spaces)
+ execute_batch_file(p_line_read + start);
+ return;
+ }
+ puts("Unknown command, try again...");
+}
+
+void TTCN_Debugger_UI::init()
+{
+ // initialize history library
+ using_history();
+ // calculate history file name
+ const char *home_directory = getenv("HOME");
+ if (home_directory == NULL) {
+ home_directory = ".";
+ }
+ ttcn3_history_filename = mprintf("%s/%s", home_directory, TTCN3_HISTORY_FILENAME);
+ // read history from file, don't bother if it does not exist
+ read_history(ttcn3_history_filename);
+ // set our own command completion function
+ rl_completion_entry_function = (Function*)complete_command;
+}
+
+void TTCN_Debugger_UI::clean_up()
+{
+ if (write_history(ttcn3_history_filename)) {
+ puts("Could not save debugger command history.");
+ }
+ Free(ttcn3_history_filename);
+}
+
+void TTCN_Debugger_UI::read_loop()
+{
+ while (ttcn3_debugger.is_halted()) {
+ // print the prompt and read a line using the readline(), which
+ // automatically handles command completion and command history
+ char* line = readline(PROMPT_TEXT);
+ if (line != NULL) {
+ process_command(line);
+ free(line);
+ }
+ else {
+ // EOF was received -> exit all
+ puts("exit all");
+ char** args = new char*[1];
+ args[0] = (char*)"all";
+ ttcn3_debugger.execute_command(D_EXIT, 1, args);
+ delete [] args;
+ }
+ }
+}
+
+void TTCN_Debugger_UI::execute_batch_file(const char* p_file_name)
+{
+ FILE* fp = fopen(p_file_name, "r");
+ if (fp == NULL) {
+ printf("Failed to open file '%s' for reading.\n", p_file_name);
+ return;
+ }
+ else {
+ printf("Executing batch file '%s'.\n", p_file_name);
+ }
+ char line[1024];
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ size_t len = strlen(line);
+ if (line[len - 1] == '\n') {
+ line[len - 1] = '\0';
+ --len;
+ }
+ if (len != 0) {
+ printf("%s\n", line);
+ process_command(line);
+ }
+ }
+ if (!feof(fp)) {
+ printf("Error occurred while reading batch file '%s' (error code: %d).\n",
+ p_file_name, ferror(fp));
+ }
+ fclose(fp);
+}
+
+void TTCN_Debugger_UI::print(const char* p_str)
+{
+ puts(p_str);
+}
+
+char* TTCN_Debugger_UI::complete_command(const char* p_prefix, int p_state)
+{
+ static int command_index;
+ static size_t prefix_len;
+ const char *command_name;
+
+ if (p_state == 0) {
+ command_index = 0;
+ prefix_len = strlen(p_prefix);
+ }
+
+ while ((command_name = debug_command_list[command_index].name)) {
+ ++command_index;
+ if (strncmp(p_prefix, command_name, prefix_len) == 0) {
+ // must allocate buffer for returned string (readline() frees it)
+ return strdup(command_name);
+ }
+ }
+ // no match found
+ return NULL;
+}
\ No newline at end of file
--- /dev/null
+/******************************************************************************
+ * 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 */
+
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
{
*
**/
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;
};
# -I. is needed because TitanLoggerApi.hh (generated) does #include <TTCN3.hh>
CPPFLAGS += -I. -I$(APIDIR)
-SOLARIS_LIBS := -lsocket -lnsl -lxml2
-SOLARIS8_LIBS := -lsocket -lnsl -lxml2
-LINUX_LIBS := -lxml2
-FREEBSD_LIBS := -lxml2
-WIN32_LIBS := -lxml2
-INTERIX_LIBS := -lxml2
+SOLARIS_LIBS := -lsocket -lnsl -lxml2 -lcurses
+SOLARIS8_LIBS := -lsocket -lnsl -lxml2 -lcurses
+LINUX_LIBS := -lxml2 -lncurses
+FREEBSD_LIBS := -lxml2 -lncurses
+WIN32_LIBS := -lxml2 -lncurses
+INTERIX_LIBS := -lxml2 -lncurses
ifeq ($(USAGE_STATS), yes)
SOLARIS_LIBS += -lresolv
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.
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
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
*
* Contributors:
* Balasko, Jeno
+ * Baranyi, Botond
* Forstner, Matyas
* Szabo, Janos Zoltan – initial implementation
*
#define MSG_UNMAP_REQ 19
#define MSG_UNMAPPED 20
#define MSG_DEBUG_HALT_REQ 101
+#define MSG_DEBUG_CONTINUE_REQ 102
+#define MSG_DEBUG_BATCH 103
/* Messages from MTC to MC (up) */
#endif
#include "../common/dbgnew.hh"
+#include "Debugger.hh"
#include <stdio.h>
#include <string.h>
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;
#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 {
}
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();
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) {
}
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;
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)
* 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
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);
#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;
}
}
- 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) {
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, "*"))
{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 {
#include "LoggingParam.hh"
#include "Profiler.hh"
+#include "Debugger.hh"
+#include "DebugCommands.hh"
#define YYERROR_VERBOSE
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;
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");
}
}
for (int i=0; i<n_chars; i++) {
if ($1[i]=='?') chars_ptr[i] = 16;
else if ($1[i]=='*') chars_ptr[i] = 17;
- else chars_ptr[i] = char_to_hexdigit($1[i]);
+ else chars_ptr[i] = char_to_hexdigit_($1[i]);
}
Free($1);
$$ = new Module_Param_Hexstring_Template(n_chars, chars_ptr);
else if ($1[i]=='*') num = 257;
else {
// first digit
- num = 16 * char_to_hexdigit($1[i]);
+ num = 16 * char_to_hexdigit_($1[i]);
i++;
if (i>=str_len) config_process_error("Unexpected end of octetstring pattern");
// second digit
- num += char_to_hexdigit($1[i]);
+ num += char_to_hexdigit_($1[i]);
}
octet_vec.push_back(num);
}
}
}
+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;
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;
}
}
-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
}
}
<xs:element name="outParamBoundness" minOccurs="0" maxOccurs="1" type="xs:boolean" />
<xs:element name="omitInValueList" minOccurs="0" maxOccurs="1" type="xs:boolean" />
<xs:element name="warningsForBadVariants" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+ <xs:element name="activateDebugger" minOccurs="0" maxOccurs="1" type="xs:boolean" />
<xs:element name="quietly" minOccurs="0" maxOccurs="1" type="xs:boolean" />
<xs:element name="namingRules" minOccurs="0" maxOccurs="1" type="xs:normalizedString" />
<xs:element name="disableSubtypeChecking" minOccurs="0" maxOccurs="1" type="xs:boolean" />
#
# 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...
#
# 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...
#
# 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...
#
# 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...
*
* Contributors:
* Balasko, Jeno
+ * Baranyi, Botond
* Bene, Tamas
* Beres, Szabolcs
* Delic, Adam
#define SHELL_TEXT "!"
#define EXIT_TEXT "quit"
#define EXIT_TEXT2 "exit"
+#define BATCH_TEXT "batch"
#define SHELL_ESCAPE '!'
#define TTCN3_HISTORY_FILENAME ".ttcn3_history"
"Execute commands in subshell." },
{ EXIT_TEXT, &Cli::exitCallback, EXIT_TEXT, "Exit Main Controller." },
{ EXIT_TEXT2, &Cli::exitCallback, EXIT_TEXT2, "Exit Main Controller." },
+ { BATCH_TEXT, &Cli::executeBatchFile, BATCH_TEXT " <batch_file>",
+ "Run commands from batch file." },
{ NULL, NULL, NULL, NULL }
};
static const DebugCommand debug_command_list[] = {
{ D_SWITCH_TEXT, D_SWITCH, D_SWITCH_TEXT " on|off",
"Switch the debugger on or off." },
- { D_ADD_BREAKPOINT_TEXT, D_ADD_BREAKPOINT,
- D_ADD_BREAKPOINT_TEXT " <module> <line>",
- "Add breakpoint at specified location." },
+ { D_SET_BREAKPOINT_TEXT, D_SET_BREAKPOINT,
+ D_SET_BREAKPOINT_TEXT " <module> <line> [<batch_file>]",
+ "Add a breakpoint at the specified location, or change the batch file of "
+ " an existing breakpoint." },
{ D_REMOVE_BREAKPOINT_TEXT, D_REMOVE_BREAKPOINT,
- D_REMOVE_BREAKPOINT_TEXT " <module> <line>",
- "Remove breakpoint from specified location." },
- { D_SET_ERROR_BEHAVIOR_TEXT, D_SET_ERROR_BEHAVIOR,
- D_SET_ERROR_BEHAVIOR_TEXT " yes|no",
- "Set whether to halt test execution when component verdict is set to 'error'." },
- { D_SET_FAIL_BEHAVIOR_TEXT, D_SET_FAIL_BEHAVIOR,
- D_SET_FAIL_BEHAVIOR_TEXT " yes|no",
- "Set whether to halt test execution when component verdict is set to 'fail'." },
+ D_REMOVE_BREAKPOINT_TEXT " all|<module> [all|<line>]", "Remove a breakpoint, "
+ "or all breakpoints from a module, or all breakpoints from all modules." },
+ { D_SET_AUTOMATIC_BREAKPOINT_TEXT, D_SET_AUTOMATIC_BREAKPOINT,
+ D_SET_AUTOMATIC_BREAKPOINT_TEXT " error|fail on|off [<batch_file>]",
+ "Switch an automatic breakpoint (truggered by an event) on or off, and/or "
+ "change its batch file." },
{ D_SET_OUTPUT_TEXT, D_SET_OUTPUT,
D_SET_OUTPUT_TEXT " console|file|both [file_name]",
"Set the output of the debugger." },
+ { D_SET_GLOBAL_BATCH_FILE_TEXT, D_SET_GLOBAL_BATCH_FILE,
+ D_SET_GLOBAL_BATCH_FILE_TEXT " on|off [batch_file_name]",
+ "Set whether a batch file should be executed automatically when test execution "
+ "is halted (breakpoint-specific batch files override this setting)." },
{ D_SET_COMPONENT_TEXT, D_SET_COMPONENT,
D_SET_COMPONENT_TEXT " mtc|<component_reference>",
"Set the test component to print debug information from." },
D_LIST_VARIABLES_TEXT " local|global|comp|all [pattern]",
"List variable names." },
{ D_PRINT_VARIABLE_TEXT, D_PRINT_VARIABLE,
- D_PRINT_VARIABLE_TEXT " <variable_name>[{ <variable_name>}]",
- "Print current value of one or more variables." },
+ D_PRINT_VARIABLE_TEXT " <variable_name>|$ [{ <variable_name>|$}]",
+ "Print current value of one or more variables ('$' is substituted with the "
+ "result of the last " D_LIST_VARIABLES_TEXT " command)." },
{ D_OVERWRITE_VARIABLE_TEXT, D_OVERWRITE_VARIABLE,
D_OVERWRITE_VARIABLE_TEXT " <variable_name> <value>",
"Overwrite the current value of a variable." },
{ D_CONTINUE_TEXT, D_CONTINUE, D_CONTINUE_TEXT, "Resume halted test execution." },
{ D_EXIT_TEXT, D_EXIT, D_EXIT_TEXT " test|all",
"Exit the current test or the execution of all tests." },
- { D_BATCH_TEXT, D_BATCH, D_BATCH_TEXT " <batch_file_name>",
- "Run commands from batch file." },
- { D_SET_HALTING_BATCH_FILE_TEXT, D_SET_HALTING_BATCH_FILE,
- D_SET_HALTING_BATCH_FILE_TEXT " yes|no [batch_file_name]",
- "Set whether a batch file should be executed automatically when test execution "
- "is halted by the debugger." },
{ NULL, D_ERROR, NULL, NULL }
};
}
}
+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.
void helpCallback(const char *arguments);
void shellCallback(const char *arguments);
void exitCallback(const char *arguments);
+
+ virtual void executeBatchFile(const char* filename);
private:
/**
* Contributors:
* Baji, Laszlo
* Balasko, Jeno
+ * Baranyi, Botond
* Bene, Tamas
* Feher, Csaba
* Forstner, Matyas
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:
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 "
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) {
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);
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);
}
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;
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()
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;
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;
else {
notify("Cannot execute debug commands before the MTC is created.");
}
- status_change();
unlock();
}
* Contributors:
* Baji, Laszlo
* Balasko, Jeno
+ * Baranyi, Botond
* Bene, Tamas
* Czimbalmos, Eduard
* Feher, Csaba
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;
};
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, ...)
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();
}
+void UserInterface::executeBatchFile(const char* /* filename */)
+{
+ error(/* severity */ 0, "This user interface does not support batch files.");
+}
+
//----------------------------------------------------------------------------
// Local Variables:
// mode: C++
*/
virtual void notify(const struct timeval* timestamp, const char* source,
int severity, const char* message) = 0;
+
+ virtual void executeBatchFile(const char* filename);
};
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
#
# 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...
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) ;