Skip to content

Commit 7db5895

Browse files
committed
Deduplicate ApplicationExitInfo handling for corresponding Integrations via HistoryDispatcher
* also update api dumps and formatting for sentry changes
1 parent 25f4089 commit 7db5895

File tree

11 files changed

+412
-351
lines changed

11 files changed

+412
-351
lines changed

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

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,7 @@ public final class io/sentry/android/core/SentryAndroidOptions : io/sentry/Sentr
339339
public fun isEnableSystemEventBreadcrumbs ()Z
340340
public fun isEnableSystemEventBreadcrumbsExtras ()Z
341341
public fun isReportHistoricalAnrs ()Z
342+
public fun isReportHistoricalTombstones ()Z
342343
public fun isTombstoneEnabled ()Z
343344
public fun setAnrEnabled (Z)V
344345
public fun setAnrReportInDebug (Z)V
@@ -368,6 +369,7 @@ public final class io/sentry/android/core/SentryAndroidOptions : io/sentry/Sentr
368369
public fun setNativeHandlerStrategy (Lio/sentry/android/core/NdkHandlerStrategy;)V
369370
public fun setNativeSdkName (Ljava/lang/String;)V
370371
public fun setReportHistoricalAnrs (Z)V
372+
public fun setReportHistoricalTombstones (Z)V
371373
public fun setTombstoneEnabled (Z)V
372374
}
373375

@@ -467,9 +469,21 @@ public class io/sentry/android/core/TombstoneIntegration : io/sentry/Integration
467469
public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V
468470
}
469471

