WatchPoint

Image link

Here is a quick way to pretty-print structures in GDB

Using pretty-printers can save you a lot of time staring at your computer screen and improve the flow of your debugging too!

In this tutorial, I start off with writing a basic pretty-printer to display the value of the si_signo field in the siginfo_t structure, before doing some more advanced stuff.

 

Why we need pretty-printers

We love debugging in GDB. We all use structures and classes in the code we write, and GDB strives to display what these are but misses the contextual information; when you use a handful of members, and in particular unions, then interpreting these more complicated structures quickly becomes overwhelming.

When GDB prints a value, it first checks whether there is a pretty-printer registered for that value. If there is, then GDB uses the pretty-printer to display the value. Otherwise, the value prints in the usual way.

For example, in my previous tutorial, I used the print info command to confirm whether the inferior program received my Ctrl-C. But, because I didn’t have a pretty-printer for the siginfo_t structure, the command returned all the data of the structure, including expanding the many unions that structure uses.

 

Messy and not easy to read.

 

Time Travel Debugging Demo Video Banner

See how easily you can write a basic pretty-printer

To start off, we write a basic pretty-printer that returns the si_signo value from the siginfo_t structure for an interrupt signal that the inferior program receives.

Almost every pretty-printer comprises of two main elements:

  • The lookup function to identify the value type, and
  • the printer function itself.

We write our program in Python.

$ vim prettyprint.py
# Start off with defining the printer as a Python object.

class SiginfoPrinter:

  # The constructor takes the value and stores it for later.

  def __init__(self, val):
    self.val = val

  # The to_string method returns the value of the
  # si_signo attribute of the directory.

  def to_string(self):
    signo = self.val['si_signo']
    return str(signo)

# Next, define the lookup function that returns the
# printer object when it receives a siginfo_t.

# The function takes the GDB value-type, which, in
# our example is used to look for the siginfo_t.

def my_pp_func(val):
  if str(val.type) == 'siginfo_t': return SiginfoPrinter(val)

# Finally, append the pretty-printer as object/ function to 
# the list of registered GDB printers.

gdb.pretty_printers.append(my_pp_func)

# Our pretty-printer is now available when we debug 
# the inferior program in GDB.

Now, run your inferior program and hit Ctrl-C to quit. Our pretty-printer returns the value 2; the value for a SIGINT.

(gdb) source prettyprint.py
(gdb) print info
$4 = 2
(gdb)

Much easier to read.

I demonstrate this basic pretty-printer in my video. Do watch it here.

Of course, we can and should improve our printer. In my next example, I print the textual name of the si_signo member. I use a file called signames,  which simply contains a list of the signal numbers with their corresponding textual signal names.

class SiginfoPrinter:

  def _init_(self, val):
    self.val = val

  # Watch the variable type that you print.
  # signames is an object and takes an integer variable.

  def to_string(self):
    signo = int (self.val['si_signo'])
    signame = signames[signo]
    return str(signo) + ' ('+ signame +')'

def my_pp_func(val):
  if str(val.type) == 'siginfo_t': return SiginfoPrinter(val)

gdb.pretty_printers.append(my_pp_func)

When we run our inferior program again and hit Ctrl-C to quit, now the printer returns the value 2, plus the textual name SIGINT.

(gdb) source signames.py
(gdb) source prettyprint.py
(gdb) print info
$5 = 2 (SIGINT)
(gdb)

That’s pretty easy.

 

New call-to-action


Are you ready to build out the pretty-printer some more?

The possibilities are endless. You can extract and print any value of a member in a structure with your pretty-printer program. In my next example, I use the code field to identify the kill signal, but also return the sender of that kill command.

class SiginfoPrinter:

  def __init__(self, val):
    self.val = val

  def to_string(self):
    signo = int(self.val['si_signo'])

    # Determine the signal with the code member,
    # for example, 0 = kill, and 128 = Ctrl-C.

    code = int(self.val['si_code'])
    signame = signames[signo]

    # Determine sender of the kill with the union _sifields

    sender = self.val['_sifields']['_kill']['si_pid']

    # If the signal is a kill (code =0) then print sender.

    if code == 0:
      return str(signo) + ' ('+ signame +') sender = '+ str(sender)

    # For any other signal print the code.

    return str(signo) + ' ('+ signame +') code = '+ str(code)

def my_pp_func(val):
  if str(val.type) == 'siginfo_t': return SiginfoPrinter(val)

db.pretty_printers.append(my_pp_func)

 

Writing pretty-printers pays off

That is it. We now have a pretty-printer for the siginfo_t structure. But, of course, you can extrapolate this handy printer to display whatever structure you want.

Investing a very small amount of time in creating your pretty-printers upfront pays off quickly and almost certainly moves up your debugging a couple of gears. You will find that you become more productive and write code you can be truly proud of.

Consider making a gdbinit per project, and committing to your project’s source control so that everyone working on that project can benefit.

Job done!

In my video, I do explain and run all the code I have written in this tutorial, so I do encourage you to watch it here.

 

GDB Training
Master GDB and save time debugging. 1-2 day on-site course for teams of C++ engineers working on complex codebases.
Learn more »

 

Learn more about pretty printers

Want to know more about what you can do with pretty printers? Check out Part 2

Sign-up Today

Do not miss my next GDB tutorial: sign up for the gdbWatchPoint mailing below.

Don’t miss my next C++ debugging tutorial: sign up to my WatchPoint mailing list below.
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