| 
									
										
										
										
											2023-09-28 13:24:15 +02:00
										 |  |  | import textwrap | 
					
						
							|  |  |  | import unittest | 
					
						
							|  |  |  | from test import support | 
					
						
							|  |  |  | from test.support import python_is_optimized | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-28 19:04:01 +02:00
										 |  |  | from .util import setup_module, DebuggerTests, CET_PROTECTION, SAMPLE_SCRIPT | 
					
						
							| 
									
										
										
										
											2023-09-28 13:24:15 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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' | 
					
						
							| 
									
										
										
										
											2023-09-28 19:04:01 +02:00
										 |  |  |         bt = self.get_stack_trace(script=SAMPLE_SCRIPT, | 
					
						
							| 
									
										
										
										
											2023-09-28 13:24:15 +02:00
										 |  |  |                                   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' | 
					
						
							| 
									
										
										
										
											2023-09-28 19:04:01 +02:00
										 |  |  |         bt = self.get_stack_trace(script=SAMPLE_SCRIPT, | 
					
						
							| 
									
										
										
										
											2023-09-28 13:24:15 +02:00
										 |  |  |                                   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") | 
					
						
							| 
									
										
										
										
											2024-04-25 18:11:59 +03:00
										 |  |  |     @support.requires_gil_enabled() | 
					
						
							| 
									
										
										
										
											2023-09-28 13:24:15 +02:00
										 |  |  |     @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 ") |