8、Perfetto新一代工具:Perfetto架构、与Systrace对比、SQL分析、可视化

说到Android系统调试,Perfetto绝对是个绕不开的话题。我记得几年前大家还在用Systrace抓trace,那时候抓个长一点的场景,动不动就丢帧、卡死,甚至直接OOM。后来Google推出了Perfetto,我第一时间就切过去了。说实话,用完之后就再也没碰过Systrace——不是怀旧,是真的回不去了。

Perfetto的架构:到底长什么样?

Perfetto的架构,说白了就是三个核心组件:数据源(Producers)、服务端(Service)、消费者(Consumers)。你想想看,整个系统就像一条流水线:Producers负责采集各种数据,Service负责把这些数据整理好存起来,Consumers负责把数据拉出来给你看。

我个人习惯把Perfetto的架构画成下面这张图,这样理解起来更直观:

Perfetto 核心架构图 Producers(数据源) ftrace(内核跟踪) atrace(Android Trace) heapprofd(内存分析) perf(性能计数器) Service(服务端) Trace Buffer 数据序列化 压缩与存储 权限管理 Consumers(消费者) Perfetto UI(可视化) trace_processor(SQL分析) 命令行工具 自定义脚本 数据流方向:Producers → Service → Consumers 支持 ftrace、atrace、heapprofd、perf 等多种数据源

这里有个关键点:Perfetto的Service是运行在系统中的一个守护进程(traced),它负责接收所有Producers发来的数据。我刚开始用的时候,一直以为Perfetto是直接写文件的,后来看了源码才发现,它其实是先写到共享内存缓冲区,再异步落盘。这样做的好处很明显——对性能的影响极小。

Perfetto vs Systrace:到底强在哪?

这个问题我经常被问到。直接说结论:Systrace能做的,Perfetto都能做;Systrace做不了的,Perfetto也能做。

我整理了一个对比表格,你一看就明白了:

对比维度 Systrace Perfetto
数据源 仅支持 ftrace + atrace ftrace、atrace、heapprofd、perf、logcat 等
内存分析 不支持 支持 native heap 和 Java heap 采样
SQL 分析 不支持 内置 trace_processor,支持 SQL 查询
长时抓取 容易 OOM,超过 10 秒就危险 支持数十分钟甚至数小时
可视化 基于 Chrome 的旧版 UI 全新 UI,支持缩放、搜索、标记
跨平台 仅限 Android Android + Linux + ChromeOS
协议 基于文本协议 基于 protobuf,更高效
我的个人建议:如果你还在用 Systrace,赶紧切换到 Perfetto。别犹豫。Systrace 在 Android 10 之后就已经被标记为 deprecated 了,Google 内部早就不维护了。

SQL分析:这才是Perfetto的杀手锏

Perfetto最让我惊艳的功能,就是它的SQL分析能力。你想想看,以前用Systrace的时候,想查某个进程的某个时间段内发生了多少次Binder调用,只能肉眼盯着看,或者写脚本解析文本。现在呢?一条SQL搞定。

Perfetto内置了一个叫trace_processor的组件,它会把trace数据导入到一个SQLite数据库中。然后你就可以用标准SQL来查询了。我给大家看几个我常用的查询:

查询某个进程的CPU使用率

SELECT 
  ts / 1e6 AS time_ms,
  dur / 1e3 AS dur_us,
  cpu,
  end_state
FROM sched
WHERE utid = (
  SELECT utid FROM thread WHERE name = 'com.example.app'
)
ORDER BY ts
LIMIT 20;

查找所有Binder事务的耗时

SELECT 
  process.name AS process_name,
  thread.name AS thread_name,
  slice.name AS binder_name,
  dur / 1e6 AS dur_ms
FROM slice
JOIN thread_track ON slice.track_id = thread_track.id
JOIN thread ON thread_track.utid = thread.utid
JOIN process ON thread.upid = process.upid
WHERE slice.name GLOB 'binder*'
  AND dur > 1000000  -- 耗时超过1ms的
ORDER BY dur DESC;
小技巧:我个人习惯在抓trace的时候加上 --txt 参数,这样生成的trace文件可以直接用文本编辑器打开查看。虽然文件会大一些,但调试的时候方便很多。

可视化:不只是好看

Perfetto的UI界面,说实话,第一次打开的时候我愣了一下——这玩意儿也太精致了吧?但用久了你会发现,它不只是好看,是真的好用。

几个我觉得特别实用的功能:

  • 无限缩放:从毫秒级到秒级,随便拖拽缩放,不会卡死。我记得Systrace在缩放的时候经常白屏,Perfetto完全没这个问题。
  • 搜索高亮:直接搜函数名、进程名、标签,所有匹配项都会高亮显示。我曾经在分析一个卡顿问题时,直接搜"Choreographer",一秒定位到丢帧位置。
  • 标记与注释:可以在时间轴上添加标记,方便团队协作。我经常在分析报告里直接截图带标记的trace,沟通效率高很多。
  • 多track联动:CPU、内存、网络、GPU,所有track可以同步滚动。你想想看,当你在CPU track上看到一个高峰,切换到内存track看看是不是同时有GC发生,这种联动分析在Systrace里基本做不到。
避坑指南:我曾经在抓取长时间trace时,忘记设置文件大小上限,结果抓了半小时,生成了一个2GB的trace文件。Perfetto UI加载这个文件花了将近10分钟。所以建议抓trace时加上 --buffer-size 参数,比如 --buffer-size 64mb,够用就好。

实战:一条SQL揪出卡顿元凶

说个我真实遇到的案例。有一次,一个App在启动时总是卡顿,大概卡了2-3秒。用Perfetto抓了trace之后,我直接跑了一条SQL:

SELECT 
  name,
  dur / 1e6 AS dur_ms,
  thread.name AS thread_name
FROM slice
JOIN thread_track ON slice.track_id = thread_track.id
JOIN thread ON thread_track.utid = thread.utid
WHERE dur > 5000000  -- 耗时超过5ms的
ORDER BY dur DESC
LIMIT 10;

结果出来,排第一的是一个叫SharedPreferences.load的操作,耗时1.2秒。嗯,问题找到了——主线程直接读了一个很大的SharedPreferences文件。后来改成异步加载,卡顿就消失了。

你看,如果没有SQL分析,我可能得在UI上翻半天才能找到这个点。但一条SQL,10秒钟就定位到了问题。这就是Perfetto的魅力所在。


好了,关于Perfetto的架构、对比、SQL分析和可视化,我就讲这么多。记住一句话:Perfetto不是Systrace的替代品,而是降维打击。如果你还没开始用,今天就可以试试。抓一次trace,跑一条SQL,你会回来感谢我的。

公众号:蓝海资料掘金营,微信deep3321