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