2. 埋点技术基础:Systrace与Perfetto简介,Trace事件的类型,如何查看系统已有的trace点
咱们做AOSP启动优化,说白了就是在跟时间赛跑。想跑赢这场赛,必须有一把精准的“计时尺”和一张“地图”。说到工具,Systrace和Perfetto就是最趁手的两把。
2.1 从Systrace到Perfetto:工具的进化
Systrace是老前辈了,Android 4.1就开始用。它的核心是读取内核的ftrace和Android的ATRACE(应用层trace),然后生成一份HTML报告。我记得刚开始做启动优化时,全组都靠它吃饭。但你想想看,Systrace有两个硬伤——数据细节不够深,而且分析时交互做得比较粗糙。
Perfetto是2018年Google推出的新一代工具,可以理解成Systrace的“Pro Max版”。它不仅能抓ftrace和ATRACE,还能并抓heap profile、CPU调度、Binder延迟、GPU渲染等上百个数据源。启动耗时分析,如果没有Perfetto,说句不好听的,就像闭着眼睛开车。
一个重要事实:从Android 10开始,Google官方已经用Perfetto完全替代了Systrace。新项目不要再开Systrace了,Perfetto才是未来的方向。
2.2 Trace事件的核心类型:B/E、S/F、C
不管用Systrace还是Perfetto,底层的trace点都遵循同一套标记协议。基本上就三类,我直接给你们总结个表:
| 类型 | 标记符 | 语义 | 典型示例 |
|---|---|---|---|
| B / E | B(begin)/ E(end) | 异步时间片开始和结束 | B|1234|inflation E|1234 |
| S / F | S(start)/ F(finish) | 同步函数调用入栈/出栈 | S|1234|onCreate F|1234 |
| C | C(counter) | 计数器,跟踪数值变化 | C|1234|MemFree:1048576 |
我刚开始做埋点的时候,总把B/E和S/F搞混。业内曾经有个笑话——用S/F标记的函数如果中间发生了异步回调,那trace图会乱成一锅粥。所以最佳实践是:同步调用用S/F,异步任务用B/E。
我的习惯:在写ATRACE时,如果函数的生命周期和调用线程完全绑定,就用S/F;如果任务可能跨线程或者存在post操作,一定用B/E。避免trace显示“反人类”的嵌套。
2.3 计数器(C)的妙用
C事件虽然看起来简单,但实际作用很大。它记录的是某个时刻的数值快照,比如剩余内存、CPU频率、帧率等。Perfetto的SQL分析引擎可以直接对C事件做聚合、求平均、画曲线,启动阶段的内存抖动通过C事件看得一清二楚。
避坑指南:我曾经在启动过程中埋了一个C事件记录“已加载的Class数量”,代码写的是每加载一个Class就更新一次。结果Perfetto图里出现了一条细密的“锯齿”——因为C事件更新太频繁,Perfetto采样率跟不上。所以C事件的更新间隔建议控制在1ms以上,否则数据会失真。
2.4 如何查看系统已有的trace点
你不需要从零开始手动埋点。AOSP有大量现成的trace点,关键是你要知道它们藏在哪。我总结了三条路:
- ATRACE宏:在frameworks/native/libs/utils/Trace.h中定义。源码里随处可见ATRACE_CALL()、ATRACE_NAME()等宏,直接启用即可。
- ftrace点:内核中的调度、中断、内存、文件系统等事件,通过/sys/kernel/tracing/available_events查看。Perfetto后台自动收集。
- Perfetto自带数据源:在Perfetto配置文件中声明data_sources,例如“android.surfaceflinger.trace”“android.hardware.power”等,Google已经内置好了。
怎么快速确认系统里到底有哪些trace点?我教你个命令:
adb shell atrace --list_categories
输出会列出所有当前设备支持的trace种类,像input、view、am、pm、rs、gfx等。如果你需要更底层的,可以看:
adb shell ls /sys/kernel/tracing/events/
这下面全是ftrace事件组,比如sched、irq、kmem、i2c、power等。每个组下面都有几十个事件,启动阶段最常用的是sched_switch、sched_wakeup、cpu_idle、tracing_mark_write。
关键技巧:如果你在Perfetto里看到一大块“灰色”区域,原因往往是某个trace点没有开启。检查一下你的profile配置中是否漏掉了对应数据源。我遇到过整整一天没找到“为什么SystemServer的某个阶段耗时200ms”,最后发现是没拉取binder_driver数据源。
2.5 知识结构全景图
2.6 实战检查:我的设备有多少trace点?
我建议你马上打开终端,连上你的开发板或Pixel手机,跑一下:
adb shell atrace --list_categories
adb shell ls /sys/kernel/tracing/events/sched
如果第二行返回了一堆sched_switch、sched_wakeup之类的文件,说明内核trace点默认就开了。如果为空,可能是内核没打开ftrace,需要重新编译内核并配置CONFIG_FTRACE=y。
嗯,开头那俩命令的输出,建议你截图保存到笔记里。后续我们在做启动耗时分段时,需要用这些来确认到底是哪个子系统没数据。