Quite a few of the codebases I've worked on have some form of linked list implementation; generally a list node type:
struct list_node { struct list_node *prev; struct list_node *next; };
... and sometimes (for type-safety) a separate type for the lists themselves:
struct list { struct list_node head; };
Debugging these lists often means a lot of manual pointer-chasing, which can be error prone. If your list iterators are causing segfaults, it'd be nice to be able to track down invalid list entries.
python gdb extensions
It turns out we can write a little code to automate this, using the gdb
module. Amongst other things, we can define our own commands to use in the standard gdb interpreter.
The skeleton code to implement a gdb command in python goes something like this:
import gdb class MyNewCommand(gdb.Command): def __init__(self): super(MyNewCommand, self).__init__("my-new-command", gdb.COMMAND_DATA, gdb.COMPLETE_SYMBOL) def invoke(self, argument, from_tty): args = gdb.string_to_argv(argument) expr = args[0] list = gdb.parse_and_eval(expr) # command implementation goes here ... MyNewCommand()
In a nutshell:
- We define a new subclass of
gdb.Command - Our new class'
__init__()
function will register the command name ("my-new-command
" in this example) with the gdb interpreter, and pass a few hints about the command's usage. - The
invoke()
function is called when the user invokes the command at the gdb interpreter interface. - The
invoke()
implementation can access the debugger's state thorugh thegdb
python module. - Finally, we create an instance of the new class to get it registered with the interpreter.
The class' docstring is used by gdb's inline help
command, which makes documenting your new command a total piece of cake.
Back to our list example, I've written a little python gdb extension to iterate and print a list:
#!/usr/bin/env python import gdb class ListPrintCommand(gdb.Command): """Iterate and print a list. list-print <EXPR> [MAX] Given a list EXPR, iterate though the list nodes' ->next pointers, printing each node iterated. We will iterate thorugh MAX list nodes, to prevent infinite loops with corrupt lists. If MAX is zero, we will iterate the entire list. List nodes types are expected to have a member named "next". List types may be the same as node types, or a separate type with an explicit head node, called "head".""" MAX_ITER = 20 def __init__(self): super(ListPrintCommand, self).__init__("list-print", gdb.COMMAND_DATA, gdb.COMPLETE_SYMBOL) def invoke(self, argument, from_tty): args = gdb.string_to_argv(argument) if len(args) == 0: print "Argument required (list to iterate)" return expr = args[0] if len(args) == 2: max_iter = int(args[1]) else: max_iter = self.MAX_ITER list = gdb.parse_and_eval(expr) fnames = [ f.name for f in list.type.fields() ] # handle lists with a separate list type.... if 'head' in fnames: head = list['head'] # ...and those with the head as a regular node elif 'next' in fnames: head = list else: print "Unknown list head type" return # if the type has a 'prev' member, we check for validity as we walk # the list check_prev = 'prev' in [ f.name for f in head.type.fields() ] print "list@%s: %s" % (head.address, head) node = head['next'] prev = head.address i = 1 while node != head.address: print "node@%s: %s #%d" % (node, node.dereference(), i) if check_prev and prev != node['prev']: print " - invalid prev pointer!" if i == max_iter: print " ... (max iterations reached)" break prev = node node = node['next'] i += 1 if check_prev and i != max_iter and head['prev'] != prev: print "list has invalid prev pointer!" ListPrintCommand()
This defines a new function, list-print
, which takes an expression as an argument, and iterates through the list nodes:
(gdb) list-print handler->devices[0]->device->boot_options list@0x6128f0: {prev = 0x6129f8, next = 0x613918} node@0x613918: {prev = 0x6128f0, next = 0x613568} #1 node@0x613568: {prev = 0x613918, next = 0x6131b8} #2 node@0x6131b8: {prev = 0x613568, next = 0x612e08} #3 node@0x612e08: {prev = 0x6131b8, next = 0x6129f8} #4 node@0x6129f8: {prev = 0x612e08, next = 0x6128f0} #5 (gdb)
To use the gdb-list macro: download gdb-list.py (2.2 kB), and source
it into your gdb session:
(gdb) source ~/devel/gdb-list/gdb-list.py
Then you should be able to invoke list-print <symbol>
to debug your list structures. Typing help list-print
will show the command's docstring:
(gdb) help list-print Iterate and print a list. list-print <EXPR> [MAX] Given a list EXPR, iterate though the list nodes' ->next pointers, printing each node iterated. We will iterate thorugh MAX list nodes, to prevent infinite loops with corrupt lists. If MAX is zero, we will iterate the entire list. List nodes types are expected to have a member named "next". List types may be the same as node types, or a separate type with an explicit head node, called "head". (gdb)
Hi, Nice article.:-) But can this script be used to print the information from linux kernel's list (e.g. struct list_head). For example the structure is something like this:
so i am able to print the content of '
struct my_struct
' ,struct my_hardware_context ahw
, but not the content of pointers and list ( e.g.struct net_device *netdev
,struct pci_dev *pdev
,struct list_head mac_list
) automatically. So can you tell me how to print these using your script ?Thanks in advance.