I've started experimenting with building gdb pretty printers for some of my C++ data structures, but the documentation is pretty thin.
As a result, I need to guess about how to do things, and frequently my pretty printers just crash with a non-useful python exception with no indication of where the actual problem is.
Is there any good way of debugging a pretty printer? I've had success in other python programs by inserting an explicit call to pydb in the code:
import pydb
pydb.debugger()
but that doesn't seem to work when running python in gdb -- it just runs past the debugger
call and doesn't stop or say or do anything.
in a directory of your choice (i.e. $(HOME)/distribs/gdb_printers). You will get 'python' subdirectory in the checkout directory. This makes pretty printing usable via command-line interface of gdb ( >(gdb) p my_std_string ).
Use pprint() in place of the regular print() Understand all the parameters you can use to customize your pretty-printed output. Get the formatted output as a string before printing it.
The usual way to examine data in your program is with the print command (abbreviated p ), or its synonym inspect . It evaluates and prints the value of an expression of the language your program is written in (see section Using GDB with Different Languages). expr is an expression (in the source language).
It means that the type of that variable has been incompletely specified. For example: struct hatstand; struct hatstand *foo; GDB knows that foo is a pointer to a hatstand structure, but the members of that structure haven't been defined. Hence, "incomplete type".
You can run pdb (one of the python debuggers) within gdb. Here is an excerpt of a gdb session with a simple example:
(gdb) print (ObjectSignature *) 0x7f71e4018000
$1 = (ObjectSignature *) 0x7f71e4018000
(gdb) python import pdb
(gdb) python pdb.run('gdb.execute("print $1[0]")')
> <string>(1)<module>()
(Pdb) from svtprinters.printers import ObjectSignaturePrinter
(Pdb) b ObjectSignaturePrinter.to_string
Breakpoint 1 at /svtfs/svtprinters/printers.py:195
(Pdb) c
$2 = > /svtfs/svtprinters/printers.py(196)to_string()
-> sizetypestr = 'invalid'
(Pdb) n
> /svtfs/svtprinters/printers.py(197)to_string()
-> sizetypeidx = int(self.val['mSizeType'])
(Pdb) self.val['mSizeType']
<gdb.Value object at 0x7effc90ff430>
(Pdb) int(self.val['mSizeType'])
3
(Pdb) n
> /svtfs/svtprinters/printers.py(199)to_string()
-> if sizetypeidx < len(self.sizetypes):
(Pdb) self.sizetypes
['unknown', 'meta_1K', 'data_4K', 'data_8K', 'data_16K', 'data_32K', 'data_64K']
(Pdb) n
> /svtfs/svtprinters/printers.py(200)to_string()
-> sizetypestr = self.sizetypes[sizetypeidx]
(Pdb)
> /svtfs/svtprinters/printers.py(202)to_string()
-> return (20*"%02x"+" %s") % tuple([self.val['mValue'][i] for i in range(20)]+[sizetypestr])
(Pdb) sizetypestr
'data_8K'
(Pdb) c
98d6687a2ea63a134901f0df140b13112e64bfb7 data_8K
(gdb)
In this example ObjectSignaturePrinter
is a class which is associated via gdb.pretty_printers
with the ObjectSignature
type in $1
. The output of the second print
command is split; $2 =
is printed before the pretty printer breakpoint is reached, and the rest of the output appears after the pdb
continue command.
It's likely that variations on this approach will work with other python debuggers.
I'm using IPython to explore the GDB API for the same purpose. I can start up an IPython kernel inside GDB, and then connect to it from another IPython console in another terminal. From there it's possible to interactively use the Python API in the running GDB process.
I have defined a helper command for GDB in my .gdbinit
file:
# gdb command to start an embedded ipython kernel.
# connect to it with "ipython3 console --existing ..."
# use gdb.parse_and_eval() to evaluate variables etc.
define ipython_embed
python
import sys
print(sys.version)
# helper functions
import gdb
def gdb_run(cmd):
print(gdb.execute(cmd, to_string=True))
def gdb_eval(expression):
return gdb.parse_and_eval(expression)
def gdb_vis(value):
return gdb.default_visualizer(value)
import IPython
IPython.embed_kernel()
end
# gdb command prompt is basically unusable after the ipython kernel stops, so just exit gdb
quit
end
Running it in GDB will print something like:
To connect another client to this kernel, use:
--existing kernel-2701.json
In another terminal, run ipython console
with these command line options to connect to GDB. You can then easily use the GDB API.
To debug your visualizer, get a printable expression with gdb.parse_and_eval()
(or gdb_eval()
), get the associated printer class with gdb.default_visualizer()
(or gdb_vis()
) and then call/debug your methods.
Note that you might have to install some additional packages for the necessary IPython support, and that some details might vary based on the Python and GDB versions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With