PageRenderTime 59ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/tool/gdb_pypy.py

https://bitbucket.org/pypy/pypy/
Python | 310 lines | 302 code | 0 blank | 8 comment | 4 complexity | 95e25a36fbfbadfbdf28663e1a549fb2 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. """
  2. Some convenience macros for gdb. If you have pypy in your path, you can simply do:
  3. (gdb) python import pypy.tool.gdb_pypy
  4. Or, alternatively:
  5. (gdb) python exec(open('/path/to/gdb_pypy.py').read())
  6. """
  7. import re
  8. import sys
  9. import os.path
  10. try:
  11. # when running inside gdb
  12. from gdb import Command
  13. except ImportError:
  14. # whenn running outside gdb: mock class for testing
  15. class Command(object):
  16. def __init__(self, name, command_class):
  17. pass
  18. MAX_DISPLAY_LENGTH = 100 # maximum number of characters displayed in rpy_string
  19. def find_field_with_suffix(val, suffix):
  20. """
  21. Return ``val[field]``, where ``field`` is the only one whose name ends
  22. with ``suffix``. If there is no such field, or more than one, raise KeyError.
  23. """
  24. names = []
  25. for field in val.type.fields():
  26. if field.name.endswith(suffix):
  27. names.append(field.name)
  28. #
  29. if len(names) == 1:
  30. return val[names[0]]
  31. elif len(names) == 0:
  32. raise KeyError("cannot find field *%s" % suffix)
  33. else:
  34. raise KeyError("too many matching fields: %s" % ', '.join(names))
  35. def lookup(val, suffix):
  36. """
  37. Lookup a field which ends with ``suffix`` following the rpython struct
  38. inheritance hierarchy (i.e., looking both at ``val`` and
  39. ``val['*_super']``, recursively.
  40. """
  41. try:
  42. return find_field_with_suffix(val, suffix)
  43. except KeyError:
  44. baseobj = find_field_with_suffix(val, '_super')
  45. return lookup(baseobj, suffix)
  46. class RPyType(Command):
  47. """
  48. Prints the RPython type of the expression.
  49. E.g.:
  50. (gdb) rpy_type l_v123
  51. GcStruct pypy.foo.Bar { super, inst_xxx, inst_yyy }
  52. """
  53. prog2typeids = {}
  54. def __init__(self, gdb=None):
  55. # dependency injection, for tests
  56. if gdb is None:
  57. import gdb
  58. self.gdb = gdb
  59. Command.__init__(self, "rpy_type", self.gdb.COMMAND_NONE)
  60. def invoke(self, arg, from_tty):
  61. # some magic code to automatically reload the python file while developing
  62. try:
  63. from pypy.tool import gdb_pypy
  64. try:
  65. reload(gdb_pypy)
  66. except:
  67. import imp
  68. imp.reload(gdb_pypy)
  69. gdb_pypy.RPyType.prog2typeids = self.prog2typeids # persist the cache
  70. self.__class__ = gdb_pypy.RPyType
  71. result = self.do_invoke(arg, from_tty)
  72. if not isinstance(result, str):
  73. result = result.decode('latin-1')
  74. print(result)
  75. except:
  76. import traceback
  77. traceback.print_exc()
  78. def do_invoke(self, arg, from_tty):
  79. try:
  80. offset = int(arg)
  81. except ValueError:
  82. obj = self.gdb.parse_and_eval(arg)
  83. if obj.type.code == self.gdb.TYPE_CODE_PTR:
  84. obj = obj.dereference()
  85. hdr = lookup(obj, '_gcheader')
  86. tid = hdr['h_tid']
  87. if tid == -42: # forwarded?
  88. return 'Forwarded'
  89. if sys.maxsize < 2**32:
  90. offset = tid & 0xFFFF # 32bit
  91. else:
  92. offset = tid & 0xFFFFFFFF # 64bit
  93. offset = int(offset) # convert from gdb.Value to python int
  94. typeids = self.get_typeids()
  95. if offset in typeids:
  96. return typeids[offset]
  97. else:
  98. return 'Cannot find the type with offset 0x%x' % offset
  99. def get_typeids(self):
  100. try:
  101. progspace = self.gdb.current_progspace()
  102. except AttributeError:
  103. progspace = None
  104. try:
  105. return self.prog2typeids[progspace]
  106. except KeyError:
  107. typeids = self.load_typeids(progspace)
  108. self.prog2typeids[progspace] = typeids
  109. return typeids
  110. def load_typeids(self, progspace=None):
  111. """
  112. Returns a mapping offset --> description
  113. """
  114. import tempfile
  115. import zlib
  116. vname = 'pypy_g_rpython_memory_gctypelayout_GCData.gcd_inst_typeids_z'
  117. length = int(self.gdb.parse_and_eval('*(long*)%s' % vname))
  118. vstart = '(char*)(((long*)%s)+1)' % vname
  119. fname = tempfile.mktemp()
  120. try:
  121. self.gdb.execute('dump binary memory %s %s %s+%d' %
  122. (fname, vstart, vstart, length))
  123. with open(fname, 'rb') as fobj:
  124. data = fobj.read()
  125. return TypeIdsMap(zlib.decompress(data).splitlines(True), self.gdb)
  126. finally:
  127. os.remove(fname)
  128. class TypeIdsMap(object):
  129. def __init__(self, lines, gdb):
  130. self.lines = lines
  131. self.gdb = gdb
  132. self.line2offset = {0: 0}
  133. self.offset2descr = {0: "(null typeid)"}
  134. def __getitem__(self, key):
  135. value = self.get(key)
  136. if value is None:
  137. raise KeyError(key)
  138. return value
  139. def __contains__(self, key):
  140. return self.get(key) is not None
  141. def _fetchline(self, linenum):
  142. if linenum in self.line2offset:
  143. return self.line2offset[linenum]
  144. line = self.lines[linenum]
  145. member, descr = [x.strip() for x in line.split(None, 1)]
  146. if sys.maxsize < 2**32:
  147. TIDT = "int*"
  148. else:
  149. TIDT = "char*"
  150. expr = ("((%s)(&pypy_g_typeinfo.%s)) - (%s)&pypy_g_typeinfo"
  151. % (TIDT, member.decode("latin-1"), TIDT))
  152. offset = int(self.gdb.parse_and_eval(expr))
  153. self.line2offset[linenum] = offset
  154. self.offset2descr[offset] = descr
  155. #print '%r -> %r -> %r' % (linenum, offset, descr)
  156. return offset
  157. def get(self, offset, default=None):
  158. # binary search through the lines, asking gdb to parse stuff lazily
  159. if offset in self.offset2descr:
  160. return self.offset2descr[offset]
  161. if not (0 < offset < sys.maxsize):
  162. return None
  163. linerange = (0, len(self.lines))
  164. while linerange[0] < linerange[1]:
  165. linemiddle = (linerange[0] + linerange[1]) >> 1
  166. offsetmiddle = self._fetchline(linemiddle)
  167. if offsetmiddle == offset:
  168. return self.offset2descr[offset]
  169. elif offsetmiddle < offset:
  170. linerange = (linemiddle + 1, linerange[1])
  171. else:
  172. linerange = (linerange[0], linemiddle)
  173. return None
  174. def is_ptr(type, gdb):
  175. if gdb is None:
  176. import gdb # so we can pass a fake one from the tests
  177. return type.code == gdb.TYPE_CODE_PTR
  178. class RPyStringPrinter(object):
  179. """
  180. Pretty printer for rpython strings.
  181. Note that this pretty prints *pointers* to strings: this way you can do "p
  182. val" and see the nice string, and "p *val" to see the underyling struct
  183. fields
  184. """
  185. def __init__(self, val):
  186. self.val = val
  187. @classmethod
  188. def lookup(cls, val, gdb=None):
  189. t = val.type
  190. if is_ptr(t, gdb) and t.target().tag == 'pypy_rpy_string0':
  191. return cls(val)
  192. return None
  193. def to_string(self):
  194. chars = self.val['rs_chars']
  195. length = int(chars['length'])
  196. items = chars['items']
  197. res = []
  198. for i in range(min(length, MAX_DISPLAY_LENGTH)):
  199. c = items[i]
  200. try:
  201. res.append(chr(c))
  202. except ValueError:
  203. # it's a gdb.Value so it has "121 'y'" as repr
  204. try:
  205. res.append(chr(int(str(c).split(" ")[0])))
  206. except ValueError:
  207. # meh?
  208. res.append(repr(c))
  209. if length > MAX_DISPLAY_LENGTH:
  210. res.append('...')
  211. string = ''.join(res)
  212. return 'r' + repr(string)
  213. class RPyListPrinter(object):
  214. """
  215. Pretty printer for rpython lists
  216. Note that this pretty prints *pointers* to lists: this way you can do "p
  217. val" and see the nice repr, and "p *val" to see the underyling struct
  218. fields
  219. """
  220. recursive = False
  221. def __init__(self, val):
  222. self.val = val
  223. @classmethod
  224. def lookup(cls, val, gdb=None):
  225. t = val.type
  226. if (is_ptr(t, gdb) and t.target().tag is not None and
  227. re.match(r'pypy_(list|array)\d*', t.target().tag)):
  228. return cls(val)
  229. return None
  230. def to_string(self):
  231. t = self.val.type
  232. if t.target().tag.startswith(r'pypy_array'):
  233. if not self.val:
  234. return 'r(null_array)'
  235. length = int(self.val['length'])
  236. items = self.val['items']
  237. allocstr = ''
  238. else:
  239. if not self.val:
  240. return 'r(null_list)'
  241. length = int(self.val['l_length'])
  242. array = self.val['l_items']
  243. allocated = int(array['length'])
  244. items = array['items']
  245. allocstr = ', alloc=%d' % allocated
  246. if RPyListPrinter.recursive:
  247. str_items = '...'
  248. else:
  249. RPyListPrinter.recursive = True
  250. try:
  251. itemlist = []
  252. for i in range(min(length, MAX_DISPLAY_LENGTH)):
  253. item = items[i]
  254. itemlist.append(str(item)) # may recurse here
  255. if length > MAX_DISPLAY_LENGTH:
  256. itemlist.append("...")
  257. str_items = ', '.join(itemlist)
  258. finally:
  259. RPyListPrinter.recursive = False
  260. return 'r[%s] (len=%d%s)' % (str_items, length, allocstr)
  261. try:
  262. import gdb
  263. RPyType() # side effects
  264. gdb.pretty_printers = [
  265. RPyStringPrinter.lookup,
  266. RPyListPrinter.lookup
  267. ] + gdb.pretty_printers
  268. except ImportError:
  269. pass