cython

Форк
0
/
libpython.py 
2821 строка · 90.7 Кб
1
#!/usr/bin/python
2

3
# NOTE: Most of this file is taken from the Python source distribution
4
# It can be found under Tools/gdb/libpython.py. It is shipped with Cython
5
# because it's not installed as a python module, and because changes are only
6
# merged into new python versions (v3.2+).
7
# We added some of our code below the "## added, not in CPython" comment.
8

9
'''
10
From gdb 7 onwards, gdb's build can be configured --with-python, allowing gdb
11
to be extended with Python code e.g. for library-specific data visualizations,
12
such as for the C++ STL types.  Documentation on this API can be seen at:
13
http://sourceware.org/gdb/current/onlinedocs/gdb/Python-API.html
14

15

16
This python module deals with the case when the process being debugged (the
17
"inferior process" in gdb parlance) is itself python, or more specifically,
18
linked against libpython.  In this situation, almost every item of data is a
19
(PyObject*), and having the debugger merely print their addresses is not very
20
enlightening.
21

22
This module embeds knowledge about the implementation details of libpython so
23
that we can emit useful visualizations e.g. a string, a list, a dict, a frame
24
giving file/line information and the state of local variables
25

26
In particular, given a gdb.Value corresponding to a PyObject* in the inferior
27
process, we can generate a "proxy value" within the gdb process.  For example,
28
given a PyObject* in the inferior process that is in fact a PyListObject*
29
holding three PyObject* that turn out to be PyBytesObject* instances, we can
30
generate a proxy value within the gdb process that is a list of bytes
31
instances:
32
  [b"foo", b"bar", b"baz"]
33

34
Doing so can be expensive for complicated graphs of objects, and could take
35
some time, so we also have a "write_repr" method that writes a representation
36
of the data to a file-like object.  This allows us to stop the traversal by
37
having the file-like object raise an exception if it gets too much data.
38

39
With both "proxyval" and "write_repr" we keep track of the set of all addresses
40
visited so far in the traversal, to avoid infinite recursion due to cycles in
41
the graph of object references.
42

43
We try to defer gdb.lookup_type() invocations for python types until as late as
44
possible: for a dynamically linked python binary, when the process starts in
45
the debugger, the libpython.so hasn't been dynamically loaded yet, so none of
46
the type names are known to the debugger
47

48
The module also extends gdb with some python-specific commands.
49
'''
50

51
# NOTE: some gdbs are linked with Python 3, so this file should be dual-syntax
52
# compatible (2.6+ and 3.0+).  See #19308.
53

54
import gdb
55
import os
56
import locale
57
import sys
58

59
# Look up the gdb.Type for some standard types:
60
# Those need to be refreshed as types (pointer sizes) may change when
61
# gdb loads different executables
62

63
def _type_char_ptr():
64
    return gdb.lookup_type('char').pointer()  # char*
65

66

67
def _type_unsigned_char_ptr():
68
    return gdb.lookup_type('unsigned char').pointer()  # unsigned char*
69

70

71
def _type_unsigned_short_ptr():
72
    return gdb.lookup_type('unsigned short').pointer()
73

74

75
def _type_unsigned_int_ptr():
76
    return gdb.lookup_type('unsigned int').pointer()
77

78

79
def _sizeof_void_p():
80
    return gdb.lookup_type('void').pointer().sizeof
81

82

83
# value computed later, see PyUnicodeObjectPtr.proxy()
84
_is_pep393 = None
85

86
Py_TPFLAGS_HEAPTYPE = (1 << 9)
87
Py_TPFLAGS_LONG_SUBCLASS     = (1 << 24)
88
Py_TPFLAGS_LIST_SUBCLASS     = (1 << 25)
89
Py_TPFLAGS_TUPLE_SUBCLASS    = (1 << 26)
90
Py_TPFLAGS_BYTES_SUBCLASS    = (1 << 27)
91
Py_TPFLAGS_UNICODE_SUBCLASS  = (1 << 28)
92
Py_TPFLAGS_DICT_SUBCLASS     = (1 << 29)
93
Py_TPFLAGS_BASE_EXC_SUBCLASS = (1 << 30)
94
Py_TPFLAGS_TYPE_SUBCLASS     = (1 << 31)
95

96

97
MAX_OUTPUT_LEN=1024
98

99
hexdigits = "0123456789abcdef"
100

101
ENCODING = locale.getpreferredencoding()
102

103
FRAME_INFO_OPTIMIZED_OUT = '(frame information optimized out)'
104
UNABLE_READ_INFO_PYTHON_FRAME = 'Unable to read information on python frame'
105
EVALFRAME = '_PyEval_EvalFrameDefault'
106

107
class NullPyObjectPtr(RuntimeError):
108
    pass
109

110

111
def safety_limit(val):
112
    # Given an integer value from the process being debugged, limit it to some
113
    # safety threshold so that arbitrary breakage within said process doesn't
114
    # break the gdb process too much (e.g. sizes of iterations, sizes of lists)
115
    return min(val, 1000)
116

117

118
def safe_range(val):
119
    # As per range, but don't trust the value too much: cap it to a safety
120
    # threshold in case the data was corrupted
121
    return range(safety_limit(int(val)))
122

123
def write_unicode(file, text):
124
    file.write(text)
125

126
try:
127
    os_fsencode = os.fsencode
128
except AttributeError:
129
    def os_fsencode(filename):
130
        if not isinstance(filename, unicode):
131
            return filename
132
        encoding = sys.getfilesystemencoding()
133
        if encoding == 'mbcs':
134
            # mbcs doesn't support surrogateescape
135
            return filename.encode(encoding)
136
        encoded = []
137
        for char in filename:
138
            # surrogateescape error handler
139
            if 0xDC80 <= ord(char) <= 0xDCFF:
140
                byte = chr(ord(char) - 0xDC00)
141
            else:
142
                byte = char.encode(encoding)
143
            encoded.append(byte)
144
        return ''.join(encoded)
145

146
class StringTruncated(RuntimeError):
147
    pass
148

149
class TruncatedStringIO:
150
    '''Similar to io.StringIO, but can truncate the output by raising a
151
    StringTruncated exception'''
152
    def __init__(self, maxlen=None):
153
        self._val = ''
154
        self.maxlen = maxlen
155

156
    def write(self, data):
157
        if self.maxlen:
158
            if len(data) + len(self._val) > self.maxlen:
159
                # Truncation:
160
                self._val += data[0:self.maxlen - len(self._val)]
161
                raise StringTruncated()
162

163
        self._val += data
164

165
    def getvalue(self):
166
        return self._val
167

168
class PyObjectPtr:
169
    """
170
    Class wrapping a gdb.Value that's either a (PyObject*) within the
171
    inferior process, or some subclass pointer e.g. (PyBytesObject*)
172

173
    There will be a subclass for every refined PyObject type that we care
174
    about.
175

176
    Note that at every stage the underlying pointer could be NULL, point
177
    to corrupt data, etc; this is the debugger, after all.
178
    """
179
    _typename = 'PyObject'
180

181
    def __init__(self, gdbval, cast_to=None):
182
        if cast_to:
183
            self._gdbval = gdbval.cast(cast_to)
184
        else:
185
            self._gdbval = gdbval
186

187
    def field(self, name):
188
        '''
189
        Get the gdb.Value for the given field within the PyObject, coping with
190
        some python 2 versus python 3 differences.
191

192
        Various libpython types are defined using the "PyObject_HEAD" and
193
        "PyObject_VAR_HEAD" macros.
194

195
        In Python 2, this these are defined so that "ob_type" and (for a var
196
        object) "ob_size" are fields of the type in question.
197

198
        In Python 3, this is defined as an embedded PyVarObject type thus:
199
           PyVarObject ob_base;
200
        so that the "ob_size" field is located insize the "ob_base" field, and
201
        the "ob_type" is most easily accessed by casting back to a (PyObject*).
202
        '''
203
        if self.is_null():
204
            raise NullPyObjectPtr(self)
205

206
        if name == 'ob_type':
207
            pyo_ptr = self._gdbval.cast(PyObjectPtr.get_gdb_type())
208
            return pyo_ptr.dereference()[name]
209

210
        if name == 'ob_size':
211
            pyo_ptr = self._gdbval.cast(PyVarObjectPtr.get_gdb_type())
212
            return pyo_ptr.dereference()[name]
213

214
        # General case: look it up inside the object:
215
        return self._gdbval.dereference()[name]
216

217
    def pyop_field(self, name):
218
        '''
219
        Get a PyObjectPtr for the given PyObject* field within this PyObject,
220
        coping with some python 2 versus python 3 differences.
221
        '''
222
        return PyObjectPtr.from_pyobject_ptr(self.field(name))
223

224
    def write_field_repr(self, name, out, visited):
225
        '''
226
        Extract the PyObject* field named "name", and write its representation
227
        to file-like object "out"
228
        '''
229
        field_obj = self.pyop_field(name)
230
        field_obj.write_repr(out, visited)
231

232
    def get_truncated_repr(self, maxlen):
233
        '''
234
        Get a repr-like string for the data, but truncate it at "maxlen" bytes
235
        (ending the object graph traversal as soon as you do)
236
        '''
237
        out = TruncatedStringIO(maxlen)
238
        try:
239
            self.write_repr(out, set())
240
        except StringTruncated:
241
            # Truncation occurred:
242
            return out.getvalue() + '...(truncated)'
243

244
        # No truncation occurred:
245
        return out.getvalue()
246

247
    def type(self):
248
        return PyTypeObjectPtr(self.field('ob_type'))
249

250
    def is_null(self):
251
        return 0 == int(self._gdbval)
252

253
    def is_optimized_out(self):
254
        '''
255
        Is the value of the underlying PyObject* visible to the debugger?
256

257
        This can vary with the precise version of the compiler used to build
258
        Python, and the precise version of gdb.
259

260
        See e.g. https://bugzilla.redhat.com/show_bug.cgi?id=556975 with
261
        PyEval_EvalFrameEx's "f"
262
        '''
263
        return self._gdbval.is_optimized_out
264

265
    def safe_tp_name(self):
266
        try:
267
            ob_type = self.type()
268
            tp_name = ob_type.field('tp_name')
269
            return tp_name.string()
270
        # NullPyObjectPtr: NULL tp_name?
271
        # RuntimeError: Can't even read the object at all?
272
        # UnicodeDecodeError: Failed to decode tp_name bytestring
273
        except (NullPyObjectPtr, RuntimeError, UnicodeDecodeError):
274
            return 'unknown'
275

276
    def proxyval(self, visited):
277
        '''
278
        Scrape a value from the inferior process, and try to represent it
279
        within the gdb process, whilst (hopefully) avoiding crashes when
280
        the remote data is corrupt.
281

282
        Derived classes will override this.
283

284
        For example, a PyIntObject* with ob_ival 42 in the inferior process
285
        should result in an int(42) in this process.
286

287
        visited: a set of all gdb.Value pyobject pointers already visited
288
        whilst generating this value (to guard against infinite recursion when
289
        visiting object graphs with loops).  Analogous to Py_ReprEnter and
290
        Py_ReprLeave
291
        '''
292

293
        class FakeRepr:
294
            """
295
            Class representing a non-descript PyObject* value in the inferior
296
            process for when we don't have a custom scraper, intended to have
297
            a sane repr().
298
            """
299

300
            def __init__(self, tp_name, address):
301
                self.tp_name = tp_name
302
                self.address = address
303

304
            def __repr__(self):
305
                # For the NULL pointer, we have no way of knowing a type, so
306
                # special-case it as per
307
                # http://bugs.python.org/issue8032#msg100882
308
                if self.address == 0:
309
                    return '0x0'
310
                return '<%s at remote 0x%x>' % (self.tp_name, self.address)
311

312
        return FakeRepr(self.safe_tp_name(),
313
                        int(self._gdbval))
314

315
    def write_repr(self, out, visited):
316
        '''
317
        Write a string representation of the value scraped from the inferior
318
        process to "out", a file-like object.
319
        '''
320
        # Default implementation: generate a proxy value and write its repr
321
        # However, this could involve a lot of work for complicated objects,
322
        # so for derived classes we specialize this
323
        return out.write(repr(self.proxyval(visited)))
324

325
    @classmethod
326
    def subclass_from_type(cls, t):
327
        '''
328
        Given a PyTypeObjectPtr instance wrapping a gdb.Value that's a
329
        (PyTypeObject*), determine the corresponding subclass of PyObjectPtr
330
        to use
331

332
        Ideally, we would look up the symbols for the global types, but that
333
        isn't working yet:
334
          (gdb) python print gdb.lookup_symbol('PyList_Type')[0].value
335
          Traceback (most recent call last):
336
            File "<string>", line 1, in <module>
337
          NotImplementedError: Symbol type not yet supported in Python scripts.
338
          Error while executing Python code.
339

340
        For now, we use tp_flags, after doing some string comparisons on the
341
        tp_name for some special-cases that don't seem to be visible through
342
        flags
343
        '''
