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=self.get_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, b, c\) File ".*gdb_sample.py", line 12, in <module> 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=self.get_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, b, c\) #[0-9]+ Frame 0x[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \(\) foo\(1, 2, 3\) ''')
class PyPrintTests(DebuggerTests): @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") def test_basic_command(self): 'Verify that the "py-print" command works' bt = self.get_stack_trace(script=self.get_sample_script(), cmds_after_breakpoint=['py-print args']) self.assertMultilineMatches(bt, r".*\nlocal 'args' = \(1, 2, 3\)\n.*") @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") def test_print_after_up(self): bt = self.get_stack_trace(script=self.get_sample_script(), cmds_after_breakpoint=['py-up', 'py-print c', 'py-print b', 'py-print a']) self.assertMultilineMatches(bt, r".*\nlocal 'c' = 3\nlocal 'b' = 2\nlocal 'a' = 1\n.*") @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") def test_printing_global(self): bt = self.get_stack_trace(script=self.get_sample_script(), cmds_after_breakpoint=['py-print __name__']) self.assertMultilineMatches(bt, r".*\nglobal '__name__' = '__main__'\n.*") @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") def test_printing_builtin(self): bt = self.get_stack_trace(script=self.get_sample_script(), cmds_after_breakpoint=['py-print len']) self.assertMultilineMatches(bt, r".*\nbuiltin 'len' = <built-in method len of module object at remote 0x-?[0-9a-f]+>\n.*")
class PyLocalsTests(DebuggerTests): @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") def test_basic_command(self): bt = self.get_stack_trace(script=self.get_sample_script(), cmds_after_breakpoint=['py-locals']) self.assertMultilineMatches(bt, r".*\nargs = \(1, 2, 3\)\n.*") @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") def test_locals_after_up(self): bt = self.get_stack_trace(script=self.get_sample_script(), cmds_after_breakpoint=['py-up', 'py-locals']) self.assertMultilineMatches(bt, r".*\na = 1\nb = 2\nc = 3\n.*")
class StackNavigationTests(DebuggerTests): @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") def test_pyup_command(self): 'Verify that the "py-up" command works' bt = self.get_stack_trace(script=self.get_sample_script(), cmds_after_breakpoint=['py-up', 'py-up']) 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\) $''') @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") def test_down_at_bottom(self): 'Verify handling of "py-down" at the bottom of the stack' bt = self.get_stack_trace(script=self.get_sample_script(), cmds_after_breakpoint=['py-down']) self.assertEndsWith(bt, 'Unable to find a newer python frame\n') @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") def test_up_at_top(self): 'Verify handling of "py-up" at the top of the stack' bt = self.get_stack_trace(script=self.get_sample_script(), cmds_after_breakpoint=['py-up'] * 5) self.assertEndsWith(bt, 'Unable to find an older python frame\n') @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") def test_up_then_down(self): 'Verify "py-up" followed by "py-down"' bt = self.get_stack_trace( script=self.get_sample_script(), cmds_after_breakpoint=['py-up', 'py-up', 'py-down']) 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 10, in baz \(args=\(1, 2, 3\)\) id\(42\) $''')
class StackNavigationTests(DebuggerTests): @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") def test_pyup_command(self): 'Verify that the "py-up" command works' bt = self.get_stack_trace(source=SAMPLE_WITH_C_CALL, cmds_after_breakpoint=['py-up', 'py-up']) self.assertMultilineMatches( bt, r'''^.* #[0-9]+ Frame 0x-?[0-9a-f]+, for file <string>, line 12, in baz \(args=\(1, 2, 3\)\) #[0-9]+ <built-in method pyobject_fastcall of module object at remote 0x[0-9a-f]+> $''') @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") def test_down_at_bottom(self): 'Verify handling of "py-down" at the bottom of the stack' bt = self.get_stack_trace(script=self.get_sample_script(), cmds_after_breakpoint=['py-down']) self.assertEndsWith(bt, 'Unable to find a newer python frame\n') @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") def test_up_at_top(self): 'Verify handling of "py-up" at the top of the stack' bt = self.get_stack_trace(script=self.get_sample_script(), cmds_after_breakpoint=['py-up'] * 5) self.assertEndsWith(bt, 'Unable to find an older python frame\n') @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") def test_up_then_down(self): 'Verify "py-up" followed by "py-down"' bt = self.get_stack_trace( source=SAMPLE_WITH_C_CALL, cmds_after_breakpoint=['py-up', 'py-up', 'py-down']) self.assertMultilineMatches( bt, r'''^.* #[0-9]+ Frame 0x-?[0-9a-f]+, for file <string>, line 12, in baz \(args=\(1, 2, 3\)\) #[0-9]+ <built-in method pyobject_fastcall of module object at remote 0x[0-9a-f]+> #[0-9]+ Frame 0x-?[0-9a-f]+, for file <string>, line 12, in baz \(args=\(1, 2, 3\)\) $''')
def test_main(): if python_is_optimized(): raise unittest.SkipTest("Python was compiled with optimizations") run_unittest(PrettyPrintTests, PyListTests, StackNavigationTests, PyBtTests, PyPrintTests, PyLocalsTests )
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=self.get_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, b, c\) File ".*gdb_sample.py", line 12, in <module> 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=self.get_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, b, c\) #[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \(\) foo\(1, 2, 3\) ''') @unittest.skipUnless(_thread, "Python was compiled without thread support") 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 @unittest.skipUnless(_thread, "Python was compiled without thread support") 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") # 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 @unittest.skipUnless(_thread, "Python was compiled without thread support") def test_pycfunction(self): 'Verify that "py-bt" displays invocations of PyCFunction instances' cmd = ('from time import sleep\n' 'def foo():\n' ' sleep(1)\n' 'def bar():\n' ' foo()\n' 'bar()\n') # Verify with "py-bt": gdb_output = self.get_stack_trace( cmd, breakpoint='time_sleep', cmds_after_breakpoint=['bt', 'py-bt'], ) self.assertIn('<built-in method sleep', gdb_output) # Verify with "py-bt-full": gdb_output = self.get_stack_trace( cmd, breakpoint='time_sleep', cmds_after_breakpoint=['py-bt-full'], ) self.assertIn('#0 <built-in method sleep', gdb_output)
pass foo(3, 4, 5) id(foo.__code__)''', breakpoint='builtin_id', cmds_after_breakpoint=[ 'print (PyFrameObject*)(((PyCodeObject*)v)->co_zombieframe)' ]) self.assertTrue( re.match( '.*\s+\$1 =\s+Frame 0x-?[0-9a-f]+, for file <string>, line 3, in foo \(\)\s+.*', gdb_output, re.DOTALL), 'Unexpected gdb representation: %r\n%s' % (gdb_output, gdb_output)) @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") class PyListTests(DebuggerTests): def assertListing(self, expected, actual): self.assertEndsWith(actual, expected) def test_basic_command(self): 'Verify that the "py-list" command works' bt = self.get_stack_trace(script=self.get_sample_script(), cmds_after_breakpoint=['py-list']) self.assertListing( ' 5 \n' ' 6 def bar(a, b, c):\n' ' 7 baz(a, b, c)\n' ' 8 \n'
def test_frames(self): gdb_output = self.get_stack_trace(''' def foo(a, b, c): pass foo(3, 4, 5) id(foo.__code__)''', breakpoint='builtin_id', cmds_after_breakpoint=['print (PyFrameObject*)(((PyCodeObject*)v)->co_zombieframe)'] ) self.assertTrue(re.match('.*\s+\$1 =\s+Frame 0x-?[0-9a-f]+, for file <string>, line 3, in foo \(\)\s+.*', gdb_output, re.DOTALL), 'Unexpected gdb representation: %r\n%s' % (gdb_output, gdb_output)) @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") class PyListTests(DebuggerTests): def assertListing(self, expected, actual): self.assertEndsWith(actual, expected) def test_basic_command(self): 'Verify that the "py-list" command works' bt = self.get_stack_trace(script=self.get_sample_script(), cmds_after_breakpoint=['py-list']) self.assertListing(' 5 \n' ' 6 def bar(a, b, c):\n' ' 7 baz(a, b, c)\n' ' 8 \n' ' 9 def baz(*args):\n'
def test_python_is_optimized(self): self.assertIsInstance(support.python_is_optimized(), bool)
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=self.get_sample_script(), cmds_after_breakpoint=['py-bt']) self.assertMultilineMatches(bt, r'''^.* Traceback \(most recent call first\): <built-in method id of module object .*> 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, b, c\) File ".*gdb_sample.py", line 12, in <module> 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=self.get_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, b, c\) #[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \(\) foo\(1, 2, 3\) ''') 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") # 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_pycfunction(self): 'Verify that "py-bt" displays invocations of PyCFunction instances' # Various optimizations multiply the code paths by which these are # called, so test a variety of calling conventions. for py_name, py_args, c_name, expected_frame_number in ( ('gmtime', '', 'time_gmtime', 1), # METH_VARARGS ('len', '[]', 'builtin_len', 1), # METH_O ('locals', '', 'builtin_locals', 1), # METH_NOARGS ('iter', '[]', 'builtin_iter', 1), # METH_FASTCALL ('sorted', '[]', 'builtin_sorted', 1), # METH_FASTCALL|METH_KEYWORDS ): with self.subTest(c_name): cmd = ('from time import gmtime\n' # (not always needed) 'def foo():\n' f' {py_name}({py_args})\n' 'def bar():\n' ' foo()\n' 'bar()\n') # Verify with "py-bt": gdb_output = self.get_stack_trace( cmd, breakpoint=c_name, cmds_after_breakpoint=['bt', 'py-bt'], ) self.assertIn(f'<built-in method {py_name}', gdb_output) # Verify with "py-bt-full": gdb_output = self.get_stack_trace( cmd, breakpoint=c_name, cmds_after_breakpoint=['py-bt-full'], ) self.assertIn( f'#{expected_frame_number} <built-in method {py_name}', 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"<method-wrapper u?'__init__' of MyList object at ")
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=self.get_sample_script(), cmds_after_breakpoint=['py-bt']) self.assertMultilineMatches( bt, r'''^.* Traceback \(most recent call first\): <built-in method id of module object .*> 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, b, c\) File ".*gdb_sample.py", line 12, in <module> 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=self.get_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, b, c\) #[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \(\) foo\(1, 2, 3\) ''') 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") # 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_pycfunction(self): 'Verify that "py-bt" displays invocations of PyCFunction instances' # Tested function must not be defined with METH_NOARGS or METH_O, # otherwise call_function() doesn't call PyCFunction_Call() cmd = ('from time import gmtime\n' 'def foo():\n' ' gmtime(1)\n' 'def bar():\n' ' foo()\n' 'bar()\n') # Verify with "py-bt": gdb_output = self.get_stack_trace( cmd, breakpoint='time_gmtime', cmds_after_breakpoint=['bt', 'py-bt'], ) self.assertIn('<built-in method gmtime', gdb_output) # Verify with "py-bt-full": gdb_output = self.get_stack_trace( cmd, breakpoint='time_gmtime', cmds_after_breakpoint=['py-bt-full'], ) self.assertIn('#2 <built-in method gmtime', 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() ''') # Verify with "py-bt": gdb_output = self.get_stack_trace( cmd, cmds_after_breakpoint=['break wrapper_call', 'continue', 'py-bt']) self.assertRegex(gdb_output, r"<method-wrapper u?'__init__' of MyList object at ")
pass foo(3, 4, 5) id(foo.__code__)""", breakpoint="builtin_id", cmds_after_breakpoint=["print (PyFrameObject*)(((PyCodeObject*)v)->co_zombieframe)"], ) self.assertTrue( re.match( ".*\s+\$1 =\s+Frame 0x-?[0-9a-f]+, for file <string>, line 3, in foo \(\)\s+.*", gdb_output, re.DOTALL ), "Unexpected gdb representation: %r\n%s" % (gdb_output, gdb_output), ) @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") class PyListTests(DebuggerTests): def assertListing(self, expected, actual): self.assertEndsWith(actual, expected) def test_basic_command(self): 'Verify that the "py-list" command works' bt = self.get_stack_trace(script=self.get_sample_script(), cmds_after_breakpoint=["py-list"]) self.assertListing( " 5 \n" " 6 def bar(a, b, c):\n" " 7 baz(a, b, c)\n" " 8 \n" " 9 def baz(*args):\n" " >10 id(42)\n"
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=self.get_sample_script(), cmds_after_breakpoint=['py-bt']) self.assertMultilineMatches( bt, r'''^.* Traceback \(most recent call first\): <built-in method id of module object .*> 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, b, c\) File ".*gdb_sample.py", line 12, in <module> 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=self.get_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, b, c\) #[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \(\) foo\(1, 2, 3\) ''') 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") # 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 # # gdb will also generate many erroneous errors such as: # Function "meth_varargs" not defined. # This is because we are calling functions from an "external" module # (_testcapimodule) rather than compiled-in functions. It seems difficult # to suppress these. See also the comment in DebuggerTests.get_stack_trace def test_pycfunction(self): 'Verify that "py-bt" displays invocations of PyCFunction instances' # Various optimizations multiply the code paths by which these are # called, so test a variety of calling conventions. for func_name, args, expected_frame in ( ('meth_varargs', '', 1), ('meth_varargs_keywords', '', 1), ('meth_o', '[]', 1), ('meth_noargs', '', 1), ('meth_fastcall', '', 1), ('meth_fastcall_keywords', '', 1), ): for obj in ( '_testcapi', '_testcapi.MethClass', '_testcapi.MethClass()', '_testcapi.MethStatic()', # XXX: bound methods don't yet give nice tracebacks # '_testcapi.MethInstance()', ): with self.subTest(f'{obj}.{func_name}'): cmd = textwrap.dedent(f''' import _testcapi def foo(): {obj}.{func_name}({args}) def bar(): foo() bar() ''') # Verify with "py-bt": gdb_output = self.get_stack_trace( cmd, breakpoint=func_name, cmds_after_breakpoint=['bt', 'py-bt'], ) self.assertIn(f'<built-in method {func_name}', gdb_output) # Verify with "py-bt-full": gdb_output = self.get_stack_trace( cmd, breakpoint=func_name, cmds_after_breakpoint=['py-bt-full'], ) self.assertIn( f'#{expected_frame} <built-in method {func_name}', 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"<method-wrapper u?'__init__' of MyList object at ")
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=self.get_sample_script(), cmds_after_breakpoint=['py-bt']) self.assertMultilineMatches( bt, """^.* Traceback \\(most recent call first\\): <built-in method id of module object .*> 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, b, c\\) File ".*gdb_sample.py", line 12, in <module> 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=self.get_sample_script(), cmds_after_breakpoint=['py-bt-full']) self.assertMultilineMatches( bt, """^.* #[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, b, c\\) #[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \\(\\) foo\\(1, 2, 3\\) """) @unittest.skipUnless(_thread, 'Python was compiled without thread support') 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) """ gdb_output = self.get_stack_trace( cmd, cmds_after_breakpoint=['thread apply all py-bt']) self.assertIn('Waiting for the GIL', gdb_output) 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') @unittest.skipUnless(_thread, 'Python was compiled without thread support') def test_gc(self): """Verify that "py-bt" indicates if a thread is garbage-collecting""" cmd = """from gc import collect id(42) def foo(): collect() def bar(): foo() bar() """ gdb_output = self.get_stack_trace( cmd, cmds_after_breakpoint=['break update_refs', 'continue', 'py-bt']) self.assertIn('Garbage-collecting', gdb_output) 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') @unittest.skipUnless(_thread, 'Python was compiled without thread support') def test_pycfunction(self): """Verify that "py-bt" displays invocations of PyCFunction instances""" cmd = ( 'from time import gmtime\ndef foo():\n gmtime(1)\ndef bar():\n foo()\nbar()\n' ) gdb_output = self.get_stack_trace( cmd, breakpoint='time_gmtime', cmds_after_breakpoint=['bt', 'py-bt']) self.assertIn('<built-in method gmtime', gdb_output) gdb_output = self.get_stack_trace(cmd, breakpoint='time_gmtime', cmds_after_breakpoint=['py-bt-full']) self.assertIn('#1 <built-in method gmtime', 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() """) gdb_output = self.get_stack_trace( cmd, cmds_after_breakpoint=['break wrapper_call', 'continue', 'py-bt']) self.assertRegex(gdb_output, "<method-wrapper u?'__init__' of MyList object at ")