Skip to content

Conversation

@markushi
Copy link
Member

@markushi markushi commented Nov 12, 2025

📜 Description

Adds ANR (Application Not Responding) profiling integration that profiles the main thread when an ANR is detected and reports the captured profiles to Sentry.

Key Changes:

  • New AnrProfilingIntegration to capture profiles during ANR events
  • AnrV2Integration now takes care of matching and capturing the profile on the next start
  • If the captured ANR event only contains system frames, a static fingerprint will get set, effectively changing the grouping behavior to group all noisy ANRs into a single issue

💡 Motivation and Context

This feature enables better ANR diagnostics by capturing profiling data at the time of ANR detection, allowing developers to identify performance bottlenecks and problematic code paths causing application hangs.

💚 How did you test it?

  • Added tests

📝 Checklist

  • I added GH Issue ID & Linear ID
  • I added tests to verify the changes.
  • No new PII added or SDK only sends newly added PII if sendDefaultPII is enabled.
  • I updated the docs if needed.
  • I updated the wizard if needed.
  • Review from the native team if needed.
  • No breaking change or entry added to the changelog.
  • No breaking change for hybrid SDKs or communicated to hybrid SDKs.

🔮 Next steps

@github-actions
Copy link
Contributor

github-actions bot commented Nov 12, 2025

Messages
📖 Do not forget to update Sentry-docs with your feature once the pull request gets approved.

Generated by 🚫 dangerJS against 0cd877d

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions
Copy link
Contributor

github-actions bot commented Nov 12, 2025

Performance metrics 🚀

  Plain With Sentry Diff
Startup time 350.00 ms 420.63 ms 70.63 ms
Size 1.58 MiB 2.19 MiB 624.94 KiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
27d7cf8 309.43 ms 364.27 ms 54.85 ms
f064536 335.52 ms 408.79 ms 73.27 ms
fc5ccaf 256.80 ms 322.36 ms 65.56 ms
e59e22a 368.02 ms 432.00 ms 63.98 ms
b750b96 408.98 ms 480.32 ms 71.34 ms
fcec2f2 328.91 ms 387.75 ms 58.84 ms
9139b91 351.35 ms 355.63 ms 4.28 ms
951caf7 323.66 ms 392.82 ms 69.16 ms
18c0bc2 306.73 ms 349.77 ms 43.03 ms
dba088c 365.46 ms 366.31 ms 0.85 ms

App size

Revision Plain With Sentry Diff
27d7cf8 1.58 MiB 2.12 MiB 549.42 KiB
f064536 1.58 MiB 2.20 MiB 633.90 KiB
fc5ccaf 1.58 MiB 2.13 MiB 557.54 KiB
e59e22a 1.58 MiB 2.20 MiB 635.34 KiB
b750b96 1.58 MiB 2.10 MiB 533.19 KiB
fcec2f2 1.58 MiB 2.12 MiB 551.50 KiB
9139b91 1.58 MiB 2.13 MiB 559.52 KiB
951caf7 1.58 MiB 2.13 MiB 558.77 KiB
18c0bc2 1.58 MiB 2.13 MiB 557.33 KiB
dba088c 1.58 MiB 2.13 MiB 558.99 KiB

@markushi markushi marked this pull request as draft December 3, 2025 07:19
@markushi
Copy link
Member Author

@sentry review

@markushi markushi marked this pull request as ready for review December 17, 2025 09:51
Copy link
Member Author

@markushi markushi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressing bot comments:

Concurrent QueueFile Access (Comments on AnrV2Integration.java:327, :367):
✅ Working as designed. The reader uses AnrProfileRotationHelper.getLastFile() which reads from a rotated/archived file, while the writer writes to the current file. They access different files.

AnrProfileManager file descriptor leak (AnrProfileManager.java:50):
✅ Already fixed. The code uses try-with-resources: try (final AnrProfileManager provider = new AnrProfileManager(options, lastFile))

lowQualityPackages initialization (AnrCulpritIdentifier.java:85):
✅ Already fixed. The field uses a static initializer block.

