This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: GDB hooks for debugging GCC


Hi,

On Fri, Aug 02, 2013 at 09:48:43PM -0400, David Malcolm wrote:
> 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.
> 

For what it is worth, I have put together a python script that I do
plan to use as a replacement for the current .gdbinit.  However, I
have intended it to be just for my personal use only so I have never
bothered to clean it up in anyway.  I do not like copying .gdbinit
around manually and I planned to (at least for now) be loading the
script manually using the source gdb command - everything else just
looked too complicated (and I did not manage to put it into ~/gdbinit
in any non-dumb way).

In summary, the attached script provides the following :

- Commands:
    - pt to call debug_tree on its one argument
    - pge to call debug_generic_expr on its one argument
    - pgg to call debug_gimple_stmt on its one argument
    - pr to call debug_rtx  on its one argument

- Very short and basic pretty Printers for
    - tree
    - symtab_node
    - cgraph_node
    - varpool_node
    - cgraph_edge
    - ipa_jump_func

- Functions accessible from gdb prompt
    - $vec_len to get a vector's length
    - $vec_item to get an ite in a  vector
    - $cgn_count_callees to count callers of a cgraph_node
    - $cgn_count_callees to count callees of a cgraph_node

- sets breakpoints in fancy_abort and internal_error if there are no
  breakpoints there yet.

...and I plan to add more.  I am not sure how I will coexist with an
"official" python extension, probably by regularly stealing stuff from
it (I already did) but running my own, as long as it will be feasible
:-) I suppose the "official" script can take some stuff from mine too,
if anything seems useful.

In any event, thanks for posting it,

Martin

import gdb
from gdb.printing import PrettyPrinter, register_pretty_printer, RegexpCollectionPrettyPrinter

# Basic commands to call internal pretty printers

class CallCompiledPrinter (gdb.Command):
    """Base class to facilitate commands calling internal pretty printers"""

    def __init__(self, command, function, typename = "tree"):
        super (CallCompiledPrinter, self).__init__(command, gdb.COMMAND_DATA,
                                                   gdb.COMPLETE_SYMBOL)
        self.function = function
        self.typename = typename
        return

    def invoke (self, arg_str, from_tty):
        argv = gdb.string_to_argv(arg_str)
        if len (argv) != 1:
            raise gdb.GdbError ("Provide the tree to print as an argument");
        try:
            t = gdb.parse_and_eval (argv[0])
        except:
            raise gdb.GdbError ("Could not evaluate %s" % argv[0])
        if t.is_optimized_out:
            raise gdb.GdbError ("Value optimized out")
        self.dont_repeat()
        gdb.execute ("call %s ((%s) 0x%x)" % (self.function, self.typename,
                                            long(t)))
        return

class TreeDebug (CallCompiledPrinter):
    """Call debug_tree on a tree"""

    def __init__(self):
        super (TreeDebug, self).__init__("pt", "debug_tree")
        return

class TreeGenericExpr (CallCompiledPrinter):
    """Call debug_generic_expr on a tree"""

    def __init__(self):
        super (TreeGenericExpr, self).__init__("pge", "debug_generic_expr")
        return

class GimpleDebug (CallCompiledPrinter):
    """Call debug_gimple_stmt on a gimple statement"""

    def __init__(self):
        super (GimpleDebug, self).__init__("pgg", "debug_gimple_stmt", "gimple")
        return

class RtxDebug (CallCompiledPrinter):
    """Call debug_rtx on an rtx"""

    def __init__(self):
        super (RtxDebug, self).__init__("pr", "debug_rtx", "rtl")
        return


# Vector related stuff

def vec_get_length (gdbval):
    try:
        if long(gdbval) == 0:
            return 0
    except:
        pass

    try:
        gdbval = gdbval["vec_"]
    except:
        pass

    try:
        if long(gdbval) == 0:
            return 0
    except:
        pass

    pfx = gdbval["vecpfx_"]
    return int(pfx["num_"])

