2010-12-03 11:19:33 -07:00
|
|
|
# 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.
|
|
|
|
|
2011-05-30 02:02:59 -06:00
|
|
|
"""GDB Pretty printers and convenience functions for Go's runtime structures.
|
2010-12-03 11:19:33 -07:00
|
|
|
|
|
|
|
This script is loaded by GDB when it finds a .debug_gdb_scripts
|
2012-02-29 08:42:25 -07:00
|
|
|
section in the compiled binary. The [68]l linkers emit this with a
|
2010-12-03 11:19:33 -07:00
|
|
|
path to this file based on the path to the runtime package.
|
|
|
|
"""
|
|
|
|
|
2010-12-15 04:00:43 -07:00
|
|
|
# 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.
|
2011-02-20 10:53:23 -07:00
|
|
|
|
2010-12-15 04:00:43 -07:00
|
|
|
|
2014-02-24 08:13:27 -07:00
|
|
|
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
|
2010-12-15 04:00:43 -07:00
|
|
|
# allow to manually reload while developing
|
|
|
|
goobjfile = gdb.current_objfile() or gdb.objfiles()[0]
|
|
|
|
goobjfile.pretty_printers = []
|
|
|
|
|
2018-01-16 13:21:34 -07:00
|
|
|
# G state (runtime2.go)
|
|
|
|
|
|
|
|
def read_runtime_const(varname, default):
|
|
|
|
try:
|
|
|
|
return int(gdb.parse_and_eval(varname))
|
|
|
|
except Exception:
|
|
|
|
return int(default)
|
|
|
|
|
|
|
|
|
|
|
|
G_IDLE = read_runtime_const("'runtime._Gidle'", 0)
|
|
|
|
G_RUNNABLE = read_runtime_const("'runtime._Grunnable'", 1)
|
|
|
|
G_RUNNING = read_runtime_const("'runtime._Grunning'", 2)
|
|
|
|
G_SYSCALL = read_runtime_const("'runtime._Gsyscall'", 3)
|
|
|
|
G_WAITING = read_runtime_const("'runtime._Gwaiting'", 4)
|
|
|
|
G_MORIBUND_UNUSED = read_runtime_const("'runtime._Gmoribund_unused'", 5)
|
|
|
|
G_DEAD = read_runtime_const("'runtime._Gdead'", 6)
|
|
|
|
G_ENQUEUE_UNUSED = read_runtime_const("'runtime._Genqueue_unused'", 7)
|
|
|
|
G_COPYSTACK = read_runtime_const("'runtime._Gcopystack'", 8)
|
|
|
|
G_SCAN = read_runtime_const("'runtime._Gscan'", 0x1000)
|
|
|
|
G_SCANRUNNABLE = G_SCAN+G_RUNNABLE
|
|
|
|
G_SCANRUNNING = G_SCAN+G_RUNNING
|
|
|
|
G_SCANSYSCALL = G_SCAN+G_SYSCALL
|
|
|
|
G_SCANWAITING = G_SCAN+G_WAITING
|
|
|
|
|
|
|
|
sts = {
|
|
|
|
G_IDLE: 'idle',
|
|
|
|
G_RUNNABLE: 'runnable',
|
|
|
|
G_RUNNING: 'running',
|
|
|
|
G_SYSCALL: 'syscall',
|
|
|
|
G_WAITING: 'waiting',
|
|
|
|
G_MORIBUND_UNUSED: 'moribund',
|
|
|
|
G_DEAD: 'dead',
|
|
|
|
G_ENQUEUE_UNUSED: 'enqueue',
|
|
|
|
G_COPYSTACK: 'copystack',
|
|
|
|
G_SCAN: 'scan',
|
|
|
|
G_SCANRUNNABLE: 'runnable+s',
|
|
|
|
G_SCANRUNNING: 'running+s',
|
|
|
|
G_SCANSYSCALL: 'syscall+s',
|
|
|
|
G_SCANWAITING: 'waiting+s',
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-16 19:56:10 -07:00
|
|
|
#
|
|
|
|
# Value wrappers
|
|
|
|
#
|
|
|
|
|
|
|
|
class SliceValue:
|
|
|
|
"Wrapper for slice values."
|
|
|
|
|
|
|
|
def __init__(self, val):
|
|
|
|
self.val = val
|
|
|
|
|
|
|
|
@property
|
|
|
|
def len(self):
|
|
|
|
return int(self.val['len'])
|
|
|
|
|
|
|
|
@property
|
|
|
|
def cap(self):
|
|
|
|
return int(self.val['cap'])
|
|
|
|
|
|
|
|
def __getitem__(self, i):
|
|
|
|
if i < 0 or i >= self.len:
|
|
|
|
raise IndexError(i)
|
|
|
|
ptr = self.val["array"]
|
|
|
|
return (ptr + i).dereference()
|
|
|
|
|
|
|
|
|
2010-12-03 11:19:33 -07:00
|
|
|
#
|
|
|
|
# Pretty Printers
|
|
|
|
#
|
|
|
|
|
2014-02-24 08:13:27 -07:00
|
|
|
|
2010-12-03 11:19:33 -07:00
|
|
|
class StringTypePrinter:
|
|
|
|
"Pretty print Go strings."
|
|
|
|
|
2015-03-30 10:36:49 -06:00
|
|
|
pattern = re.compile(r'^struct string( \*)?$')
|
2010-12-03 11:19:33 -07:00
|
|
|
|
|
|
|
def __init__(self, val):
|
|
|
|
self.val = val
|
|
|
|
|
|
|
|
def display_hint(self):
|
|
|
|
return 'string'
|
|
|
|
|
|
|
|
def to_string(self):
|
2011-02-20 10:53:23 -07:00
|
|
|
l = int(self.val['len'])
|
|
|
|
return self.val['str'].string("utf-8", "ignore", l)
|
2010-12-03 11:19:33 -07:00
|
|
|
|
|
|
|
|
|
|
|
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):
|
2015-02-16 20:04:24 -07:00
|
|
|
sval = SliceValue(self.val)
|
|
|
|
if sval.len > sval.cap:
|
2012-02-29 08:42:25 -07:00
|
|
|
return
|
2015-02-16 20:04:24 -07:00
|
|
|
for idx, item in enumerate(sval):
|
|
|
|
yield ('[{0}]'.format(idx), item)
|
2010-12-03 11:19:33 -07:00
|
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
"""
|
|
|
|
|
2014-02-24 08:13:27 -07:00
|
|
|
pattern = re.compile(r'^map\[.*\].*$')
|
2010-12-03 11:19:33 -07:00
|
|
|
|
|
|
|
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):
|
2015-02-21 10:18:33 -07:00
|
|
|
B = self.val['B']
|
2013-03-29 12:04:07 -06:00
|
|
|
buckets = self.val['buckets']
|
|
|
|
oldbuckets = self.val['oldbuckets']
|
|
|
|
flags = self.val['flags']
|
|
|
|
inttype = self.val['hash0'].type
|
|
|
|
cnt = 0
|
2014-02-24 08:13:27 -07:00
|
|
|
for bucket in xrange(2 ** int(B)):
|
2013-03-29 12:04:07 -06:00
|
|
|
bp = buckets + bucket
|
|
|
|
if oldbuckets:
|
|
|
|
oldbucket = bucket & (2 ** (B - 1) - 1)
|
|
|
|
oldbp = oldbuckets + oldbucket
|
|
|
|
oldb = oldbp.dereference()
|
2014-02-24 08:13:27 -07:00
|
|
|
if (oldb['overflow'].cast(inttype) & 1) == 0: # old bucket not evacuated yet
|
|
|
|
if bucket >= 2 ** (B - 1):
|
|
|
|
continue # already did old bucket
|
2013-03-29 12:04:07 -06:00
|
|
|
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()
|
2014-02-24 08:13:27 -07:00
|
|
|
yield str(cnt), k
|
|
|
|
yield str(cnt + 1), v
|
2013-03-29 12:04:07 -06:00
|
|
|
cnt += 2
|
|
|
|
bp = b['overflow']
|
2010-12-03 11:19:33 -07:00
|
|
|
|
2014-02-24 08:13:27 -07:00
|
|
|
|
2010-12-03 11:19:33 -07:00
|
|
|
class ChanTypePrinter:
|
|
|
|
"""Pretty print chan[T] types.
|
|
|
|
|
2010-12-15 04:00:43 -07:00
|
|
|
Chan-typed go variables are really pointers. dereference them in gdb
|
2010-12-03 11:19:33 -07:00
|
|
|
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):
|
2012-02-29 08:42:25 -07:00
|
|
|
# see chan.c chanbuf(). et is the type stolen from hchan<T>::recvq->first->elem
|
2011-09-29 13:07:38 -06:00
|
|
|
et = [x.type for x in self.val['recvq']['first'].type.target().fields() if x.name == 'elem'][0]
|
2012-02-29 08:42:25 -07:00
|
|
|
ptr = (self.val.address + 1).cast(et.pointer())
|
|
|
|
for i in range(self.val["qcount"]):
|
2011-04-14 07:32:20 -06:00
|
|
|
j = (self.val["recvx"] + i) % self.val["dataqsiz"]
|
2014-02-24 08:13:27 -07:00
|
|
|
yield ('[{0}]'.format(i), (ptr + j).dereference())
|
2011-04-14 07:32:20 -06:00
|
|
|
|
2010-12-03 11:19:33 -07:00
|
|
|
|
|
|
|
#
|
2010-12-15 04:00:43 -07:00
|
|
|
# Register all the *Printer classes above.
|
2010-12-03 11:19:33 -07:00
|
|
|
#
|
|
|
|
|
|
|
|
def makematcher(klass):
|
|
|
|
def matcher(val):
|
|
|
|
try:
|
2010-12-15 04:00:43 -07:00
|
|
|
if klass.pattern.match(str(val.type)):
|
|
|
|
return klass(val)
|
2014-02-24 08:13:27 -07:00
|
|
|
except Exception:
|
2010-12-15 04:00:43 -07:00
|
|
|
pass
|
2010-12-03 11:19:33 -07:00
|
|
|
return matcher
|
|
|
|
|
2014-02-24 08:13:27 -07:00
|
|
|
goobjfile.pretty_printers.extend([makematcher(var) for var in vars().values() if hasattr(var, 'pattern')])
|
2010-12-03 11:19:33 -07:00
|
|
|
|
2018-03-13 12:56:20 -06:00
|
|
|
|
|
|
|
#
|
|
|
|
# Utilities
|
|
|
|
#
|
|
|
|
|
|
|
|
def pc_to_int(pc):
|
|
|
|
# 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:
|
|
|
|
# str(pc) can return things like
|
|
|
|
# "0x429d6c <runtime.gopark+284>", so
|
|
|
|
# chop at first space.
|
|
|
|
pc = int(str(pc).split(None, 1)[0], 16)
|
|
|
|
return pc
|
|
|
|
|
|
|
|
|
2010-12-15 04:00:43 -07:00
|
|
|
#
|
|
|
|
# For reference, this is what we're trying to do:
|
reflect: add ArrayOf, ChanOf, MapOf, SliceOf
In order to add these, we need to be able to find references
to such types that already exist in the binary. To do that, introduce
a new linker section holding a list of the types corresponding to
arrays, chans, maps, and slices.
To offset the storage cost of this list, and to simplify the code,
remove the interface{} header from the representation of a
runtime type. It was used in early versions of the code but was
made obsolete by the kind field: a switch on kind is more efficient
than a type switch.
In the godoc binary, removing the interface{} header cuts two
words from each of about 10,000 types. Adding back the list of pointers
to array, chan, map, and slice types reintroduces one word for
each of about 500 types. On a 64-bit machine, then, this CL *removes*
a net 156 kB of read-only data from the binary.
This CL does not include the needed support for precise garbage
collection. I have created issue 4375 to track that.
This CL also does not set the 'algorithm' - specifically the equality
and copy functions - for a new array correctly, so I have unexported
ArrayOf for now. That is also part of issue 4375.
Fixes #2339.
R=r, remyoudompheng, mirtchovski, iant
CC=golang-dev
https://golang.org/cl/6572043
2012-11-13 11:06:29 -07:00
|
|
|
# eface: p *(*(struct 'runtime.rtype'*)'main.e'->type_->data)->string
|
|
|
|
# iface: p *(*(struct 'runtime.rtype'*)'main.s'->tab->Type->data)->string
|
2010-12-15 04:00:43 -07:00
|
|
|
#
|
|
|
|
# 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:
|
2014-02-24 08:13:27 -07:00
|
|
|
return str(val['tab'].type) == "struct runtime.itab *" and str(val['data'].type) == "void *"
|
|
|
|
except gdb.error:
|
2010-12-15 04:00:43 -07:00
|
|
|
pass
|
|
|
|
|
2014-02-24 08:13:27 -07:00
|
|
|
|
2010-12-15 04:00:43 -07:00
|
|
|
def is_eface(val):
|
|
|
|
try:
|
2014-02-24 08:13:27 -07:00
|
|
|
return str(val['_type'].type) == "struct runtime._type *" and str(val['data'].type) == "void *"
|
|
|
|
except gdb.error:
|
2010-12-15 04:00:43 -07:00
|
|
|
pass
|
|
|
|
|
2014-02-24 08:13:27 -07:00
|
|
|
|
2010-12-15 04:00:43 -07:00
|
|
|
def lookup_type(name):
|
|
|
|
try:
|
|
|
|
return gdb.lookup_type(name)
|
2014-02-24 08:13:27 -07:00
|
|
|
except gdb.error:
|
2010-12-15 04:00:43 -07:00
|
|
|
pass
|
|
|
|
try:
|
|
|
|
return gdb.lookup_type('struct ' + name)
|
2014-02-24 08:13:27 -07:00
|
|
|
except gdb.error:
|
2010-12-15 04:00:43 -07:00
|
|
|
pass
|
|
|
|
try:
|
|
|
|
return gdb.lookup_type('struct ' + name[1:]).pointer()
|
2014-02-24 08:13:27 -07:00
|
|
|
except gdb.error:
|
2010-12-15 04:00:43 -07:00
|
|
|
pass
|
|
|
|
|
2014-02-24 08:13:27 -07:00
|
|
|
|
2012-02-29 08:42:25 -07:00
|
|
|
def iface_commontype(obj):
|
2010-12-15 04:00:43 -07:00
|
|
|
if is_iface(obj):
|
2011-01-31 04:27:28 -07:00
|
|
|
go_type_ptr = obj['tab']['_type']
|
2010-12-15 04:00:43 -07:00
|
|
|
elif is_eface(obj):
|
2011-01-31 04:27:28 -07:00
|
|
|
go_type_ptr = obj['_type']
|
2010-12-15 04:00:43 -07:00
|
|
|
else:
|
|
|
|
return
|
2014-02-24 08:13:27 -07:00
|
|
|
|
2015-02-21 09:35:01 -07:00
|
|
|
return go_type_ptr.cast(gdb.lookup_type("struct reflect.rtype").pointer()).dereference()
|
2014-02-24 08:13:27 -07:00
|
|
|
|
2012-02-29 08:42:25 -07:00
|
|
|
|
|
|
|
def iface_dtype(obj):
|
|
|
|
"Decode type of the data field of an eface or iface struct."
|
reflect: add ArrayOf, ChanOf, MapOf, SliceOf
In order to add these, we need to be able to find references
to such types that already exist in the binary. To do that, introduce
a new linker section holding a list of the types corresponding to
arrays, chans, maps, and slices.
To offset the storage cost of this list, and to simplify the code,
remove the interface{} header from the representation of a
runtime type. It was used in early versions of the code but was
made obsolete by the kind field: a switch on kind is more efficient
than a type switch.
In the godoc binary, removing the interface{} header cuts two
words from each of about 10,000 types. Adding back the list of pointers
to array, chan, map, and slice types reintroduces one word for
each of about 500 types. On a 64-bit machine, then, this CL *removes*
a net 156 kB of read-only data from the binary.
This CL does not include the needed support for precise garbage
collection. I have created issue 4375 to track that.
This CL also does not set the 'algorithm' - specifically the equality
and copy functions - for a new array correctly, so I have unexported
ArrayOf for now. That is also part of issue 4375.
Fixes #2339.
R=r, remyoudompheng, mirtchovski, iant
CC=golang-dev
https://golang.org/cl/6572043
2012-11-13 11:06:29 -07:00
|
|
|
# known issue: dtype_name decoded from runtime.rtype is "nested.Foo"
|
2012-02-29 08:42:25 -07:00
|
|
|
# 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
|
2010-12-15 04:00:43 -07:00
|
|
|
dtype_name = dynamic_go_type['string'].dereference()['str'].string()
|
2011-09-23 02:28:02 -06:00
|
|
|
|
2010-12-15 04:00:43 -07:00
|
|
|
dynamic_gdb_type = lookup_type(dtype_name)
|
2012-02-29 08:42:25 -07:00
|
|
|
if dynamic_gdb_type is None:
|
|
|
|
return
|
2014-02-24 08:13:27 -07:00
|
|
|
|
2012-02-29 08:42:25 -07:00
|
|
|
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:
|
2011-09-23 02:28:02 -06:00
|
|
|
dynamic_gdb_type = dynamic_gdb_type.pointer()
|
|
|
|
|
2010-12-15 04:00:43 -07:00
|
|
|
return dynamic_gdb_type
|
|
|
|
|
2014-02-24 08:13:27 -07:00
|
|
|
|
2011-09-23 02:28:02 -06:00
|
|
|
def iface_dtype_name(obj):
|
|
|
|
"Decode type name of the data field of an eface or iface struct."
|
|
|
|
|
2012-02-29 08:42:25 -07:00
|
|
|
dynamic_go_type = iface_commontype(obj)
|
|
|
|
if dynamic_go_type is None:
|
2011-09-23 02:28:02 -06:00
|
|
|
return
|
|
|
|
return dynamic_go_type['string'].dereference()['str'].string()
|
|
|
|
|
2010-12-15 04:00:43 -07:00
|
|
|
|
|
|
|
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):
|
2011-03-28 09:34:22 -06:00
|
|
|
if self.val['data'] == 0:
|
|
|
|
return 0x0
|
2010-12-15 04:00:43 -07:00
|
|
|
try:
|
|
|
|
dtype = iface_dtype(self.val)
|
2014-02-24 08:13:27 -07:00
|
|
|
except Exception:
|
2010-12-15 04:00:43 -07:00
|
|
|
return "<bad dynamic type>"
|
2011-09-23 02:28:02 -06:00
|
|
|
|
2012-02-29 08:42:25 -07:00
|
|
|
if dtype is None: # trouble looking up, print something reasonable
|
2014-02-24 08:13:27 -07:00
|
|
|
return "({0}){0}".format(iface_dtype_name(self.val), self.val['data'])
|
2011-09-23 02:28:02 -06:00
|
|
|
|
2010-12-15 04:00:43 -07:00
|
|
|
try:
|
|
|
|
return self.val['data'].cast(dtype).dereference()
|
2014-02-24 08:13:27 -07:00
|
|
|
except Exception:
|
2010-12-15 04:00:43 -07:00
|
|
|
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)
|
2010-12-03 11:19:33 -07:00
|
|
|
|
|
|
|
#
|
|
|
|
# Convenience Functions
|
|
|
|
#
|
|
|
|
|
2014-02-24 08:13:27 -07:00
|
|
|
|
2010-12-03 11:19:33 -07:00
|
|
|
class GoLenFunc(gdb.Function):
|
|
|
|
"Length of strings, slices, maps or channels"
|
|
|
|
|
2014-02-24 08:13:27 -07:00
|
|
|
how = ((StringTypePrinter, 'len'), (SliceTypePrinter, 'len'), (MapTypePrinter, 'count'), (ChanTypePrinter, 'qcount'))
|
2010-12-03 11:19:33 -07:00
|
|
|
|
|
|
|
def __init__(self):
|
2014-02-24 08:13:27 -07:00
|
|
|
gdb.Function.__init__(self, "len")
|
2010-12-03 11:19:33 -07:00
|
|
|
|
|
|
|
def invoke(self, obj):
|
|
|
|
typename = str(obj.type)
|
2010-12-15 04:00:43 -07:00
|
|
|
for klass, fld in self.how:
|
2010-12-03 11:19:33 -07:00
|
|
|
if klass.pattern.match(typename):
|
|
|
|
return obj[fld]
|
|
|
|
|
2014-02-24 08:13:27 -07:00
|
|
|
|
2010-12-03 11:19:33 -07:00
|
|
|
class GoCapFunc(gdb.Function):
|
|
|
|
"Capacity of slices or channels"
|
|
|
|
|
2014-02-24 08:13:27 -07:00
|
|
|
how = ((SliceTypePrinter, 'cap'), (ChanTypePrinter, 'dataqsiz'))
|
2010-12-03 11:19:33 -07:00
|
|
|
|
|
|
|
def __init__(self):
|
2014-02-24 08:13:27 -07:00
|
|
|
gdb.Function.__init__(self, "cap")
|
2010-12-03 11:19:33 -07:00
|
|
|
|
|
|
|
def invoke(self, obj):
|
|
|
|
typename = str(obj.type)
|
2010-12-15 04:00:43 -07:00
|
|
|
for klass, fld in self.how:
|
2010-12-03 11:19:33 -07:00
|
|
|
if klass.pattern.match(typename):
|
|
|
|
return obj[fld]
|
|
|
|
|
2014-02-24 08:13:27 -07:00
|
|
|
|
2010-12-15 04:00:43 -07:00
|
|
|
class DTypeFunc(gdb.Function):
|
|
|
|
"""Cast Interface values to their dynamic type.
|
|
|
|
|
|
|
|
For non-interface types this behaves as the identity operation.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self):
|
2014-02-24 08:13:27 -07:00
|
|
|
gdb.Function.__init__(self, "dtype")
|
2010-12-15 04:00:43 -07:00
|
|
|
|
|
|
|
def invoke(self, obj):
|
|
|
|
try:
|
|
|
|
return obj['data'].cast(iface_dtype(obj))
|
2014-02-24 08:13:27 -07:00
|
|
|
except gdb.error:
|
2010-12-15 04:00:43 -07:00
|
|
|
pass
|
|
|
|
return obj
|
|
|
|
|
|
|
|
#
|
|
|
|
# Commands
|
|
|
|
#
|
|
|
|
|
|
|
|
def linked_list(ptr, linkfield):
|
|
|
|
while ptr:
|
|
|
|
yield ptr
|
|
|
|
ptr = ptr[linkfield]
|
|
|
|
|
|
|
|
|
|
|
|
class GoroutinesCmd(gdb.Command):
|
|
|
|
"List all goroutines."
|
|
|
|
|
|
|
|
def __init__(self):
|
2014-02-24 08:13:27 -07:00
|
|
|
gdb.Command.__init__(self, "info goroutines", gdb.COMMAND_STACK, gdb.COMPLETE_NONE)
|
2010-12-15 04:00:43 -07:00
|
|
|
|
2014-02-24 08:13:27 -07:00
|
|
|
def invoke(self, _arg, _from_tty):
|
2010-12-15 04:00:43 -07:00
|
|
|
# args = gdb.string_to_argv(arg)
|
|
|
|
vp = gdb.lookup_type('void').pointer()
|
2015-02-16 19:56:10 -07:00
|
|
|
for ptr in SliceValue(gdb.parse_and_eval("'runtime.allgs'")):
|
2018-01-16 13:21:34 -07:00
|
|
|
if ptr['atomicstatus'] == G_DEAD:
|
2010-12-15 04:00:43 -07:00
|
|
|
continue
|
|
|
|
s = ' '
|
2011-03-28 09:34:22 -06:00
|
|
|
if ptr['m']:
|
2010-12-15 04:00:43 -07:00
|
|
|
s = '*'
|
2012-02-29 08:42:25 -07:00
|
|
|
pc = ptr['sched']['pc'].cast(vp)
|
2018-03-13 12:56:20 -06:00
|
|
|
pc = pc_to_int(pc)
|
2014-02-24 08:13:27 -07:00
|
|
|
blk = gdb.block_for_pc(pc)
|
2018-01-16 13:21:34 -07:00
|
|
|
status = int(ptr['atomicstatus'])
|
|
|
|
st = sts.get(status, "unknown(%d)" % status)
|
|
|
|
print(s, ptr['goid'], "{0:8s}".format(st), blk.function)
|
2014-02-24 08:13:27 -07:00
|
|
|
|
2010-12-15 04:00:43 -07:00
|
|
|
|
|
|
|
def find_goroutine(goid):
|
2014-02-24 08:13:27 -07:00
|
|
|
"""
|
|
|
|
find_goroutine attempts to find the goroutine identified by goid.
|
2018-03-14 18:23:46 -06:00
|
|
|
It returns a tuple of gdb.Value's representing the stack pointer
|
2014-02-24 08:13:27 -07:00
|
|
|
and program counter pointer for the goroutine.
|
|
|
|
|
|
|
|
@param int goid
|
|
|
|
|
|
|
|
@return tuple (gdb.Value, gdb.Value)
|
|
|
|
"""
|
2010-12-15 04:00:43 -07:00
|
|
|
vp = gdb.lookup_type('void').pointer()
|
2015-02-16 19:56:10 -07:00
|
|
|
for ptr in SliceValue(gdb.parse_and_eval("'runtime.allgs'")):
|
2018-01-16 13:21:34 -07:00
|
|
|
if ptr['atomicstatus'] == G_DEAD:
|
2010-12-15 04:00:43 -07:00
|
|
|
continue
|
|
|
|
if ptr['goid'] == goid:
|
2017-06-07 08:30:49 -06:00
|
|
|
break
|
|
|
|
else:
|
|
|
|
return None, None
|
|
|
|
# Get the goroutine's saved state.
|
|
|
|
pc, sp = ptr['sched']['pc'], ptr['sched']['sp']
|
2018-01-16 13:31:12 -07:00
|
|
|
status = ptr['atomicstatus']&~G_SCAN
|
|
|
|
# Goroutine is not running nor in syscall, so use the info in goroutine
|
|
|
|
if status != G_RUNNING and status != G_SYSCALL:
|
2017-06-07 08:30:49 -06:00
|
|
|
return pc.cast(vp), sp.cast(vp)
|
2018-01-16 13:31:12 -07:00
|
|
|
|
2017-06-07 08:30:49 -06:00
|
|
|
# If the goroutine is in a syscall, use syscallpc/sp.
|
|
|
|
pc, sp = ptr['syscallpc'], ptr['syscallsp']
|
|
|
|
if sp != 0:
|
|
|
|
return pc.cast(vp), sp.cast(vp)
|
|
|
|
# Otherwise, the goroutine is running, so it doesn't have
|
|
|
|
# saved scheduler state. Find G's OS thread.
|
|
|
|
m = ptr['m']
|
|
|
|
if m == 0:
|
|
|
|
return None, None
|
|
|
|
for thr in gdb.selected_inferior().threads():
|
|
|
|
if thr.ptid[1] == m['procid']:
|
|
|
|
break
|
2017-06-08 10:05:31 -06:00
|
|
|
else:
|
2017-06-07 08:30:49 -06:00
|
|
|
return None, None
|
|
|
|
# Get scheduler state from the G's OS thread state.
|
|
|
|
curthr = gdb.selected_thread()
|
|
|
|
try:
|
|
|
|
thr.switch()
|
|
|
|
pc = gdb.parse_and_eval('$pc')
|
|
|
|
sp = gdb.parse_and_eval('$sp')
|
|
|
|
finally:
|
|
|
|
curthr.switch()
|
|
|
|
return pc.cast(vp), sp.cast(vp)
|
2010-12-15 04:00:43 -07:00
|
|
|
|
|
|
|
|
|
|
|
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):
|
2014-02-24 08:13:27 -07:00
|
|
|
gdb.Command.__init__(self, "goroutine", gdb.COMMAND_STACK, gdb.COMPLETE_NONE)
|
2010-12-15 04:00:43 -07:00
|
|
|
|
2014-02-24 08:13:27 -07:00
|
|
|
def invoke(self, arg, _from_tty):
|
2010-12-15 04:00:43 -07:00
|
|
|
goid, cmd = arg.split(None, 1)
|
2012-11-19 11:22:47 -07:00
|
|
|
goid = gdb.parse_and_eval(goid)
|
2010-12-15 04:00:43 -07:00
|
|
|
pc, sp = find_goroutine(int(goid))
|
|
|
|
if not pc:
|
2014-02-24 08:13:27 -07:00
|
|
|
print("No such goroutine: ", goid)
|
2010-12-15 04:00:43 -07:00
|
|
|
return
|
2018-03-13 12:56:20 -06:00
|
|
|
pc = pc_to_int(pc)
|
2010-12-15 04:00:43 -07:00
|
|
|
save_frame = gdb.selected_frame()
|
|
|
|
gdb.parse_and_eval('$save_sp = $sp')
|
2016-06-08 20:22:35 -06:00
|
|
|
gdb.parse_and_eval('$save_pc = $pc')
|
2014-02-24 08:13:27 -07:00
|
|
|
gdb.parse_and_eval('$sp = {0}'.format(str(sp)))
|
2016-06-08 20:22:35 -06:00
|
|
|
gdb.parse_and_eval('$pc = {0}'.format(str(pc)))
|
2010-12-15 04:00:43 -07:00
|
|
|
try:
|
|
|
|
gdb.execute(cmd)
|
2012-02-29 08:42:25 -07:00
|
|
|
finally:
|
|
|
|
gdb.parse_and_eval('$sp = $save_sp')
|
2016-06-08 20:22:35 -06:00
|
|
|
gdb.parse_and_eval('$pc = $save_pc')
|
2012-02-29 08:42:25 -07:00
|
|
|
save_frame.select()
|
2010-12-15 04:00:43 -07:00
|
|
|
|
|
|
|
|
|
|
|
class GoIfaceCmd(gdb.Command):
|
|
|
|
"Print Static and dynamic interface types"
|
|
|
|
|
|
|
|
def __init__(self):
|
2014-02-24 08:13:27 -07:00
|
|
|
gdb.Command.__init__(self, "iface", gdb.COMMAND_DATA, gdb.COMPLETE_SYMBOL)
|
2010-12-15 04:00:43 -07:00
|
|
|
|
2014-02-24 08:13:27 -07:00
|
|
|
def invoke(self, arg, _from_tty):
|
2010-12-15 04:00:43 -07:00
|
|
|
for obj in gdb.string_to_argv(arg):
|
|
|
|
try:
|
|
|
|
#TODO fix quoting for qualified variable names
|
2014-02-24 08:13:27 -07:00
|
|
|
obj = gdb.parse_and_eval(str(obj))
|
|
|
|
except Exception as e:
|
|
|
|
print("Can't parse ", obj, ": ", e)
|
2010-12-15 04:00:43 -07:00
|
|
|
continue
|
|
|
|
|
2012-02-29 08:42:25 -07:00
|
|
|
if obj['data'] == 0:
|
|
|
|
dtype = "nil"
|
|
|
|
else:
|
|
|
|
dtype = iface_dtype(obj)
|
2014-02-24 08:13:27 -07:00
|
|
|
|
2012-02-29 08:42:25 -07:00
|
|
|
if dtype is None:
|
2014-02-24 08:13:27 -07:00
|
|
|
print("Not an interface: ", obj.type)
|
2010-12-15 04:00:43 -07:00
|
|
|
continue
|
|
|
|
|
2014-02-24 08:13:27 -07:00
|
|
|
print("{0}: {1}".format(obj.type, dtype))
|
2010-12-15 04:00:43 -07:00
|
|
|
|
|
|
|
# TODO: print interface's methods and dynamic type's func pointers thereof.
|
2014-02-24 08:13:27 -07:00
|
|
|
#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"
|
2010-12-15 04:00:43 -07:00
|
|
|
# so Itype will start with a commontype which has kind = interface
|
|
|
|
|
2010-12-03 11:19:33 -07:00
|
|
|
#
|
2011-05-30 02:02:59 -06:00
|
|
|
# Register all convenience functions and CLI commands
|
2010-12-03 11:19:33 -07:00
|
|
|
#
|
2013-09-10 11:00:08 -06:00
|
|
|
GoLenFunc()
|
|
|
|
GoCapFunc()
|
|
|
|
DTypeFunc()
|
|
|
|
GoroutinesCmd()
|
|
|
|
GoroutineCmd()
|
|
|
|
GoIfaceCmd()
|