-
-
Notifications
You must be signed in to change notification settings - Fork 478
ref(android): Measure ANR thresholds on the monotonic clock (JAVA-579) #6041
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
463bd84
9c11a98
4d1f5fe
eda3222
8fa7d09
fd94017
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,60 +30,60 @@ | |
| import android.app.ActivityManager; | ||
| import android.content.Context; | ||
| import android.os.Debug; | ||
| import android.os.SystemClock; | ||
| import io.sentry.ILogger; | ||
| import io.sentry.SentryLevel; | ||
| import io.sentry.transport.ICurrentDateProvider; | ||
| import io.sentry.time.Deadline; | ||
| import io.sentry.time.MonotonicTicker; | ||
| import java.util.List; | ||
| import java.util.concurrent.TimeUnit; | ||
| import java.util.concurrent.atomic.AtomicBoolean; | ||
| import org.jetbrains.annotations.NotNull; | ||
| import org.jetbrains.annotations.TestOnly; | ||
|
|
||
| /** A watchdog timer thread that detects when the UI thread has frozen. */ | ||
| @SuppressWarnings("UnusedReturnValue") | ||
| final class ANRWatchDog extends Thread { | ||
|
|
||
| private static final long DEFAULT_POLLING_INTERVAL_MS = 500; | ||
|
|
||
| private final boolean reportInDebug; | ||
| private final ANRListener anrListener; | ||
| private final MainLooperHandler uiHandler; | ||
| private final ICurrentDateProvider timeProvider; | ||
| private final MonotonicTicker monotonicTicker; | ||
|
|
||
| /** the interval in which we check if there's an ANR, in ms */ | ||
| private long pollingIntervalMs; | ||
|
|
||
| private final long timeoutIntervalMillis; | ||
| private final @NotNull ILogger logger; | ||
|
|
||
| private volatile long lastKnownActiveUiTimestampMs = 0; | ||
| /** How long the main thread has left to run the ticker before we call it an ANR. */ | ||
| private volatile @NotNull Deadline uiResponsiveUntil; | ||
|
|
||
| private final AtomicBoolean reported = new AtomicBoolean(false); | ||
|
|
||
| private final @NotNull Context context; | ||
|
|
||
| @SuppressWarnings("UnnecessaryLambda") | ||
| private final Runnable ticker; | ||
|
|
||
| ANRWatchDog( | ||
| long timeoutIntervalMillis, | ||
| boolean reportInDebug, | ||
| @NotNull ANRListener listener, | ||
| @NotNull ILogger logger, | ||
| /** Reads the timeout, the debug behavior, the logger and the ticker off {@code options}. */ | ||
| static @NotNull ANRWatchDog create( | ||
| final @NotNull SentryAndroidOptions options, | ||
| final @NotNull ANRListener listener, | ||
| final @NotNull Context context) { | ||
| // avoid method refs on Android due to some issues with older AGP setups | ||
| // noinspection Convert2MethodRef | ||
| this( | ||
| () -> SystemClock.uptimeMillis(), | ||
| timeoutIntervalMillis, | ||
| 500, | ||
| reportInDebug, | ||
| return new ANRWatchDog( | ||
| options.getMonotonicTicker(), | ||
| options.getAnrTimeoutIntervalMillis(), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ANR clock includes device sleepMedium Severity ANR thresholds now come from Additional Locations (2)Triggered by project rule: PR Review Guidelines for Cursor Bugbot Reviewed by Cursor Bugbot for commit fd94017. Configure here. |
||
| DEFAULT_POLLING_INTERVAL_MS, | ||
| options.isAnrReportInDebug(), | ||
| listener, | ||
| logger, | ||
| options.getLogger(), | ||
| new MainLooperHandler(), | ||
| context); | ||
| } | ||
|
|
||
| @TestOnly | ||
| ANRWatchDog( | ||
| @NotNull final ICurrentDateProvider timeProvider, | ||
| @NotNull final MonotonicTicker monotonicTicker, | ||
| long timeoutIntervalMillis, | ||
| long pollingIntervalMillis, | ||
| boolean reportInDebug, | ||
|
|
@@ -94,17 +94,20 @@ final class ANRWatchDog extends Thread { | |
|
|
||
| super("|ANR-WatchDog|"); | ||
|
|
||
| this.timeProvider = timeProvider; | ||
| this.monotonicTicker = monotonicTicker; | ||
| this.timeoutIntervalMillis = timeoutIntervalMillis; | ||
| this.pollingIntervalMs = pollingIntervalMillis; | ||
| this.reportInDebug = reportInDebug; | ||
| this.anrListener = listener; | ||
| this.logger = logger; | ||
| this.uiHandler = uiHandler; | ||
| this.context = context; | ||
| this.uiResponsiveUntil = | ||
| Deadline.after(monotonicTicker, timeoutIntervalMillis, TimeUnit.MILLISECONDS); | ||
| this.ticker = | ||
| () -> { | ||
| lastKnownActiveUiTimestampMs = timeProvider.getCurrentTimeMillis(); | ||
| uiResponsiveUntil = | ||
| Deadline.after(monotonicTicker, timeoutIntervalMillis, TimeUnit.MILLISECONDS); | ||
| reported.set(false); | ||
| }; | ||
|
|
||
|
|
@@ -140,11 +143,8 @@ public void run() { | |
| return; | ||
| } | ||
|
|
||
| final long unresponsiveDurationMs = | ||
| timeProvider.getCurrentTimeMillis() - lastKnownActiveUiTimestampMs; | ||
|
|
||
| // If the main thread has not handled ticker, it is blocked. ANR. | ||
| if (unresponsiveDurationMs > timeoutIntervalMillis) { | ||
| if (uiResponsiveUntil.hasPassed()) { | ||
| if (!reportInDebug && (Debug.isDebuggerConnected() || Debug.waitingForDebugger())) { | ||
| logger.log( | ||
| SentryLevel.DEBUG, | ||
|
|
||


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This returns EITHER a monotonic clock or a wall clock depending on the platform. This is a bad abstraction.
On android it gives you a monotonic clock which is what we want for this class, but the fact that it has
Datein the name but gives you a monotonic clock is quite confusing.