From patchwork Mon Aug 26 18:42:58 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Malcolm X-Patchwork-Id: 269947 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client CN "www.sourceware.org", Issuer "StartCom Class 1 Primary Intermediate Server CA" (not verified)) by ozlabs.org (Postfix) with ESMTPS id 721A52C0089 for ; Tue, 27 Aug 2013 04:43:18 +1000 (EST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :message-id:subject:from:to:cc:date:in-reply-to:references :content-type:mime-version; q=dns; s=default; b=Omyj5m8GBRRviYdj MgwgTOpvRjYkGquJVUkWhjQtw5C2jjcI7PaEo2IdyGV5bb1ADtjpA4ifaoolcr3w I//Mn0GUYcj1hts0NCpu6pLc3WRm31Oa4Bfsf/LytJAmDrtDar0hLSbKYJVewuak 8dSHQqIkz4n8YG/oP6RFlsjWRi4= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :message-id:subject:from:to:cc:date:in-reply-to:references :content-type:mime-version; s=default; bh=Htfd9qgiNqbLLMHOCzJqMS 6H5As=; b=gcL+Is+Xw2kvZHr1WrTNJf2D7hCOIclnl/wyTZFUswjupB5U4knjJv 5FpL6fDd1W3iplRSwgTAtIJ/T4q64GsYS2VneU23tUFLoVXQJEZ/+VsjXcnX9/lV 9GRHaG/+q6bEyWofF3N96nlx0RxvprWr1/vXuTRd7jHxBSjlhBccM= Received: (qmail 7721 invoked by alias); 26 Aug 2013 18:43:11 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 7712 invoked by uid 89); 26 Aug 2013 18:43:10 -0000 Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Mon, 26 Aug 2013 18:43:10 +0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-5.6 required=5.0 tests=ALL_TRUSTED, AWL, BAYES_00, KHOP_THREADED, RP_MATCHES_RCVD autolearn=ham version=3.3.2 X-HELO: mx1.redhat.com Received: from int-mx02.intmail.prod.int.phx2.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id r7QIh6uP009224 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Mon, 26 Aug 2013 14:43:07 -0400 Received: from [10.18.25.132] ([10.18.25.132]) by int-mx02.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id r7QIh6ap027124; Mon, 26 Aug 2013 14:43:06 -0400 Message-ID: <1377542578.15461.51.camel@surprise> Subject: v3 of GDB hooks for debugging GCC From: David Malcolm To: Tom Tromey , law@redhat.com Cc: gcc-patches@gcc.gnu.org Date: Mon, 26 Aug 2013 14:42:58 -0400 In-Reply-To: <877gfedaia.fsf@fleche.redhat.com> References: <1375494523.4994.108.camel@surprise> <87mwow9ptg.fsf@fleche.redhat.com> <1377115676.9927.25.camel@surprise> <877gfedaia.fsf@fleche.redhat.com> Mime-Version: 1.0 On Wed, 2013-08-21 at 15:01 -0600, Tom Tromey wrote: > >>>>> "David" == David Malcolm writes: > > Tom> Naughty. > > David> We chatted about this at Cauldron; I haven't yet had a chance to > David> implement the magic bullet approach we discussed there. In the > David> meantime, is there a API I can call to determine how safe this kludge > David> is? > > Not right now. You can just call the function and catch the exception > that occurs if it can't be done. > > I think you can still run into trouble sometimes. For example if the > user puts a breakpoint in one of the functions used by the > pretty-printer, and then does "bt", hitting the breakpoint while > printing the backtrace... not sure what happens then, maybe a crash. > > Tom> I think you could set up the safe-path in the gcc .gdbinit. > > David> Interesting idea - but .gdbinit itself seems to get declined, so I don't > David> think this can help. > > Haha, I didn't think of that :-) But you were on the right track... if one marks gcc's .gdbinit as loadable, then gdb can happily *import* (rather than autoload) the python hooks without needing the user to grant extra permission. I'm attaching a revised patch that reworks things to use this scheme, by adding a python import line into the configure[.ac] hook that generates builddir/gcc/.gdbinit : all that a gcc hacker has to do to use the python hooks now is to ensure that their ~/.gdbinit script contains a line like this: add-auto-load-safe-path /absolute/path/to/build/gcc and it all should just work. You need this already to get the pre-existing gdbinit hooks to work with a recent gdb that has the autoload protection. [I renamed the file from gdb-hooks.py to gdbhooks.py so that it's importable as a python module. Doing it from the srcdir avoids having to copy the file to the builddir]. So in this scheme, the Python hooks piggyback on top of the older gdb hooks. Hope that's OK. As noted earlier in the thread, the Python hooks can be disabled by running: (gdb) disable pretty-printer .* gcc 7 printers disabled The patch also adds me a maintainer of gdbhooks.py into the MAINTAINERS file. (There doesn't seem to be any sort order to the maintainer part of that file, should there be?) Finally, I added a copyright header to the new file ("part of GCC", FSF assignee, GPLv3 or later). OK for trunk? Dave commit 9ef4a9c7474b56f19bfa49905944931e52e95514 Author: David Malcolm Date: Wed Aug 21 15:45:55 2013 -0400 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. diff --git a/MAINTAINERS b/MAINTAINERS index 78b288f..50ede75 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -256,6 +256,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/configure b/gcc/configure index ec662f5..c6bc3a6 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 62d3053..5d3e5ad 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 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 + +# 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 = '