PageRenderTime 43ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/runtime/runtime-gdb.py

https://code.google.com/p/gofy/
Python | 398 lines | 261 code | 78 blank | 59 comment | 50 complexity | 1cf27df3435244f3614e05c51772567b MD5 | raw file
  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. """GDB Pretty printers and convencience functions for Go's runtime structures.
  5. This script is loaded by GDB when it finds a .debug_gdb_scripts
  6. section in the compiled binary. The [68]l linkers emit this with a
  7. path to this file based on the path to the runtime package.
  8. """
  9. # Known issues:
  10. # - pretty printing only works for the 'native' strings. E.g. 'type
  11. # foo string' will make foo a plain struct in the eyes of gdb,
  12. # circumventing the pretty print triggering.
  13. # -
  14. import sys, re
  15. print >>sys.stderr, "Loading Go Runtime support."
  16. # allow to manually reload while developing
  17. goobjfile = gdb.current_objfile() or gdb.objfiles()[0]
  18. goobjfile.pretty_printers = []
  19. #
  20. # Pretty Printers
  21. #
  22. class StringTypePrinter:
  23. "Pretty print Go strings."
  24. pattern = re.compile(r'^struct string$')
  25. def __init__(self, val):
  26. self.val = val
  27. def display_hint(self):
  28. return 'string'
  29. def to_string(self):
  30. return self.val['str']
  31. class SliceTypePrinter:
  32. "Pretty print slices."
  33. pattern = re.compile(r'^struct \[\]')
  34. def __init__(self, val):
  35. self.val = val
  36. def display_hint(self):
  37. return 'array'
  38. def to_string(self):
  39. return str(self.val.type)[6:] # skip 'struct '
  40. def children(self):
  41. ptr = self.val["array"]
  42. for idx in range(self.val["len"]):
  43. yield ('[%d]' % idx, (ptr + idx).dereference())
  44. class MapTypePrinter:
  45. """Pretty print map[K]V types.
  46. Map-typed go variables are really pointers. dereference them in gdb
  47. to inspect their contents with this pretty printer.
  48. """
  49. pattern = re.compile(r'^struct hash<.*>$')
  50. def __init__(self, val):
  51. self.val = val
  52. def display_hint(self):
  53. return 'map'
  54. def to_string(self):
  55. return str(self.val.type)
  56. def children(self):
  57. stab = self.val['st']
  58. i = 0
  59. for v in self.traverse_hash(stab):
  60. yield ("[%d]" % i, v['key'])
  61. yield ("[%d]" % (i + 1), v['val'])
  62. i += 2
  63. def traverse_hash(self, stab):
  64. ptr = stab['entry'].address
  65. end = stab['end']
  66. while ptr < end:
  67. v = ptr.dereference()
  68. ptr = ptr + 1
  69. if v['hash'] == 0: continue
  70. if v['hash'] & 63 == 63: # subtable
  71. for v in self.traverse_hash(v['key'].cast(self.val['st'].type)):
  72. yield v
  73. else:
  74. yield v
  75. class ChanTypePrinter:
  76. """Pretty print chan[T] types.
  77. Chan-typed go variables are really pointers. dereference them in gdb
  78. to inspect their contents with this pretty printer.
  79. """
  80. pattern = re.compile(r'^struct hchan<.*>$')
  81. def __init__(self, val):
  82. self.val = val
  83. def display_hint(self):
  84. return 'array'
  85. def to_string(self):
  86. return str(self.val.type)
  87. def children(self):
  88. ptr = self.val['recvdataq']
  89. for idx in range(self.val["qcount"]):
  90. yield ('[%d]' % idx, ptr['elem'])
  91. ptr = ptr['link']
  92. #
  93. # Register all the *Printer classes above.
  94. #
  95. def makematcher(klass):
  96. def matcher(val):
  97. try:
  98. if klass.pattern.match(str(val.type)):
  99. return klass(val)
  100. except:
  101. pass
  102. return matcher
  103. goobjfile.pretty_printers.extend([makematcher(k) for k in vars().values() if hasattr(k, 'pattern')])
  104. #
  105. # For reference, this is what we're trying to do:
  106. # eface: p *(*(struct 'runtime.commonType'*)'main.e'->type_->data)->string
  107. # iface: p *(*(struct 'runtime.commonType'*)'main.s'->tab->Type->data)->string
  108. #
  109. # interface types can't be recognized by their name, instead we check
  110. # if they have the expected fields. Unfortunately the mapping of
  111. # fields to python attributes in gdb.py isn't complete: you can't test
  112. # for presence other than by trapping.
  113. def is_iface(val):
  114. try:
  115. return str(val['tab'].type) == "struct runtime.itab *" \
  116. and str(val['data'].type) == "void *"
  117. except:
  118. pass
  119. def is_eface(val):
  120. try:
  121. return str(val['type_'].type) == "runtime.Type *" \
  122. and str(val['data'].type) == "void *"
  123. except:
  124. pass
  125. def lookup_type(name):
  126. try:
  127. return gdb.lookup_type(name)
  128. except:
  129. pass
  130. try:
  131. return gdb.lookup_type('struct ' + name)
  132. except:
  133. pass
  134. try:
  135. return gdb.lookup_type('struct ' + name[1:]).pointer()
  136. except:
  137. pass
  138. def iface_dtype(obj):
  139. "Decode type of the data field of an eface or iface struct."
  140. if is_iface(obj):
  141. go_type_ptr = obj['tab']['Type']
  142. elif is_eface(obj):
  143. go_type_ptr = obj['type_']
  144. else:
  145. return
  146. ct = gdb.lookup_type("struct runtime.commonType").pointer()
  147. dynamic_go_type = go_type_ptr['data'].cast(ct).dereference()
  148. dtype_name = dynamic_go_type['string'].dereference()['str'].string()
  149. type_size = int(dynamic_go_type['size'])
  150. uintptr_size = int(dynamic_go_type['size'].type.sizeof) # size is itself an uintptr
  151. dynamic_gdb_type = lookup_type(dtype_name)
  152. if type_size > uintptr_size:
  153. dynamic_gdb_type = dynamic_gdb_type.pointer()
  154. return dynamic_gdb_type
  155. class IfacePrinter:
  156. """Pretty print interface values
  157. Casts the data field to the appropriate dynamic type."""
  158. def __init__(self, val):
  159. self.val = val
  160. def display_hint(self):
  161. return 'string'
  162. def to_string(self):
  163. try:
  164. dtype = iface_dtype(self.val)
  165. except:
  166. return "<bad dynamic type>"
  167. try:
  168. return self.val['data'].cast(dtype).dereference()
  169. except:
  170. pass
  171. return self.val['data'].cast(dtype)
  172. def ifacematcher(val):
  173. if is_iface(val) or is_eface(val):
  174. return IfacePrinter(val)
  175. goobjfile.pretty_printers.append(ifacematcher)
  176. #
  177. # Convenience Functions
  178. #
  179. class GoLenFunc(gdb.Function):
  180. "Length of strings, slices, maps or channels"
  181. how = ((StringTypePrinter, 'len' ),
  182. (SliceTypePrinter, 'len'),
  183. (MapTypePrinter, 'count'),
  184. (ChanTypePrinter, 'qcount'))
  185. def __init__(self):
  186. super(GoLenFunc, self).__init__("len")
  187. def invoke(self, obj):
  188. typename = str(obj.type)
  189. for klass, fld in self.how:
  190. if klass.pattern.match(typename):
  191. return obj[fld]
  192. class GoCapFunc(gdb.Function):
  193. "Capacity of slices or channels"
  194. how = ((SliceTypePrinter, 'cap'),
  195. (ChanTypePrinter, 'dataqsiz'))
  196. def __init__(self):
  197. super(GoCapFunc, self).__init__("cap")
  198. def invoke(self, obj):
  199. typename = str(obj.type)
  200. for klass, fld in self.how:
  201. if klass.pattern.match(typename):
  202. return obj[fld]
  203. class DTypeFunc(gdb.Function):
  204. """Cast Interface values to their dynamic type.
  205. For non-interface types this behaves as the identity operation.
  206. """
  207. def __init__(self):
  208. super(DTypeFunc, self).__init__("dtype")
  209. def invoke(self, obj):
  210. try:
  211. return obj['data'].cast(iface_dtype(obj))
  212. except:
  213. pass
  214. return obj
  215. #
  216. # Commands
  217. #
  218. sts = ( 'idle', 'runnable', 'running', 'syscall', 'waiting', 'moribund', 'dead', 'recovery')
  219. def linked_list(ptr, linkfield):
  220. while ptr:
  221. yield ptr
  222. ptr = ptr[linkfield]
  223. class GoroutinesCmd(gdb.Command):
  224. "List all goroutines."
  225. def __init__(self):
  226. super(GoroutinesCmd, self).__init__("info goroutines", gdb.COMMAND_STACK, gdb.COMPLETE_NONE)
  227. def invoke(self, arg, from_tty):
  228. # args = gdb.string_to_argv(arg)
  229. vp = gdb.lookup_type('void').pointer()
  230. for ptr in linked_list(gdb.parse_and_eval("'runtime.allg'"), 'alllink'):
  231. if ptr['status'] == 6: # 'gdead'
  232. continue
  233. m = ptr['m']
  234. s = ' '
  235. if m:
  236. pc = m['sched']['pc'].cast(vp)
  237. sp = m['sched']['sp'].cast(vp)
  238. s = '*'
  239. else:
  240. pc = ptr['sched']['pc'].cast(vp)
  241. sp = ptr['sched']['sp'].cast(vp)
  242. blk = gdb.block_for_pc(long((pc)))
  243. print s, ptr['goid'], "%8s" % sts[long((ptr['status']))], blk.function
  244. def find_goroutine(goid):
  245. vp = gdb.lookup_type('void').pointer()
  246. for ptr in linked_list(gdb.parse_and_eval("'runtime.allg'"), 'alllink'):
  247. if ptr['status'] == 6: # 'gdead'
  248. continue
  249. if ptr['goid'] == goid:
  250. return [(ptr['m'] or ptr)['sched'][x].cast(vp) for x in 'pc', 'sp']
  251. return None, None
  252. class GoroutineCmd(gdb.Command):
  253. """Execute gdb command in the context of goroutine <goid>.
  254. Switch PC and SP to the ones in the goroutine's G structure,
  255. execute an arbitrary gdb command, and restore PC and SP.
  256. Usage: (gdb) goroutine <goid> <gdbcmd>
  257. Note that it is ill-defined to modify state in the context of a goroutine.
  258. Restrict yourself to inspecting values.
  259. """
  260. def __init__(self):
  261. super(GoroutineCmd, self).__init__("goroutine", gdb.COMMAND_STACK, gdb.COMPLETE_NONE)
  262. def invoke(self, arg, from_tty):
  263. goid, cmd = arg.split(None, 1)
  264. pc, sp = find_goroutine(int(goid))
  265. if not pc:
  266. print "No such goroutine: ", goid
  267. return
  268. save_frame = gdb.selected_frame()
  269. gdb.parse_and_eval('$save_pc = $pc')
  270. gdb.parse_and_eval('$save_sp = $sp')
  271. gdb.parse_and_eval('$pc = 0x%x' % long(pc))
  272. gdb.parse_and_eval('$sp = 0x%x' % long(sp))
  273. try:
  274. gdb.execute(cmd)
  275. finally:
  276. gdb.parse_and_eval('$pc = $save_pc')
  277. gdb.parse_and_eval('$sp = $save_sp')
  278. save_frame.select()
  279. class GoIfaceCmd(gdb.Command):
  280. "Print Static and dynamic interface types"
  281. def __init__(self):
  282. super(GoIfaceCmd, self).__init__("iface", gdb.COMMAND_DATA, gdb.COMPLETE_SYMBOL)
  283. def invoke(self, arg, from_tty):
  284. for obj in gdb.string_to_argv(arg):
  285. try:
  286. #TODO fix quoting for qualified variable names
  287. obj = gdb.parse_and_eval("%s" % obj)
  288. except Exception, e:
  289. print "Can't parse ", obj, ": ", e
  290. continue
  291. dtype = iface_dtype(obj)
  292. if not dtype:
  293. print "Not an interface: ", obj.type
  294. continue
  295. print "%s: %s" % (obj.type, dtype)
  296. # TODO: print interface's methods and dynamic type's func pointers thereof.
  297. #rsc: "to find the number of entries in the itab's Fn field look at itab.inter->numMethods
  298. #i am sure i have the names wrong but look at the interface type and its method count"
  299. # so Itype will start with a commontype which has kind = interface
  300. #
  301. # Register all convience functions and CLI commands
  302. #
  303. for k in vars().values():
  304. if hasattr(k, 'invoke'):
  305. k()