# Python hooks for gdb for debugging GCC # Copyright (C) 2013 Free Software Foundation, Inc. # Contributed by David Malcolm # This file is part of GCC. # GCC is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free # Software Foundation; either version 3, or (at your option) any later # version. # GCC is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # You should have received a copy of the GNU General Public License # along with GCC; see the file COPYING3. If not see # . """ Enabling the debugging hooks ---------------------------- gcc/configure (from configure.ac) generates a .gdbinit within the "gcc" subdirectory of the build directory, and when run by gdb, this imports gcc/gdbhooks.py from the source directory, injecting useful Python code into gdb. You may see a message from gdb of the form: "path-to-build/gcc/.gdbinit" 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 The fix is to mark the paths of the build/gcc directory as trustworthy. An easy way to do so is by adding the following to your ~/.gdbinit script: add-auto-load-safe-path /absolute/path/to/build/gcc for the build directories for your various checkouts of gcc. If it's working, you should see the message: Successfully loaded GDB hooks for GCC as gdb starts up. During development, I've been manually invoking the code in this way, as a precanned way of printing a variety of different kinds of value: gdb \ -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: For example, an opt_pass* might appear as: (gdb) p pass $2 = The name of the pass is given ("expand"), together with the static_pass_number. Note that you can dereference the pointer in the normal way: (gdb) p *pass $4 = {type = RTL_PASS, name = 0x120a312 "expand", [etc, ...snipped...] and you can suppress pretty-printers using /r (for "raw"): (gdb) p /r pass $3 = (opt_pass *) 0x188b600 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 = (gdb) p cfun->cfg->x_entry_block_ptr $10 = (gdb) p cfun->cfg->x_exit_block_ptr $11 = CFG edges are shown with the src and dest blocks given in parentheses: (gdb) p e $1 = 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 = 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=) at ../../src/gcc/cgraphunit.c:1594 1594 execute_pass_list (g->get_passes ()->all_passes); (gdb) p node $1 = """ import re import gdb import gdb.printing import gdb.types # Convert "enum tree_code" (tree.def and tree.h) to a dict: tree_code_dict = gdb.types.make_enum_dict(gdb.lookup_type('enum tree_code')) # ...and look up specific values for use later: IDENTIFIER_NODE = tree_code_dict['IDENTIFIER_NODE'] TYPE_DECL = tree_code_dict['TYPE_DECL'] # Similarly for "enum tree_code_class" (tree.h): tree_code_class_dict = gdb.types.make_enum_dict(gdb.lookup_type('enum tree_code_class')) tcc_type = tree_code_class_dict['tcc_type'] tcc_declaration = tree_code_class_dict['tcc_declaration'] 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 '' 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) == 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) == 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 = '' 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 = '