1#!/usr/bin/python 2''' 3From gdb 7 onwards, gdb's build can be configured --with-python, allowing gdb 4to be extended with Python code e.g. for library-specific data visualizations, 5such as for the C++ STL types. Documentation on this API can be seen at: 6http://sourceware.org/gdb/current/onlinedocs/gdb/Python-API.html 7 8 9This python module deals with the case when the process being debugged (the 10"inferior process" in gdb parlance) is itself python, or more specifically, 11linked against libpython. In this situation, almost every item of data is a 12(PyObject*), and having the debugger merely print their addresses is not very 13enlightening. 14 15This module embeds knowledge about the implementation details of libpython so 16that we can emit useful visualizations e.g. a string, a list, a dict, a frame 17giving file/line information and the state of local variables 18 19In particular, given a gdb.Value corresponding to a PyObject* in the inferior 20process, we can generate a "proxy value" within the gdb process. For example, 21given a PyObject* in the inferior process that is in fact a PyListObject* 22holding three PyObject* that turn out to be PyStringObject* instances, we can 23generate a proxy value within the gdb process that is a list of strings: 24 ["foo", "bar", "baz"] 25 26Doing so can be expensive for complicated graphs of objects, and could take 27some time, so we also have a "write_repr" method that writes a representation 28of the data to a file-like object. This allows us to stop the traversal by 29having the file-like object raise an exception if it gets too much data. 30 31With both "proxyval" and "write_repr" we keep track of the set of all addresses 32visited so far in the traversal, to avoid infinite recursion due to cycles in 33the graph of object references. 34 35We try to defer gdb.lookup_type() invocations for python types until as late as 36possible: for a dynamically linked python binary, when the process starts in 37the debugger, the libpython.so hasn't been dynamically loaded yet, so none of 38the type names are known to the debugger 39 40The module also extends gdb with some python-specific commands. 41''' 42 43# NOTE: some gdbs are linked with Python 3, so this file should be dual-syntax 44# compatible (2.6+ and 3.0+). See #19308. 45 46from __future__ import print_function, with_statement 47import gdb 48import locale 49import os 50import sys 51 52if sys.version_info[0] >= 3: 53 unichr = chr 54 xrange = range 55 long = int 56 57# Look up the gdb.Type for some standard types: 58# Those need to be refreshed as types (pointer sizes) may change when 59# gdb loads different executables 60 61def _type_char_ptr(): 62 return gdb.lookup_type('char').pointer() # char* 63 64 65def _type_unsigned_char_ptr(): 66 return gdb.lookup_type('unsigned char').pointer() # unsigned char* 67 68 69def _sizeof_void_p(): 70 return gdb.lookup_type('void').pointer().sizeof 71 72 73Py_TPFLAGS_HEAPTYPE = (1 << 9) 74 75Py_TPFLAGS_INT_SUBCLASS = (1 << 23) 76Py_TPFLAGS_LONG_SUBCLASS = (1 << 24) 77Py_TPFLAGS_LIST_SUBCLASS = (1 << 25) 78Py_TPFLAGS_TUPLE_SUBCLASS = (1 << 26) 79Py_TPFLAGS_STRING_SUBCLASS = (1 << 27) 80Py_TPFLAGS_UNICODE_SUBCLASS = (1 << 28) 81Py_TPFLAGS_DICT_SUBCLASS = (1 << 29) 82Py_TPFLAGS_BASE_EXC_SUBCLASS = (1 << 30) 83Py_TPFLAGS_TYPE_SUBCLASS = (1 << 31) 84 85 86MAX_OUTPUT_LEN=1024 87 88ENCODING = locale.getpreferredencoding() 89 90class NullPyObjectPtr(RuntimeError): 91 pass 92 93 94def safety_limit(val): 95 # Given an integer value from the process being debugged, limit it to some 96 # safety threshold so that arbitrary breakage within said process doesn't 97 # break the gdb process too much (e.g. sizes of iterations, sizes of lists) 98 return min(val, 1000) 99 100 101def safe_range(val): 102 # As per range, but don't trust the value too much: cap it to a safety 103 # threshold in case the data was corrupted 104 return xrange(safety_limit(int(val))) 105 106if sys.version_info[0] >= 3: 107 def write_unicode(file, text): 108 file.write(text) 109else: 110 def write_unicode(file, text): 111 # Write a byte or unicode string to file. Unicode strings are encoded to 112 # ENCODING encoding with 'backslashreplace' error handler to avoid 113 # UnicodeEncodeError. 114 if isinstance(text, unicode): 115 text = text.encode(ENCODING, 'backslashreplace') 116 file.write(text) 117 118 119class StringTruncated(RuntimeError): 120 pass 121 122class TruncatedStringIO(object): 123 '''Similar to cStringIO, but can truncate the output by raising a 124 StringTruncated exception''' 125 def __init__(self, maxlen=None): 126 self._val = '' 127 self.maxlen = maxlen 128 129 def write(self, data): 130 if self.maxlen: 131 if len(data) + len(self._val) > self.maxlen: 132 # Truncation: 133 self._val += data[0:self.maxlen - len(self._val)] 134 raise StringTruncated() 135 136 self._val += data 137 138 def getvalue(self): 139 return self._val 140 141class PyObjectPtr(object): 142 """ 143 Class wrapping a gdb.Value that's either a (PyObject*) within the 144 inferior process, or some subclass pointer e.g. (PyStringObject*) 145 146 There will be a subclass for every refined PyObject type that we care 147 about. 148 149 Note that at every stage the underlying pointer could be NULL, point 150 to corrupt data, etc; this is the debugger, after all. 151 """ 152 _typename = 'PyObject' 153 154 def __init__(self, gdbval, cast_to=None): 155 if cast_to: 156 self._gdbval = gdbval.cast(cast_to) 157 else: 158 self._gdbval = gdbval 159 160 def field(self, name): 161 ''' 162 Get the gdb.Value for the given field within the PyObject, coping with 163 some python 2 versus python 3 differences. 164 165 Various libpython types are defined using the "PyObject_HEAD" and 166 "PyObject_VAR_HEAD" macros. 167 168 In Python 2, this these are defined so that "ob_type" and (for a var 169 object) "ob_size" are fields of the type in question. 170 171 In Python 3, this is defined as an embedded PyVarObject type thus: 172 PyVarObject ob_base; 173 so that the "ob_size" field is located insize the "ob_base" field, and 174 the "ob_type" is most easily accessed by casting back to a (PyObject*). 175 ''' 176 if self.is_null(): 177 raise NullPyObjectPtr(self) 178 179 if name == 'ob_type': 180 pyo_ptr = self._gdbval.cast(PyObjectPtr.get_gdb_type()) 181 return pyo_ptr.dereference()[name] 182 183 if name == 'ob_size': 184 try: 185 # Python 2: 186 return self._gdbval.dereference()[name] 187 except RuntimeError: 188 # Python 3: 189 return self._gdbval.dereference()['ob_base'][name] 190 191 # General case: look it up inside the object: 192 return self._gdbval.dereference()[name] 193 194 def pyop_field(self, name): 195 ''' 196 Get a PyObjectPtr for the given PyObject* field within this PyObject, 197 coping with some python 2 versus python 3 differences. 198 ''' 199 return PyObjectPtr.from_pyobject_ptr(self.field(name)) 200 201 def write_field_repr(self, name, out, visited): 202 ''' 203 Extract the PyObject* field named "name", and write its representation 204 to file-like object "out" 205 ''' 206 field_obj = self.pyop_field(name) 207 field_obj.write_repr(out, visited) 208 209 def get_truncated_repr(self, maxlen): 210 ''' 211 Get a repr-like string for the data, but truncate it at "maxlen" bytes 212 (ending the object graph traversal as soon as you do) 213 ''' 214 out = TruncatedStringIO(maxlen) 215 try: 216 self.write_repr(out, set()) 217 except StringTruncated: 218 # Truncation occurred: 219 return out.getvalue() + '...(truncated)' 220 221 # No truncation occurred: 222 return out.getvalue() 223 224 def type(self): 225 return PyTypeObjectPtr(self.field('ob_type')) 226 227 def is_null(self): 228 return 0 == long(self._gdbval) 229 230 def is_optimized_out(self): 231 ''' 232 Is the value of the underlying PyObject* visible to the debugger? 233 234 This can vary with the precise version of the compiler used to build 235 Python, and the precise version of gdb. 236 237 See e.g. https://bugzilla.redhat.com/show_bug.cgi?id=556975 with 238 PyEval_EvalFrameEx's "f" 239 ''' 240 return self._gdbval.is_optimized_out 241 242 def safe_tp_name(self): 243 try: 244 ob_type = self.type() 245 tp_name = ob_type.field('tp_name') 246 return tp_name.string() 247 # NullPyObjectPtr: NULL tp_name? 248 # RuntimeError: Can't even read the object at all? 249 # UnicodeDecodeError: Failed to decode tp_name bytestring 250 except (NullPyObjectPtr, RuntimeError, UnicodeDecodeError): 251 return 'unknown' 252 253 def proxyval(self, visited): 254 ''' 255 Scrape a value from the inferior process, and try to represent it 256 within the gdb process, whilst (hopefully) avoiding crashes when 257 the remote data is corrupt. 258 259 Derived classes will override this. 260 261 For example, a PyIntObject* with ob_ival 42 in the inferior process 262 should result in an int(42) in this process. 263 264 visited: a set of all gdb.Value pyobject pointers already visited 265 whilst generating this value (to guard against infinite recursion when 266 visiting object graphs with loops). Analogous to Py_ReprEnter and 267 Py_ReprLeave 268 ''' 269 270 class FakeRepr(object): 271 """ 272 Class representing a non-descript PyObject* value in the inferior 273 process for when we don't have a custom scraper, intended to have 274 a sane repr(). 275 """ 276 277 def __init__(self, tp_name, address): 278 self.tp_name = tp_name 279 self.address = address 280 281 def __repr__(self): 282 # For the NULL pointer, we have no way of knowing a type, so 283 # special-case it as per 284 # http://bugs.python.org/issue8032#msg100882 285 if self.address == 0: 286 return '0x0' 287 return '<%s at remote 0x%x>' % (self.tp_name, self.address) 288 289 return FakeRepr(self.safe_tp_name(), 290 long(self._gdbval)) 291 292 def write_repr(self, out, visited): 293 ''' 294 Write a string representation of the value scraped from the inferior 295 process to "out", a file-like object. 296 ''' 297 # Default implementation: generate a proxy value and write its repr 298 # However, this could involve a lot of work for complicated objects, 299 # so for derived classes we specialize this 300 return out.write(repr(self.proxyval(visited))) 301 302 @classmethod 303 def subclass_from_type(cls, t): 304 ''' 305 Given a PyTypeObjectPtr instance wrapping a gdb.Value that's a 306 (PyTypeObject*), determine the corresponding subclass of PyObjectPtr 307 to use 308 309 Ideally, we would look up the symbols for the global types, but that 310 isn't working yet: 311 (gdb) python print gdb.lookup_symbol('PyList_Type')[0].value 312 Traceback (most recent call last): 313 File "<string>", line 1, in <module> 314 NotImplementedError: Symbol type not yet supported in Python scripts. 315 Error while executing Python code. 316 317 For now, we use tp_flags, after doing some string comparisons on the 318 tp_name for some special-cases that don't seem to be visible through 319 flags 320 ''' 321 try: 322 tp_name = t.field('tp_name').string() 323 tp_flags = int(t.field('tp_flags')) 324 # RuntimeError: NULL pointers 325 # UnicodeDecodeError: string() fails to decode the bytestring 326 except (RuntimeError, UnicodeDecodeError): 327 # Handle any kind of error e.g. NULL ptrs by simply using the base 328 # class 329 return cls 330 331 #print('tp_flags = 0x%08x' % tp_flags) 332 #print('tp_name = %r' % tp_name) 333 334 name_map = {'bool': PyBoolObjectPtr, 335 'classobj': PyClassObjectPtr, 336 'instance': PyInstanceObjectPtr, 337 'NoneType': PyNoneStructPtr, 338 'frame': PyFrameObjectPtr, 339 'set' : PySetObjectPtr, 340 'frozenset' : PySetObjectPtr, 341 'builtin_function_or_method' : PyCFunctionObjectPtr, 342 'method-wrapper': wrapperobject, 343 } 344 if tp_name in name_map: 345 return name_map[tp_name] 346 347 if tp_flags & Py_TPFLAGS_HEAPTYPE: 348 return HeapTypeObjectPtr 349 350 if tp_flags & Py_TPFLAGS_INT_SUBCLASS: 351 return PyIntObjectPtr 352 if tp_flags & Py_TPFLAGS_LONG_SUBCLASS: 353 return PyLongObjectPtr 354 if tp_flags & Py_TPFLAGS_LIST_SUBCLASS: 355 return PyListObjectPtr 356 if tp_flags & Py_TPFLAGS_TUPLE_SUBCLASS: 357 return PyTupleObjectPtr 358 if tp_flags & Py_TPFLAGS_STRING_SUBCLASS: 359 return PyStringObjectPtr 360 if tp_flags & Py_TPFLAGS_UNICODE_SUBCLASS: 361 return PyUnicodeObjectPtr 362 if tp_flags & Py_TPFLAGS_DICT_SUBCLASS: 363 return PyDictObjectPtr 364 if tp_flags & Py_TPFLAGS_BASE_EXC_SUBCLASS: 365 return PyBaseExceptionObjectPtr 366 #if tp_flags & Py_TPFLAGS_TYPE_SUBCLASS: 367 # return PyTypeObjectPtr 368 369 # Use the base class: 370 return cls 371 372 @classmethod 373 def from_pyobject_ptr(cls, gdbval): 374 ''' 375 Try to locate the appropriate derived class dynamically, and cast 376 the pointer accordingly. 377 ''' 378 try: 379 p = PyObjectPtr(gdbval) 380 cls = cls.subclass_from_type(p.type()) 381 return cls(gdbval, cast_to=cls.get_gdb_type()) 382 except RuntimeError: 383 # Handle any kind of error e.g. NULL ptrs by simply using the base 384 # class 385 pass 386 return cls(gdbval) 387 388 @classmethod 389 def get_gdb_type(cls): 390 return gdb.lookup_type(cls._typename).pointer() 391 392 def as_address(self): 393 return long(self._gdbval) 394 395 396class ProxyAlreadyVisited(object): 397 ''' 398 Placeholder proxy to use when protecting against infinite recursion due to 399 loops in the object graph. 400 401 Analogous to the values emitted by the users of Py_ReprEnter and Py_ReprLeave 402 ''' 403 def __init__(self, rep): 404 self._rep = rep 405 406 def __repr__(self): 407 return self._rep 408 409 410def _write_instance_repr(out, visited, name, pyop_attrdict, address): 411 '''Shared code for use by old-style and new-style classes: 412 write a representation to file-like object "out"''' 413 out.write('<') 414 out.write(name) 415 416 # Write dictionary of instance attributes: 417 if isinstance(pyop_attrdict, PyDictObjectPtr): 418 out.write('(') 419 first = True 420 for pyop_arg, pyop_val in pyop_attrdict.iteritems(): 421 if not first: 422 out.write(', ') 423 first = False 424 out.write(pyop_arg.proxyval(visited)) 425 out.write('=') 426 pyop_val.write_repr(out, visited) 427 out.write(')') 428 out.write(' at remote 0x%x>' % address) 429 430 431class InstanceProxy(object): 432 433 def __init__(self, cl_name, attrdict, address): 434 self.cl_name = cl_name 435 self.attrdict = attrdict 436 self.address = address 437 438 def __repr__(self): 439 if isinstance(self.attrdict, dict): 440 kwargs = ', '.join(["%s=%r" % (arg, val) 441 for arg, val in self.attrdict.iteritems()]) 442 return '<%s(%s) at remote 0x%x>' % (self.cl_name, 443 kwargs, self.address) 444 else: 445 return '<%s at remote 0x%x>' % (self.cl_name, 446 self.address) 447 448def _PyObject_VAR_SIZE(typeobj, nitems): 449 if _PyObject_VAR_SIZE._type_size_t is None: 450 _PyObject_VAR_SIZE._type_size_t = gdb.lookup_type('size_t') 451 452 return ( ( typeobj.field('tp_basicsize') + 453 nitems * typeobj.field('tp_itemsize') + 454 (_sizeof_void_p() - 1) 455 ) & ~(_sizeof_void_p() - 1) 456 ).cast(_PyObject_VAR_SIZE._type_size_t) 457_PyObject_VAR_SIZE._type_size_t = None 458 459class HeapTypeObjectPtr(PyObjectPtr): 460 _typename = 'PyObject' 461 462 def get_attr_dict(self): 463 ''' 464 Get the PyDictObject ptr representing the attribute dictionary 465 (or None if there's a problem) 466 ''' 467 try: 468 typeobj = self.type() 469 dictoffset = int_from_int(typeobj.field('tp_dictoffset')) 470 if dictoffset != 0: 471 if dictoffset < 0: 472 type_PyVarObject_ptr = gdb.lookup_type('PyVarObject').pointer() 473 tsize = int_from_int(self._gdbval.cast(type_PyVarObject_ptr)['ob_size']) 474 if tsize < 0: 475 tsize = -tsize 476 size = _PyObject_VAR_SIZE(typeobj, tsize) 477 dictoffset += size 478 assert dictoffset > 0 479 assert dictoffset % _sizeof_void_p() == 0 480 481 dictptr = self._gdbval.cast(_type_char_ptr()) + dictoffset 482 PyObjectPtrPtr = PyObjectPtr.get_gdb_type().pointer() 483 dictptr = dictptr.cast(PyObjectPtrPtr) 484 return PyObjectPtr.from_pyobject_ptr(dictptr.dereference()) 485 except RuntimeError: 486 # Corrupt data somewhere; fail safe 487 pass 488 489 # Not found, or some kind of error: 490 return None 491 492 def proxyval(self, visited): 493 ''' 494 Support for new-style classes. 495 496 Currently we just locate the dictionary using a transliteration to 497 python of _PyObject_GetDictPtr, ignoring descriptors 498 ''' 499 # Guard against infinite loops: 500 if self.as_address() in visited: 501 return ProxyAlreadyVisited('<...>') 502 visited.add(self.as_address()) 503 504 pyop_attr_dict = self.get_attr_dict() 505 if pyop_attr_dict: 506 attr_dict = pyop_attr_dict.proxyval(visited) 507 else: 508 attr_dict = {} 509 tp_name = self.safe_tp_name() 510 511 # New-style class: 512 return InstanceProxy(tp_name, attr_dict, long(self._gdbval)) 513 514 def write_repr(self, out, visited): 515 # Guard against infinite loops: 516 if self.as_address() in visited: 517 out.write('<...>') 518 return 519 visited.add(self.as_address()) 520 521 pyop_attrdict = self.get_attr_dict() 522 _write_instance_repr(out, visited, 523 self.safe_tp_name(), pyop_attrdict, self.as_address()) 524 525class ProxyException(Exception): 526 def __init__(self, tp_name, args): 527 self.tp_name = tp_name 528 self.args = args 529 530 def __repr__(self): 531 return '%s%r' % (self.tp_name, self.args) 532 533class PyBaseExceptionObjectPtr(PyObjectPtr): 534 """ 535 Class wrapping a gdb.Value that's a PyBaseExceptionObject* i.e. an exception 536 within the process being debugged. 537 """ 538 _typename = 'PyBaseExceptionObject' 539 540 def proxyval(self, visited): 541 # Guard against infinite loops: 542 if self.as_address() in visited: 543 return ProxyAlreadyVisited('(...)') 544 visited.add(self.as_address()) 545 arg_proxy = self.pyop_field('args').proxyval(visited) 546 return ProxyException(self.safe_tp_name(), 547 arg_proxy) 548 549 def write_repr(self, out, visited): 550 # Guard against infinite loops: 551 if self.as_address() in visited: 552 out.write('(...)') 553 return 554 visited.add(self.as_address()) 555 556 out.write(self.safe_tp_name()) 557 self.write_field_repr('args', out, visited) 558 559class PyBoolObjectPtr(PyObjectPtr): 560 """ 561 Class wrapping a gdb.Value that's a PyBoolObject* i.e. one of the two 562 <bool> instances (Py_True/Py_False) within the process being debugged. 563 """ 564 _typename = 'PyBoolObject' 565 566 def proxyval(self, visited): 567 if int_from_int(self.field('ob_ival')): 568 return True 569 else: 570 return False 571 572 573class PyClassObjectPtr(PyObjectPtr): 574 """ 575 Class wrapping a gdb.Value that's a PyClassObject* i.e. a <classobj> 576 instance within the process being debugged. 577 """ 578 _typename = 'PyClassObject' 579 580 581class BuiltInFunctionProxy(object): 582 def __init__(self, ml_name): 583 self.ml_name = ml_name 584 585 def __repr__(self): 586 return "<built-in function %s>" % self.ml_name 587 588class BuiltInMethodProxy(object): 589 def __init__(self, ml_name, pyop_m_self): 590 self.ml_name = ml_name 591 self.pyop_m_self = pyop_m_self 592 593 def __repr__(self): 594 return ('<built-in method %s of %s object at remote 0x%x>' 595 % (self.ml_name, 596 self.pyop_m_self.safe_tp_name(), 597 self.pyop_m_self.as_address()) 598 ) 599 600class PyCFunctionObjectPtr(PyObjectPtr): 601 """ 602 Class wrapping a gdb.Value that's a PyCFunctionObject* 603 (see Include/methodobject.h and Objects/methodobject.c) 604 """ 605 _typename = 'PyCFunctionObject' 606 607 def proxyval(self, visited): 608 m_ml = self.field('m_ml') # m_ml is a (PyMethodDef*) 609 try: 610 ml_name = m_ml['ml_name'].string() 611 except UnicodeDecodeError: 612 ml_name = '<ml_name:UnicodeDecodeError>' 613 614 pyop_m_self = self.pyop_field('m_self') 615 if pyop_m_self.is_null(): 616 return BuiltInFunctionProxy(ml_name) 617 else: 618 return BuiltInMethodProxy(ml_name, pyop_m_self) 619 620 621class PyCodeObjectPtr(PyObjectPtr): 622 """ 623 Class wrapping a gdb.Value that's a PyCodeObject* i.e. a <code> instance 624 within the process being debugged. 625 """ 626 _typename = 'PyCodeObject' 627 628 def addr2line(self, addrq): 629 ''' 630 Get the line number for a given bytecode offset 631 632 Analogous to PyCode_Addr2Line; translated from pseudocode in 633 Objects/lnotab_notes.txt 634 ''' 635 co_lnotab = self.pyop_field('co_lnotab').proxyval(set()) 636 637 # Initialize lineno to co_firstlineno as per PyCode_Addr2Line 638 # not 0, as lnotab_notes.txt has it: 639 lineno = int_from_int(self.field('co_firstlineno')) 640 641 addr = 0 642 for addr_incr, line_incr in zip(co_lnotab[::2], co_lnotab[1::2]): 643 addr += ord(addr_incr) 644 if addr > addrq: 645 return lineno 646 lineno += ord(line_incr) 647 return lineno 648 649 650class PyDictObjectPtr(PyObjectPtr): 651 """ 652 Class wrapping a gdb.Value that's a PyDictObject* i.e. a dict instance 653 within the process being debugged. 654 """ 655 _typename = 'PyDictObject' 656 657 def iteritems(self): 658 ''' 659 Yields a sequence of (PyObjectPtr key, PyObjectPtr value) pairs, 660 analogous to dict.iteritems() 661 ''' 662 for i in safe_range(self.field('ma_mask') + 1): 663 ep = self.field('ma_table') + i 664 pyop_value = PyObjectPtr.from_pyobject_ptr(ep['me_value']) 665 if not pyop_value.is_null(): 666 pyop_key = PyObjectPtr.from_pyobject_ptr(ep['me_key']) 667 yield (pyop_key, pyop_value) 668 669 def proxyval(self, visited): 670 # Guard against infinite loops: 671 if self.as_address() in visited: 672 return ProxyAlreadyVisited('{...}') 673 visited.add(self.as_address()) 674 675 result = {} 676 for pyop_key, pyop_value in self.iteritems(): 677 proxy_key = pyop_key.proxyval(visited) 678 proxy_value = pyop_value.proxyval(visited) 679 result[proxy_key] = proxy_value 680 return result 681 682 def write_repr(self, out, visited): 683 # Guard against infinite loops: 684 if self.as_address() in visited: 685 out.write('{...}') 686 return 687 visited.add(self.as_address()) 688 689 out.write('{') 690 first = True 691 for pyop_key, pyop_value in self.iteritems(): 692 if not first: 693 out.write(', ') 694 first = False 695 pyop_key.write_repr(out, visited) 696 out.write(': ') 697 pyop_value.write_repr(out, visited) 698 out.write('}') 699 700class PyInstanceObjectPtr(PyObjectPtr): 701 _typename = 'PyInstanceObject' 702 703 def proxyval(self, visited): 704 # Guard against infinite loops: 705 if self.as_address() in visited: 706 return ProxyAlreadyVisited('<...>') 707 visited.add(self.as_address()) 708 709 # Get name of class: 710 in_class = self.pyop_field('in_class') 711 cl_name = in_class.pyop_field('cl_name').proxyval(visited) 712 713 # Get dictionary of instance attributes: 714 in_dict = self.pyop_field('in_dict').proxyval(visited) 715 716 # Old-style class: 717 return InstanceProxy(cl_name, in_dict, long(self._gdbval)) 718 719 def write_repr(self, out, visited): 720 # Guard against infinite loops: 721 if self.as_address() in visited: 722 out.write('<...>') 723 return 724 visited.add(self.as_address()) 725 726 # Old-style class: 727 728 # Get name of class: 729 in_class = self.pyop_field('in_class') 730 cl_name = in_class.pyop_field('cl_name').proxyval(visited) 731 732 # Get dictionary of instance attributes: 733 pyop_in_dict = self.pyop_field('in_dict') 734 735 _write_instance_repr(out, visited, 736 cl_name, pyop_in_dict, self.as_address()) 737 738class PyIntObjectPtr(PyObjectPtr): 739 _typename = 'PyIntObject' 740 741 def proxyval(self, visited): 742 result = int_from_int(self.field('ob_ival')) 743 return result 744 745class PyListObjectPtr(PyObjectPtr): 746 _typename = 'PyListObject' 747 748 def __getitem__(self, i): 749 # Get the gdb.Value for the (PyObject*) with the given index: 750 field_ob_item = self.field('ob_item') 751 return field_ob_item[i] 752 753 def proxyval(self, visited): 754 # Guard against infinite loops: 755 if self.as_address() in visited: 756 return ProxyAlreadyVisited('[...]') 757 visited.add(self.as_address()) 758 759 result = [PyObjectPtr.from_pyobject_ptr(self[i]).proxyval(visited) 760 for i in safe_range(int_from_int(self.field('ob_size')))] 761 return result 762 763 def write_repr(self, out, visited): 764 # Guard against infinite loops: 765 if self.as_address() in visited: 766 out.write('[...]') 767 return 768 visited.add(self.as_address()) 769 770 out.write('[') 771 for i in safe_range(int_from_int(self.field('ob_size'))): 772 if i > 0: 773 out.write(', ') 774 element = PyObjectPtr.from_pyobject_ptr(self[i]) 775 element.write_repr(out, visited) 776 out.write(']') 777 778class PyLongObjectPtr(PyObjectPtr): 779 _typename = 'PyLongObject' 780 781 def proxyval(self, visited): 782 ''' 783 Python's Include/longobjrep.h has this declaration: 784 struct _longobject { 785 PyObject_VAR_HEAD 786 digit ob_digit[1]; 787 }; 788 789 with this description: 790 The absolute value of a number is equal to 791 SUM(for i=0 through abs(ob_size)-1) ob_digit[i] * 2**(SHIFT*i) 792 Negative numbers are represented with ob_size < 0; 793 zero is represented by ob_size == 0. 794 795 where SHIFT can be either: 796 #define PyLong_SHIFT 30 797 #define PyLong_SHIFT 15 798 ''' 799 ob_size = long(self.field('ob_size')) 800 if ob_size == 0: 801 return 0 802 803 ob_digit = self.field('ob_digit') 804 805 if gdb.lookup_type('digit').sizeof == 2: 806 SHIFT = 15 807 else: 808 SHIFT = 30 809 810 digits = [long(ob_digit[i]) * 2**(SHIFT*i) 811 for i in safe_range(abs(ob_size))] 812 result = sum(digits) 813 if ob_size < 0: 814 result = -result 815 return result 816 817 def write_repr(self, out, visited): 818 # This ensures the trailing 'L' is printed when gdb is linked 819 # with a Python 3 interpreter. 820 out.write(repr(self.proxyval(visited)).rstrip('L')) 821 out.write('L') 822 823 824class PyNoneStructPtr(PyObjectPtr): 825 """ 826 Class wrapping a gdb.Value that's a PyObject* pointing to the 827 singleton (we hope) _Py_NoneStruct with ob_type PyNone_Type 828 """ 829 _typename = 'PyObject' 830 831 def proxyval(self, visited): 832 return None 833 834 835class PyFrameObjectPtr(PyObjectPtr): 836 _typename = 'PyFrameObject' 837 838 def __init__(self, gdbval, cast_to=None): 839 PyObjectPtr.__init__(self, gdbval, cast_to) 840 841 if not self.is_optimized_out(): 842 self.co = PyCodeObjectPtr.from_pyobject_ptr(self.field('f_code')) 843 self.co_name = self.co.pyop_field('co_name') 844 self.co_filename = self.co.pyop_field('co_filename') 845 846 self.f_lineno = int_from_int(self.field('f_lineno')) 847 self.f_lasti = int_from_int(self.field('f_lasti')) 848 self.co_nlocals = int_from_int(self.co.field('co_nlocals')) 849 self.co_varnames = PyTupleObjectPtr.from_pyobject_ptr(self.co.field('co_varnames')) 850 851 def iter_locals(self): 852 ''' 853 Yield a sequence of (name,value) pairs of PyObjectPtr instances, for 854 the local variables of this frame 855 ''' 856 if self.is_optimized_out(): 857 return 858 859 f_localsplus = self.field('f_localsplus') 860 for i in safe_range(self.co_nlocals): 861 pyop_value = PyObjectPtr.from_pyobject_ptr(f_localsplus[i]) 862 if not pyop_value.is_null(): 863 pyop_name = PyObjectPtr.from_pyobject_ptr(self.co_varnames[i]) 864 yield (pyop_name, pyop_value) 865 866 def iter_globals(self): 867 ''' 868 Yield a sequence of (name,value) pairs of PyObjectPtr instances, for 869 the global variables of this frame 870 ''' 871 if self.is_optimized_out(): 872 return () 873 874 pyop_globals = self.pyop_field('f_globals') 875 return pyop_globals.iteritems() 876 877 def iter_builtins(self): 878 ''' 879 Yield a sequence of (name,value) pairs of PyObjectPtr instances, for 880 the builtin variables 881 ''' 882 if self.is_optimized_out(): 883 return () 884 885 pyop_builtins = self.pyop_field('f_builtins') 886 return pyop_builtins.iteritems() 887 888 def get_var_by_name(self, name): 889 ''' 890 Look for the named local variable, returning a (PyObjectPtr, scope) pair 891 where scope is a string 'local', 'global', 'builtin' 892 893 If not found, return (None, None) 894 ''' 895 for pyop_name, pyop_value in self.iter_locals(): 896 if name == pyop_name.proxyval(set()): 897 return pyop_value, 'local' 898 for pyop_name, pyop_value in self.iter_globals(): 899 if name == pyop_name.proxyval(set()): 900 return pyop_value, 'global' 901 for pyop_name, pyop_value in self.iter_builtins(): 902 if name == pyop_name.proxyval(set()): 903 return pyop_value, 'builtin' 904 return None, None 905 906 def filename(self): 907 '''Get the path of the current Python source file, as a string''' 908 if self.is_optimized_out(): 909 return '(frame information optimized out)' 910 return self.co_filename.proxyval(set()) 911 912 def current_line_num(self): 913 '''Get current line number as an integer (1-based) 914 915 Translated from PyFrame_GetLineNumber and PyCode_Addr2Line 916 917 See Objects/lnotab_notes.txt 918 ''' 919 if self.is_optimized_out(): 920 return None 921 f_trace = self.field('f_trace') 922 if long(f_trace) != 0: 923 # we have a non-NULL f_trace: 924 return self.f_lineno 925 else: 926 #try: 927 return self.co.addr2line(self.f_lasti) 928 #except ValueError: 929 # return self.f_lineno 930 931 def current_line(self): 932 '''Get the text of the current source line as a string, with a trailing 933 newline character''' 934 if self.is_optimized_out(): 935 return '(frame information optimized out)' 936 filename = self.filename() 937 try: 938 f = open(filename, 'r') 939 except IOError: 940 return None 941 with f: 942 all_lines = f.readlines() 943 # Convert from 1-based current_line_num to 0-based list offset: 944 return all_lines[self.current_line_num()-1] 945 946 def write_repr(self, out, visited): 947 if self.is_optimized_out(): 948 out.write('(frame information optimized out)') 949 return 950 out.write('Frame 0x%x, for file %s, line %i, in %s (' 951 % (self.as_address(), 952 self.co_filename.proxyval(visited), 953 self.current_line_num(), 954 self.co_name.proxyval(visited))) 955 first = True 956 for pyop_name, pyop_value in self.iter_locals(): 957 if not first: 958 out.write(', ') 959 first = False 960 961 out.write(pyop_name.proxyval(visited)) 962 out.write('=') 963 pyop_value.write_repr(out, visited) 964 965 out.write(')') 966 967 def print_traceback(self): 968 if self.is_optimized_out(): 969 sys.stdout.write(' (frame information optimized out)\n') 970 return 971 visited = set() 972 sys.stdout.write(' File "%s", line %i, in %s\n' 973 % (self.co_filename.proxyval(visited), 974 self.current_line_num(), 975 self.co_name.proxyval(visited))) 976 977class PySetObjectPtr(PyObjectPtr): 978 _typename = 'PySetObject' 979 980 def proxyval(self, visited): 981 # Guard against infinite loops: 982 if self.as_address() in visited: 983 return ProxyAlreadyVisited('%s(...)' % self.safe_tp_name()) 984 visited.add(self.as_address()) 985 986 members = [] 987 table = self.field('table') 988 for i in safe_range(self.field('mask')+1): 989 setentry = table[i] 990 key = setentry['key'] 991 if key != 0: 992 key_proxy = PyObjectPtr.from_pyobject_ptr(key).proxyval(visited) 993 if key_proxy != '<dummy key>': 994 members.append(key_proxy) 995 if self.safe_tp_name() == 'frozenset': 996 return frozenset(members) 997 else: 998 return set(members) 999 1000 def write_repr(self, out, visited): 1001 out.write(self.safe_tp_name()) 1002 1003 # Guard against infinite loops: 1004 if self.as_address() in visited: 1005 out.write('(...)') 1006 return 1007 visited.add(self.as_address()) 1008 1009 out.write('([') 1010 first = True 1011 table = self.field('table') 1012 for i in safe_range(self.field('mask')+1): 1013 setentry = table[i] 1014 key = setentry['key'] 1015 if key != 0: 1016 pyop_key = PyObjectPtr.from_pyobject_ptr(key) 1017 key_proxy = pyop_key.proxyval(visited) # FIXME! 1018 if key_proxy != '<dummy key>': 1019 if not first: 1020 out.write(', ') 1021 first = False 1022 pyop_key.write_repr(out, visited) 1023 out.write('])') 1024 1025 1026class PyStringObjectPtr(PyObjectPtr): 1027 _typename = 'PyStringObject' 1028 1029 def __str__(self): 1030 field_ob_size = self.field('ob_size') 1031 field_ob_sval = self.field('ob_sval') 1032 char_ptr = field_ob_sval.address.cast(_type_unsigned_char_ptr()) 1033 # When gdb is linked with a Python 3 interpreter, this is really 1034 # a latin-1 mojibake decoding of the original string... 1035 return ''.join([chr(char_ptr[i]) for i in safe_range(field_ob_size)]) 1036 1037 def proxyval(self, visited): 1038 return str(self) 1039 1040 def write_repr(self, out, visited): 1041 val = repr(self.proxyval(visited)) 1042 if sys.version_info[0] >= 3: 1043 val = val.encode('ascii', 'backslashreplace').decode('ascii') 1044 out.write(val) 1045 1046class PyTupleObjectPtr(PyObjectPtr): 1047 _typename = 'PyTupleObject' 1048 1049 def __getitem__(self, i): 1050 # Get the gdb.Value for the (PyObject*) with the given index: 1051 field_ob_item = self.field('ob_item') 1052 return field_ob_item[i] 1053 1054 def proxyval(self, visited): 1055 # Guard against infinite loops: 1056 if self.as_address() in visited: 1057 return ProxyAlreadyVisited('(...)') 1058 visited.add(self.as_address()) 1059 1060 result = tuple([PyObjectPtr.from_pyobject_ptr(self[i]).proxyval(visited) 1061 for i in safe_range(int_from_int(self.field('ob_size')))]) 1062 return result 1063 1064 def write_repr(self, out, visited): 1065 # Guard against infinite loops: 1066 if self.as_address() in visited: 1067 out.write('(...)') 1068 return 1069 visited.add(self.as_address()) 1070 1071 out.write('(') 1072 for i in safe_range(int_from_int(self.field('ob_size'))): 1073 if i > 0: 1074 out.write(', ') 1075 element = PyObjectPtr.from_pyobject_ptr(self[i]) 1076 element.write_repr(out, visited) 1077 if self.field('ob_size') == 1: 1078 out.write(',)') 1079 else: 1080 out.write(')') 1081 1082class PyTypeObjectPtr(PyObjectPtr): 1083 _typename = 'PyTypeObject' 1084 1085 1086if sys.maxunicode >= 0x10000: 1087 _unichr = unichr 1088else: 1089 # Needed for proper surrogate support if sizeof(Py_UNICODE) is 2 in gdb 1090 def _unichr(x): 1091 if x < 0x10000: 1092 return unichr(x) 1093 x -= 0x10000 1094 ch1 = 0xD800 | (x >> 10) 1095 ch2 = 0xDC00 | (x & 0x3FF) 1096 return unichr(ch1) + unichr(ch2) 1097 1098class PyUnicodeObjectPtr(PyObjectPtr): 1099 _typename = 'PyUnicodeObject' 1100 1101 def char_width(self): 1102 _type_Py_UNICODE = gdb.lookup_type('Py_UNICODE') 1103 return _type_Py_UNICODE.sizeof 1104 1105 def proxyval(self, visited): 1106 # From unicodeobject.h: 1107 # Py_ssize_t length; /* Length of raw Unicode data in buffer */ 1108 # Py_UNICODE *str; /* Raw Unicode buffer */ 1109 field_length = long(self.field('length')) 1110 field_str = self.field('str') 1111 1112 # Gather a list of ints from the Py_UNICODE array; these are either 1113 # UCS-2 or UCS-4 code points: 1114 if self.char_width() > 2: 1115 Py_UNICODEs = [int(field_str[i]) for i in safe_range(field_length)] 1116 else: 1117 # A more elaborate routine if sizeof(Py_UNICODE) is 2 in the 1118 # inferior process: we must join surrogate pairs. 1119 Py_UNICODEs = [] 1120 i = 0 1121 limit = safety_limit(field_length) 1122 while i < limit: 1123 ucs = int(field_str[i]) 1124 i += 1 1125 if ucs < 0xD800 or ucs >= 0xDC00 or i == field_length: 1126 Py_UNICODEs.append(ucs) 1127 continue 1128 # This could be a surrogate pair. 1129 ucs2 = int(field_str[i]) 1130 if ucs2 < 0xDC00 or ucs2 > 0xDFFF: 1131 continue 1132 code = (ucs & 0x03FF) << 10 1133 code |= ucs2 & 0x03FF 1134 code += 0x00010000 1135 Py_UNICODEs.append(code) 1136 i += 1 1137 1138 # Convert the int code points to unicode characters, and generate a 1139 # local unicode instance. 1140 # This splits surrogate pairs if sizeof(Py_UNICODE) is 2 here (in gdb). 1141 result = u''.join([ 1142 (_unichr(ucs) if ucs <= 0x10ffff else '\ufffd') 1143 for ucs in Py_UNICODEs]) 1144 return result 1145 1146 def write_repr(self, out, visited): 1147 val = repr(self.proxyval(visited)) 1148 if sys.version_info[0] >= 3: 1149 val = val.encode('ascii', 'backslashreplace').decode('ascii') 1150 # This ensures the 'u' prefix is printed when gdb is linked 1151 # with a Python 3 interpreter. 1152 out.write('u') 1153 out.write(val.lstrip('u')) 1154 1155 1156class wrapperobject(PyObjectPtr): 1157 _typename = 'wrapperobject' 1158 1159 def safe_name(self): 1160 try: 1161 name = self.field('descr')['d_base']['name'].string() 1162 return repr(name) 1163 except (NullPyObjectPtr, RuntimeError, UnicodeDecodeError): 1164 return '<unknown name>' 1165 1166 def safe_tp_name(self): 1167 try: 1168 return self.field('self')['ob_type']['tp_name'].string() 1169 except (NullPyObjectPtr, RuntimeError, UnicodeDecodeError): 1170 return '<unknown tp_name>' 1171 1172 def safe_self_addresss(self): 1173 try: 1174 address = long(self.field('self')) 1175 return '%#x' % address 1176 except (NullPyObjectPtr, RuntimeError): 1177 return '<failed to get self address>' 1178 1179 def proxyval(self, visited): 1180 name = self.safe_name() 1181 tp_name = self.safe_tp_name() 1182 self_address = self.safe_self_addresss() 1183 return ("<method-wrapper %s of %s object at %s>" 1184 % (name, tp_name, self_address)) 1185 1186 def write_repr(self, out, visited): 1187 proxy = self.proxyval(visited) 1188 out.write(proxy) 1189 1190 1191def int_from_int(gdbval): 1192 return int(str(gdbval)) 1193 1194 1195def stringify(val): 1196 # TODO: repr() puts everything on one line; pformat can be nicer, but 1197 # can lead to v.long results; this function isolates the choice 1198 if True: 1199 return repr(val) 1200 else: 1201 from pprint import pformat 1202 return pformat(val) 1203 1204 1205class PyObjectPtrPrinter: 1206 "Prints a (PyObject*)" 1207 1208 def __init__ (self, gdbval): 1209 self.gdbval = gdbval 1210 1211 def to_string (self): 1212 pyop = PyObjectPtr.from_pyobject_ptr(self.gdbval) 1213 if True: 1214 return pyop.get_truncated_repr(MAX_OUTPUT_LEN) 1215 else: 1216 # Generate full proxy value then stringify it. 1217 # Doing so could be expensive 1218 proxyval = pyop.proxyval(set()) 1219 return stringify(proxyval) 1220 1221def pretty_printer_lookup(gdbval): 1222 type = gdbval.type.unqualified() 1223 if type.code != gdb.TYPE_CODE_PTR: 1224 return None 1225 1226 type = type.target().unqualified() 1227 t = str(type) 1228 if t in ("PyObject", "PyFrameObject", "PyUnicodeObject", "wrapperobject"): 1229 return PyObjectPtrPrinter(gdbval) 1230 1231""" 1232During development, I've been manually invoking the code in this way: 1233(gdb) python 1234 1235import sys 1236sys.path.append('/home/david/coding/python-gdb') 1237import libpython 1238end 1239 1240then reloading it after each edit like this: 1241(gdb) python reload(libpython) 1242 1243The following code should ensure that the prettyprinter is registered 1244if the code is autoloaded by gdb when visiting libpython.so, provided 1245that this python file is installed to the same path as the library (or its 1246.debug file) plus a "-gdb.py" suffix, e.g: 1247 /usr/lib/libpython2.6.so.1.0-gdb.py 1248 /usr/lib/debug/usr/lib/libpython2.6.so.1.0.debug-gdb.py 1249""" 1250def register (obj): 1251 if obj is None: 1252 obj = gdb 1253 1254 # Wire up the pretty-printer 1255 obj.pretty_printers.append(pretty_printer_lookup) 1256 1257register (gdb.current_objfile ()) 1258 1259 1260 1261# Unfortunately, the exact API exposed by the gdb module varies somewhat 1262# from build to build 1263# See http://bugs.python.org/issue8279?#msg102276 1264 1265class Frame(object): 1266 ''' 1267 Wrapper for gdb.Frame, adding various methods 1268 ''' 1269 def __init__(self, gdbframe): 1270 self._gdbframe = gdbframe 1271 1272 def older(self): 1273 older = self._gdbframe.older() 1274 if older: 1275 return Frame(older) 1276 else: 1277 return None 1278 1279 def newer(self): 1280 newer = self._gdbframe.newer() 1281 if newer: 1282 return Frame(newer) 1283 else: 1284 return None 1285 1286 def select(self): 1287 '''If supported, select this frame and return True; return False if unsupported 1288 1289 Not all builds have a gdb.Frame.select method; seems to be present on Fedora 12 1290 onwards, but absent on Ubuntu buildbot''' 1291 if not hasattr(self._gdbframe, 'select'): 1292 print ('Unable to select frame: ' 1293 'this build of gdb does not expose a gdb.Frame.select method') 1294 return False 1295 self._gdbframe.select() 1296 return True 1297 1298 def get_index(self): 1299 '''Calculate index of frame, starting at 0 for the newest frame within 1300 this thread''' 1301 index = 0 1302 # Go down until you reach the newest frame: 1303 iter_frame = self 1304 while iter_frame.newer(): 1305 index += 1 1306 iter_frame = iter_frame.newer() 1307 return index 1308 1309 # We divide frames into: 1310 # - "python frames": 1311 # - "bytecode frames" i.e. PyEval_EvalFrameEx 1312 # - "other python frames": things that are of interest from a python 1313 # POV, but aren't bytecode (e.g. GC, GIL) 1314 # - everything else 1315 1316 def is_python_frame(self): 1317 '''Is this a PyEval_EvalFrameEx frame, or some other important 1318 frame? (see is_other_python_frame for what "important" means in this 1319 context)''' 1320 if self.is_evalframeex(): 1321 return True 1322 if self.is_other_python_frame(): 1323 return True 1324 return False 1325 1326 def is_evalframeex(self): 1327 '''Is this a PyEval_EvalFrameEx frame?''' 1328 if self._gdbframe.name() == 'PyEval_EvalFrameEx': 1329 ''' 1330 I believe we also need to filter on the inline 1331 struct frame_id.inline_depth, only regarding frames with 1332 an inline depth of 0 as actually being this function 1333 1334 So we reject those with type gdb.INLINE_FRAME 1335 ''' 1336 if self._gdbframe.type() == gdb.NORMAL_FRAME: 1337 # We have a PyEval_EvalFrameEx frame: 1338 return True 1339 1340 return False 1341 1342 def is_other_python_frame(self): 1343 '''Is this frame worth displaying in python backtraces? 1344 Examples: 1345 - waiting on the GIL 1346 - garbage-collecting 1347 - within a CFunction 1348 If it is, return a descriptive string 1349 For other frames, return False 1350 ''' 1351 if self.is_waiting_for_gil(): 1352 return 'Waiting for the GIL' 1353 1354 if self.is_gc_collect(): 1355 return 'Garbage-collecting' 1356 1357 # Detect invocations of PyCFunction instances: 1358 frame = self._gdbframe 1359 caller = frame.name() 1360 if not caller: 1361 return False 1362 1363 if caller == 'PyCFunction_Call': 1364 arg_name = 'func' 1365 # Within that frame: 1366 # "func" is the local containing the PyObject* of the 1367 # PyCFunctionObject instance 1368 # "f" is the same value, but cast to (PyCFunctionObject*) 1369 # "self" is the (PyObject*) of the 'self' 1370 try: 1371 # Use the prettyprinter for the func: 1372 func = frame.read_var(arg_name) 1373 return str(func) 1374 except ValueError: 1375 return ('PyCFunction invocation (unable to read %s: ' 1376 'missing debuginfos?)' % arg_name) 1377 except RuntimeError: 1378 return 'PyCFunction invocation (unable to read %s)' % arg_name 1379 1380 if caller == 'wrapper_call': 1381 arg_name = 'wp' 1382 try: 1383 func = frame.read_var(arg_name) 1384 return str(func) 1385 except ValueError: 1386 return ('<wrapper_call invocation (unable to read %s: ' 1387 'missing debuginfos?)>' % arg_name) 1388 except RuntimeError: 1389 return '<wrapper_call invocation (unable to read %s)>' % arg_name 1390 1391 # This frame isn't worth reporting: 1392 return False 1393 1394 def is_waiting_for_gil(self): 1395 '''Is this frame waiting on the GIL?''' 1396 # This assumes the _POSIX_THREADS version of Python/ceval_gil.h: 1397 name = self._gdbframe.name() 1398 if name: 1399 return ('PyThread_acquire_lock' in name 1400 and 'lock_PyThread_acquire_lock' not in name) 1401 1402 def is_gc_collect(self): 1403 '''Is this frame "collect" within the garbage-collector?''' 1404 return self._gdbframe.name() == 'collect' 1405 1406 def get_pyop(self): 1407 try: 1408 f = self._gdbframe.read_var('f') 1409 frame = PyFrameObjectPtr.from_pyobject_ptr(f) 1410 if not frame.is_optimized_out(): 1411 return frame 1412 # gdb is unable to get the "f" argument of PyEval_EvalFrameEx() 1413 # because it was "optimized out". Try to get "f" from the frame 1414 # of the caller, PyEval_EvalCodeEx(). 1415 orig_frame = frame 1416 caller = self._gdbframe.older() 1417 if caller: 1418 f = caller.read_var('f') 1419 frame = PyFrameObjectPtr.from_pyobject_ptr(f) 1420 if not frame.is_optimized_out(): 1421 return frame 1422 return orig_frame 1423 except ValueError: 1424 return None 1425 1426 @classmethod 1427 def get_selected_frame(cls): 1428 _gdbframe = gdb.selected_frame() 1429 if _gdbframe: 1430 return Frame(_gdbframe) 1431 return None 1432 1433 @classmethod 1434 def get_selected_python_frame(cls): 1435 '''Try to obtain the Frame for the python-related code in the selected 1436 frame, or None''' 1437 try: 1438 frame = cls.get_selected_frame() 1439 except gdb.error: 1440 # No frame: Python didn't start yet 1441 return None 1442 1443 while frame: 1444 if frame.is_python_frame(): 1445 return frame 1446 frame = frame.older() 1447 1448 # Not found: 1449 return None 1450 1451 @classmethod 1452 def get_selected_bytecode_frame(cls): 1453 '''Try to obtain the Frame for the python bytecode interpreter in the 1454 selected GDB frame, or None''' 1455 frame = cls.get_selected_frame() 1456 1457 while frame: 1458 if frame.is_evalframeex(): 1459 return frame 1460 frame = frame.older() 1461 1462 # Not found: 1463 return None 1464 1465 def print_summary(self): 1466 if self.is_evalframeex(): 1467 pyop = self.get_pyop() 1468 if pyop: 1469 line = pyop.get_truncated_repr(MAX_OUTPUT_LEN) 1470 write_unicode(sys.stdout, '#%i %s\n' % (self.get_index(), line)) 1471 if not pyop.is_optimized_out(): 1472 line = pyop.current_line() 1473 if line is not None: 1474 sys.stdout.write(' %s\n' % line.strip()) 1475 else: 1476 sys.stdout.write('#%i (unable to read python frame information)\n' % self.get_index()) 1477 else: 1478 info = self.is_other_python_frame() 1479 if info: 1480 sys.stdout.write('#%i %s\n' % (self.get_index(), info)) 1481 else: 1482 sys.stdout.write('#%i\n' % self.get_index()) 1483 1484 def print_traceback(self): 1485 if self.is_evalframeex(): 1486 pyop = self.get_pyop() 1487 if pyop: 1488 pyop.print_traceback() 1489 if not pyop.is_optimized_out(): 1490 line = pyop.current_line() 1491 if line is not None: 1492 sys.stdout.write(' %s\n' % line.strip()) 1493 else: 1494 sys.stdout.write(' (unable to read python frame information)\n') 1495 else: 1496 info = self.is_other_python_frame() 1497 if info: 1498 sys.stdout.write(' %s\n' % info) 1499 else: 1500 sys.stdout.write(' (not a python frame)\n') 1501 1502class PyList(gdb.Command): 1503 '''List the current Python source code, if any 1504 1505 Use 1506 py-list START 1507 to list at a different line number within the python source. 1508 1509 Use 1510 py-list START, END 1511 to list a specific range of lines within the python source. 1512 ''' 1513 1514 def __init__(self): 1515 gdb.Command.__init__ (self, 1516 "py-list", 1517 gdb.COMMAND_FILES, 1518 gdb.COMPLETE_NONE) 1519 1520 1521 def invoke(self, args, from_tty): 1522 import re 1523 1524 start = None 1525 end = None 1526 1527 m = re.match(r'\s*(\d+)\s*', args) 1528 if m: 1529 start = int(m.group(0)) 1530 end = start + 10 1531 1532 m = re.match(r'\s*(\d+)\s*,\s*(\d+)\s*', args) 1533 if m: 1534 start, end = map(int, m.groups()) 1535 1536 # py-list requires an actual PyEval_EvalFrameEx frame: 1537 frame = Frame.get_selected_bytecode_frame() 1538 if not frame: 1539 print('Unable to locate gdb frame for python bytecode interpreter') 1540 return 1541 1542 pyop = frame.get_pyop() 1543 if not pyop or pyop.is_optimized_out(): 1544 print('Unable to read information on python frame') 1545 return 1546 1547 filename = pyop.filename() 1548 lineno = pyop.current_line_num() 1549 1550 if start is None: 1551 start = lineno - 5 1552 end = lineno + 5 1553 1554 if start<1: 1555 start = 1 1556 1557 try: 1558 f = open(filename, 'r') 1559 except IOError as err: 1560 sys.stdout.write('Unable to open %s: %s\n' 1561 % (filename, err)) 1562 return 1563 with f: 1564 all_lines = f.readlines() 1565 # start and end are 1-based, all_lines is 0-based; 1566 # so [start-1:end] as a python slice gives us [start, end] as a 1567 # closed interval 1568 for i, line in enumerate(all_lines[start-1:end]): 1569 linestr = str(i+start) 1570 # Highlight current line: 1571 if i + start == lineno: 1572 linestr = '>' + linestr 1573 sys.stdout.write('%4s %s' % (linestr, line)) 1574 1575 1576# ...and register the command: 1577PyList() 1578 1579def move_in_stack(move_up): 1580 '''Move up or down the stack (for the py-up/py-down command)''' 1581 frame = Frame.get_selected_python_frame() 1582 if not frame: 1583 print('Unable to locate python frame') 1584 return 1585 1586 while frame: 1587 if move_up: 1588 iter_frame = frame.older() 1589 else: 1590 iter_frame = frame.newer() 1591 1592 if not iter_frame: 1593 break 1594 1595 if iter_frame.is_python_frame(): 1596 # Result: 1597 if iter_frame.select(): 1598 iter_frame.print_summary() 1599 return 1600 1601 frame = iter_frame 1602 1603 if move_up: 1604 print('Unable to find an older python frame') 1605 else: 1606 print('Unable to find a newer python frame') 1607 1608class PyUp(gdb.Command): 1609 'Select and print the python stack frame that called this one (if any)' 1610 def __init__(self): 1611 gdb.Command.__init__ (self, 1612 "py-up", 1613 gdb.COMMAND_STACK, 1614 gdb.COMPLETE_NONE) 1615 1616 1617 def invoke(self, args, from_tty): 1618 move_in_stack(move_up=True) 1619 1620class PyDown(gdb.Command): 1621 'Select and print the python stack frame called by this one (if any)' 1622 def __init__(self): 1623 gdb.Command.__init__ (self, 1624 "py-down", 1625 gdb.COMMAND_STACK, 1626 gdb.COMPLETE_NONE) 1627 1628 1629 def invoke(self, args, from_tty): 1630 move_in_stack(move_up=False) 1631 1632# Not all builds of gdb have gdb.Frame.select 1633if hasattr(gdb.Frame, 'select'): 1634 PyUp() 1635 PyDown() 1636 1637class PyBacktraceFull(gdb.Command): 1638 'Display the current python frame and all the frames within its call stack (if any)' 1639 def __init__(self): 1640 gdb.Command.__init__ (self, 1641 "py-bt-full", 1642 gdb.COMMAND_STACK, 1643 gdb.COMPLETE_NONE) 1644 1645 1646 def invoke(self, args, from_tty): 1647 frame = Frame.get_selected_python_frame() 1648 if not frame: 1649 print('Unable to locate python frame') 1650 return 1651 1652 while frame: 1653 if frame.is_python_frame(): 1654 frame.print_summary() 1655 frame = frame.older() 1656 1657PyBacktraceFull() 1658 1659class PyBacktrace(gdb.Command): 1660 'Display the current python frame and all the frames within its call stack (if any)' 1661 def __init__(self): 1662 gdb.Command.__init__ (self, 1663 "py-bt", 1664 gdb.COMMAND_STACK, 1665 gdb.COMPLETE_NONE) 1666 1667 1668 def invoke(self, args, from_tty): 1669 frame = Frame.get_selected_python_frame() 1670 if not frame: 1671 print('Unable to locate python frame') 1672 return 1673 1674 sys.stdout.write('Traceback (most recent call first):\n') 1675 while frame: 1676 if frame.is_python_frame(): 1677 frame.print_traceback() 1678 frame = frame.older() 1679 1680PyBacktrace() 1681 1682class PyPrint(gdb.Command): 1683 'Look up the given python variable name, and print it' 1684 def __init__(self): 1685 gdb.Command.__init__ (self, 1686 "py-print", 1687 gdb.COMMAND_DATA, 1688 gdb.COMPLETE_NONE) 1689 1690 1691 def invoke(self, args, from_tty): 1692 name = str(args) 1693 1694 frame = Frame.get_selected_python_frame() 1695 if not frame: 1696 print('Unable to locate python frame') 1697 return 1698 1699 pyop_frame = frame.get_pyop() 1700 if not pyop_frame: 1701 print('Unable to read information on python frame') 1702 return 1703 1704 pyop_var, scope = pyop_frame.get_var_by_name(name) 1705 1706 if pyop_var: 1707 print('%s %r = %s' 1708 % (scope, 1709 name, 1710 pyop_var.get_truncated_repr(MAX_OUTPUT_LEN))) 1711 else: 1712 print('%r not found' % name) 1713 1714PyPrint() 1715 1716class PyLocals(gdb.Command): 1717 'Look up the given python variable name, and print it' 1718 def __init__(self): 1719 gdb.Command.__init__ (self, 1720 "py-locals", 1721 gdb.COMMAND_DATA, 1722 gdb.COMPLETE_NONE) 1723 1724 1725 def invoke(self, args, from_tty): 1726 name = str(args) 1727 1728 frame = Frame.get_selected_python_frame() 1729 if not frame: 1730 print('Unable to locate python frame') 1731 return 1732 1733 pyop_frame = frame.get_pyop() 1734 if not pyop_frame: 1735 print('Unable to read information on python frame') 1736 return 1737 1738 for pyop_name, pyop_value in pyop_frame.iter_locals(): 1739 print('%s = %s' 1740 % (pyop_name.proxyval(set()), 1741 pyop_value.get_truncated_repr(MAX_OUTPUT_LEN))) 1742 1743PyLocals() 1744