Expose current 'print' settings to Python

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.

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.

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.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=17291
This commit is contained in:
Tom Tromey
2022-06-06 09:54:45 -06:00
parent aa63b0a77e
commit c4a3dbaf11
10 changed files with 209 additions and 26 deletions

View File

@ -149,6 +149,13 @@ GNU/Linux/LoongArch (gdbserver) loongarch*-*-linux*
** gdb.Objfile now has an attribute named "is_file". This is True ** gdb.Objfile now has an attribute named "is_file". This is True
if the objfile comes from a file, and False otherwise. if the objfile comes from a file, and False otherwise.
** 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 * New features in the GDB remote stub, GDBserver
** GDBserver is now supported on LoongArch GNU/Linux. ** GDBserver is now supported on LoongArch GNU/Linux.

View File

@ -1742,6 +1742,24 @@ pretty-printer for this value exists, then it is returned. If no such
printer exists, then this returns @code{None}. printer exists, then this returns @code{None}.
@end defun @end defun
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 @node Selecting Pretty-Printers
@subsubsection Selecting Pretty-Printers @subsubsection Selecting Pretty-Printers
@cindex selecting python pretty-printers @cindex selecting python pretty-printers

View File

@ -39,6 +39,10 @@ enum gdbpy_string_repr_result
string_repr_ok string_repr_ok
}; };
/* 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, /* Helper function for find_pretty_printer which iterates over a list,
calls each function and inspects output. This will return a calls each function and inspects output. This will return a
printer object if one recognizes VALUE. If no printer is found, it 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 == Py_None) if (printer == Py_None)
return EXT_LANG_RC_NOP; return EXT_LANG_RC_NOP;
scoped_restore set_options = make_scoped_restore (&gdbpy_current_print_options,
options);
/* If we are printing a map, we want some special formatting. */ /* If we are printing a map, we want some special formatting. */
gdb::unique_xmalloc_ptr<char> hint (gdbpy_get_display_hint (printer.get ())); gdb::unique_xmalloc_ptr<char> hint (gdbpy_get_display_hint (printer.get ()));
@ -632,8 +639,12 @@ gdbpy_apply_val_pretty_printer (const struct extension_language_defn *extlang,
gdbpy_ref<> gdbpy_ref<>
apply_varobj_pretty_printer (PyObject *printer_obj, apply_varobj_pretty_printer (PyObject *printer_obj,
struct value **replacement, struct value **replacement,
struct ui_file *stream) struct ui_file *stream,
const value_print_options *opts)
{ {
scoped_restore set_options = make_scoped_restore (&gdbpy_current_print_options,
opts);
*replacement = NULL; *replacement = NULL;
gdbpy_ref<> py_str = pretty_print_one_value (printer_obj, replacement); gdbpy_ref<> py_str = pretty_print_one_value (printer_obj, replacement);
@ -688,3 +699,88 @@ gdbpy_default_visualizer (PyObject *self, PyObject *args)
return find_pretty_printer (val_obj).release (); 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 == 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 = gdb_py_object_from_ulongest (val);
if (val_obj == 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 == 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 != 0)
{
char str[2] = { (char) opts.format, 0 };
gdbpy_ref<> fmtstr = host_string_to_python_string (str);
if (fmtstr == 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 != nullptr)
*opts = *gdbpy_current_print_options;
else
get_user_print_options (opts);
}

View File

@ -673,7 +673,7 @@ valpy_format_string (PyObject *self, PyObject *args, PyObject *kw)
} }
struct value_print_options opts; struct value_print_options opts;
get_user_print_options (&opts); gdbpy_get_print_options (&opts);
opts.deref_ref = 0; opts.deref_ref = 0;
/* We need objects for booleans as the "p" flag for bools is new in /* 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; struct value_print_options opts;
get_user_print_options (&opts); gdbpy_get_print_options (&opts);
opts.deref_ref = 0; opts.deref_ref = 0;
string_file stb; string_file stb;

View File

@ -17,13 +17,15 @@
#include "python-internal.h" #include "python-internal.h"
#include "varobj.h" #include "varobj.h"
#include "varobj-iter.h" #include "varobj-iter.h"
#include "valprint.h"
/* A dynamic varobj iterator "class" for python pretty-printed /* A dynamic varobj iterator "class" for python pretty-printed
varobjs. This inherits struct varobj_iter. */ varobjs. This inherits struct varobj_iter. */
struct py_varobj_iter : public varobj_iter 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; ~py_varobj_iter () override;
std::unique_ptr<varobj_item> next () override; std::unique_ptr<varobj_item> next () override;
@ -41,6 +43,9 @@ private:
/* The python iterator returned by the printer's 'children' method, /* The python iterator returned by the printer's 'children' method,
or NULL if not available. */ or NULL if not available. */
PyObject *m_iter; PyObject *m_iter;
/* The print options to use. */
value_print_options m_opts;
}; };
/* Implementation of the 'dtor' method of pretty-printed varobj /* Implementation of the 'dtor' method of pretty-printed varobj
@ -67,6 +72,9 @@ py_varobj_iter::next ()
gdbpy_enter_varobj enter_py (m_var); gdbpy_enter_varobj enter_py (m_var);
scoped_restore set_options = make_scoped_restore (&gdbpy_current_print_options,
&m_opts);
gdbpy_ref<> item (PyIter_Next (m_iter)); gdbpy_ref<> item (PyIter_Next (m_iter));
if (item == NULL) if (item == NULL)
@ -124,9 +132,11 @@ py_varobj_iter::next ()
whose children the iterator will be iterating over. PYITER is the whose children the iterator will be iterating over. PYITER is the
python iterator actually responsible for the iteration. */ python iterator actually responsible for the iteration. */
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_var (var),
m_iter (pyiter.release ()) m_iter (pyiter.release ()),
m_opts (*opts)
{ {
} }
@ -134,13 +144,17 @@ py_varobj_iter::py_varobj_iter (struct varobj *var, gdbpy_ref<> &&pyiter)
over VAR's children. */ over VAR's children. */
std::unique_ptr<varobj_iter> std::unique_ptr<varobj_iter>
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); gdbpy_enter_varobj enter_py (var);
if (!PyObject_HasAttr (printer, gdbpy_children_cst)) if (!PyObject_HasAttr (printer, gdbpy_children_cst))
return NULL; return NULL;
scoped_restore set_options = make_scoped_restore (&gdbpy_current_print_options,
opts);
gdbpy_ref<> children (PyObject_CallMethodObjArgs (printer, gdbpy_children_cst, gdbpy_ref<> children (PyObject_CallMethodObjArgs (printer, gdbpy_children_cst,
NULL)); NULL));
if (children == NULL) if (children == NULL)
@ -157,5 +171,6 @@ py_varobj_get_iterator (struct varobj *var, PyObject *printer)
} }
return std::unique_ptr<varobj_iter> (new py_varobj_iter (var, return std::unique_ptr<varobj_iter> (new py_varobj_iter (var,
std::move (iter))); std::move (iter),
opts));
} }

