Perfetto 扩展服务器协议参考为 Perfetto UI 构建自定义宏、SQL 模块与 Proto 描述符分发端点【免费下载链接】perfettoProduction-grade client-side tracing, profiling, and analysis for complex software systems.项目地址: https://gitcode.com/GitHub_Trending/pe/perfetto导读Perfetto UI 支持通过 HTTP(S) 扩展服务器Extension Server向浏览器端分发可复用的宏Macros、PerfettoSQL 模块和 Proto 描述符让团队与组织能够把常见的 trace 分析工作流集中托管、按需加载。本文以仓库中的协议参考文档 docs/visualization/extension-server-protocol.md 为主体完整讲解扩展服务器必须实现的端点、Manifest 与各资源的 JSON 结构、命名空间约束、CORS 与鉴权要求并结合 ui/src/core_plugins/dev.perfetto.ExtensionServers 下的真实前端实现与单元测试给出可直接复制运行的静态服务器与 Python/Flask 动态服务器示例。读完本文你将具备从零构建一个可被 Perfetto UI 识别、加载并安全分发扩展的自定义服务器能力。概述扩展服务器是什么扩展服务器是承载manifest、macros、sql_modules、proto_descriptors四类 HTTP(S) 资源的端点。UI 侧的实现由dev.perfetto.ExtensionServers插件完成见 ui/src/core_plugins/dev.perfetto.ExtensionServers/index.ts它在启动时逐个拉取已配置服务器的 manifest再根据 manifest 声明的 features 与用户启用的 modules 加载对应资源。扩展服务器是可选的、非负载关键组件即使完全不配置任何服务器Perfetto UI 也能完整工作某个服务器不可达或返回错误时它会被跳过其余服务器与 UI 本体不受影响。所有扩展都是声明式的、安全的——宏只是 UI 命令序列不执行 JavaScriptSQL 模块运行在 trace processor 现有的沙箱内Proto 描述符仅是二进制类型定义纯数据。端点总览扩展服务器需要实现以下 HTTP(S) 端点{base_url}/manifest GET (required) {base_url}/modules/{module_id}/macros GET (optional) {base_url}/modules/{module_id}/sql_modules GET (optional) {base_url}/modules/{module_id}/proto_descriptors GET (optional)其中manifest是唯一必需端点其余三个可选端点仅在 manifest 声明了对应 feature 时才被 UI 拉取。这一点在源码中有明确体现loadMacros、loadSqlPackage、loadProtoDescriptors三个加载函数都会先检查manifest.features中是否包含对应名称见 extension_server.ts// Check if macros are supported. if (!manifest.features.find((f) f.name macros)) { // Not supported, return empty list. return okResult([]); }即未声明的 feature 不会被请求端点也可以安全地不实现。Manifest服务器的元数据入口端点GET {base_url}/manifestManifest 返回服务器元数据、支持的功能列表与可用模块列表。协议文档给出的完整示例{ name: Acme Corp Extensions, namespace: com.acme, features: [ {name: macros}, {name: sql_modules}, {name: proto_descriptors} ], modules: [ {id: default, name: Default}, {id: android, name: Android}, {id: chrome, name: Chrome} ] }字段说明字段类型必填说明namestring是人类可读的服务器名称显示在命令面板Command Palette的宏来源标签source chip中。namespacestring是反域名记法的唯一标识如com.acme用于对宏 ID 与 SQL 模块名实施命名约束。featuresarray是该服务器支持的功能每项含一个name字段。合法取值macros、sql_modules、proto_descriptors。modulesarray是可用模块列表。每项包含id用于 URL 路径与设置项与name人类可读的展示名。对于单模块服务器使用[{id: default, name: Default}]即可。从 UI 实现看manifest 的校验由 types.ts 中的 Zod schema 完成manifestSchema要求name、namespace、features、modules四个字段全部存在缺失任一字段都会导致校验失败。单元测试 extension_server_unittest.ts 中专门覆盖了“缺少 required 字段返回错误”的用例。模块modules与生命周期模块是扩展服务器组织内容的基本单位。例如一个企业级服务器可能提供default—— 通用宏与 SQL 模块android—— Android 专项分析工作流chrome—— Chrome 渲染性能工具当用户在 UI 中添加服务器时default模块会被自动选中其余模块需要手动开启。扩展只在UI 启动时加载一次修改服务器配置增删服务器、切换模块后需要刷新页面才能生效。Macros宏资源端点GET {base_url}/modules/{module_id}/macros仅在features包含{name: macros}时拉取。协议文档示例{ macros: [ { id: com.acme.StartupAnalysis, name: Startup Analysis, run: [ {id: dev.perfetto.RunQuery, args: [SELECT 1]}, {id: dev.perfetto.PinTracksByRegex, args: [.*CPU.*]} ] } ] }宏字段字段类型说明idstring唯一标识。必须以服务器 namespace 加.开头如com.acme.StartupAnalysis。namestring命令面板中展示的名称。runarray按顺序执行的命令列表。每项含命令id与args参数数组。run数组中的每项对应一条自动化命令。完整命令 ID 与参数清单见 Commands Automation Reference常用命令包括dev.perfetto.RunQuery—— 执行 PerfettoSQL 查询但不展示结果dev.perfetto.RunQueryAndShowTab—— 执行查询并在新查询页中展示结果dev.perfetto.PinTracksByRegex/ExpandTracksByRegex/CollapseTracksByRegex—— 按正则固定/展开/折叠轨道dev.perfetto.AddDebugSliceTrack/AddDebugCounterTrack—— 从 SQL 结果创建调试轨道dev.perfetto.CreateWorkspace/SwitchWorkspace/CopyTracksToWorkspaceByRegex—— 工作区操作dev.perfetto.AddNoteAtTimestamp—— 在指定时间戳添加备注宏的 schema 在 ui/src/core/command_manager.ts 中定义为macroSchemaid、name、run三项其中run是命令调用数组。加载完成后宏会带上来源标签default模块来源标签即服务器名非default模块为“服务器名: 模块名”sourceLabel函数见 extension_server.ts因此你可以在命令面板中区分宏来自哪个服务器、哪个模块。SQL 模块SQL 资源端点GET {base_url}/modules/{module_id}/sql_modules仅在features包含{name: sql_modules}时拉取。协议文档示例{ sql_modules: [ { name: com.acme.startup, sql: CREATE PERFETTO TABLE _startup_events AS SELECT ts, dur, name FROM slice WHERE name GLOB startup*; }, { name: com.acme.memory, sql: CREATE PERFETTO FUNCTION com_acme_rss_mb(upid INT) RETURNS FLOAT AS SELECT CAST(value AS FLOAT) / 1048576 FROM counter WHERE track_id IN (SELECT id FROM process_counter_track WHERE upid $upid AND name mem.rss) ORDER BY ts DESC LIMIT 1; } ] }SQL 模块字段字段类型说明namestring模块名。必须以服务器 namespace 加.开头如com.acme.startup。用户在查询编辑器中使用INCLUDE PERFETTO MODULE com.acme.startup;引用。sqlstringSQL 文本。可以包含CREATE PERFETTO TABLE、CREATE PERFETTO FUNCTION、CREATE PERFETTO VIEW或任何合法的 PerfettoSQL。加载后的 SQL 模块会注册为以服务器 namespace 命名的 SQL 包package。一个值得注意的实现细节同一服务器多个模块的 SQL 会被合并进同一个包而不是按模块分开注册——这是因为 trace_processor 后端按包名存储分开注册会导致后注册的模块静默覆盖先前的注册。单元测试coalesces multiple modules from same server与merges sql_modules from multiple modules into one package见 extension_server_unittest.ts正是对这一行为的回归测试。关于 PerfettoSQL 的编写方式可参考 PerfettoSQL Getting Started。Proto 描述符解码自定义协议消息端点GET {base_url}/modules/{module_id}/proto_descriptors仅在features包含{name: proto_descriptors}时拉取。协议文档示例{ proto_descriptors: [ CgdteV9wcm90bxIHbXlwcm90byI..., Cghhbm90aGVyEghhbm90aGVyIi... ] }字段说明字段类型说明proto_descriptorsarray of stringsBase64 编码的FileDescriptorSetprotobuf 消息数组。它们允许 UI 解码 trace 中嵌入的自定义 protobuf 消息而无需把.proto文件编译进 UI 本身。注意Proto 描述符不受命名空间约束——protobuf 消息本身就有基于 package 的命名空间机制protoDescriptorSchema仅是z.string()数组见 types.ts。命名空间强制约束所有宏 ID 与 SQL 模块名都必须以 manifest 中namespace的值加.开头。例如 namespace 为com.acme的服务器只能提供宏 IDcom.acme.StartupAnalysis、com.acme.MemoryCheckSQL 模块名com.acme.startup、com.acme.memory.helpersUI 会实际执行这一校验并拒绝违反约定的扩展从而避免用户配置多个扩展服务器时出现命名冲突。这一校验在两个层面实现加载期校验loadMacros遍历所有宏检查macro.id.startsWith(manifest.namespace .)不满足则返回错误Macro ID ... must start with namespace ...loadSqlPackage对 SQL 模块名做同样的校验见 extension_server.ts。校验失败的资源会被整体丢弃相关测试见rejects macros with invalid namespace prefix与rejects sql modules with invalid namespace prefix。Schema 校验manifest 与各资源的 JSON 都会经过 types.ts 中 Zod schema 的解析结构不符合预期如缺少必填字段即返回“Invalid response”错误。另外从源码看还存在一个向后兼容的例外dev.perfetto.UserMacro.前缀的旧版宏 ID 被当作 legacy 跳过校验TODO注释说明待 Google3 迁移完成后移除。CORS 要求所有 HTTPS 扩展服务器都必须设置 CORS 头允许 Perfetto UI 发起跨域请求Access-Control-Allow-Origin: https://ui.perfetto.dev Access-Control-Allow-Methods: GET Access-Control-Allow-Headers: Authorization, Content-Type如果你的服务器需要同时服务多个 Perfetto UI 部署可以反射reflect请求的Origin头而不是硬编码单一来源。GitHub 托管的服务器无需配置 CORS——raw.githubusercontent.com与 GitHub API 已设置合适的响应头。CORS 失败在浏览器控制台中表现为网络错误F12查看。受影响的服务器会被跳过其余服务器继续正常加载。另外注意Perfetto UI 通过 HTTPS 提供不能从http://地址拉取扩展混合内容限制服务器必须使用https://。鉴权请求头Perfetto UI 根据服务器配置的鉴权类型构造对应的请求头。协议文档给出的完整对照表鉴权类型请求头none无鉴权头github_patAuthorization: token pat经由 GitHub APIhttps_basicAuthorization: Basic base64(username:password)https_apikeybearerAuthorization: Bearer keyhttps_apikeyx_api_keyX-API-Key: keyhttps_apikeycustomcustom_header_name: keyhttps_sso无请求头请求以credentials: include发送这些头部在 extension_server.ts 的buildFetchRequest中按分支构造if (server.auth.type https_basic) { const credentials ${server.auth.username}:${server.auth.password}; headers[Authorization] Basic ${base64Encode(utf8Encode(credentials))}; } else if (server.auth.type https_apikey) { const {keyType, key} server.auth; if (keyType bearer) { headers[Authorization] Bearer ${key}; } else if (keyType x_api_key) { headers[X-API-Key] key; } else { headers[server.auth.customHeaderName] key; } } else if (server.auth.type https_sso) { return {url, init: {method: GET, headers, credentials: include}}; }其中custom类型需要同时提供customHeaderName自定义头名称与key。鉴权配置的 schema 见 types.ts 中的httpsAuthSchema秘密字段PAT、密码、API key均标记为secret便于设置导出时剥离。SSO 会话刷新机制对于 SSO 鉴权若请求返回 HTTP 403通常意味着 cookie 过期UI 会在隐藏 iframe 中加载服务器 base URL 以刷新 SSO 会话 cookie然后重试一次请求。该逻辑实现在fetchJson中403 且为https_sso时调用refreshSsoCookieiframe 的 onload/超时10 秒决定刷新是否成功。客户端的离线缓存与超时值得补充的实现细节UI 的fetchWithCache采用网络优先 Cache API 兜底的策略——成功响应会按 URL 缓存缓存名extension-servers网络失败或超时10 秒时返回最后一次成功的缓存内容HTTP 错误如 403、404则不会从缓存兜底因为它们代表服务端的真实拒绝如 PAT 被吊销调用方需要看到错误以触发 SSO 重试等逻辑见 extension_server.ts。GitHub 服务器的 URL 构造对于 GitHub 托管的扩展服务器UI 自动构造请求 URL未鉴权公开仓库使用raw.githubusercontent.com以避免 GitHub API 的速率限制403。https://raw.githubusercontent.com/{repo}/{ref}/{path}/manifest已鉴权私有仓库使用 GitHub Contents API。https://api.github.com/repos/{repo}/contents/{path}/manifest?ref{ref}并携带请求头Accept: application/vnd.github.rawjson源码中的buildFetchRequest对 GitHub 类型服务器做了同样处理github_pat走 API、否则走 raw并且 URL 的各路径段会经过encodeURIComponent编码、path与资源路径通过joinPath拼接见 extension_server.ts 与 url_utils.ts。单元测试覆盖了两种 GitHub URL 的构造以及 HTTPS URL 尾斜杠剥离strips trailing slash from HTTPS URL。HTTPS 服务器的 URL 归一化规则若用户输入不带://UI 自动补上https://前缀末尾多余斜杠会被去除。在“添加服务器”对话框中GitHub 服务器需要填写仓库owner/repo格式、分支/标签如main与可选的子目录路径默认/。示例一极简静态服务器一个完整的扩展服务器可以只是一组静态 JSON 文件my-extensions/ manifest modules/ default/ macros sql_modules用任意能设置 CORS 头的静态文件服务器nginx、Caddy、GCS、S3托管即可。基础场景不需要任何动态服务器逻辑——文件内容即上文中各端点的 JSON 响应体。注意 GitHub 模板仓库方案会自动构建并提交这些端点文件详见 extension-servers.md。示例二动态服务器Python/Flask当需要动态内容、企业 SSO 或与内部系统集成时可以用任意 HTTPS 动态框架实现。协议文档给出的完整 Flask 示例from flask import Flask, jsonify app Flask(__name__) app.route(/manifest) def manifest(): return jsonify({ name: My Extensions, namespace: com.example, features: [{name: macros}, {name: sql_modules}], modules: [{id: default, name: Default}], }) app.route(/modules/module/macros) def macros(module): return jsonify({ macros: [ { id: com.example.ShowLongSlices, name: Show Long Slices, run: [ { id: dev.perfetto.RunQueryAndShowTab, args: [SELECT * FROM slice ORDER BY dur DESC LIMIT 20] } ] } ] }) app.route(/modules/module/sql_modules) def sql_modules(module): return jsonify({ sql_modules: [ {name: com.example.helpers, sql: CREATE PERFETTO TABLE ...;} ] }) app.after_request def add_cors(response): response.headers[Access-Control-Allow-Origin] https://ui.perfetto.dev response.headers[Access-Control-Allow-Methods] GET response.headers[Access-Control-Allow-Headers] Authorization, Content-Type return response要点回顾manifest是唯一必需端点macros、sql_modules端点只在 manifest 声明对应 feature 后才会被请求示例中声明了macros与sql_modules因此实现了这两个端点所有宏 IDcom.example.ShowLongSlices与 SQL 模块名com.example.helpers都必须以 namespacecom.example.开头必须设置 CORS 头若服务器还支持 Basic/API Key/SSO 鉴权需按上文鉴权表处理对应请求头以及 SSO 的 403 重试若服务器还提供proto_descriptorsfeature需实现/modules/module/proto_descriptors端点。在 UI 中添加与共享扩展服务器扩展服务器的 UI 配置入口位于Settings Extension Servers齿轮图标进入设置后滚动到该区域。添加 GitHub 服务器时输入owner/repo格式的仓库、分支/标签UI 会实时拉取 manifest 并展示可用模块default自动选中私有仓库选择 Personal Access Token 鉴权token 需对仓库的 Contents 有只读权限。添加 HTTPS 服务器时输入服务器 URLhttps://前缀可省略选择模块并配置鉴权方式。所有变更都需要刷新页面生效。每个服务器支持Toggle / Edit / Share / Delete操作。点击Share会生成一个分享 URL分享链接中的秘密字段PAT、密码、API key会被自动剥离stripSecrets函数见 index.ts接收方打开链接时——若未配置该服务器则弹出预填好的“添加服务器”对话框若已配置则弹出“编辑”对话框并用分享的模块列表替换且从分享链接预填的配置在用户显式点击“Load modules from this server”之前不会向服务器发起任何请求awaitingConfirmation门控见 add_extension_server_modal.ts这是一种防恶意分享链接的安全设计。排错速查扩展加载失败时UI 会弹出一个非阻塞的错误对话框列出所有出错项已成功加载的其他服务器扩展不受影响。常见错误与排查方向错误形态含义与排查Failed to fetch url: error网络或 CORS 问题。先在浏览器新标签页直接打开该 URL 验证可达性若可达但 UI 报错打开控制台F12检查 CORS policy 报错并补 CORS 头确认使用https://HTTPS 页面不能拉取 http 内容。Fetch failed: url returned status服务器可达但返回 HTTP 错误。401/403 通常是鉴权失败检查 PAT 是否过期、SSO 会话404 说明端点路径不存在对照 端点表 检查。Failed to parse JSON from url: error响应不是合法 JSON。确认端点返回Content-Type: application/json与合法 JSON。Invalid response from url: errorJSON 合法但不符合预期 schema。对照本文各端点的 JSON 结构检查常见错误是 manifest 缺少必填字段name、namespace、features、modules或字段名写错。Module name not found on server设置中启用的模块不在 manifest 的modules列表中。编辑服务器取消该模块。Macro ID id must start with namespace ns.宏 ID 未以服务器 namespace 开头。若是你维护的服务器将宏 ID 改为com.acme.MyMacro形式见 命名空间强制约束否则联系服务器维护者。SQL module name name must start with namespace ns.同上针对 SQL 模块的name字段。扩展阅读Extension Server Setup Guide —— 使用 GitHub 模板一步步搭建扩展服务器的完整流程含config.yaml配置、SQL 模块与宏的组织方式Commands Automation Reference —— 宏中可使用的全部稳定自动化命令 ID 与参数Extending the UI —— Perfetto UI 全部扩展机制总览PerfettoSQL Getting Started —— 编写 SQL 模块与 PerfettoSQL 的入门指南【免费下载链接】perfettoProduction-grade client-side tracing, profiling, and analysis for complex software systems.项目地址: https://gitcode.com/GitHub_Trending/pe/perfetto创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考