[nemiver/varobjs-support] Initial backend support for GDB Variable Objects



commit b4f00ae37fa16727a71447b8853f8559eb4e3b8a
Author: Dodji Seketeli <dodji redhat com>
Date:   Sun Mar 15 01:15:03 2009 +0100

    Initial backend support for GDB Variable Objects
    
    	* nmv-dynamic-module.[cc|h] (DynamicModule::set_name,
    	DynamicModule::get_name, load_iface_using_context, ): New entry
    	points.
    	* src/dbgengine/nmv-dbg-common.h (Command::has_slot, Command::set_slot,
    	Command::get_slot, Command::number_of_variables_deleted,
    	ResultRecord::has_variable_children,
    	ResultRecord::has_variable_children): New entry points.
    	* src/dbgengine/nmv-i-debugger.h (IDebugger::Variable::internal_name): New
    	entry point to adapt the current variable type to gdb variable
    	objects.
    	(IDebugger::Variable::to_string): Print the internal name of the
    	variable as well as its name.
    	(IDebugger::variable_created_signal,
    	IDebugger::variable_deleted_signal,
    	IDebugger::variable_unfolded_signal,
    	IDebugger::build_qualified_internal_name,
    	IDebugger::Variable::num_expected_children,
    	IDebugger::Variable::has_expected_children,
    	IDebugger::create_variable,
    	IDebugger::delete_variable,
    	IDebugger::unfold_variable): New interfaces entry points.
    	* src/dbgengine/nmv-gdb-engine.h (GDBEngine::variable_created_signal,
    	GDBEngine::variable_deleted_signal,
    	GDBEngine::variable_unfolded_signal, GDBEngine::create_variable,
    	GDBEngine::delete_variable, GDBEngine::unfold_variable): Declare the
    	implementation of the new interface entry points.
    	* src/dbgengine/nmv-gdb-engine.cc (GDBEngine::variable_created_signal,
    	GDBEngine::variable_deleted_signal, GDBEngine::variable_unfolded_signal,
    	GDBEngine::variable_unfolded_signal, GDBEngine::create_variable,
    	GDBEngine::delete_variable, GDBEngine::unfold_variable): Implement the
    	new interface entry points.
    	(OnCreateVariableHandler, OnDeleteVariableHandler,
    	 OnUnfoldVariableHandler): New output handlers to act upon the result of
    	 GDBEngine::create_variable, GDBEngine::delete_engine and
    	 GDBEngine::unfold_variable
    	(GDBEngine::init_output_handlers): Register the new output handlers.
    	* src/dbgengine/nmv-gdbmi-parser.[cc,h] (GDBMIParser::parse_variable,
    	GDBMIParser::parse_variables_deleted): New entry points to parse the
    	GDB/MI result of variable object creation and deletion.
    	(GDBMIParser::parse_var_list_children): New entry point to parse MI
    	result of -var-list-children.
    	(GDBMIParser::parse_result_record): Use GDBMIParser::parse_variable and
    	GDBMIParser::parse_variables_delete, and
    	GDBMIParser::parse_var_list_children to parse the output
    	records of -var-list-create, -var-list-delete and -var-list-children.
    	* tests/test-vars.cc: New test case to test (in live)
    	IDebugger::create_variable, IDebugger::delete_variable,
    	IDebugger::unfold_variable.
    	* tests/Makefile.am: Add new test-vars.c to the build system.
    	* tests/fooprog.cc: Adjust this test case.
    	* tests/test-gdbmi.cc: Add test cases for parsing the result
    	of IDebugger::create_variable, IDebugger::delete_variable,
    	IDebugger::unfold_variable.
---
 src/common/nmv-dynamic-module.cc  |   17 ++
 src/common/nmv-dynamic-module.h   |   39 +++++
 src/dbgengine/nmv-dbg-common.h    |  152 ++++++++++++++----
 src/dbgengine/nmv-gdb-engine.cc   |  292 ++++++++++++++++++++++++++++++++++-
 src/dbgengine/nmv-gdb-engine.h    |   30 ++++-
 src/dbgengine/nmv-gdbmi-parser.cc |  314 ++++++++++++++++++++++++++++++++++++-
 src/dbgengine/nmv-gdbmi-parser.h  |   13 ++
 src/dbgengine/nmv-i-debugger.h    |   82 +++++++++-
 tests/Makefile.am                 |    6 +-
 tests/fooprog.cc                  |    4 +-
 tests/test-gdbmi.cc               |   50 ++++++
 tests/test-vars.cc                |  195 +++++++++++++++++++++++
 12 files changed, 1149 insertions(+), 45 deletions(-)

diff --git a/src/common/nmv-dynamic-module.cc b/src/common/nmv-dynamic-module.cc
index 984f809..0a5caa6 100644
--- a/src/common/nmv-dynamic-module.cc
+++ b/src/common/nmv-dynamic-module.cc
@@ -397,6 +397,7 @@ public:
 
 struct DynamicModule::Priv {
     UString real_library_path ;
+    UString name;
     DynamicModule::Loader *loader ;
 
     Priv () :
@@ -428,6 +429,20 @@ DynamicModule::set_real_library_path (const UString &a_path)
 }
 
 void
