[PATCH] Support 'new_children' property of GDB/MI changelist LIST
- From: Dodji Seketeli <dodji seketeli org>
- To: Nemiver Development <nemiver-list gnome org>
- Subject: [PATCH] Support 'new_children' property of GDB/MI changelist LIST
- Date: Sun, 19 Feb 2012 12:27:51 +0100
Bug "657834 - Some issues the new pretty printing" again.
Normally, when no pretty printing is used, GDB let Front-Ends consider
that the size of a variable is fixed. The variable cannot "grow". In
other words, when a variable object is created for a given variable,
it has a number N of variable members. And this N remains constant
throughout the life of the variable object.
The whole variable model of Nemiver was designed around that idea of a
variable of fixed size.
With pretty printing though, it appears that things are different.
The number of members (children) variables of e.g, an instance of
std::vector grows as new elements are added to the vector. This is
called a dynamic variable object.
So suppose that for an instance of std::vector, we have a variable
object V that is already unfolded (on which we have issued a
-var-list-children command). Suppose a new element gets added to V.
And then suppose that Nemiver issues a -var-update MI command on V.
What happens is that GDB will notify Nemiver back that a new child
element has been added to V and so that the number of children of V
has changed. This is done in part, through the 'new_children' result
of the MI tuples we get from GDB. And of course, Nemiver didn't
support that 'new_children' property until now.
So this patch supports parsing of the 'new_children' property and
adapts the Nemiver model to make it support dynamic variable objects
throughout the pipeline from the GDBEngine implementation of
IDebugger, to the graphical handling of variables.
You can see the result in color in the screencast below[1]. Your
system must be able to handle the Webm video format to watch it.
So we are getting closer to close this bug.
[1]: http://dodji.seketeli.net/films/nemiver-pretty-printing.webm
Tested and applied to master.
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.
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\",line=\"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;
+}
--
Dodji
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]