Skip to content

Screenshot 长连接轻量化与按需保存开发方案 (FTI-005) ​

0. 本方案最终目标 ​

本轮截图优化冻结以下三个产品规则:

  1. 截图采集使用独立长连接:
    • Session 开始建立一次;
    • 固定 1 秒获取 1 张;
    • Session 结束关闭。
  2. 截图序列按需保存:
    • 用户勾选 Screenshot → 保存每秒截图序列到 screenshots/;
    • 用户未勾选 Screenshot → 绝不保存截图文件,不创建目录。
  3. 主页面顶部截图始终展示:
    • 只要 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.png

2. 核心设计与契约 ​

  1. 绝对物理隔离:Screenshot 拥有独立子进程与独立 DVT 连接,发生任何异常、超时或重连,绝不影响 Performance DTX,绝不影响 StartupTiming、FrameTime、CoreProfile 与其他性能指标。
  2. 定长前缀二进制 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]
  3. 节奏控制与不追帧:
    swift
    let completedAt = DispatchTime.now().uptimeNanoseconds
    let scheduled = nextCaptureNanoseconds &+ intervalNanoseconds
    nextCaptureNanoseconds = scheduled > completedAt
        ? scheduled
        : completedAt &+ intervalNanoseconds
  4. SessionArtifactPolicy 契约与 Rule 0 隔离:
    • 扩展 SessionStoragePolicy(别名 SessionArtifactPolicy),增加 persistScreenshotSequence: Bool;
    • persistScreenshotSequence 绝不进入 CapturePlan.hash、CollectorReadiness 或 prewarmGeneration;
    • 勾选与否绝不触发重新 Prepare App 或重建 Performance DTX。
  5. 僵尸进程防护门禁 (Zombie Worker Elimination):
    • Python 端 watch_parent 守护线程监听父进程 PID 与 stdin EOF,父进程退出立即 os._exit(0);
    • Swift 端 PersistentScreenshotWorker 绑定生命周期,stop/deinit 自动强制回收,确保进程池中 Worker 计数归零。
  6. Review 与 Manifest 元数据语义:
    • Review 区分 not-requested, available, partial, requested-but-failed 四种状态;
    • Manifest 记录 screenshotPreviewEnabled, screenshotPersistenceEnabled, screenshotObservedCount, screenshotPersistedCount。

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)
  • 核心文件变更:
    1. MacApp/Sidecar/perfowl_collector_core/screenshot_worker.py(新增):实现独立子进程截图 Worker,管理 DvtProvider 与 Screenshot(dvt) channel 独立长连接;大端定长前缀二进制 IPC(Zero Base64 / Zero Large JSON);watch_parent 守护线程防止僵尸进程。
    2. MacApp/Sidecar/perfowl_collector_core_cli.py & __init__.py:增加 screenshot-worker CLI 子命令与参数解析。
    3. MacApp/Sidecar/tests/test_screenshot_worker.py(新增):覆盖封包解包、单连接多次连续 capture、重连退避容错与 CLI stdin/stdout 协议。
    4. MacApp/Sources/PerfowlCore/SessionStoragePolicy.swift:增加 persistScreenshotSequence: Bool,定义 SessionArtifactPolicy 别名。
    5. MacApp/Sources/PerfowlCore/PersistentScreenshotWorker.swift(新增):Swift Actor 封装长连接子进程驱动,管理生命周期与二进制 IPC,提供安全大端移位读取与 SIGPIPE 屏蔽。
    6. MacApp/Sources/PerfowlCore/PerformanceModels.swift & PerformanceSessionStore.swift:SessionPlan 支持存储策略序列化;Manifest 与 Store 增加截图观察数、落盘数、连接数、重连数与失败数审计字段。
    7. MacApp/Sources/PerfowlCore/SessionReview.swift & SessionReviewViews.swift:实现 screenshotSequenceStatus 四态(notRequested, available, partial, requestedButFailed)及卡片渲染。
    8. MacApp/Sources/PerfowlCore/PerformanceSessionController.swift & PerfowlWorkbenchView.swift:采集中始终启动 1 FPS 顶部预览;按 selectedMetricIDs.contains(.screenShot) 按需落盘;未勾选仅在内存持有最新单帧,磁盘 0 写入。
    9. 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 通过)。
  • 静态审计与报告:
    • docs/validation/evidence/perfowl-build59-screenshot-worker-e2-anonymous-2026-09-16.json
    • docs/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。

Perfowl · Performance Observer