From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2126) id 0E9FE3857BBD; Fri, 15 Jul 2022 15:28:52 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 0E9FE3857BBD Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable From: Tom Tromey To: gdb-cvs@sourceware.org Subject: [binutils-gdb] Expose current 'print' settings to Python X-Act-Checkin: binutils-gdb X-Git-Author: Tom Tromey X-Git-Refname: refs/heads/master X-Git-Oldrev: aa63b0a77e193b5181390f19465f33c314d0490e X-Git-Newrev: c4a3dbaf1132105586586617a59d0e7566eefd41 Message-Id: <20220715152852.0E9FE3857BBD@sourceware.org> Date: Fri, 15 Jul 2022 15:28:52 +0000 (GMT) X-BeenThere: gdb-cvs@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 15 Jul 2022 15:28:52 -0000 https://sourceware.org/git/gitweb.cgi?p=3Dbinutils-gdb.git;h=3Dc4a3dbaf1132= 105586586617a59d0e7566eefd41 commit c4a3dbaf1132105586586617a59d0e7566eefd41 Author: Tom Tromey Date: Mon Jun 6 09:54:45 2022 -0600 Expose current 'print' settings to Python =20 PR python/17291 asks for access to the current print options. While I think this need is largely satisfied by the existence of Value.format_string, it seemed to me that a bit more could be done. =20 First, while Value.format_string uses the user's settings, it does not react to temporary settings such as "print/x". This patch changes this. =20 Second, there is no good way to examine the current settings (in particular the temporary ones in effect for just a single "print"). This patch adds this as well. =20 Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=3D17291 Diff: --- gdb/NEWS | 7 ++ gdb/doc/python.texi | 18 +++++ gdb/python/py-prettyprint.c | 98 +++++++++++++++++++++++= +++- gdb/python/py-value.c | 4 +- gdb/python/py-varobj.c | 25 +++++-- gdb/python/python-internal.h | 13 +++- gdb/python/python.c | 4 ++ gdb/testsuite/gdb.python/py-format-string.exp | 49 +++++++++++--- gdb/testsuite/gdb.python/py-format-string.py | 4 ++ gdb/varobj.c | 13 ++-- 10 files changed, 209 insertions(+), 26 deletions(-) diff --git a/gdb/NEWS b/gdb/NEWS index 3c82d6d8cb0..85e1e108457 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -149,6 +149,13 @@ GNU/Linux/LoongArch (gdbserver) loongarch*-*-linux* ** gdb.Objfile now has an attribute named "is_file". This is True if the objfile comes from a file, and False otherwise. =20 + ** New function gdb.print_options that returns a dictionary of the + prevailing print options, in the form accepted by + gdb.Value.format_string. + + ** gdb.Value.format_string now uses the format provided by 'print', + if it is called during a 'print' or other similar operation. + * New features in the GDB remote stub, GDBserver =20 ** GDBserver is now supported on LoongArch GNU/Linux. diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index 19ae33012c3..4573ba67734 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -1742,6 +1742,24 @@ pretty-printer for this value exists, then it is ret= urned. If no such printer exists, then this returns @code{None}. @end defun =20 +Normally, a pretty-printer can respect the user's print settings +(including temporarily applied settings, such as @samp{/x}) simply by +calling @code{Value.format_string} (@pxref{Values From Inferior}). +However, these settings can also be queried directly: + +@findex gdb.print_options +@defun gdb.print_options () +Return a dictionary whose keys are the valid keywords that can be +given to @code{Value.format_string}, and whose values are the user's +settings. During a @code{print} or other operation, the values will +reflect any flags that are temporarily in effect. + +@smallexample +(gdb) python print (gdb.print_options ()['max_elements']) +200 +@end smallexample +@end defun + @node Selecting Pretty-Printers @subsubsection Selecting Pretty-Printers @cindex selecting python pretty-printers diff --git a/gdb/python/py-prettyprint.c b/gdb/python/py-prettyprint.c index a25a1b65944..4ef45b283f9 100644 --- a/gdb/python/py-prettyprint.c +++ b/gdb/python/py-prettyprint.c @@ -39,6 +39,10 @@ enum gdbpy_string_repr_result string_repr_ok }; =20 +/* If non-null, points to options that are in effect while + printing. */ +const struct value_print_options *gdbpy_current_print_options; + /* Helper function for find_pretty_printer which iterates over a list, calls each function and inspects output. This will return a printer object if one recognizes VALUE. If no printer is found, it @@ -604,6 +608,9 @@ gdbpy_apply_val_pretty_printer (const struct extension_= language_defn *extlang, if (printer =3D=3D Py_None) return EXT_LANG_RC_NOP; =20 + scoped_restore set_options =3D make_scoped_restore (&gdbpy_current_print= _options, + options); + /* If we are printing a map, we want some special formatting. */ gdb::unique_xmalloc_ptr hint (gdbpy_get_display_hint (printer.get = ())); =20 @@ -632,8 +639,12 @@ gdbpy_apply_val_pretty_printer (const struct extension= _language_defn *extlang, gdbpy_ref<> apply_varobj_pretty_printer (PyObject *printer_obj, struct value **replacement, - struct ui_file *stream) + struct ui_file *stream, + const value_print_options *opts) { + scoped_restore set_options =3D make_scoped_restore (&gdbpy_current_print= _options, + opts); + *replacement =3D NULL; gdbpy_ref<> py_str =3D pretty_print_one_value (printer_obj, replacement); =20 @@ -688,3 +699,88 @@ gdbpy_default_visualizer (PyObject *self, PyObject *ar= gs) =20 return find_pretty_printer (val_obj).release (); } + +/* Helper function to set a boolean in a dictionary. */ +static int +set_boolean (PyObject *dict, const char *name, bool val) +{ + gdbpy_ref<> val_obj (PyBool_FromLong (val)); + if (val_obj =3D=3D nullptr) + return -1; + return PyDict_SetItemString (dict, name, val_obj.get ()); +} + +/* Helper function to set an integer in a dictionary. */ +static int +set_unsigned (PyObject *dict, const char *name, unsigned int val) +{ + gdbpy_ref<> val_obj =3D gdb_py_object_from_ulongest (val); + if (val_obj =3D=3D nullptr) + return -1; + return PyDict_SetItemString (dict, name, val_obj.get ()); +} + +/* Implement gdb.print_options. */ +PyObject * +gdbpy_print_options (PyObject *unused1, PyObject *unused2) +{ + gdbpy_ref<> result (PyDict_New ()); + if (result =3D=3D nullptr) + return nullptr; + + value_print_options opts; + gdbpy_get_print_options (&opts); + + if (set_boolean (result.get (), "raw", + opts.raw) < 0 + || set_boolean (result.get (), "pretty_arrays", + opts.prettyformat_arrays) < 0 + || set_boolean (result.get (), "pretty_structs", + opts.prettyformat_structs) < 0 + || set_boolean (result.get (), "array_indexes", + opts.print_array_indexes) < 0 + || set_boolean (result.get (), "symbols", + opts.symbol_print) < 0 + || set_boolean (result.get (), "unions", + opts.unionprint) < 0 + || set_boolean (result.get (), "address", + opts.addressprint) < 0 + || set_boolean (result.get (), "deref_refs", + opts.deref_ref) < 0 + || set_boolean (result.get (), "actual_objects", + opts.objectprint) < 0 + || set_boolean (result.get (), "static_members", + opts.static_field_print) < 0 + || set_boolean (result.get (), "deref_refs", + opts.deref_ref) < 0 + || set_unsigned (result.get (), "max_elements", + opts.print_max) < 0 + || set_unsigned (result.get (), "max_depth", + opts.max_depth) < 0 + || set_unsigned (result.get (), "repeat_threshold", + opts.repeat_count_threshold) < 0) + return nullptr; + + if (opts.format !=3D 0) + { + char str[2] =3D { (char) opts.format, 0 }; + gdbpy_ref<> fmtstr =3D host_string_to_python_string (str); + if (fmtstr =3D=3D nullptr) + return nullptr; + if (PyDict_SetItemString (result.get (), "format", fmtstr.get ()) < = 0) + return nullptr; + } + + return result.release (); +} + +/* Helper function that either finds the prevailing print options, or + calls get_user_print_options. */ +void +gdbpy_get_print_options (value_print_options *opts) +{ + if (gdbpy_current_print_options !=3D nullptr) + *opts =3D *gdbpy_current_print_options; + else + get_user_print_options (opts); +} diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c index 12e9562e64a..93cb9b99edb 100644 --- a/gdb/python/py-value.c +++ b/gdb/python/py-value.c @@ -673,7 +673,7 @@ valpy_format_string (PyObject *self, PyObject *args, Py= Object *kw) } =20 struct value_print_options opts; - get_user_print_options (&opts); + gdbpy_get_print_options (&opts); opts.deref_ref =3D 0; =20 /* We need objects for booleans as the "p" flag for bools is new in @@ -1163,7 +1163,7 @@ valpy_str (PyObject *self) { struct value_print_options opts; =20 - get_user_print_options (&opts); + gdbpy_get_print_options (&opts); opts.deref_ref =3D 0; =20 string_file stb; diff --git a/gdb/python/py-varobj.c b/gdb/python/py-varobj.c index 372c91125d9..9e4fb6c58bb 100644 --- a/gdb/python/py-varobj.c +++ b/gdb/python/py-varobj.c @@ -17,13 +17,15 @@ #include "python-internal.h" #include "varobj.h" #include "varobj-iter.h" +#include "valprint.h" =20 /* A dynamic varobj iterator "class" for python pretty-printed varobjs. This inherits struct varobj_iter. */ =20 struct py_varobj_iter : public varobj_iter { - py_varobj_iter (struct varobj *var, gdbpy_ref<> &&pyiter); + py_varobj_iter (struct varobj *var, gdbpy_ref<> &&pyiter, + const value_print_options *opts); ~py_varobj_iter () override; =20 std::unique_ptr next () override; @@ -41,6 +43,9 @@ private: /* The python iterator returned by the printer's 'children' method, or NULL if not available. */ PyObject *m_iter; + + /* The print options to use. */ + value_print_options m_opts; }; =20 /* Implementation of the 'dtor' method of pretty-printed varobj @@ -67,6 +72,9 @@ py_varobj_iter::next () =20 gdbpy_enter_varobj enter_py (m_var); =20 + scoped_restore set_options =3D make_scoped_restore (&gdbpy_current_print= _options, + &m_opts); + gdbpy_ref<> item (PyIter_Next (m_iter)); =20 if (item =3D=3D NULL) @@ -124,9 +132,11 @@ py_varobj_iter::next () whose children the iterator will be iterating over. PYITER is the python iterator actually responsible for the iteration. */ =20 -py_varobj_iter::py_varobj_iter (struct varobj *var, gdbpy_ref<> &&pyiter) +py_varobj_iter::py_varobj_iter (struct varobj *var, gdbpy_ref<> &&pyiter, + const value_print_options *opts) : m_var (var), - m_iter (pyiter.release ()) + m_iter (pyiter.release ()), + m_opts (*opts) { } =20 @@ -134,13 +144,17 @@ py_varobj_iter::py_varobj_iter (struct varobj *var, g= dbpy_ref<> &&pyiter) over VAR's children. */ =20 std::unique_ptr -py_varobj_get_iterator (struct varobj *var, PyObject *printer) +py_varobj_get_iterator (struct varobj *var, PyObject *printer, + const value_print_options *opts) { gdbpy_enter_varobj enter_py (var); =20 if (!PyObject_HasAttr (printer, gdbpy_children_cst)) return NULL; =20 + scoped_restore set_options =3D make_scoped_restore (&gdbpy_current_print= _options, + opts); + gdbpy_ref<> children (PyObject_CallMethodObjArgs (printer, gdbpy_childre= n_cst, NULL)); if (children =3D=3D NULL) @@ -157,5 +171,6 @@ py_varobj_get_iterator (struct varobj *var, PyObject *p= rinter) } =20 return std::unique_ptr (new py_varobj_iter (var, - std::move (iter))); + std::move (iter), + opts)); } diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h index 5d296a73e9a..551438c4709 100644 --- a/gdb/python/python-internal.h +++ b/gdb/python/python-internal.h @@ -746,11 +746,16 @@ int gdbpy_is_value_object (PyObject *obj); other pretty-printer functions, because they refer to PyObject. */ gdbpy_ref<> apply_varobj_pretty_printer (PyObject *print_obj, struct value **replacement, - struct ui_file *stream); + struct ui_file *stream, + const value_print_options *opts); gdbpy_ref<> gdbpy_get_varobj_pretty_printer (struct value *value); gdb::unique_xmalloc_ptr gdbpy_get_display_hint (PyObject *printer); PyObject *gdbpy_default_visualizer (PyObject *self, PyObject *args); =20 +PyObject *gdbpy_print_options (PyObject *self, PyObject *args); +void gdbpy_get_print_options (value_print_options *opts); +extern const struct value_print_options *gdbpy_current_print_options; + void bpfinishpy_pre_stop_hook (struct gdbpy_breakpoint_object *bp_obj); void bpfinishpy_post_stop_hook (struct gdbpy_breakpoint_object *bp_obj); =20 @@ -784,8 +789,10 @@ int gdb_pymodule_addobject (PyObject *module, const ch= ar *name, =20 struct varobj_iter; struct varobj; -std::unique_ptr py_varobj_get_iterator (struct varobj *var, - PyObject *printer); +std::unique_ptr py_varobj_get_iterator + (struct varobj *var, + PyObject *printer, + const value_print_options *opts); =20 /* Deleter for Py_buffer unique_ptr specialization. */ =20 diff --git a/gdb/python/python.c b/gdb/python/python.c index 2f1a00e629c..c75896dc324 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -2560,6 +2560,10 @@ the returned string is 'ADDRESS ' wit= hout the quotes." }, "current_language () -> string\n\ Return the name of the currently selected language." }, =20 + { "print_options", gdbpy_print_options, METH_NOARGS, + "print_options () -> dict\n\ +Return the current print options." }, + {NULL, NULL, 0, NULL} }; =20 diff --git a/gdb/testsuite/gdb.python/py-format-string.exp b/gdb/testsuite/= gdb.python/py-format-string.exp index 68a4a9c8472..58bbe85f693 100644 --- a/gdb/testsuite/gdb.python/py-format-string.exp +++ b/gdb/testsuite/gdb.python/py-format-string.exp @@ -544,10 +544,12 @@ proc_with_prefix test_nibbles {} { "0010 1010" \ "42 with option ${opts}" =20 - check_format_string "a_point_t" $opts + check_format_string "a_point_t" $opts \ + [string_to_regexp "Pretty Point (0010 1010, 1100)"] check_format_string "a_point_t_pointer" $opts \ $binary_pointer_regexp - check_format_string "another_point" $opts + check_format_string "another_point" $opts \ + [string_to_regexp "Pretty Point (0111 1011, 0001 1100 1000)"] =20 check_format_string "a_struct_with_union" $opts \ "\\{the_union =3D \\{an_int =3D 0010 1010 0010 1010 0010 1010 0010 1010= , a_char =3D 0010 1010\\}\\}" @@ -574,10 +576,12 @@ proc_with_prefix test_nibbles {} { "0010'1010" \ "42 with option ${opts}" =20 - check_format_string "a_point_t" $opts + check_format_string "a_point_t" $opts \ + [string_to_regexp "Pretty Point (0010'1010, 1100)"] check_format_string "a_point_t_pointer" $opts \ $binary_pointer_regexp - check_format_string "another_point" $opts + check_format_string "another_point" $opts \ + [string_to_regexp "Pretty Point (0111'1011, 0001'1100'1000)"] =20 check_format_string "a_struct_with_union" $opts \ "\\{the_union =3D \\{an_int =3D 0010'1010'0010'1010'0010'1010'0010'1010= , a_char =3D 0010'1010\\}\\}" @@ -598,7 +602,8 @@ proc_with_prefix test_nibbles {} { check_format_string "a_symbol_pointer" $opts \ $binary_pointer_regexp =20 - check_format_string "a_point_t_ref" $opts + check_format_string "a_point_t_ref" $opts \ + [string_to_regexp "Pretty Point (0010'1010, 1100)"] check_format_string "a_base_ref" $opts } } @@ -938,9 +943,11 @@ proc_with_prefix test_format {} { "0x2a" \ "42 with option ${opts}" =20 - check_format_string "a_point_t" $opts + check_format_string "a_point_t" $opts \ + "Pretty Point \\(0x2a, 0xc\\)" check_format_string "a_point_t_pointer" $opts - check_format_string "another_point" $opts + check_format_string "another_point" $opts \ + "Pretty Point \\(0x7b, 0x1c8\\)" check_format_string "a_struct_with_union" $opts \ "\\{the_union =3D \\{an_int =3D 0x2a2a2a2a, a_char =3D 0x2a\\}\\}" check_format_string "an_enum" $opts \ @@ -961,7 +968,8 @@ proc_with_prefix test_format {} { $default_pointer_regexp =20 if { $current_lang =3D=3D "c++" } { - check_format_string "a_point_t_ref" $opts + check_format_string "a_point_t_ref" $opts \ + "Pretty Point \\(0x2a, 0xc\\)" check_format_string "a_base_ref" $opts } } @@ -974,10 +982,12 @@ proc_with_prefix test_format {} { "101010" \ "42 with option ${opts}" =20 - check_format_string "a_point_t" $opts + check_format_string "a_point_t" $opts \ + "Pretty Point \\(101010, 1100\\)" check_format_string "a_point_t_pointer" $opts \ $binary_pointer_regexp - check_format_string "another_point" $opts + check_format_string "another_point" $opts \ + "Pretty Point \\(1111011, 111001000\\)" check_format_string "a_struct_with_union" $opts \ "\\{the_union =3D \\{an_int =3D 101010001010100010101000101010, a_ch= ar =3D 101010\\}\\}" check_format_string "an_enum" $opts \ @@ -998,7 +1008,8 @@ proc_with_prefix test_format {} { $binary_pointer_regexp =20 if { $current_lang =3D=3D "c++" } { - check_format_string "a_point_t_ref" $opts + check_format_string "a_point_t_ref" $opts \ + "Pretty Point \\(101010, 1100\\)" check_format_string "a_base_ref" $opts } } @@ -1103,6 +1114,21 @@ proc test_styling {} { "{[style x variable] =3D 42, [style y variable] =3D 12}" } =20 +# Test the gdb.print_options API. +proc test_print_options {} { + gdb_test_no_output "set print elements 500" + gdb_test "python print(gdb.print_options()\['max_elements'\])" "500" \ + "examine max elements" + gdb_test "python print('format' in gdb.print_options())" "False" \ + "examine format" + + check_format_string "a_point_t" "format=3D't'" \ + "Pretty Point \\(101010, 1100\\)" \ + "print in binary to fetch options" + gdb_test "python print(saved_options\['format'\] =3D=3D 't')" "True" \ + "format was set" +} + # Run all the tests in common for both C and C++. proc_with_prefix test_all_common {} { # No options. @@ -1127,6 +1153,7 @@ proc_with_prefix test_all_common {} { test_mixed # Various error conditions. test_invalid_args + test_print_options } =20 # The current language ("c" or "c++" while running tests). diff --git a/gdb/testsuite/gdb.python/py-format-string.py b/gdb/testsuite/g= db.python/py-format-string.py index 4a1743a0f45..aa7b10445cd 100644 --- a/gdb/testsuite/gdb.python/py-format-string.py +++ b/gdb/testsuite/gdb.python/py-format-string.py @@ -18,12 +18,16 @@ =20 import gdb =20 +saved_options =3D {} + =20 class PointPrinter(object): def __init__(self, val): self.val =3D val =20 def to_string(self): + global saved_options + saved_options =3D gdb.print_options() return "Pretty Point (%s, %s)" % (self.val["x"], self.val["y"]) =20 =20 diff --git a/gdb/varobj.c b/gdb/varobj.c index 1aca015a21a..9a6d579ddf0 100644 --- a/gdb/varobj.c +++ b/gdb/varobj.c @@ -658,7 +658,11 @@ varobj_get_iterator (struct varobj *var) { #if HAVE_PYTHON if (var->dynamic->pretty_printer) - return py_varobj_get_iterator (var, var->dynamic->pretty_printer); + { + value_print_options opts; + varobj_formatted_print_options (&opts, var->format); + return py_varobj_get_iterator (var, var->dynamic->pretty_printer, &o= pts); + } #endif =20 gdb_assert_not_reached ("requested an iterator from a non-dynamic varobj= "); @@ -2146,6 +2150,8 @@ varobj_value_get_print_value (struct value *value, string_file stb; std::string thevalue; =20 + varobj_formatted_print_options (&opts, format); + #if HAVE_PYTHON if (gdb_python_initialized) { @@ -2166,7 +2172,8 @@ varobj_value_get_print_value (struct value *value, =20 gdbpy_ref<> output =3D apply_varobj_pretty_printer (value_formatter, &replacement, - &stb); + &stb, + &opts); =20 /* If we have string like output ... */ if (output !=3D NULL) @@ -2225,8 +2232,6 @@ varobj_value_get_print_value (struct value *value, } #endif =20 - varobj_formatted_print_options (&opts, format); - /* If the THEVALUE has contents, it is a regular string. */ if (!thevalue.empty ()) current_language->printstr (&stb, type, (gdb_byte *) thevalue.c_str (),