外观
启动缺失 · 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 ms | 99 ms |
| dwell(tap 预热时长) | 1161 ms | 3097 ms |
| device loss episodes | 4 | 1 |
关键事实:
- 完整轮的 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"发现正交:
- 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,若被服务接受)从没试过。 - 判据错位:Step 4G 只量了
TRACE_LOST_EVENTS计数,**从没跨多轮冷启动量"启动阶段完整率"**这个真正的产品指标。 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 configbso(总 kdebug nkdbufs)PERFOWL_STARTUP_BSOC(int)→ tap configbsoc(per-CPU override,若被服务接受)PERFOWL_STARTUP_BM(0/1)→ tap configbm(缓冲模式)- 接受十进制或
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) | 复现基线完整率 |
| B1 | PERFOWL_STARTUP_BSO=33554432(32M) | 更大档总缓冲 |
| B2 | PERFOWL_STARTUP_BSO=67108864(64M) | 极大档总缓冲 |
| C1 | PERFOWL_STARTUP_BSOC=8388608(8M/核,若被接受) | 直接给 CPU 0 加份额 |
| D1 | PERFOWL_STARTUP_BM=1 | 换缓冲模式(drain 语义) |
每轮必须记录(每项都来自真实会话,缺失即留空不补 0):
- 启动阶段完整率(
requiredMissingPhases==[]占比)、每轮缺失阶段清单 System Interface Init时长、dwelldeviceLostMarkerCount、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.pywait_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/11 | 0–4,全 CPU0 |
| BSO=2M | bso=2000000,bm=0 | ✓ | 2/3 | 1–2(如 [208,233]、[1145,1168]) |
| BSO=8M | bso=8000000,bm=0 | ✓ | 2/2 | 1–4(如 [306,1010,1255,1508]) |
| BM=1 | bm=1 | ✓ | 3/3 | 2–3(如 [305,560,1254]) |
| BSOC=8M/核 | bsoc=8388608,bm=0 | ✓ | 2/3 | 0–4(缺失轮 SysInit=219 头崩) |
| B1/B2 | bso=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被拒绝、或完整率无改善,都要如实结案,不美化。