外观
CoreProfile Raw Retention 与存储策略实现 (derivedOnly vs full)
关联背景与问题
- 关联问题:FTI-003 / FTI-004 · CoreProfile Raw 整场持续落盘导致长会话磁盘膨胀
- 现象:启用 FrameTime 持续采集后,CoreProfile binary chunk 持续原样追加到
core-profile-chunks.bin,5 分钟长测即产生约 10 GiB 的 Raw 文件与持续数十 MiB/s 的磁盘 I/O。 - 目标:默认采用
derivedOnly(产品默认),不生成也不落盘core-profile-chunks.bin,内存保留原始 chunk 供实时解码器解析,将长会话磁盘占用从 ~10 GiB 降至数百 MiB;研发调试显式选择full保留外部 blob 契约。
硬边界遵守情况 (Rule 0)
- 不变动采集核心:CoreProfile 持续采集、Startup/FrameTime 联合 filter、kperf owner 独占、Startup completion、Frame decoder 逻辑均未做任何变动;
- 读就绪与哈希隔离:
capturePlanHash、CollectorReadiness、prewarmGeneration的输入严格排除storagePolicy/retention,切换 retention 绝不触发 Collector 重启或重武装; - 异常消化:任何 quota/low-disk/writer-error 均在
RawSessionRecorder内部降级为truncated,严禁异常外溢至SessionWriter.append()。
实际修改范围与关键文件
| 组件 | 文件 | 修改说明 |
|---|---|---|
| Sidecar Core | perfowl_collector_core/storage_policy.py (新增) | 定义 CORE_PROFILE_RETENTION_DERIVED_ONLY, CORE_PROFILE_RETENTION_FULL, SessionStoragePolicy, RawRetentionLimits 及真值表解析函数 resolve_core_profile_raw_retention |
| Sidecar Core | perfowl_collector_core/__init__.py | 导出上述符号与常量 |
| Sidecar Core | perfowl_collector_core/recording.py | RawSessionRecorder 惰性创建 blob 文件;derivedOnly 不写 blob 并将 payload chunk 标记为 chunkRetention: not-retained;manifest 区分 observed 与 persisted 计数;降级捕获写入异常标记 truncated;validate_recorded_session 兼容 derivedOnly 与历史 full |
| Sidecar Core | perfowl_collector_core/delivery.py | finalize_candidate_session 对 derivedOnly 抛出 ReplayValidationError("core-profile-raw-replay-unavailable") |
| Sidecar Core | perfowl_collector_core/recovery.py | recover_checkpoint 与 _append_contiguous_tail 支持无 external blob 的会话恢复 |
| Sidecar Core | perfowl_collector_core/realtime.py | stream_candidate_session 顶层统一绑定 retention_policy,传递至 RawSessionRecorder |
| Sidecar CLI | perfowl_collector_core_cli.py | start 命令 wire 协议解析 coreProfileRawRetention;stream 命令行支持 --core-profile-raw-retention |
| Sidecar Tests | tests/test_storage_policy.py (新增) | 覆盖真值表、derivedOnly 无 blob、full blob 契约、解码一致性、离线回放防御、历史数据兼容、写异常降级、实时封印不走离线回放 8 个专项测试 |
| Swift Core | MacApp/Sources/PerfowlCore/SessionStoragePolicy.swift (新增) | Swift 侧 CoreProfileRawRetention 枚举、SessionStoragePolicy 结构体、PerfowlSettingsKey 定义 |
| Swift Core | MacApp/Sources/PerfowlCore/PerformanceModels.swift | PerformanceSessionPlan 升级到 schemaVersion 3,向后兼容解码 schema 1/2 为 historicalFull;增加 Rule 0 硬边界注释 |
| Swift Core | MacApp/Sources/PerfowlCore/CollectorSupervisor.swift | startPreparedSession 与 CollectorLaunchConfiguration 连通 wire 字段 coreProfileRawRetention |
| Swift Core | MacApp/Sources/PerfowlCore/PerformanceSessionController.swift | start 接收 storagePolicy,设置到 Session plan 并传递至 supervisor |
| Swift App | MacApp/Sources/PerfowlApp/PerfowlWorkbenchView.swift | 设置面板增加 @AppStorage(PerfowlSettingsKey.retainCoreProfileRaw) 切换项,toggleCapture() 按设置注入策略 |
| Swift Tests | MacApp/Tests/PerfowlCoreTests/SessionStoragePolicyTests.swift (新增) | 覆盖 Rule 0 哈希无关性、旧 schema 兼容为 historicalFull、schema 3 编解码与自定义策略 4 项单测 |
验证结论
- Sidecar Python 自动化测试:bash
python3 -m unittest discover -s tests -p "test_*.py" # 结果:Ran 407 tests in 7.222s. OK. - Swift Core/App 自动化测试:bash
swift test # 结果:Executed 149 tests, with 0 failures in 11.182s. - Unified CoreProfile 专项回归:bash
python3 -m unittest tests/test_unified_core_profile.py # 结果:Ran 9 tests in 2.489s. OK.
安装包交付与校验 (Build 58)
- App 产物:
MacApp/dist/Perfowl.app(CFBundleVersion=58,Developer ID Application: Jiacheng Li (76J692XB5C) 签名,Hardened Runtime 开启,时间戳有效,codesign --verify --deep --strict通过)。 - ZIP 归档:
MacApp/dist/Perfowl-internal-test-Build58-x86_64.zip(109,234,320 bytes,SHA-256:5f4d47fe8e1fa5f3c73cdb3d8db49213ad39efcc9617f91ecd828b7eb87dc4a3,解包与签名严格校验通过)。 - DMG 镜像:
MacApp/dist/Perfowl-internal-test-Build58-x86_64.dmg(141,828,802 bytes,SHA-256:6d82479f4a6da0ca452f36adfadca1eb9f9d3eca72dd9cfade63b274cc5b0240,Developer ID 容器签名,hdiutil verify与挂载校验通过)。 - 可执行文件 SHA-256:
fa574c5e2294a28108bbedfa93288743b7c0fb8928d5e2a3e98001485c21f477(6,638,160 bytes)。 - 验收报告归档:Build58 CoreProfile 存储策略与原始分块持久化分级验收
- 匿名证据文件:
docs/validation/evidence/perfowl-build58-storage-policy-e2-anonymous-2026-09-16.json
Update 2026-09-16 · E4 近 5 分钟真机磁盘验证
- Build 58 真实 Session 运行 280.123 s(4 分 40.123 秒),Session 状态
completed。 - Session 实际逻辑文件总量 203.24 MiB;文件系统
du占用约 205 MiB。相较修复前同类约 5 分钟 Session 10.11 GiB,磁盘占用下降约 98.0%(约 51 倍)。 collector-core60.35 MiB,其中raw-events.jsonl57.55 MiB;screenshots138.77 MiB,已成为本轮最大持久化项;events1.73 MiB、raw1.81 MiB、metrics.csv565.29 KiB。- 本轮设备侧实际观察到 CoreProfile 3,753,607,010 bytes / 850 chunks,但
coreProfilePersistedByteCount=0、coreProfilePersistedChunkCount=0;core-profile-chunks.bin未创建,coreProfileRawRetention=derivedOnly、coreProfileRawRetentionStatus=not-retained、coreProfileRawReplayCapability=unavailable,证明采集与实时解码继续工作而完整 Raw binary 不再落盘。 - Unified CoreProfile 主路径保持
startup-and-frame,startCount=1、restartCount=0、stopCount=1,未因存储策略引入额外 CoreProfile 生命周期。 - 本次仅完成近 5 分钟
derivedOnly真机磁盘门禁;30 分钟长测与full调试模式真机对照仍待执行,因此批次继续保持partial,不提前关闭全部 E4 门禁。
遗留项与后续门禁
- E4 真机长测对比(待真机验收):在真实 iOS 设备上运行 5 分钟与 30 分钟连续会话,验证磁盘体积由 ~10 GiB 降低至数百 MiB,确认磁盘 I/O 不再随 CoreProfile Raw 吞吐无界增长。
- Notary 与 Stapling 门禁(开放):本机未配置 Notary 凭据,待 CI/签名机执行公证与 stapling。
- GPL-3.0 法务评估门禁(开放):pymobiledevice3 外部依赖分发合规评估。