View File

@ -746,11 +746,16 @@ int gdbpy_is_value_object (PyObject *obj);
other pretty-printer functions, because they refer to PyObject. */ other pretty-printer functions, because they refer to PyObject. */
gdbpy_ref<> apply_varobj_pretty_printer (PyObject *print_obj, gdbpy_ref<> apply_varobj_pretty_printer (PyObject *print_obj,
struct value **replacement, 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); gdbpy_ref<> gdbpy_get_varobj_pretty_printer (struct value *value);
gdb::unique_xmalloc_ptr<char> gdbpy_get_display_hint (PyObject *printer); gdb::unique_xmalloc_ptr<char> gdbpy_get_display_hint (PyObject *printer);
PyObject *gdbpy_default_visualizer (PyObject *self, PyObject *args); PyObject *gdbpy_default_visualizer (PyObject *self, PyObject *args);
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_pre_stop_hook (struct gdbpy_breakpoint_object *bp_obj);
void bpfinishpy_post_stop_hook (struct gdbpy_breakpoint_object *bp_obj); void bpfinishpy_post_stop_hook (struct gdbpy_breakpoint_object *bp_obj);
@ -784,8 +789,10 @@ int gdb_pymodule_addobject (PyObject *module, const char *name,
struct varobj_iter; struct varobj_iter;
struct varobj; struct varobj;
std::unique_ptr<varobj_iter> py_varobj_get_iterator (struct varobj *var, std::unique_ptr<varobj_iter> py_varobj_get_iterator
PyObject *printer); (struct varobj *var,
PyObject *printer,
const value_print_options *opts);
/* Deleter for Py_buffer unique_ptr specialization. */ /* Deleter for Py_buffer unique_ptr specialization. */

View File

@ -2560,6 +2560,10 @@ the returned string is 'ADDRESS <SYMBOL+OFFSET>' without the quotes." },
"current_language () -> string\n\ "current_language () -> string\n\
Return the name of the currently selected language." }, Return the name of the currently selected language." },
{ "print_options", gdbpy_print_options, METH_NOARGS,
"print_options () -> dict\n\
Return the current print options." },
{NULL, NULL, 0, NULL} {NULL, NULL, 0, NULL}
}; };

