import textwrap import unittest from test import support from test.support import python_is_optimized from .util import setup_module, DebuggerTests, CET_PROTECTION, SAMPLE_SCRIPT def setUpModule(): setup_module() class PyBtTests(DebuggerTests): @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") def test_bt(self): 'Verify that the "py-bt" command works' bt = self.get_stack_trace(script=SAMPLE_SCRIPT, cmds_after_breakpoint=['py-bt']) self.assertMultilineMatches(bt, r'''^.* Traceback \(most recent call first\): File ".*gdb_sample.py", line 10, in baz id\(42\) File ".*gdb_sample.py", line 7, in bar baz\(a, b, c\) File ".*gdb_sample.py", line 4, in foo bar\(a=a, b=b, c=c\) File ".*gdb_sample.py", line 12, in foo\(1, 2, 3\) ''') @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") def test_bt_full(self): 'Verify that the "py-bt-full" command works' bt = self.get_stack_trace(script=SAMPLE_SCRIPT, cmds_after_breakpoint=['py-bt-full']) self.assertMultilineMatches(bt, r'''^.* #[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\) baz\(a, b, c\) #[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 4, in foo \(a=1, b=2, c=3\) bar\(a=a, b=b, c=c\) #[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 12, in \(\) foo\(1, 2, 3\) ''') @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") @support.requires_resource('cpu') def test_threads(self): 'Verify that "py-bt" indicates threads that are waiting for the GIL' cmd = ''' from threading import Thread class TestThread(Thread): # These threads would run forever, but we'll interrupt things with the # debugger def run(self): i = 0 while 1: i += 1 t = {} for i in range(4): t[i] = TestThread() t[i].start() # Trigger a breakpoint on the main thread id(42) ''' # Verify with "py-bt": gdb_output = self.get_stack_trace(cmd, cmds_after_breakpoint=['thread apply all py-bt']) self.assertIn('Waiting for the GIL', gdb_output) # Verify with "py-bt-full": gdb_output = self.get_stack_trace(cmd, cmds_after_breakpoint=['thread apply all py-bt-full']) self.assertIn('Waiting for the GIL', gdb_output) @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") # Some older versions of gdb will fail with # "Cannot find new threads: generic error" # unless we add LD_PRELOAD=PATH-TO-libpthread.so.1 as a workaround def test_gc(self): 'Verify that "py-bt" indicates if a thread is garbage-collecting' cmd = ('from gc import collect\n' 'id(42)\n' 'def foo():\n' ' collect()\n' 'def bar():\n' ' foo()\n' 'bar()\n') # Verify with "py-bt": gdb_output = self.get_stack_trace(cmd, cmds_after_breakpoint=['break update_refs', 'continue', 'py-bt'], ) self.assertIn('Garbage-collecting', gdb_output) # Verify with "py-bt-full": gdb_output = self.get_stack_trace(cmd, cmds_after_breakpoint=['break update_refs', 'continue', 'py-bt-full'], ) self.assertIn('Garbage-collecting', gdb_output) @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") def test_wrapper_call(self): cmd = textwrap.dedent(''' class MyList(list): def __init__(self): super(*[]).__init__() # wrapper_call() id("first break point") l = MyList() ''') cmds_after_breakpoint = ['break wrapper_call', 'continue'] if CET_PROTECTION: # 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. cmds_after_breakpoint.append('next') cmds_after_breakpoint.append('py-bt') # Verify with "py-bt": gdb_output = self.get_stack_trace(cmd, cmds_after_breakpoint=cmds_after_breakpoint) self.assertRegex(gdb_output, r"