Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d208416

Browse files
author
Victor Stinner
committed
Issue #13628: python-gdb.py is now able to retrieve more frames in the Python
traceback if Python is optimized. * delay the lookup of the size_t type, it is not available at startup * The second argument of the PyFrameObjectPtr constructor is optional, as done in other constructors * iter_builtins() and iter_globals() methods of PyFrameObjectPtr returns an empty tuple instead of None if Python is optimized * Fix py-bt and py-bt-full to handle correctly "optimized" frames * Frame.get_pyop() tries to get the frame pointer from PyEval_EvalCodeEx() if the pointer is optimized out in PyEval_EvalFrameEx()
1 parent 78ed83d commit d208416

3 files changed

Lines changed: 58 additions & 17 deletions

File tree

Lib/test/test_gdb.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,8 @@ def foo(a, b, c):
529529
re.DOTALL),
530530
'Unexpected gdb representation: %r\n%s' % (gdb_output, gdb_output))
531531

532+
@unittest.skipIf(python_is_optimized(),
533+
"Python was compiled with optimizations")
532534
class PyListTests(DebuggerTests):
533535
def assertListing(self, expected, actual):
534536
self.assertEndsWith(actual, expected)
@@ -571,6 +573,8 @@ def test_two_abs_args(self):
571573

572574
class StackNavigationTests(DebuggerTests):
573575
@unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
576+
@unittest.skipIf(python_is_optimized(),
577+
"Python was compiled with optimizations")
574578
def test_pyup_command(self):
575579
'Verify that the "py-up" command works'
576580
bt = self.get_stack_trace(script=self.get_sample_script(),
@@ -598,6 +602,8 @@ def test_up_at_top(self):
598602
'Unable to find an older python frame\n')
599603

600604
@unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
605+
@unittest.skipIf(python_is_optimized(),
606+
"Python was compiled with optimizations")
601607
def test_up_then_down(self):
602608
'Verify "py-up" followed by "py-down"'
603609
bt = self.get_stack_trace(script=self.get_sample_script(),
@@ -611,6 +617,8 @@ def test_up_then_down(self):
611617
$''')
612618

613619
class PyBtTests(DebuggerTests):
620+
@unittest.skipIf(python_is_optimized(),
621+
"Python was compiled with optimizations")
614622
def test_bt(self):
615623
'Verify that the "py-bt" command works'
616624
bt = self.get_stack_trace(script=self.get_sample_script(),
@@ -628,6 +636,8 @@ def test_bt(self):
628636
foo\(1, 2, 3\)
629637
''')
630638

