Home | History | Annotate | Download | only in runtime
      1 # Copyright 2010 The Go Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style
      3 # license that can be found in the LICENSE file.
      4 
      5 """GDB Pretty printers and convenience functions for Go's runtime structures.
      6 
      7 This script is loaded by GDB when it finds a .debug_gdb_scripts
      8 section in the compiled binary. The [68]l linkers emit this with a
      9 path to this file based on the path to the runtime package.
     10 """
     11 
     12 # Known issues:
     13 #    - pretty printing only works for the 'native' strings. E.g. 'type
     14 #      foo string' will make foo a plain struct in the eyes of gdb,
     15 #      circumventing the pretty print triggering.
     16 
     17 
     18 from __future__ import print_function
     19 import re
     20 import sys
     21 
     22 print("Loading Go Runtime support.", file=sys.stderr)
     23 #http://python3porting.com/differences.html
     24 if sys.version > '3':
     25 	xrange = range
     26 # allow to manually reload while developing
     27 goobjfile = gdb.current_objfile() or gdb.objfiles()[0]
     28 goobjfile.pretty_printers = []
     29 
     30 #
     31 #  Value wrappers
     32 #
     33 
     34 class SliceValue:
     35 	"Wrapper for slice values."
     36 
     37 	def __init__(self, val):
     38 		self.val = val
     39 
     40 	@property
     41 	def len(self):
     42 		return int(self.val['len'])
     43 
     44 	@property
     45 	def cap(self):
     46 		return int(self.val['cap'])
     47 
     48 	def __getitem__(self, i):
     49 		if i < 0 or i >= self.len:
     50 			raise IndexError(i)
     51 		ptr = self.val["array"]
     52 		return (ptr + i).dereference()
     53 
     54 
     55 #
     56 #  Pretty Printers
     57 #
     58 
     59 
     60 class StringTypePrinter:
     61 	"Pretty print Go strings."
     62 
     63 	pattern = re.compile(r'^struct string( \*)?$')
     64 
     65 	def __init__(self, val):
     66 		self.val = val
     67 
     68 	def display_hint(self):
     69 		return 'string'
     70 
     71 	def to_string(self):
     72 		l = int(self.val['len'])
     73 		return self.val['str'].string("utf-8", "ignore", l)
     74 
     75 
     76 class SliceTypePrinter:
     77 	"Pretty print slices."
     78 
     79 	pattern = re.compile(r'^struct \[\]')
     80 
     81 	def __init__(self, val):
     82 		self.val = val
     83 
     84 	def display_hint(self):
     85 		return 'array'
     86 
     87 	def to_string(self):
     88 		return str(self.val.type)[6:]  # skip 'struct '
     89 
     90 	def children(self):
     91 		sval = SliceValue(self.val)
     92 		if sval.len > sval.cap:
     93 			return
     94 		for idx, item in enumerate(sval):
     95 			yield ('[{0}]'.format(idx), item)
     96 
     97 
     98 class MapTypePrinter:
     99 	"""Pretty print map[K]V types.
    100 
    101 	Map-typed go variables are really pointers. dereference them in gdb
    102 	to inspect their contents with this pretty printer.
    103 	"""
    104 
    105 	pattern = re.compile(r'^map\[.*\].*$')
    106 
    107 	def __init__(self, val):
    108 		self.val = val
    109 
    110 	def display_hint(self):
    111 		return 'map'
    112 
    113 	def to_string(self):
    114 		return str(self.val.type)
    115 
    116 	def children(self):
    117 		B = self.val['B']
    118 		buckets = self.val['buckets']
    119 		oldbuckets = self.val['oldbuckets']
    120 		flags = self.val['flags']
    121 		inttype = self.val['hash0'].type
    122 		cnt = 0
    123 		for bucket in xrange(2 ** int(B)):
    124 			bp = buckets + bucket
    125 			if oldbuckets:
    126 				oldbucket = bucket & (2 ** (B - 1) - 1)
    127 				oldbp = oldbuckets + oldbucket
    128 				oldb = oldbp.dereference()
    129 				if (oldb['overflow'].cast(inttype) & 1) == 0:  # old bucket not evacuated yet
    130 					if bucket >= 2 ** (B - 1):
    131 						continue    # already did old bucket
    132 					bp = oldbp
    133 			while bp:
    134 				b = bp.dereference()
    135 				for i in xrange(8):
    136 					if b['tophash'][i] != 0:
    137 						k = b['keys'][i]
    138 						v = b['values'][i]
    139 						if flags & 1:
    140 							k = k.dereference()
    141 						if flags & 2:
    142 							v = v.dereference()
    143 						yield str(cnt), k
    144 						yield str(cnt + 1), v
    145 						cnt += 2
    146 				bp = b['overflow']
    147 
    148 
    149 class ChanTypePrinter:
    150 	"""Pretty print chan[T] types.
    151 
    152 	Chan-typed go variables are really pointers. dereference them in gdb
    153 	to inspect their contents with this pretty printer.
    154 	"""
    155 
    156 	pattern = re.compile(r'^struct hchan<.*>$')
    157 
    158 	def __init__(self, val):
    159 		self.val = val
    160 
    161 	def display_hint(self):
    162 		return 'array'
    163 
    164 	def to_string(self):
    165 		return str(self.val.type)
    166 
    167 	def children(self):
    168 		# see chan.c chanbuf(). et is the type stolen from hchan<T>::recvq->first->elem
    169 		et = [x.type for x in self.val['recvq']['first'].type.target().fields() if x.name == 'elem'][0]
    170 		ptr = (self.val.address + 1).cast(et.pointer())
    171 		for i in range(self.val["qcount"]):
    172 			j = (self.val["recvx"] + i) % self.val["dataqsiz"]
    173 			yield ('[{0}]'.format(i), (ptr + j).dereference())
    174 
    175 
    176 #
    177 #  Register all the *Printer classes above.
    178 #
    179 
    180 def makematcher(klass):
    181 	def matcher(val):
    182 		try:
    183 			if klass.pattern.match(str(val.type)):
    184 				return klass(val)
    185 		except Exception:
    186 			pass
    187 	return matcher
    188 
    189 goobjfile.pretty_printers.extend([makematcher(var) for var in vars().values() if hasattr(var, 'pattern')])
    190 
    191 #
    192 #  For reference, this is what we're trying to do:
    193 #  eface: p *(*(struct 'runtime.rtype'*)'main.e'->type_->data)->string
    194 #  iface: p *(*(struct 'runtime.rtype'*)'main.s'->tab->Type->data)->string
    195 #
    196 # interface types can't be recognized by their name, instead we check
    197 # if they have the expected fields.  Unfortunately the mapping of
    198 # fields to python attributes in gdb.py isn't complete: you can't test
    199 # for presence other than by trapping.
    200 
    201 
    202 def is_iface(val):
    203 	try:
    204 		return str(val['tab'].type) == "struct runtime.itab *" and str(val['data'].type) == "void *"
    205 	except gdb.error:
    206 		pass
    207 
    208 
    209 def is_eface(val):
    210 	try:
    211 		return str(val['_type'].type) == "struct runtime._type *" and str(val['data'].type) == "void *"
    212 	except gdb.error:
    213 		pass
    214 
    215 
    216 def lookup_type(name):
    217 	try:
    218 		return gdb.lookup_type(name)
    219 	except gdb.error:
    220 		pass
    221 	try:
    222 		return gdb.lookup_type('struct ' + name)
    223 	except gdb.error:
    224 		pass
    225 	try:
    226 		return gdb.lookup_type('struct ' + name[1:]).pointer()
    227 	except gdb.error:
    228 		pass
    229 
    230 
    231 def iface_commontype(obj):
    232 	if is_iface(obj):
    233 		go_type_ptr = obj['tab']['_type']
    234 	elif is_eface(obj):
    235 		go_type_ptr = obj['_type']
    236 	else:
    237 		return
    238 
    239 	return go_type_ptr.cast(gdb.lookup_type("struct reflect.rtype").pointer()).dereference()
    240 
    241 
    242 def iface_dtype(obj):
    243 	"Decode type of the data field of an eface or iface struct."
    244 	# known issue: dtype_name decoded from runtime.rtype is "nested.Foo"
    245 	# but the dwarf table lists it as "full/path/to/nested.Foo"
    246 
    247 	dynamic_go_type = iface_commontype(obj)
    248 	if dynamic_go_type is None:
    249 		return
    250 	dtype_name = dynamic_go_type['string'].dereference()['str'].string()
    251 
    252 	dynamic_gdb_type = lookup_type(dtype_name)
    253 	if dynamic_gdb_type is None:
    254 		return
    255 
    256 	type_size = int(dynamic_go_type['size'])
    257 	uintptr_size = int(dynamic_go_type['size'].type.sizeof)	 # size is itself an uintptr
    258 	if type_size > uintptr_size:
    259 			dynamic_gdb_type = dynamic_gdb_type.pointer()
    260 
    261 	return dynamic_gdb_type
    262 
    263 
    264 def iface_dtype_name(obj):
    265 	"Decode type name of the data field of an eface or iface struct."
    266 
    267 	dynamic_go_type = iface_commontype(obj)
    268 	if dynamic_go_type is None:
    269 		return
    270 	return dynamic_go_type['string'].dereference()['str'].string()
    271 
    272 
    273 class IfacePrinter:
    274 	"""Pretty print interface values
    275 
    276 	Casts the data field to the appropriate dynamic type."""
    277 
    278 	def __init__(self, val):
    279 		self.val = val
    280 
    281 	def display_hint(self):
    282 		return 'string'
    283 
    284 	def to_string(self):
    285 		if self.val['data'] == 0:
    286 			return 0x0
    287 		try:
    288 			dtype = iface_dtype(self.val)
    289 		except Exception:
    290 			return "<bad dynamic type>"
    291 
    292 		if dtype is None:  # trouble looking up, print something reasonable
    293 			return "({0}){0}".format(iface_dtype_name(self.val), self.val['data'])
    294 
    295 		try:
    296 			return self.val['data'].cast(dtype).dereference()
    297 		except Exception:
    298 			pass
    299 		return self.val['data'].cast(dtype)
    300 
    301 
    302 def ifacematcher(val):
    303 	if is_iface(val) or is_eface(val):
    304 		return IfacePrinter(val)
    305 
    306 goobjfile.pretty_printers.append(ifacematcher)
    307 
    308 #
    309 #  Convenience Functions
    310 #
    311 
    312 
    313 class GoLenFunc(gdb.Function):
    314 	"Length of strings, slices, maps or channels"
    315 
    316 	how = ((StringTypePrinter, 'len'), (SliceTypePrinter, 'len'), (MapTypePrinter, 'count'), (ChanTypePrinter, 'qcount'))
    317 
    318 	def __init__(self):
    319 		gdb.Function.__init__(self, "len")
    320 
    321 	def invoke(self, obj):
    322 		typename = str(obj.type)
    323 		for klass, fld in self.how:
    324 			if klass.pattern.match(typename):
    325 				return obj[fld]
    326 
    327 
    328 class GoCapFunc(gdb.Function):
    329 	"Capacity of slices or channels"
    330 
    331 	how = ((SliceTypePrinter, 'cap'), (ChanTypePrinter, 'dataqsiz'))
    332 
    333 	def __init__(self):
    334 		gdb.Function.__init__(self, "cap")
    335 
    336 	def invoke(self, obj):
    337 		typename = str(obj.type)
    338 		for klass, fld in self.how:
    339 			if klass.pattern.match(typename):
    340 				return obj[fld]
    341 
    342 
    343 class DTypeFunc(gdb.Function):
    344 	"""Cast Interface values to their dynamic type.
    345 
    346 	For non-interface types this behaves as the identity operation.
    347 	"""
    348 
    349 	def __init__(self):
    350 		gdb.Function.__init__(self, "dtype")
    351 
    352 	def invoke(self, obj):
    353 		try:
    354 			return obj['data'].cast(iface_dtype(obj))
    355 		except gdb.error:
    356 			pass
    357 		return obj
    358 
    359 #
    360 #  Commands
    361 #
    362 
    363 sts = ('idle', 'runnable', 'running', 'syscall', 'waiting', 'moribund', 'dead', 'recovery')
    364 
    365 
    366 def linked_list(ptr, linkfield):
    367 	while ptr:
    368 		yield ptr
    369 		ptr = ptr[linkfield]
    370 
    371 
    372 class GoroutinesCmd(gdb.Command):
    373 	"List all goroutines."
    374 
    375 	def __init__(self):
    376 		gdb.Command.__init__(self, "info goroutines", gdb.COMMAND_STACK, gdb.COMPLETE_NONE)
    377 
    378 	def invoke(self, _arg, _from_tty):
    379 		# args = gdb.string_to_argv(arg)
    380 		vp = gdb.lookup_type('void').pointer()
    381 		for ptr in SliceValue(gdb.parse_and_eval("'runtime.allgs'")):
    382 			if ptr['atomicstatus'] == 6:  # 'gdead'
    383 				continue
    384 			s = ' '
    385 			if ptr['m']:
    386 				s = '*'
    387 			pc = ptr['sched']['pc'].cast(vp)
    388 			# python2 will not cast pc (type void*) to an int cleanly
    389 			# instead python2 and python3 work with the hex string representation
    390 			# of the void pointer which we can parse back into an int.
    391 			# int(pc) will not work.
    392 			try:
    393 				#python3 / newer versions of gdb
    394 				pc = int(pc)
    395 			except gdb.error:
    396 				# str(pc) can return things like
    397 				# "0x429d6c <runtime.gopark+284>", so
    398 				# chop at first space.
    399 				pc = int(str(pc).split(None, 1)[0], 16)
    400 			blk = gdb.block_for_pc(pc)
    401 			print(s, ptr['goid'], "{0:8s}".format(sts[int(ptr['atomicstatus'])]), blk.function)
    402 
    403 
    404 def find_goroutine(goid):
    405 	"""
    406 	find_goroutine attempts to find the goroutine identified by goid.
    407 	It returns a touple of gdv.Value's representing the stack pointer
    408 	and program counter pointer for the goroutine.
    409 
    410 	@param int goid
    411 
    412 	@return tuple (gdb.Value, gdb.Value)
    413 	"""
    414 	vp = gdb.lookup_type('void').pointer()
    415 	for ptr in SliceValue(gdb.parse_and_eval("'runtime.allgs'")):
    416 		if ptr['atomicstatus'] == 6:  # 'gdead'
    417 			continue
    418 		if ptr['goid'] == goid:
    419 			return (ptr['sched'][x].cast(vp) for x in ('pc', 'sp'))
    420 	return None, None
    421 
    422 
    423 class GoroutineCmd(gdb.Command):
    424 	"""Execute gdb command in the context of goroutine <goid>.
    425 
    426 	Switch PC and SP to the ones in the goroutine's G structure,
    427 	execute an arbitrary gdb command, and restore PC and SP.
    428 
    429 	Usage: (gdb) goroutine <goid> <gdbcmd>
    430 
    431 	Note that it is ill-defined to modify state in the context of a goroutine.
    432 	Restrict yourself to inspecting values.
    433 	"""
    434 
    435 	def __init__(self):
    436 		gdb.Command.__init__(self, "goroutine", gdb.COMMAND_STACK, gdb.COMPLETE_NONE)
    437 
    438 	def invoke(self, arg, _from_tty):
    439 		goid, cmd = arg.split(None, 1)
    440 		goid = gdb.parse_and_eval(goid)
    441 		pc, sp = find_goroutine(int(goid))
    442 		if not pc:
    443 			print("No such goroutine: ", goid)
    444 			return
    445 		try:
    446 			#python3 / newer versions of gdb
    447 			pc = int(pc)
    448 		except gdb.error:
    449 			pc = int(str(pc).split(None, 1)[0], 16)
    450 		save_frame = gdb.selected_frame()
    451 		gdb.parse_and_eval('$save_pc = $pc')
    452 		gdb.parse_and_eval('$save_sp = $sp')
    453 		gdb.parse_and_eval('$pc = {0}'.format(str(pc)))
    454 		gdb.parse_and_eval('$sp = {0}'.format(str(sp)))
    455 		try:
    456 			gdb.execute(cmd)
    457 		finally:
    458 			gdb.parse_and_eval('$pc = $save_pc')
    459 			gdb.parse_and_eval('$sp = $save_sp')
    460 			save_frame.select()
    461 
    462 
    463 class GoIfaceCmd(gdb.Command):
    464 	"Print Static and dynamic interface types"
    465 
    466 	def __init__(self):
    467 		gdb.Command.__init__(self, "iface", gdb.COMMAND_DATA, gdb.COMPLETE_SYMBOL)
    468 
    469 	def invoke(self, arg, _from_tty):
    470 		for obj in gdb.string_to_argv(arg):
    471 			try:
    472 				#TODO fix quoting for qualified variable names
    473 				obj = gdb.parse_and_eval(str(obj))
    474 			except Exception as e:
    475 				print("Can't parse ", obj, ": ", e)
    476 				continue
    477 
    478 			if obj['data'] == 0:
    479 				dtype = "nil"
    480 			else:
    481 				dtype = iface_dtype(obj)
    482 
    483 			if dtype is None:
    484 				print("Not an interface: ", obj.type)
    485 				continue
    486 
    487 			print("{0}: {1}".format(obj.type, dtype))
    488 
    489 # TODO: print interface's methods and dynamic type's func pointers thereof.
    490 #rsc: "to find the number of entries in the itab's Fn field look at
    491 # itab.inter->numMethods
    492 # i am sure i have the names wrong but look at the interface type
    493 # and its method count"
    494 # so Itype will start with a commontype which has kind = interface
    495 
    496 #
    497 # Register all convenience functions and CLI commands
    498 #
    499 GoLenFunc()
    500 GoCapFunc()
    501 DTypeFunc()
    502 GoroutinesCmd()
    503 GoroutineCmd()
    504 GoIfaceCmd()
    505