Commit 7329404e authored by Botond Baranyi's avatar Botond Baranyi
Browse files

Debugger - Stage 1 (artf511247)


Signed-off-by: Botond Baranyi's avatarBotond Baranyi <botond.baranyi@ericsson.com>
parent 7076d2df
......@@ -815,8 +815,9 @@ namespace Common {
// pre_init function
bool has_pre_init = false;
bool profiled = MOD_TTCN == get_moduletype() && is_file_profiled(get_filename());
bool debugged = debugger_active && MOD_TTCN == get_moduletype();
// always generate pre_init_module if the file is profiled
if (output->functions.pre_init || profiled) {
if (output->functions.pre_init || profiled || debugged) {
output->source.static_function_prototypes =
mputstr(output->source.static_function_prototypes,
"static void pre_init_module();\n");
......@@ -848,6 +849,10 @@ namespace Common {
"TTCN3_Stack_Depth stack_depth;\n"
"ttcn3_prof.execute_line(\"%s\", 0);\n", get_modid().get_name().c_str(), get_filename());
}
if (debugged) {
output->source.static_function_bodies = mputprintf(output->source.static_function_bodies,
"%s::init_ttcn3_debugger();\n", get_modid().get_name().c_str());
}
}
output->source.static_function_bodies =
mputstr(output->source.static_function_bodies, output->functions.pre_init);
......@@ -1564,7 +1569,7 @@ namespace Common {
"{\n");
char* function_name = 0;
int line_no = -1;
while(get_profiler_code_line(get_filename(), &function_name, &line_no)) {
while (get_profiler_code_line(get_filename(), &function_name, &line_no)) {
output->source.global_vars = mputprintf(output->source.global_vars,
" ttcn3_prof.create_line(ttcn3_prof.get_element(\"%s\"), %d);\n",
get_filename(), line_no);
......@@ -1574,7 +1579,15 @@ namespace Common {
get_filename(), line_no, function_name);
}
}
output->source.global_vars = mputstr(output->source.global_vars, "}\n\n");
output->source.global_vars = mputstr(output->source.global_vars, "}\n");
}
/* TTCN-3 debugger:
generate the printing function for the types defined in this module
and initialize the debugger with this module's global variables,
component types and the components' variables */
if (debugger_active) {
generate_debugger_functions(output);
generate_debugger_init(output);
}
}
......
......@@ -268,6 +268,24 @@ namespace Common {
* module object to output->source.global_vars */
void generate_functions(output_struct *output);
void generate_conversion_functions(output_struct *output);
/** Generates the debugger initialization function for this module.
* The function creates the global debug scope associated with this module,
* and initializes it with all the global variables visible in the module
* (including imported variables).
* The debug scopes of all component types defined in the module are also
* created and initialized with their variables. */
virtual void generate_debugger_init(output_struct *output) = 0;
/** Generates the variable adding code for all global variables defined
* in this module. This function is called by generate_debugger_init()
* for both the current module and all imported modules. */
virtual char* generate_debugger_global_vars(char* str, Common::Module* current_mod) = 0;
/** Generates the debugger variable printing function, which can print values
* and templates of all types defined in this module (excluding subtypes). */
virtual void generate_debugger_functions(output_struct *output) = 0;
private:
/** Copy constructor not implemented */
Module(const Module& p);
......
/******************************************************************************
* 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
/******************************************************************************
* 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 */
......@@ -86,7 +86,8 @@ PredefFunc.cc AST.cc Code.cc Constraint.cc CompilerError.cc \
CompField.cc CompType.cc EnumItem.cc Identifier.cc Int.cc \
main.cc Real.cc Setting.cc SigParam.cc string.cc subtype.cc Stopwatch.cc \
Type.cc Type_chk.cc Type_codegen.cc TypeCompat.cc \
Typestuff.cc ustring.cc Value.cc Valuestuff.cc XerAttributes.cc subtypestuff.cc CodeGenHelper.cc
Typestuff.cc ustring.cc Value.cc Valuestuff.cc XerAttributes.cc subtypestuff.cc \
CodeGenHelper.cc DebuggerStuff.cc
MFGEN_SOURCES := makefile.c xpather.cc ProjectGenHelper.cc
......
......@@ -293,6 +293,9 @@ namespace Common {
mputprintf(effective_module_lines, "%s%d",
(effective_module_lines ? ", " : ""), yyloc.first_line);
}
if (debugger_active) {
str = mputprintf(str, "ttcn3_debugger.breakpoint_entry(%d);\n", yyloc.first_line);
}
}
if (include_line_info)
......
......@@ -24,6 +24,7 @@
* Zalanyi, Balazs Andor
*
******************************************************************************/
#ifndef _Common_Type_HH
#define _Common_Type_HH
......
......@@ -28,6 +28,7 @@
#include "../main.hh"
#include "../CodeGenHelper.hh"
#include "../../common/JSON_Tokenizer.hh"
#include "../DebuggerStuff.hh"
/* defined in asn1p.y */
extern int asn1_parse_string(const char* p_str);
......@@ -529,12 +530,14 @@ namespace Asn {
// cycle through all type assignments, insert schema segments and references
// when needed
for (size_t i = 0; i < asss->get_nof_asss(); ++i) {
Common::Assignment* ass = asss->get_ass_byIndex(i);
if (Common::Assignment::A_TYPE == ass->get_asstype()) {
Asn::Assignment* asn_ass = dynamic_cast<Asn::Assignment*>(ass);
// skip parameterized types and their instances
if (NULL == asn_ass || NULL == asn_ass->get_ass_pard()) {
Type* t = ass->get_Type();
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);
......@@ -548,12 +551,82 @@ namespace Asn {
}
}
}
}
// end of type definitions
json.put_next_token(JSON_TOKEN_OBJECT_END);
}
void Module::generate_debugger_init(output_struct *output)
{
// no debugging in ASN.1 modules
}
char* Module::generate_debugger_global_vars(char* str, Common::Module* current_mod)
{
for (size_t i = 0; i < asss->get_nof_asss(); ++i) {
Asn::Assignment* asn_ass = dynamic_cast<Asn::Assignment*>(asss->get_ass_byIndex(i));
if (asn_ass->get_ass_pard() != NULL) {
// this check must be done before get_asstype() is called
continue;
}
if (asn_ass->get_asstype() == Common::Assignment::A_CONST) {
str = generate_code_debugger_add_var(str, asn_ass, current_mod, "global");