告别Systrace,用Perfetto+SQL精准定位App卡顿

发布时间:2026/9/16 22:54:08
告别Systrace,用Perfetto+SQL精准定位App卡顿 大概是从两年前开始我系统性负责一款视频类App的卡顿分析专项。当时线上用户反馈首页信息流滑动掉帧技术同学用systrace前前后后抓了十几次要么没抓到关键窗口要么时间线太粗看不出问题整个排查过程就像在雾里找针。后来我彻底切换到perfetto这套工具链配合Web版分析界面和SQL式的数据查询很多堆在心里的“感觉哪里不对劲”终于变成了“你来看这里就是这一行代码的事”。这篇文章就是把我这段时间的实战经验做一个完整梳理写给还在systrace和perfetto之间犹豫的朋友也写给那些和我一样被掉帧卡到怀疑人生的性能工程师。1. Perfetto和Systrace到底什么关系怎么选1.1 血缘关系一套基因的不同形态先说结论Perfetto就是Systrace的“全面进化版”不是完全另起炉灶的新东西。Systrace从Android 4.1时代就开始出现它本质上是Linux内核ftrace的一个前端封装主要抓的是调度、CPU频率、内核事件和部分用户态tracepoint输出成HTML文件后在浏览器里查看。Perfetto从Android 9开始加入Android 10之后逐步成为系统推荐的trace抓取工具。它的底层仍然依赖ftrace但在架构上做了彻底重构抓取端是原生守护进程可以做到毫秒级精度和更低的开销分析端是标准的SQLite存储所有trace数据都能用SQL去查展示端是一个独立的Web UI可以处理动辄几百MB的超大trace文件。这就意味着你不光能用perfetto看到比systrace更丰富的时间线还能对它做精确的“数据挖掘”。以前用systrace查卡顿基本上靠肉眼扫时间线谁长谁短一眼看去现在可以直接写一句SQL把卡顿区间的长任务全部拉出来排序效率完全不是一个量级。1.2 什么场景用Perfetto什么场景继续用Systrace我的建议是能上Perfetto就不回头用Systrace。但也要分场景。如果是Android 9以下的老设备或者公司的硬件测试机系统版本太旧Perfetto可能不可用这时候systrace反而是兜底方案。而在Android 10以上的设备上Perfetto不仅精度更高而且抓取命令更规范抓出来的文件是结构化的protobuf格式后续可以用Trace Processor做脚本化分析这个优势在频繁回归性能问题时特别大。另外有一个容易被忽略的点系统中的atrace和perfetto在功能上有重叠但atrace的watchdog在一些老设备上会出现“莫名掐断trace”的情况Perfetto的时长控制更可靠。我自己就遇到过systrace明明设了10秒结果3秒就停了后来排查发现是atrace的buffer崩了。这种坑在Perfetto上遇到的概率低很多。1.3 卡顿分析的基本方法论先把“现象”变成“数据”做卡顿分析最忌讳一上来就开trace然后狂点手机。正确的方式是先把“线上卡顿”这个模糊现象拆成可测量的数据指标。卡顿可以拆成四类主线程耗时过长、渲染管线阻塞、系统调度异常、资源争抢导致饿死。主线程问题通常对应Choreographer#doFrame时间很长渲染问题要看RenderThread和SurfaceFlinger的同步是否失衡调度问题要看线程优先级和CPU核心资源争抢问题要看锁竞争和Binder调用。明确了这四类再决定Perfetto要抓哪些数据源。只抓默认真机数据是不够的有些场景必须显式打开sched、freq、gfx、view、binder这些数据源。这一步想清楚后面分析才会快。2. 抓取前的准备数据源选择决定分析深度2.1 开发者选项和USB连接这些基础项Perfetto抓取条件不算苛刻但基础项必须确认到位。手机打开开发者选项开启USB调试保证电脑能执行adb devices。这里有个细节部分厂商系统在USB连接模式为“仅充电”时adb会出现拒绝连接需要下拉通知栏把USB用途改成“传输文件”或“MIDI”模式。另外抓trace最好用Android 10及以上设备如果公司只有Android 9以下的测试机需要用systrace兼容方案。不过近两年的大部分项目应该都支持了新机连这些都不用操心。还需要确认手机上的Perfetto服务是否可用可以执行adb shell perfetto --version验证。如果提示找不到命令一般是系统镜像不带perfetto需要换成用户debug版或者使用SDK中自带的record_android_trace脚本。2.2 核心数据源配置不是抓得越多越好Perfetto的数据源有几十种全抓的话系统开销会明显增大分析时也像看天书所以一般按需选配。做卡顿分析我常用的配置是这样adb shell perfetto \ -o /data/misc/perfetto-traces/app_trace.pb \ -t 15s \ sched \ freq \ idle \ gfx \ view \ input \ binder_driver \ am_proc_start \ am_proc_bind \ am_proc_end \ wm \ activity \ surfaceflinger-o指定输出路径-t指定抓取时长。sched和freq用于分析CPU调度与频率gfx、view、input覆盖UI渲染链路和输入链路binder_driver可以抓到Binder事务开始与结束surfaceflinger能看到合成层信息。这里有个经验教训如果一开始什么都不懂就先抓默认全量数据源-c后不指定data source会走默认配置然后把sched、gfx、view这三个加进去。这三个是我认为卡顿分析性价比最高的数据源几乎每次都会用到。2.3 抓取时机的把控提前量比时长重要很多人在抓卡顿时习惯“卡了再抓”这是本末倒置。Perfetto的数据是环形buffer如果卡顿已经发生了再去抓很可能关键帧已经滚出buffer。正确做法是“预埋”先把trace命令跑起来让它先在后台持续写入环形buffer然后手动去复现卡顿复现完成后结束抓取。因为Perfetto从启动到真正采集数据之间有几百毫秒的延迟所以要让命令提前2-3秒启动。如果你用的是record_android_trace脚本它自带了一个--buffer_size参数可以设置大一点比如64MB避免缓冲区过早被覆盖。我在实际操作中的习惯是开始抓取后先看手机屏幕上出现“系统跟踪正在进行”的提示再静静等1秒然后开始执行复现步骤。这样虽然会多花一点时间但保证能抓到卡顿中的完整时间线而不是抓到一堆“卡顿后的和平景象”。3. 打开Trace之后怎么快速定位卡顿区间3.1 从全局火焰图找到“异常段”抓取完成后把app_trace.pb文件pull到本地用Chrome或者Edge打开https://ui.perfetto.dev把文件拖进去就可以分析。打开的第一眼不要急着盯主线程。先看整体CPU信息和顶部的时间轴总览左侧会列出所有进程和线程的CPU占用那些CPU占用突然飙升、又突然跌落的尖峰往往就是卡顿发生的位置。Perfetto的UI里按下键盘上的w键可以放大s键缩小a和d可以左右平移。快速滚动到总览中你复现卡顿的那个时间点比如我是在滑动到第3秒开始掉帧就把视野对准第3秒到第3.5秒这个区间。3.2 三段式定位法输入、执行、渲染看卡顿就像查一条流水线的哪个工位出了故障。我把UI帧过程拆成三段输入→执行→渲染。输入段对应InputReader和InputDispatcher事件执行段对应主线程里的doFrame、measure、layout、draw渲染段对应RenderThread和SurfaceFlinger合成。在Perfetto里直接搜索Choreographer#doFrame就能找到一个帧从开始到结束的切面。如果doFrame本身很长说明主线程执行阶段有问题如果doFrame很短但后面RenderThread的DrawFrame很长那就是渲染阶段卡如果doFrame到queueBuffer之间有大段空闲问题可能出在VSYNC信号延迟或SurfaceFlinger拥塞。这个方法我用了快一年定位准确率很高而且特别适合新手建立“帧的一生”的概念。3.3 借助SQL查询Trace Processor能省一半时间Perfetto最爽的一点是数据全部落在SQLite里可以直接执行SQL查询来做定量分析。在Perfetto UI中切换到“Query”标签页输入以下SQL可以查出一个进程里的所有sliceSELECT ts / 1000 AS time_ms, name, dur / 1000 AS dur_ms FROM slice WHERE ts BETWEEN 3000000000 AND 3500000000 AND track_id IN ( SELECT track_id FROM process_track WHERE process_name LIKE %com.example.app% ) ORDER BY dur DESC LIMIT 20;这里ts的单位是纳秒除以1000变成微秒再配合dur就能看到这段时间内最耗时的20个切片。用这个方法我曾经一眼查出一个看似随机发生的卡顿实际上是主线程在做SharedPreferences的apply同步阻塞那段耗时达到了400ms。SQL分析属于进阶玩法但掌握之后你会发现自己对trace的理解完全不一样。4. 实战案例一次滑动卡顿的完整定位过程4.1 案例背景和抓取数据有一次我们App的搜索结果页在快速滑动时偶发卡顿本地反复操作十次能复现两三次频率不算高但用户反馈很多。肉眼很难看出规律于是我决定上Perfetto抓一次带gfx和sched的trace。复现操作是启动App、进入搜索页、输入关键词、快速上下滑动列表10次然后退出页面重进再快速滑动5次。整个过程控制在15秒内保证都落在trace窗口里。4.2 第一步先看Frames时间线里的“红点”打开trace后我直接找到Frames轨道。Perfetto会把每一帧的绘制时间以slice形式列出来正常帧应该是比较均匀的颜色卡顿帧则会异常长。我看到在滑动过程中连续出现了几个“大红条”每根都超过100ms。这就是掉帧的现象层证据。但光知道掉帧不够还要知道掉帧是谁造成的。4.3 第二步把主线程和RenderThread拉出来看点击那个大红条的frame slicePerfetto会自动关联到该帧的主线程和RenderThread区间。主线程时间线上Choreographer#doFrame被一个名为ViewGroup.drawChild的长切片占住了再点进去发现里面竟然有一个BitmapFactory.nativeDecodeStream在解码图片。解码在draw阶段执行几乎可以说是“教科书级的错误”图片解码放到了UI绘制阶段直接堵死了主线程的渲染。问题根源是列表项里的一个自定义控件在onDraw里调用了decodeStream去加载缩略图。搜索代码确认后把解码操作移到了onBindViewHolder里并增加LRU缓存掉帧问题当场消失。4.4 第三步验证修复效果并检查是否引入新问题修复后重新抓trace同样场景下Frames时间线恢复均匀主线程doFrame耗时稳定在8ms左右。这个案例属于典型的“代码错误导致的卡顿”Perfetto的定位非常直接。但实际工作中更复杂的卡顿往往不是单点问题而是调度、内存、锁竞争等多因素叠加。这时候仅靠时间线“看”不如用SQL“算”。5. 常见问题与排查技巧实录5.1 抓取不到数据或者数据为空新手最容易遇到的就是trace抓下来打开UI后全是空的。排查顺序如下先确认命令中的数据源名称是否正确比如gfx打成了graphics再确认抓取时屏幕提示是否正常出现最后检查-o路径是否有写入权限。还有一个坑部分手机在锁屏状态下会挂起后台进程导致perfetto的buffer被系统冻结抓下来的trace断断续续。所以抓取时尽量保持屏幕常亮或者加--no-wait-request参数。5.2 时间线太乱怎么快速过滤Trace里进程线程成百上千直接看主线程像大海捞针。我的做法是先在Search框搜索进程名把关注的进程高亮再通过左侧面板只勾选CPU、Frames、Main Thread这三类轨道的visibility其它轨道全都隐藏。想看某个函数的调用耗时直接双击sliceUI会跳到该slice并显示相关的meta信息。这里记住三个快捷键f键是“适应选中区域”shift单击是“三点测距”能准确量出相邻两个事件之间的耗时。5.3 频繁卡顿但时间线看不出异常问题可能在CPU频率有些卡顿在时间线上是“隐形”的主线程不忙、RenderThread也不忙但实际就是掉帧。这种时候多半是CPU调频跟不上或者当前设备配置里大小核触发策略太保守。在Perfetto里打开cpu.freq轨道看卡顿发生时CPU频率是不是掉到了最低档。如果是就要考虑是不是温控降频、或者app进程被限制到小核运行。这种情况经常出现在特定机型上排查思路要从“代码”转移到“系统策略”。5.4 一个常用的CheatSheet我把自己日常分析的最常用操作整理一下目的操作打开traceui.perfetto.dev拖入pb文件放大/缩小按w/s平移按a/d适应整个trace按f测量两个事件间隔shift单击两个slice查看frame对应线程点击Frames里的slice右侧自动关联SQL查耗时切片Query页签执行sql语句6. 从Perfetto带来的工作流改变说起最后再聊一个实际感受。用Perfetto之前我做卡顿分析是把trace当成“事后证据”用出了问题抓一把看看工作流是发现一个问题→抓一次→分析→修复→再验证每一步之间要花很多时间在环境和工具的磨合上。用了Perfetto之后我慢慢把trace抓取变成一种“常规巡检手段”。每次版本提测前我会在关键路径上跑一轮“滑动基准trace”让测试同学按固定动作走一遍然后统一用SQL脚本扫描是否有超过50ms的长slice、是否有解码操作落在draw阶段、是否有主线程访问磁盘的迹象。这一步把很多潜在的性能隐患挡在了上线之前。如果你现在还在用老版本systrace做卡顿定位我的建议是认真花一个下午把Perfetto的Web UI和SQL查询用熟。刚开始可能觉得软件界面复杂但只要用自己的手机跑通一次完整流程你会发现它的可操作性和分析深度是systrace的十倍不止。卡顿分析这条路没有什么捷径就是一遍遍抓、一遍遍看、一遍遍算但好的工具至少能让你把力气花在刀刃上。