2. Systrace 简介与历史背景

Systrace 这个名字,搞过 Android 性能优化的应该都不陌生。我第一次接触它大概是七八年前,当时被一个 UI 掉帧问题折磨了两周——用 Log 打点打了上千行,还是复现不了。后来有个老同事丢过来一句:“你去抓个 Systrace 看看。” 嗯,就是那一把,让我彻底入了门。

说白了,Systrace 就是一个系统级的 trace 工具。它靠在内核和框架层埋设静态插桩,把 CPU 调度、Binder 通信、SurfaceFlinger 渲染、输入事件等关键路径记录下来,然后生成一张按时间轴排列的“系统心电图”。你不需要猜,一眼就能看出哪段代码抢占了主线程,哪里出现了调度延迟。

2.1 它从哪里来?

Systrace 最早出现在 Android 4.1(Jelly Bean)时期,目的是配合 Project Butter 解决界面卡顿问题。当时 Google 发现很多掉帧不是 App 代码慢,而是系统组件之间的协同出了问题——比如 CPU 频率没及时拉高,GPU 渲染完却因为 Buffer 等待错过了 Vsync。

那时候的 Systrace 还叫 systrace.py,得在 PC 上用命令行跑。后来被整合进 Android Studio 的 Profiler,再到今天大家更常用的 Perfetto(它的下一代),底层原理其实没变。我身边不少人觉得 Perfetto 一出来就扔掉 Systrace,其实没那么——很多老设备 kernel 版本低,Perfetto 的 ftrace 驱动不支持,还得靠 Systrace 救场。

2.2 核心设计思路

Systrace 的工作机制,你可以理解为三件事:

  • 打点埋桩:内核态用 ftrace,用户态用自定义的 Trace API(也就是 android.os.Trace 系列)。
  • 集中收集:通过 atrace 守护进程把各个模块的 log 聚合到一块。
  • 可视化呈现:基于 Chrome 的 about:tracing 渲染成层级时间轴。

它的数据来源非常广,我整理了一张表格,你大概扫一眼就能知道它能看什么:

模块核心事件典型问题
CPU 调度sched_switch, cpu_frequency优先级反转、核间迁移、降频
Binderbinder_transaction, binder_lock跨进程同步卡住、长时间 Binder 调用
SurfaceFlingeronFrameAvailable, syncForNextFrame帧率不稳、Buffer 耗尽
输入系统InputDispatcher::dispatch输入延迟、Window 未响应
App 内自定义 begin/end 标签关键函数耗时、过度绘制

记得有次排查一个三方浏览器内存泄漏问题,我盯着 Binder 段看了半天,最后发现是 WebView 回传图片信息时,底层用了同步 Binder 调用,每次要等上百毫秒。你要是只测内存而不看 Systrace,根本不会往这个方向想。

2.3 历史上踩过的坑

我刚开始用 Systrace 的时候,犯过两个挺典型的错误。先说说第一个:

避坑:一上来就抓全量 trace
我曾经为了看一个渲染问题,直接跑 systrace.py -t 5 没加任何分类过滤。结果抓出来一个上百兆的 HTML 文件,浏览器打开就卡死,关键信息淹没了。后来才学乖——先只盯 gfx、input、view 三个标签,确认问题域后再扩大范围。

第二个坑是时间选得太长。Systrace 的缓冲区有限,抓 10 秒以上很可能会在关键时刻被覆写。我现在一般先抓 5 秒,如果问题复现窗口比较长,就改用 --no-compress 把缓冲区扩到 8MB。

小技巧
如果你在测试机上没有 root,可以用 adb shell atrace --list_categories 看看当前系统支持哪些标签。有些定制 ROM 会砍掉部分 kernel 事件,提前确认能省不少时间。

2.4 从 Systrace 到 Perfetto 的演进

为什么后来 Perfetto 会替代 Systrace?说白了,是移动设备的硬件复杂度已经超过了过去的设计上限。

Systrace 有几个硬伤:

  • 数据格式是纯 HTML + 内嵌 JSON,超 100MB 后浏览器几乎不能交互。
  • 只支持同步 trace,Async 类型的跨度(比如网络请求)得靠 Hack。
  • Multi-process 场景下,很难把不同进程的线程按逻辑分组。

Perfetto 在 2018 年作为独立项目开源,彻底重写了 trace 流水线:

  • 采用 protobuf 二进制存储,解析速度比 JSON 快几十倍。
  • 原生支持 Trace Processor,你能用 SQL 查任意时间段的统计(比如“过去 3 秒哪个线程跑得最久”)。
  • 跨进程 View 用 Track 概念解决,不再傻乎乎按 PID 摊开。

但有一点要注意:现在 Android Studio 内建的 Profiler 底层已经切换到 Perfetto 了,不过很多线上问题依然要用 adb shell perfetto 命令行抓。我习惯把它们俩当互补工具——日常调试用 Perfetto 看细粒度数据,做回归对比时用 Systrace 脚本批量生成报告,因为 HTML 文件用浏览器打开就能分享,对协作更友好。

下面这张图可以帮你把 Systrace 的前世今生串起来:

Systrace 知识脉络 Systrace 内核 ftrace Framework 插桩 App Trace API atrace 守护进程 HTML + JSON (Systrace) Protobuf (Perfetto) 老设备 & 快速分享 SQL 分析 & 高吞吐 全新架构

这张图里你应该注意到了:Systrace 并不是一个“单一工具”,它更像是一套生态——从内核到应用层都有对应的埋点,最终统一汇到 atrace 里。Perfetto 在概念模型上完全继承了这套思想,只是实现更激进。

2.5 什么样的问题值得用 Systrace?

我个人的经验是,三类场景几乎必用:

  1. 卡顿 / 掉帧:看到 Frame miss 或者 Janky frames 报告,不要凭感觉改代码——先抓 trace 看 Choreographer 回调里哪个阶段花了超过 16ms。
  2. 启动耗时:冷启动、热启动、温启动,trace 能在时间轴上明确标出 Application 到 Activity 的各段耗时占比,比手动打点准一个数量级。
  3. 后台任务异常:比如某次定位服务把整个系统搞卡了。用 Systrace 加上 wm、am、power 标签,能看到 WakeLock 持有时间、Alarm 触发频率,问题定位快很多。

当然,也不是所有问题都靠它。你要是查网络请求的具体字节流、或者内存中某个对象的引用链,那还是得用抓包工具和 Memory Profiler。Systrace 擅长的是“时间维度的系统协作问题”,这个定位从一开始就没变过。

核心一句话
Systrace 的价值不在于它有多少功能,而在于它帮你把一个“感觉卡”的主观体验,变成了“580ms 的 Binder 同步等待 + 3 次 Vsync 错过”的客观数据。

嗯,这一节就先到这儿。记住这个基本脉络,后面我们上手抓 trace 的时候,你会更清楚每一步在做什么。