在Perfetto里框选一段卡顿,AI直接告诉你哪行代码有问题

传统 Perfetto 使用方式的局限

用过 Perfetto 的人都懂这个流程:

  1. adb shell perfetto -c - --txt 采集 trace
  2. 打开 ui.perfetto.dev,拖入 .pb 文件
  3. 在花花绿绿的时间轴上找红色帧(jank)
  4. 人肉点开每个 slice,看调用栈,猜问题

这个流程最大的痛点是:你看到了问题,但不知道为什么

一个卡顿帧,Perfetto 告诉你 dispatchLayoutStep2 耗时 229ms。然后呢?你得手动搜源码、对方法名、看上下文,才能定位到 DemoAdapter.onBindViewHolder 里那个主线程做了 100000 次 Math.sqrt() 的 for 循环。

而且这是分析一个帧的成本。如果你的 trace 里有 50 个 jank 帧,你得重复 50 次。

设计思路:不重新造轮子

我做了个决定:不自己画 UI

Perfetto UI 已经是最好的 trace 可视化工具------时间轴、slice 展开、SQL 查询面板、帧分析视图,该有的都有。重做一个没意义。

但 Perfetto UI 支持自定义插件(TypeScript)。所以方案很清晰:

arduino 复制代码
自托管 Perfetto UI + SI Bridge 插件 + WebSocket 桥接回 Python 后端

用户框选时间范围 → 插件拿到 ts 和 dur → 通过 WebSocket 发给 Python 后端 → 后端调用 frame_analyzer agent → 结果回传 Perfetto UI 展示。

scss 复制代码
用户框选时间范围 → SI Bridge Plugin (TypeScript)
    → WebSocket → BridgeServer (Python)
    → frame_analyzer agent → 结果回传 Perfetto UI

这样用户不需要离开 Perfetto UI,不需要复制粘贴任何数据,框选即分析。

插件实现:SI Bridge Plugin

插件的核心就三件事:注册面板、监听框选、WebSocket 通信。

用 Perfetto 的 registerAreaSelectionTab API 注册一个右侧面板。用户在时间轴上拖选一段范围后,面板自动显示选中区域的时间信息:

用户在时间轴上拖选一段范围后,面板自动显示选中区域的时间信息:

typescript 复制代码
// perfetto-plugin/com.smartinspector.Bridge/index.ts

export default class SIBridgePlugin implements PerfettoPlugin {
  static readonly id = 'com.smartinspector.Bridge';

  async onTraceLoad(trace: Trace): Promise<void> {
    connectWS();

    trace.selection.registerAreaSelectionTab({
      id: 'si_frame_analysis',
      name: 'SI Frame Analysis',
      render: (selection) => renderAreaSelectionTab(selection),
    });

    // 快捷键 Ctrl+Shift+A
    trace.commands.registerCommand({
      id: 'com.smartinspector.Bridge#analyzeSelection',
      name: 'SI Agent: Analyze Selected Area',
      callback: () => { /* ... */ },
    });
  }
}

用户点击 "Analyze with SI Agent" 按钮后,插件通过 WebSocket 把 ts(纳秒时间戳)和 dur(纳秒持续时间)发给后端:

typescript 复制代码
function sendAnalysis(ts: number, dur: number): void {
  if (!ws || ws.readyState !== WebSocket.OPEN) {
    state.status = 'error';
    state.error = 'SI Agent not connected. Is /open running?';
    return;
  }

  state.status = 'analyzing';
  state.progressLog = ['Sending to SI Agent...'];

  ws.send(JSON.stringify({
    type: 'frame_selected',
    payload: {ts, dur},
  }));
}

插件还有个断线自动重连机制------3 秒一次。因为 Perfetto UI 页面刷新或后端重启时,连接会断:

typescript 复制代码
ws.onclose = () => {
  console.log('[SI Bridge] Disconnected, reconnecting in 3s...');
  ws = null;
  reconnectTimer = setTimeout(connectWS, 3000);
};

BridgeServer:WebSocket 桥接层

插件运行在浏览器里,但分析逻辑在 Python 后端。中间需要一个桥接服务。

BridgeServer 用 websockets 库实现,干两件事:静态文件服务 (Perfetto UI 的 HTML/JS/WASM)和 WebSocket 桥接/bridge 端点)。

python 复制代码
# src/smartinspector/ws/bridge_server.py

