X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=gcc%2Fgdbhooks.py;h=0ab6f01f92711525578c8e37bc808b0cc2d09b82;hb=9e5508c2d006f2d4f8670e6c3fed770ac1c85e64;hp=f0a925c2b9c7210ba8b4a101da658977114024e3;hpb=1fef36449e233448605e7812f4e2874baf05ce53;p=gcc.git diff --git a/gcc/gdbhooks.py b/gcc/gdbhooks.py index f0a925c2b9c..0ab6f01f927 100644 --- a/gcc/gdbhooks.py +++ b/gcc/gdbhooks.py @@ -1,5 +1,5 @@ # Python hooks for gdb for debugging GCC -# Copyright (C) 2013 Free Software Foundation, Inc. +# Copyright (C) 2013-2020 Free Software Foundation, Inc. # Contributed by David Malcolm @@ -105,10 +105,19 @@ 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 + #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 = + $1 = + +Similarly for symtab_node and varpool_node classes. + +Cgraph edges are printed with the name of caller and callee: + (gdb) p this->callees + $4 = -> )> + +IPA reference follow very similar format: + (gdb) Value returned is $5 = -> :IPA_REF_ADDR)> vec<> pointers are printed as the address followed by the elements in braces. Here's a length 2 vec: @@ -130,7 +139,10 @@ Instead (for now) you must access m_vecdata: (gdb) p bb->preds->m_vecdata[1] $21 = 5)> """ +import os.path import re +import sys +import tempfile import gdb import gdb.printing @@ -142,12 +154,19 @@ 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'] +SSA_NAME = tree_code_dict['SSA_NAME'] # 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'] +# Python3 has int() with arbitrary precision (bignum). Python2 int() is 32-bit +# on 32-bit hosts but remote targets may have 64-bit pointers there; Python2 +# long() is always 64-bit but Python3 no longer has anything named long. +def intptr(gdbval): + return long(gdbval) if sys.version_info.major == 2 else int(gdbval) + class Tree: """ Wrapper around a gdb.Value for a tree, with various methods @@ -157,7 +176,7 @@ class Tree: self.gdbval = gdbval def is_nonnull(self): - return long(self.gdbval) + return intptr(self.gdbval) def TREE_CODE(self): """ @@ -196,7 +215,7 @@ class TreePrinter: # 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: + if intptr(self.gdbval) == 0: return '' val_TREE_CODE = self.node.TREE_CODE() @@ -204,21 +223,27 @@ class TreePrinter: # extern const enum tree_code_class tree_code_type[]; # #define TREE_CODE_CLASS(CODE) tree_code_type[(int) (CODE)] + if val_TREE_CODE == 0xa5a5: + return '' % intptr(self.gdbval) + 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: + val_code_name = val_tree_code_name[intptr(val_TREE_CODE)] + #print(val_code_name.string()) + + try: + result = '<%s 0x%x' % (val_code_name.string(), intptr(self.gdbval)) + except: + return '' % intptr(self.gdbval) + if intptr(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: + elif intptr(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: @@ -228,6 +253,8 @@ class TreePrinter: result += ' %s' % tree_TYPE_NAME.DECL_NAME().IDENTIFIER_POINTER() if self.node.TREE_CODE() == IDENTIFIER_NODE: result += ' %s' % self.node.IDENTIFIER_POINTER() + elif self.node.TREE_CODE() == SSA_NAME: + result += ' %u' % self.gdbval['base']['u']['version'] # etc result += '>' return result @@ -236,18 +263,65 @@ class TreePrinter: # Callgraph pretty-printers ###################################################################### -class CGraphNodePrinter: +class SymtabNodePrinter: def __init__(self, gdbval): self.gdbval = gdbval def to_string (self): - result = '' + result = '' val_gimple_code = self.gdbval['code'] val_gimple_code_name = gdb.parse_and_eval('gimple_code_name') - val_code_name = val_gimple_code_name[long(val_gimple_code)] + val_code_name = val_gimple_code_name[intptr(val_gimple_code)] result = '<%s 0x%x' % (val_code_name.string(), - long(self.gdbval)) + intptr(self.gdbval)) result += '>' return result @@ -285,9 +359,9 @@ class BasicBlockPrinter: self.gdbval = gdbval def to_string (self): - result = '' + return name[2:] if name.startswith('E_') else name + +###################################################################### + # TODO: # * hashtab # * location_t @@ -431,11 +527,11 @@ class GdbPrettyPrinters(gdb.printing.PrettyPrinter): def __init__(self, name): super(GdbPrettyPrinters, self).__init__(name, []) - def add_printer_for_types(self, name, class_, types): - self.subprinters.append(GdbSubprinterTypeList(name, class_, types)) + def add_printer_for_types(self, types, name, class_): + self.subprinters.append(GdbSubprinterTypeList(types, name, class_)) - def add_printer_for_regex(self, name, class_, regex): - self.subprinters.append(GdbSubprinterRegex(name, class_, regex)) + def add_printer_for_regex(self, regex, name, class_): + self.subprinters.append(GdbSubprinterRegex(regex, name, class_)) def __call__(self, gdbval): type_ = gdbval.type.unqualified() @@ -450,11 +546,34 @@ class GdbPrettyPrinters(gdb.printing.PrettyPrinter): def build_pretty_printer(): pp = GdbPrettyPrinters('gcc') - pp.add_printer_for_types(['tree'], + pp.add_printer_for_types(['tree', 'const_tree'], 'tree', TreePrinter) - pp.add_printer_for_types(['cgraph_node *'], - 'cgraph_node', CGraphNodePrinter) - pp.add_printer_for_types(['gimple', 'gimple_statement_base *'], + pp.add_printer_for_types(['cgraph_node *', 'varpool_node *', 'symtab_node *'], + 'symtab_node', SymtabNodePrinter) + pp.add_printer_for_types(['cgraph_edge *'], + 'cgraph_edge', CgraphEdgePrinter) + pp.add_printer_for_types(['ipa_ref *'], + 'ipa_ref', IpaReferencePrinter) + pp.add_printer_for_types(['dw_die_ref'], + 'dw_die_ref', DWDieRefPrinter) + pp.add_printer_for_types(['gimple', 'gimple *', + + # Keep this in the same order as gimple.def: + 'gimple_cond', 'const_gimple_cond', + 'gimple_statement_cond *', + 'gimple_debug', 'const_gimple_debug', + 'gimple_statement_debug *', + 'gimple_label', 'const_gimple_label', + 'gimple_statement_label *', + 'gimple_switch', 'const_gimple_switch', + 'gimple_statement_switch *', + 'gimple_assign', 'const_gimple_assign', + 'gimple_statement_assign *', + 'gimple_bind', 'const_gimple_bind', + 'gimple_statement_bind *', + 'gimple_phi', 'const_gimple_phi', + 'gimple_statement_phi *'], + 'gimple', GimplePrinter) pp.add_printer_for_types(['basic_block', 'basic_block_def *'], @@ -470,10 +589,250 @@ def build_pretty_printer(): 'vec', VecPrinter) + pp.add_printer_for_regex(r'opt_mode<(\S+)>', + 'opt_mode', OptMachineModePrinter) + pp.add_printer_for_types(['opt_scalar_int_mode', + 'opt_scalar_float_mode', + 'opt_scalar_mode'], + 'opt_mode', OptMachineModePrinter) + pp.add_printer_for_regex(r'pod_mode<(\S+)>', + 'pod_mode', MachineModePrinter) + pp.add_printer_for_types(['scalar_int_mode_pod', + 'scalar_mode_pod'], + 'pod_mode', MachineModePrinter) + for mode in ('scalar_mode', 'scalar_int_mode', 'scalar_float_mode', + 'complex_mode'): + pp.add_printer_for_types([mode], mode, MachineModePrinter) + return pp gdb.printing.register_pretty_printer( gdb.current_objfile(), - build_pretty_printer()) + build_pretty_printer(), + replace=True) + +def find_gcc_source_dir(): + # Use location of global "g" to locate the source tree + sym_g = gdb.lookup_global_symbol('g') + path = sym_g.symtab.filename # e.g. '../../src/gcc/context.h' + srcdir = os.path.split(path)[0] # e.g. '../../src/gcc' + return srcdir + +class PassNames: + """Parse passes.def, gathering a list of pass class names""" + def __init__(self): + srcdir = find_gcc_source_dir() + self.names = [] + with open(os.path.join(srcdir, 'passes.def')) as f: + for line in f: + m = re.match('\s*NEXT_PASS \(([^,]+).*\);', line) + if m: + self.names.append(m.group(1)) + +class BreakOnPass(gdb.Command): + """ + A custom command for putting breakpoints on the execute hook of passes. + This is largely a workaround for issues with tab-completion in gdb when + setting breakpoints on methods on classes within anonymous namespaces. + + Example of use: putting a breakpoint on "final" + (gdb) break-on-pass + Press ; it autocompletes to "pass_": + (gdb) break-on-pass pass_ + Press : + Display all 219 possibilities? (y or n) + Press "n"; then type "f": + (gdb) break-on-pass pass_f + Press to autocomplete to pass classnames beginning with "pass_f": + pass_fast_rtl_dce pass_fold_builtins + pass_feedback_split_functions pass_forwprop + pass_final pass_fre + pass_fixup_cfg pass_free_cfg + Type "in" to complete to "pass_final": + (gdb) break-on-pass pass_final + ...and hit : + Breakpoint 6 at 0x8396ba: file ../../src/gcc/final.c, line 4526. + ...and we have a breakpoint set; continue execution: + (gdb) cont + Continuing. + Breakpoint 6, (anonymous namespace)::pass_final::execute (this=0x17fb990) at ../../src/gcc/final.c:4526 + 4526 virtual unsigned int execute (function *) { return rest_of_handle_final (); } + """ + def __init__(self): + gdb.Command.__init__(self, 'break-on-pass', gdb.COMMAND_BREAKPOINTS) + self.pass_names = None + + def complete(self, text, word): + # Lazily load pass names: + if not self.pass_names: + self.pass_names = PassNames() + + return [name + for name in sorted(self.pass_names.names) + if name.startswith(text)] + + def invoke(self, arg, from_tty): + sym = '(anonymous namespace)::%s::execute' % arg + breakpoint = gdb.Breakpoint(sym) + +BreakOnPass() + +class DumpFn(gdb.Command): + """ + A custom command to dump a gimple/rtl function to file. By default, it + dumps the current function using 0 as dump_flags, but the function and flags + can also be specified. If /f are passed as the first two arguments, + the dump is written to that file. Otherwise, a temporary file is created + and opened in the text editor specified in the EDITOR environment variable. + + Examples of use: + (gdb) dump-fn + (gdb) dump-fn /f foo.1.txt + (gdb) dump-fn cfun->decl + (gdb) dump-fn /f foo.1.txt cfun->decl + (gdb) dump-fn cfun->decl 0 + (gdb) dump-fn cfun->decl dump_flags + """ + + def __init__(self): + gdb.Command.__init__(self, 'dump-fn', gdb.COMMAND_USER) + + def invoke(self, arg, from_tty): + # Parse args, check number of args + args = gdb.string_to_argv(arg) + if len(args) >= 1 and args[0] == "/f": + if len(args) == 1: + print ("Missing file argument") + return + filename = args[1] + editor_mode = False + base_arg = 2 + else: + editor = os.getenv("EDITOR", "") + if editor == "": + print ("EDITOR environment variable not defined") + return + editor_mode = True + base_arg = 0 + if len(args) - base_arg > 2: + print ("Too many arguments") + return + + # Set func + if len(args) - base_arg >= 1: + funcname = args[base_arg] + printfuncname = "function %s" % funcname + else: + funcname = "cfun ? cfun->decl : current_function_decl" + printfuncname = "current function" + func = gdb.parse_and_eval(funcname) + if func == 0: + print ("Could not find %s" % printfuncname) + return + func = "(tree)%u" % func + + # Set flags + if len(args) - base_arg >= 2: + flags = gdb.parse_and_eval(args[base_arg + 1]) + else: + flags = 0 + + # Get tempory file, if necessary + if editor_mode: + f = tempfile.NamedTemporaryFile(delete=False, suffix=".txt") + filename = f.name + f.close() + + # Open file + fp = gdb.parse_and_eval("(FILE *) fopen (\"%s\", \"w\")" % filename) + if fp == 0: + print ("Could not open file: %s" % filename) + return + + # Dump function to file + _ = gdb.parse_and_eval("dump_function_to_file (%s, %s, %u)" % + (func, fp, flags)) + + # Close file + ret = gdb.parse_and_eval("(int) fclose (%s)" % fp) + if ret != 0: + print ("Could not close file: %s" % filename) + return + + # Open file in editor, if necessary + if editor_mode: + os.system("( %s \"%s\"; rm \"%s\" ) &" % + (editor, filename, filename)) + +DumpFn() + +class DotFn(gdb.Command): + """ + A custom command to show a gimple/rtl function control flow graph. + By default, it show the current function, but the function can also be + specified. + + Examples of use: + (gdb) dot-fn + (gdb) dot-fn cfun + (gdb) dot-fn cfun 0 + (gdb) dot-fn cfun dump_flags + """ + def __init__(self): + gdb.Command.__init__(self, 'dot-fn', gdb.COMMAND_USER) + + def invoke(self, arg, from_tty): + # Parse args, check number of args + args = gdb.string_to_argv(arg) + if len(args) > 2: + print("Too many arguments") + return + + # Set func + if len(args) >= 1: + funcname = args[0] + printfuncname = "function %s" % funcname + else: + funcname = "cfun" + printfuncname = "current function" + func = gdb.parse_and_eval(funcname) + if func == 0: + print("Could not find %s" % printfuncname) + return + func = "(struct function *)%s" % func + + # Set flags + if len(args) >= 2: + flags = gdb.parse_and_eval(args[1]) + else: + flags = 0 + + # Get temp file + f = tempfile.NamedTemporaryFile(delete=False) + filename = f.name + + # Close and reopen temp file to get C FILE* + f.close() + fp = gdb.parse_and_eval("(FILE *) fopen (\"%s\", \"w\")" % filename) + if fp == 0: + print("Cannot open temp file") + return + + # Write graph to temp file + _ = gdb.parse_and_eval("start_graph_dump (%s, \"\")" % fp) + _ = gdb.parse_and_eval("print_graph_cfg (%s, %s, %u)" + % (fp, func, flags)) + _ = gdb.parse_and_eval("end_graph_dump (%s)" % fp) + + # Close temp file + ret = gdb.parse_and_eval("(int) fclose (%s)" % fp) + if ret != 0: + print("Could not close temp file: %s" % filename) + return + + # Show graph in temp file + os.system("( dot -Tx11 \"%s\"; rm \"%s\" ) &" % (filename, filename)) + +DotFn() print('Successfully loaded GDB hooks for GCC')