标注输入卡顿与启动阶段 CPU 断档诊断
背景
Owner 2026-11-02 在内部包 237 上连续录了两场目标 App A 的会话(设备 A,iOS 26),反馈 CPU 曲线断数据,并在第二场复测后判断“点击标注后数据就断了”。同日 Owner 指定 Claude 负责全项目修改与优化(兼任 Lead 与 Implementation,见 AGENTS.md),本批诊断与修复由 Claude 决策并实施。
证据(E3,会话原件)
两场均为采集完成状态:线路序号 0 缺口、0 重复、0 乱序,两条连接无错误、无重连。断档出现在采集进程收到 sysmontap 数据的时刻上,不是电脑端丢弃。
| 会话 | 时长 | 标注保存时刻 | 整机 CPU 来源间隔 > 1.5 秒 | activitytrace 逐帧来源试探期 |
|---|---|---|---|---|
| 第一场 | ≈29 秒 | 第 16.9 秒 | 2.6 秒起 2.8 秒;5.4 秒起 1.7 秒 | 0–8.3 秒 |
| 第二场 | ≈22.5 秒 | 第 12.9 秒 | 3.3 秒起 3.1 秒;6.4 秒起 2.1 秒;8.8 秒起 2.3 秒 | 0–13.1 秒 |
| 10-30 有 3 次标注的会话 | ≈87 秒 | 33.6 / 61.3 / 81.0 秒 | 无 | 0–4.4 秒 |
| 10-30 无标注的三场 | ≈52–55 秒 | 无 | 最多一次 1.6–1.7 秒 | 0–3.6~4.1 秒 |
• 第二场所有断档都发生在标注保存之前;试探期一结束,积压的 sysmontap 数据在约 9 秒内以正常两倍速率集中补到。
• 第二场电脑端队列(Supervisor 入队→Controller 出队)在标注保存前 6 秒内出现 737 / 95 / 761 / 625 毫秒等待,其余样本约 0.2–0.6 毫秒,说明输入标注期间主线程被占用。
• 两场的帧耗时来源都没有选出(第一场 primary-insufficient-evidence,第二场 resource-limited),逐帧统计只覆盖启动后约 1.2–1.6 秒。
判断
• 已证实(E3):输入标注时主线程长时间被占用。代码中标注草稿 noteText 是整个工作台根视图的 @State,每输入一个字(含输入法拼字)都会使 2700 余行的工作台连同实时图表全部重算;这是界面“停住”的直接原因。
• 已证实(E3):落盘数据里的 CPU 断档来自采集进程侧,与启动/逐帧追踪试探期重合;标注本身不写入采集进程,10-30 的三次标注都没有引起断档。
• 待证(假设):本机为 4 物理核 Intel 笔记本,启动追踪连接在会话中传输 0.2–3 GB 数据;试探期的追踪解码与界面重绘可能共同占满 CPU,使 sysmontap 读取落后。标注输入是否会加重采集侧断档,需要 A/B 复测才能定论,不写成结论。
本批实现
| 文件 | 修改 |
|---|---|
| `MacApp/Sources/PerfowlApp/PerfowlWorkbenchView.swift` | 新增私有子视图 `NoteComposer`,草稿文字状态移入其中;根视图只保留弹窗开关,提交后通过闭包调用原有 `appendNote` 并关闭弹窗。输入、提交、空白禁用与无障碍标识均保持不变。 |
• 复杂度:每次按键的重算范围从整个工作台缩小到弹窗内一个文本框与按钮,与会话长度和图表数量无关。
• 未改采集链路、指标公式与标注落盘格式。
验证
• swift build --product Perfowl 通过(E2)。
• 前后对比基准:以 host-queue-timing.json 中输入标注期间的出队等待为指标,修复前为 625–761 毫秒;修复后需 Owner 在新包上复测(待 E3)。
遗留与下一步
• A/B 复测(Owner 操作):同一目标 App,各录约 60 秒;A 场全程不加标注,B 场在第 20 秒后(试探期结束后)打开标注框并输入约 10 秒。用于区分“启动试探期断档”与“标注输入加重断档”。
• 启动/逐帧追踪试探期挤占 sysmontap:需要另立方案,方向包括给 sysmontap 独立读取优先级、限制试探期追踪数据量,以及让报告标明逐帧统计的实际覆盖时长。
• 逐帧来源过滤未生效(请求 0x31800318,实际过滤列表为空)导致帧耗时整场缺失,单独立项。