系列目录 :第一篇:异常机制全景图 | [第二篇:Kernel Panic 与系统重启](#第二篇:Kernel Panic 与系统重启) | [第三篇:Tombstone 机制](#第三篇:Tombstone 机制) | [第四篇:System Server Watchdog](#第四篇:System Server Watchdog) | [第五篇:System Server 崩溃](#第五篇:System Server 崩溃) | [第六篇:ANR 机制](#第六篇:ANR 机制) | [第七篇:Java 层崩溃](#第七篇:Java 层崩溃) | [第八篇:Trace 机制](#第八篇:Trace 机制) | 第九篇:日志系统 | 第十篇:实战方法论
一、为什么需要 Trace 机制?
你可能遇到过这些场景:
- 用户反馈 App 滑动卡顿,但 logcat 里没有任何异常日志
- 系统启动慢,但不知道卡在哪个阶段
- 掉帧严重,但无法定位是渲染慢还是 CPU 调度问题
- 系统服务调用响应慢,但不知道延迟发生在哪一端
前几篇文章介绍的 tombstone、ANR、Watchdog 都是事后分析------异常已经发生,我们只能从日志中还原现场。但很多问题(如系统卡顿、掉帧、响应慢)并不会触发明确的异常,却严重影响用户体验。Trace 机制正是为了解决这类"没有硬错误,但有严重问题"的场景。
Trace 能回答的问题
| 问题类型 | Trace 能提供的信息 |
|---|---|
| UI 卡顿 | 每个帧的渲染耗时、哪些线程在竞争 CPU |
| 启动慢 | 启动过程中各阶段耗时、Binder 调用延迟 |
| 掉帧 | SurfaceFlinger 合成时间线、VSYNC 信号 |
| 系统服务慢 | Binder 调用耗时、锁竞争、线程调度 |
| 功耗异常 | CPU 频率分布、各进程的 CPU 时间片 |
| IO 阻塞 | 磁盘读写耗时、I/O 调度延迟 |
二、Trace 体系全景
在 AOSP 7 中,trace 体系分为三层:
┌──────────────────────────────────────────────┐
│ 用户态工具层 │
│ ├─ systrace (systrace.py) │
│ ├─ atrace 命令行工具 │
│ └─ perfetto(AOSP 7 后引入,暂不展开) │
├──────────────────────────────────────────────┤
│ Framework 打点层 │
│ ├─ Trace.java (android.os.Trace) │
│ ├─ ATrace (native 层) │
│ └─ 各系统服务的 trace tag │
├──────────────────────────────────────────────┤
│ 内核追踪层 │
│ ├─ ftrace (tracefs) │
│ ├─ tracepoint / kprobe / uprobe │
│ └─ trace_marker (用户态写入内核 trace buffer) │
└──────────────────────────────────────────────┘
三、ftrace ------ 内核追踪基础
3.1 ftrace 是什么
ftrace 是 Linux 内核内置的追踪框架,通过 tracefs 文件系统暴露接口。它是 Android 所有 trace 工具的底层基础。
3.2 挂载与结构
bash
# Android 中 ftrace 挂载在
mount -t tracefs none /sys/kernel/tracing
# 或旧版本
mount -t debugfs none /sys/kernel/debug
# 关键目录结构
/sys/kernel/tracing/
├─ available_tracers # 可用的追踪器
├─ current_tracer # 当前使用的追踪器
├─ tracing_on # 1=开启 0=暂停
├─ trace # 追踪结果输出
├─ trace_marker # 用户态写入标记
├─ events/ # tracepoint 事件
│ ├─ sched/ # 调度事件
│ ├─ binder/ # Binder 事件
│ ├─ power/ # 电源管理事件
│ └─ ...
└─ options/ # 追踪选项
3.3 trace_marker 用户态打点
这是连接用户态和内核态的重要桥梁------用户态通过向 trace_marker 文件写入特定格式的数据,将标记注入内核 trace buffer:
c
// 用户态写入 trace 标记
// frameworks/native/libs/utils/Trace.cpp
static void traceInit() {
// 打开 trace_marker 文件
atrace_marker_fd = open("/sys/kernel/tracing/trace_marker", O_WRONLY);
}
void ATrace_begin(const char* name) {
// 写入 'B|<pid>|<name>' 到 trace_marker
char buf[1024];
snprintf(buf, sizeof(buf), "B|%d|%s", getpid(), name);
write(atrace_marker_fd, buf, strlen(buf));
}
void ATrace_end() {
// 写入 'E' 到 trace_marker
write(atrace_marker_fd, "E", 1);
}
关键设计 :通过简单的文件写入操作(
write()),用户态代码就能将打点信息注入内核 trace buffer,实现跨进程的精确时间同步。格式B|<pid>|<name>中的B表示 begin,E表示 end。
3.4 关键 tracepoint
| 类别 | tracepoint | 用途 |
|---|---|---|
| 调度 | sched/sched_switch |
线程切换,找出 CPU 竞争 |
| 调度 | sched/sched_wakeup |
线程被唤醒,追踪唤醒延迟 |
| 频率 | power/cpu_frequency |
CPU 频率变化,分析性能瓶颈 |
| Binder | binder/binder_transaction |
Binder 调用耗时 |
| Binder | binder/binder_transaction_received |
Binder 接收时间 |
| 图形 | mdss/mdp_vsync_* |
显示 VSYNC 信号 |
| IO | block/block_rq_issue |
IO 请求发起 |
| 内存 | kmem/kmalloc |
内存分配追踪 |
四、atrace ------ Android 用户态追踪
4.1 atrace 是什么
atrace 是 Android 提供的命令行工具,封装了 ftrace 的配置和数据采集。
bash
# 查看支持的 trace category
adb shell atrace --list_categories
# 典型输出:
gfx - Graphics
input - Input
view - View System
webview - WebView
wm - Window Manager
am - Activity Manager
sm - Sync Manager
audio - Audio
video - Video
camera - Camera
hal - Hardware Modules
app - Application
res - Resource Loading
dalvik - Dalvik VM
rs - RenderScript
bionic - Bionic C Library
power - Power Management
pm - Package Manager
sched - CPU Scheduling
freq - CPU Frequency
idle - CPU Idle
load - CPU Load
disk - Disk I/O
mmc - eMMC commands
...
4.2 常用 atrace 命令
bash
# 基础用法:追踪 10 秒,抓取 sched 和 gfx 类别
adb shell atrace -t 10 sched gfx > trace.out
# 异步模式:后台启动追踪
adb shell atrace --async_start sched freq gfx
# ... 执行测试操作 ...
adb shell atrace --async_stop > trace.out
# 追加 App 自定义 trace(需要 App 代码中使用 Trace API)
adb shell atrace -t 10 -a com.example.app sched gfx
# 输出压缩格式(减少传输大小)
adb shell atrace -t 10 -z sched gfx > trace.out
五、systrace ------ 可视化分析
5.1 systrace 是什么
systrace 是 Android SDK 提供的 Python 脚本,能够:
- 调用 atrace 收集原始数据
- 将数据转换为 HTML 可视化报告
- 提供交互式分析界面
bash
# 使用 systrace.py(位于 sdk/platform-tools/systrace/)
python systrace.py -t 10 -o trace.html sched freq gfx input view
5.2 Systrace 报告解读
报告分为多个 Section:
CPU 调度区域:
每个 CPU 核心一行,显示在该核心上运行的线程:
CPU 0: [surfaceflinger] [app] [idle] [surfaceflinger] ...
CPU 1: [Binder:1234] [idle] [app] [Binder:1234] ...
解读:
- 线程名可以看出谁在占用 CPU
- 颜色块表示不同的线程
- 空白 = idle(CPU 空闲)
进程/线程区域:
每个进程一行,显示该进程各线程的状态:
com.example.app (12345)
└─ main (12345): [RUNNING][SLEEPING][RUNNING][BLOCKED]...
└─ RenderThread (12346): [RUNNING][RUNNING]...
└─ Binder_1 (12347): [RUNNING]...
线程状态颜色:
- 绿色 = Running
- 蓝色 = Runnable(等待 CPU)
- 白色 = Sleeping
- 橙色 = Uninterruptible Sleep(IO 等待)
- 紫色 = Blocked(等待锁)
VSYNC 与渲染管线:
VSYNC: | | | | | | |
SurfaceFlinger: ==== ==== ====
App Render: ====== ======
每个竖线表示 VSYNC 信号,每个横条表示渲染操作所占的时间。理想情况下,App 的渲染操作应在 16.7ms(60fps)内完成。
Binder 调用:
App → Binder Transaction → System Server
|---- request ----| |---- processing ----| |---- reply ----|
Binder 区域显示了跨进程调用的完整时间线,可以直观看出哪个方向的延迟最大。
5.3 关键快捷键
| 快捷键 | 功能 |
|---|---|
W/S |
放大/缩小 |
A/D |
左右平移 |
M |
标记当前选择的时间范围 |
F |
聚焦当前选择 |
1-4 |
切换不同细节级别 |
? |
显示所有快捷键 |
六、API 打点:在代码中埋点
6.1 Java 层
源码路径 :frameworks/base/core/java/android/os/Trace.java
java
public final class Trace {
// ...
public static final long TRACE_TAG_APP = 1L << 12;
private static final int MAX_SECTION_NAME_LEN = 127;
// 通过 native 方法写入 trace_marker
private static native void nativeTraceBegin(long tag, String name);
private static native void nativeTraceEnd(long tag);
// App 开发者使用的便捷 API
public static void beginSection(String sectionName) {
if (isTagEnabled(TRACE_TAG_APP)) {
if (sectionName.length() > MAX_SECTION_NAME_LEN) {
throw new IllegalArgumentException("sectionName is too long");
}
nativeTraceBegin(TRACE_TAG_APP, sectionName);
}
}
public static void endSection() {
if (isTagEnabled(TRACE_TAG_APP)) {
nativeTraceEnd(TRACE_TAG_APP);
}
}
}
关键设计 :
beginSection/endSection使用TRACE_TAG_APPtag,需要 atrace 命令中加-a <package>参数才能采集到。MAX_SECTION_NAME_LEN限制名称最长 127 字符。
使用示例:
java
import android.os.Trace;
public void doHeavyWork() {
Trace.beginSection("doHeavyWork");
try {
processData();
Trace.beginSection("processData_sub");
// 子步骤
Trace.endSection();
} finally {
Trace.endSection();
}
}
6.2 Native 层
cpp
#include <utils/Trace.h>
void doHeavyWork() {
ATRACE_BEGIN("doHeavyWork");
// 耗时操作
ATRACE_END();
}
七、结合异常定位的实战案例
案例 1:UI 卡顿分析
问题:用户反馈 App 滑动卡顿
分析步骤:
1. 抓取 systrace:systrace.py -t 5 -o trace.html sched freq gfx input view
2. 打开 trace.html,找到 App 的 main 线程
3. 观察 VSYNC 信号和渲染完成时间
4. 发现:某个帧的渲染耗时 80ms(远超 16.7ms)
5. 放大该帧时间范围,发现 RenderThread 在等待 CPU
6. 查看 CPU 调度区域,发现同时有后台线程在密集计算
7. 结论:后台任务与渲染线程抢 CPU,导致掉帧
8. 修复:将后台任务移到 cpuset 后台组,降低优先级
案例 2:ANR 前兆分析
问题:App 偶尔 ANR,但 traces.txt 中主线程没有明显阻塞
分析步骤:
1. 抓取 systrace + CPU 调度
2. 发现 ANR 发生前,所有 CPU 核心 100% 占用
3. 主线程处于 Runnable 状态(等待 CPU),而非 Blocked
4. 结论:系统整体 CPU 过载,主线程分不到 CPU 时间片
5. 修复:优化后台任务,降低 CPU 使用率
案例 3:System Server 慢
问题:系统服务调用响应慢
分析步骤:
1. systrace 中开启 binder 类别
2. 观察 Binder 调用链路
3. 发现 AMS 的某个方法耗时 500ms
4. 进一步查看 AMS 线程的 trace
5. 发现 AMS 在等待 PackageManager 的锁
6. 结论:PackageManager 持有锁时间过长,导致连锁反应
八、使用技巧与最佳实践
8.1 选择合适的 trace 类别
bash
# 性能分析(最小集)
sched freq
# UI 卡顿
sched freq gfx input view
# Binder 问题
sched freq binder
# 启动速度
sched freq am wm
# 内存问题
sched freq memreclaim
# 全盘分析(注意数据量)
sched freq gfx input view wm am binder disk
8.2 减小 trace 开销
- 限制追踪时间:-t 5(5 秒通常足够)
- 只追踪必要的 App:-a com.example.app
- 使用压缩格式:-z
- 减少不相关的 category
- 在非关键路径上减少 Trace.beginSection/endSection 调用
8.3 脚本化收集
bash
#!/bin/bash
# 一键收集异常现场的 trace 数据
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
# 1. 抓取 atrace
adb shell atrace -t 10 -z sched freq gfx input view binder \
> trace_${TIMESTAMP}.trace
# 2. 导出 logcat
adb logcat -d > logcat_${TIMESTAMP}.txt
# 3. 导出 dmesg
adb shell dmesg > dmesg_${TIMESTAMP}.txt
# 4. 导出 ANR traces
adb pull /data/anr/traces.txt traces_${TIMESTAMP}.txt
echo "All logs collected with timestamp: ${TIMESTAMP}"
九、总结
-
Trace 是"事前监控"能力:不等异常发生,而是持续观察系统运行状态,发现性能瓶颈。
-
三层架构:ftrace(内核)→ atrace(Android 封装)→ systrace(可视化分析)。
-
trace_marker 是用户态与内核态的桥梁 :通过
ATrace_begin/end或Trace.beginSection/endSection将应用级打点注入内核 trace buffer。 -
systrace HTML 报告是核心分析工具:按时间轴展示 CPU 调度、线程状态、Binder 调用、渲染管线。
-
结合异常定位:systrace 可以补充 ANR/Watchdog 分析中缺失的"时间线上下文",帮助判断是锁阻塞还是 CPU 竞争。
下一篇将介绍 Android 日志系统全景------从 logcat 到 bugreport。注意:新一代 trace 工具 perfetto 在 Android 10+ 中成为默认,AOSP 7 以 atrace/systrace 为主。
本文基于 AOSP 7(Android Nougat)源码编写。