344
        try:
345
            tp_name = t.field('tp_name').string()
346
            tp_flags = int(t.field('tp_flags'))
347
        # RuntimeError: NULL pointers
348
        # UnicodeDecodeError: string() fails to decode the bytestring
349
        except (RuntimeError, UnicodeDecodeError):
350
            # Handle any kind of error e.g. NULL ptrs by simply using the base
351
            # class
352
            return cls
353

354
        #print('tp_flags = 0x%08x' % tp_flags)
355
        #print('tp_name = %r' % tp_name)
356

357
        name_map = {'bool': PyBoolObjectPtr,
358
                    'classobj': PyClassObjectPtr,
359
                    'NoneType': PyNoneStructPtr,
360
                    'frame': PyFrameObjectPtr,
361
                    'set' : PySetObjectPtr,
362
                    'frozenset' : PySetObjectPtr,
363
                    'builtin_function_or_method' : PyCFunctionObjectPtr,
364
                    'method-wrapper': wrapperobject,
365
                    }
366
        if tp_name in name_map:
367
            return name_map[tp_name]
368

369
        if tp_flags & Py_TPFLAGS_HEAPTYPE:
370
            return HeapTypeObjectPtr
371

372
        if tp_flags & Py_TPFLAGS_LONG_SUBCLASS:
373
            return PyLongObjectPtr
374
        if tp_flags & Py_TPFLAGS_LIST_SUBCLASS:
375
            return PyListObjectPtr
376
        if tp_flags & Py_TPFLAGS_TUPLE_SUBCLASS:
377
            return PyTupleObjectPtr
378
        if tp_flags & Py_TPFLAGS_BYTES_SUBCLASS:
379
            return PyBytesObjectPtr
380
        if tp_flags & Py_TPFLAGS_UNICODE_SUBCLASS:
381
            return PyUnicodeObjectPtr
382
        if tp_flags & Py_TPFLAGS_DICT_SUBCLASS:
383
            return PyDictObjectPtr
384
        if tp_flags & Py_TPFLAGS_BASE_EXC_SUBCLASS:
385
            return PyBaseExceptionObjectPtr
386
        #if tp_flags & Py_TPFLAGS_TYPE_SUBCLASS:
387
        #    return PyTypeObjectPtr
388

389
        # Use the base class:
390
        return cls
391

392
    @classmethod
393
    def from_pyobject_ptr(cls, gdbval):
394
        '''
395
        Try to locate the appropriate derived class dynamically, and cast
396
        the pointer accordingly.
397
        '''
398
        try:
399
            p = PyObjectPtr(gdbval)
400
            cls = cls.subclass_from_type(p.type())
401
            return cls(gdbval, cast_to=cls.get_gdb_type())
402
        except RuntimeError:
403
            # Handle any kind of error e.g. NULL ptrs by simply using the base
404
            # class
405
            pass
406
        return cls(gdbval)
407

408
    @classmethod
409
    def get_gdb_type(cls):
410
        return gdb.lookup_type(cls._typename).pointer()
411

412
    def as_address(self):
413
        return int(self._gdbval)
414

415
class PyVarObjectPtr(PyObjectPtr):
416
    _typename = 'PyVarObject'
417

418
class ProxyAlreadyVisited:
419
    '''
420
    Placeholder proxy to use when protecting against infinite recursion due to
421
    loops in the object graph.
422

423
    Analogous to the values emitted by the users of Py_ReprEnter and Py_ReprLeave
424
    '''
425
    def __init__(self, rep):
426
        self._rep = rep
427

428
    def __repr__(self):
429
        return self._rep
430

431

432
def _write_instance_repr(out, visited, name, pyop_attrdict, address):
433
    '''Shared code for use by all classes:
434
    write a representation to file-like object "out"'''
435
    out.write('<')
436
    out.write(name)
437

438
    # Write dictionary of instance attributes:
439
    if isinstance(pyop_attrdict, PyDictObjectPtr):
440
        out.write('(')
441
        first = True
442
        for pyop_arg, pyop_val in pyop_attrdict.iteritems():
443
            if not first:
444
                out.write(', ')
445
            first = False
446
            out.write(pyop_arg.proxyval(visited))
447
            out.write('=')
448
            pyop_val.write_repr(out, visited)
449
        out.write(')')
450
    out.write(' at remote 0x%x>' % address)
451

452

453
class InstanceProxy:
454

455
    def __init__(self, cl_name, attrdict, address):
456
        self.cl_name = cl_name
457
        self.attrdict = attrdict
458
        self.address = address
459

460
    def __repr__(self):
461
        if isinstance(self.attrdict, dict):
462
            kwargs = ', '.join(["%s=%r" % (arg, val)
463
                                for arg, val in self.attrdict.iteritems()])
464
            return '<%s(%s) at remote 0x%x>' % (self.cl_name,
465
                                                kwargs, self.address)
466
        else:
467
            return '<%s at remote 0x%x>' % (self.cl_name,
468
                                            self.address)
469

470
def _PyObject_VAR_SIZE(typeobj, nitems):
471
    if _PyObject_VAR_SIZE._type_size_t is None:
472
        _PyObject_VAR_SIZE._type_size_t = gdb.lookup_type('size_t')
473

474
    return ( ( typeobj.field('tp_basicsize') +
475
               nitems * typeobj.field('tp_itemsize') +
476
               (_sizeof_void_p() - 1)
477
             ) & ~(_sizeof_void_p() - 1)
478
           ).cast(_PyObject_VAR_SIZE._type_size_t)
479
_PyObject_VAR_SIZE._type_size_t = None
480

481
class HeapTypeObjectPtr(PyObjectPtr):
482
    _typename = 'PyObject'
483

484
    def get_attr_dict(self):
485
        '''
486
        Get the PyDictObject ptr representing the attribute dictionary
487
        (or None if there's a problem)
488
        '''
489
        try:
490
            typeobj = self.type()
491
            dictoffset = int_from_int(typeobj.field('tp_dictoffset'))
492
            if dictoffset != 0:
493
                if dictoffset < 0:
494
                    type_PyVarObject_ptr = gdb.lookup_type('PyVarObject').pointer()
495
                    tsize = int_from_int(self._gdbval.cast(type_PyVarObject_ptr)['ob_size'])
496
                    if tsize < 0:
497
                        tsize = -tsize
498
                    size = _PyObject_VAR_SIZE(typeobj, tsize)
499
                    dictoffset += size
500
                    assert dictoffset > 0
501
                    assert dictoffset % _sizeof_void_p() == 0
502

503
                dictptr = self._gdbval.cast(_type_char_ptr()) + dictoffset
504
                PyObjectPtrPtr = PyObjectPtr.get_gdb_type().pointer()
505
                dictptr = dictptr.cast(PyObjectPtrPtr)
506
                return PyObjectPtr.from_pyobject_ptr(dictptr.dereference())
507
        except RuntimeError:
508
            # Corrupt data somewhere; fail safe
509
            pass
510

511
        # Not found, or some kind of error:
512
        return None
513

514
    def proxyval(self, visited):
515
        '''
516
        Support for classes.
517

518
        Currently we just locate the dictionary using a transliteration to
519
        python of _PyObject_GetDictPtr, ignoring descriptors
520
        '''
521
        # Guard against infinite loops:
522
        if self.as_address() in visited:
523
            return ProxyAlreadyVisited('<...>')
524
        visited.add(self.as_address())
525

526
        pyop_attr_dict = self.get_attr_dict()
527
        if pyop_attr_dict:
528
            attr_dict = pyop_attr_dict.proxyval(visited)
529
        else:
530
            attr_dict = {}
531
        tp_name = self.safe_tp_name()
532

533
        # Class:
534
        return InstanceProxy(tp_name, attr_dict, int(self._gdbval))
535

536
    def write_repr(self, out, visited):
537
        # Guard against infinite loops:
538
        if self.as_address() in visited:
539
            out.write('<...>')
540
            return
541
        visited.add(self.as_address())
542

543
        pyop_attrdict = self.get_attr_dict()
544
        _write_instance_repr(out, visited,
545
                             self.safe_tp_name(), pyop_attrdict, self.as_address())
546

547
class ProxyException(Exception):
548
    def __init__(self, tp_name, args):
549
        self.tp_name = tp_name
550
        self.args = args
551

552
    def __repr__(self):
553
        return '%s%r' % (self.tp_name, self.args)
554

555
class PyBaseExceptionObjectPtr(PyObjectPtr):
556
    """
557
    Class wrapping a gdb.Value that's a PyBaseExceptionObject* i.e. an exception
558
    within the process being debugged.
559
    """
560
    _typename = 'PyBaseExceptionObject'
561

562
    def proxyval(self, visited):
563
        # Guard against infinite loops:
564
        if self.as_address() in visited:
565
            return ProxyAlreadyVisited('(...)')
566
        visited.add(self.as_address())
567
        arg_proxy = self.pyop_field('args').proxyval(visited)
568
        return ProxyException(self.safe_tp_name(),
569
                              arg_proxy)
570

571
    def write_repr(self, out, visited):
572
        # Guard against infinite loops:
573
        if self.as_address() in visited:
574
            out.write('(...)')
575
            return
576
        visited.add(self.as_address())
577

578
        out.write(self.safe_tp_name())
579
        self.write_field_repr('args', out, visited)
580

581
class PyClassObjectPtr(PyObjectPtr):
582
    """
583
    Class wrapping a gdb.Value that's a PyClassObject* i.e. a <classobj>
584
    instance within the process being debugged.
585
    """
586
    _typename = 'PyClassObject'
587

588

589
class BuiltInFunctionProxy:
590
    def __init__(self, ml_name):
591
        self.ml_name = ml_name
592

593
    def __repr__(self):
594
        return "<built-in function %s>" % self.ml_name
595

596
class BuiltInMethodProxy:
597
    def __init__(self, ml_name, pyop_m_self):
598
        self.ml_name = ml_name
599
        self.pyop_m_self = pyop_m_self
600

601
    def __repr__(self):
602
        return ('<built-in method %s of %s object at remote 0x%x>'
603
                % (self.ml_name,
604
                   self.pyop_m_self.safe_tp_name(),
605
                   self.pyop_m_self.as_address())
606
                )
607

608
class PyCFunctionObjectPtr(PyObjectPtr):
609
    """
610
    Class wrapping a gdb.Value that's a PyCFunctionObject*
611
    (see Include/methodobject.h and Objects/methodobject.c)
612
    """
613
    _typename = 'PyCFunctionObject'
614

615
    def proxyval(self, visited):
616
        m_ml = self.field('m_ml')  # m_ml is a (PyMethodDef*)
617
        try:
618
            ml_name = m_ml['ml_name'].string()
619
        except UnicodeDecodeError:
620
            ml_name = '<ml_name:UnicodeDecodeError>'
621

622
        pyop_m_self = self.pyop_field('m_self')
623
        if pyop_m_self.is_null():
624
            return BuiltInFunctionProxy(ml_name)
625
        else:
626
            return BuiltInMethodProxy(ml_name, pyop_m_self)
627

628

629
class PyCodeObjectPtr(PyObjectPtr):
630
    """
631
    Class wrapping a gdb.Value that's a PyCodeObject* i.e. a <code> instance
632
    within the process being debugged.
633
    """
634
    _typename = 'PyCodeObject'
635

636
    def addr2line(self, addrq):
637
        '''
638
        Get the line number for a given bytecode offset
639

640
        Analogous to PyCode_Addr2Line; translated from pseudocode in
641
        Objects/lnotab_notes.txt
642
        '''
643
        co_lnotab = self.pyop_field('co_lnotab').proxyval(set())
644

645
        # Initialize lineno to co_firstlineno as per PyCode_Addr2Line
646
        # not 0, as lnotab_notes.txt has it:
647
        lineno = int_from_int(self.field('co_firstlineno'))
648

649
        addr = 0
650
        for addr_incr, line_incr in zip(co_lnotab[::2], co_lnotab[1::2]):
651
            addr += ord(addr_incr)
652
            if addr > addrq:
653
                return lineno
654
            lineno += ord(line_incr)
655
        return lineno
656

657

658
class PyDictObjectPtr(PyObjectPtr):
659
    """
660
    Class wrapping a gdb.Value that's a PyDictObject* i.e. a dict instance
661
    within the process being debugged.
662
    """
663
    _typename = 'PyDictObject'
664

665
    def iteritems(self):
666
        '''
667
        Yields a sequence of (PyObjectPtr key, PyObjectPtr value) pairs,
668
        analogous to dict.iteritems()
669
        '''
670
        keys = self.field('ma_keys')
671
        values = self.field('ma_values')
672
        entries, nentries = self._get_entries(keys)
673
        for i in safe_range(nentries):
