Systrace 与 Perfetto
问题
Systrace 和 Perfetto 是什么?如何用它们分析性能问题?
答案
Systrace vs Perfetto
| 特性 | Systrace | Perfetto |
|---|---|---|
| 状态 | 已停止维护 | 官方推荐 |
| 数据源 | ftrace | ftrace + 更多 |
| UI | Chrome HTML 页面 | ui.perfetto.dev |
| 自定义 Trace | Trace.beginSection() | 同上 |
| 分析能力 | 基础 | SQL 查询、指标计算 |
抓取 Trace
# Perfetto 命令行
adb shell perfetto \
-c - --txt \
-o /data/misc/perfetto-traces/trace.pb \
<<EOF
buffers: {
size_kb: 63488
fill_policy: RING_BUFFER
}
duration_ms: 10000
data_sources: {
config {
name: "linux.ftrace"
ftrace_config {
ftrace_events: "sched/sched_switch"
ftrace_events: "power/suspend_resume"
atrace_categories: "gfx"
atrace_categories: "view"
atrace_categories: "wm"
atrace_categories: "am"
atrace_apps: "com.example.app"
}
}
}
EOF
# 拉取并在浏览器打开
adb pull /data/misc/perfetto-traces/trace.pb
# 访问 https://ui.perfetto.dev 打开文件
自定义 Trace 点
import androidx.tracing.trace
// 方式 1:Kotlin 扩展函数(推荐)
fun loadData() = trace("loadData") {
val users = repository.getUsers()
processUsers(users)
}
// 方式 2:手动 begin/end
fun loadData() {
Trace.beginSection("loadData")
try {
val users = repository.getUsers()
processUsers(users)
} finally {
Trace.endSection()
}
}
// 异步 Trace
Trace.beginAsyncSection("network_request", requestId)
// ... 异步操作
Trace.endAsyncSection("network_request", requestId)
Perfetto 分析要点
在 Perfetto UI 中重点关注:
- Choreographer#doFrame:查看每帧耗时,超过 16ms 的帧标红
- 主线程 Slice:查看主线程在做什么
- Binder 调用:跨进程调用是否耗时
- CPU 调度:主线程是否被抢占
-- Perfetto SQL 查询示例:找出超过 16ms 的帧
SELECT ts, dur, name
FROM slice
WHERE name = 'Choreographer#doFrame'
AND dur > 16000000 -- 16ms in nanoseconds
ORDER BY dur DESC
常见面试问题
Q1: 如何用 Perfetto 定位卡顿原因?
答案:
- 在 Perfetto 时间轴找到掉帧区域(红色标记)
- 放大查看该帧的主线程 Slice
- 分析耗时最长的 Section(可能是 layout、measure、或自定义 Trace)
- 如果主线程在等待锁,检查持有锁的线程在做什么
- 如果 CPU 利用率低但帧慢,可能是被其他进程抢占
Q2: 自定义 Trace 对性能有影响吗?
答案:
Trace.beginSection() 在未抓取 trace 时几乎零开销(内部检查一个标志位即返回)。抓取时有微小开销(写入 ftrace buffer),可忽略。Release 包中保留自定义 Trace 是安全的。