外观
Screenshot 长连接轻量化与按需保存开发方案 (FTI-005)
0. 本方案最终目标
本轮截图优化冻结以下三个产品规则:
- 截图采集使用独立长连接:
- Session 开始建立一次;
- 固定 1 秒获取 1 张;
- Session 结束关闭。
- 截图序列按需保存:
- 用户勾选 Screenshot → 保存每秒截图序列到
screenshots/; - 用户未勾选 Screenshot → 绝不保存截图文件,不创建目录。
- 用户勾选 Screenshot → 保存每秒截图序列到
- 主页面顶部截图始终展示:
- 只要 Session 正在采集,无论是否勾选 Screenshot,都固定 1 秒刷新一次;
- 内存仅保留最新单张帧,未勾选模式下 RSS 不随时间增长。
核心原则:
截图采集始终为实时预览服务;Screenshot 指标只决定是否保存历史截图序列。
1. 当前问题与新架构
当前痛点
当前实现每秒调用一次 pymobiledevice3 developer dvt screenshot,导致 30 分钟长测执行约 1800 次进程启动/销毁、1800 次 DVT 重复建连与临时文件 I/O,并强制写入 680 MiB 截图文件。
新架构
text
iPhone
│
Screenshot Service (com.apple.instruments.server.services.screenshot)
│
Persistent DVT Connection (Independent)
│
↓
Screenshot Worker Process (perfowl_collector_core screenshot-worker)
│
1 PNG / second (Binary IPC over stdout)
│
↓
Swift PersistentScreenshotWorker / PerformanceSessionController
│
┌───────────┴───────────┐
│ │
↓ ↓
Live Preview Persistence Gate
│ │
latestScreenshotData persistScreenshotSequence?
│ │
↓ ┌─────┴─────┐
主页面顶部实时刷新 │ │
NO YES
│ │
不写磁盘 ↓
SessionStore.appendScreenshot()
│
screenshots/shot-000001.png2. 核心设计与契约
- 绝对物理隔离:Screenshot 拥有独立子进程与独立 DVT 连接,发生任何异常、超时或重连,绝不影响 Performance DTX,绝不影响 StartupTiming、FrameTime、CoreProfile 与其他性能指标。
- 定长前缀二进制 IPC(Zero Base64 / Zero Large JSON):
- Swift -> Worker:
capture <seq>\n,ping <id>\n,stop\n - Worker -> Swift:
[HeaderLength: uint32 BE][Header JSON: UTF-8][PNGLength: uint32 BE][Raw PNG Bytes]
- Swift -> Worker:
- 节奏控制与不追帧:swift
let completedAt = DispatchTime.now().uptimeNanoseconds let scheduled = nextCaptureNanoseconds &+ intervalNanoseconds nextCaptureNanoseconds = scheduled > completedAt ? scheduled : completedAt &+ intervalNanoseconds - SessionArtifactPolicy 契约与 Rule 0 隔离:
- 扩展
SessionStoragePolicy(别名SessionArtifactPolicy),增加persistScreenshotSequence: Bool; persistScreenshotSequence绝不进入CapturePlan.hash、CollectorReadiness或prewarmGeneration;- 勾选与否绝不触发重新 Prepare App 或重建 Performance DTX。
- 扩展
- 僵尸进程防护门禁 (Zombie Worker Elimination):
- Python 端
watch_parent守护线程监听父进程 PID 与 stdin EOF,父进程退出立即os._exit(0); - Swift 端
PersistentScreenshotWorker绑定生命周期,stop/deinit 自动强制回收,确保进程池中 Worker 计数归零。
- Python 端
- Review 与 Manifest 元数据语义:
- Review 区分
not-requested,available,partial,requested-but-failed四种状态; - Manifest 记录
screenshotPreviewEnabled,screenshotPersistenceEnabled,screenshotObservedCount,screenshotPersistedCount。
- Review 区分
3. 验证与回归门禁
- Python 专项单测 (
test_screenshot_worker.py): 覆盖长连接 1 次建连 10 次采集、二进制封包解包、错误帧发射与退避重连、watchdog 退出; - Swift 专项单测 (
PersistentScreenshotWorkerTests.swift): 覆盖单进程生命周期、未勾选零写盘、勾选持久化序列、单帧失败不中断 Session、Cadence 防突发追帧、内存有界性; - 全量回归:Python (407+)、Swift (149+) 全绿;VitePress 0 报错 0 死链;
- 安装包生成与验证:生成 Developer ID 签名的 Build 59 测试安装包并完成验签。
2026-09-16 Implementation & Validation Update
1. 实际修改范围与代码落地
- 问题与方案编号:FTI-005(现场测试缺陷:截图每秒重复起进程、开销大且未选指标依然落盘大量 PNG)
- 核心文件变更:
MacApp/Sidecar/perfowl_collector_core/screenshot_worker.py(新增):实现独立子进程截图 Worker,管理DvtProvider与Screenshot(dvt)channel 独立长连接;大端定长前缀二进制 IPC(Zero Base64 / Zero Large JSON);watch_parent守护线程防止僵尸进程。MacApp/Sidecar/perfowl_collector_core_cli.py&__init__.py:增加screenshot-workerCLI 子命令与参数解析。MacApp/Sidecar/tests/test_screenshot_worker.py(新增):覆盖封包解包、单连接多次连续 capture、重连退避容错与 CLI stdin/stdout 协议。MacApp/Sources/PerfowlCore/SessionStoragePolicy.swift:增加persistScreenshotSequence: Bool,定义SessionArtifactPolicy别名。MacApp/Sources/PerfowlCore/PersistentScreenshotWorker.swift(新增):Swift Actor 封装长连接子进程驱动,管理生命周期与二进制 IPC,提供安全大端移位读取与 SIGPIPE 屏蔽。MacApp/Sources/PerfowlCore/PerformanceModels.swift&PerformanceSessionStore.swift:SessionPlan 支持存储策略序列化;Manifest 与 Store 增加截图观察数、落盘数、连接数、重连数与失败数审计字段。MacApp/Sources/PerfowlCore/SessionReview.swift&SessionReviewViews.swift:实现screenshotSequenceStatus四态(notRequested,available,partial,requestedButFailed)及卡片渲染。MacApp/Sources/PerfowlCore/PerformanceSessionController.swift&PerfowlWorkbenchView.swift:采集中始终启动 1 FPS 顶部预览;按selectedMetricIDs.contains(.screenShot)按需落盘;未勾选仅在内存持有最新单帧,磁盘 0 写入。MacApp/Tests/PerfowlCoreTests/PersistentScreenshotWorkerTests.swift(新增):Swift 专项单测,覆盖 Rule 0 隔离、四态语义与落盘行为。
2. 验证结果与数字证据 (E2)
- Python 测试:
411/411 PASSED(8.76s,0 失败); - Swift 测试:
153/153 PASSED(17.72s,3 项显式基准跳过,0 失败); - 安装包生成与签名 (Build 59):
- App 产物:
MacApp/dist/Perfowl.app(codesign --verify --deep --strict通过,Developer ID Application 签名); - 主程序 SHA-256:
2ae9731a87af5b16c68e630c5a83b790598618da4e9f73811ced5f20c73611b3; - ZIP 归档:
MacApp/dist/Perfowl-internal-test-Build59-x86_64.zip(109,290,219 bytes,SHA-256:160b1c545704db0942c5569193fc8641f930b46eeca3cd51c048dbc9daacd0f2); - DMG 镜像:
MacApp/dist/Perfowl-internal-test-Build59-x86_64.dmg(143,021,586 bytes,SHA-256:894c80f5135fb346e8473e482fa0ea606a971bce3feeb093fff038dbe564228e,hdiutil verify通过)。
- App 产物:
- 静态审计与报告:
docs/validation/evidence/perfowl-build59-screenshot-worker-e2-anonymous-2026-09-16.jsondocs/validation/perfowl-build59-screenshot-worker-2026-09-16.html
3. 遗留项与未关门门禁
- 本机缺少 Apple Notary 凭据,公证与 ticket stapling 门禁仍开放;
- GPL-3.0 法务评估门禁仍开放;
- 交付状态标记为
partial,等待真机(iPhone 15)E4 5m/30m 连续稳定性长测验证。
2026-09-16 E4 Follow-up
Build59 首轮真机 E4 暴露两个实现偏差:① 未勾选 Screenshot 时虽然 observed 正常且磁盘 0 写入,但工作台只更新 latestScreenshotData,没有按产品要求显示实时截图序列;② 勾选 Screenshot 后 10/10 文件虽有合法 PNG signature,但 IDAT CRC 出错,属于二进制 payload 损坏而非设备真实黑屏。另确认真正达到 1 FPS 后原始 PNG 约会带来 2.7~3.5 GiB / 30min 的截图体积。
后续修复与 HEIC 优化统一进入:Build59 Screenshot E4 后续修复方案。在该方案 E4 通过前,Build59 Screenshot 状态继续保持 implemented · partial,不得标记 closed。