全指标采集、实时窗口与快速收尾修复方案
1. 决策与批准边界
**方案完成,实施待用户批准。**本批只交付设计与验收合同,不变更产品代码、冻结依赖或指标算法。基线为 a9cab40 / Build 38 / Foundation 2.7 Beta。本文 E1 表示设计与代码审查;此前留存会话属于真实运行证据,但不代表修复后 E4 通过。八项问题全部进入范围,不把“入口可选”当作指标已支持。
保持唯一连接 Provider / DTX owner;不引入第二套连接内核。采用“全源准备、采集与展示解耦、单次解码增量派生、有界实时视图、增量报告封存”。放弃只增加超时、扩大缓存及停止后全量重放作为默认路径。代价是需要新的持久化检查点和恢复机制;收益是实时开销不随会话历史无限增长。
2. 已确认的用户规则
- 选择设备:准备连接、公共服务和能力探测。
- 选择应用:准备全部可用常规指标源,也准备启动分析与截图源。选中曲线不是 Adapter 列表。
- 开始:全部已就绪常规源开始实际采集;未显示的指标继续记录,完整报告保留它们。
- 启动分析与截图:提前准备,但仅勾选时实际启用。准备不意味着持续产出重型数据。底层必须短暂启动以验证就绪时,探测有预算,结束后释放探测缓存;状态标为 prepared,不冒充已持续 armed。
- 启动分析选中:启动前建立有效源与时钟边界,唯一一次冷启动。普通采集也只发送一次普通启动请求;失败不静默重复启动。
- 截图选中:默认沿用当前频率,用户设置与会话记录一致;退出勾选的生效时机在会话开始冻结。会话中曲线增删即时生效,启动/截图设置下次会话生效并明确提示。
- Mac 实时曲线固定最近 60 秒,随单调时间向左移动,所有图表共用窗口;无新点时也不伪造数据。最终报告保留完整时间跨度,按需加载。
- 不修改目标应用、不注入;缺失写 null 和原因,真实 0 保留;单源失败不阻塞其他常规源。必要连接或目标身份失败才阻止开始。
3. 八项问题与证据边界
最近两份匿名留存会话暂称 A/B,后续验证用本地私有映射确认用户测试对应关系。A 约 82.61 秒、80 sample、Raw 18.54 MB;B 约 19.02 秒、15 sample、Raw 267.49 MB。时长并非 Raw 大小的唯一决定因素。
| 问题 | 当前事实 / 假设 | 修复与关闭条件 |
|---|---|---|
| 启动全部 missing | 两份 decodedRecordCount=0、span=0;B 有 liveQueueDropCount=19。源格式/覆盖存在缺口,尚未定位唯一根因 | 审计原始头、分片、时钟、丢失、生命周期归属;源解码通过后才进入阶段映射。可控因果测试后再验证商店应用;无证据不补猜测阶段 |
| Mac 滚动卡顿 | 图表重复调用 playbackPoints;该属性每次排序整个 points。可见窗口虽为 60 秒,计算仍依赖历史 N。主线程占比尚未实测 | 固定窗口缓存、预先排序、后台分段、可见区域渲染;滚动性能及 RSS 随时长稳定 |
| Frame Time missing | 两份 acceptedRecordCount=0;A ignored=5,B unknown=27/ignored=17 | 恢复有效逐帧源、时钟及连续性;逐帧输出与独立回放一致,再校准归属与单位 |
| Memory / XcodeMemory 相同 | 两者当前均读 physFootprint;后者是 candidate-alias | 同源明示,拒绝伪造独立口径;与工具同窗对照前保持 candidate-alias |
| AvailableMemory 61.1 | 当前 vmFreeCount×VMPageSizeBytes,只代表空闲页口径;尚未逐点复核该值 | 核对原始页数、页大小来源、单位和取整;不能拿整机空闲量与进程占用作大小正确性判据。完整可回收容量另立证据门禁 |
| GPU 三项相同 | 留存 Raw 有 45/44/45、3/2/3,也有完全相同;不是“同值即错误” | 三个原始字段→metricID→Wire→图表逐层追踪;禁止互相 fallback;空闲/动画/负载同窗核查 |
| Thermal Status missing | 稳定当前等级源仍开放 | 查明受支持系统回包与枚举;未获得源前展示 source-missing,不用温度或通知名称推算 |
| GPU Counter 等待 | 旧批已知 schema/heartbeat 不代表 data row;当前两份无 gpu.counter.raw,尚需核对选择和 Adapter 生命周期 | 区分未启动、探测失败、schema-ready、首行超时、有效行与断流;源及单位具证据才关闭 |
| 停止随负载变慢 | delivery.py 串行校验、metrics、frames、report、startup、launch_trace、mapping、calibration;多次读/解码风险 | 增量派生、封存检查点;停止后不扫描全量 Raw;完整报告与独立审计分开标识 |
以上表格将内存两项拆开说明,但用户八类问题范围不变。留存 finalizer 子阶段耗时合计 A 约 0.818 秒、B 约 3.585 秒,仅包含已记录阶段,绝非点击停止到报告的端到端耗时。需新加完整边界测量。
4. 状态机与并发所有权
Disconnected → DevicePreparing → DeviceReady → AppPreparing → Prepared → Starting → Recording → CaptureStopped → Sealing → ReportReady。
每个源独立记录 unprobed/preparing/prepared/activating/streaming/missing/failed/timed-out、原因、最近有效时间和证据级别。全局 Prepared 表示必要源可运行,展示可运行源数量与失败项,不把全部指标宣称 ready。
应用、设备、指标配置绑定 generation。切换应用立即取消旧准备,迟到回包不得推进新 generation;断线、锁屏、失效 tunnel 触发重新验证。开始期间禁止重复提交;停止幂等;starting 期间也能取消,取消后禁止迟到 launch。停止超时后隔离旧 owner,未确认释放前不复用其 socket。
DTX 单 reader 只分发,不做大 JSON、base64、重解码和磁盘 fsync。每个流保持源内顺序。控制通道优先、普通源与重型源分队列;并发探测建议上限 2,协议要求串行的服务仍串行。writer 单所有者;重型解码优先采用单个受控 worker,线程受 GIL 限制时用对照数据决定是否迁移独立进程,避免盲目增加线程。截图单并发,不允许定时任务堆叠。
启动窗口启用后必须先接收有效就绪信号再 launch;激活时间计入 click-to-launch。若临时激活超过目标,明确展示 activating,禁止为了“秒开”先启动再丢 prelaunch 证据。准备 TTL、激活耗时与跨源冲突进入基线测试。
5. 轻量数据管线与一致性
唯一设备连接 / 单 DTX reader
→ 小型有序 Envelope + 有界分发
→ Raw 单 writer(持续哈希、分段索引、检查点)
→ 一次解码 → 普通指标 / Frame / Startup 增量引擎
→ 完整派生流与报告聚合持久化
→ 实时窗口快照 → 可见图表
Raw 记录是事实源,派生事件携带 raw sequence、算法版本、源质量与 generation。跨源只按可证明时间关系合并。存储 schema 若升级必须加版本、新旧读取兼容和 fixtures,旧会话不回写。
建议重型内存队列维持当前 64 MiB 上限作为起点,32 MiB 预热预算不盲目扩大。按观测 p95 吞吐 R 配置服务能力至少 1.5R,2R 短突发验证缓冲;磁盘落后时显式积压/丢失事件。磁盘 spool 亦设可配置容量和剩余空间阈值,满盘触发 partial 并停止受影响源,不无限缓存。
主机流式哈希只能证明主机所收记录的完整性,不证明设备无丢失。写盘与派生分别维护 durable sequence;checkpoint 仅在相应 fsync 成功后推进。停止时 seal_sequence 必须被全部必要派生引擎消费。崩溃恢复从最后 durable checkpoint 回放尾段,重复 sequence 幂等;保留离线全量审计命令,但不强制放进每次点击停止关键路径。
6. 固定窗口图表
设总事件 N、窗口秒数 W=60、采样率 r、可见曲线数 V、绘图宽度 P。
- 当前路径反复排序/扫描,单次更新可达 O(V×N log N),并分配中间数组。
- 新路径样本进入有界时间队列,均摊 O(1);乱序在有界窗口内归并,迟到点保留完整记录并标注,不全历史重排。
- 窗口存储 O(指标数×r×W),绘制 O(V×min(rW,2P));共享最新时间和窗口快照,禁止每条曲线重新排序。
- 用像素桶 min/max 保留尖峰、质量断点及桶内顺序;统计从全精度流计算,不从显示降采样反算。
- 默认快照刷新上限 5 Hz;滚动时可降到 2 Hz,停止滚动恢复。后台状态只发布小型 immutable 快照。
- 懒加载并跟踪实际可见性,屏幕外图表暂停绘制;完整数据仍落盘。截图缩略图后台解码,独立有界缓存;原图按需读取。
- 最终报告采用分段索引、多分辨率曲线,完整范围可浏览,缩放后加载细节,不一次加载整场所有点到主线程。
7. 随开随停与 2 秒报告合同
下列是待批准的目标预算,不是已测成绩。使用 host monotonic 时间,设备启动阶段继续使用其证据时钟,不混用。
| 边界 | 目标 | 计时与适用条件 |
|---|---|---|
| 点击→UI 状态反馈 | p95≤100ms、max≤200ms | Mac 本地;包括 starting 时取消 |
| 已准备→唯一 launch 请求 | p95≤500ms | 含启动源实际激活;普通/启动两种模式分测,设备请求完成另记 |
| 点击→关闭采样边界 | p95≤100ms、max≤200ms | 关闭样本接纳,不等于设备已释放;晚到 drain 事件隔离为收尾数据 |
| 设备源关闭与 lease 释放 | p95≤1s、max≤2s | 正常设备;异常分支限时隔离,不能将超时称成功释放 |
| 普通 Raw 接收→Wire | p95≤250ms | 首点 warmup 不补 0;慢频率源不套用 250ms 首次出值目标 |
| 点击停止→完整报告 ready | 每个验收样本≤2s | 从点击开始计时,含 drain、持久化与索引封存;不是只测绘制摘要 |
报告 2 秒要求在明确的产品支持规模内验证,初始矩阵 1/10/30/60 分钟,Raw 20 MB/300 MB/1 GB、全部常规源与两项可选源组合。更大规模不能自动推定通过。报告 ready 必须包括截至 seal 的完整常规曲线、统计及每个选中源的终态质量。深度源本身缺失可显示 missing 原因,正在积压计算不得伪装完整报告。若未满足尾段预算,显示 finalizing/积压与原因,该样本判 2 秒门禁失败。
为避免把重任务转移到采集中导致新卡顿:每秒观察 queue lag、decode throughput、writer throughput、主线程帧耗时和设备扰动;持续服务率低于输入率时门禁失败。离线 schema 研究、未知码 census、全量二次审计不属于用户报告必需产物,可显式后台独立执行,状态单列。
8. 指标口径处理与资料门禁
本方案不修改现有数学公式。实施前每个口径变更必须先更新对应 Canonical 页十要素、历史记录和测试。
- Memory/XcodeMemory:现有 physFootprint 原始 bytes 保留,UI 明示同源 candidate-alias。独立 gauge 需新证据后再命名,不人为做差。
- AvailableMemory:逐点校验
freeBytes=vmFreeCount×pageSizeBytes;展示若使用 MiB 为/1048576,若 MB 为/1000000,按实际产品合同统一并标明。负计数、页大小未知时 missing;整机总容量、空闲容量、可回收容量分开。61.1 尚未完成数值验证。 - GPU:Renderer/Tiler/Device 独立取字段、保持原始百分比,缺字段置空;同值合法性以 Raw 链为准。当前范围为 foreground-device,不宣称进程专属。
- Startup/Frame:先拿到完整可解析流,再证实时间桥、连续性、目标归属与阶段语义。禁止因为 unknown=0 就判语义正确;零记录也可能 unknown=0。
- Thermal:必须观察到明确当前等级回包、枚举与稳定读取,温度或通知事件不替代状态。
- GPU Counter:必须取得实际 data row、counter schema、设备/系统适配和单位;heartbeat 不计数值样本。建议首行 10 秒超时,恢复需重新出现合法行;低频源阈值以实际协议验证调整并入档。
若在固定版本实验中仍无源,交付研究结果和所需资料,不承诺通过工程重构创造该能力。所需资料:对应系统版本的匿名原始协议回包、受控负载/阶段标记及同窗结果;付费账号或凭据不写入文档。当前无需再次换连接库。
9. 分批实施与依赖
| 批次 | 工作 | 产物与通过门禁 |
|---|---|---|
| R0 基线与可观测性 | 两份留存重放;端到端时标、主线程 profile、队列/CPU/RSS/I/O 基线;确认版本及会话对应 | 匿名机器证据和基线;有时间戳不等于 E4 修复 |
| R1 全源准备与策略解耦 | 采集配置/展示配置分开;准备状态机、generation、幂等开始停止、选项冻结、源独立失败 | 常规曲线全隐藏仍采全量;两项可选准备但未启用时无持续 Raw;设备切换和取消测试 |
| R2 实时窗口与主线程瘦身 | 固定窗口缓存、可见性、后台分段、截图缩略图;移除全历史排序 | 60 分钟采集滚动性能不恶化、完整落盘点数不减少;与基线比较 |
| R3 单次增量派生与报告封存 | writer/checkpoint、共享解码、分段报告、恢复、尾段预算 | 离线独立 replay 与在线派生一致;崩溃/满盘/断连;每轮完整报告≤2s |
| R4 启动/Frame 源闭环 | 解码格式、分片头、时钟、loss、生命周期/帧语义逐层验证 | 可控正负例和因果测试,再目标应用 E4;没有源证据保持 partial |
| R5 内存/GPU/Thermal/Counter | 逐点映射、同源解释、字段审计、状态反馈、数据行补齐 | 原始到显示证据链;Thermal/Counter 未取得真实值则该项保持开放 |
| R6 整体验收与发布 | 全源+可选组合、长短会话、数值回放、资源开销与公证产物 | 站点归档、签名公证/独立安装、窄范围提交双推送、Webhook |
依赖 R0→R1→R2→R3;R4/R5 复用 R0 证据并在 R3 后做完整回归,R6 汇总。用户批准本方案后逐批开发,每批有独立结果与开放门禁;未经批准不启动实施。资源与耗时目标以实验收敛,不用未验证工期承诺替代技术门禁。
10. 测试与效率预算
以下预算待基线确认后由用户批准调整,禁止测试后降低标准以宣称通过:
- 主线程常规更新 p95≤8ms;滚动帧 p95≤16.7ms(60Hz 显示环境);>100ms 主线程阻塞为零。区分测试机器与刷新率,保留录制/profile 证据。
- 图表及缩略图增量缓存目标≤64 MiB;30→60 分钟稳态 App/Sidecar RSS 增长各≤10%,排除正常初次缓存后再统计,同时记录绝对峰值。
- 报告 CPU/RSS/I/O 单独计量;持续主机 CPU 较 R0 同负载不得回退超过 10%,CPU 明确按单核 100%计。新增全源负载与旧少源模式不能直接作优化对比,另做等源 A/B。
- 吞吐目标持续≥1.5×实测输入 p95,2×突发 10 秒不静默丢数据;临界吞吐/磁盘空间不足明确 partial。
- 展示与采集独立测试:默认全常规源;曲线 0/3/全部;Startup×截图四组合;窗口边界、乱序、missing/0/重置、峰谷、应用切换、取消、锁屏、断线、短存储、进程崩溃和重启恢复。
- E2:真实 Raw 尺寸回放与合成极端各至少 20 轮;必须含真实大 payload,不以 byteLength 小占位模型替代内存压力。固定输入校验 hash、依赖版本、运行环境和算法版本。
- E4:每种实际接入系统独立验收,正常短会话每组合≥3轮;10/30/60分钟各至少1轮,再对最差组合重复3轮。iOS 15+/26/27 无相应设备就保留待测,不外推。
- 数值验证区分原始传输、目标归属、数学计算、外部同窗对齐。启动与逐帧关键语义由受控 fixture 因果证明,不仅用同一解码器自证。
- 完整报告≤2s 每轮都检查,另报告 p50/p95/max、样本数、失败比例;设备释放单独报告。禁止只呈现最优轮或忽略 timeout。
11. 交付与回滚
仅方案阶段:Markdown、HTML 快照、时间线登记、构建与链接检查、窄范围提交、双远程推送尝试及 Webhook。本文前述均为设计,不宣称开发完成或数值已修复。
实施后:增量写入/报告 schema 和窗口实现均保留受控 feature flag;回滚不破坏新旧 Session 读取,切换只在会话边界生效。性能退化、sequence 丢失、报告 hash 不一致触发暂停推广并保留证据。
正式产品交付需 Developer ID、Notary accepted、staple、Gatekeeper、独立解包/安装与 SHA-256;证书/公证或法务未就绪保持 partial,不把内部包冒充正式包。每批本地窄提交并对两个远程分别记录结果,保留既有无关工作区变更。
交付最终汇报前发送企业微信 Webhook,地址从环境变量或 Keychain 读取且不输出;仅 HTTP 2xx 且 errcode=0 视为成功。失败交付标 partial 并说明重试方式。
12. 当前进度与下一阶段
本轮完成 E1 修复方案;已知八类问题仍开放,旧 E2 测试数不等于新体验通过。下一阶段等待明确批准后执行 R0 基线及 R1 策略解耦,再按 R2~R6 收口。64 项 Registry 是合同定义数量,不是 64 项在所有系统均具备 E4 数值能力。
代码溯源:MacApp/Sources/PerfowlCore/SessionTimeline.swift 的 playbackPoints;MacApp/Sources/PerfowlApp/PerfowlWorkbenchView.swift 的 WorkbenchMetricChart/segments;MacApp/Sidecar/perfowl_collector_core/{realtime,recording,delivery,metric_engine}.py。历史证据:上一轮修复、最终 HEAD 回归。