From dd735c2ce052ac995236142e4f1cb40fe423ed9e Mon Sep 17 00:00:00 2001 From: stefanosiano Date: Fri, 28 Feb 2025 17:22:05 +0100 Subject: [PATCH 1/3] added isStartProfilerOnAppStart experimental option --- .../android/core/ManifestMetadataReader.java | 6 +++++ .../core/ManifestMetadataReaderTest.kt | 25 +++++++++++++++++++ .../java/io/sentry/ExperimentalOptions.java | 17 +++++++++++++ sentry/src/main/java/io/sentry/Sentry.java | 7 +++--- .../main/java/io/sentry/SentryOptions.java | 6 +++++ .../test/java/io/sentry/SentryOptionsTest.kt | 14 +++++++++++ 6 files changed, 72 insertions(+), 3 deletions(-) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java b/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java index e85e5de923..39eed498aa 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java @@ -70,6 +70,8 @@ final class ManifestMetadataReader { static final String PROFILE_LIFECYCLE = "io.sentry.traces.profiling.lifecycle"; + static final String PROFILER_START_ON_APP_START = "io.sentry.traces.profiling.start-on-app-start"; + @ApiStatus.Experimental static final String TRACE_SAMPLING = "io.sentry.traces.trace-sampling"; static final String TRACE_PROPAGATION_TARGETS = "io.sentry.traces.trace-propagation-targets"; @@ -342,6 +344,10 @@ static void applyMetadata( ProfileLifecycle.valueOf(profileLifecycle.toUpperCase(Locale.ROOT))); } + options.getExperimental().setStartProfilerOnAppStart( + readBool( + metadata, logger, PROFILER_START_ON_APP_START, options.isStartProfilerOnAppStart())); + options.setEnableUserInteractionTracing( readBool(metadata, logger, TRACES_UI_ENABLE, options.isEnableUserInteractionTracing())); diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/ManifestMetadataReaderTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/ManifestMetadataReaderTest.kt index 9190beb916..40143a74c3 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/ManifestMetadataReaderTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/ManifestMetadataReaderTest.kt @@ -868,6 +868,31 @@ class ManifestMetadataReaderTest { assertEquals(ProfileLifecycle.TRACE, fixture.options.profileLifecycle) } + @Test + fun `applyMetadata without specifying isStartProfilerOnAppStart, stays false`() { + // Arrange + val context = fixture.getContext() + + // Act + ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider) + + // Assert + assertFalse(fixture.options.isStartProfilerOnAppStart) + } + + @Test + fun `applyMetadata reads isStartProfilerOnAppStart from metadata`() { + // Arrange + val bundle = bundleOf(ManifestMetadataReader.PROFILER_START_ON_APP_START to true) + val context = fixture.getContext(metaData = bundle) + + // Act + ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider) + + // Assert + assertTrue(fixture.options.isStartProfilerOnAppStart) + } + @Test fun `applyMetadata reads tracePropagationTargets to options`() { // Arrange diff --git a/sentry/src/main/java/io/sentry/ExperimentalOptions.java b/sentry/src/main/java/io/sentry/ExperimentalOptions.java index 4a99c00377..00954b6bac 100644 --- a/sentry/src/main/java/io/sentry/ExperimentalOptions.java +++ b/sentry/src/main/java/io/sentry/ExperimentalOptions.java @@ -27,6 +27,13 @@ public final class ExperimentalOptions { */ private @NotNull ProfileLifecycle profileLifecycle = ProfileLifecycle.MANUAL; + /** + * Whether profiling can automatically be started as early as possible during the app lifecycle, to capture more of app startup. + * If {@link ExperimentalOptions#profileLifecycle} is {@link ProfileLifecycle#MANUAL} Profiling is started automatically on startup and stopProfileSession must be called manually whenever the app startup is completed + * If {@link ExperimentalOptions#profileLifecycle} is {@link ProfileLifecycle#TRACE} Profiling is started automatically on startup, and will automatically be stopped when the root span that is associated with app startup ends + */ + private boolean startProfilerOnAppStart = false; + public ExperimentalOptions(final boolean empty) { this.sessionReplay = new SentryReplayOptions(empty); } @@ -74,4 +81,14 @@ public void setProfileSessionSampleRate(final @Nullable Double profileSessionSam } this.profileSessionSampleRate = profileSessionSampleRate; } + + @ApiStatus.Experimental + public boolean isStartProfilerOnAppStart() { + return startProfilerOnAppStart; + } + + @ApiStatus.Experimental + public void setStartProfilerOnAppStart(boolean startProfilerOnAppStart) { + this.startProfilerOnAppStart = startProfilerOnAppStart; + } } diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 02e7d168c7..cb2f32350e 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -370,9 +370,10 @@ private static void handleAppStartProfilingConfig( try { // We always delete the config file for app start profiling FileUtils.deleteRecursively(appStartProfilingConfigFile); - if (!options.isEnableAppStartProfiling()) { + if (!options.isEnableAppStartProfiling() && !options.isStartProfilerOnAppStart()) { return; } + todo isStartProfilerOnAppStart doesn't need tracing! - SentryTest takes hours to run! if (!options.isTracingEnabled()) { options .getLogger() @@ -382,8 +383,8 @@ private static void handleAppStartProfilingConfig( return; } if (appStartProfilingConfigFile.createNewFile()) { - final @NotNull TracesSamplingDecision appStartSamplingDecision = - sampleAppStartProfiling(options); + // If old app start profiling is false, it means the transaction will not be sampled, but we create the file anyway to allow continuous profiling on app start + final @NotNull TracesSamplingDecision appStartSamplingDecision = options.isEnableAppStartProfiling() ? sampleAppStartProfiling(options) : new TracesSamplingDecision(false); final @NotNull SentryAppStartProfilingOptions appStartProfilingOptions = new SentryAppStartProfilingOptions(options, appStartSamplingDecision); try (final OutputStream outputStream = diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index b7b57f4beb..538bbc0ff0 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -1813,6 +1813,12 @@ public void setProfilesSampleRate(final @Nullable Double profilesSampleRate) { return experimental.getProfileLifecycle(); } + /** Whether profiling can automatically be started as early as possible during the app lifecycle. */ + @ApiStatus.Experimental + public boolean isStartProfilerOnAppStart() { + return experimental.isStartProfilerOnAppStart(); + } + /** * Returns the profiling traces dir. path if set * diff --git a/sentry/src/test/java/io/sentry/SentryOptionsTest.kt b/sentry/src/test/java/io/sentry/SentryOptionsTest.kt index 22aec0c9a0..fc850e4bd4 100644 --- a/sentry/src/test/java/io/sentry/SentryOptionsTest.kt +++ b/sentry/src/test/java/io/sentry/SentryOptionsTest.kt @@ -303,6 +303,20 @@ class SentryOptionsTest { assertEquals(ProfileLifecycle.MANUAL, options.profileLifecycle) } + @Test + fun `when isStartProfilerOnAppStart is set to a value, value is set`() { + val options = SentryOptions().apply { + this.experimental.isStartProfilerOnAppStart = true + } + assertTrue(options.isStartProfilerOnAppStart) + } + + @Test + fun `isStartProfilerOnAppStart defaults to false`() { + val options = SentryOptions() + assertFalse(options.isStartProfilerOnAppStart) + } + @Test fun `when options is initialized, compositePerformanceCollector is set`() { assertIs(SentryOptions().compositePerformanceCollector) From d790035377fa3c2ebaef64ced3ab7e20891db237 Mon Sep 17 00:00:00 2001 From: stefanosiano Date: Tue, 4 Mar 2025 13:19:19 +0100 Subject: [PATCH 2/3] added isStartProfilerOnAppStart logic and tests --- .../android/core/ManifestMetadataReader.java | 11 +++-- sentry/api/sentry.api | 9 ++++ .../java/io/sentry/ExperimentalOptions.java | 10 +++-- sentry/src/main/java/io/sentry/Sentry.java | 14 +++++-- .../SentryAppStartProfilingOptions.java | 38 +++++++++++++++++ .../main/java/io/sentry/SentryOptions.java | 4 +- .../test/java/io/sentry/JsonSerializerTest.kt | 11 +++-- sentry/src/test/java/io/sentry/SentryTest.kt | 41 ++++++++++++++++++- 8 files changed, 123 insertions(+), 15 deletions(-) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java b/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java index 39eed498aa..a01ae5a83f 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java @@ -344,9 +344,14 @@ static void applyMetadata( ProfileLifecycle.valueOf(profileLifecycle.toUpperCase(Locale.ROOT))); } - options.getExperimental().setStartProfilerOnAppStart( - readBool( - metadata, logger, PROFILER_START_ON_APP_START, options.isStartProfilerOnAppStart())); + options + .getExperimental() + .setStartProfilerOnAppStart( + readBool( + metadata, + logger, + PROFILER_START_ON_APP_START, + options.isStartProfilerOnAppStart())); options.setEnableUserInteractionTracing( readBool(metadata, logger, TRACES_UI_ENABLE, options.isEnableUserInteractionTracing())); diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 70f60b8350..31a4d7d6da 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -446,9 +446,11 @@ public final class io/sentry/ExperimentalOptions { public fun getProfileLifecycle ()Lio/sentry/ProfileLifecycle; public fun getProfileSessionSampleRate ()Ljava/lang/Double; public fun getSessionReplay ()Lio/sentry/SentryReplayOptions; + public fun isStartProfilerOnAppStart ()Z public fun setProfileLifecycle (Lio/sentry/ProfileLifecycle;)V public fun setProfileSessionSampleRate (Ljava/lang/Double;)V public fun setSessionReplay (Lio/sentry/SentryReplayOptions;)V + public fun setStartProfilerOnAppStart (Z)V } public final class io/sentry/ExternalOptions { @@ -2506,18 +2508,22 @@ public final class io/sentry/SentryAppStartProfilingOptions : io/sentry/JsonSeri public fun getUnknown ()Ljava/util/Map; public fun isContinuousProfileSampled ()Z public fun isContinuousProfilingEnabled ()Z + public fun isEnableAppStartProfiling ()Z public fun isProfileSampled ()Z public fun isProfilingEnabled ()Z + public fun isStartProfilerOnAppStart ()Z public fun isTraceSampled ()Z public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V public fun setContinuousProfileSampled (Z)V public fun setContinuousProfilingEnabled (Z)V + public fun setEnableAppStartProfiling (Z)V public fun setProfileLifecycle (Lio/sentry/ProfileLifecycle;)V public fun setProfileSampleRate (Ljava/lang/Double;)V public fun setProfileSampled (Z)V public fun setProfilingEnabled (Z)V public fun setProfilingTracesDirPath (Ljava/lang/String;)V public fun setProfilingTracesHz (I)V + public fun setStartProfilerOnAppStart (Z)V public fun setTraceSampleRate (Ljava/lang/Double;)V public fun setTraceSampled (Z)V public fun setUnknown (Ljava/util/Map;)V @@ -2532,7 +2538,9 @@ public final class io/sentry/SentryAppStartProfilingOptions$Deserializer : io/se public final class io/sentry/SentryAppStartProfilingOptions$JsonKeys { public static final field CONTINUOUS_PROFILE_SAMPLED Ljava/lang/String; public static final field IS_CONTINUOUS_PROFILING_ENABLED Ljava/lang/String; + public static final field IS_ENABLE_APP_START_PROFILING Ljava/lang/String; public static final field IS_PROFILING_ENABLED Ljava/lang/String; + public static final field IS_START_PROFILER_ON_APP_START Ljava/lang/String; public static final field PROFILE_LIFECYCLE Ljava/lang/String; public static final field PROFILE_SAMPLED Ljava/lang/String; public static final field PROFILE_SAMPLE_RATE Ljava/lang/String; @@ -3065,6 +3073,7 @@ public class io/sentry/SentryOptions { public fun isSendClientReports ()Z public fun isSendDefaultPii ()Z public fun isSendModules ()Z + public fun isStartProfilerOnAppStart ()Z public fun isTraceOptionsRequests ()Z public fun isTraceSampling ()Z public fun isTracingEnabled ()Z diff --git a/sentry/src/main/java/io/sentry/ExperimentalOptions.java b/sentry/src/main/java/io/sentry/ExperimentalOptions.java index 00954b6bac..fc93416d02 100644 --- a/sentry/src/main/java/io/sentry/ExperimentalOptions.java +++ b/sentry/src/main/java/io/sentry/ExperimentalOptions.java @@ -28,9 +28,13 @@ public final class ExperimentalOptions { private @NotNull ProfileLifecycle profileLifecycle = ProfileLifecycle.MANUAL; /** - * Whether profiling can automatically be started as early as possible during the app lifecycle, to capture more of app startup. - * If {@link ExperimentalOptions#profileLifecycle} is {@link ProfileLifecycle#MANUAL} Profiling is started automatically on startup and stopProfileSession must be called manually whenever the app startup is completed - * If {@link ExperimentalOptions#profileLifecycle} is {@link ProfileLifecycle#TRACE} Profiling is started automatically on startup, and will automatically be stopped when the root span that is associated with app startup ends + * Whether profiling can automatically be started as early as possible during the app lifecycle, + * to capture more of app startup. If {@link ExperimentalOptions#profileLifecycle} is {@link + * ProfileLifecycle#MANUAL} Profiling is started automatically on startup and stopProfileSession + * must be called manually whenever the app startup is completed If {@link + * ExperimentalOptions#profileLifecycle} is {@link ProfileLifecycle#TRACE} Profiling is started + * automatically on startup, and will automatically be stopped when the root span that is + * associated with app startup ends */ private boolean startProfilerOnAppStart = false; diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index cb2f32350e..a3ecbacf8d 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -373,8 +373,9 @@ private static void handleAppStartProfilingConfig( if (!options.isEnableAppStartProfiling() && !options.isStartProfilerOnAppStart()) { return; } - todo isStartProfilerOnAppStart doesn't need tracing! - SentryTest takes hours to run! - if (!options.isTracingEnabled()) { + // isStartProfilerOnAppStart doesn't need tracing, as it can be started/stopped + // manually + if (!options.isStartProfilerOnAppStart() && !options.isTracingEnabled()) { options .getLogger() .log( @@ -383,8 +384,13 @@ private static void handleAppStartProfilingConfig( return; } if (appStartProfilingConfigFile.createNewFile()) { - // If old app start profiling is false, it means the transaction will not be sampled, but we create the file anyway to allow continuous profiling on app start - final @NotNull TracesSamplingDecision appStartSamplingDecision = options.isEnableAppStartProfiling() ? sampleAppStartProfiling(options) : new TracesSamplingDecision(false); + // If old app start profiling is false, it means the transaction will not be + // sampled, but we create the file anyway to allow continuous profiling on app + // start + final @NotNull TracesSamplingDecision appStartSamplingDecision = + options.isEnableAppStartProfiling() + ? sampleAppStartProfiling(options) + : new TracesSamplingDecision(false); final @NotNull SentryAppStartProfilingOptions appStartProfilingOptions = new SentryAppStartProfilingOptions(options, appStartSamplingDecision); try (final OutputStream outputStream = diff --git a/sentry/src/main/java/io/sentry/SentryAppStartProfilingOptions.java b/sentry/src/main/java/io/sentry/SentryAppStartProfilingOptions.java index e764a42218..75ab308af1 100644 --- a/sentry/src/main/java/io/sentry/SentryAppStartProfilingOptions.java +++ b/sentry/src/main/java/io/sentry/SentryAppStartProfilingOptions.java @@ -21,6 +21,8 @@ public final class SentryAppStartProfilingOptions implements JsonUnknown, JsonSe boolean isContinuousProfilingEnabled; int profilingTracesHz; boolean continuousProfileSampled; + boolean isEnableAppStartProfiling; + boolean isStartProfilerOnAppStart; @NotNull ProfileLifecycle profileLifecycle; private @Nullable Map unknown; @@ -37,6 +39,8 @@ public SentryAppStartProfilingOptions() { isContinuousProfilingEnabled = false; profileLifecycle = ProfileLifecycle.MANUAL; profilingTracesHz = 0; + isEnableAppStartProfiling = true; + isStartProfilerOnAppStart = false; } SentryAppStartProfilingOptions( @@ -52,6 +56,8 @@ public SentryAppStartProfilingOptions() { isContinuousProfilingEnabled = options.isContinuousProfilingEnabled(); profileLifecycle = options.getProfileLifecycle(); profilingTracesHz = options.getProfilingTracesHz(); + isEnableAppStartProfiling = options.isEnableAppStartProfiling(); + isStartProfilerOnAppStart = options.isStartProfilerOnAppStart(); } public void setProfileSampled(final boolean profileSampled) { @@ -134,6 +140,22 @@ public int getProfilingTracesHz() { return profilingTracesHz; } + public void setEnableAppStartProfiling(final boolean enableAppStartProfiling) { + isEnableAppStartProfiling = enableAppStartProfiling; + } + + public boolean isEnableAppStartProfiling() { + return isEnableAppStartProfiling; + } + + public void setStartProfilerOnAppStart(final boolean startProfilerOnAppStart) { + isStartProfilerOnAppStart = startProfilerOnAppStart; + } + + public boolean isStartProfilerOnAppStart() { + return isStartProfilerOnAppStart; + } + // JsonSerializable public static final class JsonKeys { @@ -147,6 +169,8 @@ public static final class JsonKeys { public static final String IS_CONTINUOUS_PROFILING_ENABLED = "is_continuous_profiling_enabled"; public static final String PROFILE_LIFECYCLE = "profile_lifecycle"; public static final String PROFILING_TRACES_HZ = "profiling_traces_hz"; + public static final String IS_ENABLE_APP_START_PROFILING = "is_enable_app_start_profiling"; + public static final String IS_START_PROFILER_ON_APP_START = "is_start_profiler_on_app_start"; } @Override @@ -165,6 +189,8 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger .value(logger, isContinuousProfilingEnabled); writer.name(JsonKeys.PROFILE_LIFECYCLE).value(logger, profileLifecycle.name()); writer.name(JsonKeys.PROFILING_TRACES_HZ).value(logger, profilingTracesHz); + writer.name(JsonKeys.IS_ENABLE_APP_START_PROFILING).value(logger, isEnableAppStartProfiling); + writer.name(JsonKeys.IS_START_PROFILER_ON_APP_START).value(logger, isStartProfilerOnAppStart); if (unknown != null) { for (String key : unknown.keySet()) { @@ -266,6 +292,18 @@ public static final class Deserializer options.profilingTracesHz = profilingTracesHz; } break; + case JsonKeys.IS_ENABLE_APP_START_PROFILING: + Boolean isEnableAppStartProfiling = reader.nextBooleanOrNull(); + if (isEnableAppStartProfiling != null) { + options.isEnableAppStartProfiling = isEnableAppStartProfiling; + } + break; + case JsonKeys.IS_START_PROFILER_ON_APP_START: + Boolean isStartProfilerOnAppStart = reader.nextBooleanOrNull(); + if (isStartProfilerOnAppStart != null) { + options.isStartProfilerOnAppStart = isStartProfilerOnAppStart; + } + break; default: if (unknown == null) { unknown = new ConcurrentHashMap<>(); diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index 538bbc0ff0..c7087dbb36 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -1813,7 +1813,9 @@ public void setProfilesSampleRate(final @Nullable Double profilesSampleRate) { return experimental.getProfileLifecycle(); } - /** Whether profiling can automatically be started as early as possible during the app lifecycle. */ + /** + * Whether profiling can automatically be started as early as possible during the app lifecycle. + */ @ApiStatus.Experimental public boolean isStartProfilerOnAppStart() { return experimental.isStartProfilerOnAppStart(); diff --git a/sentry/src/test/java/io/sentry/JsonSerializerTest.kt b/sentry/src/test/java/io/sentry/JsonSerializerTest.kt index a8f1a1e80c..38a3f49f24 100644 --- a/sentry/src/test/java/io/sentry/JsonSerializerTest.kt +++ b/sentry/src/test/java/io/sentry/JsonSerializerTest.kt @@ -1234,8 +1234,8 @@ class JsonSerializerTest { val expected = "{\"profile_sampled\":true,\"profile_sample_rate\":0.8,\"continuous_profile_sampled\":true," + "\"trace_sampled\":false,\"trace_sample_rate\":0.1,\"profiling_traces_dir_path\":null,\"is_profiling_enabled\":false," + - "\"is_continuous_profiling_enabled\":false,\"profile_lifecycle\":\"TRACE\",\"profiling_traces_hz\":65}" - + "\"is_continuous_profiling_enabled\":false,\"profile_lifecycle\":\"TRACE\",\"profiling_traces_hz\":65," + + "\"is_enable_app_start_profiling\":false,\"is_start_profiler_on_app_start\":true}" assertEquals(expected, actual) } @@ -1243,7 +1243,8 @@ class JsonSerializerTest { fun `deserializing SentryAppStartProfilingOptions`() { val jsonAppStartProfilingOptions = "{\"profile_sampled\":true,\"profile_sample_rate\":0.8,\"trace_sampled\"" + ":false,\"trace_sample_rate\":0.1,\"profiling_traces_dir_path\":null,\"is_profiling_enabled\":false," + - "\"profile_lifecycle\":\"TRACE\",\"profiling_traces_hz\":65,\"continuous_profile_sampled\":true}" + "\"profile_lifecycle\":\"TRACE\",\"profiling_traces_hz\":65,\"continuous_profile_sampled\":true," + + "\"is_enable_app_start_profiling\":false,\"is_start_profiler_on_app_start\":true}" val actual = fixture.serializer.deserialize(StringReader(jsonAppStartProfilingOptions), SentryAppStartProfilingOptions::class.java) assertNotNull(actual) @@ -1257,6 +1258,8 @@ class JsonSerializerTest { assertEquals(appStartProfilingOptions.profilingTracesHz, actual.profilingTracesHz) assertEquals(appStartProfilingOptions.profilingTracesDirPath, actual.profilingTracesDirPath) assertEquals(appStartProfilingOptions.profileLifecycle, actual.profileLifecycle) + assertEquals(appStartProfilingOptions.isEnableAppStartProfiling, actual.isEnableAppStartProfiling) + assertEquals(appStartProfilingOptions.isStartProfilerOnAppStart, actual.isStartProfilerOnAppStart) assertNull(actual.unknown) } @@ -1562,6 +1565,8 @@ class JsonSerializerTest { isContinuousProfilingEnabled = false profilingTracesHz = 65 profileLifecycle = ProfileLifecycle.TRACE + isEnableAppStartProfiling = false + isStartProfilerOnAppStart = true } private fun createSpan(): ISpan { diff --git a/sentry/src/test/java/io/sentry/SentryTest.kt b/sentry/src/test/java/io/sentry/SentryTest.kt index cf7f7384f8..bf68c63d07 100644 --- a/sentry/src/test/java/io/sentry/SentryTest.kt +++ b/sentry/src/test/java/io/sentry/SentryTest.kt @@ -1130,6 +1130,25 @@ class SentryTest { ) } + @Test + fun `init calls samplers if isStartProfilerOnAppStart is true`() { + val mockSampleTracer = mock() + val mockProfilesSampler = mock() + Sentry.init { + it.dsn = dsn + it.tracesSampleRate = 1.0 + it.experimental.isStartProfilerOnAppStart = true + it.profilesSampleRate = 1.0 + it.tracesSampler = mockSampleTracer + it.profilesSampler = mockProfilesSampler + it.executorService = ImmediateExecutorService() + it.cacheDirPath = getTempPath() + } + // Samplers are not called + verify(mockSampleTracer, never()).sample(any()) + verify(mockProfilesSampler, never()).sample(any()) + } + @Test fun `init calls app start profiling samplers in the background`() { val mockSampleTracer = mock() @@ -1220,6 +1239,24 @@ class SentryTest { assertTrue(appStartProfilingConfigFile.exists()) } + @Test + fun `init creates app start profiling config if isStartProfilerOnAppStart, even with performance disabled`() { + val path = getTempPath() + File(path).mkdirs() + val appStartProfilingConfigFile = File(path, "app_start_profiling_config") + appStartProfilingConfigFile.createNewFile() + assertTrue(appStartProfilingConfigFile.exists()) + Sentry.init { + it.dsn = dsn + it.cacheDirPath = path + it.isEnableAppStartProfiling = false + it.experimental.isStartProfilerOnAppStart = true + it.tracesSampleRate = 0.0 + it.executorService = ImmediateExecutorService() + } + assertTrue(appStartProfilingConfigFile.exists()) + } + @Test fun `init saves SentryAppStartProfilingOptions to disk`() { var options = SentryOptions() @@ -1227,9 +1264,9 @@ class SentryTest { Sentry.init { it.dsn = dsn it.cacheDirPath = path - it.tracesSampleRate = 1.0 it.tracesSampleRate = 0.5 it.isEnableAppStartProfiling = true + it.experimental.isStartProfilerOnAppStart = true it.profilesSampleRate = 0.2 it.executorService = ImmediateExecutorService() options = it @@ -1242,6 +1279,8 @@ class SentryTest { assertEquals(0.5, appStartOption.traceSampleRate) assertEquals(0.2, appStartOption.profileSampleRate) assertTrue(appStartOption.isProfilingEnabled) + assertTrue(appStartOption.isEnableAppStartProfiling) + assertTrue(appStartOption.isStartProfilerOnAppStart) } @Test From d72b09fb76b46124a183bc44b837422b5e19fe0b Mon Sep 17 00:00:00 2001 From: stefanosiano Date: Fri, 7 Mar 2025 11:28:01 +0100 Subject: [PATCH 3/3] added app start option checks in SentryPerformanceProvider --- .../core/SentryPerformanceProvider.java | 8 +++++--- .../core/SentryPerformanceProviderTest.kt | 20 +++++++++++++++++++ .../src/main/AndroidManifest.xml | 8 ++++++-- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryPerformanceProvider.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryPerformanceProvider.java index df05f880d8..93801b0cf5 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryPerformanceProvider.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryPerformanceProvider.java @@ -139,7 +139,8 @@ private void launchAppStartProfiler(final @NotNull AppStartMetrics appStartMetri return; } - if (profilingOptions.isContinuousProfilingEnabled()) { + if (profilingOptions.isContinuousProfilingEnabled() + && profilingOptions.isStartProfilerOnAppStart()) { createAndStartContinuousProfiler(context, profilingOptions, appStartMetrics); return; } @@ -150,8 +151,9 @@ private void launchAppStartProfiler(final @NotNull AppStartMetrics appStartMetri return; } - createAndStartTransactionProfiler(context, profilingOptions, appStartMetrics); - + if (profilingOptions.isEnableAppStartProfiling()) { + createAndStartTransactionProfiler(context, profilingOptions, appStartMetrics); + } } catch (FileNotFoundException e) { logger.log(SentryLevel.ERROR, "App start profiling config file not found. ", e); } catch (Throwable e) { diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/SentryPerformanceProviderTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/SentryPerformanceProviderTest.kt index 16cce1a209..83f5795f13 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/SentryPerformanceProviderTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/SentryPerformanceProviderTest.kt @@ -326,6 +326,22 @@ class SentryPerformanceProviderTest { assertFalse(AppStartMetrics.getInstance().appStartProfiler!!.isRunning) } + @Test + fun `when isEnableAppStartProfiling is false, transaction profiler is not started`() { + fixture.getSut { config -> + writeConfig(config, profilingEnabled = true, continuousProfilingEnabled = false, isEnableAppStartProfiling = false) + } + assertNull(AppStartMetrics.getInstance().appStartProfiler) + } + + @Test + fun `when isStartProfilerOnAppStart is false, continuous profiler is not started`() { + fixture.getSut { config -> + writeConfig(config, profilingEnabled = false, continuousProfilingEnabled = true, isStartProfilerOnAppStart = false) + } + assertNull(AppStartMetrics.getInstance().appStartContinuousProfiler) + } + @Test fun `when provider is closed, continuous profiler is stopped`() { val provider = fixture.getSut { config -> @@ -345,6 +361,8 @@ class SentryPerformanceProviderTest { profileSampled: Boolean = true, profileSampleRate: Double = 1.0, continuousProfileSampled: Boolean = true, + isEnableAppStartProfiling: Boolean = true, + isStartProfilerOnAppStart: Boolean = true, profilingTracesDirPath: String = traceDir.absolutePath ) { val appStartProfilingOptions = SentryAppStartProfilingOptions() @@ -357,6 +375,8 @@ class SentryPerformanceProviderTest { appStartProfilingOptions.isContinuousProfileSampled = continuousProfileSampled appStartProfilingOptions.profilingTracesDirPath = profilingTracesDirPath appStartProfilingOptions.profilingTracesHz = 101 + appStartProfilingOptions.isEnableAppStartProfiling = isEnableAppStartProfiling + appStartProfilingOptions.isStartProfilerOnAppStart = isStartProfilerOnAppStart JsonSerializer(SentryOptions.empty()).serialize(appStartProfilingOptions, FileWriter(configFile)) } //endregion diff --git a/sentry-samples/sentry-samples-android/src/main/AndroidManifest.xml b/sentry-samples/sentry-samples-android/src/main/AndroidManifest.xml index 5afe6ac180..03fb4e5f20 100644 --- a/sentry-samples/sentry-samples-android/src/main/AndroidManifest.xml +++ b/sentry-samples/sentry-samples-android/src/main/AndroidManifest.xml @@ -112,8 +112,12 @@ - - + + + + + +