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 | 优先级反转、核间迁移、降频 |
| Binder | binder_transaction, binder_lock | 跨进程同步卡住、长时间 Binder 调用 |
| SurfaceFlinger | onFrameAvailable, syncForNextFrame | 帧率不稳、Buffer 耗尽 |
| 输入系统 | InputDispatcher::dispatch | 输入延迟、Window 未响应 |
| App 内 | 自定义 begin/end 标签 | 关键函数耗时、过度绘制 |
记得有次排查一个三方浏览器内存泄漏问题,我盯着 Binder 段看了半天,最后发现是 WebView 回传图片信息时,底层用了同步 Binder 调用,每次要等上百毫秒。你要是只测内存而不看 Systrace,根本不会往这个方向想。
2.3 历史上踩过的坑
我刚开始用 Systrace 的时候,犯过两个挺典型的错误。先说说第一个:
我曾经为了看一个渲染问题,直接跑
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 并不是一个“单一工具”,它更像是一套生态——从内核到应用层都有对应的埋点,最终统一汇到 atrace 里。Perfetto 在概念模型上完全继承了这套思想,只是实现更激进。
2.5 什么样的问题值得用 Systrace?
我个人的经验是,三类场景几乎必用:
- 卡顿 / 掉帧:看到 Frame miss 或者 Janky frames 报告,不要凭感觉改代码——先抓 trace 看 Choreographer 回调里哪个阶段花了超过 16ms。
- 启动耗时:冷启动、热启动、温启动,trace 能在时间轴上明确标出 Application 到 Activity 的各段耗时占比,比手动打点准一个数量级。
- 后台任务异常:比如某次定位服务把整个系统搞卡了。用 Systrace 加上
wm、am、power标签,能看到 WakeLock 持有时间、Alarm 触发频率,问题定位快很多。
当然,也不是所有问题都靠它。你要是查网络请求的具体字节流、或者内存中某个对象的引用链,那还是得用抓包工具和 Memory Profiler。Systrace 擅长的是“时间维度的系统协作问题”,这个定位从一开始就没变过。
Systrace 的价值不在于它有多少功能,而在于它帮你把一个“感觉卡”的主观体验,变成了“580ms 的 Binder 同步等待 + 3 次 Vsync 错过”的客观数据。
嗯,这一节就先到这儿。记住这个基本脉络,后面我们上手抓 trace 的时候,你会更清楚每一步在做什么。