Skip to content

Commit d790035

Browse files
committed
added isStartProfilerOnAppStart logic and tests
1 parent dd735c2 commit d790035

File tree

8 files changed

+123
-15
lines changed

8 files changed

+123
-15
lines changed

sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -344,9 +344,14 @@ static void applyMetadata(
344344
ProfileLifecycle.valueOf(profileLifecycle.toUpperCase(Locale.ROOT)));
345345
}
346346

347-
options.getExperimental().setStartProfilerOnAppStart(
348-
readBool(
349-
metadata, logger, PROFILER_START_ON_APP_START, options.isStartProfilerOnAppStart()));
347+
options
348+
.getExperimental()
349+
.setStartProfilerOnAppStart(
350+
readBool(
351+
metadata,
352+
logger,
353+
PROFILER_START_ON_APP_START,
354+
options.isStartProfilerOnAppStart()));
350355

351356
options.setEnableUserInteractionTracing(
352357
readBool(metadata, logger, TRACES_UI_ENABLE, options.isEnableUserInteractionTracing()));

sentry/api/sentry.api

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,9 +446,11 @@ public final class io/sentry/ExperimentalOptions {
446446
public fun getProfileLifecycle ()Lio/sentry/ProfileLifecycle;
447447
public fun getProfileSessionSampleRate ()Ljava/lang/Double;
448448
public fun getSessionReplay ()Lio/sentry/SentryReplayOptions;
449+
public fun isStartProfilerOnAppStart ()Z
449450
public fun setProfileLifecycle (Lio/sentry/ProfileLifecycle;)V
450451
public fun setProfileSessionSampleRate (Ljava/lang/Double;)V
451452
public fun setSessionReplay (Lio/sentry/SentryReplayOptions;)V
453+
public fun setStartProfilerOnAppStart (Z)V
452454
}
453455

