如何通过 trace.txt 文件分析 ANR 的主线程阻塞点?
解读
国内面试场景里,面试官抛出“trace.txt”通常不是让你背定义,而是考察“线上 ANR 无日志”时的定位能力。
关键点:
- 知道 trace.txt 的生成时机(SIGQUIT 3 秒后由 Signal Catcher 线程写 /data/anr/)
- 能在 30 秒内从 2~3 万行文本里锁定“main”线程的“tid=1”栈,并区分“真正死等”与“被 Binder 卡住”
- 能把栈片段翻译成业务代码,给出“下一步怎么验证”的落地动作,而不是只说“看主线程”
知识点
-
trace.txt 结构:
----- pid 12345 at 2025-06-07 14:23:45 -----
Cmd line: com.xxx.app
...
"main" prio=5 tid=1 Blocked
| group="main" sCount=1 ucsCount=0 flags=1 obj=0x7188 self=0xb400007d4f78f800
| sysTid=12345 nice=-10 cgrp=default sched=0/0 handle=0x7d5d5cb4f8
| state=S schedstat=( 285948372 198765423 601 ) utm=21 stm=7 core=3 HZ=100
| stack=0x7fc35e9000-0x7fc35eb000 stackSize=8192KB
| held mutexes=
at sun.misc.Unsafe.park(Native method)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:190)
at com.xxx.cache.ImageLoader$1.run(ImageLoader.java:188)- locked <0x0745a3c6> (a java.lang.Object)
-
线程状态缩写:
S=sleep、R=running、D=IO 等待、B=blocked on monitor、W=waiting、N=native -
常见阻塞模式:
- Blocked/Waiting:锁或 CountDownLatch
- Native:JNI 或 IO
- Binder:对端进程慢(如 AMS、ContentProvider)
- 死锁:两个线程互相 held 同一地址的 monitor
-
量化指标:
schedstat 三值(运行时间、等待时间、切换次数)可粗略判断“CPU 饥饿”还是“IO 慢” -
国内补充:
华为/小米/OPPO 定制 ROM 会把 trace 写到 /data/system/dropbox 并加密,需adb shell setprop persist.sys.anr.debug 1后复现才能拿到明文
答案
现场回答采用“三步走”话术,既体现思路,又给出可落地的命令:
第一步:确认文件有效性
adb shell ls -l /data/anr | grep trace
拿到最新 trace.txt 后,先看头部时间戳与 ANR 弹窗时间是否匹配,防止拿到旧文件
第二步:30 秒定位主线程
- 搜索
"main" prio=5 tid=1定位首行 - 看紧跟的
state=字段:- 如果是
Blocked或Waiting,继续往下看栈顶,找locked <addr>或waiting on <addr> - 如果是
Native,看是否卡在epoll_wait、read、ioctl,大概率是网络或磁盘 IO - 如果栈顶是
BinderProxy.transactNative,再搜Binder thread段,看对端 tid 与进程名,确认是系统服务慢还是跨应用 ContentProvider 慢
- 如果是
- 若发现
held mutexes=与另一线程互相持有同一地址,即可判定死锁,把两个线程的栈一起截图给开发
第三步:映射到业务代码
把栈顶类名+行号与 mapping.txt 反混淆(R8 全量映射在 app/build/outputs/mapping/release/mapping.txt),得到真实源码位置,本地打断点或加日志复现;若栈顶是第三方 SDK,则升级或异步化
收尾一句:
“如果 trace 里主线程状态是 R 但 schedstat 等待时间极高,说明不是自身阻塞,而是 CPU 被其他进程抢占,需结合 adb shell top -t -o pid,tid,CPU,cmd 看系统负载,再决定是做进程优先级调整还是降频场景规避。”
拓展思考
-
线下压测时主动触发 SIGQUIT:
adb shell kill -3pidof com.xxx.app`` 可立即生成 trace,无需等 ANR,适合卡顿提前发现 -
自动化聚合:
国内大厂普遍把 trace.txt 上传到日志平台,用 Python 脚本批量解析“main”线程 state 与栈关键字,聚合成“锁-等待-IO-Binder”四类,每周出 ANR Top10 报表,比人工翻文件高效 -
与 Perfetto 互补:
trace.txt 只有瞬时快照,若问题偶现,可在用户授权后录制 10 秒 Perfetto 轨迹,用atrace -c -a com.xxx.app sched freq idle抓到 CPU 频率与调度信息,再对照 trace.txt 的 schedstat,判断是否因降频导致 16 ms 掉帧最终累加为 ANR -
折叠屏/多窗口场景:
大屏切换触发 Activity 重启,若主线程在onCreate做 IO,极易 ANR。此时 trace.txt 主线程栈顶往往是Instrumentation.callActivityOnCreate,但真正的阻塞在FileInputStream.read,需把 IO 提前到ViewModel+Coroutine并在onCreate只观察 LiveData -
合规与隐私:
国内上架应用市场要求“个人信息收集最小化”,上传 trace 前必须裁剪掉栈内可能含有的账号、手机号等字符串,可用proguard-rules.pro加-assumenosideeffects把敏感类方法置空,防止合规扫描被打回