Skip to content

pymobiledevice3 + py-ios-device 双 Provider 基座决策草案

已确认的范围

用户已明确:go-ios 没有当前产品不可替代的能力,后续不进入 Perfowl 的候选架构、开发计划和发布包。Perfowl 底层收敛为两类 Provider:

  • pymobiledevice3:连接基座、Developer service 路由、设备与应用目录、基础性能采集;
  • py-ios-device 深度 Provider:仅补充 pymobiledevice3 当前无法稳定提供或 Perfowl 尚未完成解析的深度原始数据;
  • Perfowl Metric Engine:唯一负责指标公式、单位、scope、质量与版本;
  • xctrace:按需校准与诊断,不是实时主链。

本页是 E1 架构草案。产品代码需要用户明确批准后再修改。

决策:不把整个 py-ios-device 变成产品基座

不接入它的设备发现、应用安装、证书、位置、XCTest、网络模拟、CLI 输出和最终指标计算。只允许一个白名单 Deep Collector Provider 调用以下原始能力:

深度能力只承接什么不承接什么
CoreProfile原始 KDebug buffer、事件码、device mach timestamp、丢包与解析错误py-ios-device 输出的 FPS / Jank / BigJank / Stutter
Process NetworkStatistics指定 PID 的原始 attributes / counters未校准的速率、范围推断和缺值 0
Energy debug gaugePID、原始字段、采样请求与回包未确认单位的 Energy 分值
GPU Counter设备返回的 counter schema、原始 counter stream、时间基固定机型 counter 名称和预计算百分比
App Launch Lifecycle原始 lifecycle / CoreProfile 事件、时间基和线程 / 进程事实已格式化的阶段耗时和总启动时间

基础 CPU、内存、线程、调度、磁盘、Graphics、设备级 Network 和 Screenshot 继续由 pymobiledevice3 Provider 负责,不因启用深度 Provider 自动切源。

“不整体接入”在工程上的准确含义

架构上不允许 Mac App、Session Schema 和 UI 依赖 py-ios-device 的类、CLI 或数据格式。所有调用都封装在独立的 PyIOSDeviceDeepCollectorProvider 后面。

但验证 Spike 阶段不建议从 GPL 仓库直接复制若干 .py 文件:其 InstrumentServer、DTX 编解码、RemoteLockdown、KPerf、GPU decoder 和 lifecycle parser 存在内部依赖。首个实验可以固定完整 Python 包版本并在独立研究 Sidecar中只暴露白名单命令;这只是隔离的实验依赖,不代表整个库成为产品架构。

生产阶段有两个待批准选项:

  1. 保留固定版本的 py-ios-device 作为 Deep Sidecar 依赖,建立 SBOM / NOTICE / GPL 交付方案;
  2. 基于原始协议证据和自有 Fixture 建立 Perfowl 自己的深度 Adapter,不直接复制库代码。

独立进程边界不会自动消除 GPL-3.0 评估,正式分发仍受现有法务门禁约束。

连接与采集关系

text
Mac App
  └── Connection Broker(pymobiledevice3)
        ├── iOS 15/16: USB Lockdown provider
        └── iOS 17+: RSD TunnelLease

                 ├── Pmd3BaseCollector
                 │      └── 基础 raw events

                 └── PyIOSDeviceDeepCollector(按需启动)
                        └── 深度 raw events

两路 raw events → Perfowl Event Envelope → Metric Engine → Session / UI / Report

Connection Broker 交付的是 TunnelLease,不是把 pymobiledevice3 的 Python对象跨进程传递。租约包含匿名设备键、iOS 版本、route、RSD host / port、可选 local proxy、Developer image 状态和 lease generation。

  • iOS 15/16:Deep Collector 可走已配对 USB Lockdown;
  • iOS 17.4+:优先使用 macOS native / 可共享 RSD;
  • iOS 17.0–17.3.1:使用 pymobiledevice3 persistent tunneld;
  • pymobiledevice3 的临时进程内 userspace tunnel 不应交给另一个进程假装可共享。

DTX 所有权与并发门禁

py-ios-device 不能读取 pymobiledevice3 已打开的同一个 DTX socket。每个 socket 必须只有一个 reader。

第一阶段采用最保守方式:

  • 普通 Session:只启动 Pmd3BaseCollector;
  • Deep Spike:Deep Collector 独占深度 DTX Session,只验证 raw 数据;
  • 不在第一阶段让两个库同时持续读取同一设备的多个 Instruments session。

如果产品最终要求基础曲线和深度指标同时实时展示,必须先做 DTX-CONCURRENCY-1:验证同一设备两个独立 DTX connection 的建链成功率、事件丢失、串流互扰、停止顺序和 30 分钟稳定性。通过后才能并行;未通过则由一个 Deep Collector 在深度模式下统一拥有所需 DVT channels。

统一原始事件契约

Deep Provider 不输出最终指标,只输出类似以下事实:

text
eventType
service / channel / selector
provider / providerVersion / providerCommit
deviceMachTimestamp / machNumer / machDenom
hostReceiveMonotonicNs / wallTimestamp
targetPID / targetGeneration / identityStatus
rawPayload / rawPayloadHash
decodeStatus / droppedEventCount / reconnectGeneration

之后由 Perfowl 完成:

  • mach time → 纳秒转换;
  • 相邻帧 FrameTime;
  • FPS 窗口;
  • SmallJank / Jank / BigJank / Stutter;
  • counter 差分与 bytes-per-second;
  • Energy / GPU 字段单位映射;
  • missing / warmup / reset / candidate / valid 质量语义。

每个公式仍需在进入 Registry、UI 和报告前单独入档。

推荐实施批次

批次内容结果边界
D0冻结 py-ios-device 版本、模块依赖、License 与白名单只有 E1;不打包产品
D1DeepCollectorProvider 协议 + fake fixture不连接真机,不改变默认 Collector
D2CoreProfile raw Spike只保存 raw,不显示 Jank
D3Process Network / Energy raw Spikescope / unit 未校准前保持 candidate
D4GPU schema / counter raw Spike未知 schema fail-closed
D5Lifecycle raw Spike被动 / 受控启动分开
D6DTX 双连接并发验证决定基础与深度能否同 Session 并行
D7公式、知识页、同场校准与灰度发布E4 后逐指标开放

下一批准边界

建议只批准 D0 + D1:建立白名单和 Provider / Fixture 契约,不接真机、不集成最终指标、不改变现有 pymobiledevice3 默认链路。完成后再批准 D2 CoreProfile raw Spike。

Perfowl · Performance Observer