Skip to content

Commit 5279759

Browse files
authored
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 commit 019d33b) * bpo-32962: python-gdb catchs UnicodeDecodeError (GH-7693) python-gdb now catchs UnicodeDecodeError exceptions when calling string(). (cherry picked from commit d22fc0b) * 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 commit 9b7c74c) (cherry picked from commit ca4cb84)
1 parent d85d479 commit 5279759

File tree

5 files changed

+48
-18
lines changed

5 files changed

+48
-18
lines changed

Lib/test/test_gdb.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,11 @@ def get_stack_trace(self, source=None, script=None,
168168
commands += ['set print entry-values no']
169169

170170
if cmds_after_breakpoint:
171-
commands += cmds_after_breakpoint
171+
# bpo-32962: When Python is compiled with -mcet -fcf-protection,
172+
# arguments are unusable before running the first instruction
173+
# of the function entry point. The 'next' command makes the
174+
# required first step.
175+
commands += ['next'] + cmds_after_breakpoint
172176
else:
173177
commands += ['backtrace']
174178

@@ -859,9 +863,12 @@ def __init__(self):
859863
id("first break point")
860864
l = MyList()
861865
''')
866+
# bpo-32962: same case as in get_stack_trace():
867+
# we need an additional 'next' command in order to read
868+
# arguments of the innermost function of the call stack.
862869
# Verify with "py-bt":
863870
gdb_output = self.get_stack_trace(cmd,
864-
cmds_after_breakpoint=['break wrapper_call', 'continue', 'py-bt'])
871+
cmds_after_breakpoint=['break wrapper_call', 'continue', 'next', 'py-bt'])
865872
self.assertRegex(gdb_output,
866873
r"<method-wrapper u?'__init__' of MyList object at ")
867874

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed test_gdb when Python is compiled with flags -mcet -fcf-protection -O0.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
python-gdb now catchs ValueError on read_var(): when Python has no debug
2+
symbols for example.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
python-gdb now catchs ``UnicodeDecodeError`` exceptions when calling
2+
``string()``.

Tools/gdb/libpython.py

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -270,12 +270,13 @@ def is_optimized_out(self):
270270

271271
def safe_tp_name(self):
272272
try:
273-
return self.type().field('tp_name').string()
274-
except NullPyObjectPtr:
275-
# NULL tp_name?
276-
return 'unknown'
277-
except RuntimeError:
278-
# Can't even read the object at all?
273+
ob_type = self.type()
274+
tp_name = ob_type.field('tp_name')
275+
return tp_name.string()
276+
# NullPyObjectPtr: NULL tp_name?
277+
# RuntimeError: Can't even read the object at all?
278+
# UnicodeDecodeError: Failed to decode tp_name bytestring
279+
except (NullPyObjectPtr, RuntimeError, UnicodeDecodeError):
279280
return 'unknown'
280281

281282
def proxyval(self, visited):
@@ -349,7 +350,9 @@ def subclass_from_type(cls, t):
349350
try:
350351
tp_name = t.field('tp_name').string()
351352
tp_flags = int(t.field('tp_flags'))
352-
except RuntimeError:
353+
# RuntimeError: NULL pointers
354+
# UnicodeDecodeError: string() fails to decode the bytestring
355+
except (RuntimeError, UnicodeDecodeError):
353356
# Handle any kind of error e.g. NULL ptrs by simply using the base
354357
# class
355358
return cls
@@ -617,7 +620,10 @@ class PyCFunctionObjectPtr(PyObjectPtr):
617620

618621
def proxyval(self, visited):
619622
m_ml = self.field('m_ml') # m_ml is a (PyMethodDef*)
620-
ml_name = m_ml['ml_name'].string()
623+
try:
624+
ml_name = m_ml['ml_name'].string()
625+
except UnicodeDecodeError:
626+
ml_name = '<ml_name:UnicodeDecodeError>'
621627

622628
pyop_m_self = self.pyop_field('m_self')
623629
if pyop_m_self.is_null():
@@ -1340,13 +1346,13 @@ def safe_name(self):
13401346
try:
13411347
name = self.field('descr')['d_base']['name'].string()
13421348
return repr(name)
1343-
except (NullPyObjectPtr, RuntimeError):
1349+
except (NullPyObjectPtr, RuntimeError, UnicodeDecodeError):
13441350
return '<unknown name>'
13451351

13461352
def safe_tp_name(self):
13471353
try:
13481354
return self.field('self')['ob_type']['tp_name'].string()
1349-
except (NullPyObjectPtr, RuntimeError):
1355+
except (NullPyObjectPtr, RuntimeError, UnicodeDecodeError):
13501356
return '<unknown tp_name>'
13511357

13521358
def safe_self_addresss(self):
@@ -1541,31 +1547,43 @@ def is_other_python_frame(self):
15411547
return False
15421548

15431549
if caller == 'PyCFunction_Call':
1550+
arg_name = 'func'
15441551
# Within that frame:
15451552
# "func" is the local containing the PyObject* of the
15461553
# PyCFunctionObject instance
15471554
# "f" is the same value, but cast to (PyCFunctionObject*)
15481555
# "self" is the (PyObject*) of the 'self'
15491556
try:
15501557
# Use the prettyprinter for the func:
1551-
func = frame.read_var('func')
1558+
func = frame.read_var(arg_name)
15521559
return str(func)
1560+
except ValueError:
1561+
return ('PyCFunction invocation (unable to read %s: '
1562+
'missing debuginfos?)' % arg_name)
15531563
except RuntimeError:
1554-
return 'PyCFunction invocation (unable to read "func")'
1564+
return 'PyCFunction invocation (unable to read %s)' % arg_name
15551565

15561566
elif caller == '_PyCFunction_FastCallDict':
1567+
arg_name = 'func_obj'
15571568
try:
1558-
func = frame.read_var('func_obj')
1569+
func = frame.read_var(arg_name)
15591570
return str(func)
1571+
except ValueError:
1572+
return ('PyCFunction invocation (unable to read %s: '
1573+
'missing debuginfos?)' % arg_name)
15601574
except RuntimeError:
1561-
return 'PyCFunction invocation (unable to read "func_obj")'
1575+
return 'PyCFunction invocation (unable to read %s)' % arg_name
15621576

15631577
if caller == 'wrapper_call':
1578+
arg_name = 'wp'
15641579
try:
1565-
func = frame.read_var('wp')
1580+
func = frame.read_var(arg_name)
15661581
return str(func)
1582+
except ValueError:
1583+
return ('<wrapper_call invocation (unable to read %s: '
1584+
'missing debuginfos?)>' % arg_name)
15671585
except RuntimeError:
1568-
return '<wrapper_call invocation>'
1586+
return '<wrapper_call invocation (unable to read %s)>' % arg_name
15691587

15701588
# This frame isn't worth reporting:
15711589
return False

0 commit comments

Comments
 (0)