第4章 ANR日志概述:trace文件、events.log、main log、系统dump

4.1 为什么需要了解多种日志

处理ANR,光看一个trace.txt够吗?说实话,远远不够。我个人习惯把ANR相关的日志分成四大类:trace文件、events.log、main log和系统dump。每一类都有自己的视角,拼在一起才能还原现场。

我在项目中遇到过不少例子:trace文件显示主线程卡在某个方法,但实际原因是系统服务超时。这时候system_server的dump信息才是关键。你想想看,如果只盯着一个trace文件,可能三小时都找不出根因。

4.2 trace文件(/data/anr/traces.txt)

这是最直接的证据。ANR发生时,系统会自动dump所有线程的堆栈信息,保存到/data/anr/traces.txt(Android 11之前)或/data/anr/下带时间戳的文件里。

  • 内容:每个线程的当前调用栈,锁等待信息(如“waiting on <0x...>”),以及Binder调用状态。
  • 特点:记录了瞬间的快照,能看出主线程在干什么,但看不出ANR之前的操作序列。
  • 获取方法:adb pull /data/anr/,需要root或用adb bugreport自动打包。

关键看点:主线程("main")的堆栈,以及"held by"或者"waiting for"的锁对象。如果看到Native method,说明卡在JNI层,得用systrace看CPU占用。

4.3 events.log(/data/system/dropbox/)

这个文件很多人忽略,但它记录了系统级的ANR事件流水。位置在/data/system/dropbox/下,文件名类似system_app_anr@timestamp.txt。

events.log里包含:

  • ANR发生的准确时间(精确到微秒)
  • 触发ANR的进程PID和包名
  • “CPU usage”段:各进程的CPU时间、CPU负载
  • “binder transaction”段:当前binder调用和等待队列

避坑指南:我曾经遇到一个ANR,trace文件里主线程在Looper.loop()空闲,没有任何锁冲突。但events.log显示当时CPU负载飙到90%,是某个后台线程疯狂写日志导致的。events.log里的CPU信息是我定位的突破口。

4.4 main log(logcat)

logcat是运行时日志,ANR相关的重要tag包括:

Tag内容
ActivityManagerANR提示信息,如“ANR in com.example.app (com.example.app/.MainActivity)” + 超时类型
WindowManager窗口焦点变化、输入事件超时的相关日志
InputDispatcher输入事件被卡住的具体时长,“Timed out waiting for...”

注意:logcat是环形缓冲区,ANR发生后可能被后续日志覆盖。所以建议第一时间用adb logcat -d -b main -b events转储。我之前踩过坑,想查Input ANR时发现缓冲区已经被刷掉了,后来改成在测试机上用logcat -b events -v time -f /sdcard/logcat_events.txt实时保存。

小技巧:用grep -E "(ANR|Watchdog|Dropped|Timeout)"过滤logcat,可以快速定位ANR行。

4.5 系统dump(dumpsys、binder信息等)

系统dump是集合包,通常通过adb bugreport获得。几个关键子模块:

  • dumpsys cpuinfo:系统整体CPU使用情况,能看到哪个进程吃资源。
  • dumpsys meminfo:内存使用,低内存会导致ANR。
  • dumpsys battery:电量状态,某些低电量策略会限制CPU。
  • dumpsys activity processes:进程状态,看是不是被冻结(procState)了。
  • binder transactions(在anr dump中):列出未完成的binder调用,能发现死锁或IPC阻塞。

举个例子,我在项目中碰到一个ANR,trace里的主线程在BinderProxy.transact(),一直没返回。通过dumpsys activity processes发现目标进程正处于“CACHED_EMPTY”状态,说明它被杀掉了或正在重启,binder调用超时导致ANR。这个信息单靠trace文件是看不出来的。

4.6 四种日志对比

日志类型核心内容获取方式适合场景
trace文件线程堆栈、锁状态adb pull /data/anr/主线程卡点、死锁
events.logANR事件流水、CPU占用、binder状态从dropbox或bugreport提取排查CPU/IO竞争、系统资源不足
main log (logcat)ANR提示、输入事件超时、AMS/WMS日志adb logcat -b main -b events快速确认ANR类型和时间
系统dump进程状态、内存、CPU使用、binder调用adb bugreport综合分析系统级问题

4.7 日志分析流程

我的做法是这样的:

  1. 先看logcat,确认ANR类型(输入/广播/服务)。得到时间戳。
  2. 翻events.log,看同一时间点的CPU占用和binder事务,判断是资源竞争还是死锁。
  3. 读trace文件,分析主线程堆栈,锁定具体代码行。
  4. 最后用系统dump查外围因素(内存、进程状态、binder调用链)。

你想想看,如果跳过第二步,很可能误判。比如有次我遇到一个Input ANR,trace显示主线程在做布局,但events.log里CPU很低,反而是I/O在等待。最后发现是SharedPreferences的commit阻塞了磁盘写入。

注意:trace文件是快照,不一定代表卡顿的全过程。如果主线程在Looper.loop()空闲,不要急于认为不是ANR——可能是等待某个回调,而回调被其他线程阻塞了。这时候events.log中的binder信息会给出答案。

ANR日志体系结构 ANR发生 trace文件 线程堆栈+锁 events.log CPU + Binder main log Logcat ANR标记 系统dump dumpsys信息 分析顺序建议 1. main log → 确认类型 2. events.log → 资源环境 3. trace文件 → 代码卡点 4. 系统dump → 外围因素

实际排查时,我也会用adb bugreport一把拉取所有日志,然后按上述顺序逐层筛查。这份工作没有捷径,但理清四类日志的作用,能让你少走很多弯路。嗯,多练几次,你也能快速定位ANR根因。