674
            ep = entries[i]
675
            if int(values):
676
                pyop_value = PyObjectPtr.from_pyobject_ptr(values[i])
677
            else:
678
                pyop_value = PyObjectPtr.from_pyobject_ptr(ep['me_value'])
679
            if not pyop_value.is_null():
680
                pyop_key = PyObjectPtr.from_pyobject_ptr(ep['me_key'])
681
                yield (pyop_key, pyop_value)
682

683
    def proxyval(self, visited):
684
        # Guard against infinite loops:
685
        if self.as_address() in visited:
686
            return ProxyAlreadyVisited('{...}')
687
        visited.add(self.as_address())
688

689
        result = {}
690
        for pyop_key, pyop_value in self.iteritems():
691
            proxy_key = pyop_key.proxyval(visited)
692
            proxy_value = pyop_value.proxyval(visited)
693
            result[proxy_key] = proxy_value
694
        return result
695

696
    def write_repr(self, out, visited):
697
        # Guard against infinite loops:
698
        if self.as_address() in visited:
699
            out.write('{...}')
700
            return
701
        visited.add(self.as_address())
702

703
        out.write('{')
704
        first = True
705
        for pyop_key, pyop_value in self.iteritems():
706
            if not first:
707
                out.write(', ')
708
            first = False
709
            pyop_key.write_repr(out, visited)
710
            out.write(': ')
711
            pyop_value.write_repr(out, visited)
712
        out.write('}')
713

714
    def _get_entries(self, keys):
715
        dk_nentries = int(keys['dk_nentries'])
716
        dk_size = int(keys['dk_size'])
717
        try:
718
            # <= Python 3.5
719
            return keys['dk_entries'], dk_size
720
        except RuntimeError:
721
            # >= Python 3.6
722
            pass
723

724
        if dk_size <= 0xFF:
725
            offset = dk_size
726
        elif dk_size <= 0xFFFF:
727
            offset = 2 * dk_size
728
        elif dk_size <= 0xFFFFFFFF:
729
            offset = 4 * dk_size
730
        else:
731
            offset = 8 * dk_size
732

733
        ent_addr = keys['dk_indices'].address
734
        ent_addr = ent_addr.cast(_type_unsigned_char_ptr()) + offset
735
        ent_ptr_t = gdb.lookup_type('PyDictKeyEntry').pointer()
736
        ent_addr = ent_addr.cast(ent_ptr_t)
737

738
        return ent_addr, dk_nentries
739

740

741
class PyListObjectPtr(PyObjectPtr):
742
    _typename = 'PyListObject'
743

744
    def __getitem__(self, i):
745
        # Get the gdb.Value for the (PyObject*) with the given index:
746
        field_ob_item = self.field('ob_item')
747
        return field_ob_item[i]
748

749
    def proxyval(self, visited):
750
        # Guard against infinite loops:
751
        if self.as_address() in visited:
752
            return ProxyAlreadyVisited('[...]')
753
        visited.add(self.as_address())
754

755
        result = [PyObjectPtr.from_pyobject_ptr(self[i]).proxyval(visited)
756
                  for i in safe_range(int_from_int(self.field('ob_size')))]
757
        return result
758

759
    def write_repr(self, out, visited):
760
        # Guard against infinite loops:
761
        if self.as_address() in visited:
762
            out.write('[...]')
763
            return
764
        visited.add(self.as_address())
765

766
        out.write('[')
767
        for i in safe_range(int_from_int(self.field('ob_size'))):
768
            if i > 0:
769
                out.write(', ')
770
            element = PyObjectPtr.from_pyobject_ptr(self[i])
771
            element.write_repr(out, visited)
772
        out.write(']')
773

774
class PyLongObjectPtr(PyObjectPtr):
775
    _typename = 'PyLongObject'
776

777
    def proxyval(self, visited):
778
        '''
779
        Python's Include/longobjrep.h has this declaration:
780
           struct _longobject {
781
               PyObject_VAR_HEAD
782
               digit ob_digit[1];
783
           };
784

785
        with this description:
786
            The absolute value of a number is equal to
787
                 SUM(for i=0 through abs(ob_size)-1) ob_digit[i] * 2**(SHIFT*i)
788
            Negative numbers are represented with ob_size < 0;
789
            zero is represented by ob_size == 0.
790

791
        where SHIFT can be either:
792
            #define PyLong_SHIFT        30
793
            #define PyLong_SHIFT        15
794
        '''
795
        ob_size = int(self.field('ob_size'))
796
        if ob_size == 0:
797
            return 0
798

799
        ob_digit = self.field('ob_digit')
800

801
        if gdb.lookup_type('digit').sizeof == 2:
802
            SHIFT = 15
803
        else:
804
            SHIFT = 30
805

806
        digits = [int(ob_digit[i]) * 2**(SHIFT*i)
807
                  for i in safe_range(abs(ob_size))]
808
        result = sum(digits)
809
        if ob_size < 0:
810
            result = -result
811
        return result
812

813
    def write_repr(self, out, visited):
814
        # Write this out as a Python 3 int literal, i.e. without the "L" suffix
815
        proxy = self.proxyval(visited)
816
        out.write("%s" % proxy)
817

818

819
class PyBoolObjectPtr(PyLongObjectPtr):
820
    """
821
    Class wrapping a gdb.Value that's a PyBoolObject* i.e. one of the two
822
    <bool> instances (Py_True/Py_False) within the process being debugged.
823
    """
824
    def proxyval(self, visited):
825
        if PyLongObjectPtr.proxyval(self, visited):
826
            return True
827
        else:
828
            return False
829

830
class PyNoneStructPtr(PyObjectPtr):
831
    """
832
    Class wrapping a gdb.Value that's a PyObject* pointing to the
833
    singleton (we hope) _Py_NoneStruct with ob_type PyNone_Type
834
    """
835
    _typename = 'PyObject'
836

837
    def proxyval(self, visited):
838
        return None
839

840

841
class PyFrameObjectPtr(PyObjectPtr):
842
    _typename = 'PyFrameObject'
843

844
    def __init__(self, gdbval, cast_to=None):
845
        PyObjectPtr.__init__(self, gdbval, cast_to)
846

847
        if not self.is_optimized_out():
848
            self.co = PyCodeObjectPtr.from_pyobject_ptr(self.field('f_code'))
849
            self.co_name = self.co.pyop_field('co_name')
850
            self.co_filename = self.co.pyop_field('co_filename')
851

852
            self.f_lineno = int_from_int(self.field('f_lineno'))
853
            self.f_lasti = int_from_int(self.field('f_lasti'))
854
            self.co_nlocals = int_from_int(self.co.field('co_nlocals'))
855
            self.co_varnames = PyTupleObjectPtr.from_pyobject_ptr(self.co.field('co_varnames'))
856

857
    def iter_locals(self):
858
        '''
859
        Yield a sequence of (name,value) pairs of PyObjectPtr instances, for
860
        the local variables of this frame
861
        '''
862
        if self.is_optimized_out():
863
            return
864

865
        f_localsplus = self.field('f_localsplus')
866
        for i in safe_range(self.co_nlocals):
867
            pyop_value = PyObjectPtr.from_pyobject_ptr(f_localsplus[i])
868
            if not pyop_value.is_null():
869
                pyop_name = PyObjectPtr.from_pyobject_ptr(self.co_varnames[i])
870
                yield (pyop_name, pyop_value)
871

872
    def iter_globals(self):
873
        '''
874
        Yield a sequence of (name,value) pairs of PyObjectPtr instances, for
875
        the global variables of this frame
876
        '''
877
        if self.is_optimized_out():
878
            return ()
879

880
        pyop_globals = self.pyop_field('f_globals')
881
        return pyop_globals.iteritems()
882

883
    def iter_builtins(self):
884
        '''
885
        Yield a sequence of (name,value) pairs of PyObjectPtr instances, for
886
        the builtin variables
887
        '''
888
        if self.is_optimized_out():
889
            return ()
890

891
        pyop_builtins = self.pyop_field('f_builtins')
892
        return pyop_builtins.iteritems()
893

894
    def get_var_by_name(self, name):
895
        '''
896
        Look for the named local variable, returning a (PyObjectPtr, scope) pair
897
        where scope is a string 'local', 'global', 'builtin'
898

899
        If not found, return (None, None)
900
        '''
901
        for pyop_name, pyop_value in self.iter_locals():
902
            if name == pyop_name.proxyval(set()):
903
                return pyop_value, 'local'
904
        for pyop_name, pyop_value in self.iter_globals():
905
            if name == pyop_name.proxyval(set()):
906
                return pyop_value, 'global'
907
        for pyop_name, pyop_value in self.iter_builtins():
908
            if name == pyop_name.proxyval(set()):
909
                return pyop_value, 'builtin'
910
        return None, None
911

912
    def filename(self):
913
        '''Get the path of the current Python source file, as a string'''
914
        if self.is_optimized_out():
915
            return FRAME_INFO_OPTIMIZED_OUT
916
        return self.co_filename.proxyval(set())
917

918
    def current_line_num(self):
919
        '''Get current line number as an integer (1-based)
920

921
        Translated from PyFrame_GetLineNumber and PyCode_Addr2Line
922

923
        See Objects/lnotab_notes.txt
924
        '''
925
        if self.is_optimized_out():
926
            return None
927
        f_trace = self.field('f_trace')
928
        if int(f_trace) != 0:
929
            # we have a non-NULL f_trace:
930
            return self.f_lineno
931

932
        try:
933
            return self.co.addr2line(self.f_lasti)
934
        except Exception:
935
            # bpo-34989: addr2line() is a complex function, it can fail in many
936
            # ways. For example, it fails with a TypeError on "FakeRepr" if
937
            # gdb fails to load debug symbols. Use a catch-all "except
938
            # Exception" to make the whole function safe. The caller has to
939
            # handle None anyway for optimized Python.
940
            return None
941

942
    def current_line(self):
943
        '''Get the text of the current source line as a string, with a trailing
944
        newline character'''
945
        if self.is_optimized_out():
946
            return FRAME_INFO_OPTIMIZED_OUT
947

948
        lineno = self.current_line_num()
949
        if lineno is None:
950
            return '(failed to get frame line number)'
951

952
        filename = self.filename()
953
        try:
954
            with open(os_fsencode(filename)) as fp:
955
                lines = fp.readlines()
956
        except OSError:
957
            return None
958

959
        try:
960
            # Convert from 1-based current_line_num to 0-based list offset
961
            return lines[lineno - 1]
962
        except IndexError:
963
            return None
964

965
    def write_repr(self, out, visited):
966
        if self.is_optimized_out():
967
            out.write(FRAME_INFO_OPTIMIZED_OUT)
968
            return
969
        lineno = self.current_line_num()
970
        lineno = str(lineno) if lineno is not None else "?"
971
        out.write('Frame 0x%x, for file %s, line %s, in %s ('
972
                  % (self.as_address(),
973
                     self.co_filename.proxyval(visited),
974
                     lineno,
975
                     self.co_name.proxyval(visited)))
976
        first = True
977
        for pyop_name, pyop_value in self.iter_locals():
978
            if not first:
979
                out.write(', ')
980
            first = False
981

982
            out.write(pyop_name.proxyval(visited))
983
            out.write('=')
984
            pyop_value.write_repr(out, visited)
985

986
        out.write(')')
987

988
    def print_traceback(self):
989
        if self.is_optimized_out():
990
            sys.stdout.write('  %s\n' % FRAME_INFO_OPTIMIZED_OUT)
991
            return
992
        visited = set()
993
        lineno = self.current_line_num()
994
        lineno = str(lineno) if lineno is not None else "?"
995
        sys.stdout.write('  File "%s", line %s, in %s\n'
996
                  % (self.co_filename.proxyval(visited),
997
                     lineno,
998
                     self.co_name.proxyval(visited)))
999

1000
class PySetObjectPtr(PyObjectPtr):
1001
    _typename = 'PySetObject'
1002

1003
    @classmethod
1004
    def _dummy_key(self):
1005
        return gdb.lookup_global_symbol('_PySet_Dummy').value()
1006

1007
    def __iter__(self):
1008
        dummy_ptr = self._dummy_key()
1009
        table = self.field('table')
1010
        for i in safe_range(self.field('mask') + 1):
1011
            setentry = table[i]
1012
            key = setentry['key']
1013
            if key != 0 and key != dummy_ptr:
1014
                yield PyObjectPtr.from_pyobject_ptr(key)
1015

1016
    def proxyval(self, visited):
1017
        # Guard against infinite loops:
1018
        if self.as_address() in visited:
1019
            return ProxyAlreadyVisited('%s(...)' % self.safe_tp_name())
1020
        visited.add(self.as_address())
1021

1022
        members = (key.proxyval(visited) for key in self)
1023
        if self.safe_tp_name() == 'frozenset':
