Skip to content

启动缺失 · kdebug 环缓冲扩容实验(bso / bsoc / bm) ​

目标:把"设备侧 kdebug 环在冷启动窗口被洪流打爆导致启动阶段缺失"这条路,用扩容再钉一次——但要钉在三个此前从未测过的维度上(更大档 bso、per-CPU 分配、以及缓冲模式 bm),并以启动阶段完整率而非 TRACE_LOST_EVENTS 计数作为判据。本文记录实验的动机、设计、代码接线与操作步骤;真机 A/B 结果待设备执行后回填,严禁补 0 / 估算 / 跨轮拼接。

1. 触发这次实验的新证据 ​

用户复测发现启动阶段缺失多集中在第一次。对本机已落盘会话离线统计(脚本 MacApp/scripts/startup-miss-correlate.py,只读;样本 n=10:4 缺失 / 6 完整):

维度(中位数)缺失轮完整轮
System Interface Init(冷启动重量)350 ms99 ms
dwell(tap 预热时长)1161 ms3097 ms
device loss episodes41

关键事实:

  • 完整轮的 System Interface Init 全部 ≤ 115 ms;缺失轮中位数 350 ms、最高 1972 ms。冷启动越重越容易缺。
  • 所有丢失 episode 全部落在 CPU 0,且时点全部落在启动窗口内(例:[417, 660, 927, 1100] ms,cpu={'0':4})。
  • dwell 不是控制变量:有反例(dwell=3349 ms 仍缺失、dwell=70 s 才完整)——故不走"给 tap 加预热"这条路。

机制判断:第一次冷启动本身最重 → 自身事件洪流打爆设备 CPU 0 的 per-CPU kdebug 环 → UIKit 之后的生命周期边界(过渡码单事件双边界)在 flush 前被 wrap → phase-not-observed。与既有启动随机缺失调查"设备侧丢失、host 无洞"一致。

2. 为什么"扩容"值得再试一次 ​

扩容此前做过一轮(调查 Step 4G):真机 config-audit 坐实下发过 bso=2000000 与 bso=8000000,设备接受但 TRACE_LOST_EVENTS(=7) 与基线完全一致 → 4 倍缓冲零改善。这是强负信号(若纯粹容量不足,4 倍怎么也该动一动),指向瓶颈更可能在 drain/导出吞吐或瞬时 per-CPU 突发,而非总容量。

但那一轮留下三个从未测透的维度,恰好与本次"CPU 0"发现正交:

  1. per-CPU 分配 vs 总量:kdebug 缓冲是 per-CPU 的。A16 为 6 核(2P+4E),若 bso 设的是总 nkdbufs 再按 CPU 均分,8M ÷ 6 ≈ 1.3M/核;而丢失 100% 集中在 CPU 0(冷启动主线程 + dyld 挤在大核),它需要的份额远超均分。从没试过更大档(32M / 64M),也没验证 bso 是否等比放大了 CPU 0 那一份;bsoc(per-CPU override,若被服务接受)从没试过。
  2. 判据错位:Step 4G 只量了 TRACE_LOST_EVENTS 计数,**从没跨多轮冷启动量"启动阶段完整率"**这个真正的产品指标。
  3. bm(buffer mode)从没系统测过:当前 bm=0。buffer mode 影响"环满怎么处理 / 怎么 drain",而非"环多大"。既然证据指向吞吐/drain 瓶颈,bm 可能比 bso 更对症(adapters.py:405 注释:py-ios-device 的 App Launch Lifecycle 路径用 bm=1)。

诚实的期望管理:bso 大概率仍无效(4 倍→零改善的先例摆在那);本次价值在于用更大档 + per-CPU 验证 + 完整率判据 + 顺带 bm 把它彻底钉死——要么证实无效正式结案,要么意外有效。

3. 代码接线(默认 OFF,不碰生产配置) ​

新增受控实验旋钮,全部默认关闭;未设任何 env 时下发给设备的 tap 配置与生产字节级一致,不改变采集就绪身份。

  • 新模块 MacApp/Sidecar/perfowl_collector_core/startup_ring_experiment.py:
    • PERFOWL_STARTUP_BSO(int)→ tap config bso(总 kdebug nkdbufs)
    • PERFOWL_STARTUP_BSOC(int)→ tap config bsoc(per-CPU override,若被服务接受)
    • PERFOWL_STARTUP_BM(0/1)→ tap config bm(缓冲模式)
    • 接受十进制或 0x 形式;上限 512 MiB 防手误;非法值直接 ValueError。
  • 接入点 adapters.py(_make_stream):在既有 bm/csd 处理之后应用实验 overlay,注入进 pymobiledevice3 的 tap _config,因此实验值会:
    • 落入 effective_config → 写进 coreprofile-config-audit.log(可审计,无隐藏状态);
    • 仅在实验激活时进入 config_signature(OFF 时签名与历史字节一致,无回归)。

验证(本机纯 Python,无 pytest 环境):默认 OFF 为 no-op 且配置字节不变、三旋钮注入正确、16 进制/空值/非法值校验正确、adapters 导入无循环依赖——全部通过。单元测试 tests/test_startup_ring_experiment.py 已提交给 CI。

4. 真机 A/B 执行步骤(待执行) ​

设备:iPhone15,4 / iOS 17.0 / USB(与既有基线同机)。判据脚本:startup-miss-correlate.py(以完整率为准,非 loss 计数)。

分组(每组 ≥ 10 次冷启动,务必包含"设备静置后的第一次冷启动"这一最重场景):

组配置目的
A0生产默认(无 env)复现基线完整率
B1PERFOWL_STARTUP_BSO=33554432(32M)更大档总缓冲
B2PERFOWL_STARTUP_BSO=67108864(64M)极大档总缓冲
C1PERFOWL_STARTUP_BSOC=8388608(8M/核,若被接受)直接给 CPU 0 加份额
D1PERFOWL_STARTUP_BM=1换缓冲模式(drain 语义)

每轮必须记录(每项都来自真实会话,缺失即留空不补 0):

  • 启动阶段完整率(requiredMissingPhases==[] 占比)、每轮缺失阶段清单
  • System Interface Init 时长、dwell
  • deviceLostMarkerCount、lostByCPU、丢失时点(相对 launch)
  • effective_config 里 bso/bsoc/bm 是否真的被设备接受(config-audit 对照)
  • 若设备拒绝 bsoc 或大档 bso → 如实记为"服务未接受",不算失败

判定:

  • 有效 = 相对 A0,完整率有可复现提升(Wilson CI 不重叠),且丢失时点/CPU0 episode 明显减少;
  • 无效 = 完整率与 A0 无统计差异 → 正式结案"扩容/换模式对设备侧启动缺失无效",回落多轮整轮择优;
  • 服务不接受 = 记录设备行为,视同该旋钮不可用。

5. 结果 ​

5.1 A0 基线(2026-09-16 18:57–19:01,百度网盘,11 轮) ​

指标值
完整率7 / 11 = 63.6%
缺失形态4 轮全为 Initial Frame Rendering(尾部截断),无头部崩
System Interface Init全部 91–188 ms(均为轻启动)

5.2 B1(bso=32M)/ B2(bso=64M):撞就绪门槛,采集失败 ​

三次尝试(19:03–19:04)全部失败,特征一致:

  • 会话目录无 collector-core/,failureReason="TimeoutError",lastProductWireSequence=17;
  • 事件时间线:诊断事件 → 恰好 5.0 秒后抛 TimeoutError,命中 CoreProfile 的 5.0s 就绪门槛(realtime.py wait_ready(timeout=5.0))。

机制判定:bso=32M/64M 把设备 kdebug 缓冲调得很大 → 设备需填更久才向 host 吐第一个 chunk → 首个可审计 chunk 迟于 5 s 到达 → 就绪门槛超时 → 会话失败。这与 Step 4G 当年 2M/8M "能出流"不矛盾(小缓冲更早 flush),并且本身是一条负信号:更大 bso 反而推迟首帧投递,既撞就绪门槛又治不了突发溢出。

边界:不为实验放宽生产 5 s 就绪门槛(属采集武装架构红线)。改为向下测 bso 可用上限,并另测 bm/bsoc。

5.3 可用档位真机对照(2026-09-16 19:13–19:21,百度网盘) ​

每个变体的 bso/bsoc/bm 均经 session 内落盘配置核对确认真实生效:

组config(已核对生效)武装完整率每轮 CPU0 丢失 episode(launch+ms)
A0 基线(none)✓7/110–4,全 CPU0
BSO=2Mbso=2000000,bm=0✓2/31–2(如 [208,233]、[1145,1168])
BSO=8Mbso=8000000,bm=0✓2/21–4(如 [306,1010,1255,1508])
BM=1bm=1✓3/32–3(如 [305,560,1254])
BSOC=8M/核bsoc=8388608,bm=0✓2/30–4(缺失轮 SysInit=219 头崩)
B1/B2bso=32M/64M✗ 撞 5s 门槛——

备注:bm=1 组另有 1 轮 host_restarted_before_finalize(宿主连接重启,与 buffer 无关,未计入)。各组 n=2–3,小样本。

5.4 结论:扩容 / 换模式无效,bso 路线结案 ​

决定性证据不是完整率(小样本不足以做统计),而是丢失本身的持续性:

  • CPU0 丢失 episode 在每一个变体里照常发生,量级(0–4)与基线一致。8M / bm=1 中那些"完整"的轮次仍带 1–4 次 CPU0 丢失,只是未砸中生命周期边界——不是丢失被消除。
  • 若扩容有效,CPU0 丢失应随缓冲增大趋向 0;实测 11 个武装轮中 10 个仍有 CPU0 丢失,唯一 0 丢失轮也在噪声内。
  • 完整率看似 9/11 高于基线 7/11,但为陷阱:完整率由冷启动重量(SysInit)主导,两个实验缺失轮恰是 SysInit 偏重者(92 尾丢 / 219 头崩),不能归因于旋钮。
  • 大档(32M/64M)额外撞 5s 就绪门槛,反而更早失败。

与 Step 4G 一致,现于 Build 60 架构再次证实:kdebug 缓冲大小(bso/bsoc)与模式(bm)均不能消除设备侧 CPU0 启动丢失——瓶颈是突发/吞吐而非容量。bso 扩容路线正式判负结案,回落多轮整轮择优(Route A)。实验旋钮保留为默认 OFF 的研发入口,不进生产默认路径。

判读:若 2M/8M 也 5 s 超时 → 现架构就绪门槛与任何 bso 不兼容,bso 路线判负结案;若能武装 → 存在 bso 上限,用能武装的最大档补满 10 轮对比完整率。

6. 边界与红线 ​

  • 默认 OFF;实验旋钮不改变生产采集就绪身份(OFF 时 config_signature 字节不变)。
  • 实验只影响设备 kdebug 缓冲配置,不动 Unified CoreProfile 生命周期、filters、kperf owner、Startup completion、Frame decoder。
  • 所有 resolved 值写入 effective_config / config-audit / 本文,杜绝隐藏状态。
  • 结果一律据实:bso/bsoc 被拒绝、或完整率无改善,都要如实结案,不美化。

Perfowl · Performance Observer