第3章 Systrace 与 Perfetto:从入门到实战

3.1 先搞清楚——Systrace 到底在干嘛

搞性能优化,尤其是卡顿问题,我手里最常用的工具就是 Systrace。说实话,刚入行那会儿我觉得这玩意儿就是个时间轴彩条,后来才发现里面门道很多。

Systrace 的核心机制很简单——它在系统层埋了 hook,能记录每个线程在什么时间点干了什么事。你看到的那一条条彩色“面条”,其实都是 Android 系统里的关键组件:SurfaceFlinger、InputReader、Choreographer、RenderThread……

你想想看,如果应用掉帧了,我第一反应就是打开 Systrace 看“Choreographer#doFrame”这个 marker。如果它显示耗时超过 16ms,那肯定是有东西在主线程里搞鬼。我曾经在一个启动优化项目里,就是通过这个 marker 发现某个第三方 SDK 在 onCreate 里做了大量 IO,直接把首帧拖到了 500ms 开外。

核心指标: 每 16ms 一帧(60fps)是红线。Systrace 里看到绿色表示流畅,黄色警告,红色就是严重掉帧。

3.2 抓取第一个 Trace 文件

抓取 Trace 文件的方式有好几种,我平时用命令行居多,尤其是要带参数定制埋点的时候。

# 抓取 5 秒,包名 com.example.app,包括常见 tag
python systrace.py -t 5 -a com.example.app \
  -o trace_output.html \
  sched freq idle am wm gfx view input res

# Android 10+ 我推荐直接改用 Perfetto 的命令行
perfetto \
  --txt -c /data/misc/perfetto-configs/trace_config.pbtx \
  -o /data/misc/perfetto-traces/trace.perfetto-trace

嗯,这里要注意:抓取时长别太长。我之前图省事抓了 30 秒,结果生成的文件有几百 MB,解析起来跟蜗牛一样。对大多数卡顿分析来说,5~10 秒足够了——你只需要复现问题的那一瞬间。

工具输出格式适用场景
Systrace (Python).html快速抓取、< Android 10 的老设备
Perfetto 命令行.perfetto-trace复杂埋点、Android 10+ 新设备
Android Studio Profiler直接可视化开发调试阶段、逐帧分析

3.3 解读 Trace —— 从“看彩虹”到“找元凶”

第一次看 Systrace 的人大概率会懵:满屏的彩条,密密麻麻的缩写。我来告诉你一个简单的方法:只看主线程。

找到你的应用进程(包名那一行),展开主线程(通常叫“主线程 (tid=xxx)”)。重点关注这几个 marker:

  • Choreographer#doFrame —— 每一次帧绘制的起点,如果它下面有长条,那就是掉帧的源头。
  • Input —— 输入事件处理,如果这里耗时多,说明点按卡顿。
  • performTraversals —— 布局/绘制过程,Layout 阶段超时常见于复杂布局。
  • dispatchAppVisibility —— 冷启动/热启动时的界面显示。

我个人习惯是:先看顶部有没有红色的“jank”标记,如果有,直接双指放大到那段区域,然后盯着主线程看它在那段时间里到底在跑什么方法。如果你发现长条上写着“Layout”或者“measure”,那就是布局层级太深或重复测量。

小技巧: 按住 Shift 并框选一段区域,Systrace 下方会自动显示这一段的耗时和事件详情。用这个功能配合“Ftrace”标签,你能精确看到系统调度行为。

3.4 从 Systrace 到 Perfetto —— 为什么我建议你快迁移?

说句实话,Systrace 抓 .html 文件在 Android 10 之后官方已经停止维护了。Perfetto 才是现在的正统。它原生支持 Android、Linux,甚至 Chrome。数据格式从 HTML 变成 protobuf,解析效率高得多,而且能追踪 GPU、内存、电源等更多维度。

我在一个视频卡顿排查项目里,就是用 Perfetto 抓了 GPU 的“gpu.renderer”标签,才确认是硬件加速没开导致的渲染管线阻塞——这个信息旧版 Systrace 根本看不到。

# Perfetto 的配置文件示例(trace_config.pbtx)
buffers: {
  size_kb: 20480
  fill_policy: RING_BUFFER
}
data_sources: {
  config {
    name: "linux.ftrace"
    ftrace_config {
      ftrace_events: "power/suspend_resume"
      ftrace_events: "sched/sched_switch"
      ftrace_events: "workqueue/workqueue_execute_start"
      ftrace_events: "workqueue/workqueue_execute_end"
    }
  }
}
duration_ms: 5000
# 启动时自动 dump
file_write_period_ms: 5000

3.5 Trace 文件解读的进阶姿势

无论是 Systrace 还是 Perfetto,解读的思路始终是一样:找长条 → 定位线程 → 分析原因。我整理了一个常见问题对照表,帮你快速判断:

现象可能原因验证方式
Choreographer#doFrame 超 16ms主线程被阻塞(IO、锁、反射调用)查看 doFrame 下方是否有长条的函数调用
Input 事件与下一个 frame 间隔很大Input 线程被优先级反转或进程被降频看 sched 标签下的进程唤醒延迟
SurfaceFlinger 频繁领空App 提交缓冲区太慢,或 GPU 渲染跟不上检查 gpu_render 和 dequeueBuffer 的耗时
WakeLock 长时间亮着应用没及时释放锁,导致系统无法休眠搜索 "wake_lock" 标签下的持锁时长

我曾经遇到过一个非常隐蔽的问题:用户反馈正常滑动时偶尔卡一下,抓了十几次 Systrace 都没发现明显长条。后来在 Perfetto 里启用了“sched_switch”事件,发现是某个后台线程频繁持有高优先级锁,导致主线程被抢占调度——说白了就是锁的优先级继承没处理好。这种问题用单纯看 marker 的方式根本发现不了,必须得分析 CPU 调度上下文切换。

注意: Perfetto 的 .perfetto-trace 文件不能直接在浏览器里双击打开。你需要访问 ui.perfetto.dev(这是个本地离线工具)或者用 traceconv 转换为 HTML。我建议你先把整站缓存下来,然后直接用 Chrome 加载本地文件,速度比在线快得多。
Systrace 与 Perfetto 知识体系 Systrace 抓取 Perfetto 抓取 ADB / Studio trace.html .perfetto-trace profilers Systrace UI 分析 Perfetto UI 分析 traceconv 转换 主线程 Marker Choreographer, Input 系统调度分析 sched_switch, WakeLock GPU/内存追踪 gpu.renderer, dmabuf 定位卡顿原因 → 针对性修复

这张图我把分析路径分成了四层:抓取 → 格式 → 工具 → 分析视角。你实际排查的时候,从上往下走一遍,基本能把问题范围缩小。注意右侧的 GPU/内存追踪,这是 Perfetto 相比 Systrace 最强的部分——我最近一次项目里就是因为看到 GPU 渲染队列有空闲,才确定是 CPU 提交缓冲区太慢导致的帧率下降。

一句话总结: 无论用 Systrace 还是 Perfetto,核心永远是「先找到异常长条,再分析上下文」。不要被满屏的数据吓到,你只需要关注主线程和关键系统服务那几行就够了。

好了,这一章的内容差不多就是这些。工具用法很简单,但真正要变成“性能直觉”还需要多练。下次你再遇到卡顿反馈,不妨先冷静下来——打开 Systrace 或者 Perfetto,抓一段数据,然后按照我上面说的思路一步步往下查。只要你看得足够多,那些彩条在你眼里就会变成一张张“犯罪现场”的地图。