454456
public final class io/sentry/ExternalOptions {
@@ -2506,18 +2508,22 @@ public final class io/sentry/SentryAppStartProfilingOptions : io/sentry/JsonSeri
25062508
public fun getUnknown ()Ljava/util/Map;
25072509
public fun isContinuousProfileSampled ()Z
25082510
public fun isContinuousProfilingEnabled ()Z
2511+
public fun isEnableAppStartProfiling ()Z
25092512
public fun isProfileSampled ()Z
25102513
public fun isProfilingEnabled ()Z
2514+
public fun isStartProfilerOnAppStart ()Z
25112515
public fun isTraceSampled ()Z
25122516
public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V
25132517
public fun setContinuousProfileSampled (Z)V
25142518
public fun setContinuousProfilingEnabled (Z)V
2519+
public fun setEnableAppStartProfiling (Z)V
25152520
public fun setProfileLifecycle (Lio/sentry/ProfileLifecycle;)V
25162521
public fun setProfileSampleRate (Ljava/lang/Double;)V
25172522
public fun setProfileSampled (Z)V
25182523
public fun setProfilingEnabled (Z)V
25192524
public fun setProfilingTracesDirPath (Ljava/lang/String;)V
25202525
public fun setProfilingTracesHz (I)V
2526+
public fun setStartProfilerOnAppStart (Z)V
25212527
public fun setTraceSampleRate (Ljava/lang/Double;)V
25222528
public fun setTraceSampled (Z)V
25232529
public fun setUnknown (Ljava/util/Map;)V
@@ -2532,7 +2538,9 @@ public final class io/sentry/SentryAppStartProfilingOptions$Deserializer : io/se
25322538
public final class io/sentry/SentryAppStartProfilingOptions$JsonKeys {
25332539
public static final field CONTINUOUS_PROFILE_SAMPLED Ljava/lang/String;
25342540
public static final field IS_CONTINUOUS_PROFILING_ENABLED Ljava/lang/String;
2541+
public static final field IS_ENABLE_APP_START_PROFILING Ljava/lang/String;
25352542
public static final field IS_PROFILING_ENABLED Ljava/lang/String;
2543+
public static final field IS_START_PROFILER_ON_APP_START Ljava/lang/String;
25362544
public static final field PROFILE_LIFECYCLE Ljava/lang/String;
25372545
public static final field PROFILE_SAMPLED Ljava/lang/String;
25382546
public static final field PROFILE_SAMPLE_RATE Ljava/lang/String;
@@ -3065,6 +3073,7 @@ public class io/sentry/SentryOptions {
30653073
public fun isSendClientReports ()Z
30663074
public fun isSendDefaultPii ()Z
30673075
public fun isSendModules ()Z
3076+
public fun isStartProfilerOnAppStart ()Z
30683077
public fun isTraceOptionsRequests ()Z
30693078
public fun isTraceSampling ()Z
30703079
public fun isTracingEnabled ()Z

sentry/src/main/java/io/sentry/ExperimentalOptions.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,13 @@ public final class ExperimentalOptions {
2828
private @NotNull ProfileLifecycle profileLifecycle = ProfileLifecycle.MANUAL;
2929

3030
/**
31-
* Whether profiling can automatically be started as early as possible during the app lifecycle, to capture more of app startup.
32-
* 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
33-
* 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
31+
* Whether profiling can automatically be started as early as possible during the app lifecycle,
32+
* to capture more of app startup. If {@link ExperimentalOptions#profileLifecycle} is {@link
33+
* ProfileLifecycle#MANUAL} Profiling is started automatically on startup and stopProfileSession
34+
* must be called manually whenever the app startup is completed If {@link
35+
* ExperimentalOptions#profileLifecycle} is {@link ProfileLifecycle#TRACE} Profiling is started
36+
* automatically on startup, and will automatically be stopped when the root span that is
37+
* associated with app startup ends
3438
*/
3539
private boolean startProfilerOnAppStart = false;
3640

sentry/src/main/java/io/sentry/Sentry.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -373,8 +373,9 @@ private static void handleAppStartProfilingConfig(
373373
if (!options.isEnableAppStartProfiling() && !options.isStartProfilerOnAppStart()) {
374374
return;
375375
}
376-
todo isStartProfilerOnAppStart doesn't need tracing! - SentryTest takes hours to run!
377-
if (!options.isTracingEnabled()) {
376+
// isStartProfilerOnAppStart doesn't need tracing, as it can be started/stopped
377+
// manually
378+
if (!options.isStartProfilerOnAppStart() && !options.isTracingEnabled()) {
378379
options
379380
.getLogger()
380381
.log(
@@ -383,8 +384,13 @@ private static void handleAppStartProfilingConfig(
383384
return;
384385
}
385386
if (appStartProfilingConfigFile.createNewFile()) {
386-
// 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
387-
final @NotNull TracesSamplingDecision appStartSamplingDecision = options.isEnableAppStartProfiling() ? sampleAppStartProfiling(options) : new TracesSamplingDecision(false);
387+
// If old app start profiling is false, it means the transaction will not be
388+
// sampled, but we create the file anyway to allow continuous profiling on app
389+
// start
390+
final @NotNull TracesSamplingDecision appStartSamplingDecision =
391+
options.isEnableAppStartProfiling()
392+
? sampleAppStartProfiling(options)
393+
: new TracesSamplingDecision(false);
388394
final @NotNull SentryAppStartProfilingOptions appStartProfilingOptions =
389395
new SentryAppStartProfilingOptions(options, appStartSamplingDecision);
390396
try (final OutputStream outputStream =

sentry/src/main/java/io/sentry/SentryAppStartProfilingOptions.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ public final class SentryAppStartProfilingOptions implements JsonUnknown, JsonSe
2121
boolean isContinuousProfilingEnabled;
2222
int profilingTracesHz;
2323
boolean continuousProfileSampled;
24+
boolean isEnableAppStartProfiling;
25+
boolean isStartProfilerOnAppStart;
2426
@NotNull ProfileLifecycle profileLifecycle;
2527

2628
private @Nullable Map<String, Object> unknown;
@@ -37,6 +39,8 @@ public SentryAppStartProfilingOptions() {
3739
isContinuousProfilingEnabled = false;
3840
profileLifecycle = ProfileLifecycle.MANUAL;
3941
profilingTracesHz = 0;
42+
isEnableAppStartProfiling = true;
43+
isStartProfilerOnAppStart = false;
4044
}
4145

4246
SentryAppStartProfilingOptions(
@@ -52,6 +56,8 @@ public SentryAppStartProfilingOptions() {
5256
isContinuousProfilingEnabled = options.isContinuousProfilingEnabled();
5357
profileLifecycle = options.getProfileLifecycle();
5458
profilingTracesHz = options.getProfilingTracesHz();
59+
isEnableAppStartProfiling = options.isEnableAppStartProfiling();
60+
isStartProfilerOnAppStart = options.isStartProfilerOnAppStart();
5561
}
5662

5763
public void setProfileSampled(final boolean profileSampled) {
@@ -134,6 +140,22 @@ public int getProfilingTracesHz() {
134140
return profilingTracesHz;
135141
}
136142

143+
public void setEnableAppStartProfiling(final boolean enableAppStartProfiling) {
144+
isEnableAppStartProfiling = enableAppStartProfiling;
145+
}
146+
147+
public boolean isEnableAppStartProfiling() {
148+
return isEnableAppStartProfiling;
149+
}
150+
151+
public void setStartProfilerOnAppStart(final boolean startProfilerOnAppStart) {
152+
isStartProfilerOnAppStart = startProfilerOnAppStart;
153+
}
154+
155+
public boolean isStartProfilerOnAppStart() {
156+
return isStartProfilerOnAppStart;
157+
}
158+
137159
// JsonSerializable
138160

139161
public static final class JsonKeys {
@@ -147,6 +169,8 @@ public static final class JsonKeys {
147169
public static final String IS_CONTINUOUS_PROFILING_ENABLED = "is_continuous_profiling_enabled";
148170
public static final String PROFILE_LIFECYCLE = "profile_lifecycle";
149171
public static final String PROFILING_TRACES_HZ = "profiling_traces_hz";
172+
public static final String IS_ENABLE_APP_START_PROFILING = "is_enable_app_start_profiling";
173+
public static final String IS_START_PROFILER_ON_APP_START = "is_start_profiler_on_app_start";
150174
}
151175

152176
@Override
@@ -165,6 +189,8 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger
165189
.value(logger, isContinuousProfilingEnabled);
166190
writer.name(JsonKeys.PROFILE_LIFECYCLE).value(logger, profileLifecycle.name());
167191
writer.name(JsonKeys.PROFILING_TRACES_HZ).value(logger, profilingTracesHz);
192+
writer.name(JsonKeys.IS_ENABLE_APP_START_PROFILING).value(logger, isEnableAppStartProfiling);
193+
writer.name(JsonKeys.IS_START_PROFILER_ON_APP_START).value(logger, isStartProfilerOnAppStart);
168194

169195
if (unknown != null) {
170196
for (String key : unknown.keySet()) {
@@ -266,6 +292,18 @@ public static final class Deserializer
266292
options.profilingTracesHz = profilingTracesHz;
267293
}
268294
break;
295+
case JsonKeys.IS_ENABLE_APP_START_PROFILING:
296+
Boolean isEnableAppStartProfiling = reader.nextBooleanOrNull();
297+
if (isEnableAppStartProfiling != null) {
298+
options.isEnableAppStartProfiling = isEnableAppStartProfiling;
299+
}
300+
break;
301+
case JsonKeys.IS_START_PROFILER_ON_APP_START:
302+
Boolean isStartProfilerOnAppStart = reader.nextBooleanOrNull();
303+
if (isStartProfilerOnAppStart != null) {
304+
options.isStartProfilerOnAppStart = isStartProfilerOnAppStart;
305+
}
306+
break;
269307
default:
270308
if (unknown == null) {
271309
unknown = new ConcurrentHashMap<>();

sentry/src/main/java/io/sentry/SentryOptions.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1813,7 +1813,9 @@ public void setProfilesSampleRate(final @Nullable Double profilesSampleRate) {
18131813
return experimental.getProfileLifecycle();
18141814
}
18151815

1816-
/** Whether profiling can automatically be started as early as possible during the app lifecycle. */
1816+
/**
1817+
* Whether profiling can automatically be started as early as possible during the app lifecycle.
1818+
*/
18171819
@ApiStatus.Experimental
18181820
public boolean isStartProfilerOnAppStart() {
18191821
return experimental.isStartProfilerOnAppStart();

sentry/src/test/java/io/sentry/JsonSerializerTest.kt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,16 +1234,17 @@ class JsonSerializerTest {
12341234

12351235
val expected = "{\"profile_sampled\":true,\"profile_sample_rate\":0.8,\"continuous_profile_sampled\":true," +
12361236
"\"trace_sampled\":false,\"trace_sample_rate\":0.1,\"profiling_traces_dir_path\":null,\"is_profiling_enabled\":false," +
1237-
"\"is_continuous_profiling_enabled\":false,\"profile_lifecycle\":\"TRACE\",\"profiling_traces_hz\":65}"
1238-
1237+
"\"is_continuous_profiling_enabled\":false,\"profile_lifecycle\":\"TRACE\",\"profiling_traces_hz\":65," +
1238+
"\"is_enable_app_start_profiling\":false,\"is_start_profiler_on_app_start\":true}"
12391239
assertEquals(expected, actual)
12401240
}
12411241

12421242
@Test
12431243
fun `deserializing SentryAppStartProfilingOptions`() {
12441244
val jsonAppStartProfilingOptions = "{\"profile_sampled\":true,\"profile_sample_rate\":0.8,\"trace_sampled\"" +
12451245
":false,\"trace_sample_rate\":0.1,\"profiling_traces_dir_path\":null,\"is_profiling_enabled\":false," +
1246-
"\"profile_lifecycle\":\"TRACE\",\"profiling_traces_hz\":65,\"continuous_profile_sampled\":true}"
1246+
"\"profile_lifecycle\":\"TRACE\",\"profiling_traces_hz\":65,\"continuous_profile_sampled\":true," +
1247+
"\"is_enable_app_start_profiling\":false,\"is_start_profiler_on_app_start\":true}"
12471248

12481249
val actual = fixture.serializer.deserialize(StringReader(jsonAppStartProfilingOptions), SentryAppStartProfilingOptions::class.java)
12491250
assertNotNull(actual)
@@ -1257,6 +1258,8 @@ class JsonSerializerTest {
12571258
assertEquals(appStartProfilingOptions.profilingTracesHz, actual.profilingTracesHz)
12581259
assertEquals(appStartProfilingOptions.profilingTracesDirPath, actual.profilingTracesDirPath)
12591260
assertEquals(appStartProfilingOptions.profileLifecycle, actual.profileLifecycle)
1261+
assertEquals(appStartProfilingOptions.isEnableAppStartProfiling, actual.isEnableAppStartProfiling)
1262+
assertEquals(appStartProfilingOptions.isStartProfilerOnAppStart, actual.isStartProfilerOnAppStart)
12601263
assertNull(actual.unknown)
12611264
}
12621265

@@ -1562,6 +1565,8 @@ class JsonSerializerTest {
15621565
isContinuousProfilingEnabled = false
15631566
profilingTracesHz = 65
15641567
profileLifecycle = ProfileLifecycle.TRACE
1568+
isEnableAppStartProfiling = false
1569+
isStartProfilerOnAppStart = true
15651570
}
15661571

15671572
private fun createSpan(): ISpan {

sentry/src/test/java/io/sentry/SentryTest.kt

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1130,6 +1130,25 @@ class SentryTest {
11301130
)
11311131
}
11321132

1133+
@Test
1134+
fun `init calls samplers if isStartProfilerOnAppStart is true`() {
1135+
val mockSampleTracer = mock<TracesSamplerCallback>()
1136+
val mockProfilesSampler = mock<ProfilesSamplerCallback>()
1137+
Sentry.init {
1138+
it.dsn = dsn
1139+
it.tracesSampleRate = 1.0
1140+
it.experimental.isStartProfilerOnAppStart = true
1141+
it.profilesSampleRate = 1.0
1142+
it.tracesSampler = mockSampleTracer
1143+
it.profilesSampler = mockProfilesSampler
1144+
it.executorService = ImmediateExecutorService()
1145+
it.cacheDirPath = getTempPath()
1146+
}
1147+
// Samplers are not called
1148+
verify(mockSampleTracer, never()).sample(any())
1149+
verify(mockProfilesSampler, never()).sample(any())
1150+
}
1151+
11331152
@Test
11341153
fun `init calls app start profiling samplers in the background`() {
11351154
val mockSampleTracer = mock<TracesSamplerCallback>()
@@ -1220,16 +1239,34 @@ class SentryTest {
12201239
assertTrue(appStartProfilingConfigFile.exists())
12211240
}
12221241

1242+
@Test
1243+
fun `init creates app start profiling config if isStartProfilerOnAppStart, even with performance disabled`() {
1244+
val path = getTempPath()
1245+
File(path).mkdirs()
1246+
val appStartProfilingConfigFile = File(path, "app_start_profiling_config")
1247+
appStartProfilingConfigFile.createNewFile()
1248+
assertTrue(appStartProfilingConfigFile.exists())
1249+
Sentry.init {
1250+
it.dsn = dsn
1251+
it.cacheDirPath = path
1252+
it.isEnableAppStartProfiling = false
1253+
it.experimental.isStartProfilerOnAppStart = true
1254+
it.tracesSampleRate = 0.0
1255+
it.executorService = ImmediateExecutorService()
1256+
}
1257+
assertTrue(appStartProfilingConfigFile.exists())
1258+
}
1259+
12231260
@Test
12241261
fun `init saves SentryAppStartProfilingOptions to disk`() {
12251262
var options = SentryOptions()
12261263
val path = getTempPath()
12271264
Sentry.init {
12281265
it.dsn = dsn
12291266
it.cacheDirPath = path
1230-
it.tracesSampleRate = 1.0
12311267
it.tracesSampleRate = 0.5
12321268
it.isEnableAppStartProfiling = true
1269+
it.experimental.isStartProfilerOnAppStart = true
12331270
it.profilesSampleRate = 0.2
12341271
it.executorService = ImmediateExecutorService()
12351272
options = it
@@ -1242,6 +1279,8 @@ class SentryTest {
12421279
assertEquals(0.5, appStartOption.traceSampleRate)
12431280
assertEquals(0.2, appStartOption.profileSampleRate)
12441281
assertTrue(appStartOption.isProfilingEnabled)
1282+
assertTrue(appStartOption.isEnableAppStartProfiling)
1283+
assertTrue(appStartOption.isStartProfilerOnAppStart)
12451284
}
12461285

12471286
@Test

0 commit comments

Comments
 (0)