+DynamicModule::set_name (const UString &a_name)
+{
+    THROW_IF_FAIL (m_priv) ;
+    m_priv->name = a_name;
+}
+
+const UString&
+DynamicModule::get_name () const
+{
+    THROW_IF_FAIL (m_priv) ;
+    return m_priv->name;
+}
+
+void
 DynamicModule::set_module_loader (DynamicModule::Loader *a_loader)
 {
     THROW_IF_FAIL (m_priv) ;
@@ -488,6 +503,8 @@ DynamicModuleManager::load_module (const UString &a_name,
     LOG_REF_COUNT (module, a_name) ;
 
     module->set_module_loader (&a_loader) ;
+    module->set_name (a_name);
+    module->set_real_library_path (a_loader.module_library_path (a_name));
     a_loader.set_dynamic_module_manager (this) ;
     LOG_REF_COUNT (module, a_name) ;
 
diff --git a/src/common/nmv-dynamic-module.h b/src/common/nmv-dynamic-module.h
index f384384..27c543c 100644
--- a/src/common/nmv-dynamic-module.h
+++ b/src/common/nmv-dynamic-module.h
@@ -152,6 +152,8 @@ private:
 
     void set_real_library_path (const UString &a_path) ;
 
+    void set_name (const UString &a_name) ;
+
     void set_module_loader (Loader *a_loader) ;
 
 
@@ -166,6 +168,9 @@ public:
     /// \return the path of the library the module has been instanciated from.
     const UString& get_real_library_path () const ;
 
+    /// \brief gets the (short) name of this dynmod
+    const UString& get_name () const;
+
     /// \brief destructor
     virtual ~DynamicModule () ;
 
@@ -390,6 +395,40 @@ DynamicModuleManager::load_iface_with_default_manager
     return get_default_manager ().load_iface<T> (a_mod_name, a_iface_name) ;
 }
 
+/// This is a shortcut method to load an interface
+/// using the same module_manager that was used to load
+/// a_iface.
+template<class T>
+SafePtr<T, ObjectRef, ObjectUnref>
+load_iface_using_context (DynModIface &a_iface,
+                          const UString &a_iface_name)
+{
+    DynamicModuleManager *manager =
+        a_iface.get_dynamic_module ().get_module_loader ()
+                                    ->get_dynamic_module_manager ();
+    THROW_IF_FAIL (manager);
+    SafePtr<T, ObjectRef, ObjectUnref> iface =
+        manager->load_iface<T> (a_iface.get_dynamic_module ().get_name (),
+                                a_iface_name);
+    THROW_IF_FAIL (iface);
+    return iface;
+}
+
+template<class T>
+SafePtr<T, ObjectRef, ObjectUnref>
+load_iface_using_context (DynamicModule &a_dynmod,
+                          const UString &a_iface_name)
+{
+    DynamicModuleManager *manager =
+        a_dynmod.get_module_loader () ->get_dynamic_module_manager ();
+
+    THROW_IF_FAIL (manager);
+    SafePtr<T, ObjectRef, ObjectUnref> iface =
+        manager->load_iface<T> (a_dynmod.get_name (), a_iface_name);
+    THROW_IF_FAIL (iface);
+    return iface;
+}
+
 NEMIVER_END_NAMESPACE (common)
 NEMIVER_END_NAMESPACE (nemiver)
 
diff --git a/src/dbgengine/nmv-dbg-common.h b/src/dbgengine/nmv-dbg-common.h
index a0d4dd6..0ae7ec4 100644
--- a/src/dbgengine/nmv-dbg-common.h
+++ b/src/dbgengine/nmv-dbg-common.h
@@ -36,20 +36,27 @@ class Command {
     UString m_tag1 ;
     UString m_cookie ;
     IDebugger::VariableSafePtr m_var ;
+    sigc::slot_base m_slot;
 
 public:
 
-    Command ()  {clear ();}
+    Command ()  :
+        m_slot (0)
+    {
+        clear ();
+    }
 
     /// \param a_value a textual command to send to the debugger.
     Command (const UString &a_value) :
-        m_value (a_value)
+        m_value (a_value),
+        m_slot (0)
     {
     }
 
     Command (const UString &a_name, const UString &a_value) :
         m_name (a_name),
-        m_value (a_value)
+        m_value (a_value),
+        m_slot (0)
     {
     }
 
@@ -58,7 +65,8 @@ public:
              const UString &a_cookie) :
         m_name (a_name),
         m_value (a_value),
-        m_cookie (a_cookie)
+        m_cookie (a_cookie),
+        m_slot (0)
     {
     }
 
@@ -84,6 +92,23 @@ public:
     void variable (const IDebugger::VariableSafePtr a_in) {m_var = a_in;}
     IDebugger::VariableSafePtr variable () const {return m_var;}
 
+    bool has_slot () const
+    {
+        return m_slot;
+    }
+
+    template<class T>
+    void set_slot (T &a_slot)
+    {
+        m_slot = a_slot;
+    }
+
+    template<class T>
+    const T& get_slot () const
+    {
+        return reinterpret_cast<const T&> (m_slot);
+    }
+
     /// @}
 
     void clear ()
@@ -361,10 +386,56 @@ public:
         size_t m_memory_address;
         bool m_has_memory_values;
 
+        // Variable Object
+        IDebugger::VariableSafePtr m_variable;
+        bool m_has_variable;
+
+        // Variable Object deletion information
+        int m_nb_variable_deleted;
+
+        // Children variables of a given variable.
+        vector<IDebugger::VariableSafePtr> m_variable_children;
+        bool m_has_variable_children;
 
     public:
         ResultRecord () {clear () ;}
 
+        /// blank out everything
+        void clear ()
+        {
+            m_kind = UNDEFINED ;
+            m_breakpoints.clear () ;
+            m_attrs.clear () ;
+            m_call_stack.clear () ;
+            m_has_call_stack = false ;
+            m_frames_parameters.clear () ;
+            m_has_frames_parameters = false ;
+            m_local_variables.clear () ;
+            m_has_local_variables = false ;
+            m_variable_value.reset () ;
+            m_has_variable_value = false ;
+            m_thread_list.clear () ;
+            m_has_thread_list = false ;
+            m_thread_id = 0;
+            m_frame_in_thread.clear () ;
+            m_thread_id_got_selected = false;
+            m_file_list.clear () ;
+            m_has_file_list = false ;
+            m_has_current_frame_in_core_stack_trace = false ;
+            m_has_changed_registers = false;
+            m_changed_registers.clear ();
+            m_has_register_values = false;
+            m_register_values.clear ();
+            m_has_register_names = false;
+            m_register_names.clear ();
+            m_memory_values.clear ();
+            m_memory_address = 0;
+            m_has_memory_values = false;
+            m_has_variable = false;
+            m_nb_variable_deleted = 0;
+            m_has_variable_children = false;
+        }
+
         /// \name accessors
 
         /// @{
@@ -530,39 +601,52 @@ public:
             m_has_current_frame_in_core_stack_trace = a_in ;
         }
 
-        /// @}
+        bool has_variable () {return m_has_variable; }
+        void has_variable (bool a_in) {m_has_variable = a_in;}
 
-        void clear ()
+        IDebugger::VariableSafePtr variable () const
         {
-            m_kind = UNDEFINED ;
-            m_breakpoints.clear () ;
-            m_attrs.clear () ;
-            m_call_stack.clear () ;
-            m_has_call_stack = false ;
-            m_frames_parameters.clear () ;
-            m_has_frames_parameters = false ;
-            m_local_variables.clear () ;
-            m_has_local_variables = false ;
-            m_variable_value.reset () ;
-            m_has_variable_value = false ;
-            m_thread_list.clear () ;
-            m_has_thread_list = false ;
-            m_thread_id = 0;
-            m_frame_in_thread.clear () ;
-            m_thread_id_got_selected = false;
-            m_file_list.clear () ;
-            m_has_file_list = false ;
-            m_has_current_frame_in_core_stack_trace = false ;
-            m_has_changed_registers = false;
-            m_changed_registers.clear ();
-            m_has_register_values = false;
-            m_register_values.clear ();
-            m_has_register_names = false;
-            m_register_names.clear ();
-            m_memory_values.clear ();
-            m_memory_address = 0;
-            m_has_memory_values = false;
+            return m_variable;
+        }
+        void variable (const IDebugger::VariableSafePtr a_var)
+        {
+            m_variable = a_var;
+            has_variable (true);
+        }
+
+        int number_of_variables_deleted () const
+        {
+            return m_nb_variable_deleted;
+        }
+
+        void number_of_variables_deleted (int a_nb)
+        {
+            m_nb_variable_deleted = a_nb;
+        }
+
+        bool has_variable_children () const
+        {
+            return m_has_variable_children;
+        }
+        void has_variable_children (bool a_in)
+        {
+            m_has_variable_children = a_in;
+        }
+
+        const vector<IDebugger::VariableSafePtr>&
+        variable_children () const
+        {
+            return m_variable_children;
+        }
+        void
+        variable_children (const vector<IDebugger::VariableSafePtr> &a_vars)
+        {
+            m_variable_children = a_vars;
+            has_variable_children (true);
         }
+
+        /// @}
+
     };//end class ResultRecord
 
 private:
diff --git a/src/dbgengine/nmv-gdb-engine.cc b/src/dbgengine/nmv-gdb-engine.cc
index c5fd75b..18d8862 100644
--- a/src/dbgengine/nmv-gdb-engine.cc
+++ b/src/dbgengine/nmv-gdb-engine.cc
@@ -227,6 +227,14 @@ public:
                                                                   read_memory_signal;
     mutable sigc::signal <void, size_t, const std::vector<uint8_t>&, const UString& >
                                                                   set_memory_signal;
+    mutable sigc::signal<void, const VariableSafePtr, const UString&>
+                                                        variable_created_signal;
+
+    mutable sigc::signal<void, const VariableSafePtr, const UString&>
+                                                        variable_deleted_signal;
+
+    mutable sigc::signal<void, const VariableSafePtr, const UString&>
+                                                        variable_unfolded_signal;
 
     //***********************
     //</GDBEngine attributes>
