• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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