2、Systrace 基础:Systrace的工作原理、抓取方法、Ftrace机制、TraceView对比
说到Android性能优化,Systrace绝对是我最常用的工具,没有之一。很多同学一上来就怼TraceView或者Profiler,其实走错了路。我个人习惯是:先上Systrace看全局,再决定要不要用其他工具深挖。今天我们就把它彻底聊透。
2.1 Systrace 到底是什么?
Systrace,说白了就是Android系统级别的“行车记录仪”。它不关心你的代码具体在算什么东西,它只关心“谁在什么时候占用了CPU”、“系统有没有卡顿”、“VSync有没有准时到达”。
它的核心价值在于:帮你从全局视角发现性能瓶颈。你想想看,一个App卡顿,可能是主线程干活太多,也可能是GPU负载过高,还可能是Binder通信堵住了。Systrace能把这些信息全部呈现在一条时间轴上。
核心定位:Systrace 是“宏观”工具,不是“微观”工具。它告诉你“哪里有问题”,但不一定告诉你“为什么有问题”。
2.2 工作原理:从用户态到内核态
我刚开始用Systrace的时候,总觉得它像个黑盒子。后来研究了一下它的原理,其实并不复杂。
Systrace的工作流程可以拆成三步:
- 内核层打点:依赖Linux内核的Ftrace机制,在内核关键路径上埋下“探针”。比如CPU调度、中断处理、系统调用等。
- 用户层打点:Android框架层在关键方法(如Choreographer.doFrame、View.onDraw)里也加了Trace标记。
- 数据收集与聚合:systrace命令会同时从内核和用户空间收集这些标记,合并成一份统一的trace文件。
嗯,这里要注意:Systrace本身不产生数据,它只是数据的搬运工。真正的数据来源是内核的Ftrace和用户空间的atrace。
2.3 Ftrace机制:Systrace的“地基”
Ftrace是Linux内核自带的一个跟踪工具。我在项目中遇到过一个问题:App滑动卡顿,但主线程看起来并不忙。后来用Systrace一看,发现是内核频繁在做CPU频率调整。这就是Ftrace的功劳。
Ftrace的核心能力包括:
- function tracer:跟踪内核函数的调用情况
- sched_switch:记录CPU的调度切换,谁切走了,谁切进来了
- irq_handler_entry/exit:记录中断处理开始和结束
- cpu_frequency:记录CPU频率变化
你可以把Ftrace想象成内核的“黑匣子”。它默认是关闭的,只有当你通过Systrace或者直接操作/sys/kernel/debug/tracing目录时,它才会开始记录。
小技巧:如果你在Systrace里看到“CPU 0”长时间处于idle状态,但其他核忙得要死,这往往说明你的线程亲和性设置有问题。我曾经在一个多线程下载器里踩过这个坑。
2.4 抓取方法:三种姿势任你选
Systrace的抓取方法,我总结了三种。不同场景用不同方法。
方法一:命令行抓取(最推荐)
我个人最常用的是命令行方式。为什么?因为可控性最强,可以指定抓取时长、缓冲区大小、要跟踪的类别。
# 基本命令
python systrace.py -t 5 -o mytrace.html
# 指定类别,抓取10秒
python systrace.py -t 10 -o trace.html gfx input view sched freq
# 抓取所有类别(慎用,数据量很大)
python systrace.py -t 5 -o full_trace.html --all-categories
常用的类别标签:
| 类别 | 说明 | 适用场景 |
|---|---|---|
| gfx | 图形渲染相关 | 卡顿、掉帧 |
| input | 输入事件处理 | 触摸响应慢 |
| view | View绘制流程 | 布局过度绘制 |
| sched | CPU调度信息 | 线程竞争、CPU饥饿 |
| freq | CPU频率变化 | 功耗、温控降频 |
方法二:Android Studio Profiler(适合快速查看)
如果你不想记命令,Android Studio自带的Profiler也集成了Systrace功能。点击CPU栏的“Record”,选择“Trace System Calls”即可。不过说实话,这种方式抓出来的trace信息量不如命令行全,适合快速验证。
方法三:设备端直接抓取(适合无PC场景)
有些时候你没法连电脑,比如在客户现场演示。这时候可以用设备端的atrace命令:
# 在adb shell里执行
atrace --async_start -b 4096 gfx input view sched
# 做一些操作...
# 停止并导出
atrace --async_stop -o /data/local/tmp/trace_output
adb pull /data/local/tmp/trace_output ./mytrace.atrace
注意:设备端抓取的.atrace文件不能直接打开,需要先用systrace.py的--from-file参数转换成HTML格式。我曾经因为这个折腾了半天才发现。
2.5 Systrace vs TraceView:到底该用谁?
这个问题我经常被问到。我的回答很简单:先Systrace,再TraceView。
它们俩的定位完全不同:
| 对比维度 | Systrace | TraceView |
|---|---|---|
| 观察粒度 | 系统级、进程级 | 方法级、函数级 |
| 开销 | 极低(<5%) | 较高(10%-50%) |
| 适用阶段 | 定位问题区域 | 深挖具体函数 |
| 数据呈现 | 时间轴、可视化 | 调用栈、耗时统计 |
| 能否看内核 | 能(通过Ftrace) | 不能 |
举个例子你就明白了。假设你的App启动慢:
- 用Systrace:一眼就能看到Application.onCreate里有个大块空白,说明有耗时操作。还能看到ContentProvider初始化占用了主线程。
- 用TraceView:能精确告诉你onCreate里哪个方法花了多少毫秒,比如findViewById占了200ms,SharedPreferences初始化占了150ms。
所以我的工作流是:先用Systrace找到“哪个阶段”有问题,再用TraceView或Method Tracing去挖“哪个方法”有问题。千万别反过来,否则你会被海量的方法调用信息淹死。
一句话总结:Systrace是“望远镜”,帮你看到森林;TraceView是“显微镜”,帮你看到树叶。先看森林,再看树叶。
2.6 避坑指南
最后分享几个我踩过的坑:
- 缓冲区太小:默认的缓冲区只有2048KB,如果你抓取时间长或者类别多,很容易丢数据。我一般设到8192或16384。
- 不要在生产环境长时间抓取:虽然Systrace开销低,但长时间开启Ftrace还是会增加功耗。我一般控制在5-10秒。
- 注意Android版本差异:Android 10之后,Systrace的抓取方式有变化,部分旧命令已经废弃。建议用最新的platform-tools。
- 多核设备要关注CPU迁移:有时候你的线程在Core 0上跑得好好的,突然被迁移到Core 4上,如果Core 4正在降频,性能就会骤降。Systrace的sched类别能清楚看到这个迁移过程。
好了,Systrace的基础就聊到这里。下一章我们会深入Systrace的UI界面,教你怎么读懂那些花花绿绿的色块和线条。到时候你会发现,原来性能问题就藏在那些“空隙”和“重叠”里。