GDB 7.0 onwards supports hooks written in Python to improve the quality-of-life within the debugger. The best known are the pretty-printing hooks [1], which we already use within libstdc++ for printing better representations of STL containers.
I've written debug hooks for when the inferior process is *GCC*, which I'm attaching. Here's a debug session of cc1 using them, with some comments added by hand: [david@surprise gcc]$ gdb \ -iex "add-auto-load-safe-path ." \ --args ./cc1 foo.c -O3 (..snip gdb startup...) Successfully loaded GDB hooks for GCC (gdb) break expand_gimple_stmt Breakpoint 5 at 0x68e94a: file ../../src/gcc/cfgexpand.c, line 2357. (gdb) run Breakpoint 5, expand_gimple_stmt (stmt= <gimple_assign 0x7ffff0440058>) at ../../src/gcc/cfgexpand.c:2357 2357 location_t saved_location = input_location; (gdb) bt #0 expand_gimple_stmt (stmt=<gimple_assign 0x7ffff0440058>) at ../../src/gcc/cfgexpand.c:2357 (note how stmt isn't just an address, it tells you the type of stmt) #1 0x0000000000694dc6 in expand_gimple_basic_block (bb= <basic_block 0x7ffff041f208 (3)>, disable_tail_calls=false) at ../../src/gcc/cfgexpand.c:4204 (note how the index of the basic block is given) #2 0x0000000000696855 in gimple_expand_cfg () at ../../src/gcc/cfgexpand.c:4723 #3 0x00000000009b59ad in execute_one_pass (pass= <opt_pass* 0x188b600 "expand"(170)>) at ../../src/gcc/passes.c:1995 #4 0x00000000009b5bb7 in execute_pass_list (pass= <opt_pass* 0x188b600 "expand"(170)>) at ../../src/gcc/passes.c:2047 (note how the name and static_pass_number of the pass is given) #5 0x00000000006c288a in expand_function (node= <cgraph_node* 0x7ffff0312720 "foo">) at ../../src/gcc/cgraphunit.c:1594 (note how the cgraph_node* is printed with the decl name, so you can easily figure out which function you're in) (gdb) fin Run till exit from #0 expand_gimple_stmt (stmt= <gimple_assign 0x7ffff0440058>) at ../../src/gcc/cfgexpand.c:2357 0x0000000000694dc6 in expand_gimple_basic_block (bb= <basic_block 0x7ffff041f208 (3)>, disable_tail_calls=false) at ../../src/gcc/cfgexpand.c:4204 4204 last = expand_gimple_stmt (stmt); Value returned is $3 = (note 9 8 10 [bb 3] NOTE_INSN_BASIC_BLOCK) (note how the rtx_def* is printed inline. This last one is actually a kludge; all the other pretty-printers work inside gdb, whereas this one injects a call into print-rtl.c into the inferior). Edges are printed giving the endpoints: (gdb) p e $1 = <edge 0x7ffff043f118 (ENTRY -> 6)> Note that this doesn't eliminate the .gdbinit script that some people use, but it's arguably far superior, in that it happens automatically, and for all values that are printed e.g. in backtraces, viewing struct fields, etc. I'd like to get this into trunk. Some remaining issues: * installation; currently one has to manually copy it into place, as noted above; it would be nice to automate things so that during a build it gets copied to cc1-gdb.py, cc1plus-gdb.py etc * it hardcoded values in a few places rather than correctly looking up enums * the kludge in the RTX printer mentioned above. Hope this is helpful Dave [1] http://sourceware.org/gdb/onlinedocs/gdb/Pretty-Printing.html
""" Installation ------------ gdb automatically looks for python files with a -gdb.py suffix relative to executables and DSOs. Currently you need to copy up this file to e.g. "cc1-gdb.py": cp ../../src/gcc/gdb-hooks.py cc1-gdb.py You may see a message from gdb of the form: cc1-gdb.py auto-loading has been declined by your `auto-load safe-path' as a protection against untrustworthy python scripts. See http://sourceware.org/gdb/onlinedocs/gdb/Auto_002dloading-safe-path.html A workaround is to add: -iex "add-auto-load-safe-path ." to the gdb invocation, to mark the current directory as trustworthy. During development, I've been manually invoking the code in this way: cp ../../src/gcc/gdb-hooks.py ./cc1-gdb.py \ && gdb \ -iex "add-auto-load-safe-path ." \ -ex "break expand_gimple_stmt" \ -ex "run" \ -ex "bt" \ --args \ ./cc1 foo.c -O3 Examples of output using the pretty-printers -------------------------------------------- Pointer values are generally shown in the form: <type address extra_info> For example, an opt_pass* might appear as: (gdb) p pass $2 = <opt_pass* 0x188b600 "expand"(170)> The name of the pass is given ("expand"), together with the static_pass_number. Note that you can suppress pretty-printers using /r (for "raw"): (gdb) p /r pass $3 = (opt_pass *) 0x188b600 and dereference the pointer in the normal way: (gdb) p *pass $4 = {type = RTL_PASS, name = 0x120a312 "expand", [etc, ...snipped...] Basic blocks are shown with their index in parentheses, apart from the CFG's entry and exit blocks, which are given as "ENTRY" and "EXIT": (gdb) p bb $9 = <basic_block 0x7ffff041f1a0 (2)> (gdb) p cfun->cfg->x_entry_block_ptr $10 = <basic_block 0x7ffff041f0d0 (ENTRY)> (gdb) p cfun->cfg->x_exit_block_ptr $11 = <basic_block 0x7ffff041f138 (EXIT)> CFG edges are shown with the src and dest blocks given in parentheses: (gdb) p e $1 = <edge 0x7ffff043f118 (ENTRY -> 6)> Tree nodes are printed using Python code that emulates print_node_brief, running in gdb, rather than in the inferior: (gdb) p cfun->decl $1 = <function_decl 0x7ffff0420b00 foo> For usability, the type is printed first (e.g. "function_decl"), rather than just "tree". RTL expressions use a kludge: they are pretty-printed by injecting calls into print-rtl.c into the inferior: Value returned is $1 = (note 9 8 10 [bb 3] NOTE_INSN_BASIC_BLOCK) (gdb) p $1 $2 = (note 9 8 10 [bb 3] NOTE_INSN_BASIC_BLOCK) (gdb) p /r $1 $3 = (rtx_def *) 0x7ffff043e140 This won't work for coredumps, and probably in other circumstances, but it's a quick way of getting lots of debuggability quickly. Callgraph nodes are printed with the name of the function decl, if available: (gdb) frame 5 #5 0x00000000006c288a in expand_function (node=<cgraph_node* 0x7ffff0312720 "foo">) at ../../src/gcc/cgraphunit.c:1594 1594 execute_pass_list (g->get_passes ()->all_passes); (gdb) p node $1 = <cgraph_node* 0x7ffff0312720 "foo"> """ import re import gdb # Hardcoded values corresponding to tree.def IDENTIFIER_NODE = 1 class Tree: """ Wrapper around a gdb.Value for a tree, with various methods corresponding to macros in gcc/tree.h """ def __init__(self, gdbval): self.gdbval = gdbval def is_nonnull(self): return long(self.gdbval) def TREE_CODE(self): """ Get gdb.Value corresponding to TREE_CODE (self) as per: #define TREE_CODE(NODE) ((enum tree_code) (NODE)->base.code) """ return self.gdbval['base']['code'] def DECL_NAME(self): """ Get Tree instance corresponding to DECL_NAME (self) """ return Tree(self.gdbval['decl_minimal']['name']) def TYPE_NAME(self): """ Get Tree instance corresponding to result of TYPE_NAME (self) """ return Tree(self.gdbval['type_common']['name']) def IDENTIFIER_POINTER(self): """ Get str correspoinding to result of IDENTIFIER_NODE (self) """ return self.gdbval['identifier']['id']['str'].string() class TreePrinter: "Prints a tree" def __init__ (self, gdbval): self.gdbval = gdbval self.node = Tree(gdbval) def to_string (self): # like gcc/print-tree.c:print_node_brief # #define TREE_CODE(NODE) ((enum tree_code) (NODE)->base.code) # tree_code_name[(int) TREE_CODE (node)]) if long(self.gdbval) == 0: return '<tree 0x0>' val_TREE_CODE = self.node.TREE_CODE() # extern const enum tree_code_class tree_code_type[]; # #define TREE_CODE_CLASS(CODE) tree_code_type[(int) (CODE)] val_tree_code_type = gdb.parse_and_eval('tree_code_type') val_tclass = val_tree_code_type[val_TREE_CODE] val_tree_code_name = gdb.parse_and_eval('tree_code_name') val_code_name = val_tree_code_name[long(val_TREE_CODE)] #print val_code_name.string() result = '<%s 0x%x' % (val_code_name.string(), long(self.gdbval)) if long(val_tclass) == 3: # tcc_declaration tree_DECL_NAME = self.node.DECL_NAME() if tree_DECL_NAME.is_nonnull(): result += ' %s' % tree_DECL_NAME.IDENTIFIER_POINTER() else: pass # TODO: labels etc elif long(val_tclass) == 2: # tcc_type tree_TYPE_NAME = Tree(self.gdbval['type_common']['name']) if tree_TYPE_NAME.is_nonnull(): if tree_TYPE_NAME.TREE_CODE() == IDENTIFIER_NODE: result += ' %s' % tree_TYPE_NAME.IDENTIFIER_POINTER() elif tree_TYPE_NAME.TREE_CODE() == TYPE_DECL: if tree_TYPE_NAME.DECL_NAME().is_nonnull(): result += ' %s' % tree_TYPE_NAME.DECL_NAME().IDENTIFIER_POINTER() if self.node.TREE_CODE() == IDENTIFIER_NODE: result += ' %s' % self.node.IDENTIFIER_POINTER() # etc result += '>' return result ###################################################################### # Callgraph pretty-printers ###################################################################### class CGraphNodePrinter: def __init__(self, gdbval): self.gdbval = gdbval def to_string (self): result = '<cgraph_node* 0x%x' % long(self.gdbval) if long(self.gdbval): # symtab_node_name calls lang_hooks.decl_printable_name # default implementation (lhd_decl_printable_name) is: # return IDENTIFIER_POINTER (DECL_NAME (decl)); symbol = self.gdbval['symbol'] tree_decl = Tree(symbol['decl']) result += ' "%s"' % tree_decl.DECL_NAME().IDENTIFIER_POINTER() result += '>' return result ###################################################################### class GimplePrinter: def __init__(self, gdbval): self.gdbval = gdbval def to_string (self): if long(self.gdbval) == 0: return '<gimple 0x0>' val_gimple_code = self.gdbval['gsbase']['code'] val_gimple_code_name = gdb.parse_and_eval('gimple_code_name') val_code_name = val_gimple_code_name[long(val_gimple_code)] result = '<%s 0x%x' % (val_code_name.string(), long(self.gdbval)) result += '>' return result ###################################################################### # CFG pretty-printers ###################################################################### def bb_index_to_str(index): if index == 0: return 'ENTRY' elif index == 1: return 'EXIT' else: return '%i' % index class BasicBlockPrinter: def __init__(self, gdbval): self.gdbval = gdbval def to_string (self): result = '<basic_block 0x%x' % long(self.gdbval) if long(self.gdbval): result += ' (%s)' % bb_index_to_str(long(self.gdbval['index'])) result += '>' return result class CfgEdgePrinter: def __init__(self, gdbval): self.gdbval = gdbval def to_string (self): result = '<edge 0x%x' % long(self.gdbval) if long(self.gdbval): src = bb_index_to_str(long(self.gdbval['src']['index'])) dest = bb_index_to_str(long(self.gdbval['dest']['index'])) result += ' (%s -> %s)' % (src, dest) result += '>' return result ###################################################################### class Rtx: def __init__(self, gdbval): self.gdbval = gdbval def GET_CODE(self): return self.gdbval['code'] def GET_RTX_LENGTH(code): val_rtx_length = gdb.parse_and_eval('rtx_length') return long(val_rtx_length[code]) def GET_RTX_NAME(code): val_rtx_name = gdb.parse_and_eval('rtx_name') return val_rtx_name[code].string() def GET_RTX_FORMAT(code): val_rtx_format = gdb.parse_and_eval('rtx_format') return val_rtx_format[code].string() class RtxPrinter: def __init__(self, gdbval): self.gdbval = gdbval self.rtx = Rtx(gdbval) def to_string (self): """ For now, a cheap kludge: invoke the inferior's print function to get a string to use the user, and return an empty string for gdb """ # We use print_inline_rtx to avoid a trailing newline gdb.execute('call print_inline_rtx (stderr, (const_rtx) %s, 0)' % long(self.gdbval)) return '' # or by hand; based on gcc/print-rtl.c:print_rtx result = ('<rtx_def 0x%x' % (long(self.gdbval))) code = self.rtx.GET_CODE() result += ' (%s' % GET_RTX_NAME(code) format_ = GET_RTX_FORMAT(code) for i in range(GET_RTX_LENGTH(code)): print format_[i] result += ')>' return result ###################################################################### class PassPrinter: def __init__(self, gdbval): self.gdbval = gdbval def to_string (self): result = '<opt_pass* 0x%x' % long(self.gdbval) if long(self.gdbval): result += (' "%s"(%i)' % (self.gdbval['name'].string(), long(self.gdbval['static_pass_number']))) result += '>' return result ###################################################################### # TODO: # * vec # * hashtab # * remove hardcoded values for enums def pretty_printer_lookup(gdbval): type_ = gdbval.type.unqualified() str_type_ = str(type_) if str_type_ == 'tree': return TreePrinter(gdbval) if str_type_ == 'cgraph_node *': return CGraphNodePrinter(gdbval) if str_type_ == 'gimple': return GimplePrinter(gdbval) # CFG: if str_type_ == 'basic_block': return BasicBlockPrinter(gdbval) if str_type_ == 'edge': return CfgEdgePrinter(gdbval) if str_type_ == 'rtx_def *': return RtxPrinter(gdbval) if str_type_ == 'opt_pass *': return PassPrinter(gdbval) def register (obj): if obj is None: obj = gdb # Wire up the pretty-printer obj.pretty_printers.append(pretty_printer_lookup) register (gdb.current_objfile ()) print('Successfully loaded GDB hooks for GCC')