1. Perfetto 简介:是什么、能做什么、为什么选择它?

做性能优化的朋友,肯定对“卡顿”这个词不陌生。但能说清楚“到底哪里卡”的人,其实不多。我见过太多团队靠猜、靠打 log、靠肉眼盯屏幕来定位帧率抖动——结果折腾几天,连根因都没摸到。

Perfetto 就是来解决这个问题的。它不是什么新鲜玩意儿,但确实好用。说白了,它是 Google 开源的性能追踪工具,专为 Android 和 Linux 设计。最早它叫 Systrace,后来团队重写了一遍,才变成今天的 Perfetto。我参与过的项目里,几乎每个线上帧率抖动问题,最后都是靠它找到真凶的。

一句话定义: Perfetto = 高性能 + 全平台 + 可视化 的追踪框架。它能以微秒级精度记录系统调度、CPU执行、GPU渲染、内存分配、帧渲染流水等所有你想看的信息。

1.1 它能做什么?

我习惯把 Perfetto 的能力分成三层,你感受一下:

  • 抓底层调度 —— 哪个线程在跑?跑在哪个 CPU 核上?被谁抢占了?调度延迟多少?这些它全知道。
  • 抓上层帧率 —— 从 App 的 Choreographer 回调,到 SurfaceFlinger 合成,再到屏幕刷新的每个 vsync 信号,都被精确打点。
  • 抓内存/GPU/功耗 —— 虽然这章我们只聊帧率,但它在其他领域的追踪能力也非常强。

直接看一张功能对比表:

监控维度 Perfetto 传统 logcat 自定义埋点
调度切换 ✅ 纳秒级 ❌ 不可见 ❌ 开销大
帧渲染时序 ✅ 完整流水线 ❌ 只有近似时间 ⚠️ 依赖手动插桩
CPU/GPU 对齐 ✅ 自动关联 ❌ ❌
可视化界面 ✅ 交互式瀑布图 ❌ 纯文本 需自建
对性能影响 ≤5% ≥20% 高频时 取决于插桩

看到这里你可能会问:那它跟 Systrace 有什么区别?嗯,它其实就是下一代 Systrace。 更快的采集引擎、更紧凑的二进制压缩、更完善的前端分析工具。我记得刚升级到 Perfetto 时,光是 trace 文件大小就比旧版缩小了 60%,但信息量反而更多了。

1.2 为什么选它来监控帧率抖动?

原因很直接:帧率抖动的根因往往不在 App 主线程本身,而在于后台调度。

举个例子。你 App 的 onDraw 明明只跑了 2ms,但帧率却掉到了 20fps。这时候光看 App 内部数据完全没用——因为问题出在 调度延迟。可能是某个后台线程抢占了 CPU,导致 SurfaceFlinger 拿不到时间片;也可能是 CPU 降频了,vsync 信号被推迟。这些信息,只有 Perfetto 能给你。

我曾经踩过的一个坑: 用 Android Studio 的 Profiler 看帧率,发现掉帧,但 CPU 占用率很低。当时百思不得其解。后来用 Perfetto 一抓,才发现有个系统服务(servicemanager)在每帧中间插入了一次 8ms 的 binder 调用——直接抢占了渲染线程的执行窗口。这事如果只靠普通工具,根本定位不到。

Perfetto 的核心优势可以归纳为三点:

  • 调度与渲染的关联性 —— 同一张时间轴上,你可以同时看到:“这个线程在等锁” 和 “SurfaceFlinger 因为没有缓冲而错过 vsync”。这种跨组件串联,是其他工具做不到的。
  • 低开销 —— 你想想看,线上环境有些问题必须在真机上复现,加 log 会影响时序,用 profiler 可能直接改变行为。Perfetto 走 ftrace + 内核缓冲区,对系统侵入极小(实测 <3% CPU),可以放心让用户跑几秒采集。
  • 丰富的分析视角 —— 从线程状态(Running/Runnable/Sleeping)到 CPU 频率变化,从 I/O 等待到锁竞争,每个维度都有对应的可视化视图。我每次拿到一个卡顿 trace,第一步就是切换成“调度视图”——几乎每次都能一眼看到问题线程。

1.3 背后的核心逻辑

要理解 Perfetto 为什么能搞定帧率抖动,你得先明白它的架构设计。我画了张图,把几个关键模块串起来:

Perfetto 帧率抖动监控核心架构 ftrace 内核追踪 CPU调度 / 中断 / 系统调用 atrace 用户态日志 SurfaceFlinger / Choreographer 自定义 Track/Perfetto SDK App层 vsync / 帧渲染标记 Perfetto 守护进程 (traced) 环形缓冲区 → 压缩写入文件 (/data/misc/perfetto-traces) Perfetto UI / traceconv 解析器 SQLite 结构化 → 按时间轴排列成多行 (tracks) 帧率抖动分析视图 调度延迟虚线 / 掉帧三角形 / 线程状态着色 / GPU pipeline 1 掉帧要点 调度延迟 数据采集层 存储层 解析与可视化层 分析应用层

图里这几个模块,我简单解析一下:

  • 采集层:ftrace 负责内核调度事件,atrace 负责 Android 服务(SurfaceFlinger、Choreographer)的用户态 trace,Perfetto SDK 可以让你在 App 里自定义打点。三者汇入同一个时间轴,保证所有数据可对齐。
  • 存储层:traced 守护进程以极低的开销将数据写入环形缓冲区,停止采集时再序列化成二进制 protobuf 文件。我记得它的写入策略是批量 flush,避免每次事件都触发 I/O。
  • 解析层:无论是用 web UI 还是命令行 traceconv,都会把二进制解析成 SQLite 数据库。所有事件都带时间戳、线程 ID、CPU 核等信息。你可以直接用 SQL 做定制分析——这个能力在问题排查时非常好用。
  • 分析层:针对帧率抖动,UI 会高亮显示“掉帧三角形”(高于 16ms 的帧)和调度延迟区间。你甚至能看到某个帧等待的 buffer 是在哪个线程里被阻塞的。

我的个人习惯: 每次拿到 trace 文件,不要急着看整个图。先拉一个 2~3 秒的掉帧段,然后只看 SurfaceFlinger 的“onFrameAvailable”和 Choreographer 的“doFrame”两个 track。如果它们之间出现了明显的延迟 gap,基本就是调度问题。这时候再去看对应的 CPU 占用和线程状态,必出结果。

1.4 需要克服的“第一关”

说实话,Perfetto 不是完全没有学习成本。我刚开始用的时候也被它的参数列表弄晕过。比如采集命令:

# 最简单的帧率追踪(需要 root 或 adb shell 权限)
adb shell perfetto \
  -c - \
  --txt \
  -o /data/misc/perfetto-traces/trace.perfetto-trace \
  <<EOF
duration_ms: 5000
buffers: {
  size_kb: 524288
}
data_sources: {
  config {
    name: "linux.ftrace"
    ftrace_config {
      ftrace_events: "sched/sched_switch"
      ftrace_events: "power/cpu_frequency"
      atrace_apps: "com.example.myapp"
    }
  }
}
EOF

这条命令从内核调度事件、CPU 频率变化、应用 atrace 三个方面采集 5 秒数据。第一次跑可能会报权限错误——我记得在 Android 12 之前还需要单独打开 atrace 的权限。不过别担心,我们第二章会专门讲环境配置和常见坑。