bpo-32962: Backport python-gdb.py and test_gdb.py from master (GH-7710) (GH-7711)
* bpo-32962: python-gdb catchs ValueError on read_var() (GH-7692) python-gdb now catchs ValueError on read_var(): when Python has no debug symbols for example. (cherry picked from commit019d33b7a4
) * bpo-32962: python-gdb catchs UnicodeDecodeError (GH-7693) python-gdb now catchs UnicodeDecodeError exceptions when calling string(). (cherry picked from commitd22fc0bc7d
) * bpo-32962: Fix test_gdb failure in debug build with -mcet -fcf-protection -O0 (GH-6754) When Python is built with the intel control-flow protection flags, -mcet -fcf-protection, gdb is not able to read the stack without actually jumping inside the function. This means an extra 'next' command is required to make the $pc (program counter) enter the function and make the stack of the function exposed to gdb. (cherry picked from commit9b7c74ca32
) (cherry picked from commitca4cb8492c
)
This commit is contained in:
parent
d85d479f0e
commit
5279759f52
|
@ -168,7 +168,11 @@ class DebuggerTests(unittest.TestCase):
|
||||||
commands += ['set print entry-values no']
|
commands += ['set print entry-values no']
|
||||||
|
|
||||||
if cmds_after_breakpoint:
|
if cmds_after_breakpoint:
|
||||||
commands += cmds_after_breakpoint
|
# bpo-32962: When Python is compiled with -mcet -fcf-protection,
|
||||||
|
# arguments are unusable before running the first instruction
|
||||||
|
# of the function entry point. The 'next' command makes the
|
||||||
|
# required first step.
|
||||||
|
commands += ['next'] + cmds_after_breakpoint
|
||||||
else:
|
else:
|
||||||
commands += ['backtrace']
|
commands += ['backtrace']
|
||||||
|
|
||||||
|
@ -859,9 +863,12 @@ id(42)
|
||||||
id("first break point")
|
id("first break point")
|
||||||
l = MyList()
|
l = MyList()
|
||||||
''')
|
''')
|
||||||
|
# bpo-32962: same case as in get_stack_trace():
|
||||||
|
# we need an additional 'next' command in order to read
|
||||||
|
# arguments of the innermost function of the call stack.
|
||||||
# Verify with "py-bt":
|
# Verify with "py-bt":
|
||||||
gdb_output = self.get_stack_trace(cmd,
|
gdb_output = self.get_stack_trace(cmd,
|
||||||
cmds_after_breakpoint=['break wrapper_call', 'continue', 'py-bt'])
|
cmds_after_breakpoint=['break wrapper_call', 'continue', 'next', 'py-bt'])
|
||||||
self.assertRegex(gdb_output,
|
self.assertRegex(gdb_output,
|
||||||
r"<method-wrapper u?'__init__' of MyList object at ")
|
r"<method-wrapper u?'__init__' of MyList object at ")
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Fixed test_gdb when Python is compiled with flags -mcet -fcf-protection -O0.
|
|
@ -0,0 +1,2 @@
|
||||||
|
python-gdb now catchs ValueError on read_var(): when Python has no debug
|
||||||
|
symbols for example.
|
|
@ -0,0 +1,2 @@
|
||||||
|
python-gdb now catchs ``UnicodeDecodeError`` exceptions when calling
|
||||||
|
``string()``.
|
|
@ -270,12 +270,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):
|
||||||
|
@ -349,7 +350,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
|
||||||
|
@ -617,7 +620,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():
|
||||||
|
@ -1340,13 +1346,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):
|
||||||
|
@ -1541,6 +1547,7 @@ class Frame(object):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if caller == 'PyCFunction_Call':
|
if caller == 'PyCFunction_Call':
|
||||||
|
arg_name = 'func'
|
||||||
# Within that frame:
|
# Within that frame:
|
||||||
# "func" is the local containing the PyObject* of the
|
# "func" is the local containing the PyObject* of the
|
||||||
# PyCFunctionObject instance
|
# PyCFunctionObject instance
|
||||||
|
@ -1548,24 +1555,35 @@ class Frame(object):
|
||||||
# "self" is the (PyObject*) of the 'self'
|
# "self" is the (PyObject*) of the 'self'
|
||||||
try:
|
try:
|
||||||
# Use the prettyprinter for the func:
|
# Use the prettyprinter for the func:
|
||||||
func = frame.read_var('func')
|
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 "func")'
|
return 'PyCFunction invocation (unable to read %s)' % arg_name
|
||||||
|
|
||||||
elif caller == '_PyCFunction_FastCallDict':
|
elif caller == '_PyCFunction_FastCallDict':
|
||||||
|
arg_name = 'func_obj'
|
||||||
try:
|
try:
|
||||||
func = frame.read_var('func_obj')
|
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 "func_obj")'
|
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
|
||||||
|
|
Loading…
Reference in New Issue