1024
            return frozenset(members)
1025
        else:
1026
            return set(members)
1027

1028
    def write_repr(self, out, visited):
1029
        # Emulate Python 3's set_repr
1030
        tp_name = self.safe_tp_name()
1031

1032
        # Guard against infinite loops:
1033
        if self.as_address() in visited:
1034
            out.write('(...)')
1035
            return
1036
        visited.add(self.as_address())
1037

1038
        # Python 3's set_repr special-cases the empty set:
1039
        if not self.field('used'):
1040
            out.write(tp_name)
1041
            out.write('()')
1042
            return
1043

1044
        # Python 3 uses {} for set literals:
1045
        if tp_name != 'set':
1046
            out.write(tp_name)
1047
            out.write('(')
1048

1049
        out.write('{')
1050
        first = True
1051
        for key in self:
1052
            if not first:
1053
                out.write(', ')
1054
            first = False
1055
            key.write_repr(out, visited)
1056
        out.write('}')
1057

1058
        if tp_name != 'set':
1059
            out.write(')')
1060

1061

1062
class PyBytesObjectPtr(PyObjectPtr):
1063
    _typename = 'PyBytesObject'
1064

1065
    def __str__(self):
1066
        field_ob_size = self.field('ob_size')
1067
        field_ob_sval = self.field('ob_sval')
1068
        char_ptr = field_ob_sval.address.cast(_type_unsigned_char_ptr())
1069
        return ''.join([chr(char_ptr[i]) for i in safe_range(field_ob_size)])
1070

1071
    def proxyval(self, visited):
1072
        return str(self)
1073

1074
    def write_repr(self, out, visited):
1075
        # Write this out as a Python 3 bytes literal, i.e. with a "b" prefix
1076

1077
        # Get a PyStringObject* within the Python 2 gdb process:
1078
        proxy = self.proxyval(visited)
1079

1080
        # Transliteration of Python 3's Objects/bytesobject.c:PyBytes_Repr
1081
        # to Python 2 code:
1082
        quote = "'"
1083
        if "'" in proxy and not '"' in proxy:
1084
            quote = '"'
1085
        out.write('b')
1086
        out.write(quote)
1087
        for byte in proxy:
1088
            if byte == quote or byte == '\\':
1089
                out.write('\\')
1090
                out.write(byte)
1091
            elif byte == '\t':
1092
                out.write('\\t')
1093
            elif byte == '\n':
1094
                out.write('\\n')
1095
            elif byte == '\r':
1096
                out.write('\\r')
1097
            elif byte < ' ' or ord(byte) >= 0x7f:
1098
                out.write('\\x')
1099
                out.write(hexdigits[(ord(byte) & 0xf0) >> 4])
1100
                out.write(hexdigits[ord(byte) & 0xf])
1101
            else:
1102
                out.write(byte)
1103
        out.write(quote)
1104

1105
class PyTupleObjectPtr(PyObjectPtr):
1106
    _typename = 'PyTupleObject'
1107

1108
    def __getitem__(self, i):
1109
        # Get the gdb.Value for the (PyObject*) with the given index:
1110
        field_ob_item = self.field('ob_item')
1111
        return field_ob_item[i]
1112

1113
    def proxyval(self, visited):
1114
        # Guard against infinite loops:
1115
        if self.as_address() in visited:
1116
            return ProxyAlreadyVisited('(...)')
1117
        visited.add(self.as_address())
1118

1119
        result = tuple(PyObjectPtr.from_pyobject_ptr(self[i]).proxyval(visited)
1120
                       for i in safe_range(int_from_int(self.field('ob_size'))))
1121
        return result
1122

1123
    def write_repr(self, out, visited):
1124
        # Guard against infinite loops:
1125
        if self.as_address() in visited:
1126
            out.write('(...)')
1127
            return
1128
        visited.add(self.as_address())
1129

1130
        out.write('(')
1131
        for i in safe_range(int_from_int(self.field('ob_size'))):
1132
            if i > 0:
1133
                out.write(', ')
1134
            element = PyObjectPtr.from_pyobject_ptr(self[i])
1135
            element.write_repr(out, visited)
1136
        if self.field('ob_size') == 1:
1137
            out.write(',)')
1138
        else:
1139
            out.write(')')
1140

1141
class PyTypeObjectPtr(PyObjectPtr):
1142
    _typename = 'PyTypeObject'
1143

1144

1145
def _unichr_is_printable(char):
1146
    # Logic adapted from Python 3's Tools/unicode/makeunicodedata.py
1147
    if char == " ":
1148
        return True
1149
    import unicodedata
1150
    return unicodedata.category(char) not in ("C", "Z")
1151

1152

1153
class PyUnicodeObjectPtr(PyObjectPtr):
1154
    _typename = 'PyUnicodeObject'
1155

1156
    def char_width(self):
1157
        _type_Py_UNICODE = gdb.lookup_type('Py_UNICODE')
1158
        return _type_Py_UNICODE.sizeof
1159

1160
    def proxyval(self, visited):
1161
        global _is_pep393
1162
        if _is_pep393 is None:
1163
            fields = gdb.lookup_type('PyUnicodeObject').fields()
1164
            _is_pep393 = 'data' in [f.name for f in fields]
1165
        if _is_pep393:
1166
            # Python 3.3 and newer
1167
            may_have_surrogates = False
1168
            compact = self.field('_base')
1169
            ascii = compact['_base']
1170
            state = ascii['state']
1171
            is_compact_ascii = (int(state['ascii']) and int(state['compact']))
1172
            if not int(state['ready']):
1173
                # string is not ready
1174
                field_length = int(compact['wstr_length'])
1175
                may_have_surrogates = True
1176
                field_str = ascii['wstr']
1177
            else:
1178
                field_length = int(ascii['length'])
1179
                if is_compact_ascii:
1180
                    field_str = ascii.address + 1
1181
                elif int(state['compact']):
1182
                    field_str = compact.address + 1
1183
                else:
1184
                    field_str = self.field('data')['any']
1185
                repr_kind = int(state['kind'])
1186
                if repr_kind == 1:
1187
                    field_str = field_str.cast(_type_unsigned_char_ptr())
1188
                elif repr_kind == 2:
1189
                    field_str = field_str.cast(_type_unsigned_short_ptr())
1190
                elif repr_kind == 4:
1191
                    field_str = field_str.cast(_type_unsigned_int_ptr())
1192
        else:
1193
            # Python 3.2 and earlier
1194
            field_length = int(self.field('length'))
1195
            field_str = self.field('str')
1196
            may_have_surrogates = self.char_width() == 2
1197

1198
        # Gather a list of ints from the Py_UNICODE array; these are either
1199
        # UCS-1, UCS-2 or UCS-4 code points:
1200
        if not may_have_surrogates:
1201
            Py_UNICODEs = [int(field_str[i]) for i in safe_range(field_length)]
1202
        else:
1203
            # A more elaborate routine if sizeof(Py_UNICODE) is 2 in the
1204
            # inferior process: we must join surrogate pairs.
1205
            Py_UNICODEs = []
1206
            i = 0
1207
            limit = safety_limit(field_length)
1208
            while i < limit:
1209
                ucs = int(field_str[i])
1210
                i += 1
1211
                if ucs < 0xD800 or ucs >= 0xDC00 or i == field_length:
1212
                    Py_UNICODEs.append(ucs)
1213
                    continue
1214
                # This could be a surrogate pair.
1215
                ucs2 = int(field_str[i])
1216
                if ucs2 < 0xDC00 or ucs2 > 0xDFFF:
1217
                    continue
1218
                code = (ucs & 0x03FF) << 10
1219
                code |= ucs2 & 0x03FF
1220
                code += 0x00010000
1221
                Py_UNICODEs.append(code)
1222
                i += 1
1223

1224
        # Convert the int code points to unicode characters, and generate a
1225
        # local unicode instance.
1226
        # This splits surrogate pairs if sizeof(Py_UNICODE) is 2 here (in gdb).
1227
        result = ''.join([
1228
            (chr(ucs) if ucs <= 0x10ffff else '\ufffd')
1229
            for ucs in Py_UNICODEs])
1230
        return result
1231

1232
    def write_repr(self, out, visited):
1233
        # Write this out as a Python 3 str literal, i.e. without a "u" prefix
1234

1235
        # Get a PyUnicodeObject* within the Python 2 gdb process:
1236
        proxy = self.proxyval(visited)
1237

1238
        # Transliteration of Python 3's Object/unicodeobject.c:unicode_repr
1239
        # to Python 2:
1240
        if "'" in proxy and '"' not in proxy:
1241
            quote = '"'
1242
        else:
1243
            quote = "'"
1244
        out.write(quote)
1245

1246
        i = 0
1247
        while i < len(proxy):
1248
            ch = proxy[i]
1249
            i += 1
1250

1251
            # Escape quotes and backslashes
1252
            if ch == quote or ch == '\\':
1253
                out.write('\\')
1254
                out.write(ch)
1255

1256
            #  Map special whitespace to '\t', \n', '\r'
1257
            elif ch == '\t':
1258
                out.write('\\t')
1259
            elif ch == '\n':
1260
                out.write('\\n')
1261
            elif ch == '\r':
1262
                out.write('\\r')
1263

1264
            # Map non-printable US ASCII to '\xhh' */
1265
            elif ch < ' ' or ch == 0x7F:
1266
                out.write('\\x')
1267
                out.write(hexdigits[(ord(ch) >> 4) & 0x000F])
1268
                out.write(hexdigits[ord(ch) & 0x000F])
1269

1270
            # Copy ASCII characters as-is
1271
            elif ord(ch) < 0x7F:
1272
                out.write(ch)
1273

1274
            # Non-ASCII characters
1275
            else:
1276
                ucs = ch
1277
                ch2 = None
1278
                if sys.maxunicode < 0x10000:
1279
                    # If sizeof(Py_UNICODE) is 2 here (in gdb), join
1280
                    # surrogate pairs before calling _unichr_is_printable.
1281
                    if (i < len(proxy)
1282
                            and 0xD800 <= ord(ch) < 0xDC00
1283
                            and 0xDC00 <= ord(proxy[i]) <= 0xDFFF):
1284
                        ch2 = proxy[i]
1285
                        ucs = ch + ch2
1286
                        i += 1
1287

1288
                # Unfortunately, Python 2's unicode type doesn't seem
1289
                # to expose the "isprintable" method
1290
                printable = _unichr_is_printable(ucs)
1291
                if printable:
1292
                    try:
1293
                        ucs.encode(ENCODING)
1294
                    except UnicodeEncodeError:
1295
                        printable = False
1296

1297
                # Map Unicode whitespace and control characters
1298
                # (categories Z* and C* except ASCII space)
1299
                if not printable:
1300
                    if ch2 is not None:
1301
                        # Match Python 3's representation of non-printable
1302
                        # wide characters.
1303
                        code = (ord(ch) & 0x03FF) << 10
1304
                        code |= ord(ch2) & 0x03FF
1305
                        code += 0x00010000
1306
                    else:
1307
                        code = ord(ucs)
1308

1309
                    # Map 8-bit characters to '\\xhh'
1310
                    if code <= 0xff:
1311
                        out.write('\\x')
1312
                        out.write(hexdigits[(code >> 4) & 0x000F])
1313
                        out.write(hexdigits[code & 0x000F])
1314
                    # Map 21-bit characters to '\U00xxxxxx'
1315
                    elif code >= 0x10000:
1316
                        out.write('\\U')
1317
                        out.write(hexdigits[(code >> 28) & 0x0000000F])
1318
                        out.write(hexdigits[(code >> 24) & 0x0000000F])
1319
                        out.write(hexdigits[(code >> 20) & 0x0000000F])
1320
                        out.write(hexdigits[(code >> 16) & 0x0000000F])
1321
                        out.write(hexdigits[(code >> 12) & 0x0000000F])
1322
                        out.write(hexdigits[(code >> 8) & 0x0000000F])
1323
                        out.write(hexdigits[(code >> 4) & 0x0000000F])
1324
                        out.write(hexdigits[code & 0x0000000F])
1325
                    # Map 16-bit characters to '\uxxxx'
1326
                    else:
1327
                        out.write('\\u')
1328
                        out.write(hexdigits[(code >> 12) & 0x000F])
1329
                        out.write(hexdigits[(code >> 8) & 0x000F])
1330
                        out.write(hexdigits[(code >> 4) & 0x000F])
1331
                        out.write(hexdigits[code & 0x000F])
1332
                else:
1333
                    # Copy characters as-is
1334
                    out.write(ch)
1335
                    if ch2 is not None:
1336
                        out.write(ch2)
1337

1338
        out.write(quote)
1339

1340

1341
class wrapperobject(PyObjectPtr):
1342
    _typename = 'wrapperobject'
1343

1344
    def safe_name(self):
1345
        try:
1346
            name = self.field('descr')['d_base']['name'].string()
1347
            return repr(name)
1348
        except (NullPyObjectPtr, RuntimeError, UnicodeDecodeError):
1349
            return '<unknown name>'
1350

1351
    def safe_tp_name(self):
1352
        try:
1353
            return self.field('self')['ob_type']['tp_name'].string()
1354
        except (NullPyObjectPtr, RuntimeError, UnicodeDecodeError):
1355
            return '<unknown tp_name>'
1356

1357
    def safe_self_addresss(self):
1358
        try:
1359
            address = int(self.field('self'))
1360
            return '%#x' % address
1361
        except (NullPyObjectPtr, RuntimeError):
1362
            return '<failed to get self address>'
1363

1364
    def proxyval(self, visited):
1365
        name = self.safe_name()
1366
        tp_name = self.safe_tp_name()
1367
        self_address = self.safe_self_addresss()
1368
        return ("<method-wrapper %s of %s object at %s>"
1369
                % (name, tp_name, self_address))
1370

1371
    def write_repr(self, out, visited):
1372
        proxy = self.proxyval(visited)
1373
        out.write(proxy)
1374

1375

1376
def int_from_int(gdbval):
1377
    return int(gdbval)
1378

1379

1380
def stringify(val):
1381
    # TODO: repr() puts everything on one line; pformat can be nicer, but
1382
    # can lead to v.long results; this function isolates the choice
1383
    if True:
1384
        return repr(val)
1385
    else:
1386
        from pprint import pformat
1387
        return pformat(val)
1388

1389

1390
class PyObjectPtrPrinter:
1391
    "Prints a (PyObject*)"
1392

1393
    def __init__ (self, gdbval):
1394
        self.gdbval = gdbval
1395

1396
    def to_string (self):
1397
        pyop = PyObjectPtr.from_pyobject_ptr(self.gdbval)
1398
        if True:
1399
            return pyop.get_truncated_repr(MAX_OUTPUT_LEN)
1400
        else:
1401
            # Generate full proxy value then stringify it.
1402
            # Doing so could be expensive
1403
            proxyval = pyop.proxyval(set())
1404
            return stringify(proxyval)
1405

1406
def pretty_printer_lookup(gdbval):
1407
    type = gdbval.type.unqualified()
1408
    if type.code != gdb.TYPE_CODE_PTR:
1409
        return None
1410

1411
    type = type.target().unqualified()
1412
    t = str(type)
1413
    if t in ("PyObject", "PyFrameObject", "PyUnicodeObject", "wrapperobject"):
1414
        return PyObjectPtrPrinter(gdbval)
1415

1416
"""
1417
During development, I've been manually invoking the code in this way:
1418
(gdb) python
1419

1420
import sys
1421
sys.path.append('/home/david/coding/python-gdb')
1422
import libpython
1423
end
1424

1425
then reloading it after each edit like this:
1426
(gdb) python reload(libpython)
1427

1428
The following code should ensure that the prettyprinter is registered
1429
if the code is autoloaded by gdb when visiting libpython.so, provided
1430
that this python file is installed to the same path as the library (or its
1431
.debug file) plus a "-gdb.py" suffix, e.g:
1432
  /usr/lib/libpython2.6.so.1.0-gdb.py
1433
  /usr/lib/debug/usr/lib/libpython2.6.so.1.0.debug-gdb.py
1434
"""
1435
def register (obj):
1436
    if obj is None:
1437
        obj = gdb
1438

1439
    # Wire up the pretty-printer
1440
    obj.pretty_printers.append(pretty_printer_lookup)
1441

1442
register (gdb.current_objfile ())
1443

1444

1445

1446
# Unfortunately, the exact API exposed by the gdb module varies somewhat
1447
# from build to build
1448
# See http://bugs.python.org/issue8279?#msg102276
1449

1450
class Frame:
1451
    '''
1452
    Wrapper for gdb.Frame, adding various methods
1453
    '''
1454
    def __init__(self, gdbframe):
1455
        self._gdbframe = gdbframe
1456

1457
    def older(self):
1458
        older = self._gdbframe.older()
1459
        if older:
1460
            return Frame(older)
1461
        else:
1462
            return None
1463

1464
    def newer(self):
1465
        newer = self._gdbframe.newer()
1466
        if newer:
1467
            return Frame(newer)
1468
        else:
1469
            return None
1470

1471
    def select(self):
1472
        '''If supported, select this frame and return True; return False if unsupported
1473

1474
        Not all builds have a gdb.Frame.select method; seems to be present on Fedora 12
1475
        onwards, but absent on Ubuntu buildbot'''
1476
        if not hasattr(self._gdbframe, 'select'):
1477
            print ('Unable to select frame: '
1478
                   'this build of gdb does not expose a gdb.Frame.select method')
1479
            return False
1480
        self._gdbframe.select()
1481
        return True
1482

1483
    def get_index(self):
1484
        '''Calculate index of frame, starting at 0 for the newest frame within
1485
        this thread'''
1486
        index = 0
1487
        # Go down until you reach the newest frame:
1488
        iter_frame = self
1489
        while iter_frame.newer():
1490
            index += 1
1491
            iter_frame = iter_frame.newer()
1492
        return index
1493

1494
    # We divide frames into:
1495
    #   - "python frames":
1496
    #       - "bytecode frames" i.e. PyEval_EvalFrameEx
1497
    #       - "other python frames": things that are of interest from a python
1498
    #         POV, but aren't bytecode (e.g. GC, GIL)
1499
    #   - everything else
1500

1501
    def is_python_frame(self):
1502
        '''Is this a _PyEval_EvalFrameDefault frame, or some other important
1503
        frame? (see is_other_python_frame for what "important" means in this
1504
        context)'''
1505
        if self.is_evalframe():
1506
            return True
1507
        if self.is_other_python_frame():
1508
            return True
1509
        return False
1510

1511
    def is_evalframe(self):
1512
        '''Is this a _PyEval_EvalFrameDefault frame?'''
1513
        if self._gdbframe.name() == EVALFRAME:
1514
            '''
1515
            I believe we also need to filter on the inline
1516
            struct frame_id.inline_depth, only regarding frames with
1517
            an inline depth of 0 as actually being this function
1518

1519
            So we reject those with type gdb.INLINE_FRAME
1520
            '''
1521
            if self._gdbframe.type() == gdb.NORMAL_FRAME:
1522
                # We have a _PyEval_EvalFrameDefault frame:
1523
                return True
1524

1525
        return False
1526

1527
    def is_other_python_frame(self):
1528
        '''Is this frame worth displaying in python backtraces?
1529
        Examples:
1530
          - waiting on the GIL
1531
          - garbage-collecting
1532
          - within a CFunction
1533
         If it is, return a descriptive string
1534
         For other frames, return False
1535
         '''
1536
        if self.is_waiting_for_gil():
1537
            return 'Waiting for the GIL'
1538

1539
        if self.is_gc_collect():
1540
            return 'Garbage-collecting'
1541

1542
        # Detect invocations of PyCFunction instances:
1543
        frame = self._gdbframe
1544
        caller = frame.name()
1545
        if not caller:
1546
            return False
1547

1548
        if (caller.startswith('cfunction_vectorcall_') or
1549
            caller == 'cfunction_call'):
1550
            arg_name = 'func'
1551
            # Within that frame:
1552
            #   "func" is the local containing the PyObject* of the
1553
            # PyCFunctionObject instance
1554
            #   "f" is the same value, but cast to (PyCFunctionObject*)
1555
            #   "self" is the (PyObject*) of the 'self'
1556
            try:
1557
                # Use the prettyprinter for the func:
1558
                func = frame.read_var(arg_name)
1559
                return str(func)
1560
            except ValueError:
1561
                return ('PyCFunction invocation (unable to read %s: '
1562
                        'missing debuginfos?)' % arg_name)
1563
            except RuntimeError:
1564
                return 'PyCFunction invocation (unable to read %s)' % arg_name
1565

1566
        if caller == 'wrapper_call':
1567
            arg_name = 'wp'
1568
            try:
1569
                func = frame.read_var(arg_name)
1570
                return str(func)
1571
            except ValueError:
1572
                return ('<wrapper_call invocation (unable to read %s: '
1573
                        'missing debuginfos?)>' % arg_name)
1574
            except RuntimeError:
1575
                return '<wrapper_call invocation (unable to read %s)>' % arg_name
1576

1577
        # This frame isn't worth reporting:
1578
        return False
1579

1580
    def is_waiting_for_gil(self):
1581
        '''Is this frame waiting on the GIL?'''
1582
        # This assumes the _POSIX_THREADS version of Python/ceval_gil.h:
1583
        name = self._gdbframe.name()
1584
        if name:
1585
            return (name == 'take_gil')
1586

1587
    def is_gc_collect(self):
1588
        '''Is this frame "collect" within the garbage-collector?'''
1589
        return self._gdbframe.name() == 'collect'
1590

1591
    def get_pyop(self):
1592
        try:
1593
            f = self._gdbframe.read_var('f')
1594
            frame = PyFrameObjectPtr.from_pyobject_ptr(f)
1595
            if not frame.is_optimized_out():
1596
                return frame
1597
            # gdb is unable to get the "f" argument of PyEval_EvalFrameEx()
1598
            # because it was "optimized out". Try to get "f" from the frame
1599
            # of the caller, PyEval_EvalCodeEx().
1600
            orig_frame = frame
1601
            caller = self._gdbframe.older()
1602
            if caller:
1603
                f = caller.read_var('f')
1604
                frame = PyFrameObjectPtr.from_pyobject_ptr(f)
1605
                if not frame.is_optimized_out():
1606
                    return frame
1607
            return orig_frame
1608
        except ValueError:
1609
            return None
1610

1611
    @classmethod
1612
    def get_selected_frame(cls):
1613
        _gdbframe = gdb.selected_frame()
1614
        if _gdbframe:
1615
            return Frame(_gdbframe)
1616
        return None
1617

1618
    @classmethod
1619
    def get_selected_python_frame(cls):
1620
        '''Try to obtain the Frame for the python-related code in the selected
1621
        frame, or None'''
1622
        try:
1623
            frame = cls.get_selected_frame()
1624
        except gdb.error:
1625
            # No frame: Python didn't start yet
1626
            return None
1627

1628
        while frame:
1629
            if frame.is_python_frame():
1630
                return frame
1631
            frame = frame.older()
1632

1633
        # Not found:
1634
        return None
1635

1636
    @classmethod
1637
    def get_selected_bytecode_frame(cls):
1638
        '''Try to obtain the Frame for the python bytecode interpreter in the
1639
        selected GDB frame, or None'''
1640
        frame = cls.get_selected_frame()
1641

1642
        while frame:
1643
            if frame.is_evalframe():
1644
                return frame
1645
            frame = frame.older()
1646

1647
        # Not found:
1648
        return None
1649

1650
    def print_summary(self):
1651
        if self.is_evalframe():
1652
            pyop = self.get_pyop()
1653
            if pyop:
1654
                line = pyop.get_truncated_repr(MAX_OUTPUT_LEN)
1655
                write_unicode(sys.stdout, '#%i %s\n' % (self.get_index(), line))
1656
                if not pyop.is_optimized_out():
1657
                    line = pyop.current_line()
1658
                    if line is not None:
1659
                        sys.stdout.write('    %s\n' % line.strip())
1660
            else:
1661
                sys.stdout.write('#%i (unable to read python frame information)\n' % self.get_index())
1662
        else:
1663
            info = self.is_other_python_frame()
1664
            if info:
1665
                sys.stdout.write('#%i %s\n' % (self.get_index(), info))
1666
            else:
1667
                sys.stdout.write('#%i\n' % self.get_index())
1668

1669
    def print_traceback(self):
1670
        if self.is_evalframe():
1671
            pyop = self.get_pyop()
1672
            if pyop:
1673
                pyop.print_traceback()
1674
                if not pyop.is_optimized_out():
1675
                    line = pyop.current_line()
1676
                    if line is not None:
1677
                        sys.stdout.write('    %s\n' % line.strip())
1678
            else:
1679
                sys.stdout.write('  (unable to read python frame information)\n')
1680
        else:
1681
            info = self.is_other_python_frame()
1682
            if info:
1683
                sys.stdout.write('  %s\n' % info)
1684
            else:
1685
                sys.stdout.write('  (not a python frame)\n')
1686

1687
class PyList(gdb.Command):
1688
    '''List the current Python source code, if any
1689

1690
    Use
1691
       py-list START
1692
    to list at a different line number within the python source.
1693

1694
    Use
1695
       py-list START, END
1696
    to list a specific range of lines within the python source.
1697
    '''
1698

1699
    def __init__(self):
1700
        gdb.Command.__init__ (self,
1701
                              "py-list",
1702
                              gdb.COMMAND_FILES,
1703
                              gdb.COMPLETE_NONE)
1704