class BridgeServer:
    def __init__(self, port=9877, on_frame_selected=None, trace_path=None):
        self.port = port
        self.on_frame_selected = on_frame_selected
        self.trace_path = trace_path

    async def _ws_handler(self, ws):
        self._ws_clients.add(ws)
        async for raw in ws:
            msg = json.loads(raw)
            if msg.get("type") == "frame_selected":
                await self._handle_frame_selected(ws, msg["payload"])

    async def _handle_frame_selected(self, ws, payload):
        ts = payload.get("ts", 0)
        dur = payload.get("dur", 0)

        async def send_progress(step, detail=""):
            await ws.send(json.dumps({
                "type": "analysis_progress",
                "payload": {"step": step, "detail": detail},
            }))

        # 调用 frame_analyzer agent
        result = await self.on_frame_selected(payload, send_progress)

        await ws.send(json.dumps({
            "type": "analysis_result",
            "payload": result,
        }))

这里有个细节:分析过程可能要 10-30 秒(查询 trace + 源码归因 + LLM 分析),如果用户等这么久看不到任何反馈会很焦虑。所以加了 send_progress 回调,每一步都把进度推给前端:

ini 复制代码
Sending to SI Agent...
  [frame] 查询 trace 切片 (ts=123456, dur=789012)...
  [frame] 找到 15 切片, 3 帧
  [attributor] Found 4 slices, searching source code...
    145.23ms  DemoAdapter.onBindViewHolder  (java)
    32.10ms   ImageLoader.execute  (java)
  [attributor] Done: 3 attributed, 1 system classes
  [frame] 调用 LLM 分析...

这些进度日志会实时显示在 Perfetto UI 右侧面板里。

另外 BridgeServer 还做了个巧妙的设计:用 /trace.pb 路由自动加载 trace 文件。Perfetto UI 支持 ?url= 参数自动加载远端 trace,所以 /open 命令打开的 URL 是:

perl 复制代码
http://127.0.0.1:9877/#!/?url=http://127.0.0.1:9877/trace.pb

用户不需要手动拖文件了。

frame_analyzer Agent:框选即分析

这是核心分析逻辑。输入就是用户框选的 ts_nsdur_ns(纳秒),输出是一份 Markdown 分析报告。

处理流程:

python 复制代码
# src/smartinspector/agents/frame_analyzer.py

def analyze_frame(trace_path, ts_ns, dur_ns, existing_summary="",
                  cached_attribution="", on_progress=None) -> str:
    # 1. 查询 trace 切片
    frame_data = query_frame_slices(trace_path, ts_ns, dur_ns)

    # 2. 构建确定性预计算结论(不让 LLM 算数)
    hints = _build_frame_hints(frame_data, existing_summary)

    # 3. 对 SI$ 切片做源码归因
    source_section = _run_source_attribution(
        frame_data, existing_summary, cached_attribution, on_progress
    )

    # 4. LLM 组织最终报告
    response = llm.invoke([SystemMessage(content=prompt), HumanMessage(content=user_content)])
    return response.content

确定性预计算:不让 LLM 算数

_build_frame_hints 做的是确定性预计算------根据帧预算(16.67ms)自动分类严重度等级:

python 复制代码
p0_threshold = frame_budget_ms  # 通常 16.67ms
for s in si_slices:
    sdur = s["dur_ms"]
    if sdur > p0_threshold:
        level = "P0"    # 超过一帧预算
    elif sdur >= p0_threshold * 0.25:
        level = "P1"    # 占帧预算 25%+
    else:
        level = "P2"    # 轻微

这些结论直接以结构化文本的形式喂给 LLM,LLM 只需要"组织语言",不需要判断"145ms 算不算严重卡顿"。

源码归因:从 SI$ 切片到源码行号

