aboutsummaryrefslogtreecommitdiff
path: root/gdb
diff options
context:
space:
mode:
authorTom Tromey <tromey@adacore.com>2023-09-07 13:40:29 -0600
committerTom Tromey <tromey@adacore.com>2023-09-26 09:29:14 -0600
commitfb282576998ca7ce70526dea42d41a7f418879c9 (patch)
tree9614218e6679bdfb2c28f2962ac16b16ea8f06a2 /gdb
parent854f72b36dc72dc23b24563c5e7f43efc373b712 (diff)
downloadgdb-fb282576998ca7ce70526dea42d41a7f418879c9.zip
gdb-fb282576998ca7ce70526dea42d41a7f418879c9.tar.gz
gdb-fb282576998ca7ce70526dea42d41a7f418879c9.tar.bz2
Introduce gdb.ValuePrinter
There was an earlier thread about adding new methods to pretty-printers: https://sourceware.org/pipermail/gdb-patches/2023-June/200503.html We've known about the need for printer extensibility for a while, but have been hampered by backward-compatibilty concerns: gdb never documented that printers might acquire new methods, and so existing printers may have attribute name clashes. To solve this problem, this patch adds a new pretty-printer tag class that signals to gdb that the printer follows new extensibility rules. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30816 Reviewed-By: Eli Zaretskii <eliz@gnu.org>
Diffstat (limited to 'gdb')
-rw-r--r--gdb/NEWS5
-rw-r--r--gdb/doc/python.texi52
-rw-r--r--gdb/python/lib/gdb/printer/bound_registers.py9
-rw-r--r--gdb/python/lib/gdb/printing.py48
-rw-r--r--gdb/python/py-prettyprint.c65
5 files changed, 135 insertions, 44 deletions
diff --git a/gdb/NEWS b/gdb/NEWS
index 957b5a9..5b9ca9b 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -293,6 +293,11 @@ show tui mouse-events
might be array- or string-like, even if they do not have the
corresponding type code.
+ ** gdb.ValuePrinter is a new class that can be used as the base
+ class for the result of applying a pretty-printer. As a base
+ class, it signals to gdb that the printer may implement new
+ pretty-printer methods.
+
*** Changes in GDB 13
* MI version 1 is deprecated, and will be removed in GDB 14.
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 82fa0e3..2aaf101 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -1722,6 +1722,22 @@ A pretty-printer is just an object that holds a value and implements a
specific interface, defined here. An example output is provided
(@pxref{Pretty Printing}).
+Because @value{GDBN} did not document extensibility for
+pretty-printers, by default @value{GDBN} will assume that only the
+basic pretty-printer methods may be available. The basic methods are
+marked as such, below.
+
+To allow extensibility, @value{GDBN} provides the
+@code{gdb.ValuePrinter} base class. This class does not provide any
+attributes or behavior, but instead serves as a tag that can be
+recognized by @value{GDBN}. For such printers, @value{GDBN} reserves
+all attributes starting with a lower-case letter. That is, in the
+future, @value{GDBN} may add a new method or attribute to the
+pretty-printer protocol, and @code{gdb.ValuePrinter}-based printers
+are expected to handle this gracefully. A simple way to do this would
+be to use a leading underscore (or two, following the Python
+name-mangling scheme) to any attributes local to the implementation.
+
@defun pretty_printer.children (self)
@value{GDBN} will call this method on a pretty-printer to compute the
children of the pretty-printer's value.
@@ -1732,8 +1748,8 @@ two elements. The first element is the ``name'' of the child; the
second element is the child's value. The value can be any Python
object which is convertible to a @value{GDBN} value.
-This method is optional. If it does not exist, @value{GDBN} will act
-as though the value has no children.
+This is a basic method, and is optional. If it does not exist,
+@value{GDBN} will act as though the value has no children.
For efficiency, the @code{children} method should lazily compute its
results. This will let @value{GDBN} read as few elements as
@@ -1751,8 +1767,8 @@ formatting of a value. The result will also be supplied to an MI
consumer as a @samp{displayhint} attribute of the variable being
printed.
-This method is optional. If it does exist, this method must return a
-string or the special value @code{None}.
+This is a basic method, and is optional. If it does exist, this
+method must return a string or the special value @code{None}.
Some display hints are predefined by @value{GDBN}:
@@ -1784,6 +1800,8 @@ display rules.
@value{GDBN} will call this method to display the string
representation of the value passed to the object's constructor.
+This is a basic method, and is optional.
+
When printing from the CLI, if the @code{to_string} method exists,
then @value{GDBN} will prepend its result to the values returned by
@code{children}. Exactly how this formatting is done is dependent on
@@ -1904,17 +1922,19 @@ if the type is supported, and the printer itself.
Here is an example showing how a @code{std::string} printer might be
written. @xref{Pretty Printing API}, for details on the API this class
-must provide.
+must provide. Note that this example uses the @code{gdb.ValuePrinter}
+base class, and is careful to use a leading underscore for its local
+state.
@smallexample
-class StdStringPrinter(object):
+class StdStringPrinter(gdb.ValuePrinter):
"Print a std::string"
def __init__(self, val):
- self.val = val
+ self.__val = val
def to_string(self):
- return self.val['_M_dataplus']['_M_p']
+ return self.__val['_M_dataplus']['_M_p']
def display_hint(self):
return 'string'
@@ -2005,25 +2025,25 @@ struct bar @{ struct foo x, y; @};
Here are the printers:
@smallexample
-class fooPrinter:
+class fooPrinter(gdb.ValuePrinter):
"""Print a foo object."""
def __init__(self, val):
- self.val = val
+ self.__val = val
def to_string(self):
- return ("a=<" + str(self.val["a"]) +
- "> b=<" + str(self.val["b"]) + ">")
+ return ("a=<" + str(self.__val["a"]) +
+ "> b=<" + str(self.__val["b"]) + ">")
-class barPrinter:
+class barPrinter(gdb.ValuePrinter):
"""Print a bar object."""
def __init__(self, val):
- self.val = val
+ self.__val = val
def to_string(self):
- return ("x=<" + str(self.val["x"]) +
- "> y=<" + str(self.val["y"]) + ">")
+ return ("x=<" + str(self.__val["x"]) +
+ "> y=<" + str(self.__val["y"]) + ">")
@end smallexample
This example doesn't need a lookup function, that is handled by the
diff --git a/gdb/python/lib/gdb/printer/bound_registers.py b/gdb/python/lib/gdb/printer/bound_registers.py
index 08f30cb..b5298b9e 100644
--- a/gdb/python/lib/gdb/printer/bound_registers.py
+++ b/gdb/python/lib/gdb/printer/bound_registers.py
@@ -14,18 +14,19 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import gdb
import gdb.printing
-class MpxBound128Printer:
+class MpxBound128Printer(gdb.ValuePrinter):
"""Adds size field to a mpx __gdb_builtin_type_bound128 type."""
def __init__(self, val):
- self.val = val
+ self.__val = val
def to_string(self):
- upper = self.val["ubound"]
- lower = self.val["lbound"]
+ upper = self.__val["ubound"]
+ lower = self.__val["lbound"]
size = upper - lower
if size > -1:
size = size + 1
diff --git a/gdb/python/lib/gdb/printing.py b/gdb/python/lib/gdb/printing.py
index c9af566..21afa4d 100644
--- a/gdb/python/lib/gdb/printing.py
+++ b/gdb/python/lib/gdb/printing.py
@@ -220,16 +220,16 @@ class RegexpCollectionPrettyPrinter(PrettyPrinter):
# A helper class for printing enum types. This class is instantiated
# with a list of enumerators to print a particular Value.
-class _EnumInstance:
+class _EnumInstance(gdb.ValuePrinter):
def __init__(self, enumerators, val):
- self.enumerators = enumerators
- self.val = val
+ self.__enumerators = enumerators
+ self.__val = val
def to_string(self):
flag_list = []
- v = int(self.val)
+ v = int(self.__val)
any_found = False
- for e_name, e_value in self.enumerators:
+ for e_name, e_value in self.__enumerators:
if v & e_value != 0:
flag_list.append(e_name)
v = v & ~e_value
@@ -237,7 +237,7 @@ class _EnumInstance:
if not any_found or v != 0:
# Leftover value.
flag_list.append("<unknown: 0x%x>" % v)
- return "0x%x [%s]" % (int(self.val), " | ".join(flag_list))
+ return "0x%x [%s]" % (int(self.__val), " | ".join(flag_list))
class FlagEnumerationPrinter(PrettyPrinter):
@@ -270,35 +270,35 @@ class FlagEnumerationPrinter(PrettyPrinter):
return None
-class NoOpScalarPrinter:
+class NoOpScalarPrinter(gdb.ValuePrinter):
"""A no-op pretty printer that wraps a scalar value."""
def __init__(self, value):
- self.value = value
+ self.__value = value
def to_string(self):
- return self.value.format_string(raw=True)
+ return self.__value.format_string(raw=True)
-class NoOpPointerReferencePrinter:
+class NoOpPointerReferencePrinter(gdb.ValuePrinter):
"""A no-op pretty printer that wraps a pointer or reference."""
def __init__(self, value):
- self.value = value
+ self.__value = value
self.num_children = 1
def to_string(self):
- return self.value.format_string(deref_refs=False)
+ return self.__value.format_string(deref_refs=False)
def children(self):
- yield "value", self.value.referenced_value()
+ yield "value", self.__value.referenced_value()
-class NoOpArrayPrinter:
+class NoOpArrayPrinter(gdb.ValuePrinter):
"""A no-op pretty printer that wraps an array value."""
def __init__(self, ty, value):
- self.value = value
+ self.__value = value
(low, high) = ty.range()
# In Ada, an array can have an index type that is a
# non-contiguous enum. In this case the indexing must be done
@@ -316,8 +316,8 @@ class NoOpArrayPrinter:
# This is a convenience to the DAP code and perhaps other
# users.
self.num_children = high - low + 1
- self.low = low
- self.high = high
+ self.__low = low
+ self.__high = high
def to_string(self):
return ""
@@ -326,24 +326,24 @@ class NoOpArrayPrinter:
return "array"
def children(self):
- for i in range(self.low, self.high + 1):
- yield (i, self.value[i])
+ for i in range(self.__low, self.__high + 1):
+ yield (i, self.__value[i])
-class NoOpStructPrinter:
+class NoOpStructPrinter(gdb.ValuePrinter):
"""A no-op pretty printer that wraps a struct or union value."""
def __init__(self, ty, value):
- self.ty = ty
- self.value = value
+ self.__ty = ty
+ self.__value = value
def to_string(self):
return ""
def children(self):
- for field in self.ty.fields():
+ for field in self.__ty.fields():
if field.name is not None:
- yield (field.name, self.value[field])
+ yield (field.name, self.__value[field])
def make_visualizer(value):
diff --git a/gdb/python/py-prettyprint.c b/gdb/python/py-prettyprint.c
index cccc94e..7a43d9d 100644
--- a/gdb/python/py-prettyprint.c
+++ b/gdb/python/py-prettyprint.c
@@ -27,6 +27,8 @@
#include "python-internal.h"
#include "cli/cli-style.h"
+extern PyTypeObject printer_object_type;
+
/* Return type of print_string_repr. */
enum gdbpy_string_repr_result
@@ -779,3 +781,66 @@ gdbpy_get_print_options (value_print_options *opts)
else
get_user_print_options (opts);
}
+
+/* A ValuePrinter is just a "tag", so it has no state other than that
+ required by Python. */
+struct printer_object
+{
+ PyObject_HEAD
+};
+
+/* The ValuePrinter type object. */
+PyTypeObject printer_object_type =
+{
+ PyVarObject_HEAD_INIT (NULL, 0)
+ "gdb.ValuePrinter", /*tp_name*/
+ sizeof (printer_object), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ 0, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "GDB value printer object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyType_GenericNew, /* tp_new */
+};
+
+/* Set up the ValuePrinter type. */
+
+static int
+gdbpy_initialize_prettyprint ()
+{
+ if (PyType_Ready (&printer_object_type) < 0)
+ return -1;
+ return gdb_pymodule_addobject (gdb_module, "ValuePrinter",
+ (PyObject *) &printer_object_type);
+}
+
+GDBPY_INITIALIZE_FILE (gdbpy_initialize_prettyprint);