diff options
author | Ondřej Surý <ondrej@sury.org> | 2011-09-13 13:13:40 +0200 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2011-09-13 13:13:40 +0200 |
commit | 5ff4c17907d5b19510a62e08fd8d3b11e62b431d (patch) | |
tree | c0650497e988f47be9c6f2324fa692a52dea82e1 /src/pkg/runtime/runtime-gdb.py | |
parent | 80f18fc933cf3f3e829c5455a1023d69f7b86e52 (diff) | |
download | golang-5ff4c17907d5b19510a62e08fd8d3b11e62b431d.tar.gz |
Imported Upstream version 60upstream/60
Diffstat (limited to 'src/pkg/runtime/runtime-gdb.py')
-rw-r--r-- | src/pkg/runtime/runtime-gdb.py | 400 |
1 files changed, 400 insertions, 0 deletions
diff --git a/src/pkg/runtime/runtime-gdb.py b/src/pkg/runtime/runtime-gdb.py new file mode 100644 index 000000000..a96f3f382 --- /dev/null +++ b/src/pkg/runtime/runtime-gdb.py @@ -0,0 +1,400 @@ +# Copyright 2010 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +"""GDB Pretty printers and convenience functions for Go's runtime structures. + +This script is loaded by GDB when it finds a .debug_gdb_scripts +section in the compiled binary. The [68]l linkers emit this with a +path to this file based on the path to the runtime package. +""" + +# Known issues: +# - pretty printing only works for the 'native' strings. E.g. 'type +# foo string' will make foo a plain struct in the eyes of gdb, +# circumventing the pretty print triggering. + + +import sys, re + +print >>sys.stderr, "Loading Go Runtime support." + +# allow to manually reload while developing +goobjfile = gdb.current_objfile() or gdb.objfiles()[0] +goobjfile.pretty_printers = [] + +# +# Pretty Printers +# + +class StringTypePrinter: + "Pretty print Go strings." + + pattern = re.compile(r'^struct string$') + + def __init__(self, val): + self.val = val + + def display_hint(self): + return 'string' + + def to_string(self): + l = int(self.val['len']) + return self.val['str'].string("utf-8", "ignore", l) + + +class SliceTypePrinter: + "Pretty print slices." + + pattern = re.compile(r'^struct \[\]') + + def __init__(self, val): + self.val = val + + def display_hint(self): + return 'array' + + def to_string(self): + return str(self.val.type)[6:] # skip 'struct ' + + def children(self): + ptr = self.val["array"] + for idx in range(self.val["len"]): + yield ('[%d]' % idx, (ptr + idx).dereference()) + + +class MapTypePrinter: + """Pretty print map[K]V types. + + Map-typed go variables are really pointers. dereference them in gdb + to inspect their contents with this pretty printer. + """ + + pattern = re.compile(r'^struct hash<.*>$') + + def __init__(self, val): + self.val = val + + def display_hint(self): + return 'map' + + def to_string(self): + return str(self.val.type) + + def children(self): + stab = self.val['st'] + i = 0 + for v in self.traverse_hash(stab): + yield ("[%d]" % i, v['key']) + yield ("[%d]" % (i + 1), v['val']) + i += 2 + + def traverse_hash(self, stab): + ptr = stab['entry'].address + end = stab['end'] + while ptr < end: + v = ptr.dereference() + ptr = ptr + 1 + if v['hash'] == 0: continue + if v['hash'] & 63 == 63: # subtable + for v in self.traverse_hash(v['key'].cast(self.val['st'].type)): + yield v + else: + yield v + + +class ChanTypePrinter: + """Pretty print chan[T] types. + + Chan-typed go variables are really pointers. dereference them in gdb + to inspect their contents with this pretty printer. + """ + + pattern = re.compile(r'^struct hchan<.*>$') + + def __init__(self, val): + self.val = val + + def display_hint(self): + return 'array' + + def to_string(self): + return str(self.val.type) + + def children(self): + # see chan.c chanbuf() + et = [x.type for x in self.val['free'].type.target().fields() if x.name == 'elem'][0] + ptr = (self.val.address + 1).cast(et.pointer()) + for i in range(self.val["qcount"]): + j = (self.val["recvx"] + i) % self.val["dataqsiz"] + yield ('[%d]' % i, (ptr + j).dereference()) + + +# +# Register all the *Printer classes above. +# + +def makematcher(klass): + def matcher(val): + try: + if klass.pattern.match(str(val.type)): + return klass(val) + except: + pass + return matcher + +goobjfile.pretty_printers.extend([makematcher(k) for k in vars().values() if hasattr(k, 'pattern')]) + +# +# For reference, this is what we're trying to do: +# eface: p *(*(struct 'runtime.commonType'*)'main.e'->type_->data)->string +# iface: p *(*(struct 'runtime.commonType'*)'main.s'->tab->Type->data)->string +# +# interface types can't be recognized by their name, instead we check +# if they have the expected fields. Unfortunately the mapping of +# fields to python attributes in gdb.py isn't complete: you can't test +# for presence other than by trapping. + + +def is_iface(val): + try: + return str(val['tab'].type) == "struct runtime.itab *" \ + and str(val['data'].type) == "void *" + except: + pass + +def is_eface(val): + try: + return str(val['_type'].type) == "struct runtime._type *" \ + and str(val['data'].type) == "void *" + except: + pass + +def lookup_type(name): + try: + return gdb.lookup_type(name) + except: + pass + try: + return gdb.lookup_type('struct ' + name) + except: + pass + try: + return gdb.lookup_type('struct ' + name[1:]).pointer() + except: + pass + + +def iface_dtype(obj): + "Decode type of the data field of an eface or iface struct." + + if is_iface(obj): + go_type_ptr = obj['tab']['_type'] + elif is_eface(obj): + go_type_ptr = obj['_type'] + else: + return + + ct = gdb.lookup_type("struct runtime.commonType").pointer() + dynamic_go_type = go_type_ptr['ptr'].cast(ct).dereference() + dtype_name = dynamic_go_type['string'].dereference()['str'].string() + type_size = int(dynamic_go_type['size']) + uintptr_size = int(dynamic_go_type['size'].type.sizeof) # size is itself an uintptr + dynamic_gdb_type = lookup_type(dtype_name) + if type_size > uintptr_size: + dynamic_gdb_type = dynamic_gdb_type.pointer() + return dynamic_gdb_type + + +class IfacePrinter: + """Pretty print interface values + + Casts the data field to the appropriate dynamic type.""" + + def __init__(self, val): + self.val = val + + def display_hint(self): + return 'string' + + def to_string(self): + if self.val['data'] == 0: + return 0x0 + try: + dtype = iface_dtype(self.val) + except: + return "<bad dynamic type>" + try: + return self.val['data'].cast(dtype).dereference() + except: + pass + return self.val['data'].cast(dtype) + + +def ifacematcher(val): + if is_iface(val) or is_eface(val): + return IfacePrinter(val) + +goobjfile.pretty_printers.append(ifacematcher) + +# +# Convenience Functions +# + +class GoLenFunc(gdb.Function): + "Length of strings, slices, maps or channels" + + how = ((StringTypePrinter, 'len' ), + (SliceTypePrinter, 'len'), + (MapTypePrinter, 'count'), + (ChanTypePrinter, 'qcount')) + + def __init__(self): + super(GoLenFunc, self).__init__("len") + + def invoke(self, obj): + typename = str(obj.type) + for klass, fld in self.how: + if klass.pattern.match(typename): + return obj[fld] + +class GoCapFunc(gdb.Function): + "Capacity of slices or channels" + + how = ((SliceTypePrinter, 'cap'), + (ChanTypePrinter, 'dataqsiz')) + + def __init__(self): + super(GoCapFunc, self).__init__("cap") + + def invoke(self, obj): + typename = str(obj.type) + for klass, fld in self.how: + if klass.pattern.match(typename): + return obj[fld] + +class DTypeFunc(gdb.Function): + """Cast Interface values to their dynamic type. + + For non-interface types this behaves as the identity operation. + """ + + def __init__(self): + super(DTypeFunc, self).__init__("dtype") + + def invoke(self, obj): + try: + return obj['data'].cast(iface_dtype(obj)) + except: + pass + return obj + +# +# Commands +# + +sts = ( 'idle', 'runnable', 'running', 'syscall', 'waiting', 'moribund', 'dead', 'recovery') + +def linked_list(ptr, linkfield): + while ptr: + yield ptr + ptr = ptr[linkfield] + + +class GoroutinesCmd(gdb.Command): + "List all goroutines." + + def __init__(self): + super(GoroutinesCmd, self).__init__("info goroutines", gdb.COMMAND_STACK, gdb.COMPLETE_NONE) + + def invoke(self, arg, from_tty): + # args = gdb.string_to_argv(arg) + vp = gdb.lookup_type('void').pointer() + for ptr in linked_list(gdb.parse_and_eval("'runtime.allg'"), 'alllink'): + if ptr['status'] == 6: # 'gdead' + continue + s = ' ' + if ptr['m']: + s = '*' + pc = ptr['sched']['pc'].cast(vp) + sp = ptr['sched']['sp'].cast(vp) + blk = gdb.block_for_pc(long((pc))) + print s, ptr['goid'], "%8s" % sts[long((ptr['status']))], blk.function + +def find_goroutine(goid): + vp = gdb.lookup_type('void').pointer() + for ptr in linked_list(gdb.parse_and_eval("'runtime.allg'"), 'alllink'): + if ptr['status'] == 6: # 'gdead' + continue + if ptr['goid'] == goid: + return [ptr['sched'][x].cast(vp) for x in 'pc', 'sp'] + return None, None + + +class GoroutineCmd(gdb.Command): + """Execute gdb command in the context of goroutine <goid>. + + Switch PC and SP to the ones in the goroutine's G structure, + execute an arbitrary gdb command, and restore PC and SP. + + Usage: (gdb) goroutine <goid> <gdbcmd> + + Note that it is ill-defined to modify state in the context of a goroutine. + Restrict yourself to inspecting values. + """ + + def __init__(self): + super(GoroutineCmd, self).__init__("goroutine", gdb.COMMAND_STACK, gdb.COMPLETE_NONE) + + def invoke(self, arg, from_tty): + goid, cmd = arg.split(None, 1) + pc, sp = find_goroutine(int(goid)) + if not pc: + print "No such goroutine: ", goid + return + save_frame = gdb.selected_frame() + gdb.parse_and_eval('$save_pc = $pc') + gdb.parse_and_eval('$save_sp = $sp') + gdb.parse_and_eval('$pc = 0x%x' % long(pc)) + gdb.parse_and_eval('$sp = 0x%x' % long(sp)) + try: + gdb.execute(cmd) + finally: + gdb.parse_and_eval('$pc = $save_pc') + gdb.parse_and_eval('$sp = $save_sp') + save_frame.select() + + +class GoIfaceCmd(gdb.Command): + "Print Static and dynamic interface types" + + def __init__(self): + super(GoIfaceCmd, self).__init__("iface", gdb.COMMAND_DATA, gdb.COMPLETE_SYMBOL) + + def invoke(self, arg, from_tty): + for obj in gdb.string_to_argv(arg): + try: + #TODO fix quoting for qualified variable names + obj = gdb.parse_and_eval("%s" % obj) + except Exception, e: + print "Can't parse ", obj, ": ", e + continue + + dtype = iface_dtype(obj) + if not dtype: + print "Not an interface: ", obj.type + continue + + print "%s: %s" % (obj.type, dtype) + +# TODO: print interface's methods and dynamic type's func pointers thereof. +#rsc: "to find the number of entries in the itab's Fn field look at itab.inter->numMethods +#i am sure i have the names wrong but look at the interface type and its method count" +# so Itype will start with a commontype which has kind = interface + +# +# Register all convenience functions and CLI commands +# +for k in vars().values(): + if hasattr(k, 'invoke'): + k() |