4. 采集 ANR 日志:adb logcat 抓取 ANR 堆栈与前后缓冲区
4.1 本章在排查主线中的位置
在“现象识别”阶段确认 ANR 发生后,数据采集是锁定根因的关键步骤。adb logcat 能够捕获 App 进程在 ANR 时刻的堆栈快照、系统日志以及内核 buffer 状态,为后续分析 I/O 阻塞、Binder 死锁、主线程耗时等提供原始证据。
核心目标:在 ANR 发生后的数秒内,通过 adb logcat 获取包含
ANR in 标签的完整日志,尤其是主线程堆栈和前后缓冲区(main buffer 与 system buffer),避免因日志循环覆盖丢失关键帧。
4.2 前置条件与工具清单
| 需求项 | 说明 |
|---|---|
| USB 调试 | Android 设备已开启开发者选项中的 USB 调试 |
| adb 工具 | 版本 ≥ 1.0.36(支持 -b 参数指定 buffer) |
| 系统权限 | 抓取 system buffer 可能需要 root 或为 system 应用 |
| 时间同步 | 设备时间与 PC 时间尽量一致,便于关联 logcat 时间戳与 ANR 发生时刻 |
4.3 抓取命令与参数详解
当 App 发生 ANR 时,Android 系统会在 /data/anr/traces.txt 中写入堆栈 dump,同时 logcat 会输出 ANR in 标记。对于 I/O 瓶颈类 ANR,必须同时捕获 main(主日志)和 system(系统事件)两个 buffer 的内容。
# 实时抓取并保存到文件(推荐)
adb logcat -b main -b system -v threadtime -d > anr_full.log
# 仅抓取 ANR 相关行(适合快速定位)
adb logcat -b main -b system -v threadtime -d | grep -E "(ANR in|DALVIK THREADS|----- pid )"
# 持续抓取直到手动停止(用于复现)
adb logcat -b main -b system -v threadtime > anr_live.log
参数说明:
-b main 抓取应用日志,-b system 抓取系统级消息(如 Watchdog、IOWait),-v threadtime 输出含线程 ID 和精确时间戳,-d 表示 dump 当前缓冲区后退出。
4.4 前后缓冲区策略与循环覆盖
Android logcat 使用环形缓冲区,默认大小因设备而异(通常 main 256KB,system 256KB)。ANR 发生后,若继续产生大量日志,关键堆栈可能被后续日志覆盖。因此建议:
- 立即执行:检测到 ANR 弹窗后,3 秒内执行
adb logcat -b main -b system -ddump 操作。 - 增大缓冲区:若环境允许,预先调整 buffer 大小(需要 root):
logcat -b main -G 1M。 - 多 buffer 备份:同时抓取 events buffer(系统事件)辅助分析 I/O 等待原因:
adb logcat -b events -v threadtime -d > events_anr.log
4.5 堆栈定位与 I/O 标记识别
抓取后的 anr_full.log 中,重点关注以下模式:
| 日志特征 | 指示含义 |
|---|---|
ANR in ... (com.example.app) | ANR 进程包名与时间戳 |
----- pid 12345 at 2025-03-20 14:30:00 ----- | 堆栈 dump 起始标记 |
main (native): #00 pc ... /system/lib/libc.so (__ioctl+4) | 主线程阻塞在 I/O 系统调用(如 ioctl、read、write) |
"main" prio=5 tid=1 Blocked | 主线程处于 Blocked 状态 |
java.io.FileInputStream.readBytes / android.database.sqlite.SQLiteSession.acquireConnection | 上层 I/O 或数据库竞争 |
排查技巧:在日志中搜索
ioctl、futex、epoll_wait 等内核符号,结合 system buffer 中的 Watchdog 信息,可快速判断是否因磁盘 I/O 高负载导致主线程无法获得 CPU 或文件锁。
4.6 常见采集误区与避坑
- 只用
-b main:遗漏 system buffer 中的系统级提示(如 I/O wait 百分比、磁盘排队深度)。 - 未使用
-d:持续监听会覆盖旧日志,丢失 ANR 现场。 - 时间戳不一致:设备与 PC 时间差导致无法匹配 ANR 报告时间。
- 忽略
/data/anr/traces.txt:该文件包含更详细的 native 堆栈,与 logcat 互为补充。
4.7 本章故障树:adb logcat 采集执行流程
下图展示了从检测 ANR 到获取有效堆栈的决策与操作路径:
4.8 本章小结检查表
| 检查项 | 是否完成 |
|---|---|
| 已连接 USB 调试并确认 adb 可用 | ☐ |
| ANR 发生后 3 秒内执行 dump 命令 | ☐ |
同时使用了 -b main 和 -b system | ☐ |
| 日志中可找到 “ANR in” 及主线程堆栈 | ☐ |
已检查 /data/anr/traces.txt 是否存在 | ☐ |
注意:如果日志中未找到堆栈,可能是 buffer 已被覆盖。建议下次复现时使用
logcat -b main -G 2M 预先扩大缓冲区,或编写自动 dump 脚本(如监听 ActivityManager: ANR in 关键词实时触发)。