第3章 ANR日志获取:四种核心手段

3.1 为什么日志是ANR分析的第一步?

你想想看,ANR发生了,你手上什么数据都没有,怎么定位问题?靠猜吗?

日志就是ANR现场的"黑匣子"。它记录了崩溃瞬间的线程堆栈、CPU负载、内存状态、Binder事务等信息。没有这些,你连主线程在干什么都不知道。

我个人习惯是:一旦复现ANR,第一件事就是拉日志。先别急着看代码,先把现场固定下来。我在项目中遇到过好几次,开发者先改代码再拉日志,结果问题复现不了,日志也拿不到,白白浪费了一整天。

所以,拿到完整、准确的日志,是ANR分析的基础。这一章我们就来聊聊四种获取日志的核心手段。

3.2 四种获取方式概览

下面这张图概括了四种方式的关系和定位。你收日志的时候,可以按需选择,也可以组合使用。

ANR日志获取四种核心手段 ANR日志获取 adb bugreport 系统全量信息包 /data/anr/traces.txt 线程堆栈快照 logcat过滤 "ANR in" 关键字 dropbox信息 系统级事件记录 按需选择 · 组合使用 · 先保现场再分析

3.3 详解四种获取方式

方式一:adb bugreport

adb bugreport 可以说是最"重"的手段。它会打包系统的全量信息,包括CPU负载、内存状态、进程列表、Binder事务、内核日志等等。说白了,一份bugreport就是设备的"体检报告"。

命令很简单:

adb bugreport ./bugreport_$(date +%Y%m%d_%H%M%S)

但是要小心——生成时间很长。我在低端机上跑过,足足等了2分多钟。如果ANR现场比较脆弱,可能等你bugreport生成完,现场已经没了。

我的习惯:先抓traces.txt和logcat,稳住现场。如果时间充裕,再补一份bugreport做全局分析。

方式二:/data/anr/traces.txt

这是我最常用的方式,没有之一。它直接告诉你ANR发生时所有线程的堆栈。主线程卡在哪儿?Binder线程在等什么?一看便知。

拉取命令:

adb root
adb shell cat /data/anr/traces.txt > traces_$(date +%Y%m%d_%H%M%S).txt

或者如果设备没root,可以用:

adb bugreport  # 然后从bugreport包里提取traces.txt

我曾经踩过这个坑:traces.txt会被覆盖。系统默认只保留最近一次ANR的traces。如果你连续复现了两次ANR,第一次的traces就被冲掉了。所以我建议:每次ANR发生后立即拉取,别等。

注意:从 Android 10 开始,traces.txt 可能不在 /data/anr/ 下,而是在 /data/anr/ 目录下按进程名分文件存储。可以用 adb shell ls /data/anr/ 查看具体文件名。

方式三:logcat过滤

你想想看,如果每次都要等bugreport生成,黄花菜都凉了。logcat过滤是最快拿到ANR线索的方式。系统在发生ANR时,会输出带 "ANR in" 关键字的日志。

过滤命令:

adb logcat | grep "ANR in"

或者更精确一点,抓取ANR完整上下文:

adb logcat -v threadtime | grep -E "ANR|am_anr|Reason:"

我在项目中遇到过一个问题:logcat缓冲区太小,ANR日志被冲掉了。尤其是游戏类应用,日志量巨大。所以我通常会提前调大缓冲区:

adb logcat -G 16M
技巧:可以用 adb logcat -b events | grep am_anr 从events缓冲区抓ANR事件,这个缓冲区通常更稳定。

方式四:dropbox信息

dropbox这块容易被忽略。它记录的是系统级的关键事件,包括ANR、crash、watchdog等。dropbox里保存的数据比traces.txt更全面,有时候还会附带CPU使用率、内存快照等。

查看方式:

adb shell dumpsys dropbox --print | grep -A 50 "ANR"

或者直接拉取所有ANR相关的dropbox条目:

adb shell dumpsys dropbox --print | grep "process_crashed_meminfo\|anr"

说实话,dropbox是"救急"用的。当traces.txt被覆盖、logcat被冲掉时,dropbox往往还能找到线索。我遇到过好几次traces.txt为空,全靠dropbox里残留的堆栈信息定位问题。

3.4 四种方式对比

方式 数据范围 获取速度 核心用途
adb bugreport 系统全量信息 较慢(30s~2min) 全局分析、跨进程依赖
/data/anr/traces.txt 线程堆栈快照 快(秒级) 定位卡顿线程、锁竞争
logcat过滤 日志流(实时) 实时 快速筛选ANR现场
dropbox信息 系统级事件记录 快(秒级) 异常事件追溯、兜底

3.5 实战建议与避坑

我的标准操作流程(SOP):

  1. ANR发生 → 第一时间 adb logcat | grep "ANR in",截取现场日志。
  2. 马上 adb shell cat /data/anr/traces.txt 拉取堆栈。
  3. 如果traces.txt被覆盖,立刻查 dumpsys dropbox 找残留数据。
  4. 时间允许的话,后台跑一份 adb bugreport 做全局补充。

避坑指南——我曾经吃过亏的几个点:

  • 不要太依赖bugreport。现场越卡,bugreport生成越慢,甚至可能把系统拖死。
  • traces.txt 不是一直存在的。系统在重启、存储空间不足时会清理它。
  • logcat 缓冲区默认只有64KB(旧设备甚至更小)。提前 adb logcat -G 16M 是保命操作。
  • dropbox 条目有数量限制,老条目会被自动删除。所以ANR发生后尽快拉取。

一点小建议:把这四种方式的命令写成脚本,放到你的工具库里。我在团队里就是这么做的——一个 anr_capture.sh 脚本,一键跑完所有日志收集步骤。这样即使你不在现场,也能让测试同学帮忙跑,大大降低沟通成本。

好了,这一章的内容就到这里。日志拿到了,下一步就是分析。怎么从堆栈里快速定位问题?那是后面章节要聊的事。先把日志收好,这是所有分析的基础。