Until recently, debuggers had limited utility for complex, high-level libraries like ITK. The built-in print capability of debuggers was limited to simple, low-level data types such as float, char* or C arrays. Examining complex C++ classes, such as an itk::Image, in an informative manner was difficult.
However, the PythonGDB project [1] has solved this problem by allowing the creation of custom pretty-printers for arbitrary data types. GDB [2] now has support for an embedded Python interpreter that hooks into much of the debugger’s functionality. In this article we describe how to use custom Python pretty-printers to improve development of ITK code.
An example
Note: The code for this example can be found in the examples subdirectory of the author’s ITK pretty-printer repository, gdb-pretty-itk [3].
Custom text printer
Run GDB on the executable that has been built with debugging symbols:
Set a break point after we have called Update() on an itk::ImageFileReader object:
ITK has written into the code Print() methods for almost all objects. This is intended to provide debugging information about the object’s state. We evaluate the next line of code to see the output of reader2D‘s Print() method:
The same output can be produced with our custom ITK pretty-printer:
Notice that in this case it is no longer necessary to have PrintSelf() methods embedded in the library’s code. We also do not need to write in the debugging statement, re-compile, re-run, and then remove the debugging statement every time we want to investigate internal behavior. We can try this on the itk::Image object:
2D Image printer
The GDB Python interface has other simple, effective, and powerful capabilities in addition to creating custom pretty-printers. For example, custom commands can be created. These commands can be loaded using 'require'. Let us load a command that allows selection of a specific pretty-printer (assuming many are available for printing the same data type):
The ‘view with’ command takes two arguments: a regular expression that specifies the module that the desired pretty-printer lookup_function is defined in (explained later), and an expression to be passed to the print command. Available modules are shown in itk-example-gdb.py. We issue:
And we immediately see the contents of the image’s BufferedRegion.

The next part of the code talks about doing some kind of beautification. What does it really do?


We see it added a beauty mark above her lip.
3D Image printer
Custom, visual pretty-printers can be generated for 3D images too. For example, iso-contours can be viewed using Mayavi [4]:

Or we can interrogate the data with an application such as ParaView or VV [5]:

Examining in an interactive IPython shell
We can dynamically explore the data in an IPython [6] shell using Matplotlib’s 2D pyplot facilities [7] or Mayavi’s 3D Mlab facilities:

This loads an IPython shell with variables in the local namespace corresponding to the printed value.
In [1]: val7
Out[1]: <enthought.mayavi.sources.vtk_file_reader.
VTKFileReader object at 0x6397fb0>
In [2]: mlab.pipeline.volume(val7)

Subregions of the image can be easily extracted and examined.
We can also save images as we progress through our program for comparison by subtraction or other exploratory methods in our IPython session.

How it works
Step 1: Define a class’s pretty-printer
A custom pretty-printer is a Python class that follows a simple API. For example, the complete itk::Object text printer is:
The class’s constructor takes a Python gdb.Value object as an argument. A to_string() method defines how to generate a string representation of the object. An optional children() method returns an object that follows the Python iterator protocol, in this case a Python list, which returns a (‘name’, value) tuple child on each iteration. The display_hint() is an optional method used by the user interface during display of the output.
Step 2: Create a lookup function
When the print command is executed in GDB, the resulting value is passed to the registered pretty-printer lookup functions. The lookup function should return either a pretty-printer class that can print the value, or the Python object None. If the lookup function returns None, GDB passes the value to the next registered lookup function. If no custom pretty printer is found to print the value, the default print behavior is executed.
A lookup function can be any Python function that performs filtering on the value. In practice, the lookup function usually consists of a few common steps focusing on the type attribute associated with the gdb.Value. If the type is a reference, it is converted to the type that it references. If the type is a typedef, it is converted to the original type. Filtering can examine template arguments. If the type is an itk::SmartPointer<>, the template argument is extracted. Finally, a pretty-printer Python dictionary associates a regular expression matching the typename to the corresponding pretty-printer class.
Step 3: Associate the lookup function with an object file
Lookup functions can be associated with GDB objfiles. Here objfiles refer to executables or libraries. A file in the same directory as the executable or library with the same name except for an appended -gdb.py gets executed when GDB loads the objfile. This script is used to register the lookup functions associated with the program or library. This allows only the pretty-printers needed by the program to be loaded, which reduces startup time. The order of the lookup functions also determines the default pretty-printer.
Further reading
The author’s GDB branch used for this article can be found on gitorious [8]. ITK pretty-printers can be found at the same location, gdb-pretty-itk. Documentation for GDB and the Python API can be generated by calling ‘make html’ in the gdb/doc directory of GDB’s source. A screencast demonstrating custom pretty printers can be found at archive.org [9].
Acknowledgements
Thanks to Tom Tromey, Jan Kratochvil, Joel Brobecker, Phil Muldoon, et al. for their work on the GDB project and helpfulness on the mailing lists and IRC.
References
[1] Work to integrate Python scripting into GDB http://sourceware.org/gdb/wiki/PythonGdb/
[2] The GNU Debugger. http://sources.redhat.com/gdb/
[3] http://gitorious.org/gdb-pretty/gdb-pretty-itk/
[4] VTK based scientific data visualizer http://code.enthought.com/projects/mayavi/
[5] VV is an open-source and cross-platform image viewer, designed for fast and simple spatio-temporal images visualization:
2D, 2D+t, 3D and 3D+t (or 4D) images http://www.creatis.insa-lyon.fr/rio/vv/
[6] An advanced interactive shell for Python. http://ipython.scipy.org/
[7] Pure Python plotting library with MATLAB like syntax. http://matplotlib.sourceforge.net/
[8] http://gitorious.org/~thewtex/gdb-python/archer-thewtex-python/commits/archer-thewtex-python-next
[9] http://www.archive.org/details/VisualDebuggingWithCustomPrettyPrinters
Matt McCormick is a PhD candidate at the University of Wisconsin-Madison in the Department of Biomedical Engineering. His research interests include diagnostic ultrasound strain imaging and parametric tissue characterization. He enjoys scientific computing and is an active member of UW-Madison Hacker Within (hackerwithin.org).