Debugging gdb pretty printers
Asked Answered
A

2

8

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.

Ab answered 20/11, 2015 at 23:20 Comment(1)
Make sure you set python print-stack full. That will get you full stack traces from exceptions. I never tried a python debugger for my pretty-printers; I mostly ended up doing print debugging.Culch
B
10

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.

Bebeeru answered 25/4, 2016 at 15:7 Comment(4)
When I try this, I can get as far as the (Pdb) prompt, but when I try to set a breakpoint with b MyObjectPrinter.to_string it just gives me the message End of file. continuing give me the same gdb.error with no chance to figure out what is wrong. Trying to step just buries me in gdb's python codeAb
I edited my answer to use an import statement and a simpler breakpoint command. If I omit that import statement and use the simpler breakpoint command I get this output: *** The specified object 'ObjectSignaturePrinter.to_string' is not a function or was not found along sys.path.. I don't have an explanation for why your breakpoint command reports the error it reports, but it might be informative to experiment with that. If you type just the function name (omit the b), that should tell you whether pdb recognizes that name.Bebeeru
I found the apparent source of the End of file message, in Pdb.checkline (/usr/lib/python2.7/pdb.py on my system, for the gdb binary I'm using for this). I'm not sure why it would find a line it doesn't consider executable if you specify just the function name. Even if I replace my to_string method to have just a pass statement, or just a string and nothing else, the breakpoint works for me. As before, it might be helpful to know how pdb interprets that function name. For me it shows as an unbound method after the import (I get a NameError if I try before the import).Bebeeru
I get this End of file error for some functions, too - all which are in a separate class (Pdb) import MyPrinters + (Pdb) b PrinterClass.myfunc --> End of file. To solve this; (Pdb) from MyPrinters import PrinterClass, then the follow-up (Pdb) b PrinterClass.myfunc leads to "Breakpoint 1 at /home/me/MyPrinters.py:847".Cholera
C
2

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.

Cowitch answered 2/11, 2018 at 22:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.