From a9847aea3b772e471fcf5dccac7059cec2eb08f4 Mon Sep 17 00:00:00 2001 From: Matthew Williams <43.matthew@gmail.com> Date: Wed, 8 Apr 2026 16:34:46 -0400 Subject: [PATCH] fix(profiling): AndroidContinuousProfiler Reset shouldStop flag on startProfiler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same fix as PerfettoContinuousProfiler — shouldStop was never reset to false after stopProfiler, so a stop/start cycle would leave shouldStop=true and silently stop the profiler after one chunk. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../core/AndroidContinuousProfiler.java | 1 + .../core/AndroidContinuousProfilerTest.kt | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidContinuousProfiler.java b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidContinuousProfiler.java index 41362c9d93e..022cd088de3 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidContinuousProfiler.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidContinuousProfiler.java @@ -117,6 +117,7 @@ public void startProfiler( final @NotNull ProfileLifecycle profileLifecycle, final @NotNull TracesSampler tracesSampler) { try (final @NotNull ISentryLifecycleToken ignored = lock.acquire()) { + shouldStop = false; if (shouldSample) { isSampled = tracesSampler.sampleSessionProfile(SentryRandom.current().nextDouble()); shouldSample = false; diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidContinuousProfilerTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidContinuousProfilerTest.kt index 162e56c36e3..70dc6519517 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidContinuousProfilerTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidContinuousProfilerTest.kt @@ -556,6 +556,24 @@ class AndroidContinuousProfilerTest { .log(eq(SentryLevel.WARNING), eq("Device is offline. Stopping profiler.")) } + @Test + fun `manual profiler can be started again after a full start-stop cycle`() { + val profiler = fixture.getSut() + + profiler.startProfiler(ProfileLifecycle.MANUAL, fixture.mockTracesSampler) + assertTrue(profiler.isRunning) + profiler.stopProfiler(ProfileLifecycle.MANUAL) + // Triggers the scheduled executor task that collects perf metrics and finalizes the chunk + fixture.executor.runAll() + assertFalse(profiler.isRunning) + + profiler.startProfiler(ProfileLifecycle.MANUAL, fixture.mockTracesSampler) + assertTrue(profiler.isRunning) + // Triggers the scheduled executor task that collects perf metrics and finalizes the chunk + fixture.executor.runAll() + assertTrue(profiler.isRunning, "shouldStop must be reset on start") + } + fun withMockScopes(closure: () -> Unit) = Mockito.mockStatic(Sentry::class.java).use { it.`when` { Sentry.getCurrentScopes() }.thenReturn(fixture.scopes)