From b6313243984b74ee6772dd8273b0e6073ad1815b Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Thu, 28 May 2009 01:09:20 +0000 Subject: [PATCH] gdb 2009-05-27 Vladimir Prus Tom Tromey Thiago Jung Bauermann * mi/mi-main.c (mi_cmd_list_features): List "python" feature. * varobj.h (varobj_set_visualizer): Declare. (varobj_get_display_hint): Likewise. (varobj_update_result_t) : New fields. * mi/mi-cmds.c (mi_cmds): Add var-set-visualizer. * mi/mi-cmds.h (mi_cmd_var_set_visualizer, mi_cmd_var_set_child_range): Declare. * mi/mi-cmd-var.c (mi_cmd_var_set_visualizer): New function. (mi_cmd_var_list_children): Emit display hint. (varobj_update_one): Emit display hint. Handle dynamic children. * python/python.c (GdbMethods): Add "default_visualizer". * python/python-internal.h (apply_varobj_pretty_printer, gdbpy_get_varobj_pretty_printer, gdbpy_get_display_hint): Declare. (gdbpy_default_visualizer): Likewise. * varobj.c: Include python.h, python-internal.h. (PyObject): New typedef. (struct varobj) : New fields. (varobj_create): Call install_default_visualizer. (instantiate_pretty_printer): New function. (varobj_set_display_format): Update. (varobj_get_display_hint): New function. (update_dynamic_varobj_children): New function. (varobj_get_num_children): Handle dynamic children. (varobj_list_children): Likewise. (install_new_value): Likewise. (varobj_add_child): New function. (install_visualizer): Likewise. (install_default_visualizer): Likewise. (varobj_set_visualizer): Likewise. (varobj_update): Handle dynamic children. (create_child): Use create_child_with_value. (create_child_with_value): New function. (value_get_print_value): Call pretty printer. Add value_formatter argument. (c_value_of_variable): Update. (varobj_invalidate): Always free all_rootvarobj. * python/python-prettyprint.c (apply_varobj_pretty_printer): New function. (gdbpy_get_varobj_pretty_printer): Likewise. (gdbpy_default_visualizer): Likewise. gdb/doc 2009-05-27 Tom Tromey * gdb.texinfo (GDB/MI Miscellaneous Commands): Document "python" feature. (GDB/MI Variable Objects): Document -var-set-visualizer. gdb/testsuite 2009-05-27 Tom Tromey Thiago Jung Bauermann * lib/mi-support.exp (mi_varobj_update_dynamic): New proc. (mi_child_regexp): Likewise. (mi_list_varobj_children_range): Likewise. (mi_get_features): Likewise. (mi_list_varobj_children): Rewrite. * gdb.python/python-mi.exp: New file. --- gdb/ChangeLog | 47 +++ gdb/doc/ChangeLog | 12 + gdb/doc/gdb.texinfo | 62 +++ gdb/mi/mi-cmd-var.c | 53 +++ gdb/mi/mi-cmds.c | 1 + gdb/mi/mi-cmds.h | 1 + gdb/mi/mi-main.c | 4 + gdb/python/python-internal.h | 8 + gdb/python/python-prettyprint.c | 74 ++++ gdb/python/python.c | 3 + gdb/testsuite/ChangeLog | 10 + gdb/testsuite/gdb.python/python-mi.exp | 99 +++++ gdb/testsuite/lib/mi-support.exp | 105 +++-- gdb/varobj.c | 534 +++++++++++++++++++++++-- gdb/varobj.h | 9 + 15 files changed, 961 insertions(+), 61 deletions(-) create mode 100644 gdb/testsuite/gdb.python/python-mi.exp diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 8365765e31..fec8eb2b3f 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,50 @@ +2009-05-27 Vladimir Prus + Tom Tromey + Thiago Jung Bauermann + + * mi/mi-main.c (mi_cmd_list_features): List "python" feature. + * varobj.h (varobj_set_visualizer): Declare. + (varobj_get_display_hint): Likewise. + (varobj_update_result_t) : New + fields. + * mi/mi-cmds.c (mi_cmds): Add var-set-visualizer. + * mi/mi-cmds.h (mi_cmd_var_set_visualizer, + mi_cmd_var_set_child_range): Declare. + * mi/mi-cmd-var.c (mi_cmd_var_set_visualizer): New function. + (mi_cmd_var_list_children): Emit display hint. + (varobj_update_one): Emit display hint. Handle dynamic children. + * python/python.c (GdbMethods): Add "default_visualizer". + * python/python-internal.h (apply_varobj_pretty_printer, + gdbpy_get_varobj_pretty_printer, gdbpy_get_display_hint): + Declare. + (gdbpy_default_visualizer): Likewise. + * varobj.c: Include python.h, python-internal.h. + (PyObject): New typedef. + (struct varobj) : New fields. + (varobj_create): Call install_default_visualizer. + (instantiate_pretty_printer): New function. + (varobj_set_display_format): Update. + (varobj_get_display_hint): New function. + (update_dynamic_varobj_children): New function. + (varobj_get_num_children): Handle dynamic children. + (varobj_list_children): Likewise. + (install_new_value): Likewise. + (varobj_add_child): New function. + (install_visualizer): Likewise. + (install_default_visualizer): Likewise. + (varobj_set_visualizer): Likewise. + (varobj_update): Handle dynamic children. + (create_child): Use create_child_with_value. + (create_child_with_value): New function. + (value_get_print_value): Call pretty printer. Add value_formatter + argument. + (c_value_of_variable): Update. + (varobj_invalidate): Always free all_rootvarobj. + * python/python-prettyprint.c (apply_varobj_pretty_printer): New + function. + (gdbpy_get_varobj_pretty_printer): Likewise. + (gdbpy_default_visualizer): Likewise. + 2009-05-27 Tom Tromey Thiago Jung Bauermann Phil Muldoon diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index 2e28f37a8e..95c9e6620d 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,15 @@ +2009-05-27 Tom Tromey + + * gdb.texinfo (GDB/MI Miscellaneous Commands): Document "python" + feature. + (GDB/MI Variable Objects): Document -var-set-visualizer. + +2009-04-02 Tom Tromey + + * gdb.texinfo (GDB/MI Miscellaneous Commands): Document "python" + feature. + (GDB/MI Variable Objects): Document -var-set-visualizer. + 2009-05-27 Tom Tromey Thiago Jung Bauermann Phil Muldoon diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 45e580cd2b..971ed041aa 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -23442,6 +23442,64 @@ Unfreezing a variable does not update it, only subsequent (gdb) @end smallexample +@subheading The @code{-var-set-visualizer} command +@findex -var-set-visualizer +@anchor{-var-set-visualizer} + +@subsubheading Synopsis + +@smallexample + -var-set-visualizer @var{name} @var{visualizer} +@end smallexample + +Set a visualizer for the variable object @var{name}. + +@var{visualizer} is the visualizer to use. The special value +@samp{None} means to disable any visualizer in use. + +If not @samp{None}, @var{visualizer} must be a Python expression. +This expression must evaluate to a callable object which accepts a +single argument. @value{GDBN} will call this object with the value of +the varobj @var{name} as an argument (this is done so that the same +Python pretty-printing code can be used for both the CLI and MI). +When called, this object must return an object which conforms to the +pretty-printing interface (@pxref{Pretty Printing}). + +The pre-defined function @code{gdb.default_visualizer} may be used to +select a visualizer by following the built-in process +(@pxref{Selecting Pretty-Printers}). This is done automatically when +a varobj is created, and so ordinarily is not needed. + +This feature is only available if Python support is enabled. The MI +command @code{-list-features} (@pxref{GDB/MI Miscellaneous Commands}) +can be used to check this. + +@subsubheading Example + +Resetting the visualizer: + +@smallexample +(gdb) +-var-set-visualizer V None +^done +@end smallexample + +Reselecting the default (type-based) visualizer: + +@smallexample +(gdb) +-var-set-visualizer V gdb.default_visualizer +^done +@end smallexample + +Suppose @code{SomeClass} is a visualizer class. A lambda expression +can be used to instantiate this class for a varobj: + +@smallexample +(gdb) +-var-set-visualizer V "lambda val: SomeClass()" +^done +@end smallexample @c %%%%%%%%%%%%%%%%%%%%%%%%%%%% SECTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @node GDB/MI Data Manipulation @@ -25001,6 +25059,10 @@ as possible presense of the @code{frozen} field in the output of @code{-varobj-create}. @item pending-breakpoints Indicates presence of the @option{-f} option to the @code{-break-insert} command. +@item python +Indicates presence of Python scripting support, Python-based +pretty-printing commands, and possible presence of the +@samp{display_hint} field in the output of @code{-var-list-children} @item thread-info Indicates presence of the @code{-thread-info} command. diff --git a/gdb/mi/mi-cmd-var.c b/gdb/mi/mi-cmd-var.c index 9de8d3d005..cad4117173 100644 --- a/gdb/mi/mi-cmd-var.c +++ b/gdb/mi/mi-cmd-var.c @@ -242,6 +242,22 @@ mi_cmd_var_set_format (char *command, char **argv, int argc) ui_out_field_string (uiout, "value", varobj_get_value (var)); } +void +mi_cmd_var_set_visualizer (char *command, char **argv, int argc) +{ + struct varobj *var; + + if (argc != 2) + error ("Usage: NAME VISUALIZER_FUNCTION."); + + var = varobj_get_handle (argv[0]); + + if (var == NULL) + error ("Variable object not found"); + + varobj_set_visualizer (var, argv[1]); +} + void mi_cmd_var_set_frozen (char *command, char **argv, int argc) { @@ -357,6 +373,7 @@ mi_cmd_var_list_children (char *command, char **argv, int argc) int numchild; enum print_values print_values; int ix; + char *display_hint; if (argc != 1 && argc != 2) error (_("mi_cmd_var_list_children: Usage: [PRINT_VALUES] NAME")); @@ -374,6 +391,13 @@ mi_cmd_var_list_children (char *command, char **argv, int argc) else print_values = PRINT_NO_VALUES; + display_hint = varobj_get_display_hint (var); + if (display_hint) + { + ui_out_field_string (uiout, "displayhint", display_hint); + xfree (display_hint); + } + if (VEC_length (varobj_p, children) == 0) return; @@ -634,6 +658,8 @@ varobj_update_one (struct varobj *var, enum print_values print_values, for (i = 0; VEC_iterate (varobj_update_result, changes, i, r); ++i) { + char *display_hint; + if (mi_version (uiout) > 1) cleanup = make_cleanup_ui_out_tuple_begin_end (uiout, NULL); ui_out_field_string (uiout, "name", varobj_get_objname (r->varobj)); @@ -667,6 +693,33 @@ varobj_update_one (struct varobj *var, enum print_values print_values, ui_out_field_int (uiout, "new_num_children", varobj_get_num_children (r->varobj)); } + + display_hint = varobj_get_display_hint (var); + if (display_hint) + { + ui_out_field_string (uiout, "displayhint", display_hint); + xfree (display_hint); + } + + if (r->children_changed) + { + int ix; + struct varobj *child; + struct cleanup *cleanup = + make_cleanup_ui_out_list_begin_end (uiout, "children"); + + VEC (varobj_p)* children = varobj_list_children (r->varobj); + + for (ix = 0; VEC_iterate (varobj_p, children, ix, child); ++ix) + { + struct cleanup *cleanup_child; + cleanup_child = make_cleanup_ui_out_tuple_begin_end (uiout, NULL); + print_varobj (child, print_values, 1 /* print expression */); + do_cleanups (cleanup_child); + } + + do_cleanups (cleanup); + } if (mi_version (uiout) > 1) do_cleanups (cleanup); diff --git a/gdb/mi/mi-cmds.c b/gdb/mi/mi-cmds.c index df8f74a107..01f9a4bcbb 100644 --- a/gdb/mi/mi-cmds.c +++ b/gdb/mi/mi-cmds.c @@ -163,6 +163,7 @@ struct mi_cmd mi_cmds[] = { "var-list-children", { NULL, 0 }, mi_cmd_var_list_children}, { "var-set-format", { NULL, 0 }, mi_cmd_var_set_format}, { "var-set-frozen", { NULL, 0 }, mi_cmd_var_set_frozen}, + { "var-set-visualizer", { NULL, 0 }, mi_cmd_var_set_visualizer}, { "var-show-attributes", { NULL, 0 }, mi_cmd_var_show_attributes}, { "var-show-format", { NULL, 0 }, mi_cmd_var_show_format}, { "var-update", { NULL, 0 }, mi_cmd_var_update}, diff --git a/gdb/mi/mi-cmds.h b/gdb/mi/mi-cmds.h index 634aac1594..afcba1e4b4 100644 --- a/gdb/mi/mi-cmds.h +++ b/gdb/mi/mi-cmds.h @@ -94,6 +94,7 @@ extern mi_cmd_argv_ftype mi_cmd_var_info_type; extern mi_cmd_argv_ftype mi_cmd_var_list_children; extern mi_cmd_argv_ftype mi_cmd_var_set_format; extern mi_cmd_argv_ftype mi_cmd_var_set_frozen; +extern mi_cmd_argv_ftype mi_cmd_var_set_visualizer; extern mi_cmd_argv_ftype mi_cmd_var_show_attributes; extern mi_cmd_argv_ftype mi_cmd_var_show_format; extern mi_cmd_argv_ftype mi_cmd_var_update; diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c index 0bf2b84850..9a064c17bf 100644 --- a/gdb/mi/mi-main.c +++ b/gdb/mi/mi-main.c @@ -1113,6 +1113,10 @@ mi_cmd_list_features (char *command, char **argv, int argc) ui_out_field_string (uiout, NULL, "pending-breakpoints"); ui_out_field_string (uiout, NULL, "thread-info"); +#if HAVE_PYTHON + ui_out_field_string (uiout, NULL, "python"); +#endif + do_cleanups (cleanup); return; } diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h index 983d24d098..35d38706e1 100644 --- a/gdb/python/python-internal.h +++ b/gdb/python/python-internal.h @@ -111,6 +111,14 @@ char *python_string_to_host_string (PyObject *obj); PyObject *target_string_to_unicode (const gdb_byte *str, int length); int gdbpy_is_string (PyObject *obj); +/* Note that these are declared here, and not in python.h with the + other pretty-printer functions, because they refer to PyObject. */ +char *apply_varobj_pretty_printer (PyObject *print_obj, + struct value **replacement); +PyObject *gdbpy_get_varobj_pretty_printer (struct value *value); +char *gdbpy_get_display_hint (PyObject *printer); +PyObject *gdbpy_default_visualizer (PyObject *self, PyObject *args); + extern PyObject *gdbpy_doc_cst; extern PyObject *gdbpy_children_cst; extern PyObject *gdbpy_to_string_cst; diff --git a/gdb/python/python-prettyprint.c b/gdb/python/python-prettyprint.c index 5be4f360d4..6e17f9ad70 100644 --- a/gdb/python/python-prettyprint.c +++ b/gdb/python/python-prettyprint.c @@ -508,6 +508,80 @@ apply_val_pretty_printer (struct type *type, const gdb_byte *valaddr, return result; } +/* Apply a pretty-printer for the varobj code. PRINTER_OBJ is the + print object. It must have a 'to_string' method (but this is + checked by varobj, not here) which takes no arguments and + returns a string. This function returns an xmalloc()d string if + the printer returns a string. The printer may return a replacement + value instead; in this case *REPLACEMENT is set to the replacement + value, and this function returns NULL. On error, *REPLACEMENT is + set to NULL and this function also returns NULL. */ +char * +apply_varobj_pretty_printer (PyObject *printer_obj, + struct value **replacement) +{ + char *result; + PyGILState_STATE state = PyGILState_Ensure (); + + *replacement = NULL; + result = pretty_print_one_value (printer_obj, replacement); + if (result == NULL); + gdbpy_print_stack (); + PyGILState_Release (state); + + return result; +} + +/* Find a pretty-printer object for the varobj module. Returns a new + reference to the object if successful; returns NULL if not. VALUE + is the value for which a printer tests to determine if it + can pretty-print the value. */ +PyObject * +gdbpy_get_varobj_pretty_printer (struct value *value) +{ + PyObject *val_obj; + PyObject *pretty_printer = NULL; + volatile struct gdb_exception except; + + TRY_CATCH (except, RETURN_MASK_ALL) + { + value = value_copy (value); + } + GDB_PY_HANDLE_EXCEPTION (except); + + val_obj = value_to_value_object (value); + if (! val_obj) + return NULL; + + pretty_printer = find_pretty_printer (val_obj); + Py_DECREF (val_obj); + return pretty_printer; +} + +/* A Python function which wraps find_pretty_printer and instantiates + the resulting class. This accepts a Value argument and returns a + pretty printer instance, or None. This function is useful as an + argument to the MI command -var-set-visualizer. */ +PyObject * +gdbpy_default_visualizer (PyObject *self, PyObject *args) +{ + PyObject *val_obj; + PyObject *cons, *printer = NULL; + struct value *value; + + if (! PyArg_ParseTuple (args, "O", &val_obj)) + return NULL; + value = value_object_to_value (val_obj); + if (! value) + { + PyErr_SetString (PyExc_TypeError, "argument must be a gdb.Value"); + return NULL; + } + + cons = find_pretty_printer (val_obj); + return cons; +} + #else /* HAVE_PYTHON */ int diff --git a/gdb/python/python.c b/gdb/python/python.c index 19098cceba..7f32d665e8 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -615,6 +615,9 @@ static PyMethodDef GdbMethods[] = { "get_parameter", get_parameter, METH_VARARGS, "Return a gdb parameter's value" }, + { "default_visualizer", gdbpy_default_visualizer, METH_VARARGS, + "Find the default visualizer for a Value." }, + { "current_objfile", gdbpy_get_current_objfile, METH_NOARGS, "Return the current Objfile being loaded, or None." }, { "objfiles", gdbpy_objfiles, METH_NOARGS, diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index f1b04641bc..c4b0fab23a 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,13 @@ +2009-05-27 Tom Tromey + Thiago Jung Bauermann + + * lib/mi-support.exp (mi_varobj_update_dynamic): New proc. + (mi_child_regexp): Likewise. + (mi_list_varobj_children_range): Likewise. + (mi_get_features): Likewise. + (mi_list_varobj_children): Rewrite. + * gdb.python/python-mi.exp: New file. + 2009-05-27 Tom Tromey Thiago Jung Bauermann Phil Muldoon diff --git a/gdb/testsuite/gdb.python/python-mi.exp b/gdb/testsuite/gdb.python/python-mi.exp new file mode 100644 index 0000000000..32588105d9 --- /dev/null +++ b/gdb/testsuite/gdb.python/python-mi.exp @@ -0,0 +1,99 @@ +# Copyright (C) 2008, 2009 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# This file is part of the GDB testsuite. It tests Python-based +# pretty-printing for MI. + +load_lib mi-support.exp +set MIFLAGS "-i=mi2" + +gdb_exit +if [mi_gdb_start] { + continue +} + +set testfile "python-prettyprint" +set srcfile ${testfile}.c +set binfile ${objdir}/${subdir}/${testfile} +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug additional_flags=-DMI}] != "" } { + untested mi2-var-child.exp + return -1 +} + +mi_delete_breakpoints +mi_gdb_reinitialize_dir $srcdir/$subdir +mi_gdb_load ${binfile} + +if {[lsearch -exact [mi_get_features] python] < 0} { + unsupported "python support is disabled" + return -1 +} + +mi_runto main + +mi_gdb_test "python execfile ('${srcdir}/${subdir}/${testfile}.py')" "" + +mi_continue_to_line [gdb_get_line_number {MI breakpoint here} ${testfile}.c] \ + "step to breakpoint" + +mi_create_floating_varobj container c "create container varobj" + +mi_list_varobj_children container { +} "examine container children=0" + +mi_next "next over update 1" + +mi_varobj_update_dynamic container { + { {container.\[0\]} {\[0\]} 0 int } +} "varobj update 1" + +mi_next "next over update 2" + +mi_varobj_update_dynamic container { + { {container.\[0\]} {\[0\]} 0 int } + { {container.\[1\]} {\[1\]} 0 int } +} "varobj update 2" + +mi_gdb_test "-var-set-visualizer container None" \ + "\\^done" \ + "clear visualizer" + +mi_gdb_test "-var-update container" \ + "\\^done,changelist=\\\[\\\]" \ + "varobj update after clearing" + +mi_gdb_test "-var-set-visualizer container gdb.default_visualizer" \ + "\\^done" \ + "choose default visualizer" + +mi_varobj_update_dynamic container { + { {container.\[0\]} {\[0\]} 0 int } + { {container.\[1\]} {\[1\]} 0 int } +} "varobj update after choosing default" + +mi_gdb_test "-var-set-visualizer container ContainerPrinter" \ + "\\^done" \ + "choose visualizer using expression" + +mi_varobj_update_dynamic container { + { {container.\[0\]} {\[0\]} 0 int } + { {container.\[1\]} {\[1\]} 0 int } +} "varobj update after choosing via expression" + +mi_continue_to_line \ + [gdb_get_line_number {Another MI breakpoint} ${testfile}.c] \ + "step to second breakpoint" + +mi_varobj_update_with_type_change container int 0 "update after type change" diff --git a/gdb/testsuite/lib/mi-support.exp b/gdb/testsuite/lib/mi-support.exp index c31111b3eb..ad78360342 100644 --- a/gdb/testsuite/lib/mi-support.exp +++ b/gdb/testsuite/lib/mi-support.exp @@ -1249,6 +1249,21 @@ proc mi_varobj_update_with_type_change { name new_type new_children testname } { mi_gdb_test "-var-update $name" $er $testname } +# Update a dynamic varobj named NAME. CHILDREN is a list of children, +# in the same form as mi_list_varobj_children. TESTNAME is the name +# of the test. +proc mi_varobj_update_dynamic {name children testname} { + set children_exp_j [mi_child_regexp $children 0] + + set er "\\^done,changelist=\\\[" + + append er "{name=\"$name\",in_scope=\"true\",type_changed=\"false\"" + append er ",children=\\\[$children_exp_j.*\\\]}\\\]" + + verbose -log "Expecting: $er" + mi_gdb_test "-var-update $name" $er $testname +} + proc mi_check_varobj_value { name value testname } { mi_gdb_test "-var-evaluate-expression $name" \ @@ -1256,6 +1271,42 @@ proc mi_check_varobj_value { name value testname } { $testname } +# Helper proc which constructs a child regexp for +# mi_list_varobj_children and mi_varobj_update_dynamic. +proc mi_child_regexp {children add_child} { + set children_exp {} + set whatever "\"\[^\"\]+\"" + + if {$add_child} { + set pre "child=" + } else { + set pre "" + } + + foreach item $children { + + set name [lindex $item 0] + set exp [lindex $item 1] + set numchild [lindex $item 2] + if {[llength $item] == 5} { + set type [lindex $item 3] + set value [lindex $item 4] + + lappend children_exp\ + "$pre{name=\"$name\",exp=\"$exp\",numchild=\"$numchild\",value=\"$value\",type=\"$type\"\(,thread-id=\"\[0-9\]+\")?}" + } elseif {[llength $item] == 4} { + set type [lindex $item 3] + + lappend children_exp\ + "$pre{name=\"$name\",exp=\"$exp\",numchild=\"$numchild\",type=\"$type\"\(,thread-id=\"\[0-9\]+\")?}" + } else { + lappend children_exp\ + "$pre{name=\"$name\",exp=\"$exp\",numchild=\"$numchild\"(,thread-id=\"\[0-9\]+\")?}" + } + } + return [join $children_exp ","] +} + # Check the results of the: # # -var-list-children VARNAME @@ -1277,39 +1328,23 @@ proc mi_check_varobj_value { name value testname } { # have no value. # proc mi_list_varobj_children { varname children testname } { + mi_list_varobj_children_range $varname [llength $children] $children \ + $testname +} +# Like mi_list_varobj_children, but assumes that a subrange has been +# selected with -var-set-child-range. NUMCHILDREN is the total number +# of children. +proc mi_list_varobj_children_range {varname numchildren children testname} { set options "" if {[llength $varname] == 2} { set options [lindex $varname 1] set varname [lindex $varname 0] } - set numchildren [llength $children] - set children_exp {} set whatever "\"\[^\"\]+\"" - foreach item $children { - - set name [lindex $item 0] - set exp [lindex $item 1] - set numchild [lindex $item 2] - if {[llength $item] == 5} { - set type [lindex $item 3] - set value [lindex $item 4] - - lappend children_exp\ - "child={name=\"$name\",exp=\"$exp\",numchild=\"$numchild\",value=\"$value\",type=\"$type\"\(,thread-id=\"\[0-9\]+\")?}" - } elseif {[llength $item] == 4} { - set type [lindex $item 3] - - lappend children_exp\ - "child={name=\"$name\",exp=\"$exp\",numchild=\"$numchild\",type=\"$type\"\(,thread-id=\"\[0-9\]+\")?}" - } else { - lappend children_exp\ - "child={name=\"$name\",exp=\"$exp\",numchild=\"$numchild\"(,thread-id=\"\[0-9\]+\")?}" - } - } - set children_exp_j [join $children_exp ","] + set children_exp_j [mi_child_regexp $children 1] if {$numchildren} { set expected "\\^done,numchild=\".*\",children=\\\[$children_exp_j.*\\\]" } { @@ -1782,3 +1817,25 @@ proc mi_check_thread_states { xstates test } { verbose -log "expecting: $pattern" mi_gdb_test "-thread-info" $pattern $test } + +# Return a list of MI features supported by this gdb. +proc mi_get_features {} { + global expect_out mi_gdb_prompt + + send_gdb "-list-features\n" + + gdb_expect { + -re "\\^done,features=\\\[(.*)\\\]\r\n$mi_gdb_prompt$" { + regsub -all -- \" $expect_out(1,string) "" features + return [split $features ,] + } + -re ".*\r\n$mi_gdb_prompt$" { + verbose -log "got $expect_out(buffer)" + return "" + } + timeout { + verbose -log "timeout in mi_gdb_prompt" + return "" + } + } +} diff --git a/gdb/varobj.c b/gdb/varobj.c index e8556d78fd..ca698dce6b 100644 --- a/gdb/varobj.c +++ b/gdb/varobj.c @@ -35,6 +35,13 @@ #include "gdbthread.h" #include "inferior.h" +#if HAVE_PYTHON +#include "python/python.h" +#include "python/python-internal.h" +#else +typedef int PyObject; +#endif + /* Non-zero if we want to see trace of varobj level stuff. */ int varobjdebug = 0; @@ -138,6 +145,12 @@ struct varobj /* Children of this object. */ VEC (varobj_p) *children; + /* Whether the children of this varobj were requested. This field is + used to decide if dynamic varobj should recompute their children. + In the event that the frontend never asked for the children, we + can avoid that. */ + int children_requested; + /* Description of the root variable. Points to root variable for children. */ struct varobj_root *root; @@ -159,6 +172,10 @@ struct varobj not fetched if either the variable is frozen, or any parents is frozen. */ int not_fetched; + + /* The pretty-printer that has been constructed. If NULL, then a + new printer object is needed, and one will be constructed. */ + PyObject *pretty_printer; }; struct cpstack @@ -190,6 +207,10 @@ static void uninstall_variable (struct varobj *); static struct varobj *create_child (struct varobj *, int, char *); +static struct varobj * +create_child_with_value (struct varobj *parent, int index, const char *name, + struct value *value); + /* Utility routines */ static struct varobj *new_variable (void); @@ -215,6 +236,8 @@ static char *cppop (struct cpstack **pstack); static int install_new_value (struct varobj *var, struct value *value, int initial); +static void install_default_visualizer (struct varobj *var); + /* Language-specific routines. */ static enum varobj_languages variable_language (struct varobj *var); @@ -233,12 +256,16 @@ static char *my_value_of_variable (struct varobj *var, enum varobj_display_formats format); static char *value_get_print_value (struct value *value, - enum varobj_display_formats format); + enum varobj_display_formats format, + PyObject *value_formatter); static int varobj_value_is_changeable_p (struct varobj *var); static int is_root_p (struct varobj *var); +static struct varobj * +varobj_add_child (struct varobj *var, const char *name, struct value *value); + /* C implementation */ static int c_number_of_children (struct varobj *var); @@ -570,6 +597,7 @@ varobj_create (char *objname, } } + install_default_visualizer (var); discard_cleanups (old_chain); return var; } @@ -676,6 +704,33 @@ varobj_delete (struct varobj *var, char ***dellist, int only_children) return delcount; } +/* Convenience function for varobj_set_visualizer. Instantiate a + pretty-printer for a given value. */ +static PyObject * +instantiate_pretty_printer (PyObject *constructor, struct value *value) +{ +#if HAVE_PYTHON + PyObject *val_obj = NULL; + PyObject *printer; + volatile struct gdb_exception except; + + TRY_CATCH (except, RETURN_MASK_ALL) + { + value = value_copy (value); + } + GDB_PY_HANDLE_EXCEPTION (except); + val_obj = value_to_value_object (value); + + if (! val_obj) + return NULL; + + printer = PyObject_CallFunctionObjArgs (constructor, val_obj, NULL); + Py_DECREF (val_obj); + return printer; +#endif + return NULL; +} + /* Set/Get variable object display format */ enum varobj_display_formats @@ -700,7 +755,8 @@ varobj_set_display_format (struct varobj *var, && var->value && !value_lazy (var->value)) { xfree (var->print_value); - var->print_value = value_get_print_value (var->value, var->format); + var->print_value = value_get_print_value (var->value, var->format, + var->pretty_printer); } return var->format; @@ -712,6 +768,21 @@ varobj_get_display_format (struct varobj *var) return var->format; } +char * +varobj_get_display_hint (struct varobj *var) +{ + char *result = NULL; + +#if HAVE_PYTHON + PyGILState_STATE state = PyGILState_Ensure (); + if (var->pretty_printer) + result = gdbpy_get_display_hint (var->pretty_printer); + PyGILState_Release (state); +#endif + + return result; +} + /* If the variable object is bound to a specific thread, that is its evaluation can always be done in context of a frame inside that thread, returns GDB id of the thread -- which @@ -744,12 +815,141 @@ varobj_get_frozen (struct varobj *var) return var->frozen; } +static int +update_dynamic_varobj_children (struct varobj *var, + VEC (varobj_p) **changed, + VEC (varobj_p) **new_and_unchanged, + int *cchanged) + +{ +#if HAVE_PYTHON + /* FIXME: we *might* want to provide this functionality as + a standalone function, so that other interested parties + than varobj code can benefit for this. */ + struct cleanup *back_to; + PyObject *children; + PyObject *iterator; + int i; + int children_changed = 0; + PyObject *printer = var->pretty_printer; + PyGILState_STATE state; + + state = PyGILState_Ensure (); + back_to = make_cleanup_py_restore_gil (&state); + + *cchanged = 0; + if (!PyObject_HasAttr (printer, gdbpy_children_cst)) + { + do_cleanups (back_to); + return 0; + } + + children = PyObject_CallMethodObjArgs (printer, gdbpy_children_cst, + NULL); + + if (!children) + { + gdbpy_print_stack (); + error ("Null value returned for children"); + } + + make_cleanup_py_decref (children); + + if (!PyIter_Check (children)) + error ("Returned value is not iterable"); + + iterator = PyObject_GetIter (children); + if (!iterator) + { + gdbpy_print_stack (); + error ("Could not get children iterator"); + } + make_cleanup_py_decref (iterator); + + for (i = 0; ; ++i) + { + PyObject *item = PyIter_Next (iterator); + PyObject *py_v; + struct value *v; + char *name; + struct cleanup *inner; + + if (!item) + break; + inner = make_cleanup_py_decref (item); + + if (!PyArg_ParseTuple (item, "sO", &name, &py_v)) + error ("Invalid item from the child list"); + + if (PyObject_TypeCheck (py_v, &value_object_type)) + { + /* If we just call convert_value_from_python for this type, + we won't know who owns the result. For this one case we + need to copy the resulting value. */ + v = value_object_to_value (py_v); + v = value_copy (v); + } + else + v = convert_value_from_python (py_v); + + /* TODO: This assume the name of the i-th child never changes. */ + + /* Now see what to do here. */ + if (VEC_length (varobj_p, var->children) < i + 1) + { + /* There's no child yet. */ + struct varobj *child = varobj_add_child (var, name, v); + if (new_and_unchanged) + VEC_safe_push (varobj_p, *new_and_unchanged, child); + children_changed = 1; + } + else + { + varobj_p existing = VEC_index (varobj_p, var->children, i); + if (install_new_value (existing, v, 0) && changed) + { + if (changed) + VEC_safe_push (varobj_p, *changed, existing); + } + else + { + if (new_and_unchanged) + VEC_safe_push (varobj_p, *new_and_unchanged, existing); + } + } + + do_cleanups (inner); + } + + if (i < VEC_length (varobj_p, var->children)) + { + int i; + children_changed = 1; + for (i = 0; i < VEC_length (varobj_p, var->children); ++i) + varobj_delete (VEC_index (varobj_p, var->children, i), NULL, 0); + } + VEC_truncate (varobj_p, var->children, i); + var->num_children = VEC_length (varobj_p, var->children); + + do_cleanups (back_to); + + *cchanged = children_changed; + return 1; +#else + gdb_assert (0 && "should never be called if Python is not enabled"); +#endif +} int varobj_get_num_children (struct varobj *var) { if (var->num_children == -1) - var->num_children = number_of_children (var); + { + int changed; + if (!var->pretty_printer + || !update_dynamic_varobj_children (var, NULL, NULL, &changed)) + var->num_children = number_of_children (var); + } return var->num_children; } @@ -762,7 +962,16 @@ varobj_list_children (struct varobj *var) { struct varobj *child; char *name; - int i; + int i, children_changed; + + var->children_requested = 1; + + if (var->pretty_printer + /* This, in theory, can result in the number of children changing without + frontend noticing. But well, calling -var-list-children on the same + varobj twice is not something a sane frontend would do. */ + && update_dynamic_varobj_children (var, NULL, NULL, &children_changed)) + return var->children; if (var->num_children == -1) var->num_children = number_of_children (var); @@ -788,12 +997,24 @@ varobj_list_children (struct varobj *var) name = name_of_child (var, i); existing = create_child (var, i, name); VEC_replace (varobj_p, var->children, i, existing); + install_default_visualizer (existing); } } return var->children; } +static struct varobj * +varobj_add_child (struct varobj *var, const char *name, struct value *value) +{ + varobj_p v = create_child_with_value (var, + VEC_length (varobj_p, var->children), + name, value); + VEC_safe_push (varobj_p, var->children, v); + install_default_visualizer (v); + return v; +} + /* Obtain the type of an object Variable as a string similar to the one gdb prints on the console */ @@ -1003,6 +1224,13 @@ install_new_value (struct varobj *var, struct value *value, int initial) a type. */ gdb_assert (var->type || CPLUS_FAKE_CHILD (var)); changeable = varobj_value_is_changeable_p (var); + + /* If the type has custom visualizer, we consider it to be always + changeable. FIXME: need to make sure this behaviour will not + mess up read-sensitive values. */ + if (var->pretty_printer) + changeable = 1; + need_to_fetch = changeable; /* We are not interested in the address of references, and given @@ -1054,12 +1282,14 @@ install_new_value (struct varobj *var, struct value *value, int initial) } } + /* Below, we'll be comparing string rendering of old and new values. Don't get string rendering if the value is lazy -- if it is, the code above has decided that the value should not be fetched. */ if (value && !value_lazy (value)) - print_value = value_get_print_value (value, var->format); + print_value = value_get_print_value (value, var->format, + var->pretty_printer); /* If the type is changeable, compare the old and the new values. If this is the initial assignment, we don't have any old value @@ -1133,6 +1363,114 @@ install_new_value (struct varobj *var, struct value *value, int initial) return changed; } +static void +install_visualizer (struct varobj *var, PyObject *visualizer) +{ +#if HAVE_PYTHON + /* If there are any children now, wipe them. */ + varobj_delete (var, NULL, 1 /* children only */); + var->num_children = -1; + + Py_XDECREF (var->pretty_printer); + var->pretty_printer = visualizer; + + install_new_value (var, var->value, 1); + + /* If we removed the visualizer, and the user ever requested the + object's children, then we must compute the list of children. + Note that we needn't do this when installing a visualizer, + because updating will recompute dynamic children. */ + if (!visualizer && var->children_requested) + varobj_list_children (var); +#else + error ("Python support required"); +#endif +} + +static void +install_default_visualizer (struct varobj *var) +{ +#if HAVE_PYTHON + struct cleanup *cleanup; + PyGILState_STATE state; + PyObject *pretty_printer = NULL; + + state = PyGILState_Ensure (); + cleanup = make_cleanup_py_restore_gil (&state); + + if (var->value) + { + pretty_printer = gdbpy_get_varobj_pretty_printer (var->value); + if (! pretty_printer) + { + gdbpy_print_stack (); + error (_("Cannot instantiate printer for default visualizer")); + } + } + + if (pretty_printer == Py_None) + { + Py_DECREF (pretty_printer); + pretty_printer = NULL; + } + + install_visualizer (var, pretty_printer); + do_cleanups (cleanup); +#else + /* No error is right as this function is inserted just as a hook. */ +#endif +} + +void +varobj_set_visualizer (struct varobj *var, const char *visualizer) +{ +#if HAVE_PYTHON + PyObject *mainmod, *globals, *pretty_printer, *constructor; + struct cleanup *back_to, *value; + PyGILState_STATE state; + + + state = PyGILState_Ensure (); + back_to = make_cleanup_py_restore_gil (&state); + + mainmod = PyImport_AddModule ("__main__"); + globals = PyModule_GetDict (mainmod); + Py_INCREF (globals); + make_cleanup_py_decref (globals); + + constructor = PyRun_String (visualizer, Py_eval_input, globals, globals); + + /* Do not instantiate NoneType. */ + if (constructor == Py_None) + { + pretty_printer = Py_None; + Py_INCREF (pretty_printer); + } + else + pretty_printer = instantiate_pretty_printer (constructor, var->value); + + Py_XDECREF (constructor); + + if (! pretty_printer) + { + gdbpy_print_stack (); + error ("Could not evaluate visualizer expression: %s", visualizer); + } + + if (pretty_printer == Py_None) + { + Py_DECREF (pretty_printer); + pretty_printer = NULL; + } + + install_visualizer (var, pretty_printer); + + do_cleanups (back_to); +#else + error ("Python support required"); +#endif +} + /* Update the values for a variable and its children. This is a two-pronged attack. First, re-parse the value for the root's expression to see if it's changed. Then go all the way @@ -1158,7 +1496,7 @@ VEC(varobj_update_result) *varobj_update (struct varobj **varp, int explicit) struct varobj **cv; struct varobj **templist = NULL; struct value *new; - VEC (varobj_p) *stack = NULL; + VEC (varobj_update_result) *stack = NULL; VEC (varobj_update_result) *result = NULL; struct frame_info *fi; @@ -1197,20 +1535,85 @@ VEC(varobj_update_result) *varobj_update (struct varobj **varp, int explicit) if (new == NULL) r.status = VAROBJ_NOT_IN_SCOPE; - - if (r.type_changed || r.changed) - VEC_safe_push (varobj_update_result, result, &r); + r.value_installed = 1; if (r.status == VAROBJ_NOT_IN_SCOPE) - return result; + { + VEC_safe_push (varobj_update_result, result, &r); + return result; + } + + VEC_safe_push (varobj_update_result, stack, &r); + } + else + { + varobj_update_result r = {*varp}; + VEC_safe_push (varobj_update_result, stack, &r); } - - VEC_safe_push (varobj_p, stack, *varp); /* Walk through the children, reconstructing them all. */ - while (!VEC_empty (varobj_p, stack)) + while (!VEC_empty (varobj_update_result, stack)) { - v = VEC_pop (varobj_p, stack); + varobj_update_result r = *(VEC_last (varobj_update_result, stack)); + struct varobj *v = r.varobj; + + VEC_pop (varobj_update_result, stack); + + /* Update this variable, unless it's a root, which is already + updated. */ + if (!r.value_installed) + { + new = value_of_child (v->parent, v->index); + if (install_new_value (v, new, 0 /* type not changed */)) + { + r.changed = 1; + v->updated = 0; + } + } + + /* We probably should not get children of a varobj that has a + pretty-printer, but for which -var-list-children was never + invoked. Presumably, such varobj is not yet expanded in the + UI, so we need not bother getting it. */ + if (v->pretty_printer) + { + VEC (varobj_p) *changed = 0, *new_and_unchanged = 0; + int i, children_changed; + varobj_p tmp; + + if (!v->children_requested) + continue; + + if (v->frozen) + continue; + + /* If update_dynamic_varobj_children returns 0, then we have + a non-conforming pretty-printer, so we skip it. */ + if (update_dynamic_varobj_children (v, &changed, &new_and_unchanged, + &children_changed)) + { + if (children_changed) + r.children_changed = 1; + for (i = 0; VEC_iterate (varobj_p, changed, i, tmp); ++i) + { + varobj_update_result r = {tmp}; + r.changed = 1; + r.value_installed = 1; + VEC_safe_push (varobj_update_result, stack, &r); + } + for (i = 0; + VEC_iterate (varobj_p, new_and_unchanged, i, tmp); + ++i) + { + varobj_update_result r = {tmp}; + r.value_installed = 1; + VEC_safe_push (varobj_update_result, stack, &r); + } + if (r.changed || r.children_changed) + VEC_safe_push (varobj_update_result, result, &r); + continue; + } + } /* Push any children. Use reverse order so that the first child is popped from the work stack first, and so @@ -1221,26 +1624,18 @@ VEC(varobj_update_result) *varobj_update (struct varobj **varp, int explicit) varobj_p c = VEC_index (varobj_p, v->children, i); /* Child may be NULL if explicitly deleted by -var-delete. */ if (c != NULL && !c->frozen) - VEC_safe_push (varobj_p, stack, c); - } - - /* Update this variable, unless it's a root, which is already - updated. */ - if (v->root->rootvar != v) - { - new = value_of_child (v->parent, v->index); - if (install_new_value (v, new, 0 /* type not changed */)) { - /* Note that it's changed */ - varobj_update_result r = {v}; - r.changed = 1; - VEC_safe_push (varobj_update_result, result, &r); - v->updated = 0; + varobj_update_result r = {c}; + VEC_safe_push (varobj_update_result, stack, &r); } } + + if (r.changed || r.type_changed) + VEC_safe_push (varobj_update_result, result, &r); } - VEC_free (varobj_p, stack); + VEC_free (varobj_update_result, stack); + return result; } @@ -1438,17 +1833,24 @@ uninstall_variable (struct varobj *var) /* Create and install a child of the parent of the given name */ static struct varobj * create_child (struct varobj *parent, int index, char *name) +{ + return create_child_with_value (parent, index, name, + value_of_child (parent, index)); +} + +static struct varobj * +create_child_with_value (struct varobj *parent, int index, const char *name, + struct value *value) { struct varobj *child; char *childs_name; - struct value *value; child = new_variable (); /* name is allocated by name_of_child */ - child->name = name; + /* FIXME: xstrdup should not be here. */ + child->name = xstrdup (name); child->index = index; - value = value_of_child (parent, index); child->parent = parent; child->root = parent->root; childs_name = xstrprintf ("%s.%s", parent->obj_name, name); @@ -1497,6 +1899,8 @@ new_variable (void) var->print_value = NULL; var->frozen = 0; var->not_fetched = 0; + var->children_requested = 0; + var->pretty_printer = 0; return var; } @@ -1531,6 +1935,14 @@ free_variable (struct varobj *var) xfree (var->root); } +#if HAVE_PYTHON + { + PyGILState_STATE state = PyGILState_Ensure (); + Py_XDECREF (var->pretty_printer); + PyGILState_Release (state); + } +#endif + xfree (var->name); xfree (var->obj_name); xfree (var->print_value); @@ -1804,23 +2216,65 @@ my_value_of_variable (struct varobj *var, enum varobj_display_formats format) } static char * -value_get_print_value (struct value *value, enum varobj_display_formats format) +value_get_print_value (struct value *value, enum varobj_display_formats format, + PyObject *value_formatter) { long dummy; struct ui_file *stb; struct cleanup *old_chain; - char *thevalue; + char *thevalue = NULL; struct value_print_options opts; if (value == NULL) return NULL; +#if HAVE_PYTHON + { + PyGILState_STATE state = PyGILState_Ensure (); + if (value_formatter && PyObject_HasAttr (value_formatter, + gdbpy_to_string_cst)) + { + char *hint; + struct value *replacement; + int string_print = 0; + + hint = gdbpy_get_display_hint (value_formatter); + if (hint) + { + if (!strcmp (hint, "string")) + string_print = 1; + xfree (hint); + } + + thevalue = apply_varobj_pretty_printer (value_formatter, + &replacement); + if (thevalue && !string_print) + { + PyGILState_Release (state); + return thevalue; + } + if (replacement) + value = replacement; + } + PyGILState_Release (state); + } +#endif + stb = mem_fileopen (); old_chain = make_cleanup_ui_file_delete (stb); get_formatted_print_options (&opts, format_code[(int) format]); opts.deref_ref = 0; - common_val_print (value, stb, 0, &opts, current_language); + opts.raw = 1; + if (thevalue) + { + make_cleanup (xfree, thevalue); + LA_PRINT_STRING (stb, builtin_type (current_gdbarch)->builtin_char, + (gdb_byte *) thevalue, strlen (thevalue), + 0, &opts); + } + else + common_val_print (value, stb, 0, &opts, current_language); thevalue = ui_file_xstrdup (stb, &dummy); do_cleanups (old_chain); @@ -1911,7 +2365,7 @@ varobj_floating_p (struct varobj *var) value is not known. If WAS_PTR is not NULL, set *WAS_PTR to 0 or 1 - depending on whether pointer was deferenced + depending on whether pointer was dereferenced in this function. */ static void adjust_value_for_child_access (struct value **value, @@ -2277,6 +2731,11 @@ c_value_of_variable (struct varobj *var, enum varobj_display_formats format) catch that case explicitly. */ struct type *type = get_type (var); + /* If we have a custom formatter, return whatever string it has + produced. */ + if (var->pretty_printer && var->print_value) + return xstrdup (var->print_value); + /* Strip top-level references. */ while (TYPE_CODE (type) == TYPE_CODE_REF) type = check_typedef (TYPE_TARGET_TYPE (type)); @@ -2321,7 +2780,8 @@ c_value_of_variable (struct varobj *var, enum varobj_display_formats format) if (format == var->format) return xstrdup (var->print_value); else - return value_get_print_value (var->value, format); + return value_get_print_value (var->value, format, + var->pretty_printer); } } } diff --git a/gdb/varobj.h b/gdb/varobj.h index f2cdcf8eff..c1ad09904f 100644 --- a/gdb/varobj.h +++ b/gdb/varobj.h @@ -71,8 +71,13 @@ typedef struct varobj_update_result_t { struct varobj *varobj; int type_changed; + int children_changed; int changed; enum varobj_scope_status status; + /* This variable is used internally by varobj_update to indicate if the + new value of varobj is already computed and installed, or has to + be yet installed. Don't use this outside varobj.c */ + int value_installed; } varobj_update_result; DEF_VEC_O (varobj_update_result); @@ -107,6 +112,8 @@ extern void varobj_set_frozen (struct varobj *var, int frozen); extern int varobj_get_frozen (struct varobj *var); +extern char *varobj_get_display_hint (struct varobj *var); + extern int varobj_get_num_children (struct varobj *var); /* Return the list of children of VAR. The returned vector @@ -141,4 +148,6 @@ extern int varobj_editable_p (struct varobj *var); extern int varobj_floating_p (struct varobj *var); +extern void varobj_set_visualizer (struct varobj *var, const char *visualizer); + #endif /* VAROBJ_H */ -- 2.34.1