@@ -2012,6 +2020,144 @@ struct OnErrorHandler : OutputHandler {
     }
 };//struct OnErrorHandler
 
+struct OnCreateVariableHandler : public OutputHandler
+{
+    GDBEngine *m_engine;
+
+    OnCreateVariableHandler (GDBEngine *a_engine) :
+        m_engine (a_engine)
+    {
+    }
+
+    bool can_handle (CommandAndOutput &a_in)
+    {
+        if (a_in.output ().has_result_record ()
+            && (a_in.output ().result_record ().kind ()
+            == Output::ResultRecord::DONE)
+            && (a_in.command ().name () == "create-variable")
+            && (a_in.output ().result_record ().has_variable ())) {
+            LOG_DD ("handler selected");
+            return true;
+        }
+        return false;
+    }
+
+    void do_handle (CommandAndOutput &a_in __attribute__ ((unused)))
+    {
+        // Set the name of the variable to the name that got stored
+        // in the tag0 member of the command.
+        a_in.output ().result_record ().variable ()->name
+                                                (a_in.command ().tag0 ());
+
+        // Call the slot associated to IDebugger::create_variable (), if
+        // any.
+        if (a_in.command ().has_slot ()) {
+            typedef sigc::slot<void, IDebugger::VariableSafePtr> SlotType;
+            SlotType slot = a_in.command ().get_slot<SlotType> ();
+            slot (a_in.output ().result_record ().variable ());
+        }
+        // Emit the general IDebugger::variable_create_signal () signal
+        m_engine->variable_created_signal ().emit
+                            (a_in.output ().result_record ().variable (),
+                             a_in.command ().cookie ());
+        if (m_engine->get_state () != IDebugger::PROGRAM_EXITED
+            || m_engine->get_state () != IDebugger::NOT_STARTED) {
+            m_engine->set_state (IDebugger::READY);
+        }
+    }
+};// end OnCreateVariableHandler
+
+struct OnDeleteVariableHandler : public OutputHandler {
+    GDBEngine *m_engine;
+
+    OnDeleteVariableHandler (GDBEngine *a_engine) :
+        m_engine (a_engine)
+    {
+    }
+
+    bool can_handle (CommandAndOutput &a_in)
+    {
+        if (a_in.output ().has_result_record ()
+            && (a_in.output ().result_record ().kind ()
+            == Output::ResultRecord::DONE)
+            && (a_in.command ().name () == "delete-variable")
+            && (a_in.output ().result_record ().number_of_variables_deleted ())) {
+            LOG_DD ("handler selected");
+            return true;
+        }
+        return false;
+    }
+
+    void do_handle (CommandAndOutput &a_in)
+    {
+        THROW_IF_FAIL (a_in.command ().variable ());
+        THROW_IF_FAIL (m_engine);
+
+        // Call the slot associated to IDebugger::delete_variable (), if
+        // Any.
+        if (a_in.command ().has_slot ()) {
+            typedef sigc::slot<void, const IDebugger::VariableSafePtr> SlotType;
+            SlotType slot = a_in.command ().get_slot<SlotType> ();
+            slot (a_in.command ().variable ());
+        }
+        // Emit the general IDebugger::variable_deleted_signal ().
+        m_engine->variable_deleted_signal ().emit
+                (a_in.command ().variable (),
+                 a_in.command ().cookie ());
+    }
+}; // end OnDeleteVariableHandler
+
+struct OnUnfoldVariableHandler : public OutputHandler {
+    GDBEngine *m_engine;
+
+    OnUnfoldVariableHandler (GDBEngine *a_engine) :
+        m_engine (a_engine)
+    {
+    }
+
+    bool can_handle (CommandAndOutput &a_in)
+    {
+        if ((a_in.output ().result_record ().kind ()
+             == Output::ResultRecord::DONE)
+            && a_in.output ().result_record ().has_variable_children ()
+            && a_in.command ().name () == "unfold-variable") {
+            LOG_DD ("handler selected");
+            return true;
+        }
+        return false;
+    }
+
+    void do_handle (CommandAndOutput &a_in)
+    {
+        // *************************************************************
+        // append the unfolded variable children to the parent variable
+        // supplied by the client code.
+        // *************************************************************
+        IDebugger::VariableSafePtr parent_var = a_in.command ().variable ();
+        THROW_IF_FAIL (parent_var);
+        typedef vector<IDebugger::VariableSafePtr> Variables;
+        Variables children_vars =
+            a_in.output ().result_record ().variable_children ();
+        for (Variables::const_iterator it = children_vars.begin ();
+             it != children_vars.end ();
+             ++it) {
+            parent_var->append (*it);
+        }
+
+        // Call the slot associated to IDebugger::unfold_variable (), if
+        // any.
+        if (a_in.command ().has_slot ()) {
+            typedef sigc::slot<void, const IDebugger::VariableSafePtr> SlotType;
+            SlotType slot = a_in.command ().get_slot<SlotType> ();
+            slot (a_in.command ().variable ());
+        }
+
+        // Now tell the world we have an unfolded variable.
+        m_engine->variable_unfolded_signal ().emit
+                                    (parent_var, a_in.command ().cookie ());
+    }
+};// End struct OnUnfoldVariableHandler
+
 //****************************
 //</GDBengine output handlers
 //***************************
@@ -2286,6 +2432,12 @@ GDBEngine::init_output_handlers ()
             (OutputHandlerSafePtr (new OnReadMemoryHandler (this)));
     m_priv->output_handler_list.add
             (OutputHandlerSafePtr (new OnSetMemoryHandler (this)));
+    m_priv->output_handler_list.add
+            (OutputHandlerSafePtr (new OnCreateVariableHandler (this)));
+    m_priv->output_handler_list.add
+            (OutputHandlerSafePtr (new OnDeleteVariableHandler (this)));
+    m_priv->output_handler_list.add
+            (OutputHandlerSafePtr (new OnUnfoldVariableHandler (this)));
 }
 
 sigc::signal<void, Output&>&
@@ -2537,6 +2689,24 @@ GDBEngine::state_changed_signal () const
     return m_priv->state_changed_signal;
 }
 
+sigc::signal<void, const IDebugger::VariableSafePtr, const UString&>&
+GDBEngine::variable_created_signal () const
+{
+    return m_priv->variable_created_signal;
+}
+
+sigc::signal<void, const IDebugger::VariableSafePtr, const UString&>&
+GDBEngine::variable_deleted_signal () const
+{
+    return m_priv->variable_deleted_signal;
+}
+
+sigc::signal<void, const IDebugger::VariableSafePtr, const UString&>&
+GDBEngine::variable_unfolded_signal () const
+{
+    return m_priv->variable_unfolded_signal;
+}
+
 //******************
 //<signal handlers>
 //******************
@@ -3613,16 +3783,134 @@ GDBEngine::set_memory (size_t a_addr,
             iter != a_bytes.end (); ++iter)
     {
         UString cmd_str;
-        cmd_str.printf ("-data-evaluate-expression \"*(unsigned char*)%zu = 0x%X\"",
+        cmd_str.printf ("-data-evaluate-expression "
+                        "\"*(unsigned char*)%zu = 0x%X\"",
                 a_addr++,
                 *iter);
-        Command command("set-memory", cmd_str, a_cookie);
+        Command command ("set-memory", cmd_str, a_cookie);
         command.tag0 ("set-memory");
         command.tag1 (UString ().printf ("0x%X",a_addr));
         queue_command (command);
     }
 }
 
