diff options
author | Tianon Gravi <admwiggin@gmail.com> | 2015-01-15 11:54:00 -0700 |
---|---|---|
committer | Tianon Gravi <admwiggin@gmail.com> | 2015-01-15 11:54:00 -0700 |
commit | f154da9e12608589e8d5f0508f908a0c3e88a1bb (patch) | |
tree | f8255d51e10c6f1e0ed69702200b966c9556a431 /src/pkg/runtime/runtime-gdb.py | |
parent | 8d8329ed5dfb9622c82a9fbec6fd99a580f9c9f6 (diff) | |
download | golang-upstream/1.4.tar.gz |
Imported Upstream version 1.4upstream/1.4
Diffstat (limited to 'src/pkg/runtime/runtime-gdb.py')
-rw-r--r-- | src/pkg/runtime/runtime-gdb.py | 478 |
1 files changed, 0 insertions, 478 deletions
diff --git a/src/pkg/runtime/runtime-gdb.py b/src/pkg/runtime/runtime-gdb.py deleted file mode 100644 index eedac7cf4..000000000 --- a/src/pkg/runtime/runtime-gdb.py +++ /dev/null @@ -1,478 +0,0 @@ -# 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. - - -from __future__ import print_function -import re -import sys - -print("Loading Go Runtime support.", file=sys.stderr) -#http://python3porting.com/differences.html -if sys.version > '3': - xrange = range -# 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): - if self.val["len"] > self.val["cap"]: - return - ptr = self.val["array"] - for idx in range(int(self.val["len"])): - yield ('[{0}]'.format(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'^map\[.*\].*$') - - 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): - B = self.val['b'] - buckets = self.val['buckets'] - oldbuckets = self.val['oldbuckets'] - flags = self.val['flags'] - inttype = self.val['hash0'].type - cnt = 0 - for bucket in xrange(2 ** int(B)): - bp = buckets + bucket - if oldbuckets: - oldbucket = bucket & (2 ** (B - 1) - 1) - oldbp = oldbuckets + oldbucket - oldb = oldbp.dereference() - if (oldb['overflow'].cast(inttype) & 1) == 0: # old bucket not evacuated yet - if bucket >= 2 ** (B - 1): - continue # already did old bucket - bp = oldbp - while bp: - b = bp.dereference() - for i in xrange(8): - if b['tophash'][i] != 0: - k = b['keys'][i] - v = b['values'][i] - if flags & 1: - k = k.dereference() - if flags & 2: - v = v.dereference() - yield str(cnt), k - yield str(cnt + 1), v - cnt += 2 - bp = b['overflow'] - - -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 is the type stolen from hchan<T>::recvq->first->elem - et = [x.type for x in self.val['recvq']['first'].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 ('[{0}]'.format(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 Exception: - pass - return matcher - -goobjfile.pretty_printers.extend([makematcher(var) for var in vars().values() if hasattr(var, 'pattern')]) - -# -# For reference, this is what we're trying to do: -# eface: p *(*(struct 'runtime.rtype'*)'main.e'->type_->data)->string -# iface: p *(*(struct 'runtime.rtype'*)'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 gdb.error: - pass - - -def is_eface(val): - try: - return str(val['_type'].type) == "struct runtime._type *" and str(val['data'].type) == "void *" - except gdb.error: - pass - - -def lookup_type(name): - try: - return gdb.lookup_type(name) - except gdb.error: - pass - try: - return gdb.lookup_type('struct ' + name) - except gdb.error: - pass - try: - return gdb.lookup_type('struct ' + name[1:]).pointer() - except gdb.error: - pass - -_rctp_type = gdb.lookup_type("struct runtime.rtype").pointer() - - -def iface_commontype(obj): - if is_iface(obj): - go_type_ptr = obj['tab']['_type'] - elif is_eface(obj): - go_type_ptr = obj['_type'] - else: - return - - return go_type_ptr.cast(_rctp_type).dereference() - - -def iface_dtype(obj): - "Decode type of the data field of an eface or iface struct." - # known issue: dtype_name decoded from runtime.rtype is "nested.Foo" - # but the dwarf table lists it as "full/path/to/nested.Foo" - - dynamic_go_type = iface_commontype(obj) - if dynamic_go_type is None: - return - dtype_name = dynamic_go_type['string'].dereference()['str'].string() - - dynamic_gdb_type = lookup_type(dtype_name) - if dynamic_gdb_type is None: - return - - type_size = int(dynamic_go_type['size']) - uintptr_size = int(dynamic_go_type['size'].type.sizeof) # size is itself an uintptr - if type_size > uintptr_size: - dynamic_gdb_type = dynamic_gdb_type.pointer() - - return dynamic_gdb_type - - -def iface_dtype_name(obj): - "Decode type name of the data field of an eface or iface struct." - - dynamic_go_type = iface_commontype(obj) - if dynamic_go_type is None: - return - return dynamic_go_type['string'].dereference()['str'].string() - - -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 Exception: - return "<bad dynamic type>" - - if dtype is None: # trouble looking up, print something reasonable - return "({0}){0}".format(iface_dtype_name(self.val), self.val['data']) - - try: - return self.val['data'].cast(dtype).dereference() - except Exception: - 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): - gdb.Function.__init__(self, "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): - gdb.Function.__init__(self, "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): - gdb.Function.__init__(self, "dtype") - - def invoke(self, obj): - try: - return obj['data'].cast(iface_dtype(obj)) - except gdb.error: - 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): - gdb.Command.__init__(self, "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) - # python2 will not cast pc (type void*) to an int cleanly - # instead python2 and python3 work with the hex string representation - # of the void pointer which we can parse back into an int. - # int(pc) will not work. - try: - #python3 / newer versions of gdb - pc = int(pc) - except gdb.error: - pc = int(str(pc), 16) - blk = gdb.block_for_pc(pc) - print(s, ptr['goid'], "{0:8s}".format(sts[int(ptr['status'])]), blk.function) - - -def find_goroutine(goid): - """ - find_goroutine attempts to find the goroutine identified by goid. - It returns a touple of gdv.Value's representing the stack pointer - and program counter pointer for the goroutine. - - @param int goid - - @return tuple (gdb.Value, gdb.Value) - """ - 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): - gdb.Command.__init__(self, "goroutine", gdb.COMMAND_STACK, gdb.COMPLETE_NONE) - - def invoke(self, arg, _from_tty): - goid, cmd = arg.split(None, 1) - goid = gdb.parse_and_eval(goid) - pc, sp = find_goroutine(int(goid)) - if not pc: - print("No such goroutine: ", goid) - return - try: - #python3 / newer versions of gdb - pc = int(pc) - except gdb.error: - pc = int(str(pc), 16) - save_frame = gdb.selected_frame() - gdb.parse_and_eval('$save_pc = $pc') - gdb.parse_and_eval('$save_sp = $sp') - gdb.parse_and_eval('$pc = {0}'.format(str(pc))) - gdb.parse_and_eval('$sp = {0}'.format(str(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): - gdb.Command.__init__(self, "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(str(obj)) - except Exception as e: - print("Can't parse ", obj, ": ", e) - continue - - if obj['data'] == 0: - dtype = "nil" - else: - dtype = iface_dtype(obj) - - if dtype is None: - print("Not an interface: ", obj.type) - continue - - print("{0}: {1}".format(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 -# -GoLenFunc() -GoCapFunc() -DTypeFunc() -GoroutinesCmd() -GoroutineCmd() -GoIfaceCmd() |