View File

@ -544,10 +544,12 @@ proc_with_prefix test_nibbles {} {
"0010 1010" \ "0010 1010" \
"42 with option ${opts}" "42 with option ${opts}"
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 \ check_format_string "a_point_t_pointer" $opts \
$binary_pointer_regexp $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)"]
check_format_string "a_struct_with_union" $opts \ check_format_string "a_struct_with_union" $opts \
"\\{the_union = \\{an_int = 0010 1010 0010 1010 0010 1010 0010 1010, a_char = 0010 1010\\}\\}" "\\{the_union = \\{an_int = 0010 1010 0010 1010 0010 1010 0010 1010, a_char = 0010 1010\\}\\}"
@ -574,10 +576,12 @@ proc_with_prefix test_nibbles {} {
"0010'1010" \ "0010'1010" \
"42 with option ${opts}" "42 with option ${opts}"
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 \ check_format_string "a_point_t_pointer" $opts \
$binary_pointer_regexp $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)"]
check_format_string "a_struct_with_union" $opts \ check_format_string "a_struct_with_union" $opts \
"\\{the_union = \\{an_int = 0010'1010'0010'1010'0010'1010'0010'1010, a_char = 0010'1010\\}\\}" "\\{the_union = \\{an_int = 0010'1010'0010'1010'0010'1010'0010'1010, a_char = 0010'1010\\}\\}"
@ -598,7 +602,8 @@ proc_with_prefix test_nibbles {} {
check_format_string "a_symbol_pointer" $opts \ check_format_string "a_symbol_pointer" $opts \
$binary_pointer_regexp $binary_pointer_regexp
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 check_format_string "a_base_ref" $opts
} }
} }
@ -938,9 +943,11 @@ proc_with_prefix test_format {} {
"0x2a" \ "0x2a" \
"42 with option ${opts}" "42 with option ${opts}"
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 "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 \ check_format_string "a_struct_with_union" $opts \
"\\{the_union = \\{an_int = 0x2a2a2a2a, a_char = 0x2a\\}\\}" "\\{the_union = \\{an_int = 0x2a2a2a2a, a_char = 0x2a\\}\\}"
check_format_string "an_enum" $opts \ check_format_string "an_enum" $opts \
@ -961,7 +968,8 @@ proc_with_prefix test_format {} {
$default_pointer_regexp $default_pointer_regexp
if { $current_lang == "c++" } { if { $current_lang == "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 check_format_string "a_base_ref" $opts
} }
} }
@ -974,10 +982,12 @@ proc_with_prefix test_format {} {
"101010" \ "101010" \
"42 with option ${opts}" "42 with option ${opts}"
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 \ check_format_string "a_point_t_pointer" $opts \
$binary_pointer_regexp $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 \ check_format_string "a_struct_with_union" $opts \
"\\{the_union = \\{an_int = 101010001010100010101000101010, a_char = 101010\\}\\}" "\\{the_union = \\{an_int = 101010001010100010101000101010, a_char = 101010\\}\\}"
check_format_string "an_enum" $opts \ check_format_string "an_enum" $opts \
@ -998,7 +1008,8 @@ proc_with_prefix test_format {} {
$binary_pointer_regexp $binary_pointer_regexp
if { $current_lang == "c++" } { if { $current_lang == "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 check_format_string "a_base_ref" $opts
} }
} }
@ -1103,6 +1114,21 @@ proc test_styling {} {
"{[style x variable] = 42, [style y variable] = 12}" "{[style x variable] = 42, [style y variable] = 12}"
} }
# 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='t'" \
"Pretty Point \\(101010, 1100\\)" \
"print in binary to fetch options"
gdb_test "python print(saved_options\['format'\] == 't')" "True" \
"format was set"
}
# Run all the tests in common for both C and C++. # Run all the tests in common for both C and C++.
proc_with_prefix test_all_common {} { proc_with_prefix test_all_common {} {
# No options. # No options.
@ -1127,6 +1153,7 @@ proc_with_prefix test_all_common {} {
test_mixed test_mixed
# Various error conditions. # Various error conditions.
test_invalid_args test_invalid_args
test_print_options
} }
# The current language ("c" or "c++" while running tests). # The current language ("c" or "c++" while running tests).

View File

@ -18,12 +18,16 @@
import gdb import gdb
saved_options = {}
class PointPrinter(object): class PointPrinter(object):
def __init__(self, val): def __init__(self, val):
self.val = val self.val = val
def to_string(self): def to_string(self):
global saved_options
saved_options = gdb.print_options()
return "Pretty Point (%s, %s)" % (self.val["x"], self.val["y"]) return "Pretty Point (%s, %s)" % (self.val["x"], self.val["y"])

View File

@ -658,7 +658,11 @@ varobj_get_iterator (struct varobj *var)
{ {
#if HAVE_PYTHON #if HAVE_PYTHON
if (var->dynamic->pretty_printer) 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, &opts);
}
#endif #endif
gdb_assert_not_reached ("requested an iterator from a non-dynamic varobj"); 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; string_file stb;
std::string thevalue; std::string thevalue;
varobj_formatted_print_options (&opts, format);
#if HAVE_PYTHON #if HAVE_PYTHON
if (gdb_python_initialized) if (gdb_python_initialized)
{ {
@ -2166,7 +2172,8 @@ varobj_value_get_print_value (struct value *value,
gdbpy_ref<> output = apply_varobj_pretty_printer (value_formatter, gdbpy_ref<> output = apply_varobj_pretty_printer (value_formatter,
&replacement, &replacement,
&stb); &stb,
&opts);
/* If we have string like output ... */ /* If we have string like output ... */
if (output != NULL) if (output != NULL)
@ -2225,8 +2232,6 @@ varobj_value_get_print_value (struct value *value,
} }
#endif #endif
varobj_formatted_print_options (&opts, format);
/* If the THEVALUE has contents, it is a regular string. */ /* If the THEVALUE has contents, it is a regular string. */
if (!thevalue.empty ()) if (!thevalue.empty ())
current_language->printstr (&stb, type, (gdb_byte *) thevalue.c_str (), current_language->printstr (&stb, type, (gdb_byte *) thevalue.c_str (),