(root)/
Python-3.11.7/
Tools/
gdb/
libpython.py
       1  #!/usr/bin/python
       2  '''
       3  From gdb 7 onwards, gdb's build can be configured --with-python, allowing gdb
       4  to be extended with Python code e.g. for library-specific data visualizations,
       5  such as for the C++ STL types.  Documentation on this API can be seen at:
       6  http://sourceware.org/gdb/current/onlinedocs/gdb/Python-API.html
       7  
       8  
       9  This python module deals with the case when the process being debugged (the
      10  "inferior process" in gdb parlance) is itself python, or more specifically,
      11  linked against libpython.  In this situation, almost every item of data is a
      12  (PyObject*), and having the debugger merely print their addresses is not very
      13  enlightening.
      14  
      15  This module embeds knowledge about the implementation details of libpython so
      16  that we can emit useful visualizations e.g. a string, a list, a dict, a frame
      17  giving file/line information and the state of local variables
      18  
      19  In particular, given a gdb.Value corresponding to a PyObject* in the inferior
      20  process, we can generate a "proxy value" within the gdb process.  For example,
      21  given a PyObject* in the inferior process that is in fact a PyListObject*
      22  holding three PyObject* that turn out to be PyBytesObject* instances, we can
      23  generate a proxy value within the gdb process that is a list of bytes
      24  instances:
      25    [b"foo", b"bar", b"baz"]
      26  
      27  Doing so can be expensive for complicated graphs of objects, and could take
      28  some time, so we also have a "write_repr" method that writes a representation
      29  of the data to a file-like object.  This allows us to stop the traversal by
      30  having the file-like object raise an exception if it gets too much data.
      31  
      32  With both "proxyval" and "write_repr" we keep track of the set of all addresses
      33  visited so far in the traversal, to avoid infinite recursion due to cycles in
      34  the graph of object references.
      35  
      36  We try to defer gdb.lookup_type() invocations for python types until as late as
      37  possible: for a dynamically linked python binary, when the process starts in
      38  the debugger, the libpython.so hasn't been dynamically loaded yet, so none of
      39  the type names are known to the debugger
      40  
      41  The module also extends gdb with some python-specific commands.
      42  '''
      43  
      44  import gdb
      45  import os
      46  import locale
      47  import sys
      48  
      49  
      50  # Look up the gdb.Type for some standard types:
      51  # Those need to be refreshed as types (pointer sizes) may change when
      52  # gdb loads different executables
      53  
      54  def _type_char_ptr():
      55      return gdb.lookup_type('char').pointer()  # char*
      56  
      57  
      58  def _type_unsigned_char_ptr():
      59      return gdb.lookup_type('unsigned char').pointer()  # unsigned char*
      60  
      61  
      62  def _type_unsigned_short_ptr():
      63      return gdb.lookup_type('unsigned short').pointer()
      64  
      65  
      66  def _type_unsigned_int_ptr():
      67      return gdb.lookup_type('unsigned int').pointer()
      68  
      69  
      70  def _sizeof_void_p():
      71      return gdb.lookup_type('void').pointer().sizeof
      72  
      73  
      74  Py_TPFLAGS_MANAGED_DICT      = (1 << 4)
      75  Py_TPFLAGS_HEAPTYPE          = (1 << 9)
      76  Py_TPFLAGS_LONG_SUBCLASS     = (1 << 24)
      77  Py_TPFLAGS_LIST_SUBCLASS     = (1 << 25)
      78  Py_TPFLAGS_TUPLE_SUBCLASS    = (1 << 26)
      79  Py_TPFLAGS_BYTES_SUBCLASS    = (1 << 27)
      80  Py_TPFLAGS_UNICODE_SUBCLASS  = (1 << 28)
      81  Py_TPFLAGS_DICT_SUBCLASS     = (1 << 29)
      82  Py_TPFLAGS_BASE_EXC_SUBCLASS = (1 << 30)
      83  Py_TPFLAGS_TYPE_SUBCLASS     = (1 << 31)
      84  
      85  
      86  MAX_OUTPUT_LEN=1024
      87  
      88  hexdigits = "0123456789abcdef"
      89  
      90  ENCODING = locale.getpreferredencoding()
      91  
      92  FRAME_INFO_OPTIMIZED_OUT = '(frame information optimized out)'
      93  UNABLE_READ_INFO_PYTHON_FRAME = 'Unable to read information on python frame'
      94  EVALFRAME = '_PyEval_EvalFrameDefault'
      95  
      96  class ESC[4;38;5;81mNullPyObjectPtr(ESC[4;38;5;149mRuntimeError):
      97      pass
      98  
      99  
     100  def safety_limit(val):
     101      # Given an integer value from the process being debugged, limit it to some
     102      # safety threshold so that arbitrary breakage within said process doesn't
     103      # break the gdb process too much (e.g. sizes of iterations, sizes of lists)
     104      return min(val, 1000)
     105  
     106  
     107  def safe_range(val):
     108      # As per range, but don't trust the value too much: cap it to a safety
     109      # threshold in case the data was corrupted
     110      return range(safety_limit(int(val)))
     111  
     112  try:
     113      os_fsencode = os.fsencode
     114  except AttributeError:
     115      def os_fsencode(filename):
     116          if not isinstance(filename, unicode):
     117              return filename
     118          encoding = sys.getfilesystemencoding()
     119          if encoding == 'mbcs':
     120              # mbcs doesn't support surrogateescape
     121              return filename.encode(encoding)
     122          encoded = []
     123          for char in filename:
     124              # surrogateescape error handler
     125              if 0xDC80 <= ord(char) <= 0xDCFF:
     126                  byte = chr(ord(char) - 0xDC00)
     127              else:
     128                  byte = char.encode(encoding)
     129              encoded.append(byte)
     130          return ''.join(encoded)
     131  
     132  class ESC[4;38;5;81mStringTruncated(ESC[4;38;5;149mRuntimeError):
     133      pass
     134  
     135  class ESC[4;38;5;81mTruncatedStringIO(ESC[4;38;5;149mobject):
     136      '''Similar to io.StringIO, but can truncate the output by raising a
     137      StringTruncated exception'''
     138      def __init__(self, maxlen=None):
     139          self._val = ''
     140          self.maxlen = maxlen
     141  
     142      def write(self, data):
     143          if self.maxlen:
     144              if len(data) + len(self._val) > self.maxlen:
     145                  # Truncation:
     146                  self._val += data[0:self.maxlen - len(self._val)]
     147                  raise StringTruncated()
     148  
     149          self._val += data
     150  
     151      def getvalue(self):
     152          return self._val
     153  
     154  class ESC[4;38;5;81mPyObjectPtr(ESC[4;38;5;149mobject):
     155      """
     156      Class wrapping a gdb.Value that's either a (PyObject*) within the
     157      inferior process, or some subclass pointer e.g. (PyBytesObject*)
     158  
     159      There will be a subclass for every refined PyObject type that we care
     160      about.
     161  
     162      Note that at every stage the underlying pointer could be NULL, point
     163      to corrupt data, etc; this is the debugger, after all.
     164      """
     165      _typename = 'PyObject'
     166  
     167      def __init__(self, gdbval, cast_to=None):
     168          if cast_to:
     169              self._gdbval = gdbval.cast(cast_to)
     170          else:
     171              self._gdbval = gdbval
     172  
     173      def field(self, name):
     174          '''
     175          Get the gdb.Value for the given field within the PyObject, coping with
     176          some python 2 versus python 3 differences.
     177  
     178          Various libpython types are defined using the "PyObject_HEAD" and
     179          "PyObject_VAR_HEAD" macros.
     180  
     181          In Python 2, this these are defined so that "ob_type" and (for a var
     182          object) "ob_size" are fields of the type in question.
     183  
     184          In Python 3, this is defined as an embedded PyVarObject type thus:
     185             PyVarObject ob_base;
     186          so that the "ob_size" field is located insize the "ob_base" field, and
     187          the "ob_type" is most easily accessed by casting back to a (PyObject*).
     188          '''
     189          if self.is_null():
     190              raise NullPyObjectPtr(self)
     191  
     192          if name == 'ob_type':
     193              pyo_ptr = self._gdbval.cast(PyObjectPtr.get_gdb_type())
     194              return pyo_ptr.dereference()[name]
     195  
     196          if name == 'ob_size':
     197              pyo_ptr = self._gdbval.cast(PyVarObjectPtr.get_gdb_type())
     198              return pyo_ptr.dereference()[name]
     199  
     200          # General case: look it up inside the object:
     201          return self._gdbval.dereference()[name]
     202  
     203      def pyop_field(self, name):
     204          '''
     205          Get a PyObjectPtr for the given PyObject* field within this PyObject,
     206          coping with some python 2 versus python 3 differences.
     207          '''
     208          return PyObjectPtr.from_pyobject_ptr(self.field(name))
     209  
     210      def write_field_repr(self, name, out, visited):
     211          '''
     212          Extract the PyObject* field named "name", and write its representation
     213          to file-like object "out"
     214          '''
     215          field_obj = self.pyop_field(name)
     216          field_obj.write_repr(out, visited)
     217  
     218      def get_truncated_repr(self, maxlen):
     219          '''
     220          Get a repr-like string for the data, but truncate it at "maxlen" bytes
     221          (ending the object graph traversal as soon as you do)
     222          '''
     223          out = TruncatedStringIO(maxlen)
     224          try:
     225              self.write_repr(out, set())
     226          except StringTruncated:
     227              # Truncation occurred:
     228              return out.getvalue() + '...(truncated)'
     229  
     230          # No truncation occurred:
     231          return out.getvalue()
     232  
     233      def type(self):
     234          return PyTypeObjectPtr(self.field('ob_type'))
     235  
     236      def is_null(self):
     237          return 0 == int(self._gdbval)
     238  
     239      def is_optimized_out(self):
     240          '''
     241          Is the value of the underlying PyObject* visible to the debugger?
     242  
     243          This can vary with the precise version of the compiler used to build
     244          Python, and the precise version of gdb.
     245  
     246          See e.g. https://bugzilla.redhat.com/show_bug.cgi?id=556975 with
     247          PyEval_EvalFrameEx's "f"
     248          '''
     249          return self._gdbval.is_optimized_out
     250  
     251      def safe_tp_name(self):
     252          try:
     253              ob_type = self.type()
     254              tp_name = ob_type.field('tp_name')
     255              return tp_name.string()
     256          # NullPyObjectPtr: NULL tp_name?
     257          # RuntimeError: Can't even read the object at all?
     258          # UnicodeDecodeError: Failed to decode tp_name bytestring
     259          except (NullPyObjectPtr, RuntimeError, UnicodeDecodeError):
     260              return 'unknown'
     261  
     262      def proxyval(self, visited):
     263          '''
     264          Scrape a value from the inferior process, and try to represent it
     265          within the gdb process, whilst (hopefully) avoiding crashes when
     266          the remote data is corrupt.
     267  
     268          Derived classes will override this.
     269  
     270          For example, a PyIntObject* with ob_ival 42 in the inferior process
     271          should result in an int(42) in this process.
     272  
     273          visited: a set of all gdb.Value pyobject pointers already visited
     274          whilst generating this value (to guard against infinite recursion when
     275          visiting object graphs with loops).  Analogous to Py_ReprEnter and
     276          Py_ReprLeave
     277          '''
     278  
     279          class ESC[4;38;5;81mFakeRepr(ESC[4;38;5;149mobject):
     280              """
     281              Class representing a non-descript PyObject* value in the inferior
     282              process for when we don't have a custom scraper, intended to have
     283              a sane repr().
     284              """
     285  
     286              def __init__(self, tp_name, address):
     287                  self.tp_name = tp_name
     288                  self.address = address
     289  
     290              def __repr__(self):
     291                  # For the NULL pointer, we have no way of knowing a type, so
     292                  # special-case it as per
     293                  # http://bugs.python.org/issue8032#msg100882
     294                  if self.address == 0:
     295                      return '0x0'
     296                  return '<%s at remote 0x%x>' % (self.tp_name, self.address)
     297  
     298          return FakeRepr(self.safe_tp_name(),
     299                          int(self._gdbval))
     300  
     301      def write_repr(self, out, visited):
     302          '''
     303          Write a string representation of the value scraped from the inferior
     304          process to "out", a file-like object.
     305          '''
     306          # Default implementation: generate a proxy value and write its repr
     307          # However, this could involve a lot of work for complicated objects,
     308          # so for derived classes we specialize this
     309          return out.write(repr(self.proxyval(visited)))
     310  
     311      @classmethod
     312      def subclass_from_type(cls, t):
     313          '''
     314          Given a PyTypeObjectPtr instance wrapping a gdb.Value that's a
     315          (PyTypeObject*), determine the corresponding subclass of PyObjectPtr
     316          to use
     317  
     318          Ideally, we would look up the symbols for the global types, but that
     319          isn't working yet:
     320            (gdb) python print gdb.lookup_symbol('PyList_Type')[0].value
     321            Traceback (most recent call last):
     322              File "<string>", line 1, in <module>
     323            NotImplementedError: Symbol type not yet supported in Python scripts.
     324            Error while executing Python code.
     325  
     326          For now, we use tp_flags, after doing some string comparisons on the
     327          tp_name for some special-cases that don't seem to be visible through
     328          flags
     329          '''
     330          try:
     331              tp_name = t.field('tp_name').string()
     332              tp_flags = int(t.field('tp_flags'))
     333          # RuntimeError: NULL pointers
     334          # UnicodeDecodeError: string() fails to decode the bytestring
     335          except (RuntimeError, UnicodeDecodeError):
     336              # Handle any kind of error e.g. NULL ptrs by simply using the base
     337              # class
     338              return cls
     339  
     340          #print('tp_flags = 0x%08x' % tp_flags)
     341          #print('tp_name = %r' % tp_name)
     342  
     343          name_map = {'bool': PyBoolObjectPtr,
     344                      'classobj': PyClassObjectPtr,
     345                      'NoneType': PyNoneStructPtr,
     346                      'frame': PyFrameObjectPtr,
     347                      'set' : PySetObjectPtr,
     348                      'frozenset' : PySetObjectPtr,
     349                      'builtin_function_or_method' : PyCFunctionObjectPtr,
     350                      'method-wrapper': wrapperobject,
     351                      }
     352          if tp_name in name_map:
     353              return name_map[tp_name]
     354  
     355          if tp_flags & Py_TPFLAGS_HEAPTYPE:
     356              return HeapTypeObjectPtr
     357  
     358          if tp_flags & Py_TPFLAGS_LONG_SUBCLASS:
     359              return PyLongObjectPtr
     360          if tp_flags & Py_TPFLAGS_LIST_SUBCLASS:
     361              return PyListObjectPtr
     362          if tp_flags & Py_TPFLAGS_TUPLE_SUBCLASS:
     363              return PyTupleObjectPtr
     364          if tp_flags & Py_TPFLAGS_BYTES_SUBCLASS:
     365              return PyBytesObjectPtr
     366          if tp_flags & Py_TPFLAGS_UNICODE_SUBCLASS:
     367              return PyUnicodeObjectPtr
     368          if tp_flags & Py_TPFLAGS_DICT_SUBCLASS:
     369              return PyDictObjectPtr
     370          if tp_flags & Py_TPFLAGS_BASE_EXC_SUBCLASS:
     371              return PyBaseExceptionObjectPtr
     372          #if tp_flags & Py_TPFLAGS_TYPE_SUBCLASS:
     373          #    return PyTypeObjectPtr
     374  
     375          # Use the base class:
     376          return cls
     377  
     378      @classmethod
     379      def from_pyobject_ptr(cls, gdbval):
     380          '''
     381          Try to locate the appropriate derived class dynamically, and cast
     382          the pointer accordingly.
     383          '''
     384          try:
     385              p = PyObjectPtr(gdbval)
     386              cls = cls.subclass_from_type(p.type())
     387              return cls(gdbval, cast_to=cls.get_gdb_type())
     388          except RuntimeError:
     389              # Handle any kind of error e.g. NULL ptrs by simply using the base
     390              # class
     391              pass
     392          return cls(gdbval)
     393  
     394      @classmethod
     395      def get_gdb_type(cls):
     396          return gdb.lookup_type(cls._typename).pointer()
     397  
     398      def as_address(self):
     399          return int(self._gdbval)
     400  
     401  class ESC[4;38;5;81mPyVarObjectPtr(ESC[4;38;5;149mPyObjectPtr):
     402      _typename = 'PyVarObject'
     403  
     404  class ESC[4;38;5;81mProxyAlreadyVisited(ESC[4;38;5;149mobject):
     405      '''
     406      Placeholder proxy to use when protecting against infinite recursion due to
     407      loops in the object graph.
     408  
     409      Analogous to the values emitted by the users of Py_ReprEnter and Py_ReprLeave
     410      '''
     411      def __init__(self, rep):
     412          self._rep = rep
     413  
     414      def __repr__(self):
     415          return self._rep
     416  
     417  
     418  def _write_instance_repr(out, visited, name, pyop_attrdict, address):
     419      '''Shared code for use by all classes:
     420      write a representation to file-like object "out"'''
     421      out.write('<')
     422      out.write(name)
     423  
     424      # Write dictionary of instance attributes:
     425      if isinstance(pyop_attrdict, (PyKeysValuesPair, PyDictObjectPtr)):
     426          out.write('(')
     427          first = True
     428          items = pyop_attrdict.iteritems()
     429          for pyop_arg, pyop_val in items:
     430              if not first:
     431                  out.write(', ')
     432              first = False
     433              out.write(pyop_arg.proxyval(visited))
     434              out.write('=')
     435              pyop_val.write_repr(out, visited)
     436          out.write(')')
     437      out.write(' at remote 0x%x>' % address)
     438  
     439  
     440  class ESC[4;38;5;81mInstanceProxy(ESC[4;38;5;149mobject):
     441  
     442      def __init__(self, cl_name, attrdict, address):
     443          self.cl_name = cl_name
     444          self.attrdict = attrdict
     445          self.address = address
     446  
     447      def __repr__(self):
     448          if isinstance(self.attrdict, dict):
     449              kwargs = ', '.join(["%s=%r" % (arg, val)
     450                                  for arg, val in self.attrdict.items()])
     451              return '<%s(%s) at remote 0x%x>' % (self.cl_name,
     452                                                  kwargs, self.address)
     453          else:
     454              return '<%s at remote 0x%x>' % (self.cl_name,
     455                                              self.address)
     456  
     457  def _PyObject_VAR_SIZE(typeobj, nitems):
     458      if _PyObject_VAR_SIZE._type_size_t is None:
     459          _PyObject_VAR_SIZE._type_size_t = gdb.lookup_type('size_t')
     460  
     461      return ( ( typeobj.field('tp_basicsize') +
     462                 nitems * typeobj.field('tp_itemsize') +
     463                 (_sizeof_void_p() - 1)
     464               ) & ~(_sizeof_void_p() - 1)
     465             ).cast(_PyObject_VAR_SIZE._type_size_t)
     466  _PyObject_VAR_SIZE._type_size_t = None
     467  
     468  class ESC[4;38;5;81mHeapTypeObjectPtr(ESC[4;38;5;149mPyObjectPtr):
     469      _typename = 'PyObject'
     470  
     471      def get_attr_dict(self):
     472          '''
     473          Get the PyDictObject ptr representing the attribute dictionary
     474          (or None if there's a problem)
     475          '''
     476          try:
     477              typeobj = self.type()
     478              dictoffset = int_from_int(typeobj.field('tp_dictoffset'))
     479              if dictoffset != 0:
     480                  if dictoffset < 0:
     481                      type_PyVarObject_ptr = gdb.lookup_type('PyVarObject').pointer()
     482                      tsize = int_from_int(self._gdbval.cast(type_PyVarObject_ptr)['ob_size'])
     483                      if tsize < 0:
     484                          tsize = -tsize
     485                      size = _PyObject_VAR_SIZE(typeobj, tsize)
     486                      dictoffset += size
     487                      assert dictoffset % _sizeof_void_p() == 0
     488  
     489                  dictptr = self._gdbval.cast(_type_char_ptr()) + dictoffset
     490                  PyObjectPtrPtr = PyObjectPtr.get_gdb_type().pointer()
     491                  dictptr = dictptr.cast(PyObjectPtrPtr)
     492                  return PyObjectPtr.from_pyobject_ptr(dictptr.dereference())
     493          except RuntimeError:
     494              # Corrupt data somewhere; fail safe
     495              pass
     496  
     497          # Not found, or some kind of error:
     498          return None
     499  
     500      def get_keys_values(self):
     501          typeobj = self.type()
     502          has_values =  int_from_int(typeobj.field('tp_flags')) & Py_TPFLAGS_MANAGED_DICT
     503          if not has_values:
     504              return None
     505          PyDictValuesPtrPtr = gdb.lookup_type("PyDictValues").pointer().pointer()
     506          valuesptr = self._gdbval.cast(PyDictValuesPtrPtr) - 4
     507          values = valuesptr.dereference()
     508          if int(values) == 0:
     509              return None
     510          values = values['values']
     511          return PyKeysValuesPair(self.get_cached_keys(), values)
     512  
     513      def get_cached_keys(self):
     514          typeobj = self.type()
     515          HeapTypePtr = gdb.lookup_type("PyHeapTypeObject").pointer()
     516          return typeobj._gdbval.cast(HeapTypePtr)['ht_cached_keys']
     517  
     518      def proxyval(self, visited):
     519          '''
     520          Support for classes.
     521  
     522          Currently we just locate the dictionary using a transliteration to
     523          python of _PyObject_GetDictPtr, ignoring descriptors
     524          '''
     525          # Guard against infinite loops:
     526          if self.as_address() in visited:
     527              return ProxyAlreadyVisited('<...>')
     528          visited.add(self.as_address())
     529  
     530          pyop_attr_dict = self.get_attr_dict()
     531          keys_values = self.get_keys_values()
     532          if keys_values:
     533              attr_dict = keys_values.proxyval(visited)
     534          elif pyop_attr_dict:
     535              attr_dict = pyop_attr_dict.proxyval(visited)
     536          else:
     537              attr_dict = {}
     538          tp_name = self.safe_tp_name()
     539  
     540          # Class:
     541          return InstanceProxy(tp_name, attr_dict, int(self._gdbval))
     542  
     543      def write_repr(self, out, visited):
     544          # Guard against infinite loops:
     545          if self.as_address() in visited:
     546              out.write('<...>')
     547              return
     548          visited.add(self.as_address())
     549  
     550          pyop_attrs = self.get_keys_values()
     551          if not pyop_attrs:
     552              pyop_attrs = self.get_attr_dict()
     553          _write_instance_repr(out, visited,
     554                               self.safe_tp_name(), pyop_attrs, self.as_address())
     555  
     556  class ESC[4;38;5;81mProxyException(ESC[4;38;5;149mException):
     557      def __init__(self, tp_name, args):
     558          self.tp_name = tp_name
     559          self.args = args
     560  
     561      def __repr__(self):
     562          return '%s%r' % (self.tp_name, self.args)
     563  
     564  class ESC[4;38;5;81mPyBaseExceptionObjectPtr(ESC[4;38;5;149mPyObjectPtr):
     565      """
     566      Class wrapping a gdb.Value that's a PyBaseExceptionObject* i.e. an exception
     567      within the process being debugged.
     568      """
     569      _typename = 'PyBaseExceptionObject'
     570  
     571      def proxyval(self, visited):
     572          # Guard against infinite loops:
     573          if self.as_address() in visited:
     574              return ProxyAlreadyVisited('(...)')
     575          visited.add(self.as_address())
     576          arg_proxy = self.pyop_field('args').proxyval(visited)
     577          return ProxyException(self.safe_tp_name(),
     578                                arg_proxy)
     579  
     580      def write_repr(self, out, visited):
     581          # Guard against infinite loops:
     582          if self.as_address() in visited:
     583              out.write('(...)')
     584              return
     585          visited.add(self.as_address())
     586  
     587          out.write(self.safe_tp_name())
     588          self.write_field_repr('args', out, visited)
     589  
     590  class ESC[4;38;5;81mPyClassObjectPtr(ESC[4;38;5;149mPyObjectPtr):
     591      """
     592      Class wrapping a gdb.Value that's a PyClassObject* i.e. a <classobj>
     593      instance within the process being debugged.
     594      """
     595      _typename = 'PyClassObject'
     596  
     597  
     598  class ESC[4;38;5;81mBuiltInFunctionProxy(ESC[4;38;5;149mobject):
     599      def __init__(self, ml_name):
     600          self.ml_name = ml_name
     601  
     602      def __repr__(self):
     603          return "<built-in function %s>" % self.ml_name
     604  
     605  class ESC[4;38;5;81mBuiltInMethodProxy(ESC[4;38;5;149mobject):
     606      def __init__(self, ml_name, pyop_m_self):
     607          self.ml_name = ml_name
     608          self.pyop_m_self = pyop_m_self
     609  
     610      def __repr__(self):
     611          return ('<built-in method %s of %s object at remote 0x%x>'
     612                  % (self.ml_name,
     613                     self.pyop_m_self.safe_tp_name(),
     614                     self.pyop_m_self.as_address())
     615                  )
     616  
     617  class ESC[4;38;5;81mPyCFunctionObjectPtr(ESC[4;38;5;149mPyObjectPtr):
     618      """
     619      Class wrapping a gdb.Value that's a PyCFunctionObject*
     620      (see Include/methodobject.h and Objects/methodobject.c)
     621      """
     622      _typename = 'PyCFunctionObject'
     623  
     624      def proxyval(self, visited):
     625          m_ml = self.field('m_ml') # m_ml is a (PyMethodDef*)
     626          try:
     627              ml_name = m_ml['ml_name'].string()
     628          except UnicodeDecodeError:
     629              ml_name = '<ml_name:UnicodeDecodeError>'
     630  
     631          pyop_m_self = self.pyop_field('m_self')
     632          if pyop_m_self.is_null():
     633              return BuiltInFunctionProxy(ml_name)
     634          else:
     635              return BuiltInMethodProxy(ml_name, pyop_m_self)
     636  
     637  # Python implementation of location table parsing algorithm
     638  def read(it):
     639      return ord(next(it))
     640  
     641  def read_varint(it):
     642      b = read(it)
     643      val = b & 63;
     644      shift = 0;
     645      while b & 64:
     646          b = read(it)
     647          shift += 6
     648          val |= (b&63) << shift
     649      return val
     650  
     651  def read_signed_varint(it):
     652      uval = read_varint(it)
     653      if uval & 1:
     654          return -(uval >> 1)
     655      else:
     656          return uval >> 1
     657  
     658  def parse_location_table(firstlineno, linetable):
     659      line = firstlineno
     660      addr = 0
     661      it = iter(linetable)
     662      while True:
     663          try:
     664              first_byte = read(it)
     665          except StopIteration:
     666              return
     667          code = (first_byte >> 3) & 15
     668          length = (first_byte & 7) + 1
     669          end_addr = addr + length
     670          if code == 15:
     671              yield addr, end_addr, None
     672              addr = end_addr
     673              continue
     674          elif code == 14: # Long form
     675              line_delta = read_signed_varint(it)
     676              line += line_delta
     677              end_line = line + read_varint(it)
     678              col = read_varint(it)
     679              end_col = read_varint(it)
     680          elif code == 13: # No column
     681              line_delta = read_signed_varint(it)
     682              line += line_delta
     683          elif code in (10, 11, 12): # new line
     684              line_delta = code - 10
     685              line += line_delta
     686              column = read(it)
     687              end_column = read(it)
     688          else:
     689              assert (0 <= code < 10)
     690              second_byte = read(it)
     691              column = code << 3 | (second_byte >> 4)
     692          yield addr, end_addr, line
     693          addr = end_addr
     694  
     695  class ESC[4;38;5;81mPyCodeObjectPtr(ESC[4;38;5;149mPyObjectPtr):
     696      """
     697      Class wrapping a gdb.Value that's a PyCodeObject* i.e. a <code> instance
     698      within the process being debugged.
     699      """
     700      _typename = 'PyCodeObject'
     701  
     702      def addr2line(self, addrq):
     703          '''
     704          Get the line number for a given bytecode offset
     705  
     706          Analogous to PyCode_Addr2Line; translated from pseudocode in
     707          Objects/lnotab_notes.txt
     708          '''
     709          co_linetable = self.pyop_field('co_linetable').proxyval(set())
     710  
     711          # Initialize lineno to co_firstlineno as per PyCode_Addr2Line
     712          # not 0, as lnotab_notes.txt has it:
     713          lineno = int_from_int(self.field('co_firstlineno'))
     714  
     715          if addrq < 0:
     716              return lineno
     717          addr = 0
     718          for addr, end_addr, line in parse_location_table(lineno, co_linetable):
     719              if addr <= addrq and end_addr > addrq:
     720                  return line
     721          assert False, "Unreachable"
     722  
     723  
     724  def items_from_keys_and_values(keys, values):
     725      entries, nentries = PyDictObjectPtr._get_entries(keys)
     726      for i in safe_range(nentries):
     727          ep = entries[i]
     728          pyop_value = PyObjectPtr.from_pyobject_ptr(values[i])
     729          if not pyop_value.is_null():
     730              pyop_key = PyObjectPtr.from_pyobject_ptr(ep['me_key'])
     731              yield (pyop_key, pyop_value)
     732  
     733  class ESC[4;38;5;81mPyKeysValuesPair:
     734  
     735      def __init__(self, keys, values):
     736          self.keys = keys
     737          self.values = values
     738  
     739      def iteritems(self):
     740          return items_from_keys_and_values(self.keys, self.values)
     741  
     742      def proxyval(self, visited):
     743          result = {}
     744          for pyop_key, pyop_value in self.iteritems():
     745              proxy_key = pyop_key.proxyval(visited)
     746              proxy_value = pyop_value.proxyval(visited)
     747              result[proxy_key] = proxy_value
     748          return result
     749  
     750  class ESC[4;38;5;81mPyDictObjectPtr(ESC[4;38;5;149mPyObjectPtr):
     751      """
     752      Class wrapping a gdb.Value that's a PyDictObject* i.e. a dict instance
     753      within the process being debugged.
     754      """
     755      _typename = 'PyDictObject'
     756  
     757      def iteritems(self):
     758          '''
     759          Yields a sequence of (PyObjectPtr key, PyObjectPtr value) pairs,
     760          analogous to dict.iteritems()
     761          '''
     762          keys = self.field('ma_keys')
     763          values = self.field('ma_values')
     764          has_values = int(values)
     765          if has_values:
     766              values = values['values']
     767          if has_values:
     768              for item in items_from_keys_and_values(keys, values):
     769                  yield item
     770              return
     771          entries, nentries = self._get_entries(keys)
     772          for i in safe_range(nentries):
     773              ep = entries[i]
     774              pyop_value = PyObjectPtr.from_pyobject_ptr(ep['me_value'])
     775              if not pyop_value.is_null():
     776                  pyop_key = PyObjectPtr.from_pyobject_ptr(ep['me_key'])
     777                  yield (pyop_key, pyop_value)
     778  
     779      def proxyval(self, visited):
     780          # Guard against infinite loops:
     781          if self.as_address() in visited:
     782              return ProxyAlreadyVisited('{...}')
     783          visited.add(self.as_address())
     784  
     785          result = {}
     786          for pyop_key, pyop_value in self.iteritems():
     787              proxy_key = pyop_key.proxyval(visited)
     788              proxy_value = pyop_value.proxyval(visited)
     789              result[proxy_key] = proxy_value
     790          return result
     791  
     792      def write_repr(self, out, visited):
     793          # Guard against infinite loops:
     794          if self.as_address() in visited:
     795              out.write('{...}')
     796              return
     797          visited.add(self.as_address())
     798  
     799          out.write('{')
     800          first = True
     801          for pyop_key, pyop_value in self.iteritems():
     802              if not first:
     803                  out.write(', ')
     804              first = False
     805              pyop_key.write_repr(out, visited)
     806              out.write(': ')
     807              pyop_value.write_repr(out, visited)
     808          out.write('}')
     809  
     810      @staticmethod
     811      def _get_entries(keys):
     812          dk_nentries = int(keys['dk_nentries'])
     813          dk_size = 1<<int(keys['dk_log2_size'])
     814  
     815          if dk_size <= 0xFF:
     816              offset = dk_size
     817          elif dk_size <= 0xFFFF:
     818              offset = 2 * dk_size
     819          elif dk_size <= 0xFFFFFFFF:
     820              offset = 4 * dk_size
     821          else:
     822              offset = 8 * dk_size
     823  
     824          ent_addr = keys['dk_indices'].address
     825          ent_addr = ent_addr.cast(_type_unsigned_char_ptr()) + offset
     826          if int(keys['dk_kind']) == 0:  # DICT_KEYS_GENERAL
     827              ent_ptr_t = gdb.lookup_type('PyDictKeyEntry').pointer()
     828          else:
     829              ent_ptr_t = gdb.lookup_type('PyDictUnicodeEntry').pointer()
     830          ent_addr = ent_addr.cast(ent_ptr_t)
     831  
     832          return ent_addr, dk_nentries
     833  
     834  
     835  class ESC[4;38;5;81mPyListObjectPtr(ESC[4;38;5;149mPyObjectPtr):
     836      _typename = 'PyListObject'
     837  
     838      def __getitem__(self, i):
     839          # Get the gdb.Value for the (PyObject*) with the given index:
     840          field_ob_item = self.field('ob_item')
     841          return field_ob_item[i]
     842  
     843      def proxyval(self, visited):
     844          # Guard against infinite loops:
     845          if self.as_address() in visited:
     846              return ProxyAlreadyVisited('[...]')
     847          visited.add(self.as_address())
     848  
     849          result = [PyObjectPtr.from_pyobject_ptr(self[i]).proxyval(visited)
     850                    for i in safe_range(int_from_int(self.field('ob_size')))]
     851          return result
     852  
     853      def write_repr(self, out, visited):
     854          # Guard against infinite loops:
     855          if self.as_address() in visited:
     856              out.write('[...]')
     857              return
     858          visited.add(self.as_address())
     859  
     860          out.write('[')
     861          for i in safe_range(int_from_int(self.field('ob_size'))):
     862              if i > 0:
     863                  out.write(', ')
     864              element = PyObjectPtr.from_pyobject_ptr(self[i])
     865              element.write_repr(out, visited)
     866          out.write(']')
     867  
     868  class ESC[4;38;5;81mPyLongObjectPtr(ESC[4;38;5;149mPyObjectPtr):
     869      _typename = 'PyLongObject'
     870  
     871      def proxyval(self, visited):
     872          '''
     873          Python's Include/longobjrep.h has this declaration:
     874             struct _longobject {
     875                 PyObject_VAR_HEAD
     876                 digit ob_digit[1];
     877             };
     878  
     879          with this description:
     880              The absolute value of a number is equal to
     881                   SUM(for i=0 through abs(ob_size)-1) ob_digit[i] * 2**(SHIFT*i)
     882              Negative numbers are represented with ob_size < 0;
     883              zero is represented by ob_size == 0.
     884  
     885          where SHIFT can be either:
     886              #define PyLong_SHIFT        30
     887              #define PyLong_SHIFT        15
     888          '''
     889          ob_size = int(self.field('ob_size'))
     890          if ob_size == 0:
     891              return 0
     892  
     893          ob_digit = self.field('ob_digit')
     894  
     895          if gdb.lookup_type('digit').sizeof == 2:
     896              SHIFT = 15
     897          else:
     898              SHIFT = 30
     899  
     900          digits = [int(ob_digit[i]) * 2**(SHIFT*i)
     901                    for i in safe_range(abs(ob_size))]
     902          result = sum(digits)
     903          if ob_size < 0:
     904              result = -result
     905          return result
     906  
     907      def write_repr(self, out, visited):
     908          # Write this out as a Python 3 int literal, i.e. without the "L" suffix
     909          proxy = self.proxyval(visited)
     910          out.write("%s" % proxy)
     911  
     912  
     913  class ESC[4;38;5;81mPyBoolObjectPtr(ESC[4;38;5;149mPyLongObjectPtr):
     914      """
     915      Class wrapping a gdb.Value that's a PyBoolObject* i.e. one of the two
     916      <bool> instances (Py_True/Py_False) within the process being debugged.
     917      """
     918      def proxyval(self, visited):
     919          if PyLongObjectPtr.proxyval(self, visited):
     920              return True
     921          else:
     922              return False
     923  
     924  class ESC[4;38;5;81mPyNoneStructPtr(ESC[4;38;5;149mPyObjectPtr):
     925      """
     926      Class wrapping a gdb.Value that's a PyObject* pointing to the
     927      singleton (we hope) _Py_NoneStruct with ob_type PyNone_Type
     928      """
     929      _typename = 'PyObject'
     930  
     931      def proxyval(self, visited):
     932          return None
     933  
     934  class ESC[4;38;5;81mPyFrameObjectPtr(ESC[4;38;5;149mPyObjectPtr):
     935      _typename = 'PyFrameObject'
     936  
     937      def __init__(self, gdbval, cast_to=None):
     938          PyObjectPtr.__init__(self, gdbval, cast_to)
     939  
     940          if not self.is_optimized_out():
     941              self._frame = PyFramePtr(self.field('f_frame'))
     942  
     943      def iter_locals(self):
     944          '''
     945          Yield a sequence of (name,value) pairs of PyObjectPtr instances, for
     946          the local variables of this frame
     947          '''
     948          if self.is_optimized_out():
     949              return
     950          return self._frame.iter_locals()
     951  
     952      def iter_globals(self):
     953          '''
     954          Yield a sequence of (name,value) pairs of PyObjectPtr instances, for
     955          the global variables of this frame
     956          '''
     957          if self.is_optimized_out():
     958              return ()
     959          return self._frame.iter_globals()
     960  
     961      def iter_builtins(self):
     962          '''
     963          Yield a sequence of (name,value) pairs of PyObjectPtr instances, for
     964          the builtin variables
     965          '''
     966          if self.is_optimized_out():
     967              return ()
     968          return self._frame.iter_builtins()
     969  
     970      def get_var_by_name(self, name):
     971  
     972          if self.is_optimized_out():
     973              return None, None
     974          return self._frame.get_var_by_name(name)
     975  
     976      def filename(self):
     977          '''Get the path of the current Python source file, as a string'''
     978          if self.is_optimized_out():
     979              return FRAME_INFO_OPTIMIZED_OUT
     980          return self._frame.filename()
     981  
     982      def current_line_num(self):
     983          '''Get current line number as an integer (1-based)
     984  
     985          Translated from PyFrame_GetLineNumber and PyCode_Addr2Line
     986  
     987          See Objects/lnotab_notes.txt
     988          '''
     989          if self.is_optimized_out():
     990              return None
     991          return self._frame.current_line_num()
     992  
     993      def current_line(self):
     994          '''Get the text of the current source line as a string, with a trailing
     995          newline character'''
     996          if self.is_optimized_out():
     997              return FRAME_INFO_OPTIMIZED_OUT
     998          return self._frame.current_line()
     999  
    1000      def write_repr(self, out, visited):
    1001          if self.is_optimized_out():
    1002              out.write(FRAME_INFO_OPTIMIZED_OUT)
    1003              return
    1004          return self._frame.write_repr(out, visited)
    1005  
    1006      def print_traceback(self):
    1007          if self.is_optimized_out():
    1008              sys.stdout.write('  %s\n' % FRAME_INFO_OPTIMIZED_OUT)
    1009              return
    1010          return self._frame.print_traceback()
    1011  
    1012  class ESC[4;38;5;81mPyFramePtr:
    1013  
    1014      def __init__(self, gdbval):
    1015          self._gdbval = gdbval
    1016  
    1017          if not self.is_optimized_out():
    1018              self.co = self._f_code()
    1019              self.co_name = self.co.pyop_field('co_name')
    1020              self.co_filename = self.co.pyop_field('co_filename')
    1021  
    1022              self.f_lasti = self._f_lasti()
    1023              self.co_nlocals = int_from_int(self.co.field('co_nlocals'))
    1024              pnames = self.co.field('co_localsplusnames')
    1025              self.co_localsplusnames = PyTupleObjectPtr.from_pyobject_ptr(pnames)
    1026  
    1027      def is_optimized_out(self):
    1028          return self._gdbval.is_optimized_out
    1029  
    1030      def iter_locals(self):
    1031          '''
    1032          Yield a sequence of (name,value) pairs of PyObjectPtr instances, for
    1033          the local variables of this frame
    1034          '''
    1035          if self.is_optimized_out():
    1036              return
    1037  
    1038  
    1039          obj_ptr_ptr = gdb.lookup_type("PyObject").pointer().pointer()
    1040  
    1041          localsplus = self._gdbval["localsplus"].cast(obj_ptr_ptr)
    1042  
    1043          for i in safe_range(self.co_nlocals):
    1044              pyop_value = PyObjectPtr.from_pyobject_ptr(localsplus[i])
    1045              if pyop_value.is_null():
    1046                  continue
    1047              pyop_name = PyObjectPtr.from_pyobject_ptr(self.co_localsplusnames[i])
    1048              yield (pyop_name, pyop_value)
    1049  
    1050      def _f_special(self, name, convert=PyObjectPtr.from_pyobject_ptr):
    1051          return convert(self._gdbval[name])
    1052  
    1053      def _f_globals(self):
    1054          return self._f_special("f_globals")
    1055  
    1056      def _f_builtins(self):
    1057          return self._f_special("f_builtins")
    1058  
    1059      def _f_code(self):
    1060          return self._f_special("f_code", PyCodeObjectPtr.from_pyobject_ptr)
    1061  
    1062      def _f_nlocalsplus(self):
    1063          return self._f_special("nlocalsplus", int_from_int)
    1064  
    1065      def _f_lasti(self):
    1066          codeunit_p = gdb.lookup_type("_Py_CODEUNIT").pointer()
    1067          prev_instr = self._gdbval["prev_instr"]
    1068          first_instr = self._f_code().field("co_code_adaptive").cast(codeunit_p)
    1069          return int(prev_instr - first_instr)
    1070  
    1071      def is_entry(self):
    1072          return self._f_special("is_entry", bool)
    1073  
    1074      def previous(self):
    1075          return self._f_special("previous", PyFramePtr)
    1076  
    1077      def iter_globals(self):
    1078          '''
    1079          Yield a sequence of (name,value) pairs of PyObjectPtr instances, for
    1080          the global variables of this frame
    1081          '''
    1082          if self.is_optimized_out():
    1083              return ()
    1084  
    1085          pyop_globals = self._f_globals()
    1086          return pyop_globals.iteritems()
    1087  
    1088      def iter_builtins(self):
    1089          '''
    1090          Yield a sequence of (name,value) pairs of PyObjectPtr instances, for
    1091          the builtin variables
    1092          '''
    1093          if self.is_optimized_out():
    1094              return ()
    1095  
    1096          pyop_builtins = self._f_builtins()
    1097          return pyop_builtins.iteritems()
    1098  
    1099      def get_var_by_name(self, name):
    1100          '''
    1101          Look for the named local variable, returning a (PyObjectPtr, scope) pair
    1102          where scope is a string 'local', 'global', 'builtin'
    1103  
    1104          If not found, return (None, None)
    1105          '''
    1106          for pyop_name, pyop_value in self.iter_locals():
    1107              if name == pyop_name.proxyval(set()):
    1108                  return pyop_value, 'local'
    1109          for pyop_name, pyop_value in self.iter_globals():
    1110              if name == pyop_name.proxyval(set()):
    1111                  return pyop_value, 'global'
    1112          for pyop_name, pyop_value in self.iter_builtins():
    1113              if name == pyop_name.proxyval(set()):
    1114                  return pyop_value, 'builtin'
    1115          return None, None
    1116  
    1117      def filename(self):
    1118          '''Get the path of the current Python source file, as a string'''
    1119          if self.is_optimized_out():
    1120              return FRAME_INFO_OPTIMIZED_OUT
    1121          return self.co_filename.proxyval(set())
    1122  
    1123      def current_line_num(self):
    1124          '''Get current line number as an integer (1-based)
    1125  
    1126          Translated from PyFrame_GetLineNumber and PyCode_Addr2Line
    1127  
    1128          See Objects/lnotab_notes.txt
    1129          '''
    1130          if self.is_optimized_out():
    1131              return None
    1132          try:
    1133              return self.co.addr2line(self.f_lasti)
    1134          except Exception as ex:
    1135              # bpo-34989: addr2line() is a complex function, it can fail in many
    1136              # ways. For example, it fails with a TypeError on "FakeRepr" if
    1137              # gdb fails to load debug symbols. Use a catch-all "except
    1138              # Exception" to make the whole function safe. The caller has to
    1139              # handle None anyway for optimized Python.
    1140              return None
    1141  
    1142      def current_line(self):
    1143          '''Get the text of the current source line as a string, with a trailing
    1144          newline character'''
    1145          if self.is_optimized_out():
    1146              return FRAME_INFO_OPTIMIZED_OUT
    1147  
    1148          lineno = self.current_line_num()
    1149          if lineno is None:
    1150              return '(failed to get frame line number)'
    1151  
    1152          filename = self.filename()
    1153          try:
    1154              with open(os_fsencode(filename), 'r', encoding="utf-8") as fp:
    1155                  lines = fp.readlines()
    1156          except IOError:
    1157              return None
    1158  
    1159          try:
    1160              # Convert from 1-based current_line_num to 0-based list offset
    1161              return lines[lineno - 1]
    1162          except IndexError:
    1163              return None
    1164  
    1165      def write_repr(self, out, visited):
    1166          if self.is_optimized_out():
    1167              out.write(FRAME_INFO_OPTIMIZED_OUT)
    1168              return
    1169          lineno = self.current_line_num()
    1170          lineno = str(lineno) if lineno is not None else "?"
    1171          out.write('Frame 0x%x, for file %s, line %s, in %s ('
    1172                    % (self.as_address(),
    1173                       self.co_filename.proxyval(visited),
    1174                       lineno,
    1175                       self.co_name.proxyval(visited)))
    1176          first = True
    1177          for pyop_name, pyop_value in self.iter_locals():
    1178              if not first:
    1179                  out.write(', ')
    1180              first = False
    1181  
    1182              out.write(pyop_name.proxyval(visited))
    1183              out.write('=')
    1184              pyop_value.write_repr(out, visited)
    1185  
    1186          out.write(')')
    1187  
    1188      def as_address(self):
    1189          return int(self._gdbval)
    1190  
    1191      def print_traceback(self):
    1192          if self.is_optimized_out():
    1193              sys.stdout.write('  %s\n' % FRAME_INFO_OPTIMIZED_OUT)
    1194              return
    1195          visited = set()
    1196          lineno = self.current_line_num()
    1197          lineno = str(lineno) if lineno is not None else "?"
    1198          sys.stdout.write('  File "%s", line %s, in %s\n'
    1199                    % (self.co_filename.proxyval(visited),
    1200                       lineno,
    1201                       self.co_name.proxyval(visited)))
    1202  
    1203      def get_truncated_repr(self, maxlen):
    1204          '''
    1205          Get a repr-like string for the data, but truncate it at "maxlen" bytes
    1206          (ending the object graph traversal as soon as you do)
    1207          '''
    1208          out = TruncatedStringIO(maxlen)
    1209          try:
    1210              self.write_repr(out, set())
    1211          except StringTruncated:
    1212              # Truncation occurred:
    1213              return out.getvalue() + '...(truncated)'
    1214  
    1215          # No truncation occurred:
    1216          return out.getvalue()
    1217  
    1218  class ESC[4;38;5;81mPySetObjectPtr(ESC[4;38;5;149mPyObjectPtr):
    1219      _typename = 'PySetObject'
    1220  
    1221      @classmethod
    1222      def _dummy_key(self):
    1223          return gdb.lookup_global_symbol('_PySet_Dummy').value()
    1224  
    1225      def __iter__(self):
    1226          dummy_ptr = self._dummy_key()
    1227          table = self.field('table')
    1228          for i in safe_range(self.field('mask') + 1):
    1229              setentry = table[i]
    1230              key = setentry['key']
    1231              if key != 0 and key != dummy_ptr:
    1232                  yield PyObjectPtr.from_pyobject_ptr(key)
    1233  
    1234      def proxyval(self, visited):
    1235          # Guard against infinite loops:
    1236          if self.as_address() in visited:
    1237              return ProxyAlreadyVisited('%s(...)' % self.safe_tp_name())
    1238          visited.add(self.as_address())
    1239  
    1240          members = (key.proxyval(visited) for key in self)
    1241          if self.safe_tp_name() == 'frozenset':
    1242              return frozenset(members)
    1243          else:
    1244              return set(members)
    1245  
    1246      def write_repr(self, out, visited):
    1247          # Emulate Python 3's set_repr
    1248          tp_name = self.safe_tp_name()
    1249  
    1250          # Guard against infinite loops:
    1251          if self.as_address() in visited:
    1252              out.write('(...)')
    1253              return
    1254          visited.add(self.as_address())
    1255  
    1256          # Python 3's set_repr special-cases the empty set:
    1257          if not self.field('used'):
    1258              out.write(tp_name)
    1259              out.write('()')
    1260              return
    1261  
    1262          # Python 3 uses {} for set literals:
    1263          if tp_name != 'set':
    1264              out.write(tp_name)
    1265              out.write('(')
    1266  
    1267          out.write('{')
    1268          first = True
    1269          for key in self:
    1270              if not first:
    1271                  out.write(', ')
    1272              first = False
    1273              key.write_repr(out, visited)
    1274          out.write('}')
    1275  
    1276          if tp_name != 'set':
    1277              out.write(')')
    1278  
    1279  
    1280  class ESC[4;38;5;81mPyBytesObjectPtr(ESC[4;38;5;149mPyObjectPtr):
    1281      _typename = 'PyBytesObject'
    1282  
    1283      def __str__(self):
    1284          field_ob_size = self.field('ob_size')
    1285          field_ob_sval = self.field('ob_sval')
    1286          char_ptr = field_ob_sval.address.cast(_type_unsigned_char_ptr())
    1287          return ''.join([chr(char_ptr[i]) for i in safe_range(field_ob_size)])
    1288  
    1289      def proxyval(self, visited):
    1290          return str(self)
    1291  
    1292      def write_repr(self, out, visited):
    1293          # Write this out as a Python 3 bytes literal, i.e. with a "b" prefix
    1294  
    1295          # Get a PyStringObject* within the Python 2 gdb process:
    1296          proxy = self.proxyval(visited)
    1297  
    1298          # Transliteration of Python 3's Objects/bytesobject.c:PyBytes_Repr
    1299          # to Python 2 code:
    1300          quote = "'"
    1301          if "'" in proxy and not '"' in proxy:
    1302              quote = '"'
    1303          out.write('b')
    1304          out.write(quote)
    1305          for byte in proxy:
    1306              if byte == quote or byte == '\\':
    1307                  out.write('\\')
    1308                  out.write(byte)
    1309              elif byte == '\t':
    1310                  out.write('\\t')
    1311              elif byte == '\n':
    1312                  out.write('\\n')
    1313              elif byte == '\r':
    1314                  out.write('\\r')
    1315              elif byte < ' ' or ord(byte) >= 0x7f:
    1316                  out.write('\\x')
    1317                  out.write(hexdigits[(ord(byte) & 0xf0) >> 4])
    1318                  out.write(hexdigits[ord(byte) & 0xf])
    1319              else:
    1320                  out.write(byte)
    1321          out.write(quote)
    1322  
    1323  class ESC[4;38;5;81mPyTupleObjectPtr(ESC[4;38;5;149mPyObjectPtr):
    1324      _typename = 'PyTupleObject'
    1325  
    1326      def __getitem__(self, i):
    1327          # Get the gdb.Value for the (PyObject*) with the given index:
    1328          field_ob_item = self.field('ob_item')
    1329          return field_ob_item[i]
    1330  
    1331      def proxyval(self, visited):
    1332          # Guard against infinite loops:
    1333          if self.as_address() in visited:
    1334              return ProxyAlreadyVisited('(...)')
    1335          visited.add(self.as_address())
    1336  
    1337          result = tuple(PyObjectPtr.from_pyobject_ptr(self[i]).proxyval(visited)
    1338                         for i in safe_range(int_from_int(self.field('ob_size'))))
    1339          return result
    1340  
    1341      def write_repr(self, out, visited):
    1342          # Guard against infinite loops:
    1343          if self.as_address() in visited:
    1344              out.write('(...)')
    1345              return
    1346          visited.add(self.as_address())
    1347  
    1348          out.write('(')
    1349          for i in safe_range(int_from_int(self.field('ob_size'))):
    1350              if i > 0:
    1351                  out.write(', ')
    1352              element = PyObjectPtr.from_pyobject_ptr(self[i])
    1353              element.write_repr(out, visited)
    1354          if self.field('ob_size') == 1:
    1355              out.write(',)')
    1356          else:
    1357              out.write(')')
    1358  
    1359  class ESC[4;38;5;81mPyTypeObjectPtr(ESC[4;38;5;149mPyObjectPtr):
    1360      _typename = 'PyTypeObject'
    1361  
    1362  
    1363  def _unichr_is_printable(char):
    1364      # Logic adapted from Python 3's Tools/unicode/makeunicodedata.py
    1365      if char == u" ":
    1366          return True
    1367      import unicodedata
    1368      return unicodedata.category(char) not in ("C", "Z")
    1369  
    1370  
    1371  class ESC[4;38;5;81mPyUnicodeObjectPtr(ESC[4;38;5;149mPyObjectPtr):
    1372      _typename = 'PyUnicodeObject'
    1373  
    1374      def char_width(self):
    1375          _type_Py_UNICODE = gdb.lookup_type('Py_UNICODE')
    1376          return _type_Py_UNICODE.sizeof
    1377  
    1378      def proxyval(self, visited):
    1379          may_have_surrogates = False
    1380          compact = self.field('_base')
    1381          ascii = compact['_base']
    1382          state = ascii['state']
    1383          is_compact_ascii = (int(state['ascii']) and int(state['compact']))
    1384          if not int(state['ready']):
    1385              # string is not ready
    1386              field_length = int(compact['wstr_length'])
    1387              may_have_surrogates = True
    1388              field_str = ascii['wstr']
    1389          else:
    1390              field_length = int(ascii['length'])
    1391              if is_compact_ascii:
    1392                  field_str = ascii.address + 1
    1393              elif int(state['compact']):
    1394                  field_str = compact.address + 1
    1395              else:
    1396                  field_str = self.field('data')['any']
    1397              repr_kind = int(state['kind'])
    1398              if repr_kind == 1:
    1399                  field_str = field_str.cast(_type_unsigned_char_ptr())
    1400              elif repr_kind == 2:
    1401                  field_str = field_str.cast(_type_unsigned_short_ptr())
    1402              elif repr_kind == 4:
    1403                  field_str = field_str.cast(_type_unsigned_int_ptr())
    1404  
    1405          # Gather a list of ints from the Py_UNICODE array; these are either
    1406          # UCS-1, UCS-2 or UCS-4 code points:
    1407          if not may_have_surrogates:
    1408              Py_UNICODEs = [int(field_str[i]) for i in safe_range(field_length)]
    1409          else:
    1410              # A more elaborate routine if sizeof(Py_UNICODE) is 2 in the
    1411              # inferior process: we must join surrogate pairs.
    1412              Py_UNICODEs = []
    1413              i = 0
    1414              limit = safety_limit(field_length)
    1415              while i < limit:
    1416                  ucs = int(field_str[i])
    1417                  i += 1
    1418                  if ucs < 0xD800 or ucs >= 0xDC00 or i == field_length:
    1419                      Py_UNICODEs.append(ucs)
    1420                      continue
    1421                  # This could be a surrogate pair.
    1422                  ucs2 = int(field_str[i])
    1423                  if ucs2 < 0xDC00 or ucs2 > 0xDFFF:
    1424                      continue
    1425                  code = (ucs & 0x03FF) << 10
    1426                  code |= ucs2 & 0x03FF
    1427                  code += 0x00010000
    1428                  Py_UNICODEs.append(code)
    1429                  i += 1
    1430  
    1431          # Convert the int code points to unicode characters, and generate a
    1432          # local unicode instance.
    1433          result = u''.join(map(chr, Py_UNICODEs))
    1434          return result
    1435  
    1436      def write_repr(self, out, visited):
    1437          # Write this out as a Python 3 str literal, i.e. without a "u" prefix
    1438  
    1439          # Get a PyUnicodeObject* within the Python 2 gdb process:
    1440          proxy = self.proxyval(visited)
    1441  
    1442          # Transliteration of Python 3's Object/unicodeobject.c:unicode_repr
    1443          # to Python 2:
    1444          if "'" in proxy and '"' not in proxy:
    1445              quote = '"'
    1446          else:
    1447              quote = "'"
    1448          out.write(quote)
    1449  
    1450          i = 0
    1451          while i < len(proxy):
    1452              ch = proxy[i]
    1453              i += 1
    1454  
    1455              # Escape quotes and backslashes
    1456              if ch == quote or ch == '\\':
    1457                  out.write('\\')
    1458                  out.write(ch)
    1459  
    1460              #  Map special whitespace to '\t', \n', '\r'
    1461              elif ch == '\t':
    1462                  out.write('\\t')
    1463              elif ch == '\n':
    1464                  out.write('\\n')
    1465              elif ch == '\r':
    1466                  out.write('\\r')
    1467  
    1468              # Map non-printable US ASCII to '\xhh' */
    1469              elif ch < ' ' or ord(ch) == 0x7F:
    1470                  out.write('\\x')
    1471                  out.write(hexdigits[(ord(ch) >> 4) & 0x000F])
    1472                  out.write(hexdigits[ord(ch) & 0x000F])
    1473  
    1474              # Copy ASCII characters as-is
    1475              elif ord(ch) < 0x7F:
    1476                  out.write(ch)
    1477  
    1478              # Non-ASCII characters
    1479              else:
    1480                  ucs = ch
    1481                  ch2 = None
    1482  
    1483                  printable = ucs.isprintable()
    1484                  if printable:
    1485                      try:
    1486                          ucs.encode(ENCODING)
    1487                      except UnicodeEncodeError:
    1488                          printable = False
    1489  
    1490                  # Map Unicode whitespace and control characters
    1491                  # (categories Z* and C* except ASCII space)
    1492                  if not printable:
    1493                      if ch2 is not None:
    1494                          # Match Python 3's representation of non-printable
    1495                          # wide characters.
    1496                          code = (ord(ch) & 0x03FF) << 10
    1497                          code |= ord(ch2) & 0x03FF
    1498                          code += 0x00010000
    1499                      else:
    1500                          code = ord(ucs)
    1501  
    1502                      # Map 8-bit characters to '\\xhh'
    1503                      if code <= 0xff:
    1504                          out.write('\\x')
    1505                          out.write(hexdigits[(code >> 4) & 0x000F])
    1506                          out.write(hexdigits[code & 0x000F])
    1507                      # Map 21-bit characters to '\U00xxxxxx'
    1508                      elif code >= 0x10000:
    1509                          out.write('\\U')
    1510                          out.write(hexdigits[(code >> 28) & 0x0000000F])
    1511                          out.write(hexdigits[(code >> 24) & 0x0000000F])
    1512                          out.write(hexdigits[(code >> 20) & 0x0000000F])
    1513                          out.write(hexdigits[(code >> 16) & 0x0000000F])
    1514                          out.write(hexdigits[(code >> 12) & 0x0000000F])
    1515                          out.write(hexdigits[(code >> 8) & 0x0000000F])
    1516                          out.write(hexdigits[(code >> 4) & 0x0000000F])
    1517                          out.write(hexdigits[code & 0x0000000F])
    1518                      # Map 16-bit characters to '\uxxxx'
    1519                      else:
    1520                          out.write('\\u')
    1521                          out.write(hexdigits[(code >> 12) & 0x000F])
    1522                          out.write(hexdigits[(code >> 8) & 0x000F])
    1523                          out.write(hexdigits[(code >> 4) & 0x000F])
    1524                          out.write(hexdigits[code & 0x000F])
    1525                  else:
    1526                      # Copy characters as-is
    1527                      out.write(ch)
    1528                      if ch2 is not None:
    1529                          out.write(ch2)
    1530  
    1531          out.write(quote)
    1532  
    1533  
    1534  class ESC[4;38;5;81mwrapperobject(ESC[4;38;5;149mPyObjectPtr):
    1535      _typename = 'wrapperobject'
    1536  
    1537      def safe_name(self):
    1538          try:
    1539              name = self.field('descr')['d_base']['name'].string()
    1540              return repr(name)
    1541          except (NullPyObjectPtr, RuntimeError, UnicodeDecodeError):
    1542              return '<unknown name>'
    1543  
    1544      def safe_tp_name(self):
    1545          try:
    1546              return self.field('self')['ob_type']['tp_name'].string()
    1547          except (NullPyObjectPtr, RuntimeError, UnicodeDecodeError):
    1548              return '<unknown tp_name>'
    1549  
    1550      def safe_self_addresss(self):
    1551          try:
    1552              address = int(self.field('self'))
    1553              return '%#x' % address
    1554          except (NullPyObjectPtr, RuntimeError):
    1555              return '<failed to get self address>'
    1556  
    1557      def proxyval(self, visited):
    1558          name = self.safe_name()
    1559          tp_name = self.safe_tp_name()
    1560          self_address = self.safe_self_addresss()
    1561          return ("<method-wrapper %s of %s object at %s>"
    1562                  % (name, tp_name, self_address))
    1563  
    1564      def write_repr(self, out, visited):
    1565          proxy = self.proxyval(visited)
    1566          out.write(proxy)
    1567  
    1568  
    1569  def int_from_int(gdbval):
    1570      return int(gdbval)
    1571  
    1572  
    1573  def stringify(val):
    1574      # TODO: repr() puts everything on one line; pformat can be nicer, but
    1575      # can lead to v.long results; this function isolates the choice
    1576      if True:
    1577          return repr(val)
    1578      else:
    1579          from pprint import pformat
    1580          return pformat(val)
    1581  
    1582  
    1583  class ESC[4;38;5;81mPyObjectPtrPrinter:
    1584      "Prints a (PyObject*)"
    1585  
    1586      def __init__ (self, gdbval):
    1587          self.gdbval = gdbval
    1588  
    1589      def to_string (self):
    1590          pyop = PyObjectPtr.from_pyobject_ptr(self.gdbval)
    1591          if True:
    1592              return pyop.get_truncated_repr(MAX_OUTPUT_LEN)
    1593          else:
    1594              # Generate full proxy value then stringify it.
    1595              # Doing so could be expensive
    1596              proxyval = pyop.proxyval(set())
    1597              return stringify(proxyval)
    1598  
    1599  def pretty_printer_lookup(gdbval):
    1600      type = gdbval.type.unqualified()
    1601      if type.code != gdb.TYPE_CODE_PTR:
    1602          return None
    1603  
    1604      type = type.target().unqualified()
    1605      t = str(type)
    1606      if t in ("PyObject", "PyFrameObject", "PyUnicodeObject", "wrapperobject"):
    1607          return PyObjectPtrPrinter(gdbval)
    1608  
    1609  """
    1610  During development, I've been manually invoking the code in this way:
    1611  (gdb) python
    1612  
    1613  import sys
    1614  sys.path.append('/home/david/coding/python-gdb')
    1615  import libpython
    1616  end
    1617  
    1618  then reloading it after each edit like this:
    1619  (gdb) python reload(libpython)
    1620  
    1621  The following code should ensure that the prettyprinter is registered
    1622  if the code is autoloaded by gdb when visiting libpython.so, provided
    1623  that this python file is installed to the same path as the library (or its
    1624  .debug file) plus a "-gdb.py" suffix, e.g:
    1625    /usr/lib/libpython2.6.so.1.0-gdb.py
    1626    /usr/lib/debug/usr/lib/libpython2.6.so.1.0.debug-gdb.py
    1627  """
    1628  def register (obj):
    1629      if obj is None:
    1630          obj = gdb
    1631  
    1632      # Wire up the pretty-printer
    1633      obj.pretty_printers.append(pretty_printer_lookup)
    1634  
    1635  register (gdb.current_objfile ())
    1636  
    1637  
    1638  
    1639  # Unfortunately, the exact API exposed by the gdb module varies somewhat
    1640  # from build to build
    1641  # See http://bugs.python.org/issue8279?#msg102276
    1642  
    1643  class ESC[4;38;5;81mFrame(ESC[4;38;5;149mobject):
    1644      '''
    1645      Wrapper for gdb.Frame, adding various methods
    1646      '''
    1647      def __init__(self, gdbframe):
    1648          self._gdbframe = gdbframe
    1649  
    1650      def older(self):
    1651          older = self._gdbframe.older()
    1652          if older:
    1653              return Frame(older)
    1654          else:
    1655              return None
    1656  
    1657      def newer(self):
    1658          newer = self._gdbframe.newer()
    1659          if newer:
    1660              return Frame(newer)
    1661          else:
    1662              return None
    1663  
    1664      def select(self):
    1665          '''If supported, select this frame and return True; return False if unsupported
    1666  
    1667          Not all builds have a gdb.Frame.select method; seems to be present on Fedora 12
    1668          onwards, but absent on Ubuntu buildbot'''
    1669          if not hasattr(self._gdbframe, 'select'):
    1670              print ('Unable to select frame: '
    1671                     'this build of gdb does not expose a gdb.Frame.select method')
    1672              return False
    1673          self._gdbframe.select()
    1674          return True
    1675  
    1676      def get_index(self):
    1677          '''Calculate index of frame, starting at 0 for the newest frame within
    1678          this thread'''
    1679          index = 0
    1680          # Go down until you reach the newest frame:
    1681          iter_frame = self
    1682          while iter_frame.newer():
    1683              index += 1
    1684              iter_frame = iter_frame.newer()
    1685          return index
    1686  
    1687      # We divide frames into:
    1688      #   - "python frames":
    1689      #       - "bytecode frames" i.e. PyEval_EvalFrameEx
    1690      #       - "other python frames": things that are of interest from a python
    1691      #         POV, but aren't bytecode (e.g. GC, GIL)
    1692      #   - everything else
    1693  
    1694      def is_python_frame(self):
    1695          '''Is this a _PyEval_EvalFrameDefault frame, or some other important
    1696          frame? (see is_other_python_frame for what "important" means in this
    1697          context)'''
    1698          if self.is_evalframe():
    1699              return True
    1700          if self.is_other_python_frame():
    1701              return True
    1702          return False
    1703  
    1704      def is_evalframe(self):
    1705          '''Is this a _PyEval_EvalFrameDefault frame?'''
    1706          if self._gdbframe.name() == EVALFRAME:
    1707              '''
    1708              I believe we also need to filter on the inline
    1709              struct frame_id.inline_depth, only regarding frames with
    1710              an inline depth of 0 as actually being this function
    1711  
    1712              So we reject those with type gdb.INLINE_FRAME
    1713              '''
    1714              if self._gdbframe.type() == gdb.NORMAL_FRAME:
    1715                  # We have a _PyEval_EvalFrameDefault frame:
    1716                  return True
    1717  
    1718          return False
    1719  
    1720      def is_other_python_frame(self):
    1721          '''Is this frame worth displaying in python backtraces?
    1722          Examples:
    1723            - waiting on the GIL
    1724            - garbage-collecting
    1725            - within a CFunction
    1726           If it is, return a descriptive string
    1727           For other frames, return False
    1728           '''
    1729          if self.is_waiting_for_gil():
    1730              return 'Waiting for the GIL'
    1731  
    1732          if self.is_gc_collect():
    1733              return 'Garbage-collecting'
    1734  
    1735          # Detect invocations of PyCFunction instances:
    1736          frame = self._gdbframe
    1737          caller = frame.name()
    1738          if not caller:
    1739              return False
    1740  
    1741          if (caller.startswith('cfunction_vectorcall_') or
    1742              caller == 'cfunction_call'):
    1743              arg_name = 'func'
    1744              # Within that frame:
    1745              #   "func" is the local containing the PyObject* of the
    1746              # PyCFunctionObject instance
    1747              #   "f" is the same value, but cast to (PyCFunctionObject*)
    1748              #   "self" is the (PyObject*) of the 'self'
    1749              try:
    1750                  # Use the prettyprinter for the func:
    1751                  func = frame.read_var(arg_name)
    1752                  return str(func)
    1753              except ValueError:
    1754                  return ('PyCFunction invocation (unable to read %s: '
    1755                          'missing debuginfos?)' % arg_name)
    1756              except RuntimeError:
    1757                  return 'PyCFunction invocation (unable to read %s)' % arg_name
    1758  
    1759          if caller == 'wrapper_call':
    1760              arg_name = 'wp'
    1761              try:
    1762                  func = frame.read_var(arg_name)
    1763                  return str(func)
    1764              except ValueError:
    1765                  return ('<wrapper_call invocation (unable to read %s: '
    1766                          'missing debuginfos?)>' % arg_name)
    1767              except RuntimeError:
    1768                  return '<wrapper_call invocation (unable to read %s)>' % arg_name
    1769  
    1770          # This frame isn't worth reporting:
    1771          return False
    1772  
    1773      def is_waiting_for_gil(self):
    1774          '''Is this frame waiting on the GIL?'''
    1775          # This assumes the _POSIX_THREADS version of Python/ceval_gil.h:
    1776          name = self._gdbframe.name()
    1777          if name:
    1778              return (name == 'take_gil')
    1779  
    1780      def is_gc_collect(self):
    1781          '''Is this frame gc_collect_main() within the garbage-collector?'''
    1782          return self._gdbframe.name() in ('collect', 'gc_collect_main')
    1783  
    1784      def get_pyop(self):
    1785          try:
    1786              frame = self._gdbframe.read_var('frame')
    1787              frame = PyFramePtr(frame)
    1788              if not frame.is_optimized_out():
    1789                  return frame
    1790              cframe = self._gdbframe.read_var('cframe')
    1791              if cframe is None:
    1792                  return None
    1793              frame = PyFramePtr(cframe["current_frame"])
    1794              if frame and not frame.is_optimized_out():
    1795                  return frame
    1796              return None
    1797          except ValueError:
    1798              return None
    1799  
    1800      @classmethod
    1801      def get_selected_frame(cls):
    1802          _gdbframe = gdb.selected_frame()
    1803          if _gdbframe:
    1804              return Frame(_gdbframe)
    1805          return None
    1806  
    1807      @classmethod
    1808      def get_selected_python_frame(cls):
    1809          '''Try to obtain the Frame for the python-related code in the selected
    1810          frame, or None'''
    1811          try:
    1812              frame = cls.get_selected_frame()
    1813          except gdb.error:
    1814              # No frame: Python didn't start yet
    1815              return None
    1816  
    1817          while frame:
    1818              if frame.is_python_frame():
    1819                  return frame
    1820              frame = frame.older()
    1821  
    1822          # Not found:
    1823          return None
    1824  
    1825      @classmethod
    1826      def get_selected_bytecode_frame(cls):
    1827          '''Try to obtain the Frame for the python bytecode interpreter in the
    1828          selected GDB frame, or None'''
    1829          frame = cls.get_selected_frame()
    1830  
    1831          while frame:
    1832              if frame.is_evalframe():
    1833                  return frame
    1834              frame = frame.older()
    1835  
    1836          # Not found:
    1837          return None
    1838  
    1839      def print_summary(self):
    1840          if self.is_evalframe():
    1841              interp_frame = self.get_pyop()
    1842              while True:
    1843                  if interp_frame:
    1844                      line = interp_frame.get_truncated_repr(MAX_OUTPUT_LEN)
    1845                      sys.stdout.write('#%i %s\n' % (self.get_index(), line))
    1846                      if not interp_frame.is_optimized_out():
    1847                          line = interp_frame.current_line()
    1848                          if line is not None:
    1849                              sys.stdout.write('    %s\n' % line.strip())
    1850                      if interp_frame.is_entry():
    1851                          break
    1852                  else:
    1853                      sys.stdout.write('#%i (unable to read python frame information)\n' % self.get_index())
    1854                      break
    1855                  interp_frame = interp_frame.previous()
    1856          else:
    1857              info = self.is_other_python_frame()
    1858              if info:
    1859                  sys.stdout.write('#%i %s\n' % (self.get_index(), info))
    1860              else:
    1861                  sys.stdout.write('#%i\n' % self.get_index())
    1862  
    1863      def print_traceback(self):
    1864          if self.is_evalframe():
    1865              interp_frame = self.get_pyop()
    1866              while True:
    1867                  if interp_frame:
    1868                      interp_frame.print_traceback()
    1869                      if not interp_frame.is_optimized_out():
    1870                          line = interp_frame.current_line()
    1871                          if line is not None:
    1872                              sys.stdout.write('    %s\n' % line.strip())
    1873                      if interp_frame.is_entry():
    1874                          break
    1875                  else:
    1876                      sys.stdout.write('  (unable to read python frame information)\n')
    1877                      break
    1878                  interp_frame = interp_frame.previous()
    1879          else:
    1880              info = self.is_other_python_frame()
    1881              if info:
    1882                  sys.stdout.write('  %s\n' % info)
    1883              else:
    1884                  sys.stdout.write('  (not a python frame)\n')
    1885  
    1886  class ESC[4;38;5;81mPyList(ESC[4;38;5;149mgdbESC[4;38;5;149m.ESC[4;38;5;149mCommand):
    1887      '''List the current Python source code, if any
    1888  
    1889      Use
    1890         py-list START
    1891      to list at a different line number within the python source.
    1892  
    1893      Use
    1894         py-list START, END
    1895      to list a specific range of lines within the python source.
    1896      '''
    1897  
    1898      def __init__(self):
    1899          gdb.Command.__init__ (self,
    1900                                "py-list",
    1901                                gdb.COMMAND_FILES,
    1902                                gdb.COMPLETE_NONE)
    1903  
    1904  
    1905      def invoke(self, args, from_tty):
    1906          import re
    1907  
    1908          start = None
    1909          end = None
    1910  
    1911          m = re.match(r'\s*(\d+)\s*', args)
    1912          if m:
    1913              start = int(m.group(0))
    1914              end = start + 10
    1915  
    1916          m = re.match(r'\s*(\d+)\s*,\s*(\d+)\s*', args)
    1917          if m:
    1918              start, end = map(int, m.groups())
    1919  
    1920          # py-list requires an actual PyEval_EvalFrameEx frame:
    1921          frame = Frame.get_selected_bytecode_frame()
    1922          if not frame:
    1923              print('Unable to locate gdb frame for python bytecode interpreter')
    1924              return
    1925  
    1926          pyop = frame.get_pyop()
    1927          if not pyop or pyop.is_optimized_out():
    1928              print(UNABLE_READ_INFO_PYTHON_FRAME)
    1929              return
    1930  
    1931          filename = pyop.filename()
    1932          lineno = pyop.current_line_num()
    1933          if lineno is None:
    1934              print('Unable to read python frame line number')
    1935              return
    1936  
    1937          if start is None:
    1938              start = lineno - 5
    1939              end = lineno + 5
    1940  
    1941          if start<1:
    1942              start = 1
    1943  
    1944          try:
    1945              f = open(os_fsencode(filename), 'r', encoding="utf-8")
    1946          except IOError as err:
    1947              sys.stdout.write('Unable to open %s: %s\n'
    1948                               % (filename, err))
    1949              return
    1950          with f:
    1951              all_lines = f.readlines()
    1952              # start and end are 1-based, all_lines is 0-based;
    1953              # so [start-1:end] as a python slice gives us [start, end] as a
    1954              # closed interval
    1955              for i, line in enumerate(all_lines[start-1:end]):
    1956                  linestr = str(i+start)
    1957                  # Highlight current line:
    1958                  if i + start == lineno:
    1959                      linestr = '>' + linestr
    1960                  sys.stdout.write('%4s    %s' % (linestr, line))
    1961  
    1962  
    1963  # ...and register the command:
    1964  PyList()
    1965  
    1966  def move_in_stack(move_up):
    1967      '''Move up or down the stack (for the py-up/py-down command)'''
    1968      # Important:
    1969      # The amount of frames that are printed out depends on how many frames are inlined
    1970      # in the same evaluation loop. As this command links directly the C stack with the
    1971      # Python stack, the results are sensitive to the number of inlined frames and this
    1972      # is likely to change between versions and optimizations.
    1973      frame = Frame.get_selected_python_frame()
    1974      if not frame:
    1975          print('Unable to locate python frame')
    1976          return
    1977      while frame:
    1978          if move_up:
    1979              iter_frame = frame.older()
    1980          else:
    1981              iter_frame = frame.newer()
    1982  
    1983          if not iter_frame:
    1984              break
    1985  
    1986          if iter_frame.is_python_frame():
    1987              # Result:
    1988              if iter_frame.select():
    1989                  iter_frame.print_summary()
    1990              return
    1991  
    1992          frame = iter_frame
    1993  
    1994      if move_up:
    1995          print('Unable to find an older python frame')
    1996      else:
    1997          print('Unable to find a newer python frame')
    1998  
    1999  
    2000  class ESC[4;38;5;81mPyUp(ESC[4;38;5;149mgdbESC[4;38;5;149m.ESC[4;38;5;149mCommand):
    2001      'Select and print all python stack frame in the same eval loop starting from the one that called this one (if any)'
    2002      def __init__(self):
    2003          gdb.Command.__init__ (self,
    2004                                "py-up",
    2005                                gdb.COMMAND_STACK,
    2006                                gdb.COMPLETE_NONE)
    2007  
    2008  
    2009      def invoke(self, args, from_tty):
    2010          move_in_stack(move_up=True)
    2011  
    2012  class ESC[4;38;5;81mPyDown(ESC[4;38;5;149mgdbESC[4;38;5;149m.ESC[4;38;5;149mCommand):
    2013      'Select and print all python stack frame in the same eval loop starting from the one called this one (if any)'
    2014      def __init__(self):
    2015          gdb.Command.__init__ (self,
    2016                                "py-down",
    2017                                gdb.COMMAND_STACK,
    2018                                gdb.COMPLETE_NONE)
    2019  
    2020  
    2021      def invoke(self, args, from_tty):
    2022          move_in_stack(move_up=False)
    2023  
    2024  # Not all builds of gdb have gdb.Frame.select
    2025  if hasattr(gdb.Frame, 'select'):
    2026      PyUp()
    2027      PyDown()
    2028  
    2029  class ESC[4;38;5;81mPyBacktraceFull(ESC[4;38;5;149mgdbESC[4;38;5;149m.ESC[4;38;5;149mCommand):
    2030      'Display the current python frame and all the frames within its call stack (if any)'
    2031      def __init__(self):
    2032          gdb.Command.__init__ (self,
    2033                                "py-bt-full",
    2034                                gdb.COMMAND_STACK,
    2035                                gdb.COMPLETE_NONE)
    2036  
    2037  
    2038      def invoke(self, args, from_tty):
    2039          frame = Frame.get_selected_python_frame()
    2040          if not frame:
    2041              print('Unable to locate python frame')
    2042              return
    2043  
    2044          while frame:
    2045              if frame.is_python_frame():
    2046                  frame.print_summary()
    2047              frame = frame.older()
    2048  
    2049  PyBacktraceFull()
    2050  
    2051  class ESC[4;38;5;81mPyBacktrace(ESC[4;38;5;149mgdbESC[4;38;5;149m.ESC[4;38;5;149mCommand):
    2052      'Display the current python frame and all the frames within its call stack (if any)'
    2053      def __init__(self):
    2054          gdb.Command.__init__ (self,
    2055                                "py-bt",
    2056                                gdb.COMMAND_STACK,
    2057                                gdb.COMPLETE_NONE)
    2058  
    2059  
    2060      def invoke(self, args, from_tty):
    2061          frame = Frame.get_selected_python_frame()
    2062          if not frame:
    2063              print('Unable to locate python frame')
    2064              return
    2065  
    2066          sys.stdout.write('Traceback (most recent call first):\n')
    2067          while frame:
    2068              if frame.is_python_frame():
    2069                  frame.print_traceback()
    2070              frame = frame.older()
    2071  
    2072  PyBacktrace()
    2073  
    2074  class ESC[4;38;5;81mPyPrint(ESC[4;38;5;149mgdbESC[4;38;5;149m.ESC[4;38;5;149mCommand):
    2075      'Look up the given python variable name, and print it'
    2076      def __init__(self):
    2077          gdb.Command.__init__ (self,
    2078                                "py-print",
    2079                                gdb.COMMAND_DATA,
    2080                                gdb.COMPLETE_NONE)
    2081  
    2082  
    2083      def invoke(self, args, from_tty):
    2084          name = str(args)
    2085  
    2086          frame = Frame.get_selected_python_frame()
    2087          if not frame:
    2088              print('Unable to locate python frame')
    2089              return
    2090  
    2091          pyop_frame = frame.get_pyop()
    2092          if not pyop_frame:
    2093              print(UNABLE_READ_INFO_PYTHON_FRAME)
    2094              return
    2095  
    2096          pyop_var, scope = pyop_frame.get_var_by_name(name)
    2097  
    2098          if pyop_var:
    2099              print('%s %r = %s'
    2100                     % (scope,
    2101                        name,
    2102                        pyop_var.get_truncated_repr(MAX_OUTPUT_LEN)))
    2103          else:
    2104              print('%r not found' % name)
    2105  
    2106  PyPrint()
    2107  
    2108  class ESC[4;38;5;81mPyLocals(ESC[4;38;5;149mgdbESC[4;38;5;149m.ESC[4;38;5;149mCommand):
    2109      'Look up the given python variable name, and print it'
    2110      def __init__(self):
    2111          gdb.Command.__init__ (self,
    2112                                "py-locals",
    2113                                gdb.COMMAND_DATA,
    2114                                gdb.COMPLETE_NONE)
    2115  
    2116  
    2117      def invoke(self, args, from_tty):
    2118          name = str(args)
    2119  
    2120          frame = Frame.get_selected_python_frame()
    2121          if not frame:
    2122              print('Unable to locate python frame')
    2123              return
    2124  
    2125          pyop_frame = frame.get_pyop()
    2126          while True:
    2127              if not pyop_frame:
    2128                  print(UNABLE_READ_INFO_PYTHON_FRAME)
    2129                  break
    2130  
    2131              sys.stdout.write('Locals for %s\n' % (pyop_frame.co_name.proxyval(set())))
    2132  
    2133              for pyop_name, pyop_value in pyop_frame.iter_locals():
    2134                  print('%s = %s'
    2135                      % (pyop_name.proxyval(set()),
    2136                          pyop_value.get_truncated_repr(MAX_OUTPUT_LEN)))
    2137  
    2138              if pyop_frame.is_entry():
    2139                  break
    2140  
    2141              pyop_frame = pyop_frame.previous()
    2142  
    2143  PyLocals()