WatchPoint
Debugging with pretty printers in GDB – part 3
In this tutorial, Software Architect Mark Williamson follows on from our previous tutorial on advanced pretty-printers for GDB, showing how to configure and control the behaviour of your printers. Read this article alongside the example code from the previous tutorial and the GDB documentation on writing pretty printers. In our previous tutorial on GDB pretty-printers, we wrote code to display the data structures used in an imaginary vector graphics program. Using these, we were able to provide clearer presentation of our data and to automatically walk data structures for display. With our pretty printers loaded we can, for instance, print our But what if we actually want the default GDB representation, including all the fields? Pretty printers aren’t obliged to show us the complete contents of the structure – indeed, omitting rarely-needed information is one reason to write a pretty-printer. We can revert to the default GDB formatting for just this command using the This allows us to view the raw data on a case-by-case basis without losing the benefit of pretty-printing in other contexts. We’ll sometimes want to temporarily turn off a particular pretty-printer – for instance, when we know we’re interested in the detailed contents of a structure during a debug session. GDB provides a set of commands to list and control pretty-printers. First of all, lets see what pretty printers are registered in our system. Use the After the list of builtin printers, we can see the We can now switch this pretty-printer on and off, by name: Having disabled our pretty-printers we will now see the raw, default GDB formatting when we print our Since all our pretty printers are handled by the one callback, this setting will apply to any other structures that were handled by our pretty-printers. For instance, our The corresponding command can be used to re-enable them: UDB Time Travel Debugger We’ve seen that we can control our pretty-printers by switching on and off the top-level callback we registered for them. The user experience isn’t great, though. Firstly, the name In this section, we’ll learn how to build configurability into pretty printers using the APIs GDB provides. Hint: the way we show here offers most flexibility but, if you’re looking for a quick fix, you have permission skip to the end! GDB provides additional classes within the In fact, note that we have not needed to use the The To add listing and configuration to our This class encapsulates knowledge of the name we want to display to the user – We’re seeing our new printer as a separate entry. We can use It’s not really necessary to define such a control class for each pretty-printer we’ve written. We can use a little Python magic to simplify things: Here, we’ve defined a We can use this This automatically adds a lookup function that will call this printer for instances of the C By adding this decorator to all of our classes, we create a library of named and configurable pretty printers: Each printer here can be enabled and disabled individually, by name, using We’ve seen how to build a configurable library of pretty-printers from the ground up using GDB’s We have the full power of Python available, so enormous flexibility is possible here. Use the By building on these techniques you can build a sophisticated library of printers whilst retaining fine-grained control of display. The source of the examples shown above can be downloaded for reference: In practice, our needs are often simple – a mapping of C type names to printer classes is likely to be adequate. In our example above we built this from lower-level components, allowing further customisation to be built through additional code. However, for most straightforward mappings of types to printers GDB’s This class achieves a similar goal to our example code (with the additional ability to specify regular expression matches for C types) and is likely to be a good place to start your journey. Visit the GDB docs example of registering simple pretty printers and scroll to the bottom for a concrete example. GDB TrainingIn this Tutorial
Printing What You Need
point_t
structure and see a friendly representation of it:(gdb) print p
$2 = Point(1, 2)
/r
formatting switch:(gdb) print /r p
$3 = {
x = 1,
y = 2
}
Controlling Pretty Printers
Listing Pretty Printers
info pretty-printer
command. In a debug session of our vector drawing example, this gives us:(gdb) info pretty-printer
global pretty-printers:
builtin
mpx_bound128
my_pp_func
my_pp_func
callback we registered in our previous tutorial. This was a single function, registered with gdb.pretty_printers.append()
, which we made return the correct printer for each structure we could handle. Since there’s only one callback, its represented in this list as if it were a single pretty-printer. By default, GDB identifies it by the callback’s function name.Switching Off Our Pretty Printer
(gdb) disable pretty-printer global my_pp_func
1 printer disabled
1 of 2 printers enabled
point_t
structure, without having to specify /r
:(gdb) print p
$7 = {
x = 1,
y = 2
}
drawing_element_t
:(gdb) print last
$4 = {
kind = ELEMENT_POINT,
el = {
point = 0x7fffffffd988,
line = 0x7fffffffd988
},
next = 0x0
}
(gdb) enable pretty-printer global my_pp_func
1 printer enabled
2 of 2 printers enabled
Find and fix test failures in minutes – including C/C++ race conditions, deadlocks and memory corruptions
Learn more »Flexible Control
my_pp_func
is not very descriptive and leaks information about our Python code. Secondly, we’ve ended up with just one setting to toggle all of our pretty-printers at once. As we write more pretty-printers, we’re going to want to control them more precisely.Meet the gdb.printing classes
gdb.printing
module that can make our printers easier to use (see the official gdb.printing documentation). Interestingly, this contains a class called PrettyPrinter
. We will need to use this but beware: it isn’t simply a base class for pretty printers.PrettyPrinter
class at all so far. It shouldn’t be confused with the core implementation of a pretty-printer, which is just a plain Python object that implements the pretty-printer API:class MyPrinter:
def __init__(self, val):
...
def to_string(self):
...
def children(self):
...
def display_hint(self):
...
PrettyPrinter
class, on the other hand, is used to describe a pretty printer to GDB. It makes it possible to look up and control pretty-printer implementations by name. For more sophisticated uses, it’s possible to use combinations of this and SubPrettyPrinter
directly to create hierarchies of printers (though that’s beyond the scope of this article).point_t
printer class (PointPrinter
), we could add something like the following to our printers.py
:class PointPrinterControl(gdb.printing.PrettyPrinter):
def __init__(self):
super().__init__('point_t printer')
def __call__(self, val):
return PointPrinter(val) if val.type.name == 'point_t' else None
gdb.pretty_printers.append(PointPrinterControl())
point_t printer
– and the lookup behaviour previously provided by my_pp_func()
(which no longer needs to handle this type). If we now attempt to list our pretty-printers we get a more interesting result:(gdb) info pretty-printer
global pretty-printers:
builtin
mpx_bound128
my_pp_func
point_t printer
enable / disable pretty-printer global "point_t printer"
to turn it on and off, independently of our other printers.Wrapping it all up
class PrinterControl(gdb.printing.PrettyPrinter):
def __init__(self, type_name, printer):
super().__init__(type_name)
self.printer = printer
def __call__(self, val):
return self.printer(val) if val.type.name == self.name else None
def type_printer(type_name):
# Decorator for pretty printers.
def _register_printer(printer):
gdb.printing.register_pretty_printer(
None,
PrinterControl(type_name, printer)
)
return _register_printer
PrinterControl
class that can handle any of our existing printers. It takes the C type name and the printer itself as constructor arguments; we are reusing the C type name as the name of the pretty-printer when we register it with GDB.@type_printer()
decorator to annotate any of our printers and make it visible to GDB’s printer configuration. Applying this to our PointPrinter
looks like:@type_printer('point_t')
class PointPrinter:
...
point_t
type and, via the PrettyPrinter
class, adds configurability.Bringing it all together
(gdb) info pretty-printer
global pretty-printers:
builtin
mpx_bound128
drawing_element_t
line_t [disabled]
point_t
enable / disable pretty-printer global TYPE_NAME
. In the example shown above, the line_t
printer has been separately disabled.The Power Of Configuration
PrettyPrinter
class – on top of our previous examples, we’ve added naming support and the ability to control individual printers.SubPrettyPrinter
and other provided classes to create more sophisticated hierarchies of printers. Use custom code to provide more complex registration and lookup behaviours.Bonus – A shortcut
RegexpCollectionPrettyPrinter
class will already provide the best solution.
1-2 day on-site course for teams of C++ engineers. Master GDB and save time debugging complex codebases.
Learn more »
Get tutorials straight to your inbox
Become a GDB Power User. Get Greg’s debugging tips directly in your inbox every 2 weeks.
Want GDB pro tips directly in your inbox?
Share this tutorial