6、Systrace基础:Systrace原理、抓取方法、时间轴解读、关键指标

说到Android性能调试,Systrace是我最常用的工具之一。它就像系统的“心电图”,能让你一眼看出哪里卡了、哪里慢了。我刚开始接触Android系统时,面对一堆log完全摸不着头脑,直到学会了Systrace——嗯,那感觉就像打开了新世界的大门。

6.1 Systrace到底是什么?

说白了,Systrace是一个轻量级的性能分析工具。它不直接告诉你“这里慢了”,而是把系统里各个模块在做什么、花了多长时间,全部画在一条时间轴上。你一看就知道:哦,原来是这个线程在等那个线程,导致界面卡了。

它的核心原理其实不复杂:

  • 打点机制:系统关键路径上预埋了trace点(比如开始绘制、开始布局)
  • 内核ftrace:利用Linux内核的ftrace框架收集调度信息
  • 用户态atrace:收集App和Framework层的trace事件
  • 时间同步:所有事件都带时间戳,精确到微秒级

关键点:Systrace的开销非常小,生产环境也能用。我在线上问题排查时经常直接抓,基本不影响用户体验。

Systrace 数据采集架构 应用层 (App) Choreographer | View绘制 | 自定义Trace.beginSection() Framework层 (Java/Native) AMS | WMS | SurfaceFlinger | InputDispatcher 内核层 (Kernel) ftrace | CPU调度 | 中断 | 内存管理 | 设备驱动 输出: trace.html / trace.json atrace atrace ftrace

6.2 怎么抓Systrace?

抓取方法有好几种,我根据场景不同会换着用。这里给你总结最实用的三种:

方法一:命令行抓取(我最常用的)

适合开发机、自动化脚本。我个人习惯用这个,因为可控性最强。

# 基本抓取,持续5秒
python systrace.py -t 5

# 抓取特定模块,比如只看GPU和输入
python systrace.py -t 5 gfx input view

# 抓取所有模块,输出到文件
python systrace.py -t 10 -o mytrace.html

# 常用模块列表
# gfx - 图形渲染
# input - 输入事件
# view - View绘制
# wm - 窗口管理
# am - Activity管理
# sched - CPU调度
# freq - CPU频率变化

我的小技巧:抓取时间别太长,5-10秒足够。太长的话文件巨大,打开都费劲。我一般先抓5秒,发现问题再针对性加长时间。

方法二:手机端直接抓(适合现场问题)

有时候问题复现条件苛刻,来不及连电脑。这时候用开发者选项里的“系统跟踪”最方便。

# 设置要跟踪的类别(在开发者选项里配置)
# 然后下拉通知栏,点击“系统跟踪”图标
# 开始 -> 复现问题 -> 停止

# 生成的trace文件在:
# /data/local/traces/
# 或者直接分享出来

方法三:代码中动态控制(适合自动化测试)

我在做自动化性能测试时,会在关键步骤前后用代码控制trace开关。

// 开始跟踪
adb shell atrace --async_start -c -b 16000 gfx input view sched freq

// 执行测试操作...

// 停止跟踪并保存
adb shell atrace --async_stop -z > /sdcard/trace_output

// 拉取到电脑
adb pull /sdcard/trace_output ./mytrace.html

注意:buffer大小(-b参数)默认是2048KB,如果抓取时间长或模块多,建议设大一点,比如16000。我曾经因为buffer太小,抓到的trace不完整,排查了半天才发现是数据被截断了。

6.3 时间轴怎么看?

打开生成的trace.html,你会看到一条长长的彩色时间轴。第一次看可能会觉得眼花,但别慌,我教你几个关键点:

界面布局

区域 说明
顶部时间轴 显示总时长,可以缩放、拖动
左侧进程/线程列表 每个进程可以展开,看到里面的线程
中间彩色条带 每个线程的活动,颜色代表不同事件类型
底部详情区 点击某个事件,显示详细信息

颜色含义

  • 绿色:正常运行,通常表示等待或空闲
  • 橙色:可运行但未运行(等待CPU)
  • 红色:正在运行(占用CPU)
  • 紫色:睡眠/阻塞(等锁、等IO)
  • 蓝色:中断处理

核心技巧:看卡顿问题,重点关注“橙色”和“紫色”区域。橙色说明CPU不够用,紫色说明在等资源。我遇到过很多次,界面卡了,一看时间轴,主线程一大片紫色——原来是等一个锁等了200ms。

6.4 关键指标怎么看?

抓到了trace,也看懂了时间轴,接下来要关注几个核心指标。这些是我在实际项目中总结出来的“必看项”:

指标一:帧率(FPS)

在trace里按“F”键,会显示每一帧的绘制情况。重点关注:

  • 掉帧:两帧间隔超过16.6ms(60fps)或8.3ms(120fps)
  • jank:连续多帧掉帧,通常意味着有耗时操作
  • 帧边界:绿色竖线表示vsync信号,如果绘制没在vsync前完成,就会掉帧

指标二:CPU调度延迟

看线程的“Running”状态是否连续。如果线程频繁在“Runnable”和“Running”之间切换,说明CPU调度有问题。

# 在trace中搜索你的线程名
# 看它的调度情况:
# - 连续红色:正常执行
# - 红橙交替:被频繁抢占
# - 大片橙色:CPU不够用,在排队

指标三:锁竞争

在trace里,锁等待会显示为紫色块,并且会标注“waiting for lock”。我建议重点关注:

  • 主线程等锁:这是大忌,直接导致界面卡顿
  • 锁持有时间:点开紫色块,看“Duration”字段,超过10ms就要警惕
  • 锁链:有时候A等B,B等C,形成死锁链

避坑指南:我曾经排查过一个诡异问题——手机滑动时偶尔卡一下,但抓trace又抓不到。后来发现是抓取时机不对。我改成在卡顿发生前就开始抓,终于抓到一次:原来是某个后台线程在持有锁做数据库操作,主线程刚好要拿同一个锁。解决方案很简单,把数据库操作放到异步线程,或者用读写锁分离。

指标四:Binder调用

跨进程通信在trace里会显示为“Binder transaction”。关注点:

  • 调用频率:短时间内大量Binder调用,说明设计不合理
  • 调用耗时:点开看“Duration”,超过5ms就要优化
  • 调用链:看是谁调了谁,有时候是A调B,B又调C,形成长链

6.5 实战:一个卡顿问题的排查思路

假设你遇到一个场景:App列表滑动时偶尔卡顿。我会这样用Systrace排查:

  1. 抓trace:用命令行抓10秒,包含gfx、input、view、sched模块
  2. 定位卡顿帧:按F键,找到掉帧的那一帧
  3. 看主线程:点击掉帧区域,看主线程在做什么
  4. 看耗时操作:如果主线程有长条红色,点开看是什么函数
  5. 看锁等待:如果有紫色块,看等的是哪把锁
  6. 看CPU调度:如果主线程是橙色,看其他核在做什么

记住:Systrace不是万能的,但它能帮你快速缩小问题范围。我一般先用Systrace定位到“是什么在耗时”,然后再用其他工具(比如perf、simpleperf)深入分析。

嗯,Systrace的基础就这些。你想想看,其实核心就是:抓对数据、看懂时间轴、关注关键指标。多练几次,你也能一眼看出系统瓶颈在哪里。