639+
@unittest.skipIf(python_is_optimized(),
640+
"Python was compiled with optimizations")
631641
def test_bt_full(self):
632642
'Verify that the "py-bt-full" command works'
633643
bt = self.get_stack_trace(script=self.get_sample_script(),
@@ -639,10 +649,12 @@ def test_bt_full(self):
639649
#[0-9]+ Frame 0x[0-9a-f]+, for file .*gdb_sample.py, line 4, in foo \(a=1, b=2, c=3\)
640650
bar\(a, b, c\)
641651
#[0-9]+ Frame 0x[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \(\)
642-
foo\(1, 2, 3\)
652+
foo\(1, 2, 3\)
643653
''')
644654

645655
class PyPrintTests(DebuggerTests):
656+
@unittest.skipIf(python_is_optimized(),
657+
"Python was compiled with optimizations")
646658
def test_basic_command(self):
647659
'Verify that the "py-print" command works'
648660
bt = self.get_stack_trace(script=self.get_sample_script(),
@@ -657,19 +669,25 @@ def test_print_after_up(self):
657669
self.assertMultilineMatches(bt,
658670
r".*\nlocal 'c' = 3\nlocal 'b' = 2\nlocal 'a' = 1\n.*")
659671

672+
@unittest.skipIf(python_is_optimized(),
673+
"Python was compiled with optimizations")
660674
def test_printing_global(self):
661675
bt = self.get_stack_trace(script=self.get_sample_script(),
662676
cmds_after_breakpoint=['py-print __name__'])
663677
self.assertMultilineMatches(bt,
664678
r".*\nglobal '__name__' = '__main__'\n.*")
665679

680+
@unittest.skipIf(python_is_optimized(),
681+
"Python was compiled with optimizations")
666682
def test_printing_builtin(self):
667683
bt = self.get_stack_trace(script=self.get_sample_script(),
668684
cmds_after_breakpoint=['py-print len'])
669685
self.assertMultilineMatches(bt,
670686
r".*\nbuiltin 'len' = <built-in method len of module object at remote 0x[0-9a-f]+>\n.*")
671687

672688
class PyLocalsTests(DebuggerTests):
689+
@unittest.skipIf(python_is_optimized(),
690+
"Python was compiled with optimizations")
673691
def test_basic_command(self):
674692
bt = self.get_stack_trace(script=self.get_sample_script(),
675693
cmds_after_breakpoint=['py-locals'])
@@ -684,8 +702,6 @@ def test_locals_after_up(self):
684702
r".*\na = 1\nb = 2\nc = 3\n.*")
685703

686704
def test_main():
687-
if python_is_optimized():
688-
raise unittest.SkipTest("Python was compiled with optimizations")
689705
run_unittest(PrettyPrintTests,
690706
PyListTests,
691707
StackNavigationTests,

Misc/NEWS

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ Core and Builtins
9797
Library
9898
-------
9999

100-
- Issue #11813: Fix inspect.getattr_static for modules. Patch by Andreas
100+
- Issue #11813: Fix inspect.getattr_static for modules. Patch by Andreas
101101
Stührk.
102102

103103
- Issue #7502: Fix equality comparison for DocTestCase instances. Patch by
@@ -314,6 +314,12 @@ Build
314314

315315
- Issue #13326: Clean __pycache__ directories correctly on OpenBSD.
316316

317+
Tools/Demos
318+
-----------
319+
320+
- Issue #13628: python-gdb.py is now able to retrieve more frames in the Python
321+
traceback if Python is optimized.
322+
317323
Tests
318324
-----
319325

Tools/gdb/libpython.py

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@
4949
_type_char_ptr = gdb.lookup_type('char').pointer() # char*
5050
_type_unsigned_char_ptr = gdb.lookup_type('unsigned char').pointer() # unsigned char*
5151
_type_void_ptr = gdb.lookup_type('void').pointer() # void*
52-
_type_size_t = gdb.lookup_type('size_t')
5352

5453
SIZEOF_VOID_P = _type_void_ptr.sizeof
5554

@@ -435,11 +434,15 @@ def __repr__(self):
435434
self.address)
436435

437436
def _PyObject_VAR_SIZE(typeobj, nitems):
437+
if _PyObject_VAR_SIZE._type_size_t is None:
438+
_PyObject_VAR_SIZE._type_size_t = gdb.lookup_type('size_t')
439+
438440
return ( ( typeobj.field('tp_basicsize') +
439441
nitems * typeobj.field('tp_itemsize') +
440442
(SIZEOF_VOID_P - 1)
441443
) & ~(SIZEOF_VOID_P - 1)
442-
).cast(_type_size_t)
444+
).cast(_PyObject_VAR_SIZE._type_size_t)
445+
_PyObject_VAR_SIZE._type_size_t = None
443446

444447
class HeapTypeObjectPtr(PyObjectPtr):
445448
_typename = 'PyObject'
@@ -806,7 +809,7 @@ def proxyval(self, visited):
806809
class PyFrameObjectPtr(PyObjectPtr):
807810
_typename = 'PyFrameObject'
808811

809-
def __init__(self, gdbval, cast_to):
812+
def __init__(self, gdbval, cast_to=None):
810813
PyObjectPtr.__init__(self, gdbval, cast_to)
811814

812815
if not self.is_optimized_out():
@@ -840,7 +843,7 @@ def iter_globals(self):
840843
the global variables of this frame
841844
'''
842845
if self.is_optimized_out():
843-
return
846+
return ()
844847

845848
pyop_globals = self.pyop_field('f_globals')
846849
return pyop_globals.iteritems()
@@ -851,7 +854,7 @@ def iter_builtins(self):
851854
the builtin variables
852855
'''
853856
if self.is_optimized_out():
854-
return
857+
return ()
855858

856859
pyop_builtins = self.pyop_field('f_builtins')
857860
return pyop_builtins.iteritems()
@@ -938,6 +941,7 @@ def write_repr(self, out, visited):
938941
def print_traceback(self):
939942
if self.is_optimized_out():
940943
sys.stdout.write(' (frame information optimized out)\n')
944+
return
941945
visited = set()
942946
sys.stdout.write(' File "%s", line %i, in %s\n'
943947
% (self.co_filename.proxyval(visited),
@@ -1403,7 +1407,20 @@ def is_evalframeex(self):
14031407
def get_pyop(self):
14041408
try:
14051409
f = self._gdbframe.read_var('f')
1406-
return PyFrameObjectPtr.from_pyobject_ptr(f)
1410+
frame = PyFrameObjectPtr.from_pyobject_ptr(f)
1411+
if not frame.is_optimized_out():
1412+
return frame
1413+
# gdb is unable to get the "f" argument of PyEval_EvalFrameEx()
1414+
# because it was "optimized out". Try to get "f" from the frame
1415+
# of the caller, PyEval_EvalCodeEx().
1416+
orig_frame = frame
1417+
caller = self._gdbframe.older()
1418+
if caller:
1419+
f = caller.read_var('f')
1420+
frame = PyFrameObjectPtr.from_pyobject_ptr(f)
1421+
if not frame.is_optimized_out():
1422+
return frame
1423+
return orig_frame
14071424
except ValueError:
14081425
return None
14091426

@@ -1434,9 +1451,10 @@ def print_summary(self):
14341451
if pyop:
14351452
line = pyop.get_truncated_repr(MAX_OUTPUT_LEN)
14361453
write_unicode(sys.stdout, '#%i %s\n' % (self.get_index(), line))
1437-
line = pyop.current_line()
1438-
if line is not None:
1439-
sys.stdout.write(line)
1454+
if not pyop.is_optimized_out():
1455+
line = pyop.current_line()
1456+
if line is not None:
1457+
sys.stdout.write(' %s\n' % line.strip())
14401458
else:
14411459
sys.stdout.write('#%i (unable to read python frame information)\n' % self.get_index())
14421460
else:
@@ -1447,9 +1465,10 @@ def print_traceback(self):
14471465
pyop = self.get_pyop()
14481466
if pyop:
14491467
pyop.print_traceback()
1450-
line = pyop.current_line()
1451-
if line is not None:
1452-
sys.stdout.write(' %s\n' % line.strip())
1468+
if not pyop.is_optimized_out():
1469+
line = pyop.current_line()
1470+
if line is not None:
1471+
sys.stdout.write(' %s\n' % line.strip())
14531472
else:
14541473
sys.stdout.write(' (unable to read python frame information)\n')
14551474
else:
@@ -1495,7 +1514,7 @@ def invoke(self, args, from_tty):
14951514
return
14961515

14971516
pyop = frame.get_pyop()
1498-
if not pyop:
1517+
if not pyop or pyop.is_optimized_out():
14991518
print 'Unable to read information on python frame'
15001519
return
15011520

0 commit comments

Comments
 (0)