_run_source_attribution 从框选范围里提取 SI$ 前缀的 trace 切片,走三步搜索:

  1. Glob :搜索包含类名的文件(DemoAdapter.java
  2. Grep:在文件中搜索方法签名,定位行号
  3. Read:读取方法体,LLM 分析具体问题

系统类(android.*androidx.*)会被自动过滤掉。

还有个优化:如果用户先跑了 /full(全量分析),归因结果会被缓存。框选分析时直接复用,只对新出现的 slice 做搜索,省掉重复的源码检索开销。

实时进度推送

整个分析过程通过 on_progress 回调逐步推送:

python 复制代码
if on_progress:
    on_progress(f"  [frame] 查询 trace 切片 (ts={ts_ns}, dur={dur_ns})...")
# ... 查询 ...
if on_progress:
    on_progress(f"  [frame] 找到 {n_slices} 切片, {n_frames} 帧")
# ... 归因 ...
if on_progress:
    on_progress(f"  [attributor] Found {len(attributable)} slices, searching source code...")

这些消息通过 WebSocket → 插件 → Perfetto UI 面板,实时显示给用户。

构建和部署

构建过程封装在 perfetto-plugin/build.sh 里,5 步搞定:

bash 复制代码
#!/usr/bin/env bash
# Step 1: Clone Perfetto 仓库(shallow clone)
git clone --depth 1 https://github.com/google/perfetto.git perfetto-build/

# Step 2: 复制插件到 Perfetto 插件目录
cp -r perfetto-plugin/com.smartinspector.Bridge perfetto-build/ui/src/plugins/

# Step 3: 注册到 default_plugins.ts
# 在默认插件列表中加入 'com.smartinspector.Bridge'

# Step 4: 编译(含依赖安装 + TypeScript 编译 + WASM)
cd perfetto-build && ui/build

# Step 5: 产物输出到 perfetto-build/ui/out/dist/

第一次编译大概 5 分钟(要下载 npm 依赖 + 编译 WASM)。后续改了插件代码,用 --skip-clone 只需 1-2 分钟。

使用时在 SmartInspector CLI 里输入 /open

csharp 复制代码
you> /open
  [bridge] Starting trace_processor_shell on :9001...
  [bridge] Server ready on :9877
  Opening Perfetto UI: http://127.0.0.1:9877/#!/?url=http://127.0.0.1:9877/trace.pb

  Trace will load automatically. Then:
    1. Drag to select a time range on the timeline
    2. Click 'SI Frame Analysis' tab in the details panel
    3. Click 'Analyze with SI Agent'

浏览器自动打开,trace 自动加载,插件自动连接。

对比:交互式 vs 全量分析

SmartInspector 有两种分析模式,互补而非替代:

/full(全量分析) /open(交互分析)
触发方式 CLI 命令 Perfetto UI 框选
分析范围 整个 trace 用户选中的时间范围
适用场景 第一次看 trace,不知道问题在哪 已经定位了大致区域,想深入分析
源码归因 全量扫描 只扫描选中范围的 SI$ 切片
输出 完整 Markdown 报告文件 面板内嵌报告

典型工作流:先跑 /full 看全局,发现某个时间段有问题,再 /open 打开 Perfetto UI,框选那段区域做定点分析。

也可以直接用 /frame ts=X dur=Y 在 CLI 里指定时间范围分析,不依赖 UI。

小结

给 Perfetto UI 加 AI 分析能力,核心思路就三个字:不造轮子

Perfetto 已经是最好的 trace 可视化工具了,我要做的是把 AI 分析能力无缝嵌入它------通过 TypeScript 插件注册面板、WebSocket 桥接 Python 后端、frame_analyzer agent 处理分析逻辑。

最终效果:用户在 Perfetto 时间轴上框选一段卡顿,点一个按钮,AI 帮你查切片、搜源码、定位到行号、给出优化建议。整个过程不需要离开 Perfetto UI。

下篇我们聊聊 SmartInspector 最核心的技术------SI$ Tag 归因体系:怎么通过运行时 Hook 给每个业务方法调用贴上"GPS 坐标",让 trace 数据从"看不懂"变成"看到源码行号"。


开源地址github.com/mufans/Smar...(示例,以实际地址为准) 系列回顾

相关推荐
迷藏4943 小时前
**TiDB 在高并发场景下的性能优化实战:从慢查询到极致吞吐的跃迁**在现代分布式系统中,数据库不仅是数据存储的
java·数据库·python·性能优化·tidb
3DVisionary5 小时前
新拓三维XTOM系统:汽车模具全场3D检测深度技术问答
性能优化·降本增效·storm·蓝光3d扫描·汽车模具制造·数字化质检·xtom测量仪
00后程序员张5 小时前
前端可视化大屏制作全指南:需求分析、技术选型与性能优化
前端·ios·性能优化·小程序·uni-app·iphone·需求分析
得物技术6 小时前
生成式召回在得物的落地技术分享与思考
算法·性能优化·程序员
lwf0061648 小时前
JPA批量操作性能优化
性能优化
设计Z源18 小时前
AS SSD Benchmark 固态硬盘测试工具
测试工具·性能优化·硬件工程
禅思院1 天前
前端性能优化:从“术“到“道“的完整修炼指南
前端·性能优化·前端性能优化·分层优化模
波波0071 天前
每日一题:.NET 性能优化常用手段有哪些?
性能优化·.net
weixin199701080162 天前
《快手商品详情页前端性能优化实战》
前端·性能优化