Skip to content

Commit

Permalink
Fix issue title on Android when a wrapped RuntimeException is thrown …
Browse files Browse the repository at this point in the history
…by the system (#3212)

* Reverse order for Android RuntimeExceptions

* Update changelog

* Address PR feedback
  • Loading branch information
markushi committed Feb 20, 2024
1 parent d007225 commit 154440a
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

- Don't wait on main thread when SDK restarts ([#3200](https://github.com/getsentry/sentry-java/pull/3200))
- Fix Jetpack Compose widgets are not being correctly identified for user interaction tracing ([#3209](https://github.com/getsentry/sentry-java/pull/3209))
- Fix issue title on Android when a wrapping `RuntimeException` is thrown by the system ([#3212](https://github.com/getsentry/sentry-java/pull/3212))
- This will change grouping of the issues that were previously titled `RuntimeInit$MethodAndArgsCaller` to have them split up properly by the original root cause exception

## 7.3.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,16 @@
import io.sentry.android.core.performance.TimeSpan;
import io.sentry.protocol.App;
import io.sentry.protocol.OperatingSystem;
import io.sentry.protocol.SentryException;
import io.sentry.protocol.SentryStackFrame;
import io.sentry.protocol.SentryStackTrace;
import io.sentry.protocol.SentryThread;
import io.sentry.protocol.SentryTransaction;
import io.sentry.protocol.User;
import io.sentry.util.HintUtils;
import io.sentry.util.Objects;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ExecutorService;
Expand Down Expand Up @@ -68,9 +73,51 @@ public DefaultAndroidEventProcessor(

setCommons(event, true, applyScopeData);

fixExceptionOrder(event);

return event;
}

/**
* The last exception is usually used for picking the issue title, but the convention is to send
* inner exceptions first, e.g. [inner, outer] This doesn't work very well on Android, as some
* hooks like Application.onCreate is wrapped by Android framework with a RuntimeException. Thus,
* if the last exception is a RuntimeInit$MethodAndArgsCaller, reverse the order to get a better
* issue title. This is a quick fix, for more details see: <a
* href="https://github.com/getsentry/sentry/issues/64074">#64074</a> <a
* href="https://github.com/getsentry/sentry/issues/59679">#59679</a> <a
* href="https://github.com/getsentry/sentry/issues/64088">#64088</a>
*
* @param event the event to process
*/
private static void fixExceptionOrder(final @NotNull SentryEvent event) {
boolean reverseExceptions = false;

final @Nullable List<SentryException> exceptions = event.getExceptions();
if (exceptions != null && exceptions.size() > 1) {
final @NotNull SentryException lastException = exceptions.get(exceptions.size() - 1);
if ("java.lang".equals(lastException.getModule())) {
final @Nullable SentryStackTrace stacktrace = lastException.getStacktrace();
if (stacktrace != null) {
final @Nullable List<SentryStackFrame> frames = stacktrace.getFrames();
if (frames != null) {
for (final @NotNull SentryStackFrame frame : frames) {
if ("com.android.internal.os.RuntimeInit$MethodAndArgsCaller"
.equals(frame.getModule())) {
reverseExceptions = true;
break;
}
}
}
}
}
}

if (reverseExceptions) {
Collections.reverse(exceptions);
}
}

private void setCommons(
final @NotNull SentryBaseEvent event,
final boolean errorEvent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ import io.sentry.TypeCheckHint.SENTRY_DART_SDK_NAME
import io.sentry.android.core.internal.util.CpuInfoUtils
import io.sentry.protocol.OperatingSystem
import io.sentry.protocol.SdkVersion
import io.sentry.protocol.SentryException
import io.sentry.protocol.SentryStackFrame
import io.sentry.protocol.SentryStackTrace
import io.sentry.protocol.SentryThread
import io.sentry.protocol.SentryTransaction
import io.sentry.protocol.User
Expand Down Expand Up @@ -573,4 +576,87 @@ class DefaultAndroidEventProcessorTest {
assertNull(thread.isMain)
}
}

@Test
fun `the exception list is reversed in case there's an RuntimeException`() {
val sut = fixture.getSut(context)
val event = SentryEvent().apply {
exceptions = listOf(
SentryException().apply {
type = "IllegalStateException"
module = "com.example"
stacktrace = SentryStackTrace(
listOf(
SentryStackFrame().apply {
function = "onCreate"
module = "com.example.Application"
filename = "Application.java"
}
)
)
},
SentryException().apply {
type = "RuntimeException"
value = "Unable to create application com.example.Application: java.lang.IllegalStateException"
module = "java.lang"
stacktrace = SentryStackTrace(
listOf(
SentryStackFrame().apply {
function = "run"
module = "com.android.internal.os.RuntimeInit\$MethodAndArgsCaller"
filename = "RuntimeInit.java"
}
)
)
}
)
}
val processedEvent = sut.process(event, Hint())
assertNotNull(processedEvent) {
assertEquals(2, it.exceptions!!.size)
assertEquals("RuntimeException", it.exceptions!![0].type)
assertEquals("IllegalStateException", it.exceptions!![1].type)
}
}

@Test
fun `the exception list is kept as-is in case there's no RuntimeException`() {
val sut = fixture.getSut(context)
val event = SentryEvent().apply {
exceptions = listOf(
SentryException().apply {
type = "IllegalStateException"
module = "com.example"
stacktrace = SentryStackTrace(
listOf(
SentryStackFrame().apply {
function = "onCreate"
module = "com.example.Application"
filename = "Application.java"
}
)
)
},
SentryException().apply {
type = "IllegalArgumentException"
module = "com.example"
stacktrace = SentryStackTrace(
listOf(
SentryStackFrame().apply {
function = "onCreate"
module = "com.example.Application"
filename = "Application.java"
}
)
)
}
)
}
val processedEvent = sut.process(event, Hint())
assertNotNull(processedEvent) {
assertEquals(2, it.exceptions!!.size)
assertEquals("IllegalStateException", it.exceptions!![0].type)
assertEquals("IllegalArgumentException", it.exceptions!![1].type)
}
}
}

0 comments on commit 154440a

Please sign in to comment.