玩转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后,解压开,我一般按这个顺序翻:

  1. 先看 bugreport-xxx.txt 头部的 ANR in ... 部分——直接搜关键字 ANR in,定位到发生ANR的进程和时间点。
  2. 再打开 FS/data/anr/ 下的traces.txt或按进程命名的trace文件——看主线程到底卡在哪个方法。
  3. 对照 FS/logcat/ 里的main日志——查找ANR发生前几秒的最后一屏日志,看看有没有异常打印、服务超时或者GC停顿。
  4. 最后看 FS/dumpsys/cpuinfo.txt——了解当时整体的CPU负载,是不是系统繁忙导致调度延迟。

有一次我遇到一个audio播放ANR,trace里主线程停在 AudioTrack.write()。我百思不得其解,直到看了cpuinfo才发现当时CPU0~CPU3被别的后台线程占满,导致AudioFlinger的异步回调无法及时唤醒。这种跨进程的依赖,不结合CPU负载是看不出来的。

两种常用获取方式的对比

方式命令输出内容适用场景
完整bugreportadb bugreport my.zip系统快照、所有日志、ANR trace、CPU/内存统计线下复现、深度分析
直接pull traceadb pull /data/anr/traces.txt仅当前ANR的线程堆栈临时快速查看(需root)
DropBox提取adb shell dumpsys dropbox --print data_app_anrANR摘要(含进程名、时间、部分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。

ANR 现场数据获取与分析流程 获取阶段 adb bugreport adb pull traces.txt dumpsys dropbox 提取关键信息 ANR进程名称 · 时间戳 分析阶段 查看主线程trace 检查logcat上下文 分析CPU/内存负载 定位ANR根因 → 修复 提示 优先使用bugreport 包含最全信息 root可用直接pull

说回这张图,左边是三种获取方式,中间是核心信息提取,右边是三个分析方向。我在实际项目中,一般先走 adb bugreport,基本能覆盖90%的场景。如果条件不允许(比如用户设备远程抓取),我会用DropBox的dumpsys命令先拿个摘要,让用户确认是否为已知问题。

最后提醒一句:ANR的现场数据是有时效性的。系统会在ANR发生后自动覆盖旧日志。我一般要求测试同学在发现ANR后**10分钟内**执行抓取命令,超过这个时间,关键的trace可能就被新的ANR冲掉了。记住这个时间窗口,你的分析效率会高很多。