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