470-
public class io/sentry/android/core/TombstoneIntegration$TombstoneProcessor : java/lang/Runnable {
471-
public fun <init> (Landroid/content/Context;Lio/sentry/IScopes;Lio/sentry/android/core/SentryAndroidOptions;Lio/sentry/transport/ICurrentDateProvider;)V
472-
public fun run ()V
472+
public final class io/sentry/android/core/TombstoneIntegration$TombstoneHint : io/sentry/hints/BlockingFlushHint, io/sentry/hints/Backfillable, io/sentry/hints/NativeCrashExit {
473+
public fun <init> (JLio/sentry/ILogger;JZ)V
474+
public fun isFlushable (Lio/sentry/protocol/SentryId;)Z
475+
public fun setFlushable (Lio/sentry/protocol/SentryId;)V
476+
public fun shouldEnrich ()Z
477+
public fun timestamp ()Ljava/lang/Long;
478+
}
479+
480+
public class io/sentry/android/core/TombstoneIntegration$TombstonePolicy : io/sentry/android/core/ApplicationExitInfoHistoryDispatcher$ApplicationExitInfoPolicy {
481+
public fun <init> (Lio/sentry/android/core/SentryAndroidOptions;)V
482+
public fun buildReport (Landroid/app/ApplicationExitInfo;Z)Lio/sentry/android/core/ApplicationExitInfoHistoryDispatcher$Report;
483+
public fun getLabel ()Ljava/lang/String;
484+
public fun getLastReportedTimestamp ()Ljava/lang/Long;
485+
public fun getTargetReason ()I
486+
public fun shouldReportHistorical ()Z
473487
}
474488

475489
public final class io/sentry/android/core/UserInteractionIntegration : android/app/Application$ActivityLifecycleCallbacks, io/sentry/Integration, java/io/Closeable {
@@ -498,7 +512,9 @@ public final class io/sentry/android/core/ViewHierarchyEventProcessor : io/sentr
498512
}
499513

500514
public final class io/sentry/android/core/cache/AndroidEnvelopeCache : io/sentry/cache/EnvelopeCache {
515+
public static final field LAST_ANR_MARKER_LABEL Ljava/lang/String;
501516
public static final field LAST_ANR_REPORT Ljava/lang/String;
517+
public static final field LAST_TOMBSTONE_MARKER_LABEL Ljava/lang/String;
502518
public static final field LAST_TOMBSTONE_REPORT Ljava/lang/String;
503519
public fun <init> (Lio/sentry/android/core/SentryAndroidOptions;)V
504520
public fun getDirectory ()Ljava/io/File;

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

Lines changed: 26 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@
1818
import io.sentry.android.core.cache.AndroidEnvelopeCache;
1919
import io.sentry.android.core.internal.threaddump.Lines;
2020
import io.sentry.android.core.internal.threaddump.ThreadDumpParser;
21-
import io.sentry.cache.EnvelopeCache;
22-
import io.sentry.cache.IEnvelopeCache;
2321
import io.sentry.hints.AbnormalExit;
2422
import io.sentry.hints.Backfillable;
2523
import io.sentry.hints.BlockingFlushHint;
@@ -39,20 +37,14 @@
3937
import java.io.IOException;
4038
import java.io.InputStream;
4139
import java.io.InputStreamReader;
42-
import java.util.ArrayList;
43-
import java.util.Collections;
4440
import java.util.List;
45-
import java.util.concurrent.TimeUnit;
4641
import org.jetbrains.annotations.ApiStatus;
4742
import org.jetbrains.annotations.NotNull;
4843
import org.jetbrains.annotations.Nullable;
4944

5045
@SuppressLint("NewApi") // we check this in AnrIntegrationFactory
5146
public class AnrV2Integration implements Integration, Closeable {
5247

53-
// using 91 to avoid timezone change hassle, 90 days is how long Sentry keeps the events
54-
static final long NINETY_DAYS_THRESHOLD = TimeUnit.DAYS.toMillis(91);
55-
5648
private final @NotNull Context context;
5749
private final @NotNull ICurrentDateProvider dateProvider;
5850
private @Nullable SentryAndroidOptions options;
@@ -92,9 +84,11 @@ public void register(@NotNull IScopes scopes, @NotNull SentryOptions options) {
9284
try {
9385
options
9486
.getExecutorService()
95-
.submit(new AnrProcessor(context, scopes, this.options, dateProvider));
87+
.submit(
88+
new ApplicationExitInfoHistoryDispatcher(
89+
context, scopes, this.options, dateProvider, new AnrV2Policy(this.options)));
9690
} catch (Throwable e) {
97-
options.getLogger().log(SentryLevel.DEBUG, "Failed to start AnrProcessor.", e);
91+
options.getLogger().log(SentryLevel.DEBUG, "Failed to start ANR processor.", e);
9892
}
9993
options.getLogger().log(SentryLevel.DEBUG, "AnrV2Integration installed.");
10094
addIntegrationToSdkVersion("AnrV2");
@@ -108,132 +102,37 @@ public void close() throws IOException {
108102
}
109103
}
110104

111-
static class AnrProcessor implements Runnable {
105+
private static final class AnrV2Policy
106+
implements ApplicationExitInfoHistoryDispatcher.ApplicationExitInfoPolicy {
112107

113-
private final @NotNull Context context;
114-
private final @NotNull IScopes scopes;
115108
private final @NotNull SentryAndroidOptions options;
116-
private final long threshold;
117-
118-
AnrProcessor(
119-
final @NotNull Context context,
120-
final @NotNull IScopes scopes,
121-
final @NotNull SentryAndroidOptions options,
122-
final @NotNull ICurrentDateProvider dateProvider) {
123-
this.context = context;
124-
this.scopes = scopes;
109+
110+
AnrV2Policy(final @NotNull SentryAndroidOptions options) {
125111
this.options = options;
126-
this.threshold = dateProvider.getCurrentTimeMillis() - NINETY_DAYS_THRESHOLD;
127112
}
128113

129-
@SuppressLint("NewApi") // we check this in AnrIntegrationFactory
130114
@Override
131-
public void run() {
132-
final ActivityManager activityManager =
133-
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
134-
135-
final List<ApplicationExitInfo> applicationExitInfoList =
136-
activityManager.getHistoricalProcessExitReasons(null, 0, 0);
137-
if (applicationExitInfoList.size() == 0) {
138-
options.getLogger().log(SentryLevel.DEBUG, "No records in historical exit reasons.");
139-
return;
140-
}
141-
142-
final IEnvelopeCache cache = options.getEnvelopeDiskCache();
143-
if (cache instanceof EnvelopeCache) {
144-
if (options.isEnableAutoSessionTracking()
145-
&& !((EnvelopeCache) cache).waitPreviousSessionFlush()) {
146-
options
147-
.getLogger()
148-
.log(
149-
SentryLevel.WARNING,
150-
"Timed out waiting to flush previous session to its own file.");
151-
152-
// if we timed out waiting here, we can already flush the latch, because the timeout is
153-
// big
154-
// enough to wait for it only once and we don't have to wait again in
155-
// PreviousSessionFinalizer
156-
((EnvelopeCache) cache).flushPreviousSession();
157-
}
158-
}
159-
160-
// making a deep copy as we're modifying the list
161-
final List<ApplicationExitInfo> exitInfos = new ArrayList<>(applicationExitInfoList);
162-
final @Nullable Long lastReportedAnrTimestamp = AndroidEnvelopeCache.lastReportedAnr(options);
163-
164-
// search for the latest ANR to report it separately as we're gonna enrich it. The latest
165-
// ANR will be first in the list, as it's filled last-to-first in order of appearance
166-
ApplicationExitInfo latestAnr = null;
167-
for (ApplicationExitInfo applicationExitInfo : exitInfos) {
168-
if (applicationExitInfo.getReason() == ApplicationExitInfo.REASON_ANR) {
169-
latestAnr = applicationExitInfo;
170-
// remove it, so it's not reported twice
171-
exitInfos.remove(applicationExitInfo);
172-
break;
173-
}
174-
}
175-
176-
if (latestAnr == null) {
177-
options
178-
.getLogger()
179-
.log(SentryLevel.DEBUG, "No ANRs have been found in the historical exit reasons list.");
180-
return;
181-
}
182-
183-
if (latestAnr.getTimestamp() < threshold) {
184-
options
185-
.getLogger()
186-
.log(SentryLevel.DEBUG, "Latest ANR happened too long ago, returning early.");
187-
return;
188-
}
189-
190-
if (lastReportedAnrTimestamp != null
191-
&& latestAnr.getTimestamp() <= lastReportedAnrTimestamp) {
192-
options
193-
.getLogger()
194-
.log(SentryLevel.DEBUG, "Latest ANR has already been reported, returning early.");
195-
return;
196-
}
115+
public @NotNull String getLabel() {
116+
return "ANR";
117+
}
197118

198-
if (options.isReportHistoricalAnrs()) {
199-
// report the remainder without enriching
200-
reportNonEnrichedHistoricalAnrs(exitInfos, lastReportedAnrTimestamp);
201-
}
119+
@Override
120+
public int getTargetReason() {
121+
return ApplicationExitInfo.REASON_ANR;
122+
}
202123

203-
// report the latest ANR with enriching, if contexts are available, otherwise report it
204-
// non-enriched
205-
reportAsSentryEvent(latestAnr, true);
124+
@Override
125+
public boolean shouldReportHistorical() {
126+
return options.isReportHistoricalAnrs();
206127
}
207128

208-
private void reportNonEnrichedHistoricalAnrs(
209-
final @NotNull List<ApplicationExitInfo> exitInfos, final @Nullable Long lastReportedAnr) {
210-
// we reverse the list, because the OS puts errors in order of appearance, last-to-first
211-
// and we want to write a marker file after each ANR has been processed, so in case the app
212-
// gets killed meanwhile, we can proceed from the last reported ANR and not process the entire
213-
// list again
214-
Collections.reverse(exitInfos);
215-
for (ApplicationExitInfo applicationExitInfo : exitInfos) {
216-
if (applicationExitInfo.getReason() == ApplicationExitInfo.REASON_ANR) {
217-
if (applicationExitInfo.getTimestamp() < threshold) {
218-
options
219-
.getLogger()
220-
.log(SentryLevel.DEBUG, "ANR happened too long ago %s.", applicationExitInfo);
221-
continue;
222-
}
223-
224-
if (lastReportedAnr != null && applicationExitInfo.getTimestamp() <= lastReportedAnr) {
225-
options
226-
.getLogger()
227-
.log(SentryLevel.DEBUG, "ANR has already been reported %s.", applicationExitInfo);
228-
continue;
229-
}
230-
231-
reportAsSentryEvent(applicationExitInfo, false); // do not enrich past events
232-
}
233-
}
129+
@Override
130+
public @Nullable Long getLastReportedTimestamp() {
131+
return AndroidEnvelopeCache.lastReportedAnr(options);
234132
}
235133

236-
private void reportAsSentryEvent(
134+
@Override
135+
public @Nullable ApplicationExitInfoHistoryDispatcher.Report buildReport(
237136
final @NotNull ApplicationExitInfo exitInfo, final boolean shouldEnrich) {
238137
final long anrTimestamp = exitInfo.getTimestamp();
239138
final boolean isBackground =
@@ -247,7 +146,7 @@ private void reportAsSentryEvent(
247146
SentryLevel.WARNING,
248147
"Not reporting ANR event as there was no thread dump for the ANR %s",
249148
exitInfo.toString());
250-
return;
149+
return null;
251150
}
252151
final AnrV2Hint anrHint =
253152
new AnrV2Hint(
@@ -284,19 +183,7 @@ private void reportAsSentryEvent(
284183
}
285184
}
286185

287-
final @NotNull SentryId sentryId = scopes.captureEvent(event, hint);
288-
final boolean isEventDropped = sentryId.equals(SentryId.EMPTY_ID);
289-
if (!isEventDropped) {
290-
// Block until the event is flushed to disk and the last_reported_anr marker is updated
291-
if (!anrHint.waitFlush()) {
292-
options
293-
.getLogger()
294-
.log(
295-
SentryLevel.WARNING,
296-
"Timed out waiting to flush ANR event to disk. Event: %s",
297-
event.getEventId());
298-
}
299-
}
186+
return new ApplicationExitInfoHistoryDispatcher.Report(event, hint, anrHint);
300187
}
301188

302189
private @NotNull ParseResult parseThreadDump(
@@ -379,6 +266,7 @@ public boolean ignoreCurrentThread() {
379266
return false;
380267
}
381268

269+
@NotNull
382270
@Override
383271
public Long timestamp() {
384272
return timestamp;

0 commit comments

Comments
 (0)