Skip to content

fix(android): identify and correctly structure Java/Kotlin frames…#5116

Open
supervacuus wants to merge 11 commits intomainfrom
fix/android/id_java_kotlin_frames_tombstone
Open

fix(android): identify and correctly structure Java/Kotlin frames…#5116
supervacuus wants to merge 11 commits intomainfrom
fix/android/id_java_kotlin_frames_tombstone

Conversation

@supervacuus
Copy link
Collaborator

@supervacuus supervacuus commented Feb 25, 2026

…in mixed Tombstone stack traces.

📜 Description

This PR adds:

  • identification of platform="java" in mixed platform Tombstone stack frames
  • restructuring of those platform="java" frames to the expected backend format (using module, function, and platform: "java" instead of package, function, instruction_addr as used for native frames)
  • a snapshot test against the tombstone that checks the identification of Java frames and in-app doesn't regress

💡 Motivation and Context

This is a planned item of the "wrap-up" UI improvements in https://linear.app/getsentry/project/tombstone-support-android-0024cb6e3ffd/

It covers the SDK part of https://linear.app/getsentry/issue/ANDROID-265/improve-ux-for-mixed-stacktraces (getsentry/sentry#107318).

💚 How did you test it?

📝 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

@linear
Copy link

linear bot commented Feb 25, 2026

@github-actions
Copy link
Contributor

github-actions bot commented Feb 25, 2026

Semver Impact of This PR

🟢 Patch (bug fixes)

📋 Changelog Preview

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


Bug Fixes 🐛

  • (android) Identify and correctly structure Java/Kotlin frames… by supervacuus in #5116

🤖 This preview updates automatically when you update the PR.

@supervacuus
Copy link
Collaborator Author

While testing this manually, it became apparent that the SDK-level inApp detection and the one on the server disagree. See here: https://sentry-sdks.sentry.io/issues/7288803722/events/af5e7573571f4cd98616f947e081ad09/

In the event, only the JNI frame is kept as inApp because it is a native frame. The Java frames from the package are marked as "framework" -> "system (see the data property for evidence of an overwrite):

image

Not sure if this is really a problem, but it certainly was unexpected.

Please also keep in mind that this PR should be blocked for merging/releasing until UI issues are fixed here: getsentry/sentry#107318. For instance, currently, there is no stable rendering between native and Java frames.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 25, 2026

Performance metrics 🚀

  Plain With Sentry Diff
Startup time 348.67 ms 412.24 ms 63.58 ms
Size 0 B 0 B 0 B

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
6405ec5 310.88 ms 354.56 ms 43.69 ms
d5a29b6 298.62 ms 391.78 ms 93.16 ms
f064536 329.00 ms 395.62 ms 66.62 ms
fcec2f2 357.47 ms 447.32 ms 89.85 ms
bbc35bb 324.88 ms 425.73 ms 100.85 ms
694d587 312.37 ms 402.77 ms 90.41 ms
b8bd880 314.56 ms 336.50 ms 21.94 ms
889ecea 367.58 ms 437.52 ms 69.94 ms
e2dce0b 308.96 ms 360.10 ms 51.14 ms
83884a0 334.46 ms 400.92 ms 66.46 ms

App size

Revision Plain With Sentry Diff
6405ec5 1.58 MiB 2.12 MiB 552.23 KiB
d5a29b6 1.58 MiB 2.12 MiB 549.37 KiB
f064536 1.58 MiB 2.20 MiB 633.90 KiB
fcec2f2 1.58 MiB 2.12 MiB 551.50 KiB
bbc35bb 1.58 MiB 2.12 MiB 553.01 KiB
694d587 1.58 MiB 2.19 MiB 620.06 KiB
b8bd880 1.58 MiB 2.29 MiB 722.92 KiB
889ecea 1.58 MiB 2.11 MiB 539.75 KiB
e2dce0b 0 B 0 B 0 B
83884a0 1.58 MiB 2.29 MiB 722.97 KiB

Previous results on branch: fix/android/id_java_kotlin_frames_tombstone

Startup times

Revision Plain With Sentry Diff
b9f7b6b 296.53 ms 355.34 ms 58.81 ms
c0f9db0 309.27 ms 381.48 ms 72.21 ms
b1f1d23 311.64 ms 365.73 ms 54.09 ms
c911bc0 369.24 ms 420.27 ms 51.03 ms
faa47e5 357.79 ms 451.31 ms 93.52 ms
238420b 318.77 ms 364.60 ms 45.83 ms

App size

Revision Plain With Sentry Diff
b9f7b6b 1.58 MiB 2.29 MiB 720.04 KiB
c0f9db0 0 B 0 B 0 B
b1f1d23 0 B 0 B 0 B
c911bc0 0 B 0 B 0 B
faa47e5 0 B 0 B 0 B
238420b 1.58 MiB 2.29 MiB 720.12 KiB

