Diagnose ANR traces using the following rules and format the output with references/anr_trace_report_template.md.
1. Pre-flight context checks
- Process match: confirm that the dumped process (
----- pid N at .../Cmd line:header) matches the ANRSubject.Input dispatching timed outsubjects often carry no package or PID; recordunknownrather than aborting. - Capture delay (
Immediatevs.Delayed Snapshot):Immediate: dump captured at the ANR timestamp (the unresponsive thread's stack reflects the freeze).Delayed Snapshot (unresponsive thread stack bypassed): significant lag between the ANR and dump timestamps, ORDebug Storerecords a slow main-threadLooperMsgthat finished right before the dump while the unresponsive thread is now idle innativePollOnce. Don't dismiss as bug-free or misclassify a completed slow transaction as a Sync Barrier Leak.Debug Storeexists only on Android 15+ (absence on older devices is not a signal). ItsLooperMsgentry logs slow (over 2 seconds)ActivityThread$Hmessages (such as Activity lifecycle transactions orBIND_APPLICATION), whereas receiver and service start or bind callbacks use separateDebug Storetags and genericHandler.postrunnables are not logged. A completedLooperMsgtherefore directly identifies a slow main-thread callback: reportIndeterminate (Delayed Snapshot)and attribute it using Section 2 (APP_DEFECTunless external starvation holds).
- User-perceived ANR:
Yes(Foreground UI freeze / Foreground termination): anyInput dispatching timed outANR, OR any component, service, job, or startup ANR whenForeground: Yes(killing a foreground process terminates the user's visible app).No(Silent background termination): background component or watchdog ANRs whenForeground: No.
2. Attribution verdict
Always commit to a single primary Verdict (never output "Shared" or
"Mixed"). Record any secondary environmental or app contention in
Contributing Factor (None | EXTERNAL (<process or metric>) | APP
(<thread or workload>)).
The verdict's job is to tell the developer whether there is something in the
app to fix. Evaluate the verdicts in the following order and stop at the
first one whose evidence is present; record the lower-ranked explanation, if
any, as a one-line Contributing Factor. Precedence never promotes a verdict
without real app-side evidence.
APP_DEFECT(application code, lock, lifecycle contract, or threading bug). Applies when the unresponsive thread (tid=1, thebinder:thread for ContentProvider ANRs, or the custom-HandlerorgoAsync()worker thread for broadcast receivers) has a concrete app-side blocking dependency or contract violation, regardless of background system load:- Direct I/O or heavy computation on the unresponsive thread, a synchronous
wait on in-process or cross-process work (such as
Future.get(), a latch, synchronous SDK initialization, an avoidable synchronous Binder call, a lock, monitor, or futex whose holder is actively executing app work on the CPU, or a deadlock where the holder waits on a resource the unresponsive thread owns), or a View threading violation that leaked a sync barrier (Section 3). This isAPP_DEFECTeven when the waited-on work is slow because of app-driven CPU, GC, or memory pressure; the primary fix is removing the blocking dependency and the overload goes inContributing Factor: APP (<thread or workload>). (DistinguishBlockedon<0x...>from normalNativeidle polling inMessageQueue.nativePollOnce.) - Guard: a lock or GC wait whose target thread (the lock holder or GC
daemon, when the app's own heap is not exhausted) is itself starved by
external load (waiting on the run queue or in kernel I/O
state=D) is not app-side evidence only if the device is actually saturated; fall through to the following verdicts. If total CPU has headroom and the holder still can't get scheduled because the app runs it at a much lower priority than the waiting unresponsive thread, that is a priority inversion the app created: stay withAPP_DEFECT.
- Direct I/O or heavy computation on the unresponsive thread, a synchronous
wait on in-process or cross-process work (such as
APP_RESOURCE_OVERLOAD(system resource contention driven by the target app). Applies when device CPU, memory, or I/O is saturated (or thrashing on major page faults) AND the target app is a dominant resource driver (for example, a top CPU consumer alongside heavy major page faults, GC thrashing, or excessive thread creation in the hundreds). If the target app has a low share of total CPU whilekswapd0or other processes dominate—even if cold-start code paging produces several hundred major faults or standard framework/SDK thread pools—fall through toEXTERNAL_BOTTLENECK. Other processes being loaded when the target app is a dominant driver does not downgrade this verdict; note them inContributing Factor: EXTERNAL (<process or metric>).EXTERNAL_BOTTLENECK(External Starvation; target app is a passive victim). Applies only when neither verdict 1 nor verdict 2 holds: other processes or the kernel saturate CPU, memory, or I/O while the target app consumes negligible or low CPU and has no blocking app-side dependency (including cold-start class, library, or provider initialization stalled in disk wait or starved under system I/O and memory pressure, or app threads suspended in kernel cgroup freezer channels).- Strict no-fix rule: when Verdict is
EXTERNAL_BOTTLENECK, never propose app code fixes or defensive workarounds. Fill both Section 4 fields withN/A -- No application code change required (External system bottleneck).
- Strict no-fix rule: when Verdict is
3. Root cause and special forensic patterns
Sync Barrier Leakvs.Indeterminatevs.Async Lifecycle Timeout(whentid=1is innativePollOnce,dispatchVsync, or rendering VSYNC frames):- Exclusions first:
- All threads suspended in kernel cgroup freezer channels ->
EXTERNAL_BOTTLENECK(External Starvation). Debug Storeshows a completed slowLooperMsgcovering the ANR window, or dump capture was significantly delayed ->Indeterminate (Delayed Snapshot)(with the Verdict from Section 2 evidence, orAPP_DEFECTwhen no external or resource-overload evidence is present).- Uncompleted async component contract (
BroadcastReceiver.goAsync()withoutPendingResult.finish(),JobServicetimeout, orSHORT_SERVICEexceeding its deadline withoutService.stopSelf()) ->Async Lifecycle Timeout. Only claim this when the trace shows it (for example,GoAsyncor job-stop records inDebug Store, or aSHORT_SERVICEtimeout named in theSubject:line); never infer an async contract leak from an emptyDebug Store. - Mechanism:
ViewRootImpl.requestLayout()callscheckThread(), but invalidation paths (invalidate()->onDescendantInvalidated()->scheduleTraversals()) don't, allowing a background thread to enterscheduleTraversals()concurrently with the main thread. Both callmHandler.getLooper().getQueue().postSyncBarrier()and overwrite the unsynchronizedmTraversalBarriertoken;doTraversal()removes only the latest token, leaking an orphaned sync barrier (Message.target == null) inMessageQueue. Synchronous messages starve behind the barrier while asynchronous VSYNC frames continue to run. Android 17 fixed this platform race; Android 16 and older remain vulnerable, so check the trace's Android version when weighing this pattern. - ANR subject signatures: background component timeouts (
Broadcast of Intent,executing service) andInput dispatching timed out (Application does not have a focused window), because the window-focus request is also a synchronous main-thread message that starves behind the leaked barrier. A no-focused-window subject alone is not evidence of a sync barrier (for example, if companion code or window state explicitly setsFLAG_NOT_FOCUSABLE); apply the criteria that follow. View Threading Violation (Sync Barrier Leak [Confirmed]): background thread caught mutating UI views/ViewRootImplin the dump, ORtid=1is actively rendering a VSYNC frame (doFrame -> doTraversal) while a synchronous message (SchRcvinDebug Store) starves alongside a spinning background worker.View Threading Violation (Sync Barrier Leak [probable]): when the offending background thread has already exited, don't diagnose by elimination alone. Require corroborating VSYNC rendering evidence (such astid=1caught indispatchVsync, or elevated context switches and CPU ticks onmainandRenderThreadshowing asynchronous rendering continued while the synchronous message starved), immediate capture,tid=1innativePollOnce(idle or dispatching VSYNC), an empty or absentDebug Store, and no lock, IPC, orstate=DI/O wait ontid=1.- Remediation and detection: confine all View mutations to the main thread
at the root scope (launching on
Dispatchers.MainorlifecycleScopeand wrapping only slow I/O inwithContext(Dispatchers.IO)). When the offending call site isn't on the stack, audit background callbacks and pinpoint it using Android 17's compatibility check (adb shell am compat enable ENFORCE_THREAD_CHECKS_ON_VIEW_ROOT_IMPL_APIS <package>, debuggable builds only), Android 17'sView#registerCalledFromWrongThreadListener(requirescompileSdk 37; guard calls withBuild.VERSION.SDK_INT >= 37) for production telemetry, the Robolectric unit-test flag-Drobolectric.enforceViewMethodsCalledOnMainThread=true, or crash logs forIllegalStateExceptionatMessageQueue.removeSyncBarrier.
- Blocked or missing unresponsive thread:
- If the unresponsive thread is
Blockedon a lock, identify the holder thread and diagnose what it is doing (app work, GC, I/O, waiting on a lock the unresponsive thread owns, or a lower-priority thread being starved) rather than the waiting thread's own frames; the remediation follows from the holder's workload. - If
tid=1is absent fromDALVIK THREADS, locate the main thread among the(not attached)entries inDALVIK THREADS(sysTid == PID) before concluding anything; its frames (such as VM teardown waiting on other threads) reveal the actual stall.
- If the unresponsive thread is
- Stackless traces (stack-dump timeout or missing thread stacks):
- Distinguish using kernel
Waiting Channels(sysTid=<tid> <wchan>lines) and/procstats: - External starvation (
EXTERNAL_BOTTLENECK): severe system I/O or memory pressure with the main thread (sysTid == PID) in kernel disk or file system wait and negligible app CPU. - Internal futex contention (
APP_DEFECT/Lock Contention): the main thread and a convoy of app worker threads parked infutex_*wait channels while the app actively consumes CPU. - App-driven overload (
APP_RESOURCE_OVERLOAD): the app dominates CPU or major page faults without an internal futex convoy. - ART
Signal Catchervs. customSIGQUITinterception: ART's built-inSignal Catcherthread normally waits insigtimedwait/do_sigtimedwait. Never flag it alone; only flag customSIGQUITinterception when an additional app thread also appears in a signal-wait channel.
- Distinguish using kernel
- Secondary or compounding threads (
Secondary / Compounding Threads):- Scan worker threads and report concurrent aggravators (or
None): kernel disk sync (state=Dinfsync/fdatasync), third-party JNI or C/C++ hooks in ART/GC/signal paths, custom signal-wait threads, unbounded thread pools, or thread priority abuse.
- Scan worker threads and report concurrent aggravators (or