def vec_get_item (gdbval, i):
    """Return i-th item in vector represented by gdbval"""
    l = True;
    try:
        l = long(gdbval)
    except:
        pass
    if l == 0:
        raise gdb.GdbError("Cannot access item of NULL vector")
    
    try:
        gdbval = gdbval["vec_"]
    except:
        pass

    try:
        l = long(gdbval)
    except:
        pass
    if l == 0:
        raise gdb.GdbError("Cannot access item of NULL vector")
    
    if i >= vec_get_length(gdbval):
        raise gdb.GdbError(("Index %i out of vector bounds %i"
                            % (i, vec_get_length(gdbval))))
    return gdbval["vecdata_"][i]

def vec_iter (gdbval):
    """Return and iterator over vector represented by gdbval"""

    for i in range (vec_get_length (gdbval)):
        yield vec_get_item(gdbval, i)
        pass
    return

class VecGetLength (gdb.Function):
    """Return length of a vector"""

    def __init__ (self):
        super (VecGetLength, self).__init__("vec_len")
        return
    
    def invoke (self, vec):
        return vec_get_length (vec)

class VecGetItem (gdb.Function):
    """Return length of a vector"""

    def __init__ (self):
        super (VecGetItem, self).__init__("vec_item")
        return
    
    def invoke (self, vec, i):
        return vec_get_item (vec, i)

# Tree related stuff

def tree_get_identifier_str (gdbval):
    """Return the identifier string encoded by it"""
    return gdbval['identifier']['id']['str'].string()

def tree_get_decl_name (gdbval):
    """Return tree decl name"""

    decl_name = gdbval['decl_minimal']['name']
    if long(decl_name) == 0:
        return ""
    return tree_get_identifier_str(decl_name)

class TreePrettyPrinter (PrettyPrinter):
    """Pretty printer for TREEs"""

    def __init__ (self):
        super (TreePrettyPrinter, self).__init__("tree",)
        return

    def __call__(self, gdbval):
        if str(gdbval.type.unqualified()) == "tree":
            self.gdbval = gdbval;
            return self
        return None

    def to_string(self):
        if long(self.gdbval) == 0:
            return "NULL_TREE"
        if self.gdbval.is_optimized_out:
            return "<tree optimized out>"
        
        try:
            code = self.gdbval['base']['code']
        except:
            return "<unknown tree 0x%x>" % long(self.gdbval)

        tree_code_name = gdb.parse_and_eval("tree_code_name")
        str_code_name = tree_code_name[long(code)].string()
        result = '<%s 0x%x' % (str_code_name, long(self.gdbval))

        tree_code_type = gdb.parse_and_eval("tree_code_type")
        tclass = long(tree_code_type[code])

        if tclass == 3:  #tcc reference
            result = result + " " + tree_get_decl_name (self.gdbval) +">"
        else:
            result = result + ">"

        return result

# Symbol table related stuff

def symtab_node_name(gdbval):
    """Return node description string in the form of name/symbol_order"""

    if long(gdbval) == 0:
        return ""
    sym = gdbval["symbol"]
    return tree_get_decl_name(sym["decl"])


def symtab_node_string(gdbval):
    """Return node description string in the form of name/symbol_order"""

    if long(gdbval) == 0:
        return "NULL"
    sym = gdbval["symbol"]
    return "%s/%i" % (symtab_node_name(gdbval), sym["order"])

def symtab_get_sym_type(gdbval):
    """Return the type constant of a symbol table node"""
    return long(gdbval["symbol"]["type"])

def symtab_node_is_function(gdbval):
    """Return true if gdbval represents a function"""
    try:
        symtype = symtab_get_sym_type (gdbval)
    except:
        return False
    return symtype == 1
    
def symtab_node_is_variable(gdbval):
    """Return true if gdbval represents a variable"""
    try:
        symtype = symtab_get_sym_type (gdbval)
    except:
        return False
    return symtype == 2

