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。

🔧 修复动作:提升日志采集可靠性的操作

针对上述根因,执行以下修复动作:

  1. 增大 logcat 缓冲区:通过 adb logcat -G 2MB 将主缓冲区扩至 2MB,避免关键日志被覆盖。
  2. 建立采集脚本自动化:编写 shell 脚本同时启动 traces 监控、logcat 过滤和 dmesg 采集,减少人工遗漏。
  3. 使用 anr_monitor 工具:在应用中集成 ANRWatchDog 或 BlockCanary,在 ANR 发生时主动 dump 当前线程堆栈到本地文件,绕过系统 traces.txt 可能缺失的问题。
  4. 优化 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
日志采集决策与流程故障树 日志采集决策与流程故障树 🚨 ANR 触发 📦 bugreport 📄 traces.txt 📋 logcat 过滤 ⚙️ kernel 日志 🔍 日志完整性检查 ✅ 采集验证完成 📌 判定路径 四项采集全部通过 → 可用 任意一项失败 → 返回修复