diff --git a/ChangeLog b/ChangeLog index 4e71eaf7375..2f8ddb7da40 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2013-08-27 David Malcolm + + * MAINTAINERS (gdbhooks.py): Add myself as maintainer. + 2013-08-26 Caroline Tice * MAINTAINERS: Correct earliers update: Move myself from libvtv diff --git a/MAINTAINERS b/MAINTAINERS index 51017c8bf2c..6d9a4cd3898 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -257,6 +257,7 @@ testsuite Rainer Orth ro@CeBiTec.Uni-Bielefeld.DE testsuite Mike Stump mikestump@comcast.net testsuite Janis Johnson janisjo@codesourcery.com register allocation Vladimir Makarov vmakarov@redhat.com +gdbhooks.py David Malcolm dmalcolm@redhat.com Note that individuals who maintain parts of the compiler need approval to check in changes outside of the parts of the compiler they maintain. diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 0903dc43ffd..1657b44e374 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,9 @@ +2013-08-27 David Malcolm + + * gdbhooks.py: New. + * configure.ac (gdbinit.in): Add import of gcc/gdbhooks.py. + * configure: Regenerate. + 2013-08-27 Martin Jambor * ipa-prop.h (ipa_pass_through_data): New field type_preserved. diff --git a/gcc/configure b/gcc/configure index ec662f50233..c6bc3a69d84 100755 --- a/gcc/configure +++ b/gcc/configure @@ -27397,6 +27397,7 @@ if test "x$subdirs" != x; then done fi echo "source ${srcdir}/gdbinit.in" >> .gdbinit +echo "python import sys; sys.path.append('${srcdir}'); import gdbhooks" >> .gdbinit gcc_tooldir='$(libsubdir)/$(libsubdir_to_prefix)$(target_noncanonical)' diff --git a/gcc/configure.ac b/gcc/configure.ac index 62d3053e6e6..5d3e5ad5823 100644 --- a/gcc/configure.ac +++ b/gcc/configure.ac @@ -5181,6 +5181,7 @@ if test "x$subdirs" != x; then done fi echo "source ${srcdir}/gdbinit.in" >> .gdbinit +echo "python import sys; sys.path.append('${srcdir}'); import gdbhooks" >> .gdbinit gcc_tooldir='$(libsubdir)/$(libsubdir_to_prefix)$(target_noncanonical)' AC_SUBST(gcc_tooldir) diff --git a/gcc/gdbhooks.py b/gcc/gdbhooks.py new file mode 100644 index 00000000000..3d69b11bfe9 --- /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 + +# 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 = '