Skip to content
Draft
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

### Performance

- Parse the app start profiling config with only the deserializer it needs instead of building a full `JsonSerializer` and `SentryOptions`, cutting 188 of 221 allocations on the main thread before `Application.onCreate` ([#5867](https://github.com/getsentry/sentry-java/pull/5867))
- Create the outbox and cache directories lazily in their consumers instead of during SDK init, moving the `mkdirs()` calls off the init (main) thread ([#5792](https://github.com/getsentry/sentry-java/pull/5792))
- Reduce the number of SDK threads: `LifecycleWatcher` now schedules the session-end task on the shared timer executor instead of creating a dedicated `java.util.Timer` thread ([#5819](https://github.com/getsentry/sentry-java/pull/5819))
- Reduce the number of SDK threads: `RateLimiter` now schedules its rate-limit-lifted notifications on the shared timer executor instead of creating a dedicated `java.util.Timer` thread ([#5814](https://github.com/getsentry/sentry-java/pull/5814))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import io.sentry.ILogger;
import io.sentry.ISentryLifecycleToken;
import io.sentry.ITransactionProfiler;
import io.sentry.JsonSerializer;
import io.sentry.JsonObjectReader;
import io.sentry.SentryAppStartProfilingOptions;
import io.sentry.SentryExecutorService;
import io.sentry.SentryLevel;
Expand Down Expand Up @@ -119,8 +119,7 @@ private void launchAppStartProfiler(final @NotNull AppStartMetrics appStartMetri
try (final @NotNull Reader reader =
new BufferedReader(new InputStreamReader(new FileInputStream(configFile)))) {
final @Nullable SentryAppStartProfilingOptions profilingOptions =
new JsonSerializer(SentryOptions.empty())
.deserialize(reader, SentryAppStartProfilingOptions.class);
deserializeProfilingConfig(reader);

if (profilingOptions == null) {
logger.log(
Expand Down Expand Up @@ -151,6 +150,29 @@ private void launchAppStartProfiler(final @NotNull AppStartMetrics appStartMetri
}
}

/**
* Parses the app start profiling config with only the deserializer it needs. Going through {@link
* io.sentry.JsonSerializer} would allocate a full {@link SentryOptions} plus all 56 registered
* deserializers on the main thread before {@code Application.onCreate}, to use exactly one of
* them.
*
* <p>Returns null on malformed input, matching what {@code JsonSerializer.deserialize} did, so
* callers keep reporting it as a deserialization failure rather than a read error. The vendored
* JSON reader signals bad input with {@link java.io.IOException} (including {@code
* MalformedJsonException} and {@code EOFException}), {@link IllegalStateException} on a token
* type mismatch, and {@link NumberFormatException} on an unparseable number โ€” all {@link
* Exception} subclasses, so {@code Error} propagates instead of being swallowed here.
*/
private @Nullable SentryAppStartProfilingOptions deserializeProfilingConfig(
final @NotNull Reader reader) {
try (final @NotNull JsonObjectReader jsonReader = new JsonObjectReader(reader)) {
return new SentryAppStartProfilingOptions.Deserializer().deserialize(jsonReader, logger);
} catch (Exception e) {
logger.log(SentryLevel.ERROR, "Error when deserializing", e);
return null;
}
}

private void createAndStartContinuousProfiler(
final @NotNull Context context,
final @NotNull SentryAppStartProfilingOptions profilingOptions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,21 @@ class SentryPerformanceProviderTest {
)
}

@Test
fun `when config file is malformed, profiler is not started`() {
fixture.getSut { config -> config.writeText("{\"profile_sampled\": tru") }
assertNull(AppStartMetrics.getInstance().appStartProfiler)
assertNull(AppStartMetrics.getInstance().appStartContinuousProfiler)
verify(fixture.logger).log(eq(SentryLevel.ERROR), eq("Error when deserializing"), any())
verify(fixture.logger)
.log(
eq(SentryLevel.WARNING),
eq(
"Unable to deserialize the SentryAppStartProfilingOptions. App start profiling will not start."
),
)
}

@Test
fun `when profiling is disabled, profiler is not started`() {
fixture.getSut { config ->
Expand Down
Loading