Skip to content

Commit 1d66db7

Browse files
committed
fix(android): Keep a newer NDK tombstone marker
Crash service workers for processes that crash close together can finish out of order. A worker that wrote an older tombstone timestamp let `TombstoneIntegration` report the newer crash again on the next launch. Write the marker only when it is newer than the current one, and serialize the workers.
1 parent c779feb commit 1d66db7

2 files changed

Lines changed: 28 additions & 2 deletions

File tree

‎sentry-android-ndk-outproc/src/main/java/io/sentry/android/ndk/SentryNdkCrashService.java‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import io.sentry.android.core.cache.AndroidEnvelopeCache;
2929
import io.sentry.android.core.internal.tombstone.TombstoneParser;
3030
import io.sentry.ndk.SentryNdk.CrashDaemon;
31+
import io.sentry.util.FileUtils;
3132
import java.io.BufferedInputStream;
3233
import java.io.Closeable;
3334
import java.io.File;
@@ -625,15 +626,24 @@ ApplicationExitInfo findMatchingExitInfo(
625626
return mergedFile;
626627
}
627628

628-
private void writeLastTombstoneMarker(final @NotNull File envelopeFile, final long timestamp)
629-
throws IOException {
629+
// workers for concurrent crashes must not move the marker back
630+
private static synchronized void writeLastTombstoneMarker(
631+
final @NotNull File envelopeFile, final long timestamp) throws IOException {
630632
final @Nullable File runDir = envelopeFile.getParentFile();
631633
final @Nullable File nativeDir = runDir != null ? runDir.getParentFile() : null;
632634
final @Nullable File cacheDir = nativeDir != null ? nativeDir.getParentFile() : null;
633635
if (cacheDir == null) {
634636
return;
635637
}
636638
final File marker = new File(cacheDir, AndroidEnvelopeCache.LAST_TOMBSTONE_REPORT);
639+
final @Nullable String lastTimestamp = FileUtils.readText(marker);
640+
try {
641+
if (lastTimestamp != null && Long.parseLong(lastTimestamp.trim()) >= timestamp) {
642+
return;
643+
}
644+
} catch (NumberFormatException ignored) {
645+
// overwrite an invalid marker
646+
}
637647
try (final OutputStream outputStream = new FileOutputStream(marker)) {
638648
outputStream.write(String.valueOf(timestamp).getBytes("UTF-8"));
639649
}

‎sentry-android-ndk-outproc/src/test/java/io/sentry/android/ndk/SentryNdkCrashServiceTest.kt‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,22 @@ class SentryNdkCrashServiceTest {
194194
assertThat(marker().readText()).isEqualTo("2000")
195195
}
196196

197+
@Test
198+
@Config(instrumentedPackages = ["io.sentry.ndk"], shadows = [CrashDaemonShadow::class])
199+
fun `keeps a newer marker`() {
200+
val service = Robolectric.setupService(SentryNdkCrashService::class.java)
201+
val envelope = writeNativeEnvelope(timestamp = 1000)
202+
addExitInfo(service, pid = 1, timestamp = 2000, trace = tombstone())
203+
crashDaemon(envelope, CrashDaemon.SendResult.SENT)
204+
marker().writeText("3000")
205+
206+
configureDaemon(service, pid = 1, fd = 10, tombstoneEnabled = true)
207+
awaitWorker()
208+
209+
assertThat(CrashDaemonShadow.sends).containsExactly(listOf("${envelope.path}.merged", 2000L))
210+
assertThat(marker().readText()).isEqualTo("3000")
211+
}
212+
197213
@Test
198214
@Config(instrumentedPackages = ["io.sentry.ndk"], shadows = [CrashDaemonShadow::class])
199215
fun `writes no marker when the send fails`() {

0 commit comments

Comments
 (0)