1705

1706
    def invoke(self, args, from_tty):
1707
        import re
1708

1709
        start = None
1710
        end = None
1711

1712
        m = re.match(r'\s*(\d+)\s*', args)
1713
        if m:
1714
            start = int(m.group(0))
1715
            end = start + 10
1716

1717
        m = re.match(r'\s*(\d+)\s*,\s*(\d+)\s*', args)
1718
        if m:
1719
            start, end = map(int, m.groups())
1720

1721
        # py-list requires an actual PyEval_EvalFrameEx frame:
1722
        frame = Frame.get_selected_bytecode_frame()
1723
        if not frame:
1724
            print('Unable to locate gdb frame for python bytecode interpreter')
1725
            return
1726

1727
        pyop = frame.get_pyop()
1728
        if not pyop or pyop.is_optimized_out():
1729
            print(UNABLE_READ_INFO_PYTHON_FRAME)
1730
            return
1731

1732
        filename = pyop.filename()
1733
        lineno = pyop.current_line_num()
1734
        if lineno is None:
1735
            print('Unable to read python frame line number')
1736
            return
1737

1738
        if start is None:
1739
            start = lineno - 5
1740
            end = lineno + 5
1741

1742
        if start<1:
1743
            start = 1
1744

1745
        try:
1746
            f = open(os_fsencode(filename))
1747
        except OSError as err:
1748
            sys.stdout.write('Unable to open %s: %s\n'
1749
                             % (filename, err))
1750
            return
1751
        with f:
1752
            all_lines = f.readlines()
1753
            # start and end are 1-based, all_lines is 0-based;
1754
            # so [start-1:end] as a python slice gives us [start, end] as a
1755
            # closed interval
1756
            for i, line in enumerate(all_lines[start-1:end]):
1757
                linestr = str(i+start)
1758
                # Highlight current line:
1759
                if i + start == lineno:
1760
                    linestr = '>' + linestr
1761
                sys.stdout.write('%4s    %s' % (linestr, line))
1762

1763

1764
# ...and register the command:
1765
PyList()
1766

1767
def move_in_stack(move_up):
1768
    '''Move up or down the stack (for the py-up/py-down command)'''
1769
    frame = Frame.get_selected_python_frame()
1770
    if not frame:
1771
        print('Unable to locate python frame')
1772
        return
1773

1774
    while frame:
1775
        if move_up:
1776
            iter_frame = frame.older()
1777
        else:
1778
            iter_frame = frame.newer()
1779

1780
        if not iter_frame:
1781
            break
1782

1783
        if iter_frame.is_python_frame():
1784
            # Result:
1785
            if iter_frame.select():
1786
                iter_frame.print_summary()
1787
            return
1788

1789
        frame = iter_frame
1790

1791
    if move_up:
1792
        print('Unable to find an older python frame')
1793
    else:
1794
        print('Unable to find a newer python frame')
1795

1796
class PyUp(gdb.Command):
1797
    'Select and print the python stack frame that called this one (if any)'
1798
    def __init__(self):
1799
        gdb.Command.__init__ (self,
1800
                              "py-up",
1801
                              gdb.COMMAND_STACK,
1802
                              gdb.COMPLETE_NONE)
1803

1804

1805
    def invoke(self, args, from_tty):
1806
        move_in_stack(move_up=True)
1807

1808
class PyDown(gdb.Command):
1809
    'Select and print the python stack frame called by this one (if any)'
1810
    def __init__(self):
1811
        gdb.Command.__init__ (self,
1812
                              "py-down",
1813
                              gdb.COMMAND_STACK,
1814
                              gdb.COMPLETE_NONE)
1815

1816

1817
    def invoke(self, args, from_tty):
1818
        move_in_stack(move_up=False)
1819

1820
# Not all builds of gdb have gdb.Frame.select
1821
if hasattr(gdb.Frame, 'select'):
1822
    PyUp()
1823
    PyDown()
1824

1825
class PyBacktraceFull(gdb.Command):
1826
    'Display the current python frame and all the frames within its call stack (if any)'
1827
    def __init__(self):
1828
        gdb.Command.__init__ (self,
1829
                              "py-bt-full",
1830
                              gdb.COMMAND_STACK,
1831
                              gdb.COMPLETE_NONE)
1832

1833

1834
    def invoke(self, args, from_tty):
1835
        frame = Frame.get_selected_python_frame()
1836
        if not frame:
1837
            print('Unable to locate python frame')
1838
            return
1839

1840
        while frame:
1841
            if frame.is_python_frame():
1842
                frame.print_summary()
1843
            frame = frame.older()
1844

1845
PyBacktraceFull()
1846

1847
class PyBacktrace(gdb.Command):
1848
    'Display the current python frame and all the frames within its call stack (if any)'
1849
    def __init__(self):
1850
        gdb.Command.__init__ (self,
1851
                              "py-bt",
1852
                              gdb.COMMAND_STACK,
1853
                              gdb.COMPLETE_NONE)
1854

1855

1856
    def invoke(self, args, from_tty):
1857
        frame = Frame.get_selected_python_frame()
1858
        if not frame:
1859
            print('Unable to locate python frame')
1860
            return
1861

1862
        sys.stdout.write('Traceback (most recent call first):\n')
1863
        while frame:
1864
            if frame.is_python_frame():
1865
                frame.print_traceback()
1866
            frame = frame.older()
1867

1868
PyBacktrace()
1869

1870
class PyPrint(gdb.Command):
1871
    'Look up the given python variable name, and print it'
1872
    def __init__(self):
1873
        gdb.Command.__init__ (self,
1874
                              "py-print",
1875
                              gdb.COMMAND_DATA,
1876
                              gdb.COMPLETE_NONE)
1877

1878

1879
    def invoke(self, args, from_tty):
1880
        name = str(args)
1881

1882
        frame = Frame.get_selected_python_frame()
1883
        if not frame:
1884
            print('Unable to locate python frame')
1885
            return
1886

1887
        pyop_frame = frame.get_pyop()
1888
        if not pyop_frame:
1889
            print(UNABLE_READ_INFO_PYTHON_FRAME)
1890
            return
1891

1892
        pyop_var, scope = pyop_frame.get_var_by_name(name)
1893

1894
        if pyop_var:
1895
            print('%s %r = %s'
1896
                   % (scope,
1897
                      name,
1898
                      pyop_var.get_truncated_repr(MAX_OUTPUT_LEN)))
1899
        else:
1900
            print('%r not found' % name)
1901

1902
PyPrint()
1903

1904
class PyLocals(gdb.Command):
1905
    'Look up the given python variable name, and print it'
1906
    def __init__(self):
1907
        gdb.Command.__init__ (self,
1908
                              "py-locals",
1909
                              gdb.COMMAND_DATA,
1910
                              gdb.COMPLETE_NONE)
1911

1912

1913
    def invoke(self, args, from_tty):
1914
        name = str(args)
1915

1916
        frame = Frame.get_selected_python_frame()
1917
        if not frame:
1918
            print('Unable to locate python frame')
1919
            return
1920

1921
        pyop_frame = frame.get_pyop()
1922
        if not pyop_frame:
1923
            print(UNABLE_READ_INFO_PYTHON_FRAME)
1924
            return
1925

1926
        for pyop_name, pyop_value in pyop_frame.iter_locals():
1927
            print('%s = %s' % (
1928
                pyop_name.proxyval(set()),
1929
                pyop_value.get_truncated_repr(MAX_OUTPUT_LEN),
1930
            ))
1931

1932
PyLocals()
1933

1934

1935
##################################################################
1936
## added, not in CPython
1937
##################################################################
1938

1939
import re
1940
import warnings
1941
import tempfile
1942
import functools
1943
import textwrap
1944
import itertools
1945
import traceback
1946

1947

1948
def dont_suppress_errors(function):
1949
    "*sigh*, readline"
1950
    @functools.wraps(function)
1951
    def wrapper(*args, **kwargs):
1952
        try:
1953
            return function(*args, **kwargs)
1954
        except Exception:
1955
            traceback.print_exc()
1956
            raise
1957

1958
    return wrapper
1959

1960
class PyGlobals(gdb.Command):
1961
    'List all the globals in the currently select Python frame'
1962
    def __init__(self):
1963
        gdb.Command.__init__ (self,
1964
                              "py-globals",
1965
                              gdb.COMMAND_DATA,
1966
                              gdb.COMPLETE_NONE)
1967

1968
    @dont_suppress_errors
1969
    def invoke(self, args, from_tty):
1970
        name = str(args)
1971

1972
        frame = Frame.get_selected_python_frame()
1973
        if not frame:
1974
            print('Unable to locate python frame')
1975
            return
1976

1977
        pyop_frame = frame.get_pyop()
1978
        if not pyop_frame:
1979
            print(UNABLE_READ_INFO_PYTHON_FRAME)
1980
            return
1981

1982
        for pyop_name, pyop_value in pyop_frame.iter_locals():
1983
            print('%s = %s'
1984
                   % (pyop_name.proxyval(set()),
1985
                      pyop_value.get_truncated_repr(MAX_OUTPUT_LEN)))
1986

1987
    def get_namespace(self, pyop_frame):
1988
        return pyop_frame.iter_globals()
1989

1990

1991
PyGlobals()
1992

1993
# This function used to be a part of CPython's libpython.py (as a member function of frame).
1994
# It isn't anymore, so I copied it.
1995
def is_evalframeex(frame):
1996
    '''Is this a PyEval_EvalFrameEx frame?'''
1997
    if frame._gdbframe.name() == 'PyEval_EvalFrameEx':
1998
        '''
1999
        I believe we also need to filter on the inline
2000
        struct frame_id.inline_depth, only regarding frames with
2001
        an inline depth of 0 as actually being this function
2002

2003
        So we reject those with type gdb.INLINE_FRAME
2004
        '''
2005
        if frame._gdbframe.type() == gdb.NORMAL_FRAME:
2006
            # We have a PyEval_EvalFrameEx frame:
2007
            return True
2008

2009
    return False
2010

2011
class PyNameEquals(gdb.Function):
2012

2013
    def _get_pycurframe_attr(self, attr):
2014
        frame = Frame(gdb.selected_frame())
2015
        if is_evalframeex(frame):
2016
            pyframe = frame.get_pyop()
2017
            if pyframe is None:
2018
                warnings.warn("Use a Python debug build, Python breakpoints "
2019
                              "won't work otherwise.")
2020
                return None
2021

2022
            return getattr(pyframe, attr).proxyval(set())
2023

2024
        return None
2025

2026
    @dont_suppress_errors
2027
    def invoke(self, funcname):
2028
        attr = self._get_pycurframe_attr('co_name')
2029
        return attr is not None and attr == funcname.string()
2030

2031
PyNameEquals("pyname_equals")
2032

2033

2034
class PyModEquals(PyNameEquals):
2035

2036
    @dont_suppress_errors
2037
    def invoke(self, modname):
2038
        attr = self._get_pycurframe_attr('co_filename')
2039
        if attr is not None:
2040
            filename, ext = os.path.splitext(os.path.basename(attr))
2041
            return filename == modname.string()
2042
        return False
2043

2044
PyModEquals("pymod_equals")
2045

2046

2047
class PyBreak(gdb.Command):
2048
    """
2049
    Set a Python breakpoint. Examples:
2050

2051
    Break on any function or method named 'func' in module 'modname'
2052

2053
        py-break modname.func
2054

2055
    Break on any function or method named 'func'
2056

2057
        py-break func
2058
    """
2059

2060
    @dont_suppress_errors
2061
    def invoke(self, funcname, from_tty):
2062
        if '.' in funcname:
2063
            modname, dot, funcname = funcname.rpartition('.')
2064
            cond = '$pyname_equals("%s") && $pymod_equals("%s")' % (funcname,
2065
                                                                    modname)
2066
        else:
2067
            cond = '$pyname_equals("%s")' % funcname
2068

2069
        gdb.execute('break PyEval_EvalFrameEx if ' + cond)
2070

2071
PyBreak("py-break", gdb.COMMAND_RUNNING, gdb.COMPLETE_NONE)
2072

2073

2074
class _LoggingState:
2075
    """
2076
    State that helps to provide a reentrant gdb.execute() function.
2077
    """
2078

2079
    def __init__(self):
2080
        f = tempfile.NamedTemporaryFile('r+')
2081
        self.file = f
2082
        self.filename = f.name
2083
        self.fd = f.fileno()
2084
        _execute("set logging file %s" % self.filename)
2085
        self.file_position_stack = []
2086

2087
    def __enter__(self):
2088
        if not self.file_position_stack:
2089
            _execute("set logging redirect on")
2090
            _execute("set logging on")
2091
            _execute("set pagination off")
2092

2093
        self.file_position_stack.append(os.fstat(self.fd).st_size)
2094
        return self
2095

2096
    def getoutput(self):
2097
        gdb.flush()
