2. 日志采集准备
📋 现象识别:日志缺失的典型表现
在 ANR 问题排查中,日志采集阶段的“现象”往往不是 ANR 本身,而是 “该有的日志没有到位”。以下是四类常见缺失表现:
- traces.txt 为空或不存在:
/data/anr/traces.txt文件大小为 0,或文件根本未生成,导致无法查看主线程堆栈。 - bugreport 生成超时或截断:执行
adb bugreport耗时超过 2 分钟,或生成的 zip 包内关键文件(如 dumpstack.txt)缺失。 - logcat 丢失关键窗口:ANR 发生前后 30 秒的日志被滚动覆盖,或过滤规则过于严格导致主线程阻塞信息被过滤掉。
- kernel 日志无异常记录:
dmesg中未发现 watchdog、低内存 kill、或死锁相关输出,怀疑日志采集时机滞后。
⏱ 排查起点:当 ANR 问题复现后,首先检查以上四项日志是否齐全、时间戳是否覆盖 ANR 发生时刻。任何一项缺失,都会让后续原因定位陷入“盲人摸象”。
📡 数据采集:四类日志的 adb 获取方法
针对上述缺失现象,需要建立标准化的采集流程。下表列出每类日志的用途、获取命令与关键参数:
| 日志类型 | 核心用途 | adb 采集命令 | 关键参数 / 路径 |
|---|---|---|---|
| bugreport | 系统快照(进程、内存、wake lock、disk 等) | adb bugreport |
输出为 bugreport-xxx.zip,包含 dumpstate、dumpsys、logcat 等 |
| traces.txt | 所有 Java 线程堆栈(ANR 时自动 dump) | adb shell cat /data/anr/traces.txt |
多进程 traces 文件也可能为 traces-<pid>.txt |
| logcat | 应用与系统运行日志(主线程阻塞、广播超时等) | adb logcat -v time -b main -b system -b events *:W |
建议保留 -v time 时间戳,使用 *:W 过滤 warning 及以上级别 |
| kernel 日志 | 内核态异常(OOM killer、死锁、watchdog 触发) | adb shell dmesg |
可配合 dmesg -w > kernel.log 实时输出 |
🔥 采集优先级:
traces.txt 应作为 第一优先级,因为它是 ANR 发生时最直接的线程快照。bugreport 体积大、生成慢,建议在 traces.txt 确认后再决定是否补充。
实时过滤采集示例
以下命令组合可在 ANR 复现时实时捕获关键日志,避免滚动覆盖:
# 启动 logcat 后台采集,按 PID 过滤(假设目标进程 PID 为 1234)
adb logcat -v time --pid=1234 *:W > anr_pid1234.log &
# 同时采集 kernel 日志
adb shell dmesg -w > kernel_anr.log &
# 触发复现后,等待 30 秒后停止采集
sleep 30
kill %1 %2
采集完成后,使用以下命令将 traces.txt 拉取到本地:
adb shell cat /data/anr/traces.txt > local_traces.txt
# 若权限不足,可先执行 adb root 或使用 adb shell "cat /data/anr/traces.txt" 重定向
🔍 原因定位:日志采集失败的根因分析
即使按上述命令采集,仍可能出现日志缺失。常见根因可归纳为以下四类:
| 缺失现象 | 常见根因 | 判断方法 |
|---|---|---|
| traces.txt 为空 | ANR 发生时系统未触发 dump(如进程已被 kill) | 检查 logcat -b events 中 am_anr 事件是否出现 |
| bugreport 生成超时 | 系统服务 hang 住,导致 dumpstate 卡在某个 dumpsys 环节 | 观察 bugreport 输出末尾停留在哪个 service 模块 |
| logcat 缺失关键段 | 缓冲区太小或被其他进程日志冲刷 | 执行 logcat -g 查看各缓冲区大小(默认 256KB) |
| dmesg 无异常 | 采集时机晚于 ANR 发生超过 5 分钟,内核 ring buffer 被覆盖 | 查看 dmesg 第一行时间戳,与 ANR 时间对比 |
⚠️ 易踩坑:
adb bugreport 在部分 MIUI / EMUI 定制系统上会被拦截或裁剪,此时应改用 adb shell dumpsys 逐项拉取。此外,traces.txt 在多进程场景下可能生成多个文件,注意检查 /data/anr/ 目录下所有 traces-*.txt。
🔧 修复动作:提升日志采集可靠性的操作
针对上述根因,执行以下修复动作:
- 增大 logcat 缓冲区:通过
adb logcat -G 2MB将主缓冲区扩至 2MB,避免关键日志被覆盖。 - 建立采集脚本自动化:编写 shell 脚本同时启动 traces 监控、logcat 过滤和 dmesg 采集,减少人工遗漏。
- 使用 anr_monitor 工具:在应用中集成
ANRWatchDog或BlockCanary,在 ANR 发生时主动 dump 当前线程堆栈到本地文件,绕过系统 traces.txt 可能缺失的问题。 - 优化 bugreport 策略:对于量产设备,改为按需拉取
dumpsys cpuinfo、dumpsys meminfo、dumpsys window等单项,避免整体超时。
# 示例:自动化采集脚本片段(anr_collector.sh)
#!/system/bin/sh
ANR_TIME=$(date '+%Y%m%d_%H%M%S')
LOG_DIR=/sdcard/anr_collect/$ANR_TIME
mkdir -p $LOG_DIR
# 采集 traces
cat /data/anr/traces.txt > $LOG_DIR/traces.txt &
# 采集 logcat(过滤目标 PID)
logcat -v time --pid=$1 *:W > $LOG_DIR/logcat_anr.log &
# 采集 kernel 日志
dmesg > $LOG_DIR/kernel.log &
# 采集进程列表
ps -A > $LOG_DIR/ps_list.txt
✅ 复测验证:日志采集完整性检查清单
执行修复动作后,通过以下清单验证日志采集是否达到可用标准:
| 检查项 | 验收标准 | 验证命令/方法 |
|---|---|---|
| traces.txt 完整性 | 包含至少 5 个线程堆栈,且主线程状态为 RUNNABLE / BLOCKED | grep "main" traces.txt | head -20 |
| logcat 时间覆盖 | ANR 发生前 30s 至发生后 10s 的日志均存在 | 检索 am_anr 事件,对比首尾时间戳 |
| dmesg 有效性 | 时间戳与 ANR 时刻偏差 ≤ 3 分钟 | dmesg | tail -n 50 | grep -E "watchdog|oom|kill" |
| bugreport 关键文件 | 包含 dumpstack.txt、dumpsys.txt、logcat.txt |
解压 zip 后检查文件列表 |
📌 判断规则:以上四项全部通过,日志采集阶段判定为“可用”;若任意一项不通过,需返回修复动作步骤,调整采集参数或脚本逻辑。
🛡️ 预防方案:建立标准化日志采集规范
为避免每次 ANR 排查都重复“日志缺失—补采”的低效循环,建议实施以下预防措施:
- 固化采集脚本:将
anr_collector.sh集成到测试环境启动器中,随应用启动自动运行。 - 配置 logcat 持久化:在开发机上通过
adb logcat -v time -f /data/local/tmp/logcat_persist.log -r 2048 -n 5 *:W实现循环写入,保留最近 10MB 历史日志。 - traces.txt 自动备份:利用 inotify 监控
/data/anr/目录,一旦文件变化立即备份到/sdcard/独立分区。 - 定期检查缓冲区容量:在 CI 流程中加入
adb logcat -g断言,确保 main/events/system 缓冲区 ≥ 512KB。
# 预防:inotify 监控 traces 目录(需 root)
inotifywait -m /data/anr/ -e create -e modify |
while read path action file; do
if [[ "$file" == traces* ]]; then
cp "$path/$file" "/sdcard/anr_backup/$(date '+%Y%m%d_%H%M%S')_$file"
fi
done