diff options
author | David Malcolm <dmalcolm@redhat.com> | 2013-08-28 03:01:20 +0000 |
---|---|---|
committer | David Malcolm <dmalcolm@gcc.gnu.org> | 2013-08-28 03:01:20 +0000 |
commit | 8dce4dbc068ec1c2fa9ff64f0570e5c1079407a7 (patch) | |
tree | c0b78765ca4c3a2f4d8521caa4232178bcbc6f38 /gcc/gdbhooks.py | |
parent | 99236f3ae03ca0a9c9f779b0b745acb5a9881143 (diff) | |
download | gcc-8dce4dbc068ec1c2fa9ff64f0570e5c1079407a7.zip gcc-8dce4dbc068ec1c2fa9ff64f0570e5c1079407a7.tar.gz gcc-8dce4dbc068ec1c2fa9ff64f0570e5c1079407a7.tar.bz2 |
Initial version of gdb hooks
* MAINTAINERS (gdbhooks.py): Add myself as maintainer
gcc/
* gdbhooks.py: New.
* configure.ac (gdbinit.in): Add import of gcc/gdbhooks.py.
* configure: Regenerate.
From-SVN: r202040
Diffstat (limited to 'gcc/gdbhooks.py')
-rw-r--r-- | gcc/gdbhooks.py | 397 |
1 files changed, 397 insertions, 0 deletions
diff --git a/gcc/gdbhooks.py b/gcc/gdbhooks.py new file mode 100644 index 0000000..3d69b11 --- /dev/null +++ b/gcc/gdbhooks.py @@ -0,0 +1,397 @@ +# Python hooks for gdb for debugging GCC +# Copyright (C) 2013 Free Software Foundation, Inc. + +# Contributed by David Malcolm <dmalcolm@redhat.com> + +# 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 +# <http://www.gnu.org/licenses/>. + +""" +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: + <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 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 = <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 +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 '<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) == 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 = '<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 +# * location_t + +class GdbSubprinter(gdb.printing.SubPrettyPrinter): + def __init__(self, name, str_type_, class_): + super(GdbSubprinter, self).__init__(name) + self.str_type_ = str_type_ + self.class_ = class_ + +class GdbPrettyPrinters(gdb.printing.PrettyPrinter): + def __init__(self, name): + super(GdbPrettyPrinters, self).__init__(name, []) + + def add_printer(self, name, exp, class_): + self.subprinters.append(GdbSubprinter(name, exp, class_)) + + def __call__(self, gdbval): + type_ = gdbval.type.unqualified() + str_type_ = str(type_) + for printer in self.subprinters: + if printer.enabled and str_type_ == printer.str_type_: + return printer.class_(gdbval) + + # Couldn't find a pretty printer (or it was disabled): + return None + + +def build_pretty_printer(): + pp = GdbPrettyPrinters('gcc') + pp.add_printer('tree', 'tree', TreePrinter) + pp.add_printer('cgraph_node', 'cgraph_node *', CGraphNodePrinter) + pp.add_printer('gimple', 'gimple', GimplePrinter) + pp.add_printer('basic_block', 'basic_block', BasicBlockPrinter) + pp.add_printer('edge', 'edge', CfgEdgePrinter) + pp.add_printer('rtx_def', 'rtx_def *', RtxPrinter) + pp.add_printer('opt_pass', 'opt_pass *', PassPrinter) + return pp + +gdb.printing.register_pretty_printer( + gdb.current_objfile(), + build_pretty_printer()) + +print('Successfully loaded GDB hooks for GCC') |