class SymtabNodePrettyPrinter:
    """Pretty printer for symbol table nodes"""

    def __init__(self, gdbval):
        self.gdbval = gdbval
        return

    def to_string(self):
        if long(self.gdbval) == 0:
            return "<symtab_symbol NULL>"

        try:
            sym = self.gdbval["symbol"]
            symtype = long(sym["type"])
        except:
            return "<unknown symbol table node at 0x%x>" % long(sef.gdbval)
        if symtype == 0:
            tstr = "symtab_symbol"
        elif symtype == 1:
            tstr = "symtab_function"
        elif symtype == 2:
            tstr = "symtab_variable"
        else:
            return "<bogus symbol table node at 0x%x>" % long(sef.gdbval)
            
        return "<%s 0x%x %s>" % (tstr, long(self.gdbval),
                                    symtab_node_string (self.gdbval))

class CgraphEdgePrettyPrinter:
    """Pretty printer for call graph edges"""

    def __init__(self, gdbval):
        self.gdbval = gdbval
        return

    def to_string(self):
        if long(self.gdbval) == 0:
            return "<cgraph_edge NULL>"
        caller_str = symtab_node_string (self.gdbval["caller"])
        result = "<cgraph_edge %s -> " % caller_str
        if long(self.gdbval["indirect_unknown_callee"]) != 0:
            ii = self.gdbval["indirect_info"]
            param_index = long(ii["param_index"])
            if param_index >= 0:
                result += "PARM %i" % param_index
            else:
                result += "UNKNOWN"
                pass
            if long(ii["polymorphic"]) != 0:
                result += " polymorphic"
                pass
            if long(ii["agg_contents"]) != 0:
                result += " aggregate"
                pass
            if long(ii["member_ptr"]) != 0:
                result += " member_ptr"
                pass
            pass
        else:
            result += symtab_node_string (self.gdbval["callee"])
            if long(self.gdbval["inline_failed"]) == 0:
                result += " inlined"
                pass
            if long(self.gdbval["indirect_inlining_edge"]) != 0:
                result += " ii_edge"
                pass
            pass
        result += ">"
        return result
                
class SymtabPrinterDispatcher (PrettyPrinter):
    """Pretty printer dispatcher for symbol table data"""
    def __init__ (self):
        super (SymtabPrinterDispatcher, self).__init__("symtab")
        return

    def __call__(self, gdbval):
        strtype = str(gdbval.type.unqualified())
        if (strtype == "symtab_node_def *" or
            strtype == "cgraph_node *" or
            strtype == "varpool_node *"):
            return SymtabNodePrettyPrinter(gdbval)
        if (strtype == "cgraph_edge *"):
            return CgraphEdgePrettyPrinter(gdbval)
        return None
    
class CgraphNodeCountCallers (gdb.Function):
    """Return number of callers a particular call graph node has."""

    def __init__ (self):
        super (CgraphNodeCountCallers, self).__init__("cgn_count_callers")
        return

    def invoke (self, node):
        e = node["callers"]
        count = 0

        while long(e) != 0:
            count = count + 1
            e = e["next_caller"]
            pass
        return count

class CgraphNodeCountCallees (gdb.Function):
    """Return number of callees a particular call graph node has."""

    def __init__ (self):
        super (CgraphNodeCountCallees, self).__init__("cgn_count_callees")
        return

    def invoke (self, node):
        e = node["callees"]
        count = 0

        while long(e) != 0:
            count = count + 1
            e = e["next_callee"]
            pass
        return count

# ipa-prop stuff

