Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Debugging gdb pretty printers

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.

like image 987
Chris Dodd Avatar asked Nov 20 '15 23:11

Chris Dodd


People also ask

How do I enable pretty print in GDB?

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 ).

Which command is used for the best pretty printing?

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.

What does print do in GDB?

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).

What does incomplete type mean in GDB?

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".


2 Answers

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.

like image 109
Eirik Fuller Avatar answered Sep 18 '22 12:09

Eirik Fuller


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.

like image 32
mooware Avatar answered Sep 21 '22 12:09

mooware