Created
November 29, 2021 11:14
-
-
Save ZedThree/36a53f3136f43d8170cd6e0c57fc0fe2 to your computer and use it in GitHub Desktop.
GDB pretty-printer for mpark.variant based on the libstdc++ pretty-printers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Pretty-printers for mpark.variant based on those for libstdc++. | |
# Copyright (C) 2008-2021 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 <http://www.gnu.org/licenses/>. | |
import gdb | |
import re | |
# Python 3 stuff | |
Iterator = object | |
# Python 3 folds these into the normal functions. | |
imap = map | |
izip = zip | |
# Also, int subsumes long | |
long = int | |
# Try to use the new-style pretty-printing if available. | |
_use_gdb_pp = True | |
try: | |
import gdb.printing | |
except ImportError: | |
_use_gdb_pp = False | |
# Try to install type-printers. | |
_use_type_printing = False | |
try: | |
import gdb.types | |
if hasattr(gdb.types, "TypePrinter"): | |
_use_type_printing = True | |
except ImportError: | |
pass | |
def get_template_arg_list(type_obj): | |
"Return a type's template arguments as a list" | |
n = 0 | |
template_args = [] | |
while True: | |
try: | |
template_args.append(type_obj.template_argument(n)) | |
except RuntimeError: | |
return template_args | |
n += 1 | |
class SingleObjContainerPrinter(object): | |
"Base class for printers of containers of single objects" | |
def __init__(self, val, viz, hint=None): | |
self.contained_value = val | |
self.visualizer = viz | |
self.hint = hint | |
def _recognize(self, type): | |
"""Return TYPE as a string after applying type printers""" | |
global _use_type_printing | |
if not _use_type_printing: | |
return str(type) | |
return gdb.types.apply_type_recognizers( | |
gdb.types.get_type_recognizers(), type | |
) or str(type) | |
class _contained(Iterator): | |
def __init__(self, val): | |
self.val = val | |
def __iter__(self): | |
return self | |
def __next__(self): | |
if self.val is None: | |
raise StopIteration | |
retval = self.val | |
self.val = None | |
return ("[contained value]", retval) | |
def children(self): | |
if self.contained_value is None: | |
return self._contained(None) | |
if hasattr(self.visualizer, "children"): | |
return self.visualizer.children() | |
return self._contained(self.contained_value) | |
def display_hint(self): | |
# if contained value is a map we want to display in the same way | |
if hasattr(self.visualizer, "children") and hasattr( | |
self.visualizer, "display_hint" | |
): | |
return self.visualizer.display_hint() | |
return self.hint | |
class MparkVariantPrinter(SingleObjContainerPrinter): | |
"Print a mpark::variant" | |
def __init__(self, typename, val): | |
alternatives = get_template_arg_list(val.type) | |
self.typename = ( | |
f"{typename}<{', '.join([self._recognize(alt) for alt in alternatives]),}>" | |
) | |
self.index = val["impl_"]["index_"] | |
if self.index >= len(alternatives): | |
self.contained_type = None | |
contained_value = None | |
visualizer = None | |
else: | |
self.contained_type = alternatives[int(self.index)] | |
addr = val["impl_"]["data_"].address | |
contained_value = addr.cast(self.contained_type.pointer()).dereference() | |
visualizer = gdb.default_visualizer(contained_value) | |
super().__init__(contained_value, visualizer, "array") | |
def to_string(self): | |
if self.contained_value is None: | |
return f"{self.typename} [no contained value]" | |
if hasattr(self.visualizer, "children"): | |
return f"{self.typename} [index {self.index}] containing {self.visualizer.to_string}" | |
return f"{self.typename} [index {self.index}]" | |
# A "regular expression" printer which conforms to the | |
# "SubPrettyPrinter" protocol from gdb.printing. | |
class RxPrinter(object): | |
def __init__(self, name, function): | |
super(RxPrinter, self).__init__() | |
self.name = name | |
self.function = function | |
self.enabled = True | |
def invoke(self, value): | |
if not self.enabled: | |
return None | |
if value.type.code == gdb.TYPE_CODE_REF: | |
if hasattr(gdb.Value, "referenced_value"): | |
value = value.referenced_value() | |
return self.function(self.name, value) | |
# A pretty-printer that conforms to the "PrettyPrinter" protocol from | |
# gdb.printing. It can also be used directly as an old-style printer. | |
class Printer(object): | |
def __init__(self, name): | |
super(Printer, self).__init__() | |
self.name = name | |
self.subprinters = [] | |
self.lookup = {} | |
self.enabled = True | |
self.compiled_rx = re.compile("^([a-zA-Z0-9_:]+)(<.*>)?$") | |
def add(self, name, function): | |
printer = RxPrinter(name, function) | |
self.subprinters.append(printer) | |
self.lookup[name] = printer | |
@staticmethod | |
def get_basic_type(type): | |
# If it points to a reference, get the reference. | |
if type.code == gdb.TYPE_CODE_REF: | |
type = type.target() | |
# Get the unqualified type, stripped of typedefs. | |
type = type.unqualified().strip_typedefs() | |
return type.tag | |
def __call__(self, val): | |
typename = self.get_basic_type(val.type) | |
if not typename: | |
return None | |
# All the types we match are template types, so we can use a | |
# dictionary. | |
match = self.compiled_rx.match(typename) | |
if not match: | |
return None | |
basename = match.group(1) | |
if val.type.code == gdb.TYPE_CODE_REF: | |
if hasattr(gdb.Value, "referenced_value"): | |
val = val.referenced_value() | |
if basename in self.lookup: | |
return self.lookup[basename].invoke(val) | |
# Cannot find a pretty printer. Return None. | |
return None | |
mpark_variant_printer = None | |
def register_mpark_variant_printers(obj): | |
"Register libstdc++ pretty-printers with objfile Obj." | |
global _use_gdb_pp | |
global mpark_variant_printer | |
if _use_gdb_pp: | |
gdb.printing.register_pretty_printer(obj, mpark_variant_printer) | |
else: | |
if obj is None: | |
obj = gdb | |
obj.pretty_printers.append(mpark_variant_printer) | |
def build_mpark_variant_dictionary(): | |
global mpark_variant_printer | |
mpark_variant_printer = Printer("mpark") | |
mpark_variant_printer.add("mpark::variant", MparkVariantPrinter) | |
build_mpark_variant_dictionary() | |
register_mpark_variant_printers(None) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment