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