[nemiver] Support 'new_children' property of GDB/MI changelist LIST
- From: Dodji Seketeli <dodji src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [nemiver] Support 'new_children' property of GDB/MI changelist LIST
- Date: Sat, 18 Feb 2012 14:39:57 +0000 (UTC)
commit bfdfdc9302e8bfe1784ae4e72416548f88258f87
Author: Dodji Seketeli <dodji seketeli org>
Date: Fri Dec 30 16:43:29 2011 +0100
Support 'new_children' property of GDB/MI changelist LIST
* src/dbgengine/nmv-dbg-common.h (VarChange): New type.
(apply_debugger_variable_change): New public entry point.
(Output::ResultRecord::m_has_var_changes):
(Output::ResultRecord::m_new_num_children): New members
(Output::ResultRecord::clear): Clear new m_var_changes and
m_new_num_children.
(Output::ResultRecord::has_var_changes):New.
(Output::ResultRecord::var_changes): New.
(Output::ResultRecord::new_num_children): New.
* src/dbgengine/nmv-dbg-common.cc (VarChange::Priv): New
(VarChange::*): New.
(update_debugger_variable): New.
* src/dbgengine/nmv-gdbmi-parser.h (parse_var_changed_list): New.
* src/dbgengine/nmv-gdbmi-parser.cc
(grok_var_changed_list_components): Split out from
GDBMIParser::parse_var_changed_list. Make it support
"new_children" property.
(GDBMIParser::parse_var_changed_list): Use new
grok_var_changed_list_components.
(operator<<): Added a few overloads of this to stream instances of
VariableSafePtr, Variable, VarChangePtr to ostream.
(dump_gdbmi): Added a few overloads of this for GDBMI types.
(GDBMIParser::parse_result_record<PREFIX_VARIABLES_CHANGED_LIST>):
Adjust to list<VarChangePtr> instead of
list<IDebugger::VariableSafePtr>.
* src/dbgengine/nmv-i-debugger.h (IDebugger::load_program): Add
two new overloads to simplify inferior load from client code.
* src/dbgengine/nmv-gdb-engine.h (GDBEngine::load_program): Add
two new overloads to simplify inferior load from client code.
* src/dbgengine/nmv-gdb-engine.cc (read_default_config): Add logs.
(on_conf_key_changed_signal): Likewise. Don't queue
-enable-pretty-printing command if gdb is not running. That will
make us lose the queued command once load_program is later called.
(OnListChangedVariableHandler::do_handle): Apply variable changes
to relevant variables and notify client code with the
sub-variables that got modified.
(GDBEngine::load_program): Two new overloads to simplify the
client code. Add comments. Cleanup.
* src/persp/dbgperspective/nmv-local-vars-inspector.cc
(update_a_local_variable, on_local_variables_listed_signal): Add
logs.
* src/persp/dbgperspective/nmv-variables-utils.cc (is_empty_row)
(get_row_name): New static functions.
(get_variable_columns, find_a_variable): Update logging.
(walk_path_from_row): Add a recurse argument that indicates if we
are being called recursively or not. Use that to determine row
where the walk stops at. USe is_empty_row. Add logging.
(find_a_variable_descendent): Add logging. Support looking for a
variable member that is a new sibling. That is, a variable member
not present in the graphical representation of its root variable,
even though that root variable is graphically represented.
(variables_match): Fix logic.
(update_a_variable, update_a_variable_real): Add comments and
logging. Support new variable members.
* tests/test-gdbmi.cc (gv_output_record9): New output record as a
result of -var-update.
* tests/test-pretty-print.cc: New test case.
* tests/pretty-print.cc: New inferior for new test case.
* tests/Makefile.am: Update build system for adding new test case.
src/dbgengine/Makefile.am | 6 +-
src/dbgengine/nmv-dbg-common.cc | 174 +++++++++++++
src/dbgengine/nmv-dbg-common.h | 113 +++++++--
src/dbgengine/nmv-gdb-engine.cc | 128 ++++++++--
src/dbgengine/nmv-gdb-engine.h | 5 +
src/dbgengine/nmv-gdbmi-parser.cc | 268 +++++++++++++++-----
src/dbgengine/nmv-gdbmi-parser.h | 2 +-
src/dbgengine/nmv-i-debugger.h | 5 +
.../dbgperspective/nmv-local-vars-inspector.cc | 7 +-
src/persp/dbgperspective/nmv-variables-utils.cc | 162 ++++++++++--
tests/Makefile.am | 12 +-
tests/pretty-print.cc | 22 ++
tests/test-gdbmi.cc | 30 ++-
tests/test-pretty-print.cc | 206 +++++++++++++++
14 files changed, 1005 insertions(+), 135 deletions(-)
---
diff --git a/src/dbgengine/Makefile.am b/src/dbgengine/Makefile.am
index 8415db2..d1cd044 100644
--- a/src/dbgengine/Makefile.am
+++ b/src/dbgengine/Makefile.am
@@ -95,7 +95,7 @@ publicheaders_DATA=$(dynmodheaders)
publicheadersdir=$(NEMIVER_INCLUDE_DIR)/dynmods
libgdbmod_la_LDFLAGS=-module -avoid-version -Wl,--as-needed
-libgdbmod_la_LIBADD=libdbgcommon.la libgdbmiparser.la \
+libgdbmod_la_LIBADD=libgdbmiparser.la \
libgdbengine.la libdebuggerutils.la @NEMIVERCOMMON_LIBS@ \
$(abs_top_builddir)/src/langs/libnemivercparser.la \
$(abs_top_builddir)/src/common/libnemivercommon.la
@@ -118,11 +118,11 @@ libvarobjwalkermod_la_LIBADD= NEMIVERCOMMON_LIBS@ \
libvarlistwalkermod_la_LDFLAGS=-module -avoid-version
libvarlistwalkermod_la_LIBADD= NEMIVERCOMMON_LIBS@ \
- $(abs_top_builddir)/src/common/libnemivercommon.la
+$(abs_top_builddir)/src/common/libnemivercommon.la
libgdbmiparser_la_LIBADD= \
@NEMIVERCOMMON_LIBS@ \
-$(abs_top_builddir)/src/common/libnemivercommon.la
+$(abs_top_builddir)/src/common/libnemivercommon.la libdbgcommon.la
config_DATA=gdbengine.conf varlist.conf varwalker.conf varobjwalker.conf varlistwalker.conf cpptrait.conf
configdir= NEMIVER_SYSTEM_CONFIG_DIR@
diff --git a/src/dbgengine/nmv-dbg-common.cc b/src/dbgengine/nmv-dbg-common.cc
index cdfaeab..c440a8d 100644
--- a/src/dbgengine/nmv-dbg-common.cc
+++ b/src/dbgengine/nmv-dbg-common.cc
@@ -64,5 +64,179 @@ OutputHandlerList::submit_command_and_output (CommandAndOutput &a_cao)
}
}
+/// Private stuff of the VarChange type.
+struct VarChange::Priv {
+ /// The variable this change is to be applied to.
+ IDebugger::VariableSafePtr variable;
+ int new_num_children;
+ list<IDebugger::VariableSafePtr> new_children;
+
+ Priv ()
+ : new_num_children (-1)
+ {
+ }
+
+ Priv (IDebugger::VariableSafePtr a_var,
+ int a_new_num_children,
+ list<IDebugger::VariableSafePtr> &a_new_children)
+ : variable (a_var),
+ new_num_children (a_new_num_children),
+ new_children (a_new_children)
+ {
+ }
+};
+
+VarChange::VarChange ()
+{
+ m_priv.reset (new Priv);
+}
+
+VarChange::VarChange (IDebugger::VariableSafePtr a_var,
+ int new_num_children,
+ list<IDebugger::VariableSafePtr> &a_changed_children)
+{
+ m_priv.reset (new Priv (a_var, new_num_children,
+ a_changed_children));
+}
+
+/// Returns the variable this change is to be applied to. The
+/// returned variable contains the new value(s) as a result member
+/// value change.
+const IDebugger::VariableSafePtr
+VarChange::variable () const
+{
+ return m_priv->variable;
+}
+
+/// Setter of the variable the current change is to be applied to.
+void
+VarChange::variable (const IDebugger::VariableSafePtr a_var)
+{
+ m_priv->variable = a_var;
+}
+
+/// If the change encompasses new children sub variables, then this
+/// accessor returns a positive value. If the change is about
+/// removing all the children sub variables then this accessor returns
+/// zero. Otherwise, if no children got added or removed the this
+/// returns -1.
+int
+VarChange::new_num_children () const
+{
+ return m_priv->new_num_children;
+}
+
+void
+VarChange::new_num_children (int a)
+{
+ m_priv->new_num_children = a;
+}
+
+/// If new_num_children() returned a positive number then this
+/// accessor returns the list of new children variables.
+const list<IDebugger::VariableSafePtr>&
+VarChange::new_children () const
+{
+ return m_priv->new_children;
+}
+
+list<IDebugger::VariableSafePtr>&
+VarChange::new_children ()
+{
+ return m_priv->new_children;
+}
+
+void
+VarChange::new_children (const list<IDebugger::VariableSafePtr>& a_vars)
+{
+ m_priv->new_children = a_vars;
+}
+
+/// Apply current change to a given variable.
+void
+VarChange::apply_to_variable (IDebugger::VariableSafePtr a_var,
+ list<IDebugger::VariableSafePtr> &a_changed_vars)
+{
+ IDebugger::VariableSafePtr applied_to;
+
+ IDebugger::VariableSafePtr v;
+ if (*a_var == *variable ()) {
+ applied_to = a_var;
+ update_debugger_variable (*applied_to, *variable ());
+ } else {
+ // variable must be a descendant of a_var.
+ v = a_var->get_descendant (variable ()->internal_name ());
+ THROW_IF_FAIL (v);
+ applied_to = v;
+ if (variable ()->name ().empty () && !v->name ().empty ())
+ variable ()->name (v->name ());
+ }
+
+ a_changed_vars.push_back (applied_to);
+
+ if (new_num_children () > (int) a_var->members ().size ()) {
+ // There are new children
+ THROW_IF_FAIL (new_children ().size () > 0);
+ list<IDebugger::VariableSafePtr>::const_iterator i;
+ for (i = new_children ().begin ();
+ i != new_children ().end ();
+ ++i) {
+ v = a_var->get_descendant ((*i)->internal_name ());
+ THROW_IF_FAIL (!v);
+ // *i is a new child of variable. Append it now.
+ applied_to->append (*i);
+ a_changed_vars.push_back (*i);
+ }
+ } else if (new_num_children () > -1
+ && (unsigned) new_num_children () < a_var->members ().size ()) {
+ // Some children got removed from applied_to. We assume these
+ // got removed from the end of the children list, b/c
+ // conceptually, if a variable get removedfrom the middle of
+ // the children list, it must be reported as two pieces of
+ // change:
+ //
+ // 1/ a set of changes to the values of all the variables
+ // that have indexes greater of equal to the variable
+ // that got removed.
+ //
+ // 2/ The former last child element is now going to be
+ // reported as a child that got removed from the end of
+ // the children list.
+ // So we assume we are handling the event 2/ now.
+ list<IDebugger::VariableSafePtr>::iterator last =
+ applied_to->members ().end ();
+ int num_to_erase = applied_to->members ().size () - new_num_children ();
+ for (--last; num_to_erase; --num_to_erase, --last) {
+ applied_to->members ().erase (last);
+ last = a_var->members ().end ();
+ }
+ } else {
+ THROW_IF_FAIL (new_children ().empty ());
+ }
+}
+
+/// Update variable a_to with new bits from a_from. Note that only
+/// things that can reasonably change are updated here.
+void
+update_debugger_variable (IDebugger::Variable &a_to,
+ IDebugger::Variable &a_from)
+{
+ if (!a_from.value ().empty ())
+ a_to.value (a_from.value ());
+ if (!a_from.type ().empty ())
+ a_to.value (a_to.type ());
+ a_to.has_more_children (a_from.has_more_children ());
+ a_to.in_scope (a_from.in_scope ());
+ a_to.is_dynamic (a_from.is_dynamic ());
+ // If a_from is the result of -var-update --all-values, then it
+ // won't have the user visible name of the variable set (it will
+ // only have the varobj name aka internal_name set). In that
+ // case, update the user visible name of a_from, if we have it.
+ if (a_from.name ().empty () && !a_to.name ().empty ())
+ a_from.name (a_to.name ());
+}
+
+
+
NEMIVER_END_NAMESPACE (nemiver)
diff --git a/src/dbgengine/nmv-dbg-common.h b/src/dbgengine/nmv-dbg-common.h
index d41f56b..53e4c5a 100644
--- a/src/dbgengine/nmv-dbg-common.h
+++ b/src/dbgengine/nmv-dbg-common.h
@@ -25,9 +25,59 @@
#ifndef __NMV_DBG_COMMON_H_H__
#define __NMV_DBG_COMMON_H_H__
#include "nmv-i-debugger.h"
+#include <tr1/memory>
NEMIVER_BEGIN_NAMESPACE (nemiver)
+/// Abstracts a change that happened to a variable as reported by the
+/// -var-update GDB/MI command, for GDB backends. The change can be
+/// either a new set of children sub variable appearing, or a change
+/// of a variable value.
+class VarChange {
+ struct Priv;
+ std::tr1::shared_ptr<Priv> m_priv;
+
+public:
+ VarChange ();
+ VarChange (IDebugger::VariableSafePtr a_var,
+ int new_num_children,
+ list<IDebugger::VariableSafePtr> &a_changed_children);
+
+ /// Returns the variable this change is to be applied to. The
+ /// returned variable contains the new value(s) as a result member
+ /// value change.
+ const IDebugger::VariableSafePtr variable () const;
+ void variable (const IDebugger::VariableSafePtr);
+
+ /// If the change encompasses new children sub variables, then this
+ /// accessor returns a positive value. If the change is about
+ /// removing all the children sub variables then this accessor
+ /// returns zero. Otherwise, if no children got added or removed
+ /// the this returns -1.
+ int new_num_children () const;
+ void new_num_children (int);
+
+ /// If new_num_children() returned a positive number then this
+ /// accessor returns the list of new children variables.
+ const list<IDebugger::VariableSafePtr>& new_children () const;
+ list<IDebugger::VariableSafePtr>& new_children ();
+ void new_children (const list<IDebugger::VariableSafePtr>&);
+
+ /// Apply current change to a given variable.
+ void apply_to_variable (IDebugger::VariableSafePtr a_var,
+ std::list<IDebugger::VariableSafePtr> &a_changed_vars);
+};
+typedef std::tr1::shared_ptr<VarChange> VarChangePtr;
+
+/// Update variable a_to with new bits from a_from. Note that only
+/// things that can reasonably change are updated here.
+void update_debugger_variable (IDebugger::Variable &a_to,
+ IDebugger::Variable &a_from);
+
+void
+apply_debugger_variable_change (IDebugger::VariableSafePtr a_var,
+ const VarChange &a_change);
+
/// \brief A container of the textual command sent to the debugger
class Command {
UString m_cookie;
@@ -146,7 +196,6 @@ public:
m_tag4.clear ();
m_should_emit_signal = true;
}
-
};//end class Command
/// \brief the output received from the debugger.
@@ -434,10 +483,19 @@ public:
vector<IDebugger::VariableSafePtr> m_variable_children;
bool m_has_variable_children;
- // Updated sub variables (direct/indirect children)
- // of a given variable
- list<IDebugger::VariableSafePtr> m_changed_var_list;
- bool m_has_changed_var_list;
+ // A list of the changes that occurred on a given variable.
+ // Whenever a user issues IDebugger::list_changed_variables on
+ // a given variable, the result is this m_var_changes. Each
+ // element of the list represents a change in the value of the
+ // given variable (like a change in one of its scalar members,
+ // a new member, or removal of last N members), or a change in
+ // one of the children of the variables.
+ list<VarChangePtr> m_var_changes;
+ bool m_has_var_changes;
+
+ // Thew new number of children of a dynamic variable. Set to
+ // -1 by default.
+ int m_new_num_children;
// The path expression of a variable object.
UString m_path_expression;
@@ -482,11 +540,13 @@ public:
m_memory_address = 0;
m_has_memory_values = false;
m_asm_instrs.clear ();
- m_has_asm_instrs = false;
- m_has_variable = false;
+ m_has_asm_instrs = false;
+ m_has_variable = false;
m_nb_variable_deleted = 0;
m_has_variable_children = false;
- m_has_changed_var_list = false;
+ m_var_changes.clear ();
+ m_has_var_changes = false;
+ m_new_num_children = -1;
m_path_expression.clear ();
m_has_path_expression = false;
m_variable_format = IDebugger::Variable::UNDEFINED_FORMAT;
@@ -728,24 +788,43 @@ public:
has_variable_children (true);
}
- bool has_changed_var_list () const
+ bool has_var_changes () const
{
- return m_has_changed_var_list;
+ return m_has_var_changes;
}
- void has_changed_var_list (bool a_in)
+ void has_var_changes (bool a_in)
{
- m_has_changed_var_list = a_in;
+ m_has_var_changes = a_in;
}
- const list<IDebugger::VariableSafePtr>& changed_var_list () const
+
+ /// Returns a list of the changes that occurred on a given variable.
+ /// Whenever a user issues IDebugger::list_changed_variables on
+ /// a given variable, the result is this m_var_changes. Each
+ /// element of the list represents a change in the value of the
+ /// given variable (like a change in one of its scalar members,
+ /// a new member, or removal of last N members), or a change in
+ /// one of the children of the variables.
+ const list<VarChangePtr>& var_changes () const
{
- return m_changed_var_list;
+ return m_var_changes;
}
- void changed_var_list (const list<IDebugger::VariableSafePtr> &a_in)
+
+ /// Sets a list of the changes that occurred on a given variable.
+ /// Whenever a user issues IDebugger::list_changed_variables on
+ /// a given variable, the result is this m_var_changes. Each
+ /// element of the list represents a change in the value of the
+ /// given variable (like a change in one of its scalar members,
+ /// a new member, or removal of last N members), or a change in
+ /// one of the children of the variables.
+ void var_changes (const list<VarChangePtr> &a_in)
{
- m_changed_var_list = a_in;
- has_changed_var_list (true);
+ m_var_changes = a_in;
+ has_var_changes (true);
}
+ int new_num_children () const {return m_new_num_children;}
+ void new_num_children (int a) {m_new_num_children = a;}
+
const UString& path_expression () const
{
return m_path_expression;
diff --git a/src/dbgengine/nmv-gdb-engine.cc b/src/dbgengine/nmv-gdb-engine.cc
index 3d44c07..d050adf 100644
--- a/src/dbgengine/nmv-gdb-engine.cc
+++ b/src/dbgengine/nmv-gdb-engine.cc
@@ -920,6 +920,8 @@ public:
void read_default_config ()
{
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
get_conf_mgr ()->get_key_value (CONF_KEY_FOLLOW_FORK_MODE,
follow_fork_mode);
get_conf_mgr ()->get_key_value (CONF_KEY_DISASSEMBLY_FLAVOR,
@@ -1186,6 +1188,8 @@ public:
void on_conf_key_changed_signal (const UString &a_key,
const UString &a_namespace)
{
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
NEMIVER_TRY
if (a_key == CONF_KEY_FOLLOW_FORK_MODE
@@ -1199,7 +1203,9 @@ public:
conf_mgr->get_key_value (a_key, e, a_namespace);
if (e != enable_pretty_printing) {
enable_pretty_printing = e;
- if (!pretty_printing_enabled_once && enable_pretty_printing) {
+ if (is_gdb_running ()
+ && !pretty_printing_enabled_once
+ && enable_pretty_printing) {
queue_command (Command ("-enable-pretty-printing"));
pretty_printing_enabled_once = true;
}
@@ -2880,7 +2886,7 @@ struct OnListChangedVariableHandler : public OutputHandler
if (a_in.output ().has_result_record ()
&& a_in.output ().result_record ().kind ()
== Output::ResultRecord::DONE
- && a_in.output ().result_record ().has_changed_var_list ()
+ && a_in.output ().result_record ().has_var_changes ()
&& a_in.command ().name () == "list-changed-variables") {
LOG_DD ("handler selected");
return true;
@@ -2891,25 +2897,46 @@ struct OnListChangedVariableHandler : public OutputHandler
void do_handle (CommandAndOutput &a_in)
{
THROW_IF_FAIL (a_in.command ().variable ());
- THROW_IF_FAIL (a_in.output ().result_record ().has_changed_var_list ());
-
- // We are going to build the list of descendants of
- // a_in.command ().variable () that got returned as being
- // changed.
+ THROW_IF_FAIL (a_in.output ().result_record ().has_var_changes ());
+
+ // Each element of a_in.output ().result_record ().var_changes
+ // () describes changes that occurred to the variable
+ // a_in.command ().variable (). Some of these changes might
+ // be new members of a_in.command ().variable () that are not
+ // yet represented in it. Some of these change might just be
+ // change in some member values, or changes to some of its
+ // children. We'll now apply those changes to
+ // a_in.command().variable() and its children as it fits and
+ // come up with a list of updated variables, that we'll notify
+ // client code with.
list<IDebugger::VariableSafePtr> vars;
- const list<IDebugger::VariableSafePtr> &changed_vars =
- a_in.output ().result_record ().changed_var_list ();
- list<IDebugger::VariableSafePtr>::const_iterator it;
- IDebugger::VariableSafePtr v;
- for (it = changed_vars.begin (); it != changed_vars.end (); ++it) {
- v = a_in.command ().variable ()->get_descendant
- ((*it)->internal_name ());
- // Okay, so v now contains the a descendant variable of
- // a_in.command ().variable () which value got modified.
- // Let's set the value of v to the new updated value.
- v->value ((*it)->value ());
- if (v) {
- vars.push_back (v);
+ const list<VarChangePtr> &var_changes =
+ a_in.output ().result_record ().var_changes ();
+
+ IDebugger::VariableSafePtr variable = a_in.command ().variable ();
+ list<VariableSafePtr> changed_sub_vars;
+ // Each element of var_changes is either a change of variable
+ // itself, or a change of one its children. So apply those
+ // changes to variable so that it reflects its new state, and
+ // notify the client code with the variable and it's
+ // sub-variables in their new states.
+ for (list<VarChangePtr>::const_iterator i = var_changes.begin ();
+ i != var_changes.end ();
+ ++i) {
+ // Apply the variable changes to each i->variable. The
+ // result is going to be a list VariableSafePtr that is
+ // going to be sent back to notify the client code.
+ (*i)->apply_to_variable (variable, changed_sub_vars);
+ LOG_DD ("Num sub vars:" << (int) changed_sub_vars.size ());
+ for (list<VariableSafePtr>::const_iterator j =
+ changed_sub_vars.begin ();
+ j != changed_sub_vars.end ();
+ ++j)
+ {
+ LOG_DD ("sub var: " << (*j)->internal_name ()
+ << "/" << (*j)->name ()
+ << " num children: " << (int) (*j)->members ().size ());
+ vars.push_back (*j);
}
}
// Call the slot associated to
@@ -2923,7 +2950,7 @@ struct OnListChangedVariableHandler : public OutputHandler
}
// Tell the world about the descendant variables that changed.
m_engine->changed_variables_signal ().emit
- (vars, a_in.command ().cookie ());
+ (vars, a_in.command ().cookie ());
}
};//end OnListChangedVariableHandler
@@ -2984,6 +3011,40 @@ GDBEngine::~GDBEngine ()
LOG_D ("delete", "destructor-domain");
}
+/// Load an inferior program to debug.
+///
+/// \param a_prog a path to the inferior program to debug.
+bool
+GDBEngine::load_program (const UString &a_prog)
+{
+ std::vector<UString> empty_args;
+ return load_program (a_prog, empty_args);
+}
+
+/// Load an inferior program to debug.
+///
+/// \param a_prog a path to the inferior program to debug.
+///
+/// \param a_args the arguments to the inferior program
+bool
+GDBEngine::load_program (const UString &a_prog,
+ const vector<UString> &a_args)
+{
+ return load_program (a_prog, a_args, ".", /*a_force=*/false);
+}
+
+/// Load an inferior program to debug.
+///
+/// \param a_prog a path to the inferior program to debug.
+///
+/// \param a_args the arguments to the inferior program
+///
+/// \param a_workingdir the path of the directory under which to look
+/// for the inferior program to load. This is used if a_prog couldn't
+/// be found by just using its path as given.
+///
+/// \param a_force if this is true and the command queue is stuck,
+/// clear it to force the loading of the inferior.
bool
GDBEngine::load_program (const UString &a_prog,
const vector<UString> &a_args,
@@ -2998,10 +3059,28 @@ GDBEngine::load_program (const UString &a_prog,
search_paths, tty_path, a_force);
}
+/// Load an inferior program to debug.
+///
+/// \param a_prog a path to the inferior program to debug.
+///
+/// \param a_argv the arguments to the inferior program
+///
+/// \param a_workingdir the path of the directory under which to look
+/// for the inferior program to load. This is used if a_prog couldn't
+/// be found by just using its path as given.
+///
+/// \param a_source_search_dirs a vector of directories under which to
+/// look for the source files of the objects that make up the inferior
+/// program.
+///
+/// \param a_tty_path the tty path the inferior should use.
+///
+/// \param a_force if this is true and the command queue is stuck,
+/// clear it to force the loading of the inferior.
bool
GDBEngine::load_program (const UString &a_prog,
const vector<UString> &a_argv,
- const UString &working_dir,
+ const UString &a_working_dir,
const vector<UString> &a_source_search_dirs,
const UString &a_tty_path,
bool a_force)
@@ -3013,7 +3092,7 @@ GDBEngine::load_program (const UString &a_prog,
if (!m_priv->is_gdb_running ()) {
vector<UString> gdb_opts;
- if (m_priv->launch_gdb_and_set_args (working_dir,
+ if (m_priv->launch_gdb_and_set_args (a_working_dir,
a_source_search_dirs,
a_prog, a_argv,
gdb_opts) == false)
@@ -3052,8 +3131,7 @@ GDBEngine::load_program (const UString &a_prog,
} else {
LOG_DD ("not setting LD_BIND_NOW environment variable ");
}
- if (m_priv->enable_pretty_printing
- && !m_priv->pretty_printing_enabled_once)
+ if (m_priv->enable_pretty_printing)
queue_command (Command ("-enable-pretty-printing"));
} else {
Command command ("load-program",
diff --git a/src/dbgengine/nmv-gdb-engine.h b/src/dbgengine/nmv-gdb-engine.h
index cea76ed..f6b5651 100644
--- a/src/dbgengine/nmv-gdb-engine.h
+++ b/src/dbgengine/nmv-gdb-engine.h
@@ -276,6 +276,11 @@ public:
const UString &a_value);
void set_solib_prefix_path (const UString &a_name);
+ bool load_program (const UString &a_prog);
+
+ bool load_program (const UString &a_prog,
+ const vector<UString> &a_args);
+
bool load_program (const UString &a_prog,
const vector<UString> &a_args,
const UString &a_working_dir,
diff --git a/src/dbgengine/nmv-gdbmi-parser.cc b/src/dbgengine/nmv-gdbmi-parser.cc
index 3462f8a..2d4d602 100644
--- a/src/dbgengine/nmv-gdbmi-parser.cc
+++ b/src/dbgengine/nmv-gdbmi-parser.cc
@@ -199,6 +199,9 @@ const char* PATH_EXPR = "path_expr";
static const char* PREFIX_ASM_INSTRUCTIONS= "asm_insns=";
const char* PREFIX_VARIABLE_FORMAT = "format=";
+static bool grok_var_changed_list_components (GDBMIValueSafePtr a_value,
+ list<VarChangePtr> &a_var_changes);
+
static bool
is_string_start (gunichar a_c)
{
@@ -256,7 +259,7 @@ str_to_stopped_reason (const UString &a_str)
bool
gdbmi_tuple_to_string (GDBMITupleSafePtr a_result,
- UString &a_string)
+ UString &a_string)
{
if (!a_result)
@@ -460,6 +463,79 @@ operator<< (std::ostream &a_out, const IDebugger::Variable &a_var)
return a_out;
}
+std::ostream&
+operator<< (std::ostream &a_out, const IDebugger::VariableSafePtr &a_var)
+{
+ if (a_var)
+ a_out << a_var;
+ else
+ a_out << "";
+ return a_out;
+}
+
+std::ostream&
+operator<< (std::ostream &a_out,
+ const std::list<IDebugger::VariableSafePtr> &a_vars)
+{
+ a_out << "<variablelist length='" << a_vars.size () << "'>";
+ std::list<IDebugger::VariableSafePtr>::const_iterator i = a_vars.begin ();
+ for (; i != a_vars.end (); ++i) {
+ a_out << **i;
+ }
+ a_out << "</variablelist>";
+ return a_out;
+}
+
+std::ostream&
+operator<< (std::ostream &a_out, const VarChangePtr &a_change)
+{
+ a_out << "<varchange>";
+
+ if (a_change->variable ())
+ a_out << a_change->variable ();
+ else
+ a_out << "";
+
+ a_out << "<newnumchildren>"
+ << a_change->new_num_children ()
+ << "</newnumchildren>"
+ << "<newchildren>"
+ << a_change->new_children ()
+ << "</newchildren>"
+ << "</varchange>";
+ return a_out;
+}
+
+void
+dump_gdbmi (const GDBMIResultSafePtr &a_result)
+{
+ std::cout << a_result;
+}
+
+void
+dump_gdbmi (const GDBMITupleSafePtr &a_tuple)
+{
+ std::cout << a_tuple;
+}
+
+void
+dump_gdbmi (const GDBMIListSafePtr &a_list)
+{
+ std::cout << a_list;
+}
+
+void
+dump_gdbmi (const GDBMIValueSafePtr &a_var)
+{
+ std::cout << a_var;
+}
+
+void
+dump_gdbmi (const IDebugger::Variable &a_var)
+{
+ std::cout << a_var;
+}
+
struct QuickUStringLess : public std::binary_function<const UString,
const UString,
bool>
@@ -2007,9 +2083,9 @@ fetch_gdbmi_result:
} else if (!RAW_INPUT.compare (cur,
strlen (PREFIX_VARIABLES_CHANGED_LIST),
PREFIX_VARIABLES_CHANGED_LIST)) {
- list<IDebugger::VariableSafePtr> vars;
- if (parse_var_changed_list (cur, cur, vars)) {
- result_record.changed_var_list (vars);
+ list<VarChangePtr> var_changes;
+ if (parse_var_changed_list (cur, cur, var_changes)) {
+ result_record.var_changes (var_changes);
} else {
LOG_PARSING_ERROR2 (cur);
}
@@ -3508,64 +3584,56 @@ GDBMIParser::parse_var_list_children
return true;
}
-// We want to parse something that likes:
-// 'changelist=[{name="var1",value="3",in_scope="true",type_changed="false"}]'
-// It's a RESULT, which value is a LIST of TUPLEs.
-// Each TUPLE represents a child variable.
-bool
-GDBMIParser::parse_var_changed_list (UString::size_type a_from,
- UString::size_type &a_to,
- list<IDebugger::VariableSafePtr> &a_vars)
+/// This function takes a GDB/MI VALUE data structure that contains a
+/// LIST of VALUEs representing the list of variables that changed (as
+/// a result of the command -var-update --all-values <varobj-name>).
+/// Each VALUE in the LIST (so each changed variable) contains a TUPLE
+/// representing the components of the variable.
+///
+/// This function analyses this input and constructs a list of
+/// instances of IDebugger::Variable representing the list of changed
+/// variables as returned by the -var-udpate command.
+///
+/// \param a_value the GDB/MI VALUE data structure presented above.
+///
+/// \param a_vars an output parameter. The resulting list of
+/// variables built by this function.
+///
+/// \return true upon succesful completion
+static bool
+grok_var_changed_list_components (GDBMIValueSafePtr a_value,
+ list<VarChangePtr> &a_var_changes)
{
- LOG_FUNCTION_SCOPE_NORMAL_D (GDBMI_PARSING_DOMAIN);
- UString::size_type cur = a_from;
- CHECK_END2 (cur);
-
- if (RAW_INPUT.compare (cur, strlen (PREFIX_VARIABLES_CHANGED_LIST),
- PREFIX_VARIABLES_CHANGED_LIST)) {
- LOG_PARSING_ERROR2 (cur);
- return false;
- }
- GDBMIResultSafePtr result;
- if (!parse_gdbmi_result (cur, cur, result) || !result) {
- LOG_PARSING_ERROR2 (cur);
- return false;
- }
- // The name of RESULT must be CHANGELIST
- if (result->variable () != CHANGELIST) {
- LOG_ERROR ("expected gdbmi variable " << CHANGELIST << ", got: "
- << result->variable () << "\'");
- return false;
- }
// The value of the RESULT must be a LIST
- if (!result->value ()
- || result->value ()->content_type () != GDBMIValue::LIST_TYPE
- || !result->value ()->get_list_content ()) {
+ if (!a_value
+ || a_value->content_type () != GDBMIValue::LIST_TYPE
+ || !a_value->get_list_content ()) {
LOG_ERROR ("expected a LIST value for the GDBMI variable "
<< CHANGELIST);
return false;
}
// Have we got an empty list ?
- if (result->value ()->get_list_content ()->empty ()) {
- a_vars.clear ();
- a_to = cur;
+ if (a_value->get_list_content ()->empty ()) {
return true;
}
- if (result->value ()->get_list_content ()->content_type ()
- != GDBMIList::VALUE_TYPE) {
+ if (a_value->get_list_content ()->content_type ()
+ != GDBMIList::VALUE_TYPE) {
LOG_ERROR ("expected a TUPLE content in the LIST value of "
<< CHANGELIST);
return false;
}
+
list<GDBMIValueSafePtr> values;
- result->value ()->get_list_content ()->get_value_content (values);
+ a_value->get_list_content ()->get_value_content (values);
if (values.empty ()) {
LOG_ERROR ("expected a non empty TUPLE content in the LIST value of "
<< CHANGELIST);
return false;
}
+
+ list<VarChangePtr> var_changes;
for (list<GDBMIValueSafePtr>::const_iterator value_it = values.begin ();
value_it != values.end ();
++value_it) {
@@ -3580,45 +3648,127 @@ GDBMIParser::parse_var_changed_list (UString::size_type a_from,
}
// the components of a given child variable
list<GDBMIResultSafePtr> comps =
- (*value_it)->get_tuple_content ()->content ();
- UString n, v, internal_name, value;
- bool in_scope = true;
- IDebugger::VariableSafePtr var;
+ (*value_it)->get_tuple_content ()->content ();
+ UString n, internal_name, value, display_hint, type;
+ bool in_scope = true, has_more = false, dynamic = false;
+ int new_num_children = -1;
+ list<IDebugger::VariableSafePtr> children;
+ list<VarChangePtr> sub_var_changes;
// Walk the list of components of the child variable and really
// build the damn variable
for (list<GDBMIResultSafePtr>::const_iterator it = comps.begin ();
it != comps.end ();
++it) {
if (!(*it)
- || (*it)->value ()->content_type () != GDBMIValue::STRING_TYPE) {
+ || ((*it)->value ()->content_type () != GDBMIValue::STRING_TYPE
+ && (*it)->value ()->content_type () != GDBMIValue::LIST_TYPE)) {
continue;
}
+
n = (*it)->variable ();
- v = (*it)->value ()->get_string_content ();
- if (n == "name") {
- internal_name = v;
- } else if (n == "value") {
- value = v;
- } else if (n == "in_scope") {
- in_scope = (v == "true");
- } else if (n == "type_changed") {
- // type_changed = (v == "true");
+ if ((*it)->value ()->content_type () == GDBMIValue::STRING_TYPE) {
+ UString v;
+ v = (*it)->value ()->get_string_content ();
+ if (n == "name") {
+ internal_name = v;
+ } else if (n == "value") {
+ value = v;
+ } else if (n == "type") {
+ type = v;
+ } else if (n == "in_scope") {
+ in_scope = (v == "true");
+ } else if (n == "type_changed") {
+ // type_changed = (v == "true");
+ } else if (n == "has_more") {
+ has_more = (v == "0") ? false : true;
+ } else if (n == "dynamic") {
+ dynamic = (v == "0") ? false : true;
+ } else if (n == "displayhint") {
+ display_hint = v;
+ } else if (n == "new_num_children") {
+ new_num_children = atoi (v.c_str ());
+ }
+ } else if ((*it)->value ()->content_type () == GDBMIValue::LIST_TYPE) {
+ if (n == "new_children") {
+ // (it*)->value () should contain one value (a TUPLE) per
+ // child. Each child containing the components
+ // that appeared as part of the "changed values"
+ // of this variable. This can happen with a
+ // pretty printed variable like a container into
+ // which new elements got added.
+ grok_var_changed_list_components ((*it)->value (),
+ sub_var_changes);
+ }
}
}
if (!internal_name.empty ()) {
- var = IDebugger::VariableSafePtr
+ IDebugger::VariableSafePtr var;
+ var.reset
(new IDebugger::Variable (internal_name,
"" /* name */,
value,
- "" /* type */,
+ type,
in_scope));
- a_vars.push_back (var);
+ var->is_dynamic (dynamic);
+ var->display_hint (display_hint);
+ var->has_more_children (has_more);
+ // If var has some new children that appeared, append them
+ // as member now.
+ VarChangePtr var_change (new VarChange);
+ var_change->variable (var);
+ list<VarChangePtr>::const_iterator i;
+ for (i = sub_var_changes.begin ();
+ i != sub_var_changes.end ();
+ ++i) {
+ // None of the new sub variables of var should
+ // themselves contain any new sub variable.
+ THROW_IF_FAIL ((*i)->new_children ().empty ());
+ THROW_IF_FAIL ((*i)->new_num_children () == -1);
+ var_change->new_children ().push_back ((*i)->variable ());
+ }
+ var_change->new_num_children (new_num_children);
+ var_changes.push_back (var_change);
}
}
- a_to = cur;
+ a_var_changes = var_changes;
return true;
}
+// We want to parse something that looks likes:
+// 'changelist=[{name="var1",value="3",in_scope="true",type_changed="false"}]'
+// It's a RESULT, which value is a LIST of TUPLEs.
+// Each TUPLE represents a child variable.
+bool
+GDBMIParser::parse_var_changed_list (UString::size_type a_from,
+ UString::size_type &a_to,
+ list<VarChangePtr> &a_var_changes)
+{
+ LOG_FUNCTION_SCOPE_NORMAL_D (GDBMI_PARSING_DOMAIN);
+ UString::size_type cur = a_from;
+ CHECK_END2 (cur);
+
+ if (RAW_INPUT.compare (cur, strlen (PREFIX_VARIABLES_CHANGED_LIST),
+ PREFIX_VARIABLES_CHANGED_LIST)) {
+ LOG_PARSING_ERROR2 (cur);
+ return false;
+ }
+ GDBMIResultSafePtr result;
+ if (!parse_gdbmi_result (cur, cur, result) || !result) {
+ LOG_PARSING_ERROR2 (cur);
+ return false;
+ }
+ // The name of RESULT must be CHANGELIST
+ if (result->variable () != CHANGELIST) {
+ LOG_ERROR ("expected gdbmi variable " << CHANGELIST << ", got: "
+ << result->variable () << "\'");
+ return false;
+ }
+
+ a_to = cur;
+
+ return grok_var_changed_list_components (result->value (), a_var_changes);
+}
+
/// Parse the result of -var-info-path-expression.
/// It's basically a RESULT of the form:
/// path_expr="((Base)c).m_size)"
diff --git a/src/dbgengine/nmv-gdbmi-parser.h b/src/dbgengine/nmv-gdbmi-parser.h
index 3b2d0ea..10160e7 100644
--- a/src/dbgengine/nmv-gdbmi-parser.h
+++ b/src/dbgengine/nmv-gdbmi-parser.h
@@ -633,7 +633,7 @@ public:
bool parse_var_changed_list (UString::size_type a_from,
UString::size_type &a_to,
- list<IDebugger::VariableSafePtr> &a_vars);
+ list<VarChangePtr> &a_var_changes);
bool parse_var_path_expression (UString::size_type a_from,
UString::size_type &a_to,
diff --git a/src/dbgengine/nmv-i-debugger.h b/src/dbgengine/nmv-i-debugger.h
index 3bfcbd1..39c83cc 100644
--- a/src/dbgengine/nmv-i-debugger.h
+++ b/src/dbgengine/nmv-i-debugger.h
@@ -1195,6 +1195,11 @@ public:
virtual void set_solib_prefix_path (const UString &a_name) = 0;
+ virtual bool load_program (const UString &a_prog) = 0;
+
+ virtual bool load_program (const UString &a_prog,
+ const vector<UString> &a_args) = 0;
+
virtual bool load_program (const UString &a_prog,
const vector<UString> &a_args,
const UString &a_working_dir,
diff --git a/src/persp/dbgperspective/nmv-local-vars-inspector.cc b/src/persp/dbgperspective/nmv-local-vars-inspector.cc
index 85bb00d..06330b0 100644
--- a/src/persp/dbgperspective/nmv-local-vars-inspector.cc
+++ b/src/persp/dbgperspective/nmv-local-vars-inspector.cc
@@ -413,6 +413,7 @@ public:
THROW_IF_FAIL (tree_view);
+ LOG_DD ("updating variable: " << a_var->internal_name ());
Gtk::TreeModel::iterator parent_row_it;
if (get_local_variables_row_iterator (parent_row_it)) {
vutil::update_a_variable (a_var, *tree_view,
@@ -799,8 +800,12 @@ public:
// separately by a call to update_a_local_variable.
// At least that's the current behaviour with the "GDB/MI
// Variable Objects" based backend we are using.
+ LOG_DD ("Going to update var: " << (*it)->internal_name ()
+ << " that has number of children "
+ << (int) (*it)->members ().size ());
+
update_a_local_variable (*it,
- false /* do not update members */);
+ false /* Do not update members */);
local_vars_changed_at_prev_stop.push_back (*it);
}
diff --git a/src/persp/dbgperspective/nmv-variables-utils.cc b/src/persp/dbgperspective/nmv-variables-utils.cc
index 7c5762e..0038a8d 100644
--- a/src/persp/dbgperspective/nmv-variables-utils.cc
+++ b/src/persp/dbgperspective/nmv-variables-utils.cc
@@ -40,6 +40,21 @@ static void update_a_variable_real (const IDebugger::VariableSafePtr a_var,
bool a_is_new_frame,
bool a_update_members);
+static bool is_empty_row (const Gtk::TreeModel::iterator &a_row_it);
+
+static UString get_row_name (const Gtk::TreeModel::iterator &a_row_it);
+
+/// Return a copy of the name of a variable's row, as presented to the
+/// user. That name is actually the name of the variable as presented
+/// to the user.
+static UString
+get_row_name (const Gtk::TreeModel::iterator &a_row_it)
+
+{
+ Glib::ustring str = (*a_row_it)[get_variable_columns ().name];
+ return str;
+}
+
VariableColumns&
get_variable_columns ()
{
@@ -140,7 +155,7 @@ update_a_variable_node (const IDebugger::VariableSafePtr a_var,
LOG_FUNCTION_SCOPE_NORMAL_DD;
if (a_var) {
LOG_DD ("going to really update variable '"
- << a_var->name ()
+ << a_var->internal_name ()
<< "'");
} else {
LOG_DD ("eek, got null variable");
@@ -239,6 +254,7 @@ find_a_variable (const IDebugger::VariableSafePtr a_var,
{
LOG_FUNCTION_SCOPE_NORMAL_DD;
+ LOG_DD ("looking for variable: " << a_var->internal_name ());
if (!a_var) {
LOG_DD ("got null var, returning false");
return false;
@@ -252,11 +268,11 @@ find_a_variable (const IDebugger::VariableSafePtr a_var,
var = row_it->get_value (get_variable_columns ().variable);
if (variables_match (a_var, row_it)) {
a_out_row_it = row_it;
- LOG_DD ("found variable");
+ LOG_DD ("found variable at row: " << get_row_name (row_it));
return true;
}
}
- LOG_DD ("didn't find variable " << a_var->name ());
+ LOG_DD ("didn't find variable " << a_var->internal_name ());
return false;
}
@@ -298,16 +314,26 @@ static bool
walk_path_from_row (const Gtk::TreeModel::iterator &a_from,
const list<int>::const_iterator &a_path_start,
const list<int>::const_iterator &a_path_end,
- Gtk::TreeModel::iterator &a_to)
+ Gtk::TreeModel::iterator &a_to,
+ bool a_recursed)
{
LOG_FUNCTION_SCOPE_NORMAL_DD;
+ LOG_DD ("starting from row: " << get_row_name (a_from));
+
if (a_path_start == a_path_end) {
- if (a_from->parent ()) {
- a_to = a_from->parent ();
- return true;
+ if (!a_recursed) {
+ a_to = a_from;
+ } else {
+ if (a_from->parent ()) {
+ a_to = a_from->parent ();
+ } else {
+ LOG_DD ("return false");
+ return false;
+ }
}
- return false;
+ LOG_DD ("return true, row name: " << get_row_name (a_to));
+ return true;
}
Gtk::TreeModel::iterator row = a_from;
@@ -315,18 +341,27 @@ walk_path_from_row (const Gtk::TreeModel::iterator &a_from,
steps < *a_path_start && row;
++steps, ++row) {
// stepping at the current level;
+ LOG_DD ("stepped: " << steps);
}
- if (!row)
+ if (is_empty_row (row)) {
// we reached the end of the current level. That means the path
// was not well suited for this variable tree view. Bail out.
+ LOG_DD ("return false");
return false;
+ }
// Dive down one level.
list<int>::const_iterator from = a_path_start;
from++;
+ if (from == a_path_end) {
+ a_to = row;
+ LOG_DD ("return true: " << get_row_name (row));
+ return true;
+ }
return walk_path_from_row (row->children ().begin (),
- from, a_path_end, a_to);
+ from, a_path_end, a_to,
+ /*a_recursed=*/true);
}
/// Find a member variable that is a descendent of a root ancestor variable
@@ -350,12 +385,16 @@ find_a_variable_descendent (const IDebugger::VariableSafePtr a_descendent,
return false;
}
+ LOG_DD ("looking for descendent: "
+ << a_descendent->internal_name ());
+
// first, find the root variable the descendant belongs to.
IDebugger::VariableSafePtr root_var = a_descendent->root ();
THROW_IF_FAIL (root_var);
+ LOG_DD ("root var: " << root_var->internal_name ());
Gtk::TreeModel::iterator root_var_row;
if (!find_a_variable (root_var, a_parent_row, root_var_row)) {
- LOG_DD ("didn't find root variable " << root_var->name ());
+ LOG_DD ("didn't find root variable " << root_var->internal_name ());
return false;
}
@@ -368,8 +407,11 @@ find_a_variable_descendent (const IDebugger::VariableSafePtr a_descendent,
// let's walk the path from the root variable down to the descendent
// now.
if (!walk_path_from_row (root_var_row, path.begin (),
- path.end (), a_out_row)) {
- THROW ("fatal: should not be reached");
+ path.end (), a_out_row,
+ /*a_recursed=*/false)) {
+ // This can happen if a_descendent is a new child of its root
+ // variable.
+ return false;
}
return true;
}
@@ -392,8 +434,10 @@ variables_match (const IDebugger::VariableSafePtr &a_var,
return false;
if (a_var->internal_name () == var->internal_name ())
return true;
-
- return var->equals_by_value (*a_var);
+ else if (a_var->internal_name ().empty ()
+ && var->internal_name ().empty ())
+ return var->equals_by_value (*a_var);
+ return false;
}
// Update the graphical representation of variable a_var.
@@ -421,22 +465,63 @@ update_a_variable (const IDebugger::VariableSafePtr a_var,
THROW_IF_FAIL (a_parent_row_it);
Gtk::TreeModel::iterator row_it;
+ // First lets try to see if a_var is already graphically
+ // represented as a descendent of the graphical node
+ // a_parent_row_it.
bool found_variable = find_a_variable_descendent (a_var,
a_parent_row_it,
row_it);
+ IDebugger::VariableSafePtr var = a_var;
if (!found_variable) {
+ LOG_DD ("here");
+ // So a_parent_row_it doesn't have any descendent row that
+ // contains a_var. So maybe a_var is a new variable member
+ // that appeared? To verify this hypothesis, let's try to
+ // find the root variable of a_var under the a_parent_row_it
+ // node.
+ IDebugger::VariableSafePtr root = a_var->root ();
+ if (find_a_variable (root, a_parent_row_it, row_it)) {
+ // So the root node root is already graphically
+ // represented under a_parent_row_it, by row_it. That
+ // means a_var is a new member of "root". Let's update
+ // root altogether. That will indirectly graphically
+ // append a_var underneath row_it, at the right spot.
+ LOG_DD ("Found a new member to append: "
+ << a_var->internal_name ());
+ var = root;
+ a_update_members = true;
+ // For now, we voluntarily don't handle highlighting when
+ // adding new members as we don't handle this case well
+ // and mess things up instead.
+ a_handle_highlight = false;
+ goto real_job;
+ }
LOG_ERROR ("could not find variable in inspector: "
- + a_var->name ());
+ + a_var->internal_name ());
return false;
}
- update_a_variable_real (a_var, a_tree_view,
+ real_job:
+ update_a_variable_real (var, a_tree_view,
row_it, a_truncate_type, a_handle_highlight,
a_is_new_frame, a_update_members);
return true;
}
+/// Return true iff the row pointed to by the iterator in argument has
+/// no instance of IDebugger::Variable stored in.
+static bool
+is_empty_row (const Gtk::TreeModel::iterator &a_row_it)
+{
+ if (!a_row_it)
+ return true;
+ IDebugger::VariableSafePtr v = (*a_row_it)[get_variable_columns ().variable];
+ if (!v)
+ return true;
+ return false;
+}
+
// Subroutine of update_a_variable. See that function comments to learn
// more.
// \param a_var the symbolic representation of the variable we are interested in.
@@ -459,24 +544,52 @@ update_a_variable_real (const IDebugger::VariableSafePtr a_var,
bool a_is_new_frame,
bool a_update_members)
{
+ LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+ LOG_DD ("Going to update variable " << a_var->internal_name ());
+ LOG_DD ("Its num members: " << (int) a_var->members ().size ());
+
+ if (a_update_members) {
+ LOG_DD ("Going to update its members too");
+ } else {
+ LOG_DD ("Not going to update its members, though");
+ }
+
update_a_variable_node (a_var,
a_tree_view,
a_row_it,
a_truncate_type,
a_handle_highlight,
a_is_new_frame);
+
Gtk::TreeModel::iterator row_it;
list<IDebugger::VariableSafePtr>::const_iterator var_it;
Gtk::TreeModel::Children rows = a_row_it->children ();
+
if (a_update_members) {
+ LOG_DD ("Updating members of" << a_var->internal_name ());
for (row_it = rows.begin (), var_it = a_var->members ().begin ();
- row_it != rows.end () && var_it != a_var->members ().end ();
- ++row_it, ++var_it) {
- update_a_variable_real (*var_it, a_tree_view,
- row_it, a_truncate_type,
- a_handle_highlight,
- a_is_new_frame,
- true /* update members */);
+ var_it != a_var->members ().end ();
+ ++var_it) {
+ if (row_it != rows.end () && !is_empty_row (row_it)) {
+ LOG_DD ("updating member: " << (*var_it)->internal_name ());
+ update_a_variable_real (*var_it, a_tree_view,
+ row_it, a_truncate_type,
+ a_handle_highlight,
+ a_is_new_frame,
+ true /* update members */);
+ ++row_it;
+ } else {
+ // var_it is a member that is new, compared to the
+ // previous members of a_var that were already present
+ // in the graphical representation. Thus we need to
+ // append this new child member to the graphical
+ // representation of a_var.
+ LOG_DD ("appending new member: "
+ << (*var_it)->internal_name ());
+ append_a_variable (*var_it, a_tree_view,
+ a_row_it, a_truncate_type);
+ }
}
}
}
@@ -670,4 +783,3 @@ visualize_a_variable (const IDebugger::VariableSafePtr a_var,
NEMIVER_END_NAMESPACE (variables_utils2)
NEMIVER_END_NAMESPACE (nemiver)
-
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 5a069bf..6a55a02 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -16,7 +16,7 @@ runtestlocalvarslist runtestcpplexer \
runtestcppparser runtestvarpathexpr \
runtestlibtoolwrapperdetection \
runtestenv runtesttypes runtestdisassemble \
-runtestvariableformat
+runtestvariableformat runtestprettyprint
else
@@ -29,7 +29,7 @@ $(TESTS) \
runtestcore runteststdout docore inout \
pointerderef fooprog templatedvar \
gtkmmtest dostackoverflow bigvar threads \
-forkparent forkchild
+forkparent forkchild prettyprint
runtestgdbmi_SOURCES=$(h)/test-gdbmi.cc
runtestgdbmi_LDADD= @NEMIVERCOMMON_LIBS@ \
@@ -51,6 +51,9 @@ forkparent_LDADD=
forkchild_SOURCES=$(h)/fork-child.cc
forkchild_LDADD=
+prettyprint_SOURCES=$(h)/pretty-print.cc
+prettyprint_LDADD=
+
runtestbreakpoint_SOURCES=$(h)/test-breakpoint.cc
runtestbreakpoint_LDADD= NEMIVERCOMMON_LIBS@ \
$(top_builddir)/src/common/libnemivercommon.la \
@@ -76,6 +79,11 @@ runtestvariableformat_LDADD= NEMIVERCOMMON_LIBS@ \
$(top_builddir)/src/common/libnemivercommon.la \
$(top_builddir)/src/dbgengine/libdebuggerutils.la
+runtestprettyprint_SOURCES=$(h)/test-pretty-print.cc
+runtestprettyprint_LDADD= NEMIVERCOMMON_LIBS@ \
+$(top_builddir)/src/common/libnemivercommon.la \
+$(top_builddir)/src/dbgengine/libdebuggerutils.la
+
#runtestoverloads_SOURCES=$(h)/test-overloads.cc
#runtestoverloads_LDADD= NEMIVERCOMMON_LIBS@ \
#$(top_builddir)/src/common/libnemivercommon.la
diff --git a/tests/pretty-print.cc b/tests/pretty-print.cc
new file mode 100644
index 0000000..0d54317
--- /dev/null
+++ b/tests/pretty-print.cc
@@ -0,0 +1,22 @@
+#include <vector>
+#include <string>
+
+int
+main()
+{
+ unsigned __attribute__((unused)) s;
+ std::string s1 = "kÃlÃ", s2 = "fila", s3 = "saba";
+ std::vector<std::string> v;
+ v.push_back(s1);
+ v.push_back(s2);
+ v.push_back(s3);
+
+ std::vector<std::string>::iterator i = v.begin ();
+ i += 2;
+
+ v.erase (i);
+
+ s = v.size ();
+
+ return 0;
+}
diff --git a/tests/test-gdbmi.cc b/tests/test-gdbmi.cc
index 1a12a4e..29f2e3f 100644
--- a/tests/test-gdbmi.cc
+++ b/tests/test-gdbmi.cc
@@ -63,6 +63,7 @@ static const char *gv_output_record7="^done,changelist=[{name=\"var1.public.m_fi
static const char *gv_output_record8="^done,changelist=[]\n";
+static const char *gv_output_record9="^done,changelist=[{name=\"var1\",value=\"{...}\",in_scope=\"true\",type_changed=\"false\",new_num_children=\"2\",displayhint=\"array\",dynamic=\"1\",has_more=\"0\",new_children=[{name=\"var1.[1]\",exp=\"[1]\",numchild=\"0\",value=\" \\\"fila\\\"\",type=\"std::basic_string<char, std::char_traits<char>, std::allocator<char> >\",thread-id=\"1\",displayhint=\"string\",dynamic=\"1\"}]},{name=\"var1.[0]\",value=\"\\\"k\\303\\251l\\303\\251\\\"\",in_scope=\"true\",type_changed=\"false\",displayhint=\"array\",dynamic=\"1\",has_more=\"0\"}]\n";
static const char *gv_stack0 =
"stack=[frame={level=\"0\",addr=\"0x000000330f832f05\",func=\"raise\",file=\"../nptl/sysdeps/unix/sysv/linux/raise.c\",fullname=\"/usr/src/debug/glibc-20081113T2206/nptl/sysdeps/unix/sysv/linux/raise.c\",line=\"64\"},frame={level=\"1\",addr=\"0x000000330f834a73\",func=\"abort\",file=\"abort.c\",fullname=\"/usr/src/debug/glibc-20081113T2206/stdlib/abort.c\",line=\"88\"},frame={level=\"2\",addr=\"0x0000000000400872\",func=\"overflow_after_n_recursions\",file=\"do-stack-overflow.cc\",fullname=\"/home/dodji/devel/git/nemiver.git/tests/do-stack-overflow.cc\",line=\"7\"},frame={level=\"3\",addr=\"0x000000000040087e\",func=\"overflow_after_n_recursions\",file=\"do-stack-overflow.cc\",fullname=\"/home/dodji/devel/git/nemiver.git/tests/do-stack-overflow.cc\",line=\"8\"},frame={level=\"4\",addr=\"0x000000000040087e\",func=\"overflow_after_n_recursions\",file=\"do-stack-overflow.cc\",fullname=\"/home/dodji/devel/git/nemiver.git/tests/do-stack-overflow.cc\",line=\"8\"},frame={level=\"5\
",addr=\"0x000000000040087e\",func=\"overflow_after_n_recursions\",file=\"do-stack-overflow.cc\",fullname=\"/home/dodji/devel/git/nemiver.git/tests/do-stack-overflow.cc\",line=\"8\"},frame={level=\"6\",addr=\"0x000000000040087e\",func=\"overflow_after_n_recursions\",file=\"do-stack-overflow.cc\",fullname=\"/home/dodji/devel/git/nemiver.git/tests/do-stack-overflow.cc\",line=\"8\"},frame={level=\"7\",addr=\"0x000000000040087e\",func=\"overflow_after_n_recursions\",file=\"do-stack-overflow.cc\",fullname=\"/home/dodji/devel/git/nemiver.git/tests/do-stack-overflow.cc\",line=\"8\"},frame={level=\"8\",addr=\"0x000000000040087e\",func=\"overflow_after_n_recursions\",file=\"do-stack-overflow.cc\",fullname=\"/home/dodji/devel/git/nemiver.git/tests/do-stack-overflow.cc\",line=\"8\"},frame={level=\"9\",addr=\"0x000000000040087e\",func=\"overflow_after_n_recursions\",file=\"do-stack-overflow.cc\",fullname=\"/home/dodji/devel/git/nemiver.git/tests/do-stack-overflow.cc\",line=\"8\"},frame=
{level=\"10\",addr=\"0x000000000040087e\",func=\"overflow_after_n_recursions\",file=\"do-stack-overflow.cc\",fullname=\"/home/dodji/devel/git/nemiver.git/tests/do-stack-overflow.cc\",line=\"8\"},frame={level=\"11\",addr=\"0x000000000040087e\",func=\"overflow_after_n_recursions\",file=\"do-stack-overflow.cc\",fullname=\"/home/dodji/devel/git/nemiver.git/tests/do-stack-overflow.cc\",line=\"8\"},frame={level=\"12\",addr=\"0x000000000040087e\",func=\"overflow_after_n_recursions\",file=\"do-stack-overflow.cc\",fullname=\"/home/dodji/devel/git/nemiver.git/tests/do-stack-overflow.cc\",line=\"8\"},frame={level=\"13\",addr=\"0x000000000040087e\",func=\"overflow_after_n_recursions\",file=\"do-stack-overflow.cc\",fullname=\"/home/dodji/devel/git/nemiver.git/tests/do-stack-overflow.cc\",line=\"8\"},frame={level=\"14\",addr=\"0x000000000040087e\",func=\"overflow_after_n_recursions\",file=\"do-stack-overflow.cc\",fullname=\"/home/dodji/devel/git/nemiver.git/tests/do-stack-overflow.cc\",li
ne=\"8\"},frame={level=\"15\",addr=\"0x000000000040087e\",func=\"overflow_after_n_recursions\",file=\"do-stack-overflow.cc\",fullname=\"/home/dodji/devel/git/nemiver.git/tests/do-stack-overflow.cc\",line=\"8\"},frame={level=\"16\",addr=\"0x000000000040087e\",func=\"overflow_after_n_recursions\",file=\"do-stack-overflow.cc\",fullname=\"/home/dodji/devel/git/nemiver.git/tests/do-stack-overflow.cc\",line=\"8\"},frame={level=\"17\",addr=\"0x000000000040087e\",func=\"overflow_after_n_recursions\",file=\"do-stack-overflow.cc\",fullname=\"/home/dodji/devel/git/nemiver.git/tests/do-stack-overflow.cc\",line=\"8\"},frame={level=\"18\",addr=\"0x000000000040087e\",func=\"overflow_after_n_recursions\",file=\"do-stack-overflow.cc\",fullname=\"/home/dodji/devel/git/nemiver.git/tests/do-stack-overflow.cc\",line=\"8\"},frame={level=\"19\",addr=\"0x000000000040087e\",func=\"overflow_after_n_recursions\",file=\"do-stack-overflow.cc\",fullname=\"/home/dodji/devel/git/nemiver.git/tests/do-stack-
overflow.cc\",line=\"8\"},frame={level=\"20\",addr=\"0x000000000040087e\",func=\"overflow_after_n_recursions\",file=\"do-stack-overflow.cc\",fullname=\"/home/dodji/devel/git/nemiver.git/tests/do-stack-overflow.cc\",line=\"8\"},frame={level=\"21\",addr=\"0x000000000040087e\",func=\"overflow_after_n_recursions\",file=\"do-stack-overflow.cc\",fullname=\"/home/dodji/devel/git/nemiver.git/tests/do-stack-overflow.cc\",line=\"8\"},frame={level=\"22\",addr=\"0x000000000040087e\",func=\"overflow_after_n_recursions\",file=\"do-stack-overflow.cc\",fullname=\"/home/dodji/devel/git/nemiver.git/tests/do-stack-overflow.cc\",line=\"8\"},frame={level=\"23\",addr=\"0x000000000040087e\",func=\"overflow_after_n_recursions\",file=\"do-stack-overflow.cc\",fullname=\"/home/dodji/devel/git/nemiver.git/tests/do-stack-overflow.cc\",line=\"8\"},frame={level=\"24\",addr=\"0x000000000040087e\",func=\"overflow_after_n_recursions\",file=\"do-stack-overflow.cc\",fullname=\"/home/dodji/devel/git/nemiver.git
/tests/do-stack-overflow.cc\",line=\"8\"},frame={level=\"25\",addr=\"0x000000000040087e\",func=\"overflow_after_n_recursions\",file=\"do-stack-overflow.cc\",fullname=\"/home/dodji/devel/git/nemiver.git/tests/do-stack-overflow.cc\",line=\"8\"}]";
@@ -412,16 +413,41 @@ test_output_record ()
&& v[i]->name () == "m_age"
&& v[i]->type () == "unsigned int"
&& v[i]->internal_name () == "var2.public.m_age");
+
// gv_output_record7 should result in 1 variable.
parser.push_input (gv_output_record7);
is_ok = parser.parse_output_record (0, to, output);
BOOST_REQUIRE (is_ok);
- BOOST_REQUIRE (output.result_record ().has_changed_var_list ());
+ BOOST_REQUIRE (output.result_record ().has_var_changes ());
+
// gv_output_record8 should result in 0 variable.
parser.push_input (gv_output_record8);
is_ok = parser.parse_output_record (0, to, output);
BOOST_REQUIRE (is_ok);
- BOOST_REQUIRE (output.result_record ().has_changed_var_list ());
+ BOOST_REQUIRE (output.result_record ().has_var_changes ());
+
+ // gv_output_record9 should contain a list of 2 var_changes.
+ parser.push_input (gv_output_record9);
+ is_ok = parser.parse_output_record (0, to, output);
+ BOOST_REQUIRE (is_ok);
+ BOOST_REQUIRE (output.result_record ().has_var_changes ());
+ BOOST_REQUIRE (output.result_record ().var_changes ().size () == 2);
+ {
+ // The first var_change should represent a variable of internal
+ // name "var1" that has one new child named var1.[1]
+ list<nemiver::VarChangePtr>::const_iterator it =
+ output.result_record ().var_changes ().begin ();
+ BOOST_REQUIRE ((*it)->variable ()->internal_name () == "var1");
+ BOOST_REQUIRE ((*it)->new_children ().size () == 1);
+ BOOST_REQUIRE ((*it)->new_children ().front ()-> internal_name ()
+ == "var1.[1]");
+
+ // The second var_change should represent a variable of
+ // internal name var1.[0] which value is "kÃlÃ".
+ ++it;
+ BOOST_REQUIRE ((*it)->variable ()->internal_name () == "var1.[0]");
+ BOOST_REQUIRE ((*it)->variable ()->value () == "\"kÃlÃ\"");
+ }
}
void
diff --git a/tests/test-pretty-print.cc b/tests/test-pretty-print.cc
new file mode 100644
index 0000000..d345a7b
--- /dev/null
+++ b/tests/test-pretty-print.cc
@@ -0,0 +1,206 @@
+#include "config.h"
+#include <iostream>
+#include <map>
+#include <string>
+#include <boost/test/minimal.hpp>
+#include <glibmm.h>
+#include "common/nmv-initializer.h"
+#include "common/nmv-exception.h"
+#include "nmv-debugger-utils.h"
+
+using namespace nemiver;
+using namespace nemiver::common;
+
+Glib::RefPtr<Glib::MainLoop> s_loop =
+ Glib::MainLoop::create (Glib::MainContext::get_default ());
+
+IDebugger::VariableSafePtr v_var;
+
+static void
+on_engine_died_signal ()
+{
+ s_loop->quit ();
+}
+
+static void
+on_program_finished_signal ()
+{
+ s_loop->quit ();
+}
+
+static void
+on_running_signal ()
+{
+}
+
+static void
+on_variable_unfolded_with_n_children (const IDebugger::VariableSafePtr a_var,
+ unsigned n,
+ IDebuggerSafePtr a_debugger)
+{
+ NEMIVER_TRY;
+
+ BOOST_REQUIRE (a_var->name () == "v");
+ BOOST_REQUIRE (a_var->members ().size () == n);
+ a_debugger->step_over ();
+
+ NEMIVER_CATCH_NOX;
+}
+
+static void
+on_variable_v_created_empty (const IDebugger::VariableSafePtr a_var,
+ IDebuggerSafePtr a_debugger)
+{
+ NEMIVER_TRY;
+
+ BOOST_REQUIRE (a_var->name () == "v");
+ BOOST_REQUIRE (a_var->is_dynamic ());
+ BOOST_REQUIRE (a_var->members ().empty ());
+ v_var = a_var;
+ a_debugger->step_over ();
+
+ NEMIVER_CATCH_NOX;
+}
+
+static void
+on_variable_v_has_n_children (const std::list<IDebugger::VariableSafePtr> &a_vars,
+ unsigned n, IDebuggerSafePtr a_debugger)
+{
+ NEMIVER_TRY;
+
+ MESSAGE ("Number of children v is supposed to have: " << (int) n);
+ MESSAGE ("a_vars.size(): " << (int) a_vars.size ());
+
+ if (a_vars.size () == 1) {
+ // It means that a_vars only contains "v" itself, and that it
+ // needs unfolding.
+ IDebugger::VariableSafePtr var = a_vars.front ();
+ BOOST_REQUIRE (var->name () == "v");
+ if (var->needs_unfolding ())
+ a_debugger->unfold_variable
+ (var, sigc::bind (&on_variable_unfolded_with_n_children,
+ 1, a_debugger));
+ else
+ a_debugger->step_over ();
+
+ } else {
+ // A vars should contain the last updated child of "v", as
+ // well as v itself.
+ BOOST_REQUIRE (a_vars.size () == 2);
+
+ // And the first variable of a_vars should be "v".
+ std::list<IDebugger::VariableSafePtr>::const_iterator it =
+ a_vars.begin ();
+ IDebugger::VariableSafePtr var = *it;
+ BOOST_REQUIRE (var->name () == "v");
+ // And v should contain n children.
+ BOOST_REQUIRE (var->members ().size () == n);
+ a_debugger->step_over ();
+ }
+
+ NEMIVER_CATCH_NOX;
+}
+
+static void
+on_stopped_signal (IDebugger::StopReason /*a_reason*/,
+ bool a_has_frame,
+ const IDebugger::Frame &a_frame,
+ int, int,
+ const UString &/*a_cookie*/,
+ IDebuggerSafePtr &a_debugger)
+{
+ static int nb_stops_in_main = 0;
+
+ NEMIVER_TRY;
+
+ if (a_has_frame) {
+ MESSAGE ("frame name: " << a_frame.function_name ());
+ if (a_frame.function_name () == "main") {
+ ++nb_stops_in_main;
+ MESSAGE ("stopped " << nb_stops_in_main << " times in main");
+ if (nb_stops_in_main < 3) {
+ a_debugger->step_over ();
+ } else if (nb_stops_in_main == 3) {
+ // We just stopped right after the creation of the v
+ // vector, which is empty for now.
+ a_debugger->create_variable
+ ("v", sigc::bind (&on_variable_v_created_empty, a_debugger));
+ } else if (nb_stops_in_main == 4) {
+ // We should be stopped right after s1 has been
+ // added to the v vector.
+ BOOST_REQUIRE (v_var);
+ a_debugger->list_changed_variables
+ (v_var,
+ sigc::bind (&on_variable_v_has_n_children,
+ 1,
+ a_debugger));
+ } else if (nb_stops_in_main == 5) {
+ // We should be stopped right after s2 has been
+ // added to the v vector.
+ BOOST_REQUIRE (v_var);
+ a_debugger->list_changed_variables
+ (v_var,
+ sigc::bind (&on_variable_v_has_n_children,
+ 2,
+ a_debugger));
+ } else if (nb_stops_in_main == 6) {
+ // We should be stopped right after s3 has been
+ // added to the v vector.
+ BOOST_REQUIRE (v_var);
+ a_debugger->list_changed_variables
+ (v_var,
+ sigc::bind (&on_variable_v_has_n_children,
+ 3,
+ a_debugger));
+ } else if (nb_stops_in_main){
+ a_debugger->do_continue ();
+ }
+ }
+ } else {
+ a_debugger->step_over ();
+ }
+
+ NEMIVER_CATCH_NOX;
+}
+
+NEMIVER_API int
+test_main (int, char **)
+{
+ NEMIVER_TRY;
+
+ Initializer::do_init ();
+
+ IDebuggerSafePtr debugger =
+ debugger_utils::load_debugger_iface_with_confmgr ();
+
+ // setup the debugger with the glib mainloop
+ debugger->set_event_loop_context (Glib::MainContext::get_default ());
+
+ //*******************************
+ //<connect to IDebugger events>
+ //******************************
+ debugger->engine_died_signal ().connect (&on_engine_died_signal);
+ debugger->program_finished_signal ().connect (&on_program_finished_signal);
+ debugger->running_signal ().connect (&on_running_signal);
+ debugger->stopped_signal ().connect (sigc::bind (&on_stopped_signal,
+ debugger));
+ //*******************************
+ //</connect to IDebugger events>
+ //******************************
+
+ debugger->enable_pretty_printing ();
+ debugger->load_program ("prettyprint");
+ debugger->set_breakpoint ("main");
+
+ debugger->run ();
+
+ //****************************************
+ //run the event loop.
+ //****************************************
+ s_loop->run ();
+
+ v_var.reset ();
+ NEMIVER_CATCH_AND_RETURN_NOX (-1);
+
+ return 0;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]