Perfetto Trace Processor RPC 架构深度解析:WASM 桥接与 HTTP 远程调用
【免费下载链接】perfettoProduction-grade client-side tracing, profiling, and analysis for complex software systems.项目地址: https://gitcode.com/GitHub_Trending/pe/perfetto
导读
本文围绕 src/trace_processor/rpc/README.md 所描述的 Trace Processor 远程调用(RPC)体系展开,剖析 Perfetto 如何让「进程内 C++ 库」形态的 Trace Processor 变成可被浏览器(WASM)、远程 Python 客户端与超大规模 trace(>2GB)场景访问的远程服务。读完本文,你将掌握wasm_bridge与httpd两条远程通道的工作原理、trace_processor_shell -D的 HTTP 服务端用法、RPC 线上协议的帧格式与全部TPM_*方法,以及--remote客户端与 stdio 管道的实现路径。
一、为什么需要 RPC:Trace Processor 的三种使用形态
Perfetto 的 Trace Processor 本质上是一个把原始 trace(ftrace、systrace、proto 等)解析进 SQLite 数据库、并提供 PerfettoSQL 查询能力的高性能分析引擎。在 protos/perfetto/trace_processor/trace_processor.proto 的头部注释中,官方明确列出了三种使用模式:
- 进程内(in-process)模式:从 C++ 直接链接调用,或直接使用
trace_processor_shell交互式 CLI。此时不涉及任何二进制编解码,直接面对 include/perfetto/trace_processor/trace_processor.h 定义的公共 C++ API。 - WASM 模式:Trace Processor 被编译成 WebAssembly,运行在浏览器标签页内的 worker 中,通过 JS↔WASM 互操作调用,对应 src/trace_processor/rpc/wasm_bridge.cc。
- HTTP+RPC 模式:以
trace_processor_shell -D启动一个独立的 HTTP 服务,UI 与 Python API 通过网络协议访问,对应 src/trace_processor/rpc/httpd.cc。
src/trace_processor/rpc/目录正是后两种「远程」形态的核心实现,由两大目标(target)构成:wasm_bridge(WASM 互操作桥)与httpd(HTTP RPC 模块)。其核心枢纽是 src/trace_processor/rpc/rpc.h 中定义的Rpc类——它承担了协议编解码(marshalling/unmarshalling)的职责,但不关心具体传输层(是 HTTP、WebSocket、UNIX socket 还是 WASM 的 postMessage 通道),传输层由各自模块实现。
二、核心枢纽:Rpc 类与字节管道(byte-pipe)协议
2.1 Rpc 类设计
Rpc类是整套远程调用体系的神经中枢。从 src/trace_processor/rpc/rpc.h 的注释可以看到它的定位:
本类处理 Trace Processor RPC API 的二进制 {,un}marshalling……当 Trace Processor 的客户端不是进程内 C++ 代码而是远程进程时使用。本类不定义传输如何工作(HTTP 还是 WASM 互操作调用),只负责编解码。
关键设计点:
Rpc内部创建并持有一个TraceProcessor实例,其生命周期与Rpc实例绑定(构造时也可通过std::unique_ptr<TraceProcessor>参数接管外部已创建好的实例)。Rpc的方法集合是公共TraceProcessor接口的一个子集镜像,但入参出参都是 proto 编码的二进制缓冲区(std::vector<uint8_t>/ 裸指针 + 长度)。- 从 v15 开始引入了byte-pipe(字节管道)RPC 接口(见 rpc.h):这是一个与远程 Trace Processor 实例的双向字节通道,底层可以是 TCP socket、两个进程间的
pipe(2)、WebSocket,或是 JS+WASM 场景下的 postMessage 通道。线上交换的消息全部是TraceProcessorRpcproto。
2.2 线上帧格式
protos/perfetto/trace_processor/trace_processor.proto 定义了最底层的线上协议:字节管道两侧各自是一串线性的TraceProcessorRpc消息序列,每条消息之前都带有一个 proto tag(field = 1、type = length-delimited)和 varint 编码的长度前缀。这样整条流也可以被当作TraceProcessorRpcStream的 repeated 字段来读写——这与trace.proto中Trace与TracePacket的复用技巧一致。
TraceProcessorRpc消息的关键字段:
| 字段 | 类型 | 说明 |
|---|---|---|
seq | int64 | 单调递增计数器,仅用于调试,检测底层流是否丢包/重复。注意:响应不保证与请求同号,因为一次请求(如返回多行的查询)可能产生多条响应消息。 |
request/response | TraceProcessorMethod | 一对 oneof,分别承载客户端→服务端的请求方法与服务端→客户端的响应方法。 |
invalid_request | TraceProcessorMethod | 客户端发来服务端不认识的方法时返回(典型场景:客户端比服务端新),可用于客户端做能力探测。 |
fatal_error | string | 出现不可恢复错误时返回,典型情况是seq序列被打断(例如两个 UI 标签页同时连接同一个--httpd实例)。 |
消息交换按请求序号严格递增进行校验:在 rpc.cc 中,如果收到req.seq() != 0 && rx_seq_id_ != 0 && req.seq() != rx_seq_id_ + 1,服务端会记录(ERR:rpc_seq)错误(UI 的error_dialog.ts会拦截该错误串)并通过fatal_error断开连接。同时协议允许序号从 0 重启——这正是浏览器刷新后重新连接外部trace_processor_shell --httpd的场景。
2.3 方法分派与全部 TPM_* 方法
消息的request/response字段使用TraceProcessorMethod枚举(定义于 trace_processor.proto),目前共 16 个有效方法(4、12、14 已保留废弃):
| 方法名 | 编号 | 用途 |
|---|---|---|
TPM_APPEND_TRACE_DATA | 1 | 追加一段 trace 二进制数据给解析器 |
TPM_FINALIZE_TRACE_DATA | 2 | 通知解析器数据已结束(EOF) |
TPM_QUERY_STREAMING | 3 | 以流式批次执行 SQL 查询 |
TPM_COMPUTE_METRIC | 5 | 计算指定指标(metrics) |
TPM_GET_METRIC_DESCRIPTORS | 6 | 获取指标描述符集合 |
TPM_RESTORE_INITIAL_TABLES | 7 | 恢复初始表:删除 trace 加载后(UI/用户)创建的所有表与视图,保留内置表 |
TPM_ENABLE_METATRACE | 8 | 开启 metatrace(TP 自身性能追踪) |
TPM_DISABLE_AND_READ_METATRACE | 9 | 停止并读取 metatrace 结果 |
TPM_GET_STATUS | 10 | 获取服务端状态(已加载 trace 名、版本、API 版本) |
TPM_RESET_TRACE_PROCESSOR | 11 | 按新的 Config 重置 Trace Processor |
TPM_REGISTER_SQL_PACKAGE | 13 | 注册 SQL 包(PerfettoSQL 模块) |
TPM_SUMMARIZE_TRACE | 15 | 生成 trace 摘要(Trace Summary) |
TPM_CREATE_SUMMARIZER | 16 | 创建 Summarizer(摘要器)实例 |
TPM_UPDATE_SUMMARIZER_SPEC | 17 | 更新 Summarizer 的 spec |
TPM_QUERY_SUMMARIZER | 18 | 查询 Summarizer 结果 |
TPM_DESTROY_SUMMARIZER | 19 | 销毁 Summarizer |
TPM_STATEMENT_STREAMING | 20 | 逐条执行 SQL 语句(sqlite3_prepare_v2 风格,支持 start_offset 游标) |
TPM_EXPORT | 21 | 导出 trace(Perfetto / Arrow TAR / SQLite 三种格式) |
ParseRpcRequest(rpc.cc)是方法分派的 switch 中枢。值得注意的实现细节:
TPM_QUERY_STREAMING:把 SQL 交给ExecuteQuery执行后,用QueryResultSerializer将结果切分为约 128KB 的批次,通过StreamSerializerResponses逐批回调发送(回调是内联执行的,整条调用链在一个 callstack 内完成)。- 防呆保护:如果单个 cell 超过 256 MiB 导致批次过大(proto 4 字节长度字段溢出),服务端会优雅地放弃该批次并返回错误提示「Consider writing large values to a file instead」,而不是产生无法解析的帧(rpc.cc)。
- 未知方法处理:
default分支回复invalid_request,让新客户端可以借此做特性探测(rpc.cc)。 TPM_RESET_TRACE_PROCESSOR:支持在重置时设置drop_track_event_data_before、ingest_ftrace_in_raw_table、analyze_trace_proto_content、ftrace_drop_until_all_cpus_valid、parsing_mode(DEFAULT / TOKENIZE_ONLY / TOKENIZE_AND_SORT)、sorting_mode(DEFAULT_HEURISTICS / FORCE_FULL_SORT)以及extra_parsing_descriptors(运行时注入解析描述符)等配置(rpc.cc)。
2.4 查询结果的批次化编码
TPM_QUERY_STREAMING的响应是QueryResult,其核心是CellsBatch(trace_processor.proto):
- 单元格按「行 → 列」顺序扫描,一个批次内含
cells(类型数组:CELL_NULL/CELL_VARINT/CELL_FLOAT64/CELL_STRING/CELL_BLOB)+ 按类型分列的 packed 数据字段; - 字符串单元被 NUL 结尾后拼接进单个
string_cells字段——这是刻意为之的性能优化:JS 解码字符串开销大,一次性 decode +split('\0')比逐条解码 N 个字符串快得多; - 批次在「数千个 cell」或「字符串/blob 负载数百 KB」时触发切分,
is_last_batch标记最后一批; - 响应还携带
statement_count、statement_with_output_count、last_statement_sql与累计elapsed_time_ms。
序列化与反序列化分别由 src/trace_processor/rpc/query_result_serializer.cc(含 benchmark 与单测)和 src/trace_processor/rpc/query_result_deserializer.cc 实现,--remote客户端复用了同一套 wire 格式(见 query_result_deserializer.h 注释)。
三、wasm_bridge:浏览器内的原生解析引擎
wasm_bridge是JS/HTML 通过 WASM 的ccall调用 Trace Processor的互操作桥,对应文件 src/trace_processor/rpc/wasm_bridge.cc。它是 Perfetto UI 在浏览器内直接分析 trace 的默认路径——UI 的 WASM worker 里跑着完整的 Trace Processor。
对外导出的 C 函数(extern "C"+EMSCRIPTEN_KEEPALIVE)只有两个:
trace_processor_rpc_init(RpcResponseFn*, max_write_size):创建全局Rpc实例与Rpc::Stream,在 WASM 线性内存中预留一块max_write_size大小的写缓冲区,返回 JS 需要写入首个请求的地址。RpcResponseFn是一个由wasm_bridge.ts传入的 JS 绑定回调:当 C++ 侧有响应时,回调进入 JS,JS 把 proto 编码的TraceProcessorRpc响应拷贝出来并postMessage()给 controller 线程。trace_processor_on_rpc_request(size):EndRequest(size)结束当前请求、BeginRequest预留下一块缓冲区并返回下一个可写地址。设计上刻意「返回下一个地址而不是接收地址」,这样每个请求只需一次 JS→WASM 调用,因为 JS 在知道大小之前就能先拿到写入位置。
由于 WASM 的size_t是 32 位,Rpc接口中所有长度参数均使用uint32_t而非size_t,以避免 ABI 不匹配(见 rpc.h 注释)。此外wasm_bridge.cc中还注册了内存耗尽处理器:当sbrk()扩容失败时统一 abort 并输出与_emscripten_resize_heap一致的 OOM 消息,让 UI 的error_dialog.ts能弹出内存不足提示。
该桥接的 TS 侧实现位于ui/src/trace_processor/wasm_bridge.ts(同一目录下的姊妹文件),负责 JS↔WASM 的完整调用栈编排。
四、httpd:面向大 trace 与 Python 的 HTTP RPC 服务
4.1 定位与启动方式
httpd是 HTTP RPC 模块,暴露一个protobuf-over-HTTP 的 RPC 接口,允许与远程 Trace Processor 实例交互。README 明确其两大使用场景:
- 超大型 trace(>2GB):超过 WASM 内存上限的 trace 必须交给原生进程解析,UI 通过与本地
--httpd服务通信获得「Trace Processor 原生加速」; - Python 互操作:
perfetto.TraceProcessor(addr='localhost:9001')即通过该接口工作。
启动命令来自 src/trace_processor/trace_processor_shell.cc:
trace_processor_shell -D [trace_file.pb]相关命令行参数:
| 参数 | 说明 |
|---|---|
-D, --httpd | 启用 HTTP RPC 服务器 |
--http-port PORT | 指定 HTTP RPC 端口(默认9001,常量kBindPort定义于 httpd.cc) |
--http-ip-address ip | 指定监听 IP(默认localhost) |
--http-additional-cors-origins origin1,origin2,... | 追加 CORS 白名单,默认白名单为https://ui.perfetto.dev、http://localhost:10000、http://127.0.0.1:10000(httpd.cc) |
--stdiod | 同时启用 stdio RPC 服务器(见第五节) |
服务启动后,UI 侧的工作方式为:打开或刷新https://ui.perfetto.dev,在弹出「Trace Processor native acceleration」对话框时点击 YES,UI 会自动连接localhost:9001。访问http://127.0.0.1:9001/会返回一段纯文本帮助页(httpd.cc),其中也提到了 Python 用法perfetto.TraceProcessor(addr='localhost:9001')。相关文档见 docs/visualization/large-traces.md 与 docs/analysis/trace-processor.md。
4.2 传输通道:WebSocket 优先,chunked HTTP 兼容
httpd.cc中Httpd类继承base::HttpRequestHandler,为每个连接维护独立的ConnState(每个连接持有一个Rpc::Stream——因为分帧状态(tokenizer)不能跨连接共享,两个对端的字节交错进同一个 tokenizer 会互相破坏)。RPC 字节流经SendRpcChunk发送(httpd.cc):
- WebSocket 通道(
/websocket端点,UI 使用):直接SendWebsocketMessage发送帧;握手时若 Origin 不在 CORS 白名单会返回 403; - chunked HTTP 通道(
/rpc端点,Python API 使用):每个 RPC 调用一次 POST,响应以Transfer-Encoding: chunked分块返回; data == nullptr表示不可恢复的 RPC 错误,服务端发送终止块0\r\n\r\n后关闭连接。
4.3 全部 REST 端点
OnHttpRequest(httpd.cc)把 HTTP URI 映射到Rpc方法,存在三代端点并存的局面:
第一代(UI 当前使用)
| 端点 | 说明 |
|---|---|
/status | 返回StatusResult:已加载 trace 名、人类可读版本号、version_code、api_version |
/websocket | WebSocket 升级握手,承载 byte-pipe RPC 流 |
第二代(/rpcchunked 端点,Python API 仍在使用):所有 RPC 方法复用到这一个端点,每个 POST 请求一次 RPC 调用,x-seq-id请求头承载序号以做乱序检测。
第三代(REST 风格,每个方法一个端点,UI 已不用、未来可能移除):/parse、/notify_eof、/restore_initial_tables、/query(chunked 分批返回)、/compute_metric、/trace_summary、/enable_metatrace、/disable_and_read_metatrace、/export。
代码注释(httpd.cc)解释了两代 legacy 端点的由来:最初每个方法一个 REST 端点、再演进出/rpc复用端点,最终迁移到 byte-pipe + WebSocket 模型,因为它们共享完全相同的{de,}serialization格式,只是方法分派的位置不同(见 rpc.h 注释)。
4.4 状态与版本协商
TPM_GET_STATUS//status返回的StatusResult(trace_processor.proto)包含:
loaded_trace_name:当前已加载的 trace 名(使用trace_processor_shell -D trace_file.pftrace启动时会预加载);human_readable_version:类似v11.0.123,仅供人类展示,不可解析;version_code:类似v42.1-deadbeef0,可用于相等性比较来判断服务端与 UI 是否同一构建版本;api_version:即TRACE_PROCESSOR_CURRENT_API_VERSION,当前为14(trace_processor.proto)。
API 版本机制的设计意图(proto 注释有详细说明):v15 之前每次 UI 依赖的新特性(新表、新 SQL 算子、UI 必需的新指标)都会递增该值,但容易漏更;现在 UI 通过api_version与version_code做更激进的版本匹配——若服务端与 UI 不匹配,UI 会尝试引导用户跳转到匹配版本的 UI。注意human_readable_version明确「不要解析」,而version_code只用于等值比较。
五、stdiod 与 --remote:更多传输形态
5.1 stdiod:stdin/stdout 管道
src/trace_processor/rpc/stdiod.cc 实现了最简单的字节管道传输:以--stdiod启动后,RunStdioRpcServer从STDIN_FILENO按 4096 字节块读取请求、把响应写到STDOUT_FILENO。stdin/stdout 本质上是单对端的管道,所以全局只需一个Rpc::Stream。该模式常用于进程间管道协作与调试。
5.2 --remote 客户端与 RemoteTraceProcessor
与httpd服务端配套的是--remote客户端模式:src/trace_processor/rpc/remote_trace_processor.cc 实现了一个「把每个方法调用 marshal 到远端」的TraceProcessor实现,让上层代码几乎无感地使用远程实例。
传输层抽象为 src/trace_processor/rpc/rpc_transport.h 中的RpcTransport接口(Send/Recv),两种实现:
- WebSocket 客户端:连接 http 服务的
/websocket端点; - AF_UNIX 字节管道:连接 UNIX socket 文件。
地址解析规则(ConnectRpcTransport):host:port或scheme://→ WebSocket;绝对路径或*.sock→ AF_UNIX 管道;裸会话名 → 约定路径下的 AF_UNIX 管道(会话路径约定集中在 src/trace_processor/rpc/session_paths.cc,配套tp server unix与--remote <addr>双向使用)。会话生命周期管理(空闲超时、IdleReaper 等)在 src/trace_processor/rpc/session_lifecycle.cc。
RemoteTraceProcessor也明确标注了限制:RegisterFileContent、RegisterMetric、ExtendMetricsProto等方法不支持 --remote(返回ErrStatus,见 remote_trace_processor.cc)。另外--remote模式下 trace 已由会话加载配置,部分 shell 标志不能与其组合(见 src/trace_processor/shell/common_flags.cc)。
六、围绕 RPC 的辅助能力:指标、摘要、导出与 SQL 包
除了查询,byte-pipe 协议还承载了 Trace Processor 的多项核心能力,全部在Rpc层有对应实现:
- 指标计算(
TPM_COMPUTE_METRIC):ComputeMetricInternal支持三种输出格式(rpc.cc)——BINARY_PROTOBUF、TEXTPROTO、JSON。proto 注释特别说明bytes metrics之所以不直接引用TraceMetrics类型,是为了避免为 metrics protos 生成 protozero 代码,改用反射机制编解码,从而支持在运行时读取新的 proto。 - trace 摘要(
TPM_SUMMARIZE_TRACE):ComputeTraceSummaryInternal接收二进制/textproto 形式的TraceSummarySpec与计算规格(指定 metric ID 列表或run_all_metrics),输出二进制或 textproto 摘要(rpc.cc)。 - Summarizer(
TPM_CREATE/UPDATE/QUERY/DESTROY_SUMMARIZER):以客户端提供的 ID 管理多个 Summarizer 实例(summarizers_FlatHashMap),支持查询结果的存在性判断、表名、行数、列、耗时、standalone_sql(可独立复制运行、不引用内部物化表)等元信息(rpc.cc)。 - 导出(
TPM_EXPORT):支持PERFETTO、ARROW_TAR、SQLITE三种格式。SQLite 导出需要随机访问输出,服务端会在服务端控制的临时文件中物化数据库再流式回传——客户端永远不会指定服务端上的路径,因此不会暴露服务端文件系统(rpc.cc)。 - SQL 包注册(
TPM_REGISTER_SQL_PACKAGE):按「包名 + 模块列表 +allow_override」注册 PerfettoSQL 模块,对应trace_processor_shell的--add-sql-package PATH[@PACKAGE]参数(trace_processor_shell.cc)。 - Metatrace(
TPM_ENABLE/DISABLE_AND_READ_METATRACE):以类别位掩码(QUERY_TIMELINE、QUERY_DETAILED、FUNCTION_CALL、DB、API_TIMELINE)开启 TP 自身的性能追踪,结果以Trace消息字节返回(rpc.cc)。
七、测试与工程质量
该目录附带了完整的测试保障:
- src/trace_processor/rpc/rpc_unittest.cc:RPC 编解码与分派逻辑的单测;
- src/trace_processor/rpc/query_result_serializer_unittest.cc 与 query_result_serializer_benchmark.cc:查询结果批次化序列化的正确性与性能测试;
- src/trace_processor/rpc/session_paths_unittest.cc:会话路径解析测试。
构建方面,目标定义在 src/trace_processor/rpc/BUILD.gn(httpd、wasm_bridge、stdiod、remote客户端等多个 source_set),并在 src/trace_processor/trace_processor_shell.cc 与 src/trace_processor/shell/BUILD.gn 中接入 shell 可执行文件。
结语
src/trace_processor/rpc/目录体现了 Perfetto 在「单机进程内引擎」与「远程服务」之间架设的一条高度统一的协议通道:无论传输层是 WASM 线性内存、WebSocket、chunked HTTP、UNIX socket 还是 stdin/stdout 管道,上层都复用同一套TraceProcessorRpc字节管道协议与Rpc编解码逻辑。理解这套架构后,你既可以用trace_processor_shell -D为超大 trace 提供原生加速,也可以通过perfetto.TraceProcessor(addr=...)编写远程分析脚本,甚至可以为 Trace Processor 实现自定义的传输适配器——只要遵循TraceProcessorRpcStream帧格式与seq序列规则即可。
【免费下载链接】perfettoProduction-grade client-side tracing, profiling, and analysis for complex software systems.项目地址: https://gitcode.com/GitHub_Trending/pe/perfetto
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考