Skip to content

Commit 37e43bb

Browse files
committed
adapt tryEndPreviousSession() in EnvelopeCache to NativeCrashExit hint
1 parent a2ac06e commit 37e43bb

File tree

2 files changed

+85
-20
lines changed

2 files changed

+85
-20
lines changed

sentry/src/main/java/io/sentry/cache/EnvelopeCache.java

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import io.sentry.Session;
2121
import io.sentry.UncaughtExceptionHandlerIntegration;
2222
import io.sentry.hints.AbnormalExit;
23+
import io.sentry.hints.NativeCrashExit;
2324
import io.sentry.hints.SessionEnd;
2425
import io.sentry.hints.SessionStart;
2526
import io.sentry.transport.NoOpEnvelopeCache;
@@ -119,10 +120,10 @@ private boolean storeInternal(final @NotNull SentryEnvelope envelope, final @Not
119120
}
120121
}
121122

122-
if (HintUtils.hasType(hint, AbnormalExit.class)) {
123+
if (HintUtils.hasType(hint, AbnormalExit.class)
124+
|| HintUtils.hasType(hint, NativeCrashExit.class)) {
123125
tryEndPreviousSession(hint);
124126
}
125-
// TODO: adapt tryEndPreviousSession(hint); to CrashExit.class
126127

127128
if (HintUtils.hasType(hint, SessionStart.class)) {
128129
movePreviousSession(currentSessionFile, previousSessionFile);
@@ -202,20 +203,20 @@ private boolean storeInternal(final @NotNull SentryEnvelope envelope, final @Not
202203
@SuppressWarnings("JavaUtilDate")
203204
private void tryEndPreviousSession(final @NotNull Hint hint) {
204205
final Object sdkHint = HintUtils.getSentrySdkHint(hint);
205-
if (sdkHint instanceof AbnormalExit) {
206-
final File previousSessionFile = getPreviousSessionFile(directory.getAbsolutePath());
206+
final File previousSessionFile = getPreviousSessionFile(directory.getAbsolutePath());
207207

208-
if (previousSessionFile.exists()) {
209-
options.getLogger().log(WARNING, "Previous session is not ended, we'd need to end it.");
208+
if (previousSessionFile.exists()) {
209+
options.getLogger().log(WARNING, "Previous session is not ended, we'd need to end it.");
210210

211-
try (final Reader reader =
212-
new BufferedReader(
213-
new InputStreamReader(new FileInputStream(previousSessionFile), UTF_8))) {
214-
final Session session = serializer.getValue().deserialize(reader, Session.class);
215-
if (session != null) {
211+
try (final Reader reader =
212+
new BufferedReader(
213+
new InputStreamReader(new FileInputStream(previousSessionFile), UTF_8))) {
214+
final Session session = serializer.getValue().deserialize(reader, Session.class);
215+
if (session != null) {
216+
Date timestamp = null;
217+
if (sdkHint instanceof AbnormalExit) {
216218
final AbnormalExit abnormalHint = (AbnormalExit) sdkHint;
217219
final @Nullable Long abnormalExitTimestamp = abnormalHint.timestamp();
218-
Date timestamp = null;
219220

220221
if (abnormalExitTimestamp != null) {
221222
timestamp = DateUtils.getDateTime(abnormalExitTimestamp);
@@ -233,17 +234,33 @@ private void tryEndPreviousSession(final @NotNull Hint hint) {
233234

234235
final String abnormalMechanism = abnormalHint.mechanism();
235236
session.update(Session.State.Abnormal, null, true, abnormalMechanism);
236-
// we have to use the actual timestamp of the Abnormal Exit here to mark the session
237-
// as finished at the time it happened
238-
session.end(timestamp);
239-
writeSessionToDisk(previousSessionFile, session);
237+
} else if (sdkHint instanceof NativeCrashExit) {
238+
final NativeCrashExit nativeCrashHint = (NativeCrashExit) sdkHint;
239+
final @NotNull Long nativeCrashExitTimestamp = nativeCrashHint.timestamp();
240+
241+
timestamp = DateUtils.getDateTime(nativeCrashExitTimestamp);
242+
// sanity check if the native crash exit actually happened when the session was alive
243+
final Date sessionStart = session.getStarted();
244+
if (sessionStart == null || timestamp.before(sessionStart)) {
245+
options
246+
.getLogger()
247+
.log(
248+
WARNING,
249+
"Native crash exit happened before previous session start, not ending the session.");
250+
return;
251+
}
252+
session.update(Session.State.Crashed, null, true, null);
240253
}
241-
} catch (Throwable e) {
242-
options.getLogger().log(SentryLevel.ERROR, "Error processing previous session.", e);
254+
// we have to use the actual timestamp of the Abnormal or Crash Exit here to mark the
255+
// session as finished at the time it happened
256+
session.end(timestamp);
257+
writeSessionToDisk(previousSessionFile, session);
243258
}
244-
} else {
245-
options.getLogger().log(DEBUG, "No previous session file to end.");
259+
} catch (Throwable e) {
260+
options.getLogger().log(SentryLevel.ERROR, "Error processing previous session.", e);
246261
}
262+
} else {
263+
options.getLogger().log(DEBUG, "No previous session file to end.");
247264
}
248265
}
249266

sentry/src/test/java/io/sentry/cache/EnvelopeCacheTest.kt

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import io.sentry.UncaughtExceptionHandlerIntegration.UncaughtExceptionHint
1717
import io.sentry.cache.EnvelopeCache.PREFIX_CURRENT_SESSION_FILE
1818
import io.sentry.cache.EnvelopeCache.SUFFIX_SESSION_FILE
1919
import io.sentry.hints.AbnormalExit
20+
import io.sentry.hints.NativeCrashExit
2021
import io.sentry.hints.SessionEndHint
2122
import io.sentry.hints.SessionStartHint
2223
import io.sentry.protocol.SentryId
@@ -347,6 +348,53 @@ class EnvelopeCacheTest {
347348
assertEquals(null, updatedSession.abnormalMechanism)
348349
}
349350

351+
@Test
352+
fun `NativeCrashExit hint marks previous session as crashed with crash timestamp`() {
353+
val cache = fixture.getSUT()
354+
355+
val previousSessionFile = EnvelopeCache.getPreviousSessionFile(fixture.options.cacheDirPath!!)
356+
val session = createSession()
357+
fixture.options.serializer.serialize(session, previousSessionFile.bufferedWriter())
358+
359+
val nativeCrashTimestamp = session.started!!.time + TimeUnit.HOURS.toMillis(3)
360+
val envelope = SentryEnvelope.from(fixture.options.serializer, SentryEvent(), null)
361+
val nativeCrashHint = NativeCrashExit { nativeCrashTimestamp }
362+
val hints = HintUtils.createWithTypeCheckHint(nativeCrashHint)
363+
cache.storeEnvelope(envelope, hints)
364+
365+
val updatedSession =
366+
fixture.options.serializer.deserialize(
367+
previousSessionFile.bufferedReader(),
368+
Session::class.java,
369+
)
370+
assertEquals(State.Crashed, updatedSession!!.status)
371+
assertEquals(nativeCrashTimestamp, updatedSession.timestamp!!.time)
372+
}
373+
374+
@Test
375+
fun `when NativeCrashExit happened before previous session start, does not mark as crashed`() {
376+
val cache = fixture.getSUT()
377+
378+
val previousSessionFile = EnvelopeCache.getPreviousSessionFile(fixture.options.cacheDirPath!!)
379+
val session = createSession()
380+
val nativeCrashTimestamp = session.started!!.time - TimeUnit.HOURS.toMillis(3)
381+
fixture.options.serializer.serialize(session, previousSessionFile.bufferedWriter())
382+
383+
val envelope = SentryEnvelope.from(fixture.options.serializer, SentryEvent(), null)
384+
val nativeCrashHint = NativeCrashExit { nativeCrashTimestamp }
385+
val hints = HintUtils.createWithTypeCheckHint(nativeCrashHint)
386+
cache.storeEnvelope(envelope, hints)
387+
388+
val updatedSession =
389+
fixture.options.serializer.deserialize(
390+
previousSessionFile.bufferedReader(),
391+
Session::class.java,
392+
)
393+
assertEquals(Ok, updatedSession!!.status)
394+
assertTrue(nativeCrashTimestamp < updatedSession.started!!.time)
395+
assertTrue(nativeCrashTimestamp < updatedSession.timestamp!!.time)
396+
}
397+
350398
@Test
351399
fun `failing to store returns false`() {
352400
val serializer = mock<ISerializer>()

0 commit comments

Comments
 (0)