[nemiver] Support 'new_children' property of GDB/MI changelist LIST



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]