序言
推荐一个工具,可以在apk编译的时候给每个方法添加 trace。
项目地址
https://github.com/Gracker/TraceFix
使用方法
https://androidperformance.com/2021/09/13/android-systrace-Responsiveness-in-action-2/
还有一个项目也可以使用。字节跳动的。
https://github.com/bytedance/btrace/blob/master/README.zh-CN.md
接入tracefix 指南
Android 工程接入 TraceFix:编译期自动插桩,Perfetto 直看方法耗时
本文记录在一个多模块 Android 系统应用里接入 TraceFix 的完整流程。
场景:启动优化、录屏/相机等多模块工程,希望在不改业务源码的前提下,给方法自动加上Trace.beginSection/endSection,配合 Perfetto 分析耗时。
文中工程名、包名、模块名均已脱敏。
TraceFix 是什么
TraceFix 是一个编译期字节码插桩Gradle 插件。它会在方法前后自动插入成对的:
android.os.Trace.beginSection("...");// ... 原方法逻辑 ...android.os.Trace.endSection();这些 slice 会出现在 Perfetto / systrace 的app类别里。
它只负责「让 App 产生 trace 数据」,不负责采集。采集仍需atrace、perfetto或 Perfetto UI。
版本怎么选
TraceFix 有两条版本线(见官方 README):
| 版本线 | 适用场景 | Artifact |
|---|---|---|
0.1.x | AGP 7.4 ~ 8.x | io.github.gracker:TraceFix:0.1.0 |
0.2.x | AGP 9.1+、Android 17 新基线 | io.github.gracker:TraceFix:0.2.0 |
本文工程环境:
- AGP 8.13
- Gradle 8.13
- compileSdk / targetSdk 37
因此选用0.1.0。若后续升级到 AGP 9.1+,再切0.2.x并可用traceFix {}做更细粒度配置。
接入步骤
1. 在gradle.properties增加开关
prop_traceFixVersion=0.1.0 prop_traceFixEnabled=true prop_traceFixDebugOnly=true| 属性 | 含义 |
|---|---|
prop_traceFixEnabled | 总开关,false时完全不插桩 |
prop_traceFixDebugOnly | true时仅 Debug 变体插桩,Release/CI 包不受影响 |
prop_traceFixVersion | 插件版本,便于统一升级 |
2. 根工程build.gradle引入插件
在buildscript.dependencies中添加:
buildscript{repositories{// 能访问 Maven Central 即可;企业内网可用镜像仓库mavenCentral()google()// ... 其他已有仓库 ...}dependencies{// ... 已有 classpath ...classpath("io.github.gracker:TraceFix:${prop_traceFixVersion}"){changing=true}}}注意:若
google()/mavenCentral()在内网环境有 SSL 或访问问题,可改为使用公司 Maven 镜像,确保能解析io.github.gracker:TraceFix。
3. 新建tracefix.gradle(插件应用逻辑)
放在工程根目录,与build.gradle同级:
deftraceFixEnabled=project.hasProperty('prop_traceFixEnabled')&&prop_traceFixEnabled.toBoolean()if(!traceFixEnabled){return}deftaskNames=gradle.startParameter.taskNames.collect{it.toLowerCase()}defdebugOnly=!project.hasProperty('prop_traceFixDebugOnly')||prop_traceFixDebugOnly.toBoolean()if(debugOnly&&!taskNames.isEmpty()){defbuildsReleaseLike=taskNames.any{it.contains('release')||it.contains('coverage')}defbuildsDebug=taskNames.any{it.contains('debug')}if(buildsReleaseLike&&!buildsDebug){println("[TraceFix] skip${project.path}, tasks=${taskNames}")return}}apply plugin:'auto-add-systrace'println("[TraceFix] applied${project.path}, tasks=${taskNames}")设计意图:
- 本地 / IDE Sync(任务列表为空)→ 启用,方便开发
assembleXxxDebug→ 启用- 纯
assembleXxxRelease、流水线任务 → 跳过,避免污染正式包
4. 在根build.gradle批量挂到业务模块
多模块工程里,业务代码往往在library而非app。只对真正包含业务逻辑的模块插桩:
deftraceFixModules=['app','core',// 原 common 模块'feature-main',// 原 floatwindow 模块'feature-media',// 原 av 模块'feature-settings','feature-alert','compat-impl',]subprojects{sub->if(!traceFixModules.contains(sub.name)){return}sub.pluginManager.withPlugin('com.android.application'){sub.apply from:rootProject.file('tracefix.gradle')}sub.pluginManager.withPlugin('com.android.library'){sub.apply from:rootProject.file('tracefix.gradle')}}常见坑:只给
app模块加插件,而app几乎没有业务代码 → Perfetto 里看不到自己的方法。务必覆盖实际承载逻辑的 library。
5. 编译验证
./gradlew clean :app:assembleDomesticDebug构建日志中应出现:
[TraceFix] applied :feature-main, tasks=[...] [INFO][TraceFixPlugin]register official class instrumentation for project: :feature-main [INFO][TraceFixPlugin]instrumentation registered for variant: domesticDebug用javap抽查某个业务类(类名已脱敏):
javap-c\feature-main/build/intermediates/classes/.../MyApplication.class\|grepTrace期望输出:
invokestatic #xxx // Method android/os/Trace.beginSection:(Ljava/lang/String;)V invokestatic #xxx // Method android/os/Trace.endSection:()V说明插桩已写入字节码。源码里不会出现这些调用,这是正常的。
如何抓 trace
TraceFix 插桩后,需用系统工具采集。示例(包名已脱敏):
adb shell atrace-b65536-t15\-acom.example.myapp\sched am wm gfx view binder_driver dalvik app参数说明
| 参数 | 作用 |
|---|---|
-b 65536 | buffer 64MB,减少数据被覆盖 |
-t 15 | 采集 15 秒 |
-a com.example.myapp | 只采该 App 的 App 侧 trace,过滤其他应用 |
sched am wm ... | 系统类别:CPU 调度、Activity、窗口、渲染、Binder 等 |
app | 必选,TraceFix 的beginSection走这个类别 |
为什么需要-a?
- 不加
-a:所有应用的apptrace 都会进来,难以定位 - 加
-a:只保留目标包名的方法 slice,和 TraceFix 配合更清晰
推荐操作节奏
# 1. 安装 Debug 包(带 TraceFix 插桩)adbinstall-rapp-domestic-debug.apk# 2. 可选:冷启动adb shell am force-stop com.example.myapp# 3. 执行 atrace,立刻在手机上复现场景(如:启动功能、点按钮)adb shell atrace-b65536-t15-acom.example.myapp sched am wm gfx view binder_driver dalvik app# 4. 用 Perfetto UI 打开输出文件# https://ui.perfetto.dev在 Perfetto 中:
- 找到进程
com.example.myapp - 展开对应线程的时间线
- 查找带方法名 + hash 后缀的 slice(TraceFix 命名规则)
为什么建议只开 Debug
| 构建类型 | 是否插桩 | 原因 |
|---|---|---|
| Debug | ✅ | 本地性能分析 |
| Release | ❌ | 避免体积、性能、流水线包受影响 |
| Coverage / 专用测试变体 | ❌ | 与插桩无关,保持构建纯净 |
通过prop_traceFixDebugOnly=true+tracefix.gradle的任务名判断实现,无需改业务代码。
若希望 Release 本地包也能 trace,设prop_traceFixDebugOnly=false即可(不建议用于正式发布)。
常见问题
Q1:Debug 包了,Perfetto 还是看不到自己的方法?
按顺序排查:
- 是否 clean 全量重编?library 模块未重编会沿用旧产物
- 业务模块是否都挂了 TraceFix?只插
app往往不够 - 采集命令是否包含
app类别? - 是否加了
-a 你的包名? - 是否在源码里找
Trace.beginSection?应在字节码 / Perfetto 里看
Q2:编译出现 D8 警告Expected stack map table?
TraceFix 插桩后偶发,多数情况下WARNING 不影响trace 采集与运行。若变成 ERROR 再针对性处理。
Q3:和 btrace 3.0 怎么选?
| TraceFix | btrace 3.0 | |
|---|---|---|
| 原理 | 编译期插桩 | 运行时 Hook + 同步抓栈 |
| 改源码 | 不需要 | 需在Application.attachBaseContext调RheaTrace3.init() |
| Android 15+ | 可用 | 官方已知:对象分配 Hook 在 15+ 不生效,易出现空 trace |
| 方法覆盖 | 可插桩的方法较全 | 主要在对象创建、锁等抓栈点 |
在高版本 Android(API 35+)上做启动分析,TraceFix 往往更稳妥。
Q4:内网拉不到 TraceFix 依赖?
- 确认 Maven 镜像已代理 Maven Central
- 不要把
google()放在buildscript最前导致 SSL 失败 - 可手动确认:
io.github.gracker:TraceFix:0.1.0是否在镜像中存在
最小接入清单(Checklist)
gradle.properties配置版本与开关- 根
build.gradle添加classpath - 新建
tracefix.gradle subprojects挂到所有业务 library + appclean后编 Debug 包javap确认Trace.beginSection存在atrace采集时带-a和app- Perfetto UI 分析
参考链接
- TraceFix 仓库:https://github.com/Gracker/TraceFix
- 中文 README:README_zh.md
- Perfetto UI:https://ui.perfetto.dev
使用网页版抓取trace注意事项
要配置抓取的包名。
验证
查看生成的参数
要有这个