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 			break
    420 	else:
    421 		return None, None
    422 	# Get the goroutine's saved state.
    423 	pc, sp = ptr['sched']['pc'], ptr['sched']['sp']
    424 	# If the goroutine is stopped, sched.sp will be non-0.
    425 	if sp != 0:
    426 		return pc.cast(vp), sp.cast(vp)
    427 	# If the goroutine is in a syscall, use syscallpc/sp.
    428 	pc, sp = ptr['syscallpc'], ptr['syscallsp']
    429 	if sp != 0:
    430 		return pc.cast(vp), sp.cast(vp)
    431 	# Otherwise, the goroutine is running, so it doesn't have
    432 	# saved scheduler state. Find G's OS thread.
    433 	m = ptr['m']
    434 	if m == 0:
    435 		return None, None
    436 	for thr in gdb.selected_inferior().threads():
    437 		if thr.ptid[1] == m['procid']:
    438 			break
    439 	else:
    440 		return None, None
    441 	# Get scheduler state from the G's OS thread state.
    442 	curthr = gdb.selected_thread()
    443 	try:
    444 		thr.switch()
    445 		pc = gdb.parse_and_eval('$pc')
    446 		sp = gdb.parse_and_eval('$sp')
    447 	finally:
    448 		curthr.switch()
    449 	return pc.cast(vp), sp.cast(vp)
    450 
    451 
    452 class GoroutineCmd(gdb.Command):
    453 	"""Execute gdb command in the context of goroutine <goid>.
    454 
    455 	Switch PC and SP to the ones in the goroutine's G structure,
    456 	execute an arbitrary gdb command, and restore PC and SP.
    457 
    458 	Usage: (gdb) goroutine <goid> <gdbcmd>
    459 
    460 	Note that it is ill-defined to modify state in the context of a goroutine.
    461 	Restrict yourself to inspecting values.
    462 	"""
    463 
    464 	def __init__(self):
    465 		gdb.Command.__init__(self, "goroutine", gdb.COMMAND_STACK, gdb.COMPLETE_NONE)
    466 
    467 	def invoke(self, arg, _from_tty):
    468 		goid, cmd = arg.split(None, 1)
    469 		goid = gdb.parse_and_eval(goid)
    470 		pc, sp = find_goroutine(int(goid))
    471 		if not pc:
    472 			print("No such goroutine: ", goid)
    473 			return
    474 		try:
    475 			#python3 / newer versions of gdb
    476 			pc = int(pc)
    477 		except gdb.error:
    478 			pc = int(str(pc).split(None, 1)[0], 16)
    479 		save_frame = gdb.selected_frame()
    480 		gdb.parse_and_eval('$save_sp = $sp')
    481 		gdb.parse_and_eval('$save_pc = $pc')
    482 		gdb.parse_and_eval('$sp = {0}'.format(str(sp)))
    483 		gdb.parse_and_eval('$pc = {0}'.format(str(pc)))
    484 		try:
    485 			gdb.execute(cmd)
    486 		finally:
    487 			gdb.parse_and_eval('$sp = $save_sp')
    488 			gdb.parse_and_eval('$pc = $save_pc')
    489 			save_frame.select()
    490 
    491 
    492 class GoIfaceCmd(gdb.Command):
    493 	"Print Static and dynamic interface types"
    494 
    495 	def __init__(self):
    496 		gdb.Command.__init__(self, "iface", gdb.COMMAND_DATA, gdb.COMPLETE_SYMBOL)
    497 
    498 	def invoke(self, arg, _from_tty):
    499 		for obj in gdb.string_to_argv(arg):
    500 			try:
    501 				#TODO fix quoting for qualified variable names
    502 				obj = gdb.parse_and_eval(str(obj))
    503 			except Exception as e:
    504 				print("Can't parse ", obj, ": ", e)
    505 				continue
    506 
    507 			if obj['data'] == 0:
    508 				dtype = "nil"
    509 			else:
    510 				dtype = iface_dtype(obj)
    511 
    512 			if dtype is None:
    513 				print("Not an interface: ", obj.type)
    514 				continue
    515 
    516 			print("{0}: {1}".format(obj.type, dtype))
    517 
    518 # TODO: print interface's methods and dynamic type's func pointers thereof.
    519 #rsc: "to find the number of entries in the itab's Fn field look at
    520 # itab.inter->numMethods
    521 # i am sure i have the names wrong but look at the interface type
    522 # and its method count"
    523 # so Itype will start with a commontype which has kind = interface
    524 
    525 #
    526 # Register all convenience functions and CLI commands
    527 #
    528 GoLenFunc()
    529 GoCapFunc()
    530 DTypeFunc()
    531 GoroutinesCmd()
    532 GoroutineCmd()
    533 GoIfaceCmd()
    534