Skip to content

Commit f56490b

Browse files
feat: minimal tombstone integration (#4933)
* feat: minimal tombstone integration (disabled by default, options internal) * redo a seemingly missed spotlessApply * use non-plural option + mark integration also as internal + update API * init size exceptions since we know we only need one Co-authored-by: Markus Hintersteiner <[email protected]> * move `instructionAddressAdjustment` to `SentryStackTrace` * add copyright notice to tombstone.proto * add historical tombstone option * looks like we need to add a TombstoneEventProcessor anyway. * ...so let's add it to the options if the SDK supports it (adapt to >= S, since that is the earliest version where `REASON_CRASH_NATIVE` provides tombstones via the `traceInputStream`) * Adapt `AndroidEnvelopeCache` to also write Tombstone timestamp markers * Integrate handling of historical Tombstone option + last tombstone marker This currently does not work: While we now can optionally enable reporting of "historical" tombstones, by making the `TombstoneHint` `Backfillable` it will automatically be enriched by the `ANRv2EventProcessor` which is currently the only `BackfillingEventProcessor`. The `ANRv2EventProcessor` is partially written in way that is potentially generic for events with `Backfillable` hints, but other parts are enriching as if those are always were ANRs, which up to now was true, but with Tombstones that assumption now breaks. Next Steps: * There is considerable duplication between the ANRv2Integration and TombstoneIntegration * sprinkle with TODOs to highlight next PR steps * fix typo * implement TombstoneParser as Closable and close the tombstone stream * tighten code with final and null annotations. * eliminate obsolete null check * Deduplicate ApplicationExitInfo handling for corresponding Integrations via HistoryDispatcher * also update api dumps and formatting for sentry changes * update tombstone message construction * fix abortMessage check * convert AnrV2EventProcessor to a more generic ApplicationExitInfoEventProcessor this handles all events with Backfillable hint, but adds an interface HintEnricher, to allow hint-specific enrichment (like for ANRs) before and after the generic backfilling happened. * reintroduce, update and correct old inline docs where they make sense. * remove obsolete TombstoneEventProcessor * clean up tombstone error handling * convert ApplicationExitInfoHistoryDispatcher.removeLatest() to use an iterator instead of for-each * add TombstoneIntegrationTest * replace assertions with explicit RuntimeException * use test base class for AEI * remove obsolete TODO RE: mechanism change * clarify open TODO RE: AbnormalExit discriminator for ANR hints * refactor instructionAddressAdjustment from String to enum * deduplicate hex formatting of addresses and TombstoneParser * adapt tryEndPreviousSession() in EnvelopeCache to NativeCrashExit hint * move isBackgroundAnr() to the AnrHintEnricher since it is only used there. * revert method-references back to lambdas * clean up exception assignment in TombstoneParser * properly format the debug-id as an OLE GUID * remove obsolete equality between code-id and debug-id * ensure that errors in the char->digit conversion is also handled * fix debugId/codeId look-up and checks in TombstoneIntegrationTest * make movePreviousSession idempotent wrt the previous session * reuse existing codeId -> debugId conversion instead of adding a new converter * make PreviousSessionFinalizer aware of tombstone handled sessions and add crashedLastRun * bump SDK config for TombstoneIntegrationTest to version 31 (S) * add changelog entry * retrieve test tombstone.pb fixture as a resource rather than a file path. * attribute pb to be binary and re-commit the non-corrupted tombstone.pb * reset crashedLastRun in PreviousSessionFinalizer and add test-case * format * fix Ok Session termination test * format * clarify comments and variable names in PreviousSessionFinalizer * only enable distribution and sizeAnalysis if the SENTRY_AUTH_TOKEN environment variable is present and `isNotBlank()`` * compress tombstone test resource to reduce its repo weight down to less than 20% * add round-trip serialization test for `instructionAddressAdjustment` --------- Co-authored-by: Markus Hintersteiner <[email protected]>
1 parent 2387c2c commit f56490b

35 files changed

+2575
-820
lines changed

.gitattributes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
* text eol=lf
22
*.png binary
33
*.jpg binary
4+
*.pb binary
5+
*.gz binary
46

57
# These are explicitly windows files and should use crlf
68
*.bat text eol=crlf

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@
1515
- Discard envelopes on `4xx` and `5xx` response ([#4950](https://github.com/getsentry/sentry-java/pull/4950))
1616
- This aims to not overwhelm Sentry after an outage or load shedding (including HTTP 429) where too many events are sent at once
1717

18+
### Feature
19+
20+
- Add a Tombstone integration that detects native crashes without relying on the NDK integration, but instead using `ApplicationExitInfo.REASON_CRASH_NATIVE` on Android 12+. ([#4933](https://github.com/getsentry/sentry-java/pull/4933))
21+
- Currently exposed via options as an _internal_ API only.
22+
- If enabled alongside the NDK integration, crashes will be reported as two separate events. Users should enable only one; deduplication between both integrations will be added in a future release.
23+
1824
## 8.29.0
1925

2026
### Fixes

gradle/libs.versions.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ spotless = "7.0.4"
4141
gummyBears = "0.12.0"
4242
camerax = "1.3.0"
4343
openfeature = "1.18.2"
44+
protobuf = "4.33.1"
4445

4546
[plugins]
4647
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
@@ -60,6 +61,7 @@ spotless = { id = "com.diffplug.spotless", version.ref = "spotless" }
6061
detekt = { id = "io.gitlab.arturbosch.detekt", version = "1.23.8" }
6162
jacoco-android = { id = "com.mxalbert.gradle.jacoco-android", version = "0.2.0" }
6263
kover = { id = "org.jetbrains.kotlinx.kover", version = "0.7.3" }
64+
protobuf = { id = "com.google.protobuf", version = "0.9.5" }
6365
vanniktech-maven-publish = { id = "com.vanniktech.maven.publish", version = "0.30.0" }
6466
springboot2 = { id = "org.springframework.boot", version.ref = "springboot2" }
6567
springboot3 = { id = "org.springframework.boot", version.ref = "springboot3" }
@@ -139,6 +141,8 @@ otel-javaagent-extension-api = { module = "io.opentelemetry.javaagent:openteleme
139141
otel-semconv = { module = "io.opentelemetry.semconv:opentelemetry-semconv", version.ref = "otelSemanticConventions" }
140142
otel-semconv-incubating = { module = "io.opentelemetry.semconv:opentelemetry-semconv-incubating", version.ref = "otelSemanticConventionsAlpha" }
141143
p6spy = { module = "p6spy:p6spy", version = "3.9.1" }
144+
protobuf-javalite = { module = "com.google.protobuf:protobuf-javalite", version.ref = "protobuf"}
145+
protoc = { module = "com.google.protobuf:protoc", version.ref = "protobuf" }
142146
quartz = { module = "org.quartz-scheduler:quartz", version = "2.3.0" }
143147
reactor-core = { module = "io.projectreactor:reactor-core", version = "3.5.3" }
144148
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }

sentry-android-core/api/sentry-android-core.api

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -141,13 +141,6 @@ public final class io/sentry/android/core/AnrIntegrationFactory {
141141
public static fun create (Landroid/content/Context;Lio/sentry/android/core/BuildInfoProvider;)Lio/sentry/Integration;
142142
}
143143

144-
public final class io/sentry/android/core/AnrV2EventProcessor : io/sentry/BackfillingEventProcessor {
145-
public fun <init> (Landroid/content/Context;Lio/sentry/android/core/SentryAndroidOptions;Lio/sentry/android/core/BuildInfoProvider;)V
146-
public fun getOrder ()Ljava/lang/Long;
147-
public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent;
148-
public fun process (Lio/sentry/protocol/SentryTransaction;Lio/sentry/Hint;)Lio/sentry/protocol/SentryTransaction;
149-
}
150-
151144
public class io/sentry/android/core/AnrV2Integration : io/sentry/Integration, java/io/Closeable {
152145
public fun <init> (Landroid/content/Context;)V
153146
public fun close ()V
@@ -203,6 +196,13 @@ public final class io/sentry/android/core/AppState$LifecycleObserver : androidx/
203196
public fun onStop (Landroidx/lifecycle/LifecycleOwner;)V
204197
}
205198

199+
public final class io/sentry/android/core/ApplicationExitInfoEventProcessor : io/sentry/BackfillingEventProcessor {
200+
public fun <init> (Landroid/content/Context;Lio/sentry/android/core/SentryAndroidOptions;Lio/sentry/android/core/BuildInfoProvider;)V
201+
public fun getOrder ()Ljava/lang/Long;
202+
public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent;
203+
public fun process (Lio/sentry/protocol/SentryTransaction;Lio/sentry/Hint;)Lio/sentry/protocol/SentryTransaction;
204+
}
205+
206206
public final class io/sentry/android/core/BuildConfig {
207207
public static final field BUILD_TYPE Ljava/lang/String;
208208
public static final field DEBUG Z
@@ -352,6 +352,8 @@ public final class io/sentry/android/core/SentryAndroidOptions : io/sentry/Sentr
352352
public fun isEnableSystemEventBreadcrumbs ()Z
353353
public fun isEnableSystemEventBreadcrumbsExtras ()Z
354354
public fun isReportHistoricalAnrs ()Z
355+
public fun isReportHistoricalTombstones ()Z
356+
public fun isTombstoneEnabled ()Z
355357
public fun setAnrEnabled (Z)V
356358
public fun setAnrReportInDebug (Z)V
357359
public fun setAnrTimeoutIntervalMillis (J)V
@@ -381,6 +383,8 @@ public final class io/sentry/android/core/SentryAndroidOptions : io/sentry/Sentr
381383
public fun setNativeHandlerStrategy (Lio/sentry/android/core/NdkHandlerStrategy;)V
382384
public fun setNativeSdkName (Ljava/lang/String;)V
383385
public fun setReportHistoricalAnrs (Z)V
386+
public fun setReportHistoricalTombstones (Z)V
387+
public fun setTombstoneEnabled (Z)V
384388
}
385389

386390
public abstract interface class io/sentry/android/core/SentryAndroidOptions$BeforeCaptureCallback {
@@ -469,6 +473,29 @@ public final class io/sentry/android/core/SystemEventsBreadcrumbsIntegration : i
469473
public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V
470474
}
471475

476+
public class io/sentry/android/core/TombstoneIntegration : io/sentry/Integration, java/io/Closeable {
477+
public fun <init> (Landroid/content/Context;)V
478+
public fun close ()V
479+
public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V
480+
}
481+
482+
public final class io/sentry/android/core/TombstoneIntegration$TombstoneHint : io/sentry/hints/BlockingFlushHint, io/sentry/hints/Backfillable, io/sentry/hints/NativeCrashExit {
483+
public fun <init> (JLio/sentry/ILogger;JZ)V
484+
public fun isFlushable (Lio/sentry/protocol/SentryId;)Z
485+
public fun setFlushable (Lio/sentry/protocol/SentryId;)V
486+
public fun shouldEnrich ()Z
487+
public fun timestamp ()Ljava/lang/Long;
488+
}
489+
490+
public class io/sentry/android/core/TombstoneIntegration$TombstonePolicy : io/sentry/android/core/ApplicationExitInfoHistoryDispatcher$ApplicationExitInfoPolicy {
491+
public fun <init> (Lio/sentry/android/core/SentryAndroidOptions;)V
492+
public fun buildReport (Landroid/app/ApplicationExitInfo;Z)Lio/sentry/android/core/ApplicationExitInfoHistoryDispatcher$Report;
493+
public fun getLabel ()Ljava/lang/String;
494+
public fun getLastReportedTimestamp ()Ljava/lang/Long;
495+
public fun getTargetReason ()I
496+
public fun shouldReportHistorical ()Z
497+
}
498+
472499
public final class io/sentry/android/core/UserInteractionIntegration : android/app/Application$ActivityLifecycleCallbacks, io/sentry/Integration, java/io/Closeable {
473500
public fun <init> (Landroid/app/Application;Lio/sentry/util/LoadClass;)V
474501
public fun close ()V
@@ -495,11 +522,15 @@ public final class io/sentry/android/core/ViewHierarchyEventProcessor : io/sentr
495522
}
496523

497524
public final class io/sentry/android/core/cache/AndroidEnvelopeCache : io/sentry/cache/EnvelopeCache {
525+
public static final field LAST_ANR_MARKER_LABEL Ljava/lang/String;
498526
public static final field LAST_ANR_REPORT Ljava/lang/String;
527+
public static final field LAST_TOMBSTONE_MARKER_LABEL Ljava/lang/String;
528+
public static final field LAST_TOMBSTONE_REPORT Ljava/lang/String;
499529
public fun <init> (Lio/sentry/android/core/SentryAndroidOptions;)V
500530
public fun getDirectory ()Ljava/io/File;
501531
public static fun hasStartupCrashMarker (Lio/sentry/SentryOptions;)Z
502532
public static fun lastReportedAnr (Lio/sentry/SentryOptions;)Ljava/lang/Long;
533+
public static fun lastReportedTombstone (Lio/sentry/SentryOptions;)Ljava/lang/Long;
503534
public fun store (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)V
504535
public fun storeEnvelope (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)Z
505536
}

sentry-android-core/build.gradle.kts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ plugins {
88
alias(libs.plugins.jacoco.android)
99
alias(libs.plugins.errorprone)
1010
alias(libs.plugins.gradle.versions)
11+
alias(libs.plugins.protobuf)
1112
}
1213

1314
android {
@@ -83,6 +84,7 @@ dependencies {
8384
implementation(libs.androidx.lifecycle.common.java8)
8485
implementation(libs.androidx.lifecycle.process)
8586
implementation(libs.androidx.core)
87+
implementation(libs.protobuf.javalite)
8688

8789
errorprone(libs.errorprone.core)
8890
errorprone(libs.nopen.checker)
@@ -109,3 +111,10 @@ dependencies {
109111
testRuntimeOnly(libs.androidx.fragment.ktx)
110112
testRuntimeOnly(libs.timber)
111113
}
114+
115+
protobuf {
116+
protoc { artifact = libs.protoc.get().toString() }
117+
generateProtoTasks {
118+
all().forEach { task -> task.builtins { create("java") { option("lite") } } }
119+
}
120+
}

sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import android.app.Application;
66
import android.content.Context;
77
import android.content.pm.PackageInfo;
8+
import android.os.Build;
89
import io.sentry.CompositePerformanceCollector;
910
import io.sentry.DeduplicateMultithreadedEventProcessor;
1011
import io.sentry.DefaultCompositePerformanceCollector;
@@ -188,7 +189,8 @@ static void initializeIntegrationsAndProcessors(
188189
options.addEventProcessor(new PerformanceAndroidEventProcessor(options, activityFramesTracker));
189190
options.addEventProcessor(new ScreenshotEventProcessor(options, buildInfoProvider));
190191
options.addEventProcessor(new ViewHierarchyEventProcessor(options));
191-
options.addEventProcessor(new AnrV2EventProcessor(context, options, buildInfoProvider));
192+
options.addEventProcessor(
193+
new ApplicationExitInfoEventProcessor(context, options, buildInfoProvider));
192194
if (options.getTransportGate() instanceof NoOpTransportGate) {
193195
options.setTransportGate(new AndroidTransportGate(options));
194196
}
@@ -373,6 +375,10 @@ static void installDefaultIntegrations(
373375
final Class<?> sentryNdkClass = loadClass.loadClass(SENTRY_NDK_CLASS_NAME, options.getLogger());
374376
options.addIntegration(new NdkIntegration(sentryNdkClass));
375377

378+
if (buildInfoProvider.getSdkInfoVersion() >= Build.VERSION_CODES.S) {
379+
options.addIntegration(new TombstoneIntegration(context));
380+
}
381+
376382
// this integration uses android.os.FileObserver, we can't move to sentry
377383
// before creating a pure java impl.
378384
options.addIntegration(EnvelopeFileObserverIntegration.getOutboxFileObserver());

0 commit comments

Comments
 (0)