2098
        self.file.seek(self.file_position_stack[-1])
2099
        result = self.file.read()
2100
        return result
2101

2102
    def __exit__(self, exc_type, exc_val, tb):
2103
        startpos = self.file_position_stack.pop()
2104
        self.file.seek(startpos)
2105
        self.file.truncate()
2106
        if not self.file_position_stack:
2107
            _execute("set logging off")
2108
            _execute("set logging redirect off")
2109
            _execute("set pagination on")
2110

2111

2112
def execute(command, from_tty=False, to_string=False):
2113
    """
2114
    Replace gdb.execute() with this function and have it accept a 'to_string'
2115
    argument (new in 7.2). Have it properly capture stderr also. Ensure
2116
    reentrancy.
2117
    """
2118
    if to_string:
2119
        with _logging_state as state:
2120
            _execute(command, from_tty)
2121
            return state.getoutput()
2122
    else:
2123
        _execute(command, from_tty)
2124

2125

2126
_execute = gdb.execute
2127
gdb.execute = execute
2128
_logging_state = _LoggingState()
2129

2130

2131
def get_selected_inferior():
2132
    """
2133
    Return the selected inferior in gdb.
2134
    """
2135
    # Woooh, another bug in gdb! Is there an end in sight?
2136
    # http://sourceware.org/bugzilla/show_bug.cgi?id=12212
2137
    return gdb.inferiors()[0]
2138

2139
    selected_thread = gdb.selected_thread()
2140

2141
    for inferior in gdb.inferiors():
2142
        for thread in inferior.threads():
2143
            if thread == selected_thread:
2144
                return inferior
2145

2146

2147
def source_gdb_script(script_contents, to_string=False):
2148
    """
2149
    Source a gdb script with script_contents passed as a string. This is useful
2150
    to provide defines for py-step and py-next to make them repeatable (this is
2151
    not possible with gdb.execute()). See
2152
    http://sourceware.org/bugzilla/show_bug.cgi?id=12216
2153
    """
2154
    fd, filename = tempfile.mkstemp()
2155
    f = os.fdopen(fd, 'w')
2156
    f.write(script_contents)
2157
    f.close()
2158
    gdb.execute("source %s" % filename, to_string=to_string)
2159
    os.remove(filename)
2160

2161

2162
def register_defines():
2163
    source_gdb_script(textwrap.dedent("""\
2164
        define py-step
2165
        -py-step
2166
        end
2167

2168
        define py-next
2169
        -py-next
2170
        end
2171

2172
        document py-step
2173
        %s
2174
        end
2175

2176
        document py-next
2177
        %s
2178
        end
2179
    """) % (PyStep.__doc__, PyNext.__doc__))
2180

2181

2182
def stackdepth(frame):
2183
    "Tells the stackdepth of a gdb frame."
2184
    depth = 0
2185
    while frame:
2186
        frame = frame.older()
2187
        depth += 1
2188

2189
    return depth
2190

2191

2192
class ExecutionControlCommandBase(gdb.Command):
2193
    """
2194
    Superclass for language specific execution control. Language specific
2195
    features should be implemented by lang_info using the LanguageInfo
2196
    interface. 'name' is the name of the command.
2197
    """
2198

2199
    def __init__(self, name, lang_info):
2200
        super().__init__(
2201
                                name, gdb.COMMAND_RUNNING, gdb.COMPLETE_NONE)
2202
        self.lang_info = lang_info
2203

2204
    def install_breakpoints(self):
2205
        all_locations = itertools.chain(
2206
            self.lang_info.static_break_functions(),
2207
            self.lang_info.runtime_break_functions())
2208

2209
        for location in all_locations:
2210
            result = gdb.execute('break %s' % location, to_string=True)
2211
            yield re.search(r'Breakpoint (\d+)', result).group(1)
2212

2213
    def delete_breakpoints(self, breakpoint_list):
2214
        for bp in breakpoint_list:
2215
            gdb.execute("delete %s" % bp)
2216

2217
    def filter_output(self, result):
2218
        reflags = re.MULTILINE
2219

2220
        output_on_halt = [
2221
            (r'^Program received signal .*', reflags|re.DOTALL),
2222
            (r'.*[Ww]arning.*', 0),
2223
            (r'^Program exited .*', reflags),
2224
        ]
2225

2226
        output_always = [
2227
            # output when halting on a watchpoint
2228
            (r'^(Old|New) value = .*', reflags),
2229
            # output from the 'display' command
2230
            (r'^\d+: \w+ = .*', reflags),
2231
        ]
2232

2233
        def filter_output(regexes):
2234
            output = []
2235
            for regex, flags in regexes:
2236
                for match in re.finditer(regex, result, flags):
2237
                    output.append(match.group(0))
2238

2239
            return '\n'.join(output)
2240

2241
        # Filter the return value output of the 'finish' command
2242
        match_finish = re.search(r'^Value returned is \$\d+ = (.*)', result,
2243
                                 re.MULTILINE)
2244
        if match_finish:
2245
            finish_output = 'Value returned: %s\n' % match_finish.group(1)
2246
        else:
2247
            finish_output = ''
2248

2249
        return (filter_output(output_on_halt),
2250
                finish_output + filter_output(output_always))
2251

2252
    def stopped(self):
2253
        return get_selected_inferior().pid == 0
2254

2255
    def finish_executing(self, result):
2256
        """
2257
        After doing some kind of code running in the inferior, print the line
2258
        of source code or the result of the last executed gdb command (passed
2259
        in as the `result` argument).
2260
        """
2261
        output_on_halt, output_always = self.filter_output(result)
2262

2263
        if self.stopped():
2264
            print(output_always)
2265
            print(output_on_halt)
2266
        else:
2267
            frame = gdb.selected_frame()
2268
            source_line = self.lang_info.get_source_line(frame)
2269
            if self.lang_info.is_relevant_function(frame):
2270
                raised_exception = self.lang_info.exc_info(frame)
2271
                if raised_exception:
2272
                    print(raised_exception)
2273

2274
            if source_line:
2275
                if output_always.rstrip():
2276
                    print(output_always.rstrip())
2277
                print(source_line)
2278
            else:
2279
                print(result)
2280

2281
    def _finish(self):
2282
        """
2283
        Execute until the function returns (or until something else makes it
2284
        stop)
2285
        """
2286
        if gdb.selected_frame().older() is not None:
2287
            return gdb.execute('finish', to_string=True)
2288
        else:
2289
            # outermost frame, continue
2290
            return gdb.execute('cont', to_string=True)
2291

2292
    def _finish_frame(self):
2293
        """
2294
        Execute until the function returns to a relevant caller.
2295
        """
2296
        while True:
2297
            result = self._finish()
2298

2299
            try:
2300
                frame = gdb.selected_frame()
2301
            except RuntimeError:
2302
                break
2303

2304
            hitbp = re.search(r'Breakpoint (\d+)', result)
2305
            is_relevant = self.lang_info.is_relevant_function(frame)
2306
            if hitbp or is_relevant or self.stopped():
2307
                break
2308

2309
        return result
2310

2311
    def finish(self, *args):
2312
        "Implements the finish command."
2313
        result = self._finish_frame()
2314
        self.finish_executing(result)
2315

2316
    def step(self, stepinto, stepover_command='next'):
2317
        """
2318
        Do a single step or step-over. Returns the result of the last gdb
2319
        command that made execution stop.
2320

2321
        This implementation, for stepping, sets (conditional) breakpoints for
2322
        all functions that are deemed relevant. It then does a step over until
2323
        either something halts execution, or until the next line is reached.
2324

2325
        If, however, stepover_command is given, it should be a string gdb
2326
        command that continues execution in some way. The idea is that the
2327
        caller has set a (conditional) breakpoint or watchpoint that can work
2328
        more efficiently than the step-over loop. For Python this means setting
2329
        a watchpoint for f->f_lasti, which means we can then subsequently
2330
        "finish" frames.
2331
        We want f->f_lasti instead of f->f_lineno, because the latter only
2332
        works properly with local trace functions, see
2333
        PyFrameObjectPtr.current_line_num and PyFrameObjectPtr.addr2line.
2334
        """
2335
        if stepinto:
2336
            breakpoint_list = list(self.install_breakpoints())
2337

2338
        beginframe = gdb.selected_frame()
2339

2340
        if self.lang_info.is_relevant_function(beginframe):
2341
            # If we start in a relevant frame, initialize stuff properly. If
2342
            # we don't start in a relevant frame, the loop will halt
2343
            # immediately. So don't call self.lang_info.lineno() as it may
2344
            # raise for irrelevant frames.
2345
            beginline = self.lang_info.lineno(beginframe)
2346

2347
            if not stepinto:
2348
                depth = stackdepth(beginframe)
2349

2350
        newframe = beginframe
2351

2352
        while True:
2353
            if self.lang_info.is_relevant_function(newframe):
2354
                result = gdb.execute(stepover_command, to_string=True)
2355
            else:
2356
                result = self._finish_frame()
2357

2358
            if self.stopped():
2359
                break
2360

2361
            newframe = gdb.selected_frame()
2362
            is_relevant_function = self.lang_info.is_relevant_function(newframe)
2363
            try:
2364
                framename = newframe.name()
2365
            except RuntimeError:
2366
                framename = None
2367

2368
            m = re.search(r'Breakpoint (\d+)', result)
2369
            if m:
2370
                if is_relevant_function and m.group(1) in breakpoint_list:
2371
                    # although we hit a breakpoint, we still need to check
2372
                    # that the function, in case hit by a runtime breakpoint,
2373
                    # is in the right context
2374
                    break
2375

2376
            if newframe != beginframe:
2377
                # new function
2378

2379
                if not stepinto:
2380
                    # see if we returned to the caller
2381
                    newdepth = stackdepth(newframe)
2382
                    is_relevant_function = (newdepth < depth and
2383
                                            is_relevant_function)
2384

2385
                if is_relevant_function:
2386
                    break
2387
            else:
2388
                # newframe equals beginframe, check for a difference in the
2389
                # line number
2390
                lineno = self.lang_info.lineno(newframe)
2391
                if lineno and lineno != beginline:
2392
                    break
2393

2394
        if stepinto:
2395
            self.delete_breakpoints(breakpoint_list)
2396

2397
        self.finish_executing(result)
2398

2399
    def run(self, args, from_tty):
2400
        self.finish_executing(gdb.execute('run ' + args, to_string=True))
2401

2402
    def cont(self, *args):
2403
        self.finish_executing(gdb.execute('cont', to_string=True))
2404

2405

2406
class LanguageInfo:
2407
    """
2408
    This class defines the interface that ExecutionControlCommandBase needs to
2409
    provide language-specific execution control.
2410

2411
    Classes that implement this interface should implement:
2412

2413
        lineno(frame)
2414
            Tells the current line number (only called for a relevant frame).
2415
            If lineno is a false value it is not checked for a difference.
2416

2417
        is_relevant_function(frame)
2418
            tells whether we care about frame 'frame'
2419

2420
        get_source_line(frame)
2421
            get the line of source code for the current line (only called for a
2422
            relevant frame). If the source code cannot be retrieved this
2423
            function should return None
2424

2425
        exc_info(frame) -- optional
2426
            tells whether an exception was raised, if so, it should return a
2427
            string representation of the exception value, None otherwise.
2428

2429
        static_break_functions()
2430
            returns an iterable of function names that are considered relevant
2431
            and should halt step-into execution. This is needed to provide a
2432
            performing step-into
2433

2434
        runtime_break_functions() -- optional
2435
            list of functions that we should break into depending on the
2436
            context
2437
    """
2438

2439
    def exc_info(self, frame):
2440
        "See this class' docstring."
2441

2442
    def runtime_break_functions(self):
2443
        """
2444
        Implement this if the list of step-into functions depends on the
2445
        context.
2446
        """
2447
        return ()
2448

2449

2450
class PythonInfo(LanguageInfo):
2451

2452
    def pyframe(self, frame):
2453
        pyframe = Frame(frame).get_pyop()
2454
        if pyframe:
2455
            return pyframe
2456
        else:
2457
            raise gdb.RuntimeError(
2458
                "Unable to find the Python frame, run your code with a debug "
2459
                "build (configure with --with-pydebug or compile with -g).")
2460

2461
    def lineno(self, frame):
2462
        return self.pyframe(frame).current_line_num()
2463

2464
    def is_relevant_function(self, frame):
2465
        return Frame(frame).is_evalframeex()
2466

2467
    def get_source_line(self, frame):
2468
        try:
2469
            pyframe = self.pyframe(frame)
2470
            return '%4d    %s' % (pyframe.current_line_num(),
2471
                                  pyframe.current_line().rstrip())
2472
        except OSError:
2473
            return None
2474

2475
    def exc_info(self, frame):
2476
        try:
2477
            tstate = frame.read_var('tstate').dereference()
