玩转adb:拿到ANR的第一手现场
做ANR分析,最怕什么?怕没有案发现场。很多时候开发小伙伴抱怨:“用户说卡死了,但日志里啥都没有。” 嗯,那是因为你没用对工具。今天我重点讲两个最核心的诊断命令——adb bugreport 和 ANR日志的主动导出。说白了,这是你分析ANR的“入场券”。
核心原则:ANR发生后第一时间执行adb bugreport,不要重启设备,不要清日志。一旦重启,大部分线索就消失了。
adb bugreport 到底能抓什么?
很多人以为bugreport就是个日志包。其实它是系统的“体检报告”。我习惯在复现ANR后,立刻在电脑上敲这句:
adb bugreport my_anr_report.zip
这条命令会自动收集以下关键信息:
- 系统信息:CPU负载、内存占用、进程列表、电池状态
- ANR traces:所有线程的调用栈(藏在
FS/data/anr/目录下) - logcat 历史:从最近一次启动到现在的所有日志(包含
ANR in关键字) - dumpsys 快照:各种系统服务的内部状态(如
activity、window、cpuinfo) - 内核日志:kernel log、lowmemorykiller 记录等
你看,一个包就把ANR前后几分钟的系统快照全拿了。我记得有一次线上用户报告弹窗卡死,用bugreport抓回来,直接看到 ActivityManager: ANR in com.example.player,后面的trace里清清楚楚显示主线程在等一个Binder锁——锁的持有者是一个ContentProvider的初始化过程。问题马上就定位了。
ANR日志单独导出的场景
但有时候,你不需要整个bugreport。比如刚好在现场,只想快速看一下当前ANR的trace,可以直接pull:
adb root
adb remount
adb pull /data/anr/traces.txt ./anr_traces.txt
注意:非root设备可能无法直接读取/data/anr/。这时可以用adb bugreport的变通方式,或者用adb shell dumpsys dropbox --print data_app_anr来提取系统DropBox中记录的ANR摘要。
我个人更推荐直接用bugreport,因为它不仅包含trace,还包含时间轴、logcat上下文和CPU统计。你想想看,单看一个trace文件,你只知道“哪个线程在等哪里”,但你不知道“为什么CPU当时没让这个线程跑起来”——CPU占用、调度延迟这些信息,bugreport里全有。
高效分析 bugreport 的小技巧
拿到zip后,解压开,我一般按这个顺序翻:
- 先看
bugreport-xxx.txt头部的ANR in ...部分——直接搜关键字ANR in,定位到发生ANR的进程和时间点。 - 再打开
FS/data/anr/下的traces.txt或按进程命名的trace文件——看主线程到底卡在哪个方法。 - 对照
FS/logcat/里的main日志——查找ANR发生前几秒的最后一屏日志,看看有没有异常打印、服务超时或者GC停顿。 - 最后看
FS/dumpsys/cpuinfo.txt——了解当时整体的CPU负载,是不是系统繁忙导致调度延迟。
有一次我遇到一个audio播放ANR,trace里主线程停在 AudioTrack.write()。我百思不得其解,直到看了cpuinfo才发现当时CPU0~CPU3被别的后台线程占满,导致AudioFlinger的异步回调无法及时唤醒。这种跨进程的依赖,不结合CPU负载是看不出来的。
两种常用获取方式的对比
| 方式 | 命令 | 输出内容 | 适用场景 |
|---|---|---|---|
| 完整bugreport | adb bugreport my.zip | 系统快照、所有日志、ANR trace、CPU/内存统计 | 线下复现、深度分析 |
| 直接pull trace | adb pull /data/anr/traces.txt | 仅当前ANR的线程堆栈 | 临时快速查看(需root) |
| DropBox提取 | adb shell dumpsys dropbox --print data_app_anr | ANR摘要(含进程名、时间、部分trace) | 非root设备,只想看摘要 |
避坑指南
我曾经犯过一个错:为了复现ANR,连续执行了三次bugreport,结果后两次覆盖了第一次的trace。后来我养成了习惯——每次抓包前检查文件时间戳,并在文件名里带上时间,比如 bugreport_anr_20250315_1430.zip。
另一个常见坑:权限不足。Android 10 及以上,adb bugreport不需要root,但直接pull /data/anr/需要root或者系统应用权限。如果你没root,老老实实用bugreport吧,别浪费时间尝试提权。
还有个注意事项:多进程ANR。有时候一个进程ANR会导致关联进程也被杀掉,但bugreport里只会记录最先触发的那一个。我遇到过SurfaceFlinger ANR,结果实际上是因为某个第三方应用占用了大量GPU资源。光看app自身的trace是看不出名堂的,一定要查看系统服务(如SurfaceFlinger)的trace。
说回这张图,左边是三种获取方式,中间是核心信息提取,右边是三个分析方向。我在实际项目中,一般先走 adb bugreport,基本能覆盖90%的场景。如果条件不允许(比如用户设备远程抓取),我会用DropBox的dumpsys命令先拿个摘要,让用户确认是否为已知问题。
最后提醒一句:ANR的现场数据是有时效性的。系统会在ANR发生后自动覆盖旧日志。我一般要求测试同学在发现ANR后**10分钟内**执行抓取命令,超过这个时间,关键的trace可能就被新的ANR冲掉了。记住这个时间窗口,你的分析效率会高很多。