Skip to content

[3.7] bpo-32962: Backport python-gdb.py and test_gdb.py from master #7710

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 15, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions Lib/test/test_gdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,11 @@ def get_stack_trace(self, source=None, script=None,
commands += ['set print entry-values no']

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:
commands += ['backtrace']

Expand Down Expand Up @@ -847,9 +851,12 @@ def __init__(self):
id("first break point")
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":
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,
r"<method-wrapper u?'__init__' of MyList object at ")

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed test_gdb when Python is compiled with flags -mcet -fcf-protection -O0.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
python-gdb now catchs ValueError on read_var(): when Python has no debug
symbols for example.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
python-gdb now catchs ``UnicodeDecodeError`` exceptions when calling
``string()``.
37 changes: 25 additions & 12 deletions Tools/gdb/libpython.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,12 +270,13 @@ def is_optimized_out(self):

def safe_tp_name(self):
try:
return self.type().field('tp_name').string()
except NullPyObjectPtr:
# NULL tp_name?
return 'unknown'
except RuntimeError:
# Can't even read the object at all?
ob_type = self.type()
tp_name = ob_type.field('tp_name')
return tp_name.string()
# NullPyObjectPtr: NULL tp_name?
# RuntimeError: Can't even read the object at all?
# UnicodeDecodeError: Failed to decode tp_name bytestring
except (NullPyObjectPtr, RuntimeError, UnicodeDecodeError):
return 'unknown'

def proxyval(self, visited):
Expand Down Expand Up @@ -349,7 +350,9 @@ def subclass_from_type(cls, t):
try:
tp_name = t.field('tp_name').string()
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
# class
return cls
Expand Down Expand Up @@ -617,7 +620,10 @@ class PyCFunctionObjectPtr(PyObjectPtr):

def proxyval(self, visited):
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')
if pyop_m_self.is_null():
Expand Down Expand Up @@ -1340,13 +1346,13 @@ def safe_name(self):
try:
name = self.field('descr')['d_base']['name'].string()
return repr(name)
except (NullPyObjectPtr, RuntimeError):
except (NullPyObjectPtr, RuntimeError, UnicodeDecodeError):
return '<unknown name>'

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

def safe_self_addresss(self):
Expand Down Expand Up @@ -1552,15 +1558,22 @@ def is_other_python_frame(self):
# Use the prettyprinter for the func:
func = frame.read_var(arg_name)
return str(func)
except ValueError:
return ('PyCFunction invocation (unable to read %s: '
'missing debuginfos?)' % arg_name)
except RuntimeError:
return 'PyCFunction invocation (unable to read %s)' % arg_name

if caller == 'wrapper_call':
arg_name = 'wp'
try:
func = frame.read_var('wp')
func = frame.read_var(arg_name)
return str(func)
except ValueError:
return ('<wrapper_call invocation (unable to read %s: '
'missing debuginfos?)>' % arg_name)
except RuntimeError:
return '<wrapper_call invocation>'
return '<wrapper_call invocation (unable to read %s)>' % arg_name

# This frame isn't worth reporting:
return False
Expand Down