class JumpFunctionPrettyPrinter:
    """Pretty printer for a jump function"""

    def __init__(self, gdbval):
        self.gdbval = gdbval
        return

    def agg_jf_to_string(self):
        r = "\n  Aggregate values:"
        vec = self.gdbval["agg"]["items"]
        for j in vec_iter(vec):
            r += ("\n  offset={:05d} value={}".format(long(j["offset"]),
                                                      j["value"]))
            pass
        return r

    def to_string(self):
        try:
            if long(self.gdbval) == 0:
                return "<ipa_jump_func NULL>"
        except:
            pass

        tp = int(self.gdbval["type"])
        if tp == int(gdb.parse_and_eval("IPA_JF_UNKNOWN")):
            r = "IPA_JF_UNKNOWN"
            pass
        elif tp == int(gdb.parse_and_eval("IPA_JF_KNOWN_TYPE")):
            kt = self.gdbval["value"]["known_type"]
            r = ("IPA_JF_KNOWN_TYPE offset={:d} base_type={}, component_"
                 + "type={}").format(long(kt["offset"]),
                                     kt["base_type"].string,
                                     kt["component_type"])
            pass
        elif tp == int(gdb.parse_and_eval("IPA_JF_CONST")):
            c = self.gdbval["value"]["constant"]
            r = ("IPA_JF_CONST value={}, rdesc=0x{:x}"
                 +" ").format(c["value"], long(c["rdesc"]))
            pass
        elif tp == int(gdb.parse_and_eval("IPA_JF_PASS_THROUGH")):
            ptd = self.gdbval["value"]["pass_through"]
            r = ("IPA_JF_PASS_THROUGH formal_id={:d}, agg_preserved={:d}"
                 + " ").format(int(ptd["formal_id"]),
                               int(ptd["agg_preserved"]))

            opnum = int(ptd["operation"])
            if opnum == int(gdb.parse_and_eval("NOP_EXPR")):
                codenames = gdb.parse_and_eval("tree_code_name")
                opstr = codenames[opnum].string()
                r += "operation={}, operand={}".format(opstr, ptd["operand"]) 
                pass
            pass
        elif tp == int(gdb.parse_and_eval("IPA_JF_ANCESTOR")):
            an = self.gdbval["value"]["ancestor"]
            r = ("IPA_JF_ANCESTOR offset={:d}, type={}, formal_id={:d} "
                 + "agg_preserved={:d}").format(long(an["offset"]),
                                                an["type"], an["formal_id"],
                                                an["agg_preserved"])
            pass
        else:
            r = "UNKNOWN JF TYPE"
            pass

        if long(self.gdbval["agg"]["items"]) != 0:
            r += self.agg_jf_to_string()
            pass

        return r

class IPAPropPrinterDispatcher (PrettyPrinter):
    """Pretty printer dispatcher for ipa-prop data"""
    def __init__ (self):
        super (IPAPropPrinterDispatcher, self).__init__("ipa-prop")
        return

    def __call__(self, gdbval):
        strtype = str(gdbval.type.unqualified())
        if (strtype == "ipa_jump_func"
            or strtype == "ipa_jump_func *"):
            return JumpFunctionPrettyPrinter(gdbval)
        return None


# Set basic breakpoints

def set_basic_breakpoints():
    for i in ["fancy_abort", "internal_error"]:
        found = False
        if gdb.breakpoints():
            for j in gdb.breakpoints():
                if j.location.find(i) >= 0:
                    print ("Breakpoint %i already set in %s (hit %i times)"
                           % (j.number, i, j.hit_count))
                    found = True
                    break
                pass
            pass

        if not found:
            gdb.Breakpoint (i)
            pass
        pass
    return

# Quit if not in gcc


TreeDebug()
TreeGenericExpr()
GimpleDebug()
RtxDebug()

VecGetLength()
VecGetItem()
register_pretty_printer(gdb.current_objfile(), TreePrettyPrinter(), True)
register_pretty_printer(gdb.current_objfile(), SymtabPrinterDispatcher(), True)
    
CgraphNodeCountCallers()
CgraphNodeCountCallees()
register_pretty_printer(gdb.current_objfile(), IPAPropPrinterDispatcher(),
                        True)

set_basic_breakpoints()

gdb.execute("set unwindonsignal on")
gdb.execute("skip file tree.h")

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]