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的工作流程可以拆成三步:

  1. 内核层打点:依赖Linux内核的Ftrace机制,在内核关键路径上埋下“探针”。比如CPU调度、中断处理、系统调用等。
  2. 用户层打点:Android框架层在关键方法(如Choreographer.doFrame、View.onDraw)里也加了Trace标记。
  3. 数据收集与聚合: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界面,教你怎么读懂那些花花绿绿的色块和线条。到时候你会发现,原来性能问题就藏在那些“空隙”和“重叠”里。