+void
+GDBEngine::create_variable (const UString &a_name,
+                            const UString &a_cookie)
+{
+    LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+    if (a_name.empty ()) {
+        LOG ("got empty name");
+        return;
+    }
+
+    Command command ("create-variable",
+                     "-var-create - * " + a_name,
+                     a_cookie);
+    command.tag0 (a_name);
+
+    queue_command (Command ("create-variable",
+                            "-var-create - * " + a_name,
+                            a_cookie));
+}
+
+void
+GDBEngine::create_variable (const UString &a_name ,
+                            const sigc::slot<void, const VariableSafePtr> &a_slot)
+{
+    LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+    if (a_name.empty ()) {
+        LOG ("got empty name");
+        return;
+    }
+
+    Command command ("create-variable",
+                     "-var-create - * " + a_name);
+    command.tag0 (a_name);
+    command.set_slot (a_slot);
+
+    queue_command (command);
+}
+
+void
+GDBEngine::delete_variable (const VariableSafePtr a_var,
+                            const UString &a_cookie)
+{
+    LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+    THROW_IF_FAIL (a_var);
+    THROW_IF_FAIL (!a_var->internal_name ().empty ());
+
+    Command command ("delete-variable",
+                     "-var-delete " + a_var->internal_name (),
+                     a_cookie);
+    command.variable (a_var);
+    queue_command (command);
+}
+
+void
+GDBEngine::delete_variable (const VariableSafePtr a_var,
+                            const sigc::slot<void,const VariableSafePtr> &a_slot)
+{
+    LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+    THROW_IF_FAIL (a_var);
+    THROW_IF_FAIL (!a_var->internal_name ().empty ());
+
+    Command command ("delete-variable",
+                     "-var-delete " + a_var->internal_name ());
+    command.variable (a_var);
+    command.set_slot (a_slot);
+
+    queue_command (command);
+}
+
+void
+GDBEngine::unfold_variable (const VariableSafePtr a_var,
+                            const UString &a_cookie)
+{
+    LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+    THROW_IF_FAIL (a_var);
+    if (a_var->internal_name ().empty ()) {
+        UString qname;
+        a_var->build_qualified_internal_name (qname);
+        a_var->internal_name (qname);
+    }
+    THROW_IF_FAIL (!a_var->internal_name ().empty ());
+
+    Command command ("unfold-variable",
+                     "-var-list-children" + a_var->internal_name (),
+                     a_cookie);
+    command.variable (a_var);
+
+    queue_command (command);
+}
+
+void
+GDBEngine::unfold_variable (const VariableSafePtr a_var,
+                            const sigc::slot<void, const VariableSafePtr> &a_slot)
+{
+    LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+    THROW_IF_FAIL (a_var);
+    if (a_var->internal_name ().empty ()) {
+        UString qname;
+        a_var->build_qualified_internal_name (qname);
+        a_var->internal_name (qname);
+    }
+    THROW_IF_FAIL (!a_var->internal_name ().empty ());
+
+    Command command ("unfold-variable",
+                     "-var-list-children " + a_var->internal_name ());
+    command.variable (a_var);
+    command.set_slot (a_slot);
+
+    queue_command (command);
+}
+
 //****************************
 //</GDBEngine methods>
 //****************************
diff --git a/src/dbgengine/nmv-gdb-engine.h b/src/dbgengine/nmv-gdb-engine.h
index 833ec75..aaeda62 100644
--- a/src/dbgengine/nmv-gdb-engine.h
+++ b/src/dbgengine/nmv-gdb-engine.h
@@ -173,6 +173,15 @@ public:
                                                     read_memory_signal () const;
     sigc::signal <void, size_t, const std::vector<uint8_t>&, const UString& >&
                                                       set_memory_signal () const;
+
+    sigc::signal<void, const VariableSafePtr, const UString&>&
+                                                variable_created_signal () const;
+
+    sigc::signal<void, const VariableSafePtr, const UString&>&
+                                        variable_deleted_signal () const;
+
+    sigc::signal<void, const VariableSafePtr, const UString&>&
+                                                variable_unfolded_signal () const;
     //*************
     //</signals>
     //*************
@@ -290,7 +299,7 @@ public:
     map<int, IDebugger::BreakPoint>& get_cached_breakpoints () ;
 
     void set_catch (const UString &a_event,
-					const UString &a_cookie)  ;
+                    const UString &a_cookie)  ;
 
     void enable_breakpoint (gint a_break_num,
                             const UString &a_cookie="");
@@ -359,7 +368,8 @@ public:
                             int &a_proc_pid,
                             UString &a_exe_path) ;
 
-    typedef std::map<UString, std::list<IDebugger::VariableSafePtr> > VarsPerFilesMap ;
+    typedef std::map<UString, std::list<IDebugger::VariableSafePtr> >
+                                                                VarsPerFilesMap ;
     bool extract_global_variable_list (Output &a_output,
                                        VarsPerFilesMap &a_vars) ;
 
@@ -382,6 +392,22 @@ public:
     void set_memory (size_t a_addr,
                      const std::vector<uint8_t>& a_bytes,
                      const UString& a_cookie="");
+
+    void create_variable (const UString &a_name,
+                          const UString &a_cookie="");
+    void create_variable (const UString &a_name,
+                          const sigc::slot<void, const VariableSafePtr>&);
+
+    void delete_variable (const VariableSafePtr a_var,
+                          const UString &a_cookie);
+
+    void delete_variable (const VariableSafePtr a_var,
+                          const sigc::slot<void,const VariableSafePtr>&);
+
+    void unfold_variable (VariableSafePtr a_var,
+                          const UString &a_cookie);
+    void unfold_variable (VariableSafePtr a_var,
+                          const sigc::slot<void, const VariableSafePtr> &);
 };//end class GDBEngine
 
 NEMIVER_END_NAMESPACE (nemiver)
