
Systrace这个名字老Android开发应该都不陌生。做性能优化如果没用过它那就像开车不看仪表盘——你的App卡不卡、掉帧掉在哪全靠感觉猜。这几年Google主推Perfetto但大量存量项目、企业内部的性能分析流程里Systrace依然是最常用、最直接的系统级性能采样工具。它可以在一张时间轴上同时展示CPU调度、线程状态、应用消息处理、View绘制和渲染管线让你一眼看出卡顿到底发生在哪个环节。这篇博文就围绕Systrace的完整使用流程来写从底层原理讲到实操命令再讲怎么看懂那些花花绿绿的色块最后把我踩过的坑一并整理出来。无论你是刚接触性能优化的新手还是被线上卡顿问题折磨的“老油条”这篇都能当作一份随手可查的参考。文章里所有命令和操作都是我在Android 7到Android 13的设备上实测过的版本差异会单独说明。1. Systrace能做什么以及它背后的实现思路1.1 它到底解决了什么问题应用卡顿通常分几类主线程里的耗时操作、View过度绘制导致的渲染压力、CPU调度异常导致的任务延迟、甚至系统Framework层的Binder调用阻塞。这些问题的共同点是“偶发、难以复现、肉眼不可见”——Logcat只能看到业务日志Profiler只能看到Java层调用底层到底发生了什么需要一个能同时覆盖内核态和用户态的全局视角。Systrace就是干这个的。它不主打“定位到某一行代码”而是把时间轴上所有发生的事情排列出来哪个线程在跑、跑了多久、期间有没有被抢占、渲染有没有掉帧、Binder通信有没有卡住、CPU频率是否被限制。这些信息拼在一起就能把“卡顿”这个模糊的感受变成“主线程在A点到B点之间阻塞了300ms期间在等Binder响应”这样具体的结论。说白了Systrace适合回答三类问题帧率不稳、掉帧到底掉在哪一帧、启动流程里哪个阶段耗时最长、某个后台任务为什么迟迟没有执行完。这三类基本上覆盖了App性能优化的绝大多数场景。1.2 底层基于Linux内核的ftrace机制Systrace并不神秘它的核心技术是Linux内核自带的ftrace。ftrace是内核里的一个跟踪框架可以监听调度器事件、中断、CPU频率变化、进程状态切换等。Systrace通过adb向设备下发指令让内核打开指定的事件跟踪开关同时把应用侧用户态的trace点也打开两者打上同一个时间戳最终合并成一份带完整时间轴的trace文件。用户态这一侧Android的Java层和Native层都有对应的trace埋点。举例来说View体系里的measure、layout、draw阶段会写入trace标记Choreographer处理vsync的doFrame也会写标记系统渲染线程RenderThread的DrawFrame同样有标记。这些用户态事件和内核态的sched调度事件、CPU频率事件合在一起就构成了完整的时间线。所以才说Systrace的价值在于“全局”。单纯看Android Studio的CPU Profiler你只能看到应用内部的方法耗时单纯看内核日志你又看不到业务代码的上下文。Systrace把两边拼在一起而且开销极低适合做长时间的现场采集。2. 环境准备与采集方法2.1 工具链构成用Systrace需要准备以下几样东西一台Android手机或模拟器建议Android 7.0及以上系统自带atrace服务。Android SDK中的platform-tools里面包含adb命令。Python环境老版本的systrace脚本依赖Python 2.7。如果要打开trace.html做可视化分析需要一个浏览器不过现在基本都用Perfetto UI打开后面会详说。很多新手在这里会卡一下明明装了Android Studio但直接运行systrace.py却提示找不到模块。原因是systrace脚本不在默认的PATH里它位于Android SDK目录下的platform-tools/systrace/子目录中。完整路径一般是$ANDROID_HOME/platform-tools/systrace/systrace.py如果是Windows就在SDK的安装目录下找到platform-tools文件夹再进systrace子目录。懒得找的话直接把systrace.py和adb所在目录都加到系统PATH里之后就可以在任何终端直接调用。Python版本这一步很关键。Android 9之前的systrace.py如果你用Python 3去跑大概率会报语法错误。原因很直接这个脚本是老代码用的是Python 2的print语法。解决办法有两个一是装一个Python 2.7专门跑它二是如果设备是Android 10及以上的系统直接改用Perfetto采集。现代设备的采集命令是adb shell perfetto -o /data/misc/perfetto-traces/trace.perfetto-trace -t 10s sched freq idle gfx view wm am不过大部分项目还在用老systrace流程主要是因为历史文档多、同事之间传下来的脚本也都是这套。我会把两条路径都讲清楚设备老就用systrace.py设备新就用Perfetto采集回来的trace用Perfetto UI打开不影响分析效果。2.2 确认设备状态并开启调试正式开始采集前先确认设备能被adb识别。插上手机开启开发者选项和USB调试然后执行adb devices看到类似下面这样的输出就说明连接正常List of devices attached R58M2xxxxxx device如果显示unauthorized需要在手机上勾选“允许USB调试”的授权弹窗。如果是无线调试Android 11及以上可以用adb pair 192.168.1.100:37000 adb connect 192.168.1.100:39731确保设备在线后还需要确认系统里有atrace这个命令。执行adb shell which atrace只要能输出路径比如/system/bin/atrace就说明系统支持Systrace采集。这一步主要是排除那些定制过度的系统版本个别厂商ROM可能会把atrace阉割掉导致后续怎么跑都提示某标签不支持。2.3 采集命令的参数详解老版Systrace的采集命令长这样python systrace.py -t 10 -a com.example.app -o trace.html gfx input view sched freq wm am这一串参数看起来简单但每个选项都有讲究-t指定采集时长单位秒。一般采集10秒到20秒就够用太短可能抓不到问题现场太长产生的trace文件会非常大分析起来反而费劲。-a指定要跟踪的应用包名。这里是关键不加这个参数Systrace只采集系统级信息看不到应用内部的线程名称和View绘制的详细事件加上了之后应用进程里的Java线程会被标记成有名字的线程而不是一堆pid数字。-o指定输出文件名通常以.html结尾。后面的一串参数是要开启的trace类别标签每个标签对应一类事件。常用的标签我列在下面标签含义什么场景下需要gfxGraphics包括View绘制、RenderThread、帧生成事件分析掉帧、卡顿的首选viewView体系包括measure、layout、draw界面布局性能问题schedCPU调度包括线程状态、CPU占用、唤醒延迟任何性能问题都要带上freqCPU频率变化发热降频导致的性能问题wmWindowManager相关事件窗口切换、启动、动画问题amActivityManager相关事件Activity启动流程、生命周期耗时input输入事件分发点击无响应、触摸卡顿binder_driverBinder驱动层事务跨进程通信阻塞hal硬件抽象层相机、传感器、显示相关我个人习惯打底标签是gfx input view sched freq wm am这套组合能覆盖绝大多数应用层性能问题。binder_driver和hal标签会产出大量数据文件膨胀很快除非明确怀疑是Binder问题否则不建议日常采集时开。2.4 从Systrace到Perfetto的采集迁移Android 10之后Google逐步把性能采集工具链统一到Perfetto上。老的systrace.py脚本依然能跑但底层调用的atrace服务和Perfetto的采集能力有差距。新设备上更推荐直接用Perfetto命令采集adb shell perfetto -o /data/misc/perfetto-traces/your-trace.perfetto-trace -t 10s -a com.example.app sched freq idle gfx view wm am需要注意Perfetto默认的输出路径是设备上的/data/misc/perfetto-traces/目录这里需要root权限才能写入。对大多数开发者来说最方便的做法是配合--txt按文本配置或直接采集到/data/local/tmp下adb shell perfetto -o /data/local/tmp/trace.perfetto-trace -t 10s sched freq gfx view adb pull /data/local/tmp/trace.perfetto-trace ./Perfetto采集完的trace文件后缀是.perfetto-trace不再是.html。但这不影响分析——用Chrome打开Perfetto UI地址是ui.perfetto.dev把文件拖进去就能看。值得高兴的是老的systrace采集出来的trace.htmlPerfetto UI同样能打开并自动解析所以新老工具链之间的数据格式是兼容的。3. 实操流程从零开始采集一份可用的Trace3.1 以“应用启动耗时分析”为实战案例理论讲多了容易飘下面直接走一遍完整的实操流程。我用一个常见场景来说明App冷启动速度变慢需要找出启动过程中到底哪个环节消耗了最多时间。第一步在设备上准备好测试条件。清理后台进程确保应用是冷启动状态。可以执行adb shell am force-stop com.example.app第二步启动Systrace采集。这里我建议先设置一个较长的采集窗口比如12秒然后在这12秒内手动点击应用图标。命令如下python systrace.py -t 12 -a com.example.app -o launch_trace.html gfx view sched wm am注意一个细节执行命令后终端会显示“Starting tracing...”并阻塞住此时立刻去手机上点击应用图标让启动过程完整落在采集窗口内。采集结束后终端会自动退出并生成trace.html文件。第三步用Chrome打开trace.html。如果你看到的是一个空白页面或者提示脚本错误多半是浏览器版本太新老的Systrace查看器已经不受支持。这个时候直接把同一个文件拖到ui.perfetto.dev里打开就行Perfetto UI会兼容解析旧格式界面虽然不同但数据完全够看。第四步在Perfetto UI里左侧的进程列表中勾选com.example.app进程和system_server进程。时间轴会刷新显示出该进程的线程活动、Frame渲染标记以及启动期间系统侧的事件。查看主线程从Application创建到Activity onResume结束之间的事件就能看到第一个卡点在哪。3.2 复现掉帧问题的采集技巧掉帧问题和启动问题不太一样它需要“复现动作”。比方说用户报告进入首页后快速滑动Feed列表会明显卡顿。采集前的准备动作是先把App预热到目标页面停留在首页然后开始采集python systrace.py -t 10 -a com.example.app -o jank_trace.html gfx view sched input freq同样开始采集后尽快在手机上快速上下滑动列表持续到采集结束。因为掉帧是一个瞬间事件10秒窗口里重复操作几次总能抓到掉帧现场。如果怕抓不准可以把时长加到15秒手动滑动两三个来回保证至少有一次是触发卡顿的。采集结束后看数据重点关注gfx标签下的Frame行或者是UI Thread下的doFrame事件。Perfetto UI里每一帧的渲染完成时间会以圆点标记的形式显示在Render Thread或GPU Completion行上。相邻圆点之间的时间间隔如果超过16.6msAndroid 120Hz设备需要按8.3ms算就说明掉帧了。点开具体帧往下钻取Choreographer#doFrame、ViewRootImpl#performTraversals、RenderThread#DrawFrame这些事件就能把掉帧的原因从“发生了”定位到“发生在哪个方法”。3.3 如何保存和分享Trace文件分析完trace后如果要把问题带到群里讨论或者作为bug单的附件提交直接分享trace.html是不合适的——这种文件体积大、打开门槛高。更好的做法是同时导出一份截图和一份事件摘要。Perfetto UI右上角有一个“Export trace”的选项可以把当前预览范围的trace数据重新导出成一个小文件。它还会生成一个嵌入式的html页面带数据预览功能对方打开后不用额外安装工具就能看到问题区间。如果是老版Systrace的html文件直接在Chrome里打印成PDF或者截图把关键区间放大后再截图发给别人配合文字说明更高效。毕竟性能问题的结论靠的是分析而不是把一整份原始数据甩给对方。4. 如何读懂Trace输出4.1 界面操作与时间轴阅读拿到一份trace文件之后新手最容易懵的地方是界面上一堆色块和线条到底看哪里。在Perfetto UI或者老版Trace UI里核心的时间轴从上到下分为多个泳道Lane每个泳道代表一个线程或一个CPU核心。你可以把它理解成一个超级精确的甘特图横轴是时间纵轴是线程/CPU色块的宽度代表该线程在对应时间段内的运行或等待状态。先看颜色。线程状态不同色块颜色也不同绿色正在运行。正常应该很饱满。蓝色可运行但等待CPU分配。说明线程在排队有调度延迟。白色/红色睡眠或等待锁。红色经常意味着死锁或长时间阻塞。橙色中断处理。理论上主线程的颜色在用户交互期间应该是绿色占大多数。如果大量出现蓝色说明CPU资源紧张如果大量红色或白色说明线程在等某个东西比如I/O、Binder响应、锁。页面操作快捷键上老版Trace UI支持W放大、S缩小、A左移、D右移、M加标记。Perfetto UI默认操作是鼠标滚轮缩放按住空格键拖动平移。排列好视图后双击某个色块会跳到对应的事件详情显示这个事件在哪个线程、持续了多久、调用了什么方法。4.2 从Trace中定位三类典型问题第一类典型问题是掉帧。掉帧的根源非常复杂但trace里看是有直接信号的。在gfx标签行如果发现Frame标记长时间不出现或者说连续多帧的完成时间都超过帧预算就要看它前一个doFrame事件是否耗时过长。常见情况是doFrame里调用了performTraversals而performTraversals里面的measure或layout耗时超过了几十ms说明布局复杂度太高也可能是主线程执行了一个很长的Message导致doFrame被推迟色块上能看到一个明显很宽的事件块。第二类典型问题是主线程阻塞等待。此时主线程色块在很长一段区间里不是绿色而是红色或白色旁边往往伴随着Binder相关的细条。展开Binder行查看是否有Binder transaction长达几百毫秒没有返回。这类问题通常指向跨进程调用比如频繁访问ContentProvider、SystemService调用、或者应用间通信引发了死锁。第三类典型问题是CPU频率限制导致的性能下降。如果CPU频率显示一直压在低档而sched行里又出现大量可运行但等待的蓝块说明系统因为温度或者功耗限制在降频这种问题应用层代码很难直接解决能做的就是减少同一时刻的CPU密集任务把峰值压力平摊。遇到这种问题trace里的freq行会展示得很清楚——频率曲线一直在低谷区间爬不上去。4.3 使用Alert和Track Event加速分析Perfetto UI和老版Trace UI都有一个Alert面板系统会自动标记出一些异常事件比如“Scheduling delay”“Long View.draw”“Dropped frames”。这个功能相当好用相当于系统帮你圈了重点。我通常会先扫一眼Alert列表看有没有红色级别的警告然后直接跳到对应时间点看周边上下文。另外一个能明显提升分析效率的补充是用自定义Trace API。在代码里给关键方法加埋点这样trace里会直接显示自定义事件段。Java层用android.os.Trace.beginSection和endSection方法名会出现在trace的Events行中Native层走ATRACE_BEGIN宏。埋点不是越多越好关键路径加两三个就有价值了比如启动流程中的Application.onCreate、MainActivity.onCreate、首帧绘制完成。分析时你就能在一堆系统事件里快速确定“哦问题确实出现在MainActivity.onCreate的loadData方法这一段”直接省下大量找代码的时间。5. 常见问题与排查技巧实录5.1 设备无法采集或提示标签不支持这是最常遇到的坑。执行采集命令后如果终端提示unrecognized option gfx或者AtraceError说明设备的atrace版本不支持你传入的标签。解决方法是先看设备支持哪些标签adb shell atrace --list_categories输出里会列出当前设备支持的tags列表你对照着调整systrace命令行里的标签参数把不支持的去掉就行。部分国产ROM还会出现permission denied的报错原因通常是系统级Trace被安全策略禁用了需要到开发者选项里打开“USB调试”中的“禁用权限监控”或者“安全设置”开关不同手机叫法不一样但路径都在开发者选项里。5.2 trace文件打不开或白屏老版Systrace生成的trace.html放在新版Chrome里打开经常白屏原因是老的Trace UI基于已经废弃的Web技术。解决方案是直接使用Perfetto UI打开 https://ui.perfetto.dev点击左侧“Open trace file”选择trace.html或.perfetto-trace文件Perfetto UI兼容旧格式解析能力比老UI更强加载大文件也更快。实在需要老UI的话可以用Firefox旧版本或Chrome的兼容模式但我不推荐在这上面浪费时间直接上Perfetto UI是正解。5.3 trace文件过大分析卡顿采集时间一长trace很容易达到几十MB甚至上百MB。分析这种巨无霸文件非常痛苦。解决问题的思路不是“提高电脑配置”而是“少采集点数据”。第一个办法是缩短采集时间日常分析控制在10秒内。第二个是精准选择标签不确定是Binder问题就不要开binder_driver不确定是相机问题就不要开hal。第三个是利用Perfetto的“只保留目标进程”功能老版Trace UI也有一个“Filter”输入框可以输入进程名界面只会显示匹配到的进程信息。这样即便文件很大界面操作流畅度也能接受。5.4 不同Android版本的差异Android 9之前的设备只能使用systrace.py Python 2来采集。Android 10之后的设备推荐直接用Perfetto采集因为在Android 10上Perfetto是系统默认采集方案事件覆盖更全面。如果不确定手里的设备应该用哪套先执行adb shell perfetto --version能输出版本号就用Perfetto否则退回老的systrace.py。另外Android 12开始部分系统进程名称有了变化比如system_server的部分线程改名分析时不要因为找不到预期线程名就慌先搜索关键事件关键词比死磕线程名更有效率。5.5 采集期间手动操作的最佳时机还有一个实操中很重要的细节从命令行执行采集到trace真正开始录中间有几百毫秒的初始化时间。如果你在命令执行后立刻操作手机容易把关键动作录在真正启动之前。稳妥的做法是执行命令后先看终端输出当出现明确的trace开始日志老版显示“Starting tracing”Perfetto按配置启动后无明确提示可以预开3秒的-t缓冲再开始操作手机。我自己的习惯是长按应用图标准备点击的姿势摆好终端命令一敲下去看到日志输出后立刻点击。这样基本能保证关键动作落在采集窗口内。6. 从Systrace到Perfetto工具迭代下的不变思路尽管现在Google主推Perfetto新的性能分析工作流也已经完全迁移到Perfetto UI上但Systrace时代建立起来的分析方法论完全没有过时。你会看sched、会看Frame、会看Binder那换到Perfetto只是换了个界面底层的事件模型和排查思路是一脉相承的。这就像老司机开新车仪表盘布局变了但“转速表高了要换挡”的逻辑没变。我个人在工作中最常用的组合是本地开发阶段用Android Studio的CPU Profiler做初步筛查遇到难以定位的偶现卡顿就上Systrace/Perfetto做现场采集两者互相印证。线上问题则通过Logcat的帧耗时统计粗筛再配合灰度包抓取现场trace。没有哪个工具能包打天下Systrace的价值在于给你一张“上帝视角”的系统全景图而这正是解决疑难性能问题最需要的东西。最后分享一个实用小技巧在Perfetto UI里分析完问题区间后可以用右上角的“Download”把当前选中的时间范围单独导出。这样后续在issue里贴证据时不需要把几百MB的原始trace发出去只导出一个几MB的小片段就够了对方打开后依然能看到完整的线程和事件信息。这个习惯能省很多沟通成本也让性能问题报告显得专业很多。