Stack depth calculation (AggregatedStackTrace.java:38):
✅ Already fixed.

Incomplete stack trace handling (AnrCulpritIdentifier.java:138):
✅ Already fixed. Code has if (stackTraceMap.isEmpty()) return null; guard at line 140.

Empty debug log (AnrV2Integration.java:346):
✅ Already fixed.

Missing rotate() call (AnrV2Integration.java:424):
✅ Working as designed. Rotation should only happen once after app start, not on every ANR event read.

Stack trace serialization (AnrStackTrace.java:35):
✅ Already fixed.

Missing listener removal (AnrProfilingIntegration.java:79):
✅ Already fixed.

Profile ID mismatch (AnrV2Integration.java:418):
✅ Working as designed. We intentionally return chunk.getProfilerId() to reference the chunk_id as profiler_id.

hasOnlySystemFrames() fingerprinting (AnrV2Integration.java:321, :497):
✅ FIXED in this update. Modified to return false when exceptions list is null/empty, preventing incorrect fingerprinting when profiling fails.

@linear
Copy link

linear bot commented Jan 30, 2026


if (this.options == null) {
return;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dead code due to requireNonNull throwing before null check

Low Severity

The if (this.options == null) return; check is unreachable dead code. Objects.requireNonNull on line 51-54 throws an IllegalArgumentException when the ternary expression evaluates to null (i.e., when options is not a SentryAndroidOptions). The null check can never execute because the exception is thrown first.

Fix in Cursor Fix in Web

@github-actions
Copy link
Contributor

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

  • (anr) Profile main thread when ANR and report ANR profiles to Sentry by markushi in #4899
  • (distribution) Add install_groups support by runningcode in #5062
  • (spotlight) Extract SpotlightIntegration to separate module by romtsn in #5064

Bug Fixes 🐛

  • Establish native exception mechanisms by supervacuus in #5052

Internal Changes 🔧

Deps

  • Bump urllib3 from 2.6.0 to 2.6.3 in the pip group across 1 directory by dependabot in #5003
  • Update Native SDK to v0.12.4 by github-actions in #5061
  • Bump getsentry/github-workflows/.github/workflows/updater.yml from 2 to 3 by dependabot in #4884
  • Bump actions/cache from 4 to 5 by dependabot in #4997
  • Bump github/codeql-action from 4.31.10 to 4.31.11 by dependabot in #5057
  • Bump getsentry/craft from 2.19.0 to 2.20.0 by dependabot in #5058

Other

  • (android) Update targetSdk to API 36 (Android 16) by markushi in #5016
  • (ci) Write permission for statuses in changelog preview by supervacuus in #5053
  • (samples) Convert main screen to Jetpack Compose by markushi in #5017

🤖 This preview updates automatically when you update the PR.

### Feature
### Features

- Add new experimental option to capture profiles for ANRs ([#4899](https://github.com/getsentry/sentry-java/pull/4899))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • 🚫 The changelog entry seems to be part of an already released section ## 8.31.0.
    Consider moving the entry to the ## Unreleased section, please.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

final long now = SystemClock.uptimeMillis();
final long diff = now - lastMainThreadExecutionTime;

if (diff < 1000) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardcoded value instead of defined constant

Low Severity

The comparison diff < 1000 uses a hardcoded value instead of the defined constant THRESHOLD_SUSPICION_MS (which equals 1000) declared on line 32. This inconsistency between the hardcoded literal and the constant makes the threshold relationship unclear and creates maintenance risk if the value needs to change.

Fix in Cursor Fix in Web


if (currentFile.exists()) {
currentFile.renameTo(oldFile);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

File rotation ignores operation failures silently

Medium Severity

The performRotationIfNeeded method ignores the return values of oldFile.delete() and currentFile.renameTo(oldFile). If delete() fails and the old file still exists, renameTo() may also fail on some platforms. The rotation is marked complete regardless of actual success, meaning getLastFile() could return stale profile data from a previous session instead of the correct ANR profile. This could cause incorrect profiling data to be attached to ANR events.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants