Skip to content

StartupTiming 启动阶段指标 v14

定义

StartupTiming 的 metricID、业务公式和展示语义继承 v13。v14 将屏障收紧为 挂起源就绪:目标 PID 先挂起存在,CoreProfile 首个非空、timebase 合法且 trace header 含该 PID 的 chunk 先写入 immutable Session,然后才 SIGCONT。因目标已在 header 中可精确归属,Startup 订阅收窄为当前阶段 decoder 真正消费的 4 组事件。

metricID、scope、unit 与来源字段

metricID定义scopeunit来源字段
ios.process.startup.app_launch_total_msApp Launch 完整首尾耗时processmillisecondsSystem Interface begin → Initial Frame terminal
ios.process.startup.system_interface_msSystem Interface Initializationprocessmilliseconds版本化 lifecycle mapping 完整 Span
ios.process.startup.static_runtime_msStatic Runtime Initializationprocessmilliseconds版本化 lifecycle mapping 完整 Span
ios.process.startup.uikit_initialization_msUIKit Initializationprocessmilliseconds版本化 lifecycle mapping 完整 Span
ios.process.startup.uikit_scene_creation_msUIKit Scene Creationprocessmilliseconds同 generation 的完整同名 Span之和
ios.process.startup.will_finish_launching_mswillFinishLaunchingWithOptions()processmilliseconds版本化 lifecycle mapping 完整 Span
ios.process.startup.did_finish_launching_msdidFinishLaunchingWithOptions()processmilliseconds版本化 lifecycle mapping 完整 Span
ios.process.startup.scene_will_connect_mssceneWillConnectTo()processmilliseconds版本化 lifecycle mapping 完整 Span
ios.process.startup.scene_will_enter_foreground_mssceneWillEnterForeground()processmilliseconds版本化 lifecycle mapping 完整 Span
ios.process.startup.initial_frame_rendering_msInitial Frame RenderingprocessmillisecondsUIKit begin → 呈现 terminal
ios.process.startup.observed_running_mslaunch 请求到观察到进程processmillisecondslaunch.requested.rawobserved-running/rebound
ios.process.startup.foreground_ready_mslaunch 请求到观察到前台processmillisecondslaunch.requested.rawobserved-foreground

Registry 事实源为 MacApp/Sources/PerfowlCore/PerformanceModels.swift;v14 未新增 metricID。

业务计算、窗口、聚合与取整

text
coreTimestampNs = floor(coreMachTicks × coreNumer / coreDenom)
spanDurationMs = (endCoreTimestampNs - startCoreTimestampNs) / 1,000,000
phaseTotalMs = sum(同 phase 的所有完整 Span durationMs)
appLaunchTotalMs = (initialFrameEndTimestampNs - systemInterfaceBeginTimestampNs) / 1,000,000
  • 窗口是一次 controlled-cold-launch 的单一 PID/generation,不跨启动聚合。
  • 同名阶段按线程 stack 配对;阶段为完整 Span 求和,总时间用首尾锚点差。
  • KDebug 跨 CPU/分块按 Mach tick、到达序排序;Double 全精度保存,UI 才格式化。
  • 缺失、未闭合、跨 generation、负耗时和不可解码值均为 null/—

Fixture Mach 桥原始字段

字段含义约束
sequence本次进程内 marker 序号从 1 单调递增
phase / edge生命周期阶段及 begin/end每阶段唯一闭合
machAbsoluteTicks回调边界调用 mach_absolute_time() 的原始 tickUInt64 正整数
machNumer / machDenommach_timebase_info正整数,且本 Session 不变
injectedDelayMsFixture 单变量延迟0–2000 ms
bufferModeCoreProfile 实际源配置01;同批不混合
samplingTriggerEnabled是否发送第二 sampling trigger auxiliaryStartup 因果批次必须为 true
samplingIntervalNssampling trigger 的 si固定 5,000,000 ns
samplingTriggerVersion冻结配置来源版本py-ios-device-app-lifecycle.2.4.25
streamPhasechunk 到达阶段activepost-stop-drain
endOfStream是否为 stop 后空结束通知boolean
coreProfileDrainStatusSession 结束排空状态not-required / end-of-stream / quiescent / timeout / failed
coreProfilePostStopChunkCountstop 后仍写入 raw Session 的 chunk 数非负整数,不缺值填 0
coreProfileDrainIdleSeconds静默完成窗口固定 2 秒
coreProfileDrainMaxSecondsdrain 总等待上限固定 6 秒

本地文件只保存 Fixture 自身的受控数据;进入公开证据前移除设备与签名身份。

同钟换算与完整性公式

text
fixtureTimestampNs = floor(fixtureMachTicks × fixtureNumer / fixtureDenom)
coreTimestampNs    = floor(coreMachTicks × coreNumer / coreDenom)

markerDurationMs = (fixtureEndNs - fixtureBeginNs) / 1,000,000
markerDelayErrorMs = abs(markerDurationMs - injectedDelayMs)
markerToleranceMs = max(5 ms, injectedDelayMs × 5%)

sameTimebase = fixtureNumer == coreNumer AND fixtureDenom == coreDenom
insideCoreWindow = coreMinMachTicks <= fixtureBeginTicks <= fixtureEndTicks <= coreMaxMachTicks
clockBridgePassed = sameTimebase AND insideCoreWindow
                    AND markerDelayErrorMs <= markerToleranceMs
                    AND identityVerified AND droppedEventHighWater == 0

受控启动的数据源 readiness 先于上述桥接公式执行:

text
prelaunchSourceReady = firstCoreProfileChunkObserved
                       AND firstChunk.byteLength > 0
                       AND firstChunk.timeConfig.numer > 0
                       AND firstChunk.timeConfig.denom > 0

semanticCalibrationLaunchAllowed = prelaunchSourceReady

if debugID == 0x25010004 AND arg0 > 0 AND arg1 > 0:
    threadPid[arg1] = arg0

v13 的受控启动屏障优先于上述 v9 首 chunk readiness:

text
sourceActivationReady = coreProfileSetConfigSent
                        AND coreProfileStartSent
                        AND coreProfileStartAckObserved
resumeAllowed = targetSuspended AND sourceActivationReady
launchRequestMonotonicNs = sigcontDispatchMonotonicNs
  • launchBarrierMode=process-control-suspended-v1launchRequestBoundary=process-resumeresumeSignal=SIGCONT
  • 首 CoreProfile chunk 允许在 resume 之后到达;目标 PID 在 CoreProfile 启动前已存在且保持挂起,trace header 可用于精确归属。
  • 挂起、start ack、单消费者启动、resume 和 PID 归属任一失败时 fail closed。

v14 进一步要求真实数据就绪后才 resume:

text
startupSuspendedFilters = {
  0x1F070000, 0x2BDC0000, 0x2B870000, 0x31CA0000
}
preResumeSourceReady = firstChunk.byteLength > 0
                       AND firstChunk.timeConfig.numer > 0
                       AND firstChunk.timeConfig.denom > 0
                       AND traceHeader.threadPidMap contains suspendedPID
resumeAllowed = targetSuspended AND preResumeSourceReady
  • launchBarrierMode=process-control-suspended-source-ready-v1;首 chunk 先写盘,resume 后再写 launch.requested.raw

  • v14 不使用动态线程发现过滤类;header 缺少目标 PID 时 fail closed,不用事件邻近猜测。

  • 首 chunk 必须先写入 immutable raw Session,再发出 launch.requested.raw

  • 后续采集复用同一 iterator/subscription,不创建第二 DTX reader 或 owner。

  • prelaunch trace header 尚无目标进程时,精确承接 CoreProfile PERF_THD_Dataarg0=pid / arg1=tid 作为动态 thread 归属;该字段语义来自冻结 pykdebugparser kperf_thread_info_sample 解码。

  • CoreProfile bufferMode 写入 raw payload;重复性和因果聚合只接受同一模式,不对不同设备端缓冲策略做数值拼接。

  • readiness 超时、空 chunk 或 timebase 缺失时,本次启动语义校准 fail closed,不用无覆盖窗口的数据继续计算。

  • Startup trace 的 CoreProfile setConfig: 必须使用同一 DTX service/channel 发送两个有序 auxiliary:mainTraceConfig 在前,samplingTriggerConfig 在后;不得为第二配置建立新 socket。

  • sampling trigger 固定为 tsf=[65537]ta=[[0],[2],[1,1,0]]si=5,000,000nstk=1;UUID 每 Session 随机且不进入公开证据。

  • 非 Startup 的 FrameTime/FPS CoreProfile 路径不启用第二 trigger。启用与未启用的 Session 只做修正前后对照,不混合聚合。

text
samplingTriggerCompatible =
    allSessions.samplingTriggerEnabled == true
    AND allSessions.samplingIntervalNs == 5_000_000
    AND allSessions.samplingTriggerVersion == "py-ios-device-app-lifecycle.2.4.25"

coreProfileDrainPassed = coreProfileDrainStatus IN {"end-of-stream", "quiescent"}
                         AND coreProfileDrainIdleSeconds == 2
                         AND coreProfileDrainMaxSeconds == 6

startupRequiredFilters = {
  0x07000000, 0x25010000, 0x1F070000,
  0x2BDC0000, 0x2B870000, 0x31CA0000
}
filterSetExact = actualKdebugFilters == startupRequiredFilters
windowHeadroomMs = (coreMaxMachTicks - markerEndTicks)
                   * coreNumer / coreDenom / 1,000,000
  • samplingTriggerCompatible 仅用于显式启用 trigger 的研究批次;正式路径默认关闭时不要求该式。

  • quiescent 表示 stop 已发出、唯一消费者保持工作、最后一条 post-stop 消息后连续 2 秒无新消息,且总等待未超过 6 秒;这是对未承诺空结束通知的 CoreProfile 协议的可审计完成条件。

  • 启动阶段进入因果与 mapping 门禁前必须满足 coreProfileDrainPassed。总等待超时情况下即使 marker 落入窗口,也只保留诊断候选。

  • 6 组最小充分过滤类分别覆盖线程→PID、PERF_THD_Data、System/Static、UIKit/AppDelegate/Scene 及 Initial Frame terminal;集合外事件不参与当前版本计算。

  • filterSetExact=false 的 Session 不与 v12 聚合;windowHeadroomMs < 0 表示阶段边界被窗口截断,指标保持 null/—

  • 这里不拟合 offset:两源都显式使用同一设备的 mach_absolute_time tick,offset 固定为 0;若任一源无法证明为 Mach absolute,状态为 unsupported-clock-schema

  • timebase 变化、marker 倒退、重复边界、旧文件混入、跨 PID/generation 或窗口外均拒绝桥接。

  • control 的注入延迟为 0;仅校验耗时非负且边界闭合,不把真实回调执行时间强制为 0。

因果与 Mapping 门禁

text
controlMedianMs = median(至少 3 个 control 完整 CoreProfile Span)
delayedMedianMs = median(至少 3 个 delayed 完整 CoreProfile Span)
observedDeltaMs = delayedMedianMs - controlMedianMs
delayErrorMs = abs(observedDeltaMs - injectedDelayMs)
toleranceMs = max(20 ms, injectedDelayMs × 20%)
fixtureEvidencePassed = clockBridgePassed AND delayErrorMs <= toleranceMs

最终 mapping 同时要求:Fixture Mach 桥通过、单变量因果门禁通过、Mapping v3 同系统至少 3 次且至少 2 App 的重复性通过。多个候选同步响应时为 ambiguous

质量语义

  • auditable:同一 Mach absolute 时间基、完整边界、窗口与数据质量门禁全部通过。
  • unsupported-clock-schema:任一源不是可证明的 Mach absolute tick 或 timebase 不合法。
  • timebase-mismatch:Fixture 与 CoreProfile numer/denom 不一致。
  • outside-core-window:Fixture marker 不在本次 CoreProfile 记录窗口。
  • source-not-ready-before-launch:受控 launch 前未获得非空且 timebase 合法的 CoreProfile 首 chunk。
  • target-thread-unresolved:trace header 与动态 PERF_THD_Data 均未建立目标 PID 的 thread 归属,目标 Span 保持缺失。
  • marker-incomplete:begin/end、序号或配对不完整。
  • marker-delay-out-of-tolerance:Fixture 注入执行自身未达到门槛。
  • missing:marker 文件、CoreProfile 记录、身份或质量事实缺失。

这些状态与业务指标的 candidate/partial/missing 分开记录;缺值不写 0。

当前校准状态

  • E4:iOS 17 隔离探针确认 ActivityTrace 与 CoreProfile 并发会令 CoreProfile 0 条;单 provider、双 provider 结果相同,CoreProfile 单源对照为 401,517 条。因此 ActivityTrace 不进入实时启动链。
  • E2:Fixture Mach marker、沙箱取回、桥接审计器与单元测试已实现。
  • E4 iOS 17 根因证据:签名 Fixture 已安装;control 短 Session 取回 4 条 marker,timebase 一致、identity verified、drop=0,但 marker 早于首个 CoreProfile 解码窗口,状态为 outside-core-window
  • E4 iOS 17:prelaunch readiness 修正后,control 的 marker、timebase、identity 与 drop 门禁通过;300ms delayed 仍高概率为 outside-core-window
  • E4 iOS 18:开发描述文件更新、Fixture 安装及 control 时钟桥已通过;未启用第二 trigger 的 300ms willFinish 基线仍为 outside-core-window
  • E4 sampling trigger 对照:iOS 17 与 iOS 18 的 willFinish 300ms 仍为 outside-core-window,不能证明第二 trigger 有独特窗口收益;正式路径默认关闭。
  • E4 stop/drain 结果:iOS 17/18 四轮 willFinish / didFinish 300 ms 均达到 identity verified、drop=0、marker 完整与 drain=quiescent,但均为 outside-core-window;drain 不是剩余根因。
  • E4 窗口诊断:iOS 17 失败轮的 CoreProfile 窗口约 493–505 ms,iOS 18 约 214–224 ms;16 MiB raw chunk 证明设备端缓冲占用是下一个校准变量。
  • E4 待验收:使用 6 组最小充分过滤集重跑两种系统的 willFinish / didFinish;两端因果门禁未通过前,内部阶段继续 null/—

版本历史

  • v14(2026-09-06):首个真实 CoreProfile chunk 与 trace header PID 归属先于恢复,Startup 过滤集收窄为 4 组必需阶段事件。来源:Startup 挂起源就绪屏障 v1 方案
  • v13(2026-09-06):新增 ProcessControl 挂起启动屏障,将源启动 ack 与 App 恢复边界固化为可审计时序。来源:Startup 挂起启动屏障 v1 方案
  • v12(2026-09-06):新增 6 组 CoreProfile 最小充分过滤集、过滤同质性与窗口余量门禁。来源:Startup 最小充分过滤集 v1 方案
  • v11(2026-09-06):新增 CoreProfile stop-before-cancel、2 秒静默/6 秒总上限尾部 drain、结束状态与 post-stop raw 标记;sampling trigger 因 E4 无窗口收益改为默认关闭候选。来源:Startup CoreProfile Stop/Drain v1 方案
  • v10(2026-09-06):增加同一 pymobiledevice3 DTX owner 下双 auxiliary CoreProfile sampling trigger、源配置同质性与不混合聚合门禁。来源:Startup CoreProfile Sampling Trigger v1 方案
  • v9(2026-09-06):基于 iOS 17 outside-core-window E4,新增首 CoreProfile chunk 写盘先于 controlled launch 的 readiness 门禁。来源:Startup Prelaunch Readiness v1 修正方案
  • v8(2026-09-06):基于真机互斥证据,将并发 ActivityTrace 方案修订为 Fixture Mach absolute 离线桥。来源:Startup Mach Clock Bridge v1 修订方案
  • v7(2026-09-06):提出 ActivityTrace → CoreProfile 锚点桥,后被 E4 并发互斥证据取代。
  • v6(2026-09-06):冻结 Fixture 单变量延迟与 Semantic Evidence 质量契约。

Perfowl · Performance Observer