From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-il1-x12c.google.com (mail-il1-x12c.google.com [IPv6:2607:f8b0:4864:20::12c]) by sourceware.org (Postfix) with ESMTPS id 37CAA3858C62 for ; Mon, 11 Sep 2023 17:28:28 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 37CAA3858C62 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=adacore.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=adacore.com Received: by mail-il1-x12c.google.com with SMTP id e9e14a558f8ab-34e26daab46so14330945ab.0 for ; Mon, 11 Sep 2023 10:28:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=adacore.com; s=google; t=1694453307; x=1695058107; darn=sourceware.org; h=to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=I9yH0U1EOzwrHpIuyLU/QDfZJCrPnDige3flyZEQJOg=; b=btlgYQRbnB8+5pkL1VQ/CNh0tad4oBJWKdACsCQxJY5HJ+kzcnfr4ofBxf1IWid95J 7TeTxBEk5ZPF7K1KcObiXd/VgRn3i3LcNdpGxW7iDROquxoooiQA17VWsAtTlPGmYn8t ZbPOsWp882MOoxDRO6Jo7go9eX95p8Pb4dpdTjCtL4I1yf3eSRmB/iJD0bo9Buh5g/T1 3/L8wxX/I9ATDPrLERLt75mr7zTh5ZPNJ1NqOkouabwpg0Jgva6DDB2ITWUkAOMYxaoh HzzIAmvhWfX0gMWB3575qCwG2fnrk8CTMzk5WG4zPaZPMi32JOjpJ4Wd9m2h/9Rpuvom rqjg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1694453307; x=1695058107; h=to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=I9yH0U1EOzwrHpIuyLU/QDfZJCrPnDige3flyZEQJOg=; b=VEJxB3ne7QDMmlqNsPDLPi7pHnyJi5NhYINGcFk3+XXIWpgBUWlQdR2saWOQEB+J2G 2qD1OlMcS8nQbkv5taf/4lZKvlNRCJsOUifkjkThfSWDakElYhW6IKyzMeoGHEyc6qI6 fNZx2aJclWSrInkR4WjmYsueZSvnAW2tX+vgNYtyLDcT+/a84r5mKxgfSAOZAR4FHTKc mBmA0Y8ycPh9QlqLzck71VuremF3R/4rl8N3MqiXrapCFYJZBfcXwlU80ls6Jcua3ww1 jomwXwdrhtbZDfxIc+/WQc2JdJ47sUDFqea9vkl4qNel+oHP5OkE3FHAyWF0X5mzRAYl wlgA== X-Gm-Message-State: AOJu0YwGooPta+uXAcvJfsd+IMiQT6CZiuaTmcGsK8A+Hyy5Lc4Pwu7M ROKZCI9vzdH5m3N7PR3qJHs9o2b4M5wYgfHoKhckUg== X-Google-Smtp-Source: AGHT+IGecQdzA5REeeHRFvY7SFMBT2ne35dxQTkm5Ib6v7CMpKbcu/Cj8T/g+abLHMB3atdWy/PjTQ== X-Received: by 2002:a05:6e02:1c0c:b0:34c:d86f:46e9 with SMTP id l12-20020a056e021c0c00b0034cd86f46e9mr13230891ilh.12.1694453307332; Mon, 11 Sep 2023 10:28:27 -0700 (PDT) Received: from localhost.localdomain (71-211-130-31.hlrn.qwest.net. [71.211.130.31]) by smtp.gmail.com with ESMTPSA id j18-20020a056e02015200b0034989674ad7sm2455324ilr.52.2023.09.11.10.28.26 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 11 Sep 2023 10:28:26 -0700 (PDT) From: Tom Tromey Date: Mon, 11 Sep 2023 11:28:25 -0600 Subject: [PATCH 1/2] Introduce gdb.ValuePrinter MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20230911-pp-v2-v1-1-c4e0d40c8b63@adacore.com> References: <20230911-pp-v2-v1-0-c4e0d40c8b63@adacore.com> In-Reply-To: <20230911-pp-v2-v1-0-c4e0d40c8b63@adacore.com> To: gdb-patches@sourceware.org X-Mailer: b4 0.12.3 X-Spam-Status: No, score=-11.7 required=5.0 tests=BAYES_00,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,KAM_SHORT,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: 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 --- gdb/NEWS | 5 +++ gdb/doc/python.texi | 52 ++++++++++++++------- gdb/python/lib/gdb/printer/bound_registers.py | 9 ++-- gdb/python/lib/gdb/printing.py | 40 ++++++++--------- gdb/python/py-prettyprint.c | 65 +++++++++++++++++++++++++++ 5 files changed, 131 insertions(+), 40 deletions(-) diff --git a/gdb/NEWS b/gdb/NEWS index 98ff00d5efc..cca86d82de8 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -286,6 +286,11 @@ info main 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 5b13958aeaf..d09f5e03997 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 of 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 08f30cbf286..b5298b9ee28 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 . +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 1a761a6aa57..80851590134 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("" % 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,21 +270,21 @@ 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 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 @@ -302,8 +302,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 "" @@ -312,24 +312,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 cccc94e319b..7a43d9d7881 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); -- 2.40.1