[nemiver/varobjs-support] Add support to list/assign/evaluate/query variable objects
- From: Dodji Seketeli <dodji src gnome org>
- To: svn-commits-list gnome org
- Subject: [nemiver/varobjs-support] Add support to list/assign/evaluate/query variable objects
- Date: Thu, 26 Mar 2009 11:39:14 -0400 (EDT)
commit 29991a6629cdf2e552cdae9f6f93b3135b1c6403
Author: Dodji Seketeli <dodji redhat com>
Date: Thu Mar 26 16:37:32 2009 +0100
Add support to list/assign/evaluate/query variable objects
* src/dbgengine/nmv-dbg-common.h:
(Output::ResultRecord::has_changed_var_list,
Output::ResultRecord::changed_var_list): New entry points.
* src/dbgengine/nmv-i-debugger.h:
(IDebugger::Variable::parent): Make this return a proper SafePtr
instead of a bare pointer.
(IDebugger::Variable::has_parent,
IDebugger::Variable::root,
IDebugger::Variable::get_descendant,
IDebugger::variable_expression_evaluated_signal,
IDebugger::changed_variable_signals,
IDebugger::assigned_variable_signal,
IDebugger::assign_variable,
IDebugger::evaluate_variable_expr,
IDebugger::list_changed_variables): New entry points.
* src/dbgengine/nmv-gdb-engine.h:
(GDBEngine::variable_expression_evaluated_signal,
GDBEngine::changed_variables_signal,
GDBEngine::assigned_variable_signal): Declare new signal getters.
(GDBEngine::assign_variable, GDBEngine::assign_variable,
GDBEngine::list_changed_variables): Declare new entry points.
* src/dbgengine/nmv-gdb-engine.cc:
(OnStoppedHandler::do_handle): Add more logging.
(OnResultRecordHandler::can_handle,
OnResultRecordHandler::do_handle): Handle replies of assign-variable
and evaluate-expression commands.
(OnListChangedVariableHandler): New output handler for the
list-changed-variables command.
(GDBEngine::init_output_handlers): Setup the
new OnListChangedVariableHandler.
(GDBEngine::variable_expression_evaluated_signal,
GDBEngine::changed_variables_signal,
GDBEngine::assigned_variable_signal): Define new signal getters.
(GDBEngine::unfold_variable): Send the proper command to GDB to make
it list all the values of each variable node.
(GDBEngine::assign_variable,
GDBEngine::evaluate_variable_expr,
GDBEngine::list_changed_variables): Define new entry points.
* src/dbgengine/nmv-gdbmi-parser.h :
(GDBMIParser::parse_var_changed_list): Declare new entry point.
* src/dbgengine/nmv-gdbmi-parser.cc:
(GDBMIParser::parse_var_list_children): Add a comment to describe the
function.
(GDBMIParser::parse_var_changed_list): Define new entry point to parse
the result of GDBEngine::list_changed_variables.
(GDBMIParser::parse_result_record): Use the new
GDBMIParser::parse_var_changed_list to parse the result record of the
call GDBEngine::list_changed_variables.
* tests/test-gdbmi.cc: Add tests for parsing the result of
IDebugger::list_changed_list.
* tests/test-vars.cc: Updated this varobjs test to be able to assign
values to a variable, list/update changed children of a given variable
object, get the value of a variable object.
This entry adds Basically adds support to assign a value to a variable
object, list/update changed children of a given variable object and
get the value of a variable object.
---
src/dbgengine/nmv-dbg-common.h | 24 ++++
src/dbgengine/nmv-gdb-engine.cc | 240 ++++++++++++++++++++++++++++++++++++-
src/dbgengine/nmv-gdb-engine.h | 32 +++++
src/dbgengine/nmv-gdbmi-parser.cc | 123 ++++++++++++++++++-
src/dbgengine/nmv-gdbmi-parser.h | 5 +-
src/dbgengine/nmv-i-debugger.h | 91 ++++++++++++++-
tests/test-gdbmi.cc | 11 ++-
tests/test-vars.cc | 130 ++++++++++++++++++---
8 files changed, 629 insertions(+), 27 deletions(-)
diff --git a/src/dbgengine/nmv-dbg-common.h b/src/dbgengine/nmv-dbg-common.h
index 0ae7ec4..92596a0 100644
--- a/src/dbgengine/nmv-dbg-common.h
+++ b/src/dbgengine/nmv-dbg-common.h
@@ -397,6 +397,11 @@ 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;
+
public:
ResultRecord () {clear () ;}
@@ -434,6 +439,7 @@ public:
m_has_variable = false;
m_nb_variable_deleted = 0;
m_has_variable_children = false;
+ m_has_changed_var_list = false;
}
/// \name accessors
@@ -645,6 +651,24 @@ public:
has_variable_children (true);
}
+ bool has_changed_var_list () const
+ {
+ return m_has_changed_var_list;
+ }
+ void has_changed_var_list (bool a_in)
+ {
+ m_has_changed_var_list = a_in;
+ }
+ const list<IDebugger::VariableSafePtr>& changed_var_list () const
+ {
+ return m_changed_var_list;
+ }
+ void changed_var_list (const list<IDebugger::VariableSafePtr> &a_in)
+ {
+ m_changed_var_list = a_in;
+ has_changed_var_list (true);
+ }
+
/// @}
};//end class ResultRecord
diff --git a/src/dbgengine/nmv-gdb-engine.cc b/src/dbgengine/nmv-gdb-engine.cc
index 18d8862..76f8466 100644
--- a/src/dbgengine/nmv-gdb-engine.cc
+++ b/src/dbgengine/nmv-gdb-engine.cc
@@ -236,6 +236,15 @@ public:
mutable sigc::signal<void, const VariableSafePtr, const UString&>
variable_unfolded_signal;
+ mutable sigc::signal<void, const VariableSafePtr, const UString&>
+ variable_expression_evaluated_signal;
+
+ mutable sigc::signal<void, const list<VariableSafePtr>&, const UString&>
+ changed_variables_signal;
+
+ mutable sigc::signal<void, VariableSafePtr, const UString&>
+ assigned_variable_signal;
+
//***********************
//</GDBEngine attributes>
//************************
@@ -1106,7 +1115,9 @@ struct OnStoppedHandler: OutputHandler {
LOG_FUNCTION_SCOPE_NORMAL_DD;
THROW_IF_FAIL (m_is_stopped && m_engine);
- if (a_in.has_command ()) {}
+ LOG_DD ("stopped. Command name was: '"
+ << a_in.command ().name () << "' "
+ << "Cookie was '" << a_in.command ().cookie () << "'");
int thread_id = m_out_of_band_record.thread_id ();
int breakpoint_number = -1;
@@ -1533,6 +1544,8 @@ struct OnResultRecordHandler : OutputHandler {
m_engine (a_engine)
{}
+ // TODO: split this OutputHandler into several different handlers.
+ // Ideally there should be one handler per command sent to GDB.
bool can_handle (CommandAndOutput &a_in)
{
if ((a_in.command ().name () == "print-variable-value"
@@ -1540,7 +1553,9 @@ struct OnResultRecordHandler : OutputHandler {
|| a_in.command ().name () == "print-pointed-variable-value"
|| a_in.command ().name () == "dereference-variable"
|| a_in.command ().name () == "set-register-value"
- || a_in.command ().name () == "set-memory")
+ || a_in.command ().name () == "set-memory"
+ || a_in.command ().name () == "assign-variable"
+ || a_in.command ().name () == "evaluate-expression")
&& a_in.output ().has_result_record ()
&& (a_in.output ().result_record ().kind ()
== Output::ResultRecord::DONE)
@@ -1598,6 +1613,48 @@ struct OnResultRecordHandler : OutputHandler {
var->set (*a_in.output ().result_record ().variable_value ());
m_engine->variable_value_set_signal ().emit
(var, a_in.command ().cookie ());
+ } else if (a_in.command ().name () == "assign-variable") {
+ THROW_IF_FAIL (a_in.command ().variable ());
+ THROW_IF_FAIL (a_in.output ().result_record ().variable_value ());
+
+ // Set the result we got from gdb into the variable
+ // handed to us by the client code.
+ a_in.command ().variable ()->value
+ (a_in.output ().result_record ().variable_value ()->value ());
+
+ // Call the slot associated to IDebugger::assign_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 ());
+ }
+
+ // Tell the world that the variable got assigned
+ m_engine->assigned_variable_signal ().emit
+ (a_in.command ().variable (), a_in.command ().cookie ());
+ } else if (a_in.command ().name () == "evaluate-expression") {
+ THROW_IF_FAIL (a_in.command ().variable ());
+ THROW_IF_FAIL (a_in.output ().result_record ().variable_value ());
+
+ // Set the result we got from gdb into the variable
+ // handed to us by the client code.
+ a_in.command ().variable ()->value
+ (a_in.output ().result_record ().variable_value ()->value ());
+
+ // Call the slot associated to
+ // IDebugger::evaluate_variable_expr(), 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 ());
+ }
+
+ // Tell the world that the expression got evaluated
+ m_engine->variable_expression_evaluated_signal ().emit
+ (a_in.command ().variable (), a_in.command ().cookie ());
} else if (a_in.command ().name () == "print-pointed-variable-value") {
LOG_DD ("got print-pointed-variable-value");
THROW_IF_FAIL (var_name != "");
@@ -2158,6 +2215,66 @@ struct OnUnfoldVariableHandler : public OutputHandler {
}
};// End struct OnUnfoldVariableHandler
+struct OnListChangedVariableHandler : public OutputHandler {
+ GDBEngine *m_engine;
+
+ OnListChangedVariableHandler (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.output ().result_record ().has_changed_var_list ()
+ && a_in.command ().name () == "list-changed-variables") {
+ LOG_DD ("handler selected");
+ return true;
+ }
+ return false;
+ }
+
+ 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.
+ 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);
+ }
+ }
+ // Call the slot associated to
+ // IDebugger::list_changed_variables(), if any.
+ if (a_in.command ().has_slot ()) {
+ typedef sigc::slot<void,
+ const list<IDebugger::VariableSafePtr>&>
+ SlotType;
+ SlotType slot = a_in.command ().get_slot<SlotType> ();
+ slot (vars);
+ }
+ // Tell the world about the descendant variables that changed.
+ m_engine->changed_variables_signal ().emit
+ (vars, a_in.command ().cookie ());
+ }
+};//end OnListChangedVariableHandler
+
//****************************
//</GDBengine output handlers
//***************************
@@ -2438,6 +2555,8 @@ GDBEngine::init_output_handlers ()
(OutputHandlerSafePtr (new OnDeleteVariableHandler (this)));
m_priv->output_handler_list.add
(OutputHandlerSafePtr (new OnUnfoldVariableHandler (this)));
+ m_priv->output_handler_list.add
+ (OutputHandlerSafePtr (new OnListChangedVariableHandler (this)));
}
sigc::signal<void, Output&>&
@@ -2707,6 +2826,24 @@ GDBEngine::variable_unfolded_signal () const
return m_priv->variable_unfolded_signal;
}
+sigc::signal<void, const IDebugger::VariableSafePtr, const UString&>&
+GDBEngine::variable_expression_evaluated_signal () const
+{
+ return m_priv->variable_expression_evaluated_signal;
+}
+
+sigc::signal<void, const list<IDebugger::VariableSafePtr>&, const UString&>&
+GDBEngine::changed_variables_signal () const
+{
+ return m_priv->changed_variables_signal;
+}
+
+sigc::signal<void, IDebugger::VariableSafePtr, const UString&>&
+GDBEngine::assigned_variable_signal () const
+{
+ return m_priv->assigned_variable_signal;
+}
+
//******************
//<signal handlers>
//******************
@@ -3882,7 +4019,8 @@ GDBEngine::unfold_variable (const VariableSafePtr a_var,
THROW_IF_FAIL (!a_var->internal_name ().empty ());
Command command ("unfold-variable",
- "-var-list-children" + a_var->internal_name (),
+ "-var-list-children --all-values "
+ + a_var->internal_name (),
a_cookie);
command.variable (a_var);
@@ -3911,6 +4049,102 @@ GDBEngine::unfold_variable (const VariableSafePtr a_var,
queue_command (command);
}
+void
+GDBEngine::assign_variable (const VariableSafePtr a_var,
+ const UString &a_expression,
+ const UString &a_cookie)
+{
+ THROW_IF_FAIL (a_var);
+ THROW_IF_FAIL (!a_var->internal_name ().empty ());
+ THROW_IF_FAIL (!a_expression.empty ());
+
+ Command command ("assign-variable",
+ "-var-assign "
+ + a_var->internal_name () + " " + a_expression,
+ a_cookie);
+ command.variable (a_var);
+ queue_command (command);
+
+}
+
+void
+GDBEngine::assign_variable
+ (const VariableSafePtr a_var,
+ const UString &a_expression,
+ const sigc::slot<void, const VariableSafePtr>& a_slot)
+{
+ THROW_IF_FAIL (a_var);
+ THROW_IF_FAIL (!a_var->internal_name ().empty ());
+ THROW_IF_FAIL (!a_expression.empty ());
+
+ Command command ("assign-variable",
+ "-var-assign "
+ + a_var->internal_name () + " " + a_expression);
+ command.variable (a_var);
+ command.set_slot (a_slot);
+ queue_command (command);
+}
+
+void
+GDBEngine::evaluate_variable_expr (const VariableSafePtr a_var,
+ const UString &a_cookie)
+{
+ THROW_IF_FAIL (a_var);
+ THROW_IF_FAIL (!a_var->internal_name ().empty ());
+
+ Command command ("evaluate-expression",
+ "-var-evaluate-expression " + a_var->internal_name (),
+ a_cookie);
+ command.variable (a_var);
+ queue_command (command);
+}
+
+void
+GDBEngine::evaluate_variable_expr
+ (const VariableSafePtr a_var,
+ const sigc::slot<void, const VariableSafePtr> &a_slot)
+{
+ THROW_IF_FAIL (a_var);
+ THROW_IF_FAIL (!a_var->internal_name ().empty ());
+
+ Command command ("evaluate-expression",
+ "-var-evaluate-expression " + a_var->internal_name ());
+ command.variable (a_var);
+ command.set_slot (a_slot);
+ queue_command (command);
+}
+
+void
+GDBEngine::list_changed_variables (VariableSafePtr a_var,
+ const UString &a_cookie)
+{
+ THROW_IF_FAIL (a_var);
+ THROW_IF_FAIL (!a_var->internal_name ().empty ());
+
+ Command command ("list-changed-variables",
+ "-var-update --all-values "
+ + a_var->internal_name (),
+ a_cookie);
+ command.variable (a_var);
+ queue_command (command);
+}
+
+void
+GDBEngine::list_changed_variables
+ (VariableSafePtr a_var,
+ const sigc::slot<void, const list<VariableSafePtr> > &a_slot)
+{
+ THROW_IF_FAIL (a_var);
+ THROW_IF_FAIL (!a_var->internal_name ().empty ());
+
+ Command command ("list-changed-variables",
+ "-var-update --all-values "
+ + 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 aaeda62..9c2dd46 100644
--- a/src/dbgengine/nmv-gdb-engine.h
+++ b/src/dbgengine/nmv-gdb-engine.h
@@ -182,6 +182,16 @@ public:
sigc::signal<void, const VariableSafePtr, const UString&>&
variable_unfolded_signal () const;
+
+ sigc::signal<void, const VariableSafePtr, const UString&>&
+ variable_expression_evaluated_signal () const;
+
+ sigc::signal<void, const list<VariableSafePtr>&, const UString&>&
+ changed_variables_signal () const;
+
+ sigc::signal<void, VariableSafePtr, const UString&>&
+ assigned_variable_signal () const;
+
//*************
//</signals>
//*************
@@ -408,6 +418,28 @@ public:
const UString &a_cookie);
void unfold_variable (VariableSafePtr a_var,
const sigc::slot<void, const VariableSafePtr> &);
+
+ void assign_variable (const VariableSafePtr a_var,
+ const UString &a_expression,
+ const UString &a_cookie);
+
+ void assign_variable
+ (const VariableSafePtr a_var,
+ const UString &a_expression,
+ const sigc::slot<void, const VariableSafePtr>& a_slot);
+
+ void evaluate_variable_expr (const VariableSafePtr a_var,
+ const UString &a_cookie);
+ void evaluate_variable_expr
+ (const VariableSafePtr a_var,
+ const sigc::slot<void, const VariableSafePtr> &a_slot);
+
+ void list_changed_variables (VariableSafePtr a_root,
+ const UString &a_cookie);
+
+ void list_changed_variables
+ (VariableSafePtr a_root,
+ const sigc::slot<void, const list<VariableSafePtr> > &a_slot);
};//end class GDBEngine
NEMIVER_END_NAMESPACE (nemiver)
diff --git a/src/dbgengine/nmv-gdbmi-parser.cc b/src/dbgengine/nmv-gdbmi-parser.cc
index 83404dc..704e76a 100644
--- a/src/dbgengine/nmv-gdbmi-parser.cc
+++ b/src/dbgengine/nmv-gdbmi-parser.cc
@@ -144,8 +144,10 @@ 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";
+const char* PREFIX_NUMCHILD = "numchild=\"";
+const char* NUMCHILD = "numchild";
+const char* PREFIX_VARIABLES_CHANGED_LIST = "changelist=[";
+const char* CHANGELIST = "changelist";
bool parse_c_string_body (const UString &a_input,
UString::size_type a_from,
@@ -4837,6 +4839,15 @@ fetch_gdbmi_result:
} else {
LOG_PARSING_ERROR2 (cur);
}
+ } 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);
+ } else {
+ LOG_PARSING_ERROR2 (cur);
+ }
} else {
GDBMIResultSafePtr result;
if (!parse_gdbmi_result (cur, cur, result)
@@ -6074,6 +6085,10 @@ GDBMIParser::parse_variables_deleted (UString::size_type a_from,
return true;
}
+// We want to parse something like:
+// 'numchild=N,children=[{name=NAME,numchild=N,type=TYPE}]'
+// It's actually two RESULTs, separated by a comma. The second
+// result is a LIST of TUPLEs. Each TUPLE represents a children variable.
bool
GDBMIParser::parse_var_list_children
(UString::size_type a_from,
@@ -6084,7 +6099,6 @@ GDBMIParser::parse_var_list_children
UString::size_type cur = a_from;
CHECK_END2 (cur);
-
if (RAW_INPUT.compare (cur, strlen (PREFIX_NUMCHILD),
PREFIX_NUMCHILD)) {
LOG_PARSING_ERROR2 (cur);
@@ -6247,6 +6261,109 @@ 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)
+{
+ 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 ()) {
+ LOG_ERROR ("expected a LIST value for the GDBMI variable "
+ << CHANGELIST);
+ return false;
+ }
+
+ if (result->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);
+ if (values.empty ()) {
+ LOG_ERROR ("expected a non empty TUPLE content in the LIST value of "
+ << CHANGELIST);
+ return false;
+ }
+ for (list<GDBMIValueSafePtr>::const_iterator value_it = values.begin ();
+ value_it != values.end ();
+ ++value_it) {
+ if (!(*value_it)) {
+ continue;
+ }
+ if ((*value_it)->content_type () != GDBMIValue::TUPLE_TYPE
+ || !(*value_it)->get_tuple_content ()) {
+ LOG_ERROR ("expected a non empty TUPLE content in the LIST value of "
+ << CHANGELIST);
+ return false;
+ }
+ // 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 = false, type_changed = false;
+ IDebugger::VariableSafePtr var;
+ // 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) {
+ 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 (!internal_name.empty ()) {
+ var = IDebugger::VariableSafePtr
+ (new IDebugger::Variable (internal_name,
+ "" /* name */,
+ value,
+ "" /* type */));
+ 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,
diff --git a/src/dbgengine/nmv-gdbmi-parser.h b/src/dbgengine/nmv-gdbmi-parser.h
index 01afbe6..e8c817f 100644
--- a/src/dbgengine/nmv-gdbmi-parser.h
+++ b/src/dbgengine/nmv-gdbmi-parser.h
@@ -795,11 +795,14 @@ public:
UString::size_type &a_to,
vector<IDebugger::VariableSafePtr> &a_vars);
+ bool parse_var_changed_list (UString::size_type a_from,
+ UString::size_type &a_to,
+ list<IDebugger::VariableSafePtr> &a_vars);
+
bool parse_result_record (UString::size_type a_from,
UString::size_type &a_to,
Output::ResultRecord &a_record);
-
bool parse_output_record (UString::size_type a_from,
UString::size_type &a_to,
Output &a_output);
diff --git a/src/dbgengine/nmv-i-debugger.h b/src/dbgengine/nmv-i-debugger.h
index ff56f93..a3bd89c 100644
--- a/src/dbgengine/nmv-i-debugger.h
+++ b/src/dbgengine/nmv-i-debugger.h
@@ -357,12 +357,41 @@ public:
void type (const UString &a_type) {m_type = a_type;}
void type (const string &a_type) {m_type = a_type;}
- Variable* parent () const {return m_parent;}
+ /// Return true if this instance of Variable has a parent variable,
+ /// false otherwise.
+ bool has_parent () const
+ {
+ return m_parent != 0;
+ }
+
+ /// A getter of the parent Variable of the current instance.
+ const VariableSafePtr parent () const
+ {
+ VariableSafePtr parent (m_parent, true/*add a reference*/);
+ return parent;
+ }
+
+ /// Parent variable setter.
+ /// We don't increase the ref count of the
+ /// parent variable to avoid memory leaks due to refcounting cyles.
+ /// The parent already holds a reference on this instance of
+ /// Variable so we really should not hold a reference on it as
+ /// well.
void parent (Variable *a_parent)
{
m_parent = a_parent;
}
+ /// \return the root parent of this variable.
+ /// Beware what is returned here is a bare pointer.
+ const VariableSafePtr root () const
+ {
+ if (!has_parent ()) {
+ return VariableSafePtr (this, true /*increase refcount*/);
+ }
+ return parent ()->root ();
+ }
+
void to_string (UString &a_str,
bool a_show_var_name = false,
const UString &a_indent_str="") const
@@ -522,6 +551,31 @@ public:
{
return m_num_expected_children != 0;
}
+
+ /// Return the descendant of the current instance of Variable.
+ /// \param a_internal_path the internal fully qualified path of the
+ /// descendant variable.
+ const VariableSafePtr get_descendant
+ (const UString &a_internal_path) const
+ {
+ VariableSafePtr result;
+ if (internal_name () == a_internal_path) {
+ result.reset (this, true /*take refcount*/);
+ return result;
+ }
+ for (list<VariableSafePtr>::const_iterator it = m_members.begin ();
+ it != m_members.end ();
+ ++it) {
+ if (*it && (*it)->internal_name () == a_internal_path) {
+ return *it;
+ }
+ result = (*it)->get_descendant (a_internal_path);
+ if (result) {
+ return result;
+ }
+ }
+ return result;
+ }
};//end class Variable
enum State {
@@ -744,6 +798,15 @@ public:
virtual sigc::signal<void, const VariableSafePtr, const UString&>&
variable_unfolded_signal () const = 0;
+
+ virtual sigc::signal<void, const VariableSafePtr, const UString&>&
+ variable_expression_evaluated_signal () const = 0;
+
+ virtual sigc::signal<void, const list<VariableSafePtr>&, const UString&>&
+ changed_variables_signal () const = 0;
+
+ virtual sigc::signal<void, VariableSafePtr, const UString&>&
+ assigned_variable_signal () const = 0;
/// @}
virtual void do_init (IConfMgrSafePtr &a_conf_mgr) = 0;
@@ -920,8 +983,30 @@ public:
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;
+ virtual void unfold_variable
+ (VariableSafePtr a_var,
+ const sigc::slot<void, const VariableSafePtr> &) = 0;
+
+ virtual void assign_variable (const VariableSafePtr a_var,
+ const UString &a_expression,
+ const UString &a_cookie) = 0;
+
+ virtual void assign_variable
+ (const VariableSafePtr a_var,
+ const UString &a_expression,
+ const sigc::slot<void, const VariableSafePtr>& a_slot) = 0;
+
+ virtual void evaluate_variable_expr (const VariableSafePtr a_var,
+ const UString &a_cookie) = 0;
+ virtual void evaluate_variable_expr
+ (const VariableSafePtr a_var,
+ const sigc::slot<void, const VariableSafePtr> &a_slot)= 0;
+
+ virtual void list_changed_variables (VariableSafePtr a_root,
+ const UString &a_cookie) = 0;
+ virtual void list_changed_variables
+ (VariableSafePtr a_root,
+ const sigc::slot<void, const list<VariableSafePtr> > &a_slot) = 0;
};//end IDebugger
NEMIVER_END_NAMESPACE (nemiver)
diff --git a/tests/test-gdbmi.cc b/tests/test-gdbmi.cc
index ce2c3cb..e0ca024 100644
--- a/tests/test-gdbmi.cc
+++ b/tests/test-gdbmi.cc
@@ -54,6 +54,8 @@ 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";
+static const char *gv_output_record7="^done,changelist=[{name=\"var1.public.m_first_name.public.npos\",value=\"1\",in_scope=\"true\",type_changed=\"false\"}]\n";
+
//the partial result of a gdbmi command: -stack-list-argument 1 command
//this command is used to implement IDebugger::list_frames_arguments()
static const char* gv_stack_arguments0 =
@@ -291,13 +293,13 @@ test_output_record ()
== "var1");
BOOST_REQUIRE (output.result_record ().variable ()->type () == "Person");
- // gv_output_record4 should result in 2 deleted variables.
+ // gv_output_record5 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.
+ // gv_output_record6 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);
@@ -323,6 +325,11 @@ 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 ());
}
void
diff --git a/tests/test-vars.cc b/tests/test-vars.cc
index c431d71..e71cb3b 100644
--- a/tests/test-vars.cc
+++ b/tests/test-vars.cc
@@ -22,6 +22,13 @@ 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_changed_variables_listed_signal
+ (const list<IDebugger::VariableSafePtr> &a_vars,
+ IDebuggerSafePtr a_debugger);
+void on_assign_variable_signal (const IDebugger::VariableSafePtr a_var,
+ IDebuggerSafePtr a_debugger);
+void on_evaluate_variable_expr_signal (const IDebugger::VariableSafePtr a_var,
+ IDebuggerSafePtr a_debugger);
void
on_command_done_signal (const UString&, const UString&)
@@ -48,7 +55,10 @@ on_running_signal ()
void
on_variable_created_signal (IDebugger::VariableSafePtr a_var)
{
- MESSAGE ("variable created: " << a_var->name ());
+ UString var_str;
+ a_var->to_string (var_str, true /*show var name*/);
+ MESSAGE ("variable created " << var_str);
+
nb_vars_created++;
if (a_var->name () == "person") {
// put the variable aside so that we
@@ -64,7 +74,7 @@ on_variable_created_signal2 (IDebugger::VariableSafePtr a_var,
{
UString var_str;
a_var->to_string (var_str, true /*show var name*/);
- MESSAGE ("variable created: " << var_str);
+ MESSAGE ("variable created (second report): " << var_str);
nb_vars_created2++;
unfold_requests++;
@@ -74,6 +84,45 @@ on_variable_created_signal2 (IDebugger::VariableSafePtr a_var,
}
void
+on_variable_created_in_func4_signal (IDebugger::VariableSafePtr a_var,
+ IDebuggerSafePtr a_debugger)
+{
+ BOOST_REQUIRE (a_var->name () == "i");
+ MESSAGE ("variable " << a_var->name () << " created");
+ nb_vars_created++;
+ a_debugger->assign_variable (a_var, "3456",
+ sigc::bind (&on_assign_variable_signal,
+ a_debugger));
+}
+
+void
+on_assign_variable_signal (const IDebugger::VariableSafePtr a_var,
+ IDebuggerSafePtr a_debugger)
+{
+ BOOST_REQUIRE (a_var);
+ BOOST_REQUIRE (a_var->name () == "i");
+ BOOST_REQUIRE (a_var->value () == "3456");
+ MESSAGE (a_var->name () << " assigned to: " + a_var->value ());
+
+ a_debugger->evaluate_variable_expr
+ (a_var,
+ sigc::bind (&on_evaluate_variable_expr_signal,
+ a_debugger));
+}
+
+void
+on_evaluate_variable_expr_signal (const IDebugger::VariableSafePtr a_var,
+ IDebuggerSafePtr a_debugger)
+{
+ BOOST_REQUIRE (a_var);
+ BOOST_REQUIRE (a_var->name () == "i");
+ BOOST_REQUIRE (a_var->value () == "3456");
+ MESSAGE (a_var->name () << " evaluated to: " + a_var->value ());
+
+ a_debugger->do_continue ();
+}
+
+void
on_variable_unfolded_signal (const IDebugger::VariableSafePtr a_var,
IDebuggerSafePtr a_debugger)
{
@@ -92,9 +141,27 @@ on_variable_unfolded_signal (const IDebugger::VariableSafePtr a_var,
a_debugger));
}
}
- if (unfold_requests == 0 && var_to_delete)
- a_debugger->delete_variable (var_to_delete,
- &on_variable_deleted_signal);
+ if (unfold_requests == 0)
+ a_debugger->step_over ();
+}
+
+void
+on_changed_variables_listed_signal
+ (const list<IDebugger::VariableSafePtr> &a_vars,
+ IDebuggerSafePtr a_debugger)
+{
+ IDebugger::VariableSafePtr root = a_vars.front ()->root ();
+ THROW_IF_FAIL (root);
+ int nb_elems = a_vars.size ();
+ BOOST_REQUIRE (nb_elems == 3);
+ MESSAGE ("The changed members are: ");
+ for (list<IDebugger::VariableSafePtr>::const_iterator it = a_vars.begin ();
+ it != a_vars.end ();
+ ++it) {
+ MESSAGE ("name: " + (*it)->internal_name ());
+ MESSAGE ("value: " + (*it)->value ());
+ }
+ a_debugger->delete_variable (root, &on_variable_deleted_signal);
}
void
@@ -109,37 +176,65 @@ on_variable_deleted_signal (const IDebugger::VariableSafePtr a_var)
void
on_variable_deleted_signal2 (const IDebugger::VariableSafePtr a_var,
- const UString&)
+ const UString&,
+ IDebuggerSafePtr a_debugger)
{
MESSAGE ("variable deleted. Name: "
<< a_var->name ()
<< ", Internal Name: "
<< a_var->internal_name ());
nb_vars_deleted2++;
+ a_debugger->step_over ();
}
void
on_stopped_signal (IDebugger::StopReason /*a_reason*/,
- bool,
- const IDebugger::Frame&,
+ bool a_has_frame,
+ const IDebugger::Frame &a_frame,
int,
int,
- const UString&,
- IDebuggerSafePtr &debugger)
+ const UString &a_cookie,
+ IDebuggerSafePtr &a_debugger)
{
static int nb_stops = 0;
nb_stops++;
MESSAGE ("stopped " << nb_stops << " times");
+ if (a_has_frame) {
+ MESSAGE ("frame name: " << a_frame.function_name ());
+ }
- if (nb_stops == 2) {
+ if (a_has_frame && a_frame.function_name () == "func4") {
+ static int nb_stops_in_func4;
+ nb_stops_in_func4++;
+ MESSAGE ("stopped in func4 " << nb_stops_in_func4 << " times");
+ if (nb_stops_in_func4 == 3) {
+ a_debugger->create_variable
+ ("i",
+ sigc::bind (&on_variable_created_in_func4_signal,
+ a_debugger));
+ } else if (nb_stops_in_func4 < 5 ) {
+ a_debugger->step_over ("in-func4");
+ } else {
+ a_debugger->do_continue ();
+ }
+ } else 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);
+ a_debugger->create_variable ("person",
+ &on_variable_created_signal);
MESSAGE ("Requested creation of variable 'person'");
+ } else if (nb_stops == 6) {
+ // We passed the point where we changed the value of the members
+ // of the 'person' variale, in fooprog.
+ // let's now ask the debugger to tell us which descendant variable
+ // was changed exactly.
+ a_debugger->list_changed_variables
+ (var_to_delete,
+ sigc::bind (&on_changed_variables_listed_signal, a_debugger));
+ } else {
+ a_debugger->step_over ();
}
- debugger->step_over ();
}
NEMIVER_API int
@@ -170,12 +265,17 @@ test_main (int argc __attribute__((unused)),
debugger->variable_created_signal ().connect (sigc::bind
(&on_variable_created_signal2,
debugger));
- debugger->variable_deleted_signal ().connect (&on_variable_deleted_signal2);
+ debugger->variable_deleted_signal ().connect
+ (sigc::bind (&on_variable_deleted_signal2, debugger));
//*******************************
//</connect to IDebugger events>
//******************************
debugger->load_program ("fooprog", ".");
debugger->set_breakpoint ("main");
+ debugger->set_breakpoint ("func4");
+ // Set a breakpoint right after the person variable members get
+ // modified
+ debugger->set_breakpoint ("fooprog.cc", 98);
debugger->run ();
//****************************************
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]