| 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\): |
| <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=a, b=b, c=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=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 <module> \(\) |
| foo\(1, 2, 3\) |
| ''') |
| |
| @unittest.skipIf(python_is_optimized(), |
| "Python was compiled with optimizations") |
| @support.requires_gil_enabled() |
| @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"<method-wrapper u?'__init__' of MyList object at ") |