@sentry
Copy link

sentry bot commented Mar 13, 2026

Sentry Build Distribution

App Name App ID Version Configuration Install Page
SDK Size io.sentry.tests.size 8.37.1 (1) release Install Build

Copy link
Member

@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.

LGTM, nice!, I'll not marked it as approved, as we should get the frontend changes in first.

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 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Tests expect empty string but production code returns null
    • Updated both bare-function Tombstone parser tests to assert null module values, matching production behavior when no module can be extracted.

Create PR

Or push these changes by commenting:

@cursor push 59609e9828
Preview (59609e9828)
diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/internal/tombstone/TombstoneParserTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/internal/tombstone/TombstoneParserTest.kt
--- a/sentry-android-core/src/test/java/io/sentry/android/core/internal/tombstone/TombstoneParserTest.kt
+++ b/sentry-android-core/src/test/java/io/sentry/android/core/internal/tombstone/TombstoneParserTest.kt
@@ -13,6 +13,7 @@
 import kotlin.test.Test
 import kotlin.test.assertEquals
 import kotlin.test.assertNotNull
+import kotlin.test.assertNull
 import org.mockito.kotlin.mock
 
 class TombstoneParserTest {
@@ -496,7 +497,7 @@
     val frame = event.threads!![0].stacktrace!!.frames!![0]
     assertEquals("java", frame.platform)
     assertEquals("myMethod", frame.function)
-    assertEquals("", frame.module)
+    assertNull(frame.module)
   }
 
   @Test
@@ -505,7 +506,7 @@
     val frame = event.threads!![0].stacktrace!!.frames!![0]
     assertEquals("java", frame.platform)
     assertEquals("myMethod", frame.function)
-    assertEquals("", frame.module)
+    assertNull(frame.module)
   }
 
   @Test

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

cursoragent and others added 3 commits March 13, 2026 10:39
* test(android): add proguard tombstone test cases

* remove esr from the expected register set (used only for hard-faults)

* Format code

* re-enable sentryCompose and disable auto-installation in the gradle plugin

* adapt parseTombstoneWithJavaFunctionName in TombstoneParserTest to epitaph and make the snapshot test independent of order.

---------

Co-authored-by: Sentry Github Bot <[email protected]>
@supervacuus supervacuus force-pushed the fix/android/id_java_kotlin_frames_tombstone branch from 4324585 to f7473a5 Compare March 13, 2026 12:11
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 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Double normalizeFunctionName call for same input
    • The Java frame path now normalizes frame.functionName once and reuses that normalized value for both module and function extraction.

Create PR

Or push these changes by commenting:

@cursor push 44ba30fd16
Preview (44ba30fd16)
diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/tombstone/TombstoneParser.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/tombstone/TombstoneParser.java
--- a/sentry-android-core/src/main/java/io/sentry/android/core/internal/tombstone/TombstoneParser.java
+++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/tombstone/TombstoneParser.java
@@ -152,8 +152,9 @@
       final SentryStackFrame stackFrame = new SentryStackFrame();
       if (isJavaFrame(frame)) {
         stackFrame.setPlatform("java");
-        final String module = extractJavaModuleName(frame.functionName);
-        stackFrame.setFunction(extractJavaFunctionName(frame.functionName));
+        final String normalizedFunctionName = normalizeFunctionName(frame.functionName);
+        final String module = extractJavaModuleName(normalizedFunctionName);
+        stackFrame.setFunction(extractJavaFunctionName(normalizedFunctionName));
         stackFrame.setModule(module);
 
         // For Java frames, check in-app against the module (package name), which is what
@@ -217,21 +218,19 @@
     return normalized;
   }
 
-  private static @Nullable String extractJavaModuleName(String fqFunctionName) {
-    final String normalized = normalizeFunctionName(fqFunctionName);
-    if (normalized.contains(".")) {
-      return normalized.substring(0, normalized.lastIndexOf("."));
+  private static @Nullable String extractJavaModuleName(String normalizedFunctionName) {
+    if (normalizedFunctionName.contains(".")) {
+      return normalizedFunctionName.substring(0, normalizedFunctionName.lastIndexOf("."));
     } else {
       return null;
     }
   }
 
-  private static @Nullable String extractJavaFunctionName(String fqFunctionName) {
-    final String normalized = normalizeFunctionName(fqFunctionName);
-    if (normalized.contains(".")) {
-      return normalized.substring(normalized.lastIndexOf(".") + 1);
+  private static @Nullable String extractJavaFunctionName(String normalizedFunctionName) {
+    if (normalizedFunctionName.contains(".")) {
+      return normalizedFunctionName.substring(normalizedFunctionName.lastIndexOf(".") + 1);
     } else {
-      return normalized;
+      return normalizedFunctionName;
     }
   }

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

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.

3 participants