diff --git a/tests/v1/worker/test_gpu_profiler.py b/tests/v1/worker/test_gpu_profiler.py index 3e236a73b1..347b5b379a 100644 --- a/tests/v1/worker/test_gpu_profiler.py +++ b/tests/v1/worker/test_gpu_profiler.py @@ -112,6 +112,34 @@ def test_max_iterations(default_profiler_config): # Should have stopped now assert profiler._running is False assert profiler.stop_call_count == 1 + # And fully reset, not just paused -- a later start_profile must not be a + # permanent no-op just because max_iterations already fired once. + assert profiler._active is False + assert profiler._active_iteration_count == 0 + assert profiler._profiling_for_iters == 0 + + +def test_restart_after_max_iterations(default_profiler_config): + """A start_profile after an auto-stop must actually restart, not be + silently ignored (regression test: auto-stop used to leave _active + True forever, so start() always bailed out early).""" + default_profiler_config.max_iterations = 2 + profiler = ConcreteWorkerProfiler(default_profiler_config) + + profiler.start() + profiler.step() + profiler.step() + profiler.step() # exceeds max, auto-stops + assert profiler._running is False + assert profiler.start_call_count == 1 + + profiler.start() + assert profiler._active is True + assert profiler._running is True + assert profiler.start_call_count == 2 + + profiler.step() + assert profiler._running is True def test_delayed_start_and_max_iters(default_profiler_config): diff --git a/vllm/profiler/wrapper.py b/vllm/profiler/wrapper.py index 2f56d0fc7c..877a390d6d 100644 --- a/vllm/profiler/wrapper.py +++ b/vllm/profiler/wrapper.py @@ -120,11 +120,13 @@ class WorkerProfiler(ABC): and self._running and self._profiling_for_iters > self._max_iters ): - # Automatically stop the profiler after max iters - # will be marked as not running, but leave as active so that stop - # can clean up properly + # Automatically stop the profiler after max iters. Go through the + # public stop() (not _call_stop() directly) so _active and the + # iteration counters reset too -- otherwise a later start_profile + # is silently ignored forever, since start() bails out early + # whenever _active is still True. logger.info_once("Max profiling iterations reached. Stopping profiler...") - self._call_stop() + self.stop() return def _profiler_step(self) -> bool: