• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import textwrap
2import unittest
3from test import support
4from test.support import python_is_optimized
5
6from .util import setup_module, DebuggerTests, CET_PROTECTION, SAMPLE_SCRIPT
7
8
9def setUpModule():
10    setup_module()
11
12
13class PyBtTests(DebuggerTests):
14    @unittest.skipIf(python_is_optimized(),
15                     "Python was compiled with optimizations")
16    def test_bt(self):
17        'Verify that the "py-bt" command works'
18        bt = self.get_stack_trace(script=SAMPLE_SCRIPT,
19                                  cmds_after_breakpoint=['py-bt'])
20        self.assertMultilineMatches(bt,
21                                    r'''^.*
22Traceback \(most recent call first\):
23  <built-in method id of module object .*>
24  File ".*gdb_sample.py", line 10, in baz
25    id\(42\)
26  File ".*gdb_sample.py", line 7, in bar
27    baz\(a, b, c\)
28  File ".*gdb_sample.py", line 4, in foo
29    bar\(a=a, b=b, c=c\)
30  File ".*gdb_sample.py", line 12, in <module>
31    foo\(1, 2, 3\)
32''')
33
34    @unittest.skipIf(python_is_optimized(),
35                     "Python was compiled with optimizations")
36    def test_bt_full(self):
37        'Verify that the "py-bt-full" command works'
38        bt = self.get_stack_trace(script=SAMPLE_SCRIPT,
39                                  cmds_after_breakpoint=['py-bt-full'])
40        self.assertMultilineMatches(bt,
41                                    r'''^.*
42#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\)
43    baz\(a, b, c\)
44#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 4, in foo \(a=1, b=2, c=3\)
45    bar\(a=a, b=b, c=c\)
46#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \(\)
47    foo\(1, 2, 3\)
48''')
49
50    @unittest.skipIf(python_is_optimized(),
51                     "Python was compiled with optimizations")
52    @support.requires_gil_enabled()
53    @support.requires_resource('cpu')
54    def test_threads(self):
55        'Verify that "py-bt" indicates threads that are waiting for the GIL'
56        cmd = '''
57from threading import Thread
58
59class TestThread(Thread):
60    # These threads would run forever, but we'll interrupt things with the
61    # debugger
62    def run(self):
63        i = 0
64        while 1:
65             i += 1
66
67t = {}
68for i in range(4):
69   t[i] = TestThread()
70   t[i].start()
71
72# Trigger a breakpoint on the main thread
73id(42)
74
75'''
76        # Verify with "py-bt":
77        gdb_output = self.get_stack_trace(cmd,
78                                          cmds_after_breakpoint=['thread apply all py-bt'])
79        self.assertIn('Waiting for the GIL', gdb_output)
80
81        # Verify with "py-bt-full":
82        gdb_output = self.get_stack_trace(cmd,
83                                          cmds_after_breakpoint=['thread apply all py-bt-full'])
84        self.assertIn('Waiting for the GIL', gdb_output)
85
86    @unittest.skipIf(python_is_optimized(),
87                     "Python was compiled with optimizations")
88    # Some older versions of gdb will fail with
89    #  "Cannot find new threads: generic error"
90    # unless we add LD_PRELOAD=PATH-TO-libpthread.so.1 as a workaround
91    def test_gc(self):
92        'Verify that "py-bt" indicates if a thread is garbage-collecting'
93        cmd = ('from gc import collect\n'
94               'id(42)\n'
95               'def foo():\n'
96               '    collect()\n'
97               'def bar():\n'
98               '    foo()\n'
99               'bar()\n')
100        # Verify with "py-bt":
101        gdb_output = self.get_stack_trace(cmd,
102                                          cmds_after_breakpoint=['break update_refs', 'continue', 'py-bt'],
103                                          )
104        self.assertIn('Garbage-collecting', gdb_output)
105
106        # Verify with "py-bt-full":
107        gdb_output = self.get_stack_trace(cmd,
108                                          cmds_after_breakpoint=['break update_refs', 'continue', 'py-bt-full'],
109                                          )
110        self.assertIn('Garbage-collecting', gdb_output)
111
112    @unittest.skipIf(python_is_optimized(),
113                     "Python was compiled with optimizations")
114    def test_wrapper_call(self):
115        cmd = textwrap.dedent('''
116            class MyList(list):
117                def __init__(self):
118                    super(*[]).__init__()   # wrapper_call()
119
120            id("first break point")
121            l = MyList()
122        ''')
123        cmds_after_breakpoint = ['break wrapper_call', 'continue']
124        if CET_PROTECTION:
125            # bpo-32962: same case as in get_stack_trace():
126            # we need an additional 'next' command in order to read
127            # arguments of the innermost function of the call stack.
128            cmds_after_breakpoint.append('next')
129        cmds_after_breakpoint.append('py-bt')
130
131        # Verify with "py-bt":
132        gdb_output = self.get_stack_trace(cmd,
133                                          cmds_after_breakpoint=cmds_after_breakpoint)
134        self.assertRegex(gdb_output,
135                         r"<method-wrapper u?'__init__' of MyList object at ")
136