Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 15 additions & 41 deletions Lib/test/test_profiling/test_sampling_profiler/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
requires_subprocess,
captured_stdout,
captured_stderr,
SHORT_TIMEOUT,
)

from .helpers import (
Expand All @@ -37,6 +38,9 @@
)
from .mocks import MockFrameInfo, MockThreadInfo, MockInterpreterInfo

# Duration for profiling tests - long enough for process to complete naturally
PROFILING_TIMEOUT = str(int(SHORT_TIMEOUT))


@skip_if_not_supported
@unittest.skipIf(
Expand Down Expand Up @@ -381,47 +385,15 @@ def cpu_intensive_work():
result = result % 1000000
return result

def medium_computation():
"""Medium complexity function."""
result = 0
for i in range(100):
result += i * i
return result

def fast_loop():
"""Fast simple loop."""
total = 0
for i in range(50):
total += i
return total

def nested_calls():
"""Test nested function calls."""
def level1():
def level2():
return medium_computation()
return level2()
return level1()

def main_loop():
"""Main test loop with different execution paths."""
iteration = 0

while True:
iteration += 1
"""Main test loop."""
max_iterations = 200

# Different execution paths - focus on CPU intensive work
if iteration % 3 == 0:
# Very CPU intensive
result = cpu_intensive_work()
elif iteration % 2 == 0:
# Expensive recursive operation (increased frequency for slower machines)
result = slow_fibonacci(12)
for iteration in range(max_iterations):
if iteration % 2 == 0:
result = slow_fibonacci(15)
else:
# Medium operation
result = nested_calls()

# No sleep - keep CPU busy
result = cpu_intensive_work()

if __name__ == "__main__":
main_loop()
Expand All @@ -434,9 +406,10 @@ def test_sampling_basic_functionality(self):
mock.patch("sys.stdout", captured_output),
):
try:
# Sample for up to SHORT_TIMEOUT seconds, but process exits after fixed iterations
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is basically just a longer duration - if we stop sampling, then the expected function (which doesn't run in the first 5 iterations) won't show up and the test will fail.

Perhaps we can check for the first function that runs in the test? Or else sample the entire process so that we know it exits after having run the function we're checking for?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now is sampling the entire process. The iterations are such that SHORT_TIMEOUT is effectively too long for the typical duration of that process

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay yeah, I agree (after going through a complicated thought process involving waiting for the process to actually start, which would also require a timeout, which would also be SHORT_TIMEOUT...)

Copy link
Member Author

@pablogsal pablogsal Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah good point. In any case the duration starts counting after the process has started and tell us it's ready. The profiler waits for confirmation from a special wrapper before starting to profile so the time fo the process to start shouldn't count. I still think 30 seconds is more than enough in any case as this is racy anyway. I will look if I can simplify what we check to make it a bit safer

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have simplified the test a lot, hopefully this does the trick

profiling.sampling.sample.sample(
subproc.process.pid,
duration_sec=2,
duration_sec=SHORT_TIMEOUT,
sample_interval_usec=1000, # 1ms
show_summary=False,
)
Expand Down Expand Up @@ -578,7 +551,8 @@ def test_sample_target_script(self):
script_file.flush()
self.addCleanup(close_and_unlink, script_file)

test_args = ["profiling.sampling.sample", "-d", "1", script_file.name]
# Sample for up to SHORT_TIMEOUT seconds, but process exits after fixed iterations
test_args = ["profiling.sampling.sample", "-d", PROFILING_TIMEOUT, script_file.name]

with (
mock.patch("sys.argv", test_args),
Expand Down Expand Up @@ -612,7 +586,7 @@ def test_sample_target_module(self):
test_args = [
"profiling.sampling.sample",
"-d",
"1",
PROFILING_TIMEOUT,
"-m",
"test_module",
]
Expand Down
Loading