| 
									
										
										
										
											2022-04-16 17:36:29 -04:00
										 |  |  | import dis | 
					
						
							| 
									
										
										
										
											2018-07-31 20:55:14 +00:00
										 |  |  | import textwrap | 
					
						
							|  |  |  | import unittest | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-26 00:12:54 +02:00
										 |  |  | from test import support | 
					
						
							|  |  |  | from test.support import os_helper | 
					
						
							| 
									
										
										
										
											2018-07-31 20:55:14 +00:00
										 |  |  | from test.support.script_helper import assert_python_ok | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-16 17:36:29 -04:00
										 |  |  | def example(): | 
					
						
							|  |  |  |     x = [] | 
					
						
							| 
									
										
										
										
											2022-11-02 10:42:57 -07:00
										 |  |  |     for i in range(0): | 
					
						
							| 
									
										
										
										
											2022-04-16 17:36:29 -04:00
										 |  |  |         x.append(i) | 
					
						
							|  |  |  |     x = "this is" | 
					
						
							|  |  |  |     y = "an example" | 
					
						
							|  |  |  |     print(x, y) | 
					
						
							| 
									
										
										
										
											2018-07-31 20:55:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-16 17:36:29 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-26 00:12:54 +02:00
										 |  |  | @unittest.skipUnless(support.Py_DEBUG, "lltrace requires Py_DEBUG") | 
					
						
							| 
									
										
										
										
											2018-07-31 20:55:14 +00:00
										 |  |  | class TestLLTrace(unittest.TestCase): | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-16 17:36:29 -04:00
										 |  |  |     def run_code(self, code): | 
					
						
							|  |  |  |         code = textwrap.dedent(code).strip() | 
					
						
							|  |  |  |         with open(os_helper.TESTFN, 'w', encoding='utf-8') as fd: | 
					
						
							|  |  |  |             self.addCleanup(os_helper.unlink, os_helper.TESTFN) | 
					
						
							|  |  |  |             fd.write(code) | 
					
						
							|  |  |  |         status, stdout, stderr = assert_python_ok(os_helper.TESTFN) | 
					
						
							|  |  |  |         self.assertEqual(stderr, b"") | 
					
						
							|  |  |  |         self.assertEqual(status, 0) | 
					
						
							|  |  |  |         result = stdout.decode('utf-8') | 
					
						
							| 
									
										
										
										
											2022-05-26 00:12:54 +02:00
										 |  |  |         if support.verbose: | 
					
						
							| 
									
										
										
										
											2022-04-16 17:36:29 -04:00
										 |  |  |             print("\n\n--- code ---") | 
					
						
							|  |  |  |             print(code) | 
					
						
							|  |  |  |             print("\n--- stdout ---") | 
					
						
							|  |  |  |             print(result) | 
					
						
							|  |  |  |             print() | 
					
						
							|  |  |  |         return result | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_lltrace(self): | 
					
						
							|  |  |  |         stdout = self.run_code("""
 | 
					
						
							|  |  |  |             def dont_trace_1(): | 
					
						
							|  |  |  |                 a = "a" | 
					
						
							|  |  |  |                 a = 10 * a | 
					
						
							|  |  |  |             def trace_me(): | 
					
						
							|  |  |  |                 for i in range(3): | 
					
						
							|  |  |  |                     +i | 
					
						
							|  |  |  |             def dont_trace_2(): | 
					
						
							|  |  |  |                 x = 42 | 
					
						
							|  |  |  |                 y = -x | 
					
						
							|  |  |  |             dont_trace_1() | 
					
						
							| 
									
										
										
										
											2022-04-16 18:57:00 -04:00
										 |  |  |             __lltrace__ = 1 | 
					
						
							| 
									
										
										
										
											2022-04-16 17:36:29 -04:00
										 |  |  |             trace_me() | 
					
						
							| 
									
										
										
										
											2022-04-16 18:57:00 -04:00
										 |  |  |             del __lltrace__ | 
					
						
							| 
									
										
										
										
											2022-04-16 17:36:29 -04:00
										 |  |  |             dont_trace_2() | 
					
						
							|  |  |  |         """)
 | 
					
						
							|  |  |  |         self.assertIn("GET_ITER", stdout) | 
					
						
							|  |  |  |         self.assertIn("FOR_ITER", stdout) | 
					
						
							| 
									
										
										
										
											2023-01-06 14:47:57 +00:00
										 |  |  |         self.assertIn("CALL_INTRINSIC_1", stdout) | 
					
						
							| 
									
										
										
										
											2022-04-16 17:36:29 -04:00
										 |  |  |         self.assertIn("POP_TOP", stdout) | 
					
						
							|  |  |  |         self.assertNotIn("BINARY_OP", stdout) | 
					
						
							|  |  |  |         self.assertNotIn("UNARY_NEGATIVE", stdout) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assertIn("'trace_me' in module '__main__'", stdout) | 
					
						
							|  |  |  |         self.assertNotIn("dont_trace_1", stdout) | 
					
						
							|  |  |  |         self.assertNotIn("'dont_trace_2' in module", stdout) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_lltrace_different_module(self): | 
					
						
							|  |  |  |         stdout = self.run_code("""
 | 
					
						
							|  |  |  |             from test import test_lltrace | 
					
						
							| 
									
										
										
										
											2022-04-16 18:57:00 -04:00
										 |  |  |             test_lltrace.__lltrace__ = 1 | 
					
						
							| 
									
										
										
										
											2022-04-16 17:36:29 -04:00
										 |  |  |             test_lltrace.example() | 
					
						
							|  |  |  |         """)
 | 
					
						
							|  |  |  |         self.assertIn("'example' in module 'test.test_lltrace'", stdout) | 
					
						
							|  |  |  |         self.assertIn('LOAD_CONST', stdout) | 
					
						
							|  |  |  |         self.assertIn('FOR_ITER', stdout) | 
					
						
							|  |  |  |         self.assertIn('this is an example', stdout) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # check that offsets match the output of dis.dis() | 
					
						
							| 
									
										
										
										
											2022-11-02 10:42:57 -07:00
										 |  |  |         instr_map = {i.offset: i for i in dis.get_instructions(example, adaptive=True)} | 
					
						
							| 
									
										
										
										
											2022-04-16 17:36:29 -04:00
										 |  |  |         for line in stdout.splitlines(): | 
					
						
							|  |  |  |             offset, colon, opname_oparg = line.partition(":") | 
					
						
							|  |  |  |             if not colon: | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             offset = int(offset) | 
					
						
							|  |  |  |             opname_oparg = opname_oparg.split() | 
					
						
							|  |  |  |             if len(opname_oparg) == 2: | 
					
						
							|  |  |  |                 opname, oparg = opname_oparg | 
					
						
							|  |  |  |                 oparg = int(oparg) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 (opname,) = opname_oparg | 
					
						
							|  |  |  |                 oparg = None | 
					
						
							|  |  |  |             self.assertEqual(instr_map[offset].opname, opname) | 
					
						
							|  |  |  |             self.assertEqual(instr_map[offset].arg, oparg) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 20:55:14 +00:00
										 |  |  |     def test_lltrace_does_not_crash_on_subscript_operator(self): | 
					
						
							|  |  |  |         # If this test fails, it will reproduce a crash reported as | 
					
						
							|  |  |  |         # bpo-34113. The crash happened at the command line console of | 
					
						
							| 
									
										
										
										
											2022-04-16 18:57:00 -04:00
										 |  |  |         # debug Python builds with __lltrace__ enabled (only possible in console), | 
					
						
							| 
									
										
										
										
											2021-10-07 01:13:48 +02:00
										 |  |  |         # when the internal Python stack was negatively adjusted | 
					
						
							| 
									
										
										
										
											2022-04-16 17:36:29 -04:00
										 |  |  |         stdout = self.run_code("""
 | 
					
						
							| 
									
										
										
										
											2018-07-31 20:55:14 +00:00
										 |  |  |             import code | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             console = code.InteractiveConsole() | 
					
						
							| 
									
										
										
										
											2022-04-16 18:57:00 -04:00
										 |  |  |             console.push('__lltrace__ = 1') | 
					
						
							| 
									
										
										
										
											2018-07-31 20:55:14 +00:00
										 |  |  |             console.push('a = [1, 2, 3]') | 
					
						
							|  |  |  |             console.push('a[0] = 1') | 
					
						
							|  |  |  |             print('unreachable if bug exists') | 
					
						
							| 
									
										
										
										
											2022-04-16 17:36:29 -04:00
										 |  |  |         """)
 | 
					
						
							|  |  |  |         self.assertIn("unreachable if bug exists", stdout) | 
					
						
							| 
									
										
										
										
											2018-07-31 20:55:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | if __name__ == "__main__": | 
					
						
							|  |  |  |     unittest.main() |