2478
            if gdb.parse_and_eval('tstate->frame == f'):
2479
                # tstate local variable initialized, check for an exception
2480
                if sys.version_info >= (3, 12, 0, 'alpha', 6):
2481
                    inf_type = inf_value = tstate['current_exception']
2482
                else:
2483
                    inf_type = tstate['curexc_type']
2484
                    inf_value = tstate['curexc_value']
2485

2486
                if inf_type:
2487
                    return 'An exception was raised: %s' % (inf_value,)
2488
        except (ValueError, RuntimeError):
2489
            # Could not read the variable tstate or it's memory, it's ok
2490
            pass
2491

2492
    def static_break_functions(self):
2493
        yield 'PyEval_EvalFrameEx'
2494

2495

2496
class PythonStepperMixin:
2497
    """
2498
    Make this a mixin so CyStep can also inherit from this and use a
2499
    CythonCodeStepper at the same time.
2500
    """
2501

2502
    def python_step(self, stepinto):
2503
        """
2504
        Set a watchpoint on the Python bytecode instruction pointer and try
2505
        to finish the frame
2506
        """
2507
        output = gdb.execute('watch f->f_lasti', to_string=True)
2508
        watchpoint = int(re.search(r'[Ww]atchpoint (\d+):', output).group(1))
2509
        self.step(stepinto=stepinto, stepover_command='finish')
2510
        gdb.execute('delete %s' % watchpoint)
2511

2512

2513
class PyStep(ExecutionControlCommandBase, PythonStepperMixin):
2514
    "Step through Python code."
2515

2516
    stepinto = True
2517

2518
    @dont_suppress_errors
2519
    def invoke(self, args, from_tty):
2520
        self.python_step(stepinto=self.stepinto)
2521

2522

2523
class PyNext(PyStep):
2524
    "Step-over Python code."
2525

2526
    stepinto = False
2527

2528

2529
class PyFinish(ExecutionControlCommandBase):
2530
    "Execute until function returns to a caller."
2531

2532
    invoke = dont_suppress_errors(ExecutionControlCommandBase.finish)
2533

2534

2535
class PyRun(ExecutionControlCommandBase):
2536
    "Run the program."
2537

2538
    invoke = dont_suppress_errors(ExecutionControlCommandBase.run)
2539

2540

2541
class PyCont(ExecutionControlCommandBase):
2542

2543
    invoke = dont_suppress_errors(ExecutionControlCommandBase.cont)
2544

2545

2546
def _pointervalue(gdbval):
2547
    """
2548
    Return the value of the pointer as a Python int.
2549

2550
    gdbval.type must be a pointer type
2551
    """
2552
    # don't convert with int() as it will raise a RuntimeError
2553
    if gdbval.address is not None:
2554
        return int(gdbval.address)
2555
    else:
2556
        # the address attribute is None sometimes, in which case we can
2557
        # still convert the pointer to an int
2558
        return int(gdbval)
2559

2560

2561
def pointervalue(gdbval):
2562
    pointer = _pointervalue(gdbval)
2563
    try:
2564
        if pointer < 0:
2565
            raise gdb.GdbError("Negative pointer value, presumably a bug "
2566
                               "in gdb, aborting.")
2567
    except RuntimeError:
2568
        # work around yet another bug in gdb where you get random behaviour
2569
        # and tracebacks
2570
        pass
2571

2572
    return pointer
2573

2574

2575
def get_inferior_unicode_postfix():
2576
    try:
2577
        gdb.parse_and_eval('PyUnicode_FromEncodedObject')
2578
    except RuntimeError:
2579
        try:
2580
            gdb.parse_and_eval('PyUnicodeUCS2_FromEncodedObject')
2581
        except RuntimeError:
2582
            return 'UCS4'
2583
        else:
2584
            return 'UCS2'
2585
    else:
2586
        return ''
2587

2588

2589
class PythonCodeExecutor:
2590

2591
    Py_single_input = 256
2592
    Py_file_input = 257
2593
    Py_eval_input = 258
2594

2595
    def malloc(self, size):
2596
        chunk = (gdb.parse_and_eval("(void *) malloc((size_t) %d)" % size))
2597

2598
        pointer = pointervalue(chunk)
2599
        if pointer == 0:
2600
            raise gdb.GdbError("No memory could be allocated in the inferior.")
2601

2602
        return pointer
2603

2604
    def alloc_string(self, string):
2605
        pointer = self.malloc(len(string))
2606
        get_selected_inferior().write_memory(pointer, string)
2607

2608
        return pointer
2609

2610
    def alloc_pystring(self, string):
2611
        stringp = self.alloc_string(string)
2612
        PyString_FromStringAndSize = 'PyString_FromStringAndSize'
2613

2614
        try:
2615
            gdb.parse_and_eval(PyString_FromStringAndSize)
2616
        except RuntimeError:
2617
            # Python 3
2618
            PyString_FromStringAndSize = ('PyUnicode%s_FromStringAndSize' %
2619
                                               (get_inferior_unicode_postfix(),))
2620

2621
        try:
2622
            result = gdb.parse_and_eval(
2623
                '(PyObject *) %s((char *) %d, (size_t) %d)' % (
2624
                            PyString_FromStringAndSize, stringp, len(string)))
2625
        finally:
2626
            self.free(stringp)
2627

2628
        pointer = pointervalue(result)
2629
        if pointer == 0:
2630
            raise gdb.GdbError("Unable to allocate Python string in "
2631
                               "the inferior.")
2632

2633
        return pointer
2634

2635
    def free(self, pointer):
2636
        gdb.parse_and_eval("(void) free((void *) %d)" % pointer)
2637

2638
    def incref(self, pointer):
2639
        "Increment the reference count of a Python object in the inferior."
2640
        gdb.parse_and_eval('Py_IncRef((PyObject *) %d)' % pointer)
2641

2642
    def xdecref(self, pointer):
2643
        "Decrement the reference count of a Python object in the inferior."
2644
        # Py_DecRef is like Py_XDECREF, but a function. So we don't have
2645
        # to check for NULL. This should also decref all our allocated
2646
        # Python strings.
2647
        gdb.parse_and_eval('Py_DecRef((PyObject *) %d)' % pointer)
2648

2649
    def evalcode(self, code, input_type, global_dict=None, local_dict=None):
2650
        """
2651
        Evaluate python code `code` given as a string in the inferior and
2652
        return the result as a gdb.Value. Returns a new reference in the
2653
        inferior.
2654

2655
        Of course, executing any code in the inferior may be dangerous and may
2656
        leave the debuggee in an unsafe state or terminate it altogether.
2657
        """
2658
        if '\0' in code:
2659
            raise gdb.GdbError("String contains NUL byte.")
2660

2661
        code += '\0'
2662

2663
        pointer = self.alloc_string(code)
2664

2665
        globalsp = pointervalue(global_dict)
2666
        localsp = pointervalue(local_dict)
2667

2668
        if globalsp == 0 or localsp == 0:
2669
            raise gdb.GdbError("Unable to obtain or create locals or globals.")
2670

2671
        code = """
2672
            PyRun_String(
2673
                (char *) %(code)d,
2674
                (int) %(start)d,
2675
                (PyObject *) %(globals)s,
2676
                (PyObject *) %(locals)d)
2677
        """ % dict(code=pointer, start=input_type,
2678
                   globals=globalsp, locals=localsp)
2679

2680
        with FetchAndRestoreError():
2681
            try:
2682
                pyobject_return_value = gdb.parse_and_eval(code)
2683
            finally:
2684
                self.free(pointer)
2685

2686
        return pyobject_return_value
2687

2688

2689
class FetchAndRestoreError(PythonCodeExecutor):
2690
    """
2691
    Context manager that fetches the error indicator in the inferior and
2692
    restores it on exit.
2693
    """
2694

2695
    def __init__(self):
2696
        self.sizeof_PyObjectPtr = gdb.lookup_type('PyObject').pointer().sizeof
2697
        self.pointer = self.malloc(self.sizeof_PyObjectPtr * 3)
2698

2699
        type = self.pointer
2700
        value = self.pointer + self.sizeof_PyObjectPtr
2701
        traceback = self.pointer + self.sizeof_PyObjectPtr * 2
2702

2703
        self.errstate = type, value, traceback
2704

2705
    def __enter__(self):
2706
        gdb.parse_and_eval("PyErr_Fetch(%d, %d, %d)" % self.errstate)
2707

2708
    def __exit__(self, *args):
2709
        if gdb.parse_and_eval("(int) PyErr_Occurred()"):
2710
            gdb.parse_and_eval("PyErr_Print()")
2711

2712
        pyerr_restore = ("PyErr_Restore("
2713
                            "(PyObject *) *%d,"
2714
                            "(PyObject *) *%d,"
2715
                            "(PyObject *) *%d)")
2716

2717
        try:
2718
            gdb.parse_and_eval(pyerr_restore % self.errstate)
2719
        finally:
2720
            self.free(self.pointer)
2721

2722

2723
class FixGdbCommand(gdb.Command):
2724

2725
    def __init__(self, command, actual_command):
2726
        super().__init__(command, gdb.COMMAND_DATA,
2727
                                            gdb.COMPLETE_NONE)
2728
        self.actual_command = actual_command
2729

2730
    def fix_gdb(self):
2731
        """
2732
        It seems that invoking either 'cy exec' and 'py-exec' work perfectly
2733
        fine, but after this gdb's python API is entirely broken.
2734
        Maybe some uncleared exception value is still set?
2735
        sys.exc_clear() didn't help. A demonstration:
2736

2737
        (gdb) cy exec 'hello'
2738
        'hello'
2739
        (gdb) python gdb.execute('cont')
2740
        RuntimeError: Cannot convert value to int.
2741
        Error while executing Python code.
2742
        (gdb) python gdb.execute('cont')
2743
        [15148 refs]
2744

2745
        Program exited normally.
2746
        """
2747
        warnings.filterwarnings('ignore', r'.*', RuntimeWarning,
2748
                                re.escape(__name__))
2749
        try:
2750
            int(gdb.parse_and_eval("(void *) 0")) == 0
2751
        except RuntimeError:
2752
            pass
2753
        # warnings.resetwarnings()
2754

2755
    @dont_suppress_errors
2756
    def invoke(self, args, from_tty):
2757
        self.fix_gdb()
2758
        try:
2759
            gdb.execute('%s %s' % (self.actual_command, args))
2760
        except RuntimeError as e:
2761
            raise gdb.GdbError(str(e))
2762
        self.fix_gdb()
2763

2764

2765
def _evalcode_python(executor, code, input_type):
2766
    """
2767
    Execute Python code in the most recent stack frame.
2768
    """
2769
    global_dict = gdb.parse_and_eval('PyEval_GetGlobals()')
2770
    local_dict = gdb.parse_and_eval('PyEval_GetLocals()')
2771

2772
    if (pointervalue(global_dict) == 0 or pointervalue(local_dict) == 0):
2773
        raise gdb.GdbError("Unable to find the locals or globals of the "
2774
                           "most recent Python function (relative to the "
2775
                           "selected frame).")
2776

2777
    return executor.evalcode(code, input_type, global_dict, local_dict)
2778

2779

2780
class PyExec(gdb.Command):
2781

2782
    def readcode(self, expr):
2783
        if expr:
2784
            return expr, PythonCodeExecutor.Py_single_input
2785
        else:
2786
            lines = []
2787
            while True:
2788
                try:
2789
                    line = input('>')
2790
                except EOFError:
2791
                    break
2792
                else:
2793
                    if line.rstrip() == 'end':
2794
                        break
2795

2796
                    lines.append(line)
2797

2798
            return '\n'.join(lines), PythonCodeExecutor.Py_file_input
2799

2800
    @dont_suppress_errors
2801
    def invoke(self, expr, from_tty):
2802
        expr, input_type = self.readcode(expr)
2803
        executor = PythonCodeExecutor()
2804
        executor.xdecref(_evalcode_python(executor, input_type, global_dict, local_dict))
2805

2806

2807
gdb.execute('set breakpoint pending on')
2808

2809
if hasattr(gdb, 'GdbError'):
2810
     # Wrap py-step and py-next in gdb defines to make them repeatable.
2811
    py_step = PyStep('-py-step', PythonInfo())
2812
    py_next = PyNext('-py-next', PythonInfo())
2813
    register_defines()
2814
    py_finish = PyFinish('py-finish', PythonInfo())
2815
    py_run = PyRun('py-run', PythonInfo())
2816
    py_cont = PyCont('py-cont', PythonInfo())
2817

2818
    py_exec = FixGdbCommand('py-exec', '-py-exec')
2819
    _py_exec = PyExec("-py-exec", gdb.COMMAND_DATA, gdb.COMPLETE_NONE)
2820
else:
2821
    warnings.warn("Use gdb 7.2 or higher to use the py-exec command.")
2822

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.