| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
|
Sorry, something went wrong.
Semver Impact of This PR🟡 Minor (new features) 📋 Changelog PreviewThis is how your changes will appear in the changelog. This PR will not appear in the changelog. 🤖 This preview updates automatically when you update the PR. |
Sorry, something went wrong.
📲 Install BuildsAndroid
|
Sorry, something went wrong.
Performance metrics 🚀
Baseline results on branch: mainStartup times
App size
Previous results on branch: claude/dreamy-solomonStartup times
App size
|
Sorry, something went wrong.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Autofix DetailsBugbot Autofix prepared a fix for the issue found in the latest run.
Or push these changes by commenting:
@cursor push 56eb859503
diff --git a/sentry/src/main/java/io/sentry/SentryEnvelopeItemHeader.java b/sentry/src/main/java/io/sentry/SentryEnvelopeItemHeader.java
--- a/sentry/src/main/java/io/sentry/SentryEnvelopeItemHeader.java
+++ b/sentry/src/main/java/io/sentry/SentryEnvelopeItemHeader.java
@@ -219,8 +219,9 @@
if (itemCount != null) {
writer.name(JsonKeys.ITEM_COUNT).value(itemCount);
}
- if (metaLength != null) {
- writer.name(JsonKeys.META_LENGTH).value(metaLength);
+ final @Nullable Integer metaLengthValue = getMetaLength();
+ if (metaLengthValue != null) {
+ writer.name(JsonKeys.META_LENGTH).value(metaLengthValue);
}
writer.name(JsonKeys.LENGTH).value(getLength());
if (unknown != null) {This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.
Sorry, something went wrong.
Adds a new boolean option `useProfilingManager` that gates whether the SDK uses Android's ProfilingManager API (API 35+) for Perfetto-based profiling. On devices below API 35 where ProfilingManager is not available, no profiling data is collected — the legacy Debug-based profiler is not used as a fallback. Wired through SentryOptions and ManifestMetadataReader (AndroidManifest meta-data). Defaults to false (opt-in). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds UI controls to the profiling sample activity for testing both legacy and Perfetto profiling paths. Enables useProfilingManager flag in the sample manifest for API 35+ testing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Show active profiler status line with (i) info button to show SDK config (sample rates, lifecycle mode, use-profiling-manager) - Conditionally show Start(Manual) or Start(Transaction) button based on profileLifecycle mode, since each is a no-op in the wrong mode - Hide duration seekbar in MANUAL mode (only affects transaction length) - Remove inline profiling result TextView; show results via Toast and in the (i) dialog instead - Apply AppTheme.Main to fix edge-to-edge clipping on API 35+ - Add indices to the bitmap list items so user can see the list view jumping around Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ager is set When useProfilingManager is true, SentryPerformanceProvider now skips creating the legacy Debug-based profiler at app start. This ensures AndroidOptionsInitializer creates a Perfetto profiler instead, without needing special handover logic between the two profiling engines. The useProfilingManager flag is persisted in SentryAppStartProfilingOptions (written at end of Sentry.init(), read on next app launch) so the decision is available before SDK initialization. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> squash into options commit
…Profiler Introduces PerfettoProfiler, which uses Android's ProfilingManager system service (API 35+) for Perfetto-based stack sampling. When useProfilingManager is enabled, AndroidContinuousProfiler selects PerfettoProfiler at init time via createWithProfilingManager(); on older devices no profiling data is collected and the legacy Debug-based profiler is not used as a fallback. Key changes: - PerfettoProfiler: calls requestProfiling(STACK_SAMPLING), waits for ProfilingResult via CountDownLatch, reads .pftrace via getResultFilePath() - AndroidContinuousProfiler: factory methods createLegacy() / createWithProfilingManager() replace the public constructor; init() split into initLegacy() / initProfilingManager() for clarity; stopFuture uses cancel(false) to avoid interrupting the Perfetto result wait - AndroidOptionsInitializer: branches on isUseProfilingManager() to select the correct factory method - SentryEnvelopeItem: fromPerfettoProfileChunk() builds a single envelope item with meta_length header separating JSON metadata from binary .pftrace - SentryEnvelopeItemHeader: adds metaLength field for the binary format - ProfileChunk: adds contentType and version fields; Builder.setContentType() - SentryClient: routes Perfetto chunks to fromPerfettoProfileChunk() Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
There are 3 total unresolved issues (including 1 from previous review).
Autofix DetailsBugbot Autofix prepared fixes for both issues found in the latest run.
Or push these changes by commenting:
@cursor push ce6f706c8b
diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java
--- a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java
+++ b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java
@@ -337,6 +337,17 @@
performanceCollector.start(chunkId.toString());
}
} else {
+ if (options.isUseProfilingManager()
+ && buildInfoProvider.getSdkInfoVersion() < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+ options
+ .getLogger()
+ .log(
+ SentryLevel.INFO,
+ "useProfilingManager is enabled, but API level is below %d. Continuous profiling is disabled.",
+ Build.VERSION_CODES.VANILLA_ICE_CREAM);
+ options.setContinuousProfiler(NoOpContinuousProfiler.getInstance());
+ return;
+ }
final @NotNull SentryFrameMetricsCollector frameMetricsCollector =
Objects.requireNonNull(
options.getFrameMetricsCollector(), "options.getFrameMetricsCollector is required");
diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/PerfettoContinuousProfiler.java b/sentry-android-core/src/main/java/io/sentry/android/core/PerfettoContinuousProfiler.java
--- a/sentry-android-core/src/main/java/io/sentry/android/core/PerfettoContinuousProfiler.java
+++ b/sentry-android-core/src/main/java/io/sentry/android/core/PerfettoContinuousProfiler.java
@@ -107,7 +107,6 @@
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;
@@ -118,6 +117,7 @@
}
switch (profileLifecycle) {
case TRACE:
+ shouldStop = false;
activeTraceCount = Math.max(0, activeTraceCount); // safety check.
activeTraceCount++;
break;
@@ -128,6 +128,7 @@
"Unexpected call to startProfiler(MANUAL) while profiler already running. Skipping.");
return;
}
+ shouldStop = false;
break;
}
if (!isRunning()) {
diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt
--- a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt
+++ b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt
@@ -376,7 +376,16 @@
assertTrue(fixture.sentryOptions.continuousProfiler is AndroidContinuousProfiler)
}
+ @Config(sdk = [34])
@Test
+ fun `init with profiling manager below API 35 sets no-op continuous profiler`() {
+ fixture.initSut(configureOptions = { isUseProfilingManager = true }, useRealContext = true)
+
+ assertEquals(NoOpTransactionProfiler.getInstance(), fixture.sentryOptions.transactionProfiler)
+ assertEquals(NoOpContinuousProfiler.getInstance(), fixture.sentryOptions.continuousProfiler)
+ }
+
+ @Test
fun `init with profilesSampleRate should set Android transaction profiler`() {
fixture.initSut(configureOptions = { profilesSampleRate = 1.0 })
diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/PerfettoContinuousProfilerTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/PerfettoContinuousProfilerTest.kt
--- a/sentry-android-core/src/test/java/io/sentry/android/core/PerfettoContinuousProfilerTest.kt
+++ b/sentry-android-core/src/test/java/io/sentry/android/core/PerfettoContinuousProfilerTest.kt
@@ -132,4 +132,24 @@
"Profiler should continue running after chunk restart — shouldStop must be reset on start",
)
}
+
+ @Test
+ fun `manual start while trace profiling is running does not cancel pending trace stop`() {
+ val profiler = fixture.getSut()
+
+ profiler.startProfiler(ProfileLifecycle.TRACE, fixture.mockTracesSampler)
+ assertTrue(profiler.isRunning)
+
+ profiler.stopProfiler(ProfileLifecycle.TRACE)
+ profiler.startProfiler(ProfileLifecycle.MANUAL, fixture.mockTracesSampler)
+
+ fixture.executor.runAll()
+
+ assertFalse(profiler.isRunning)
+ verify(fixture.mockLogger)
+ .log(
+ eq(SentryLevel.WARNING),
+ eq("Unexpected call to startProfiler(MANUAL) while profiler already running. Skipping."),
+ )
+ }
}This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.
Sorry, something went wrong.
…eader SentryEnvelopeItemHeader.serialize() checked the raw metaLength field instead of calling getMetaLength(), so the callable path used by Perfetto profile chunks was never invoked and meta_length was never written to the envelope header JSON. Refactor SentryEnvelopeItemHeader to remove the metaLength field entirely — all constructors now store a single calculateMetaLength callable. Eager constructors (deserializer) wrap the Integer in a lambda. All constructors delegate to one private primary constructor. In fromPerfettoProfileChunk, replace the round-trip through ProfileChunk.setMetaLength/getMetaLength with a local AtomicReference shared between the CachedItem lambda and the header callable, keeping meta_length as an envelope transport concern rather than in ProfileChunk
…uousProfiler Separate the Perfetto/ProfilingManager profiling backend into its own IContinuousProfiler implementation to keep the two backends independent. - AndroidContinuousProfiler is restored to legacy-only (no Perfetto fields, no conditional branches, no @SuppressLint annotations) - PerfettoContinuousProfiler is a new @RequiresApi(35) class that delegates to PerfettoProfiler and always sets content_type="perfetto" - AndroidOptionsInitializer branches on useProfilingManager to pick the right implementation - Consistent locking: startInternal/stopInternal both require caller to hold the lock, with callers wrapped accordingly - Renamed rootSpanCounter to activeTraceCount in PerfettoContinuousProfiler - Extracted tryResolveScopes/onScopesAvailable from initScopes in both classes - Fixed duplicate listener bug in PerfettoProfiler (was using local lambda instead of class-scope profilingResultListener)
Move deprecation timeline: will be deprecated in the next major release and removed in the one after. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Missed this class in the previous commit, causing -Werror build failure. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The perfettoProfilerFactory lambda captured the raw context parameter, which could be an Activity. Even though getApplicationContext() was called inside the lambda, the closure itself held a strong ref to the Activity, preventing GC after destroy. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tion test The enableLegacyProfiling field was added to serialization but the test's expected JSON was not updated. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…Profiling is false (#5765) * Fix javadoc errors * fix(profiling): Disable transaction-based profiling when enableLegacyProfiling is false Transaction-based profiling (profilesSampleRate/profilesSampler) always relies on the legacy Debug-based profiler and is not supported by the ProfilingManager (Perfetto) backend. Previously enableLegacyProfiling only controlled continuous profiling, so profilesSampleRate + enableLegacyProfiling=false would still run the legacy transaction profiler. Now setting enableLegacyProfiling to false also disables transaction-based profiling on all devices, logging a warning to guide users towards profileSessionSampleRate for continuous profiling. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Remove changelog entry --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ll app context Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ame timestamps Synchronize both the read and the write side of the performance collection data list. CompositePerformanceCollector.stop() hands back its live list while its timer thread may still append to it, so iterating it could throw ConcurrentModificationException. The existing reader-side lock in AndroidProfiler had no matching writer-side lock, so it never excluded anything. Also drop frame metrics whose chunk-relative timestamp is negative, matching AndroidProfiler. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit d8b0ed6. Configure here.
Sorry, something went wrong.
|
🎉 💯 🙌 |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
📜 Description
Adds support for Android's ProfilingManager API (API 35+), which captures continuous profiles (profile chunks) using OS-level Perfetto stack sampling instead of the legacy Debug.startMethodTracingSampling engine.
Behavior by API level:
⚙️ New options
⚠️ Limitations
💡 Motivation and Context
💚 How did you test it?
📝 Checklist