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')

Reply via email to