Skip to content

Commit b497381

Browse files
committed
make PreviousSessionFinalizer aware of tombstone handled sessions and add crashedLastRun
1 parent 2f35586 commit b497381

File tree

3 files changed

+89
-13
lines changed

3 files changed

+89
-13
lines changed

sentry/src/main/java/io/sentry/PreviousSessionFinalizer.java

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,16 +86,30 @@ public void run() {
8686
"Stream from path %s resulted in a null envelope.",
8787
previousSessionFile.getAbsolutePath());
8888
} else {
89-
Date timestamp = null;
9089
final File crashMarkerFile =
9190
new File(options.getCacheDirPath(), NATIVE_CRASH_MARKER_FILE);
92-
if (crashMarkerFile.exists()) {
91+
92+
if (session.getStatus() == Session.State.Crashed) {
93+
// this means the session was already updated from tombstone data and there is no need
94+
// to consider the crash-marker file from the Native SDK. However, we can use this to
95+
// set crashedLastRun.
96+
SentryCrashLastRunState.getInstance().setCrashedLastRun(true);
97+
} else if (crashMarkerFile.exists()) {
98+
// this means that the Native SDK is solely responsible for Native crashes and we must
99+
// update the session state and timestamp.
93100
options
94101
.getLogger()
95102
.log(INFO, "Crash marker file exists, last Session is gonna be Crashed.");
96103

97-
timestamp = getTimestampFromCrashMarkerFile(crashMarkerFile);
104+
@Nullable Date timestamp = getTimestampFromCrashMarkerFile(crashMarkerFile);
105+
session.update(Session.State.Crashed, null, true);
106+
session.end(timestamp);
107+
}
98108

109+
// Independent of whether the TombstoneIntegration is active or the Native SDK operates
110+
// alone, we must finalize the Native SDK crash marker life-cycle, otherwise it would
111+
// report crashes forever.
112+
if (crashMarkerFile.exists()) {
99113
if (!crashMarkerFile.delete()) {
100114
options
101115
.getLogger()
@@ -104,12 +118,6 @@ public void run() {
104118
"Failed to delete the crash marker file. %s.",
105119
crashMarkerFile.getAbsolutePath());
106120
}
107-
session.update(Session.State.Crashed, null, true);
108-
}
109-
// if the session has abnormal mechanism, we do not overwrite its end timestamp, because
110-
// it's already set
111-
if (session.getAbnormalMechanism() == null) {
112-
session.end(timestamp);
113121
}
114122

115123
// if the App. has been upgraded and there's a new version of the SDK running,

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,6 @@ private boolean storeInternal(final @NotNull SentryEnvelope envelope, final @Not
131131

132132
boolean crashedLastRun = false;
133133
final File crashMarkerFile = new File(options.getCacheDirPath(), NATIVE_CRASH_MARKER_FILE);
134-
// TODO: this should probably check whether the Native SDK integration is currently enabled or
135-
// remove the marker file if it isn't. Otherwise, application that disable the Native SDK,
136-
// will report a crash for the last run forever.
137134
if (crashMarkerFile.exists()) {
138135
crashedLastRun = true;
139136
}
@@ -158,7 +155,6 @@ private boolean storeInternal(final @NotNull SentryEnvelope envelope, final @Not
158155
}
159156
}
160157

161-
// TODO: maybe set crashLastRun for tombstone too?
162158
SentryCrashLastRunState.getInstance().setCrashedLastRun(crashedLastRun);
163159

164160
flushPreviousSession();

sentry/src/test/java/io/sentry/PreviousSessionFinalizerTest.kt

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ import io.sentry.Session.State.Crashed
44
import io.sentry.cache.EnvelopeCache
55
import java.io.File
66
import java.util.Date
7+
import kotlin.test.AfterTest
8+
import kotlin.test.BeforeTest
79
import kotlin.test.assertFalse
10+
import kotlin.test.assertNull
11+
import kotlin.test.assertTrue
812
import org.junit.Rule
913
import org.junit.Test
1014
import org.junit.rules.TemporaryFolder
@@ -17,6 +21,16 @@ import org.mockito.kotlin.verify
1721
class PreviousSessionFinalizerTest {
1822
@get:Rule val tmpDir = TemporaryFolder()
1923

24+
@BeforeTest
25+
fun `set up`() {
26+
SentryCrashLastRunState.getInstance().reset()
27+
}
28+
29+
@AfterTest
30+
fun `tear down`() {
31+
SentryCrashLastRunState.getInstance().reset()
32+
}
33+
2034
class Fixture {
2135
val options = SentryOptions()
2236
val scopes = mock<IScopes>()
@@ -205,4 +219,62 @@ class PreviousSessionFinalizerTest {
205219
)
206220
verify(fixture.scopes, never()).captureEnvelope(any())
207221
}
222+
223+
@Test
224+
fun `when previous session is already crashed, sets crashedLastRun to true`() {
225+
// Create a session that is already in Crashed state (simulating tombstone integration)
226+
val crashedSession =
227+
Session(null, null, null, "io.sentry.sample@1.0").apply { update(Crashed, null, true) }
228+
229+
val finalizer = fixture.getSut(tmpDir, session = crashedSession)
230+
231+
// crashedLastRun should not be set before running the finalizer
232+
assertNull(SentryCrashLastRunState.getInstance().isCrashedLastRun(null, false))
233+
234+
finalizer.run()
235+
236+
// crashedLastRun should be set to true after running the finalizer
237+
assertTrue(SentryCrashLastRunState.getInstance().isCrashedLastRun(null, false)!!)
238+
}
239+
240+
@Test
241+
fun `when native crash marker exists but session is not crashed, does not set crashedLastRun`() {
242+
// Session is not crashed, but native crash marker exists
243+
val finalizer =
244+
fixture.getSut(
245+
tmpDir,
246+
session = Session(null, null, null, "io.sentry.sample@1.0"),
247+
nativeCrashTimestamp = Date(2023, 10, 1),
248+
)
249+
250+
// crashedLastRun should not be set before running the finalizer
251+
assertNull(SentryCrashLastRunState.getInstance().isCrashedLastRun(null, false))
252+
253+
finalizer.run()
254+
255+
// crashedLastRun should NOT be set by PreviousSessionFinalizer for native crash marker case
256+
// (it's handled by EnvelopeCache at session start instead)
257+
assertNull(SentryCrashLastRunState.getInstance().isCrashedLastRun(null, false))
258+
}
259+
260+
@Test
261+
fun `when previous session is already crashed and native crash marker exists, sets crashedLastRun and deletes marker`() {
262+
// Session is already crashed (tombstone case) AND native crash marker exists
263+
val crashedSession =
264+
Session(null, null, null, "io.sentry.sample@1.0").apply { update(Crashed, null, true) }
265+
266+
val finalizer =
267+
fixture.getSut(tmpDir, session = crashedSession, nativeCrashTimestamp = Date(2023, 10, 1))
268+
269+
val nativeCrashMarker =
270+
File(fixture.options.cacheDirPath!!, EnvelopeCache.NATIVE_CRASH_MARKER_FILE)
271+
assertTrue(nativeCrashMarker.exists())
272+
273+
finalizer.run()
274+
275+
// crashedLastRun should be set to true
276+
assertTrue(SentryCrashLastRunState.getInstance().isCrashedLastRun(null, false)!!)
277+
// Native crash marker should be deleted
278+
assertFalse(nativeCrashMarker.exists())
279+
}
208280
}

0 commit comments

Comments
 (0)