内容提要
本文介绍如何用 Perfetto SDK 的 Track Event 为系统 Trace 补充业务语义,解决系统 Trace 无法反映解码帧、加载场景、任务排队等业务信息的问题。内容按接入顺序展开:判断是否需要接入及接入层级,定义业务阶段字典与 backend,给出最小接入骨架、category 开关、与系统 Trace 合抓、跨线程 flow,最后说明如何控制写入量及 App 侧现场 Trace 协议。
延伸解读
先判断是否需要接入 SDK
文章强调,没有业务语义缺口时不必接 Perfetto SDK。Java/Kotlin 层临时标记函数耗时可用 android.os.Trace 或 AndroidX tracing,抓取时打开应用 atrace;native 长期埋点、跨平台引擎、游戏、播放器、Camera pipeline 才适合 Track Event。只有普通 slice/counter 表达不了复杂结构化状态时,再考虑自定义 DataSource。选型应先看场景,而不是为了形式替换已有 ATrace。
业务阶段字典要先于代码
Track Event 不是把宏塞进每个函数。文章建议先定义业务阶段字典,明确事件名、category、phase、必填参数、单位、采样和报告指标,再写代码。字段改名、删字段、改单位都要提升 arg_schema_version。长期报告字段不要叫 debug.frame_id,而应使用业务 schema 中的 frame_id/request_id,并记录 frame_id_domain、nullable_policy 和兼容策略,否则后续 SQL 和看板难以稳定复用。
category 是线上开关,不是随手命名
category 决定哪些事件被打开,应作为配置接口设计。C++ SDK 中未命中规则的非 debug、非 slow category 默认打开,C SDK 默认相反,因此线上 preset 建议显式 disabled_categories: "*" 再白名单打开。debug 和 slow tag 默认关闭,专项分析时再开。category 按子系统和开关粒度划分,页面、实验组、请求类型放参数,避免 category 数量膨胀。
现场 Trace 要拆成三层协议
App 侧接 Perfetto 最容易把 App 当成小型 adb shell 客户端,但普通 App 没有随意启动系统 tracing 的权限。文章建议拆成平台侧预置受控 TraceConfig、App 侧记录埋点并按约定激活 trigger、服务端接收 Trace package 并跑 stats 与摘要。STOP trigger 配合 Ring Buffer 可保留问题发生前的一段现场,触发名进入 preset 后要保持稳定,改名会让服务端聚合和历史数据断开。
Q&A
Perfetto SDK 的 Track Event 主要解决什么问题?
Track Event 用于为系统 Trace 补充业务语义,解决系统 Trace 无法反映解码帧、加载场景、任务排队等业务信息的问题。系统 Trace 能告诉你线程何时运行、CPU 在哪个核、FrameTimeline 哪一帧超时,但不知道播放器正在解哪个 frame id、游戏引擎正在加载哪个 scene、业务任务在队列里排了多久。Track Event 负责告诉你当时在 decode 哪一帧、哪个队列堵住、哪个请求跨线程流转,与系统 Trace 合在一起才能把“主线程慢了 18ms”缩小到具体帧附近的候选区间。
什么情况下应该用 android.os.Trace,什么情况下用 Perfetto SDK?
Java/Kotlin 层临时标记函数耗时用 android.os.Trace 或 AndroidX tracing,抓取时打开应用 atrace;Android-only 场景如果 android.os.Trace 和 NDK ATrace_* 已经够用,官方建议继续使用。Perfetto SDK 更适合 native 模块、跨平台模块(如跨平台引擎、游戏、播放器、Camera pipeline)以及需要结构化业务语义的场景,它直接产生 Track Event,支持 category、slice、counter、flow、参数、独立 track,方便和系统 Trace 放在同一时间轴。简单 Android-only section 不必为了形式替换 ATrace。
Perfetto SDK 的 Track Event 和自定义 DataSource 分别适合什么场景?
Track Event 适合表达函数耗时、业务阶段、队列长度、frame id、请求 id、跨线程关系,接入成本低,Trace Processor 和 UI 原生支持。自定义 DataSource 适合普通 slice、counter 和 debug annotation 表达不了的结构(如周期性转储子系统状态),或数据量太大需要强类型 schema 减少每条事件体积的场景。自定义 DataSource 需要维护 schema,通常还要给 Trace Processor 补解析,门槛更高,不适合作为第一版埋点方案。只要 slice、counter、flow 和参数能表达问题,优先用 Track Event。
如何设计业务阶段字典?
先定义业务阶段字典,再写代码。字典要让后续 SQL、报告和看板知道:事件叫什么、属于哪个阶段、字段单位是什么、缺字段时怎么降级。例如 DecodeFrame 属于 player 阶段,必填参数 frame_id、stream_id、codec,单位/基数是 frame_id 在 stream_id 内唯一,采样每帧一次,报告指标 decode_dur_ms、decode_max_ms。字段改名、删字段、改单位都要提升 arg_schema_version。长期报告字段不要叫 debug.frame_id,而应叫业务 schema 里的 frame_id / request_id,并记录 frame_id_domain、nullable_policy 和兼容策略。缺 RenderSubmit 时,报告只能输出 decode/upload 阶段,不能写端到端结论,字段里应写 missing_marker=RenderSubmit、evidence_grade=partial。
Perfetto SDK 的 in-process backend 和 system backend 有什么区别?
kInProcessBackend 由 App 自己创建 tracing session,适合单进程验证、离线测试、只看业务事件。kSystemBackend 由外部 perfetto 命令或系统服务控制,适合和 sched、freq、Binder、FrameTimeline 合并分析。Android 性能分析里更常用 system backend,App 在这里只是 producer,系统 tracing session 决定什么时候开始、什么时候停止。收集系统 Trace 时,App 不能自己读取整份系统 Trace,避免拿到其他进程的数据。
如何用 Perfetto SDK 实现跨线程 flow?
用 flow 把相关事件连起来。不要直接把容易复用的业务 frame_id 当 flow id,先分配进程内不复用的 token。例如在 EnqueueFrame、DecodeFrame 中使用 perfetto::Flow::ProcessScoped(flow_id),在最终消费阶段 RenderFrame 中使用 perfetto::TerminatingFlow::ProcessScoped(flow_id)。选中事件时,Perfetto UI 会用箭头展示关系。ProcessScoped 要求 id 至少要在进程内、一次 Trace 生命周期内稳定,不要直接拿容易复用的临时指针当 flow id;跨进程 flow 不要继续用 ProcessScoped 语义,至少要使用全局唯一 id,并明确 namespace 和来源进程。多阶段 pipeline 也可以给每条边单独分配 flow id,避免一条 flow 被多条路径复用。
如何控制 Track Event 的写入量?
Track Event 开销低但不是免费。建议给每类埋点设预算:按 events/sec、每帧事件数、参数字节数和目标 Trace 时长估算写入量;高频 debug category 默认关闭,专项 Trace 可用 1/N 采样或触发后短窗口开启;昂贵字符串、JSON、容器遍历、锁内状态快照放在 enabled 判断或 lambda 内,参数在宏调用前已经拼好时 category gating 救不了这部分开销。producer 侧兜底:确认瓶颈来自 shared memory 突发时,再评估 shmem_size_hint_kb 和 BufferExhaustedPolicy::kStall,默认先降事件密度、拆大包。采集验收:长 Trace 里把业务 Track Event 放到独立 central buffer,抓完用验收查询过一遍 traced_buf_* 和 track_event_* 统计项。
App 侧现场 Trace 协议如何设计?
现场方案拆成三层:平台侧预置受控 TraceConfig,声明 trigger、Ring Buffer、文件路径、数据源白名单和采集上限;App 侧记录业务埋点和页面状态,检测慢帧、卡死、超时等异常,按平台约定激活 trigger;服务端接收 Trace package,跑 stats、摘要 SQL 和聚合索引。Perfetto 的 STOP trigger 适合这类场景:trace 先以 Ring Buffer 方式循环记录,App 检测到问题后触发一个已声明的名字,系统在 stop_delay_ms 之后收尾。线上 App 不应假设自己能执行 /system/bin/trigger_perfetto,更推荐由平台提供受控接口,App 只提交受限请求。Trace package 要带上下文,最小必需包括 Trace 本体、业务 metadata、采集 preset、stats 可信度结果。