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