Commit c6807afd authored by Stefan Behnel's avatar Stefan Behnel

Update libpython from latest CPython upstream (1ceb3a3d17).

parent 4eef0edd
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
From gdb 7 onwards, gdb's build can be configured --with-python, allowing gdb From gdb 7 onwards, gdb's build can be configured --with-python, allowing gdb
to be extended with Python code e.g. for library-specific data visualizations, to be extended with Python code e.g. for library-specific data visualizations,
such as for the C++ STL types. Documentation on this API can be seen at: such as for the C++ STL types. Documentation on this API can be seen at:
https://sourceware.org/gdb/current/onlinedocs/gdb/Python-API.html http://sourceware.org/gdb/current/onlinedocs/gdb/Python-API.html
This python module deals with the case when the process being debugged (the This python module deals with the case when the process being debugged (the
...@@ -48,7 +48,7 @@ The module also extends gdb with some python-specific commands. ...@@ -48,7 +48,7 @@ The module also extends gdb with some python-specific commands.
''' '''
# NOTE: some gdbs are linked with Python 3, so this file should be dual-syntax # NOTE: some gdbs are linked with Python 3, so this file should be dual-syntax
# compatible (2.7+ and 3.3+). See #19308. # compatible (2.6+ and 3.0+). See #19308.
from __future__ import print_function from __future__ import print_function
import gdb import gdb
...@@ -276,12 +276,13 @@ class PyObjectPtr(object): ...@@ -276,12 +276,13 @@ class PyObjectPtr(object):
def safe_tp_name(self): def safe_tp_name(self):
try: try:
return self.type().field('tp_name').string() ob_type = self.type()
except NullPyObjectPtr: tp_name = ob_type.field('tp_name')
# NULL tp_name? return tp_name.string()
return 'unknown' # NullPyObjectPtr: NULL tp_name?
except RuntimeError: # RuntimeError: Can't even read the object at all?
# Can't even read the object at all? # UnicodeDecodeError: Failed to decode tp_name bytestring
except (NullPyObjectPtr, RuntimeError, UnicodeDecodeError):
return 'unknown' return 'unknown'
def proxyval(self, visited): def proxyval(self, visited):
...@@ -315,7 +316,7 @@ class PyObjectPtr(object): ...@@ -315,7 +316,7 @@ class PyObjectPtr(object):
def __repr__(self): def __repr__(self):
# For the NULL pointer, we have no way of knowing a type, so # For the NULL pointer, we have no way of knowing a type, so
# special-case it as per # special-case it as per
# https://bugs.python.org/issue8032#msg100882 # http://bugs.python.org/issue8032#msg100882
if self.address == 0: if self.address == 0:
return '0x0' return '0x0'
return '<%s at remote 0x%x>' % (self.tp_name, self.address) return '<%s at remote 0x%x>' % (self.tp_name, self.address)
...@@ -355,7 +356,9 @@ class PyObjectPtr(object): ...@@ -355,7 +356,9 @@ class PyObjectPtr(object):
try: try:
tp_name = t.field('tp_name').string() tp_name = t.field('tp_name').string()
tp_flags = int(t.field('tp_flags')) tp_flags = int(t.field('tp_flags'))
except RuntimeError: # RuntimeError: NULL pointers
# UnicodeDecodeError: string() fails to decode the bytestring
except (RuntimeError, UnicodeDecodeError):
# Handle any kind of error e.g. NULL ptrs by simply using the base # Handle any kind of error e.g. NULL ptrs by simply using the base
# class # class
return cls return cls
...@@ -623,7 +626,10 @@ class PyCFunctionObjectPtr(PyObjectPtr): ...@@ -623,7 +626,10 @@ class PyCFunctionObjectPtr(PyObjectPtr):
def proxyval(self, visited): def proxyval(self, visited):
m_ml = self.field('m_ml') # m_ml is a (PyMethodDef*) m_ml = self.field('m_ml') # m_ml is a (PyMethodDef*)
ml_name = m_ml['ml_name'].string() try:
ml_name = m_ml['ml_name'].string()
except UnicodeDecodeError:
ml_name = '<ml_name:UnicodeDecodeError>'
pyop_m_self = self.pyop_field('m_self') pyop_m_self = self.pyop_field('m_self')
if pyop_m_self.is_null(): if pyop_m_self.is_null():
...@@ -736,7 +742,7 @@ class PyDictObjectPtr(PyObjectPtr): ...@@ -736,7 +742,7 @@ class PyDictObjectPtr(PyObjectPtr):
else: else:
offset = 8 * dk_size offset = 8 * dk_size
ent_addr = keys['dk_indices']['as_1'].address ent_addr = keys['dk_indices'].address
ent_addr = ent_addr.cast(_type_unsigned_char_ptr()) + offset ent_addr = ent_addr.cast(_type_unsigned_char_ptr()) + offset
ent_ptr_t = gdb.lookup_type('PyDictKeyEntry').pointer() ent_ptr_t = gdb.lookup_type('PyDictKeyEntry').pointer()
ent_addr = ent_addr.cast(ent_ptr_t) ent_addr = ent_addr.cast(ent_ptr_t)
...@@ -934,35 +940,50 @@ class PyFrameObjectPtr(PyObjectPtr): ...@@ -934,35 +940,50 @@ class PyFrameObjectPtr(PyObjectPtr):
if long(f_trace) != 0: if long(f_trace) != 0:
# we have a non-NULL f_trace: # we have a non-NULL f_trace:
return self.f_lineno return self.f_lineno
else:
#try: try:
return self.co.addr2line(self.f_lasti) return self.co.addr2line(self.f_lasti)
#except ValueError: except Exception:
# return self.f_lineno # bpo-34989: addr2line() is a complex function, it can fail in many
# ways. For example, it fails with a TypeError on "FakeRepr" if
# gdb fails to load debug symbols. Use a catch-all "except
# Exception" to make the whole function safe. The caller has to
# handle None anyway for optimized Python.
return None
def current_line(self): def current_line(self):
'''Get the text of the current source line as a string, with a trailing '''Get the text of the current source line as a string, with a trailing
newline character''' newline character'''
if self.is_optimized_out(): if self.is_optimized_out():
return '(frame information optimized out)' return '(frame information optimized out)'
lineno = self.current_line_num()
if lineno is None:
return '(failed to get frame line number)'
filename = self.filename() filename = self.filename()
try: try:
f = open(os_fsencode(filename), 'r') with open(os_fsencode(filename), 'r') as fp:
lines = fp.readlines()
except IOError: except IOError:
return None return None
with f:
all_lines = f.readlines() try:
# Convert from 1-based current_line_num to 0-based list offset: # Convert from 1-based current_line_num to 0-based list offset
return all_lines[self.current_line_num()-1] return lines[lineno - 1]
except IndexError:
return None
def write_repr(self, out, visited): def write_repr(self, out, visited):
if self.is_optimized_out(): if self.is_optimized_out():
out.write('(frame information optimized out)') out.write('(frame information optimized out)')
return return
out.write('Frame 0x%x, for file %s, line %i, in %s (' lineno = self.current_line_num()
lineno = str(lineno) if lineno is not None else "?"
out.write('Frame 0x%x, for file %s, line %s, in %s ('
% (self.as_address(), % (self.as_address(),
self.co_filename.proxyval(visited), self.co_filename.proxyval(visited),
self.current_line_num(), lineno,
self.co_name.proxyval(visited))) self.co_name.proxyval(visited)))
first = True first = True
for pyop_name, pyop_value in self.iter_locals(): for pyop_name, pyop_value in self.iter_locals():
...@@ -981,9 +1002,11 @@ class PyFrameObjectPtr(PyObjectPtr): ...@@ -981,9 +1002,11 @@ class PyFrameObjectPtr(PyObjectPtr):
sys.stdout.write(' (frame information optimized out)\n') sys.stdout.write(' (frame information optimized out)\n')
return return
visited = set() visited = set()
sys.stdout.write(' File "%s", line %i, in %s\n' lineno = self.current_line_num()
lineno = str(lineno) if lineno is not None else "?"
sys.stdout.write(' File "%s", line %s, in %s\n'
% (self.co_filename.proxyval(visited), % (self.co_filename.proxyval(visited),
self.current_line_num(), lineno,
self.co_name.proxyval(visited))) self.co_name.proxyval(visited)))
class PySetObjectPtr(PyObjectPtr): class PySetObjectPtr(PyObjectPtr):
...@@ -1092,6 +1115,7 @@ class PyBytesObjectPtr(PyObjectPtr): ...@@ -1092,6 +1115,7 @@ class PyBytesObjectPtr(PyObjectPtr):
out.write(quote) out.write(quote)
# Added in Cython for Py2 backwards compatibility.
class PyStringObjectPtr(PyBytesObjectPtr): class PyStringObjectPtr(PyBytesObjectPtr):
_typename = 'PyStringObject' _typename = 'PyStringObject'
...@@ -1166,7 +1190,7 @@ class PyUnicodeObjectPtr(PyObjectPtr): ...@@ -1166,7 +1190,7 @@ class PyUnicodeObjectPtr(PyObjectPtr):
def proxyval(self, visited): def proxyval(self, visited):
global _is_pep393 global _is_pep393
if _is_pep393 is None: if _is_pep393 is None:
fields = gdb.lookup_type('PyUnicodeObject').target().fields() fields = gdb.lookup_type('PyUnicodeObject').fields()
_is_pep393 = 'data' in [f.name for f in fields] _is_pep393 = 'data' in [f.name for f in fields]
if _is_pep393: if _is_pep393:
# Python 3.3 and newer # Python 3.3 and newer
...@@ -1351,13 +1375,13 @@ class wrapperobject(PyObjectPtr): ...@@ -1351,13 +1375,13 @@ class wrapperobject(PyObjectPtr):
try: try:
name = self.field('descr')['d_base']['name'].string() name = self.field('descr')['d_base']['name'].string()
return repr(name) return repr(name)
except (NullPyObjectPtr, RuntimeError): except (NullPyObjectPtr, RuntimeError, UnicodeDecodeError):
return '<unknown name>' return '<unknown name>'
def safe_tp_name(self): def safe_tp_name(self):
try: try:
return self.field('self')['ob_type']['tp_name'].string() return self.field('self')['ob_type']['tp_name'].string()
except (NullPyObjectPtr, RuntimeError): except (NullPyObjectPtr, RuntimeError, UnicodeDecodeError):
return '<unknown tp_name>' return '<unknown tp_name>'
def safe_self_addresss(self): def safe_self_addresss(self):
...@@ -1435,8 +1459,8 @@ The following code should ensure that the prettyprinter is registered ...@@ -1435,8 +1459,8 @@ The following code should ensure that the prettyprinter is registered
if the code is autoloaded by gdb when visiting libpython.so, provided if the code is autoloaded by gdb when visiting libpython.so, provided
that this python file is installed to the same path as the library (or its that this python file is installed to the same path as the library (or its
.debug file) plus a "-gdb.py" suffix, e.g: .debug file) plus a "-gdb.py" suffix, e.g:
/usr/lib/libpython3.7.so.1.0-gdb.py /usr/lib/libpython2.6.so.1.0-gdb.py
/usr/lib/debug/usr/lib/libpython3.7.so.1.0.debug-gdb.py /usr/lib/debug/usr/lib/libpython2.6.so.1.0.debug-gdb.py
""" """
def register (obj): def register (obj):
if obj is None: if obj is None:
...@@ -1451,7 +1475,7 @@ register (gdb.current_objfile ()) ...@@ -1451,7 +1475,7 @@ register (gdb.current_objfile ())
# Unfortunately, the exact API exposed by the gdb module varies somewhat # Unfortunately, the exact API exposed by the gdb module varies somewhat
# from build to build # from build to build
# See https://bugs.python.org/issue8279?#msg102276 # See http://bugs.python.org/issue8279?#msg102276
class Frame(object): class Frame(object):
''' '''
...@@ -1563,15 +1587,22 @@ class Frame(object): ...@@ -1563,15 +1587,22 @@ class Frame(object):
# Use the prettyprinter for the func: # Use the prettyprinter for the func:
func = frame.read_var(arg_name) func = frame.read_var(arg_name)
return str(func) return str(func)
except ValueError:
return ('PyCFunction invocation (unable to read %s: '
'missing debuginfos?)' % arg_name)
except RuntimeError: except RuntimeError:
return 'PyCFunction invocation (unable to read %s)' % arg_name return 'PyCFunction invocation (unable to read %s)' % arg_name
if caller == 'wrapper_call': if caller == 'wrapper_call':
arg_name = 'wp'
try: try:
func = frame.read_var('wp') func = frame.read_var(arg_name)
return str(func) return str(func)
except ValueError:
return ('<wrapper_call invocation (unable to read %s: '
'missing debuginfos?)>' % arg_name)
except RuntimeError: except RuntimeError:
return '<wrapper_call invocation>' return '<wrapper_call invocation (unable to read %s)>' % arg_name
# This frame isn't worth reporting: # This frame isn't worth reporting:
return False return False
...@@ -1730,6 +1761,9 @@ class PyList(gdb.Command): ...@@ -1730,6 +1761,9 @@ class PyList(gdb.Command):
filename = pyop.filename() filename = pyop.filename()
lineno = pyop.current_line_num() lineno = pyop.current_line_num()
if lineno is None:
print('Unable to read python frame line number')
return
if start is None: if start is None:
start = lineno - 5 start = lineno - 5
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment