gh-138122: Make sampling profiler integration tests more resilient (#142382)

The tests were flaky on slow machines because subprocesses could finish
before enough samples were collected. This adds synchronization similar
to test_external_inspection: test scripts now signal when they start
working, and the profiler waits for this signal before sampling.

Test scripts now run in infinite loops until killed rather than for
fixed iterations, ensuring the profiler always has active work to
sample regardless of machine speed.
This commit is contained in:
Pablo Galindo Salgado 2025-12-07 22:41:15 +00:00 committed by GitHub
parent ff2577f56e
commit ef51a7c8f3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 185 additions and 126 deletions

View file

@ -39,32 +39,26 @@ def setUpClass(cls):
import gc
class ExpensiveGarbage:
"""Class that triggers GC with expensive finalizer (callback)."""
def __init__(self):
self.cycle = self
def __del__(self):
# CPU-intensive work in the finalizer callback
result = 0
for i in range(100000):
result += i * i
if i % 1000 == 0:
result = result % 1000000
def main_loop():
"""Main loop that triggers GC with expensive callback."""
while True:
ExpensiveGarbage()
gc.collect()
if __name__ == "__main__":
main_loop()
_test_sock.sendall(b"working")
while True:
ExpensiveGarbage()
gc.collect()
'''
def test_gc_frames_enabled(self):
"""Test that GC frames appear when gc tracking is enabled."""
with (
test_subprocess(self.gc_test_script) as subproc,
test_subprocess(self.gc_test_script, wait_for_working=True) as subproc,
io.StringIO() as captured_output,
mock.patch("sys.stdout", captured_output),
):
@ -94,7 +88,7 @@ def test_gc_frames_enabled(self):
def test_gc_frames_disabled(self):
"""Test that GC frames do not appear when gc tracking is disabled."""
with (
test_subprocess(self.gc_test_script) as subproc,
test_subprocess(self.gc_test_script, wait_for_working=True) as subproc,
io.StringIO() as captured_output,
mock.patch("sys.stdout", captured_output),
):
@ -133,18 +127,13 @@ def setUpClass(cls):
cls.native_test_script = """
import operator
def main_loop():
while True:
# Native code in the middle of the stack:
operator.call(inner)
def inner():
# Python code at the top of the stack:
for _ in range(1_000_0000):
pass
if __name__ == "__main__":
main_loop()
_test_sock.sendall(b"working")
while True:
operator.call(inner)
"""
def test_native_frames_enabled(self):
@ -154,10 +143,7 @@ def test_native_frames_enabled(self):
)
self.addCleanup(close_and_unlink, collapsed_file)
with (
test_subprocess(self.native_test_script) as subproc,
):
# Suppress profiler output when testing file export
with test_subprocess(self.native_test_script, wait_for_working=True) as subproc:
with (
io.StringIO() as captured_output,
mock.patch("sys.stdout", captured_output),
@ -199,7 +185,7 @@ def test_native_frames_enabled(self):
def test_native_frames_disabled(self):
"""Test that native frames do not appear when native tracking is disabled."""
with (
test_subprocess(self.native_test_script) as subproc,
test_subprocess(self.native_test_script, wait_for_working=True) as subproc,
io.StringIO() as captured_output,
mock.patch("sys.stdout", captured_output),
):