diff --git a/src/dbgengine/nmv-gdbmi-parser.cc b/src/dbgengine/nmv-gdbmi-parser.cc
index 741051b..83404dc 100644
--- a/src/dbgengine/nmv-gdbmi-parser.cc
+++ b/src/dbgengine/nmv-gdbmi-parser.cc
@@ -111,6 +111,8 @@ while (!m_priv->index_passed_end (a_from) && isblank (RAW_CHAR_AT (a_from))) { \
 
 #define RAW_INPUT m_priv->input.raw ()
 
+#define END_OF_INPUT(cur) m_priv->index_passed_end (cur)
+
 using namespace std;
 using namespace nemiver::common;
 
@@ -139,6 +141,11 @@ const char* PREFIX_REGISTER_VALUES = "register-values=";
 const char* PREFIX_MEMORY_VALUES = "addr=";
 const char* PREFIX_RUNNING_ASYNC_OUTPUT = "*running,";
 const char* PREFIX_STOPPED_ASYNC_OUTPUT = "*stopped,";
+const char* PREFIX_NAME = "name=\"";
+const char* PREFIX_VARIABLE_DELETED = "ndeleted=\"";
+const char* NDELETED = "ndeleted";
+const char* PREFIX_NUMCHILD= "numchild=\"";
+const char* NUMCHILD= "numchild";
 
 bool parse_c_string_body (const UString &a_input,
                           UString::size_type a_from,
@@ -4721,7 +4728,8 @@ fetch_gdbmi_result:
                 parse_gdbmi_result (cur, cur, result);
                 THROW_IF_FAIL (result);
                 LOG_D ("parsed result", GDBMI_PARSING_DOMAIN);
-            } else if (!m_priv->input.raw ().compare (cur, strlen (PREFIX_STACK_ARGS),
+            } else if (!m_priv->input.raw ().compare (cur,
+                                                      strlen (PREFIX_STACK_ARGS),
                                                       PREFIX_STACK_ARGS)) {
                 map<int, list<IDebugger::VariableSafePtr> > frames_args;
                 if (!parse_stack_arguments (cur, cur, frames_args)) {
@@ -4796,6 +4804,39 @@ fetch_gdbmi_result:
                     LOG_D ("parsed memory values", GDBMI_PARSING_DOMAIN);
                     result_record.memory_values (addr, values);
                 }
+            } else if (!RAW_INPUT.compare (cur,
+                                           strlen (PREFIX_NAME),
+                                           PREFIX_NAME)) {
+                IDebugger::VariableSafePtr var;
+                if (!parse_variable (cur, cur, var)) {
+                    LOG_PARSING_ERROR2 (cur);
+                } else {
+                    THROW_IF_FAIL (var);
+                    LOG_D ("parsed variable, name: "
+                           << var->name ()
+                           << "internal name: "
+                           << var->internal_name (),
+                           GDBMI_PARSING_DOMAIN);
+                    result_record.variable (var);
+                }
+            } else if (!RAW_INPUT.compare (cur, strlen (PREFIX_VARIABLE_DELETED),
+                                           PREFIX_VARIABLE_DELETED)) {
+                unsigned int nb_variables_deleted = 0;
+                if (parse_variables_deleted (cur, cur, nb_variables_deleted)
+                    && nb_variables_deleted) {
+                    result_record.number_of_variables_deleted
+                                                        (nb_variables_deleted);
+                } else {
+                    LOG_PARSING_ERROR2 (cur);
+                }
+            } else if (!RAW_INPUT.compare (cur, strlen (PREFIX_NUMCHILD),
+                                           PREFIX_NUMCHILD)) {
+                vector<IDebugger::VariableSafePtr> vars;
+                if (parse_var_list_children (cur, cur, vars)) {
+                    result_record.variable_children (vars);
+                } else {
+                    LOG_PARSING_ERROR2 (cur);
+                }
             } else {
                 GDBMIResultSafePtr result;
                 if (!parse_gdbmi_result (cur, cur, result)
@@ -5998,6 +6039,215 @@ GDBMIParser::parse_variable_value (const UString::size_type a_from,
 }
 
 bool
+GDBMIParser::parse_variables_deleted (UString::size_type a_from,
+                                      UString::size_type &a_to,
+                                      unsigned int &a_nb_vars_deleted)
+{
+    LOG_FUNCTION_SCOPE_NORMAL_D (GDBMI_PARSING_DOMAIN);
+    UString::size_type cur = a_from;
+    CHECK_END2 (cur);
+
+    if (RAW_INPUT.compare (cur, strlen (PREFIX_VARIABLE_DELETED),
+                           PREFIX_VARIABLE_DELETED)) {
+        LOG_PARSING_ERROR2 (cur);
+        return false;
+    }
+    GDBMIResultSafePtr result;
+    if (!parse_gdbmi_result (cur, cur, result) || !result) {
+        LOG_PARSING_ERROR2 (cur);
+        return false;
+    }
+    if (result->variable () != NDELETED) {
+        LOG_ERROR ("expected gdbmi variable " << NDELETED << ", got: "
+                   << result->variable () << "\'");
+        return false;
+    }
+    if (!result->value ()
+        || result->value ()->content_type () != GDBMIValue::STRING_TYPE) {
+        LOG_ERROR ("expected a string value for "
+                   "the GDBMI variable " << NDELETED);
+        return false;
+    }
+    UString val = result->value ()->get_string_content ();
+    a_nb_vars_deleted = val.empty () ? 0 : atoi (val.c_str ());
+    a_to = cur;
+    return true;
+}
+
+bool
+GDBMIParser::parse_var_list_children
+                            (UString::size_type a_from,
+                             UString::size_type &a_to,
+                             std::vector<IDebugger::VariableSafePtr> &a_vars)
+{
+    LOG_FUNCTION_SCOPE_NORMAL_D (GDBMI_PARSING_DOMAIN);
+    UString::size_type cur = a_from;
+    CHECK_END2 (cur);
+
+
+    if (RAW_INPUT.compare (cur, strlen (PREFIX_NUMCHILD),
+                           PREFIX_NUMCHILD)) {
+        LOG_PARSING_ERROR2 (cur);
+        return false;
+    }
+    GDBMIResultSafePtr result;
+    if (!parse_gdbmi_result (cur, cur, result) || !result) {
+        LOG_PARSING_ERROR2 (cur);
+        return false;
+    }
+    if (result->variable () != NUMCHILD) {
+        LOG_ERROR ("expected gdbmi variable " << NUMCHILD<< ", got: "
+                   << result->variable () << "\'");
+        return false;
+    }
+    if (!result->value ()
+        || result->value ()->content_type () != GDBMIValue::STRING_TYPE) {
+        LOG_ERROR ("expected a string value for "
+                   "the GDBMI variable " << NUMCHILD);
+        return false;
+    }
+    UString s = result->value ()->get_string_content ();
+    unsigned num_children = s.empty () ? 0 : atoi (s.c_str ());
+
+    if (!num_children) {
+        LOG_D ("Variable has zero children",
+               GDBMI_PARSING_DOMAIN);
+        a_to = cur;
+        return true;
+    }
+
+    SKIP_BLANK2 (cur);
+    if (RAW_CHAR_AT (cur) != ',') {
+        LOG_PARSING_ERROR2 (cur);
+        return false;
+    }
+    ++cur;
+    SKIP_BLANK2 (cur);
+    result.reset ();
+    if (!parse_gdbmi_result (cur, cur, result) || !result) {
+        LOG_PARSING_ERROR2 (cur);
+        return false;
+    }
+    if (result->variable () != "children") {
+        LOG_ERROR ("expected gdbmi variable " << "children" << ", got: "
+                   << result->variable () << "\'");
+        return false;
+    }
+    if (!result->value ()
+        || result->value ()->content_type () != GDBMIValue::LIST_TYPE) {
+        LOG_ERROR ("expected a LIST value for "
+                   "the GDBMI variable " << "children");
+        return false;
+    }
+    GDBMIListSafePtr children = result->value ()->get_list_content ();
+    THROW_IF_FAIL (children);
+
+    if (children->empty ()) {
+        a_to = cur;
+        return true;
+    }
+    if (children->content_type () != GDBMIList::RESULT_TYPE) {
+        LOG_ERROR ("Expected the elements of the 'children' LIST to be "
+                   "of kind RESULT");
+        return false;
+    }
+    list<GDBMIResultSafePtr> children_results;
+    children->get_result_content (children_results);
+
+    if (children_results.empty ()) {
+        a_to = cur;
+        return true;
+    }
+
+    // Walk the children variables, that are packed in the children_result
+    // list of RESULT. Each RESULT represents a variable, that is a child
+    // of a_var.
+    typedef list<GDBMIResultSafePtr>::const_iterator ResultsIter;
+    for (ResultsIter result_it = children_results.begin ();
+         result_it != children_results.end ();
+         ++result_it) {
+        if (!*result_it) {
+            LOG_ERROR ("Got NULL RESULT in the list of children variables.");
+            continue;
+        }
+        if ((*result_it)->variable () != "child") {
+            LOG_ERROR ("Expected the name of the child result to be 'child'."
+                       "Got " << (*result_it)->variable ());
+            return false;
+        }
+        if ((*result_it)->value ()->content_type () != GDBMIValue::TUPLE_TYPE) {
+            LOG_ERROR ("Values of the 'child' result must be a kind TYPE");
+            return false;
+        }
+        // The components of a given variable result_it are packed into
+        // the list of RESULT below.
+        list<GDBMIResultSafePtr> child_comps =
+                        (*result_it)->value ()->get_tuple_content ()->content ();
+
+        // Walk the list of the components of the current child of a_var.
+        // At this end of this walk, we will be able to build a child
+        // of a_var, instance of IDebugger::Variable.
+        UString s, v, name, internal_name, value, type;
+        unsigned int numchildren = 0;
+        for (ResultsIter it = child_comps.begin ();
+             it != child_comps.end ();
+             ++it) {
+            if (!(*it)) {
+                LOG_ERROR ("Got a NULL RESULT as a variable component");
+                continue;
+            }
+            v = (*it)->variable ();
+            s = (*it)->value ()->get_string_content ();
+            if (v == "name") {
+                if ((*it)->value ()->content_type ()
+                    != GDBMIValue::STRING_TYPE) {
+                    LOG_ERROR ("Variable internal name should "
+                               "have a string value");
+                    continue;
+                }
+                internal_name = s;
+            } else if (v == "exp") {
+                if ((*it)->value ()->content_type ()
+                    != GDBMIValue::STRING_TYPE) {
+                    LOG_ERROR ("Variable name should "
+                               "have a string value");
+                    continue;
+                }
+                name = s;
+            } else if (v == "value") {
+                value = s;
+            } else if (v == "type") {
+                if ((*it)->value ()->content_type ()
+                    != GDBMIValue::STRING_TYPE) {
+                    LOG_ERROR ("Variable type should "
+                               "have a string value");
+                    continue;
+                }
+                type = s;
+            } else if (v == "numchild") {
+                numchildren = atoi (s.c_str ());
+            }
+        }
+        if (!name.empty ()
+            && !internal_name.empty ()
+            /* We don't require type to be non empty
+             * because members might have empty types. For instance,
+             * GDB considers the 'public' or 'protected' keywords of a class
+             * to be a member without type.
+             * We don't require value to be non empty either, as
+             * members that have children won't have any value.  */) {
+            IDebugger::VariableSafePtr var
+                (new IDebugger::Variable (internal_name,
+                                          name, value, type));
+            var->num_expected_children (numchildren);
+            a_vars.push_back (var);
+        }
+    }
+    a_to = cur;
+    return true;
+}
+
+bool
 GDBMIParser::parse_register_names (UString::size_type a_from,
                                    UString::size_type &a_to,
                                    std::map<IDebugger::register_id_t,
@@ -6304,6 +6554,68 @@ GDBMIParser::parse_memory_values (UString::size_type a_from,
 }
 
 bool
+GDBMIParser::parse_variable (UString::size_type a_from,
+                             UString::size_type &a_to,
+                             IDebugger::VariableSafePtr &a_var)
+{
+    LOG_FUNCTION_SCOPE_NORMAL_D (GDBMI_PARSING_DOMAIN);
+
+    UString::size_type cur=a_from;
+
+    if (RAW_INPUT.compare (cur, strlen (PREFIX_NAME), PREFIX_NAME)) {
+        LOG_PARSING_ERROR2 (cur);
+        return false;
+    }
+
+    IDebugger::VariableSafePtr var (new IDebugger::Variable);
+    // Okay, this is going to be a sequence of RESULT (name=value), where
+    // RESULTs are separated by a comma, e.g:
+    // name="var1", numchild="0", value="0",type="int"
+    GDBMIResultSafePtr result;
+    UString value;
+    bool got_variable = false;
+    SKIP_BLANK2 (cur);
+    while (!END_OF_INPUT (cur)
+           && RAW_CHAR_AT (cur) != '\n'
+           && parse_gdbmi_result (cur, cur, result)
+           && result) {
+        LOG_D ("result variable name: " << result->variable (),
+               GDBMI_PARSING_DOMAIN);
+        // the value of result must be a string
+        THROW_IF_FAIL (result->value ()
+                       && (result->value ()->content_type ()
+                           == GDBMIValue::STRING_TYPE));
+        value = result->value ()->get_string_content ();
+        if (result->variable () == "name") {
+            var->internal_name (value);
+            got_variable = true;
+        } else if (result->variable () == "value") {
+            var->value (value);
+        } else if (result->variable () == "numchild") {
+            int s = atoi (value.c_str ());
+            LOG_D ("num children: " << s, GDBMI_PARSING_DOMAIN);
+        } else if (result->variable () == "type") {
+            var->type (value);
+        } else {
+            LOG_D ("hugh? unknown result variable: " << result->variable (),
+                   GDBMI_PARSING_DOMAIN);
+        }
+        SKIP_BLANK2 (cur);
+        if (RAW_CHAR_AT (cur) == ',') {
+            cur++;
+        }
+        SKIP_BLANK2 (cur);
+    }
+
+    if (got_variable) {
+        a_to = cur;
+        a_var = var;
+        return true;
+    }
+    return false;
+}
+
+bool
 GDBMIParser::parse_overloads_choice_prompt
                         (UString::size_type a_from,
                          UString::size_type &a_to,
diff --git a/src/dbgengine/nmv-gdbmi-parser.h b/src/dbgengine/nmv-gdbmi-parser.h
index 202e86c..01afbe6 100644
--- a/src/dbgengine/nmv-gdbmi-parser.h
+++ b/src/dbgengine/nmv-gdbmi-parser.h
@@ -783,6 +783,18 @@ public:
                               size_t& a_start_addr,
                               std::vector<uint8_t> &a_values);
 
+    bool parse_variable (UString::size_type a_from,
+                         UString::size_type &a_to,
+                         IDebugger::VariableSafePtr &a_var);
+
+    bool parse_variables_deleted (UString::size_type a_from,
+                                  UString::size_type &a_to,
+                                  unsigned int &a_nb_vars_deleted);
+
+    bool parse_var_list_children (UString::size_type a_from,
+                                  UString::size_type &a_to,
+                                  vector<IDebugger::VariableSafePtr> &a_vars);
+
     bool parse_result_record (UString::size_type a_from,
                               UString::size_type &a_to,
                               Output::ResultRecord &a_record);
@@ -791,6 +803,7 @@ public:
     bool parse_output_record (UString::size_type a_from,
                               UString::size_type &a_to,
                               Output &a_output);
+
     //*********************
     //</Parsing entry points.>
     //*********************
diff --git a/src/dbgengine/nmv-i-debugger.h b/src/dbgengine/nmv-i-debugger.h
index 35ed562..ff56f93 100644
--- a/src/dbgengine/nmv-i-debugger.h
+++ b/src/dbgengine/nmv-i-debugger.h
@@ -279,6 +279,7 @@ public:
     typedef SafePtr<Variable, ObjectRef, ObjectUnref> VariableSafePtr;
     class Variable : public Object {
         list<VariableSafePtr> m_members;
+        UString m_internal_name;
         UString m_name;
         UString m_name_caption;
         UString m_value;
@@ -288,8 +289,22 @@ public:
         //it can be dereferenced. The variable
         //it points to is stored in m_dereferenced
         VariableSafePtr m_dereferenced;
+        unsigned int m_num_expected_children;
 
     public:
+        Variable (const UString &a_internal_name,
+                  const UString &a_name,
+                  const UString &a_value,
+                  const UString &a_type) :
+            m_internal_name (a_internal_name),
+            m_name (a_name),
+            m_value (a_value),
+            m_type (a_type),
+            m_parent (0),
+            m_num_expected_children (0)
+
+        {
+        }
 
         Variable (const UString &a_name,
                   const UString &a_value,
@@ -297,14 +312,16 @@ public:
             m_name (a_name),
             m_value (a_value),
             m_type (a_type),
-            m_parent (0)
+            m_parent (0),
+            m_num_expected_children (0)
 
         {
         }
 
         Variable (const UString &a_name) :
             m_name (a_name),
-            m_parent (0)
+            m_parent (0),
+            m_num_expected_children (0)
         {}
 
         Variable () :
@@ -320,6 +337,9 @@ public:
             a_var->parent (this);
         }
 
+        const UString& internal_name () const {return m_internal_name;}
+        void internal_name (const UString &a_in) {m_internal_name = a_in;}
+
         const UString& name () const {return m_name;}
         void name (const UString &a_name)
         {
@@ -351,6 +371,10 @@ public:
                 if (name () != "") {
                     a_str += a_indent_str + name ();
                 }
+
+                if (!internal_name ().empty ()) {
+                    a_str += "(" + internal_name () + ")";
+                }
             }
             if (value () != "") {
                 if (a_show_var_name) {
@@ -395,6 +419,21 @@ public:
             }
         }
 
+        void build_qualified_internal_name (UString &a_qname) const
+        {
+            UString qname;
+            if (parent () == 0) {
+                a_qname = internal_name ();
+            } else if (parent ()) {
+                parent ()->build_qname (qname);
+                qname.chomp ();
+                qname += "." + name ();
+                a_qname = qname;
+            } else {
+                THROW ("should not be reached");
+            }
+        }
+
         void set_dereferenced (VariableSafePtr a_derefed)
         {
             m_dereferenced  = a_derefed;
@@ -468,6 +507,21 @@ public:
                 append (var);
             }
         }
+
+        unsigned int num_expected_children () const
+        {
+            return m_num_expected_children;
+        }
+
+        void num_expected_children (unsigned int a_in)
+        {
+            m_num_expected_children = a_in;
+        }
+
+        bool has_expected_children () const
+        {
+            return m_num_expected_children != 0;
+        }
     };//end class Variable
 
     enum State {
@@ -681,6 +735,15 @@ public:
                           const std::vector<uint8_t>&,/*values*/
                           const UString& >&
                                           set_memory_signal () const = 0;
+
+    virtual sigc::signal<void, const VariableSafePtr, const UString&>&
+                                        variable_created_signal () const = 0;
+
+    virtual sigc::signal<void, const VariableSafePtr, const UString&>&
+                                        variable_deleted_signal () const = 0;
+
+    virtual sigc::signal<void, const VariableSafePtr, const UString&>&
+                                        variable_unfolded_signal () const = 0;
     /// @}
 
     virtual void do_init (IConfMgrSafePtr &a_conf_mgr) = 0;
@@ -845,7 +908,20 @@ public:
             const std::vector<uint8_t>& a_bytes,
             const UString& a_cookie="") = 0;
 
-
+    virtual void create_variable (const UString &a_name,
+                                  const UString &a_cookie="") = 0;
+    virtual void create_variable (const UString &a_name,
+                                  const sigc::slot<void,
+                                                   const VariableSafePtr>&) = 0;
+    virtual void delete_variable (const VariableSafePtr a_var,
+                                  const UString &a_cookie="") = 0;
+    virtual void delete_variable (const VariableSafePtr a_var,
+                                  const sigc::slot<void,
+                                                   const VariableSafePtr>&) = 0;
+    virtual void unfold_variable (VariableSafePtr a_var,
+                                  const UString &a_cookie) = 0;
+    virtual void unfold_variable (VariableSafePtr a_var,
+                                  const sigc::slot<void, const VariableSafePtr> &) = 0;
 };//end IDebugger
 
 NEMIVER_END_NAMESPACE (nemiver)
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 7c8998a..14e44e4 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -6,7 +6,7 @@ if AUTOTESTS
 #runtestglobalvariables
 TESTS=\
 runtestgdbmi runtestunicode \
-runtestcpptrait runtestvarlist \
+runtestvars runtestcpptrait runtestvarlist \
 runtestvarwalker runtestbreakpoint \
 runtestderef \
 runtestlocalvarslist runtestcpplexer \
@@ -71,6 +71,10 @@ runtestunicode_LDADD= NEMIVERCOMMON_LIBS@ \
 @BOOST_UNIT_TEST_FRAMEWORK_STATIC_LIB@ \
 $(top_builddir)/src/common/libnemivercommon.la
 
+runtestvars_SOURCES=test-vars.cc
+runtestvars_LDADD= NEMIVERCOMMON_LIBS@ \
+$(top_builddir)/src/common/libnemivercommon.la
+
 runtestvarlist_SOURCES=test-var-list.cc
 runtestvarlist_LDADD= NEMIVERCOMMON_LIBS@ @BOOST_TEST_EXEC_MONITOR_LIB@ \
 $(top_builddir)/src/common/libnemivercommon.la
diff --git a/tests/fooprog.cc b/tests/fooprog.cc
index 62c01d1..f1be621 100644
--- a/tests/fooprog.cc
+++ b/tests/fooprog.cc
@@ -85,9 +85,9 @@ func4 (Person &a_person)
 }
 
 int
-main (int a_argc, char *a_argv[])
+main (int a_argc __attribute__((unused)),
+      char *a_argv[] __attribute__((unused)))
 {
-    if (a_argc || a_argv) {}
     Person person ("Bob", "Barton", 15) ;
     func1 () ;
 
diff --git a/tests/test-gdbmi.cc b/tests/test-gdbmi.cc
index 2561e61..ce2c3cb 100644
--- a/tests/test-gdbmi.cc
+++ b/tests/test-gdbmi.cc
@@ -48,6 +48,11 @@ static const char *gv_output_record2=
 "(gdb)";
 
 static const char *gv_output_record3="^done,thread-ids={thread-id=\"1\"},current-thread-id=\"1\",number-of-threads=\"1\"\n";
+static const char *gv_output_record4="^done,name=\"var1\",numchild=\"1\",value=\"{...}\",type=\"Person\"\n";
+
+static const char *gv_output_record5="^done,ndeleted=\"2\"\n";
+
+static const char *gv_output_record6="^done,numchild=\"3\",children=[child={name=\"var2.public.m_first_name\",exp=\"m_first_name\",numchild=\"2\",type=\"string\"},child={name=\"var2.public.m_family_name\",exp=\"m_family_name\",numchild=\"2\",type=\"string\"},child={name=\"var2.public.m_age\",exp=\"m_age\",numchild=\"0\",type=\"unsigned int\"}]\n";
 
 //the partial result of a gdbmi command: -stack-list-argument 1 command
 //this command is used to implement IDebugger::list_frames_arguments()
@@ -273,6 +278,51 @@ test_output_record ()
     parser.push_input (gv_output_record3);
     is_ok = parser.parse_output_record (0, to, output);
     BOOST_REQUIRE (is_ok) ;
+
+    // gv_output_record4 should result in a variable.
+    parser.push_input (gv_output_record4);
+    is_ok = parser.parse_output_record (0, to, output);
+    BOOST_REQUIRE (is_ok);
+    BOOST_REQUIRE (output.has_result_record ());
+    BOOST_REQUIRE (output.result_record ().has_variable ());
+    BOOST_REQUIRE (output.result_record ().variable ());
+    BOOST_REQUIRE (output.result_record ().variable ()->name ().empty ());
+    BOOST_REQUIRE (output.result_record ().variable ()->internal_name ()
+                   == "var1");
+    BOOST_REQUIRE (output.result_record ().variable ()->type () == "Person");
+
+    // gv_output_record4 should result in 2 deleted variables.
+    parser.push_input (gv_output_record5);
+    is_ok = parser.parse_output_record (0, to, output);
+    BOOST_REQUIRE (is_ok);
+    BOOST_REQUIRE (output.result_record ().number_of_variables_deleted () == 2);
+
+    // gv_output_record5 should result in 3 children variables.
+    parser.push_input (gv_output_record6);
+    is_ok = parser.parse_output_record (0, to, output);
+    BOOST_REQUIRE (is_ok);
+    BOOST_REQUIRE (output.result_record ().has_variable_children ());
+    vector<IDebugger::VariableSafePtr> v =
+            output.result_record ().variable_children ();
+    BOOST_REQUIRE (v.size () == 3);
+
+    int i = 0;
+    BOOST_REQUIRE (v[i]
+                   && v[i]->name () == "m_first_name"
+                   && v[i]->type () == "string"
+                   && v[i]->internal_name () == "var2.public.m_first_name");
+
+    i = 1;
+    BOOST_REQUIRE (v[i]
+                   && v[i]->name () == "m_family_name"
+                   && v[i]->type () == "string"
+                   && v[i]->internal_name () == "var2.public.m_family_name");
+
+    i = 2;
+    BOOST_REQUIRE (v[i]
+                   && v[i]->name () == "m_age"
+                   && v[i]->type () == "unsigned int"
+                   && v[i]->internal_name () == "var2.public.m_age");
 }
 
 void
diff --git a/tests/test-vars.cc b/tests/test-vars.cc
new file mode 100644
index 0000000..c431d71
--- /dev/null
+++ b/tests/test-vars.cc
@@ -0,0 +1,195 @@
+#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-i-var-list-walker.h"
+
+using namespace nemiver ;
+using namespace nemiver::common ;
+
+Glib::RefPtr<Glib::MainLoop> s_loop =
+    Glib::MainLoop::create (Glib::MainContext::get_default ()) ;
+static int nb_vars_created;
+static int nb_vars_created2;
+static int nb_vars_deleted;
+static int nb_vars_deleted2;
+static int unfold_requests;
+static IDebugger::VariableSafePtr var_to_delete;
+
+void on_variable_deleted_signal (const IDebugger::VariableSafePtr a_var);
+void on_variable_unfolded_signal (const IDebugger::VariableSafePtr a_var,
+                                  IDebuggerSafePtr a_debugger);
+
+void
+on_command_done_signal (const UString&, const UString&)
+{
+}
+
+void
+on_engine_died_signal ()
+{
+    s_loop->quit ();
+}
+
+void
+on_program_finished_signal ()
+{
+    s_loop->quit ();
+}
+
+void
+on_running_signal ()
+{
+}
+
+void
+on_variable_created_signal (IDebugger::VariableSafePtr a_var)
+{
+    MESSAGE ("variable created: " << a_var->name ());
+    nb_vars_created++;
+    if (a_var->name () == "person") {
+        // put the variable aside so that we
+        // can delete it later
+        var_to_delete = a_var;
+    }
+}
+
+void
+on_variable_created_signal2 (IDebugger::VariableSafePtr a_var,
+                             const UString &,
+                             IDebuggerSafePtr a_debugger)
+{
+    UString var_str;
+    a_var->to_string (var_str, true /*show var name*/);
+    MESSAGE ("variable created: " << var_str);
+    nb_vars_created2++;
+
+    unfold_requests++;
+    a_debugger->unfold_variable (a_var,
+                                 sigc::bind (&on_variable_unfolded_signal,
+                                             a_debugger));
+}
+
+void
+on_variable_unfolded_signal (const IDebugger::VariableSafePtr a_var,
+                             IDebuggerSafePtr a_debugger)
+{
+    unfold_requests--;
+    UString var_str;
+    a_var->to_string (var_str, true /*show var name*/);
+    MESSAGE ("variable unfolded: " << var_str);
+    typedef list<IDebugger::VariableSafePtr>::const_iterator VarIter;
+    for (VarIter it = a_var->members ().begin ();
+         it != a_var->members ().end ();
+         ++it) {
+        if ((*it)->has_expected_children ()) {
+            unfold_requests++;
+            a_debugger->unfold_variable (*it,
+                                         sigc::bind (&on_variable_unfolded_signal,
+                                                     a_debugger));
+        }
+    }
+    if (unfold_requests == 0 && var_to_delete)
+        a_debugger->delete_variable (var_to_delete,
+                                     &on_variable_deleted_signal);
+}
+
+void
+on_variable_deleted_signal (const IDebugger::VariableSafePtr a_var)
+{
+    MESSAGE ("variable deleted. Name: "
+             << a_var->name ()
+             << ", Internal Name: "
+             << a_var->internal_name ());
+    nb_vars_deleted++;
+}
+
+void
+on_variable_deleted_signal2 (const IDebugger::VariableSafePtr a_var,
+                             const UString&)
+{
+    MESSAGE ("variable deleted. Name: "
+             << a_var->name ()
+             << ", Internal Name: "
+             << a_var->internal_name ());
+    nb_vars_deleted2++;
+}
+
+void
+on_stopped_signal (IDebugger::StopReason /*a_reason*/,
+                   bool,
+                   const IDebugger::Frame&,
+                   int,
+                   int,
+                   const UString&,
+                   IDebuggerSafePtr &debugger)
+{
+    static int nb_stops = 0;
+
+    nb_stops++;
+    MESSAGE ("stopped " << nb_stops << " times");
+
+    if (nb_stops == 2) {
+        // Okay we stepped once, now we can now create the variable
+        // for the person variable.
+        debugger->create_variable ("person",
+                                   &on_variable_created_signal);
+        MESSAGE ("Requested creation of variable 'person'");
+    }
+    debugger->step_over ();
+}
+
+NEMIVER_API int
+test_main (int argc __attribute__((unused)),
+           char **argv __attribute__ ((unused)))
+{
+    NEMIVER_TRY
+
+    Initializer::do_init ();
+
+    //load the IDebugger interface
+    IDebuggerSafePtr debugger =
+        DynamicModuleManager::load_iface_with_default_manager<IDebugger>
+                                                                ("gdbengine",
+                                                                 "IDebugger");
+    //setup the debugger with the glib mainloop
+    debugger->set_event_loop_context (Glib::MainContext::get_default ());
+
+    //*******************************
+    //<connect to IDebugger events>
+    //******************************
+    debugger->command_done_signal ().connect (&on_command_done_signal);
+    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));
+    debugger->variable_created_signal ().connect (sigc::bind
+                                              (&on_variable_created_signal2,
+                                               debugger));
+    debugger->variable_deleted_signal ().connect (&on_variable_deleted_signal2);
+    //*******************************
+    //</connect to IDebugger events>
+    //******************************
+    debugger->load_program ("fooprog", ".");
+    debugger->set_breakpoint ("main");
+    debugger->run ();
+
+    //****************************************
+    //run the event loop.
+    //****************************************
+    s_loop->run ();
+
+    NEMIVER_CATCH_AND_RETURN_NOX (-1)
+
+    BOOST_REQUIRE (nb_vars_created == nb_vars_created2);
+    BOOST_REQUIRE (nb_vars_created > 0);
+    BOOST_REQUIRE (nb_vars_deleted == nb_vars_deleted2);
+    BOOST_REQUIRE (nb_vars_deleted > 0);
+
+    return 0;
+}
+



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]