gdbWatchPoint

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

Written by Dr Greg Law | Last updated 31st May 2020

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.

Output - Print Info

Messy and not easy to read.

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.

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.

Do not miss my next GDB tutorial: sign up for the gdbWatchPoint mailing below.
Get tutorials straight to your inbox
Become a GDB Power User. Get Greg's debugging tips directly in your inbox every 2 weeks.
  • Concurrency Defects Resolution
    New techniques to help quickly find and resolve defects in multithreaded and multiprocess applications
    Download the technical paper

Get the latest news, tips and tricks from Undo

Sign up to our newsletter