| 
									
										
										
										
											2023-09-28 13:24:15 +02:00
										 |  |  | import re | 
					
						
							|  |  |  | import unittest | 
					
						
							|  |  |  | from test.support import python_is_optimized | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-28 19:04:01 +02:00
										 |  |  | from .util import run_gdb, setup_module, DebuggerTests, SAMPLE_SCRIPT | 
					
						
							| 
									
										
										
										
											2023-09-28 13:24:15 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def setUpModule(): | 
					
						
							|  |  |  |     setup_module() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def gdb_has_frame_select(): | 
					
						
							|  |  |  |     # Does this build of gdb have gdb.Frame.select ? | 
					
						
							|  |  |  |     stdout, stderr = run_gdb("--eval-command=python print(dir(gdb.Frame))") | 
					
						
							|  |  |  |     m = re.match(r'.*\[(.*)\].*', stdout) | 
					
						
							|  |  |  |     if not m: | 
					
						
							|  |  |  |         raise unittest.SkipTest( | 
					
						
							|  |  |  |             f"Unable to parse output from gdb.Frame.select test\n" | 
					
						
							|  |  |  |             f"stdout={stdout!r}\n" | 
					
						
							|  |  |  |             f"stderr={stderr!r}\n") | 
					
						
							|  |  |  |     gdb_frame_dir = m.group(1).split(', ') | 
					
						
							|  |  |  |     return "'select'" in gdb_frame_dir | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | HAS_PYUP_PYDOWN = gdb_has_frame_select() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @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' | 
					
						
							| 
									
										
										
										
											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-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' | 
					
						
							|  |  |  |                            '  11    \n' | 
					
						
							|  |  |  |                            '  12    foo(1, 2, 3)\n', | 
					
						
							|  |  |  |                            bt) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_one_abs_arg(self): | 
					
						
							|  |  |  |         'Verify the "py-list" command with one absolute argument' | 
					
						
							| 
									
										
										
										
											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-list 9']) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assertListing('   9    def baz(*args):\n' | 
					
						
							|  |  |  |                            ' >10        id(42)\n' | 
					
						
							|  |  |  |                            '  11    \n' | 
					
						
							|  |  |  |                            '  12    foo(1, 2, 3)\n', | 
					
						
							|  |  |  |                            bt) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_two_abs_args(self): | 
					
						
							|  |  |  |         'Verify the "py-list" command with two absolute arguments' | 
					
						
							| 
									
										
										
										
											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-list 1,3']) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assertListing('   1    # Sample script for use by test_gdb\n' | 
					
						
							|  |  |  |                            '   2    \n' | 
					
						
							|  |  |  |                            '   3    def foo(a, b, c):\n', | 
					
						
							|  |  |  |                            bt) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | SAMPLE_WITH_C_CALL = """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from _testcapi import pyobject_vectorcall | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def foo(a, b, c): | 
					
						
							|  |  |  |     bar(a, b, c) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def bar(a, b, c): | 
					
						
							|  |  |  |     pyobject_vectorcall(baz, (a, b, c), None) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def baz(*args): | 
					
						
							|  |  |  |     id(42) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | foo(1, 2, 3) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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_vectorcall 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' | 
					
						
							| 
									
										
										
										
											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-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' | 
					
						
							| 
									
										
										
										
											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-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_vectorcall 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\)\) | 
					
						
							|  |  |  | $''')
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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(source=SAMPLE_WITH_C_CALL, | 
					
						
							|  |  |  |                                   cmds_after_breakpoint=['py-up', '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(source=SAMPLE_WITH_C_CALL, | 
					
						
							|  |  |  |                                   cmds_after_breakpoint=['py-up', '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): | 
					
						
							| 
									
										
										
										
											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-up', '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): | 
					
						
							| 
									
										
										
										
											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-up', '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): | 
					
						
							| 
									
										
										
										
											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-up', '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): | 
					
						
							| 
									
										
										
										
											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-up', 'py-up', 'py-locals']) | 
					
						
							|  |  |  |         self.assertMultilineMatches(bt, | 
					
						
							|  |  |  |                                     r'''^.*
 | 
					
						
							|  |  |  | Locals for foo | 
					
						
							|  |  |  | a = 1 | 
					
						
							|  |  |  | b = 2 | 
					
						
							|  |  |  | c = 3 | 
					
						
							|  |  |  | Locals for <module> | 
					
						
							|  |  |  | .*$''')
 |