github.com/varialus/godfly@v0.0.0-20130904042352-1934f9f095ab/src/pkg/runtime/runtime-gdb.py (about)

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