]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-150429: Fix sampling profiler generator stack test (#150433)
authorPablo Galindo Salgado <Pablogsal@gmail.com>
Mon, 1 Jun 2026 20:27:39 +0000 (21:27 +0100)
committerGitHub <noreply@github.com>
Mon, 1 Jun 2026 20:27:39 +0000 (21:27 +0100)
Lib/test/test_profiling/test_sampling_profiler/test_blocking.py

index 102eb51b556cc77635727fb65d31e40636bd7c4b..1f4b6da3281056177e729c16af80be79edc675ed 100644 (file)
@@ -39,8 +39,9 @@ class TestBlockingModeStackAccuracy(unittest.TestCase):
     @classmethod
     def setUpClass(cls):
         # Test script that uses a generator consumed in a loop.
-        # When consume_generator is on the arithmetic lines (temp1, temp2, etc.),
-        # fibonacci_generator should NOT be in the stack at all.
+        # When consume_generator is the executing leaf frame on the arithmetic
+        # lines (temp1, temp2, etc.), fibonacci_generator should NOT be in the
+        # stack at all.
         # Line numbers are important here - see ARITHMETIC_LINES below.
         cls.generator_script = textwrap.dedent('''
             def fibonacci_generator(n):
@@ -65,29 +66,32 @@ class TestBlockingModeStackAccuracy(unittest.TestCase):
             main()
         ''')
         # Line numbers of the arithmetic operations in consume_generator.
-        # These are the lines where fibonacci_generator should NOT be in the stack.
-        # The socket injection code adds 7 lines before our script.
-        # temp1 = value + 1  -> line 17
-        # temp2 = value * 2  -> line 18
-        # temp3 = value - 1  -> line 19
-        # result = ...       -> line 20
-        cls.ARITHMETIC_LINES = {17, 18, 19, 20}
+        # These are the lines where fibonacci_generator should NOT be in the
+        # stack when consume_generator is the executing leaf frame. They account
+        # for the socket prelude added by test_subprocess().
+        # temp1 = value + 1  -> line 16
+        # temp2 = value * 2  -> line 17
+        # temp3 = value - 1  -> line 18
+        # result = ...       -> line 19
+        cls.ARITHMETIC_LINES = {16, 17, 18, 19}
 
     def test_generator_not_under_consumer_arithmetic(self):
         """Test that fibonacci_generator doesn't appear when consume_generator does arithmetic.
 
-        When consume_generator is executing arithmetic lines (temp1, temp2, etc.),
-        fibonacci_generator should NOT be anywhere in the stack - it's not being
-        called at that point.
+        When consume_generator is the leaf frame on arithmetic lines (temp1,
+        temp2, etc.), fibonacci_generator should NOT be anywhere in the stack -
+        it's not being called at that point. Non-leaf frame line numbers are
+        caller/resume metadata, not proof that the frame is executing.
 
         Valid stacks:
-          - consume_generator at 'for value in gen:' line WITH fibonacci_generator
-            at the top (generator is yielding)
+          - fibonacci_generator at the top (generator is executing), with
+            consume_generator below it
           - consume_generator at arithmetic lines WITHOUT fibonacci_generator
             (we're just doing math, not calling the generator)
 
         Invalid stacks (indicate torn/inconsistent reads):
-          - consume_generator at arithmetic lines WITH fibonacci_generator
+          - consume_generator leaf frame at arithmetic lines WITH
+            fibonacci_generator
             anywhere in the stack
 
         Note: call_tree is ordered from bottom (index 0) to top (index -1).
@@ -110,6 +114,8 @@ class TestBlockingModeStackAccuracy(unittest.TestCase):
         total_samples = 0
         invalid_stacks = 0
         arithmetic_samples = 0
+        generator_samples = 0
+        generator_not_leaf_samples = 0
 
         for (call_tree, _thread_id), count in collector.stack_counter.items():
             total_samples += count
@@ -117,15 +123,21 @@ class TestBlockingModeStackAccuracy(unittest.TestCase):
             if not call_tree:
                 continue
 
-            # Find consume_generator in the stack and check its line number
-            for i, (filename, lineno, funcname) in enumerate(call_tree):
-                if funcname == "consume_generator" and lineno in self.ARITHMETIC_LINES:
-                    arithmetic_samples += count
-                    # Check if fibonacci_generator appears anywhere in this stack
-                    func_names = [frame[2] for frame in call_tree]
-                    if "fibonacci_generator" in func_names:
-                        invalid_stacks += count
-                    break
+            # Non-leaf frame line numbers can point at resume locations while
+            # a callee is the executing leaf frame.
+            _, lineno, funcname = call_tree[-1]
+            func_names = [frame[2] for frame in call_tree]
+
+            if "fibonacci_generator" in func_names:
+                generator_samples += count
+                if funcname != "fibonacci_generator":
+                    generator_not_leaf_samples += count
+
+            if funcname == "consume_generator" and lineno in self.ARITHMETIC_LINES:
+                arithmetic_samples += count
+                # Check if fibonacci_generator appears anywhere in this stack.
+                if "fibonacci_generator" in func_names:
+                    invalid_stacks += count
 
         self.assertGreater(total_samples, 10,
             f"Expected at least 10 samples, got {total_samples}")
@@ -134,8 +146,15 @@ class TestBlockingModeStackAccuracy(unittest.TestCase):
         self.assertGreater(arithmetic_samples, 0,
             f"Expected some samples on arithmetic lines, got {arithmetic_samples}")
 
+        self.assertGreater(generator_samples, 0,
+            f"Expected some samples in fibonacci_generator, got {generator_samples}")
+
+        self.assertEqual(generator_not_leaf_samples, 0,
+            f"Found {generator_not_leaf_samples}/{generator_samples} stacks where "
+            f"fibonacci_generator appears but is not the leaf frame.")
+
         self.assertEqual(invalid_stacks, 0,
             f"Found {invalid_stacks}/{arithmetic_samples} invalid stacks where "
             f"fibonacci_generator appears in the stack when consume_generator "
-            f"is on an arithmetic line. This indicates torn/inconsistent stack "
-            f"traces are being captured.")
+            f"is the leaf frame on an arithmetic line. This indicates "
+            f"torn/inconsistent stack traces are being captured.")