双模时钟:离线可复现与在线真实感如何兼得
测试平台架构有个绕不开的两难:
- 要可复现,就得把环境做”假”——数据回放、时间受控,但这离真实世界远;
- 要真实感,就得接真环境——真总线、真 ECU、真实时钟,但每一次运行都不一样,bug 抓到了却重现不了。
多数平台在这道选择题里二选一。Cyclone Core 的答案是不做选择,做时钟:一个双模时钟,让同一套引擎在两种物理里行走。这篇讲清楚这个架构决策。
一、先分清两个旋钮:时钟 × 环境
“离线/在线”其实是两个独立旋钮的组合:
| 虚拟时钟 | 真实时钟 | |
|---|---|---|
| 回放数据 | ① 离线模式:完全受控的实验室 | ③ 很少用(不展开) |
| 实采总线 | ④ 没意义(真实数据不等你) | ② 在线模式:真实世界的现场 |
有用的主要是两格:离线模式 = 虚拟时钟 + 回放文件,在线模式 = 真实时钟 + 实时总线。
二、离线模式:时间由平台注入
离线模式下,时间不是流逝的,是注入的。调度器以固定步长(1ms 一个 tick)推动整个世界:发一帧数据、走一步被测逻辑、记一笔日志。传感器数据来自文件回放,整个世界是封闭的。
这换来离线模式的王冠:比特级一致。同一份场景 YAML、同一个 seed,跑一千次,日志逐字节相同,判定结果必然相同。由此得到三个工程红利:
- bug 永久可复现——抓到即钉死,不存在”我机器上是好的”;
- CI 友好——结果可缓存(内容寻址的 PASS),流水线不抖;
- 免费的大规模采样——seed 矩阵跑 300 把做统计判定(见本专栏第 1–3 篇),机器时就是全部成本。
三、在线模式:时间由物理注入
在线模式下,时钟服从物理世界(操作系统时钟、总线真实节拍),数据来自 SocketCAN/以太网上的真实节点。你得到的是离线给不了的东西:
- 真实时序:端到端时延、死线错过、抖动——虚拟时钟里”1 tick 永远是 1 tick”,证明不了 CPU 上跑了多快;
- 真实硬件行为:真实 ECU 的启动抖动、总线负载、协议栈脾气;
- 现场证据:问题发生在真实环境里,证据链从真实环境采集。
四、诚实的代价:为什么在线 + 真实 ECU 必须放弃比特级
这是本文最重要的一节,因为很多平台在这里说假话。
接上真实 ECU 后,比特级一致在物理上不可能,至少有五个泄漏点:
- 传感器与采样噪声:ADC 量化、热噪声,同一物理量两次采样不会逐比特相同;
- 时钟漂移:每个节点的晶振有自己的 ppm 级偏差,分布式系统没有”同一个现在”;
- 总线仲裁:CAN 仲裁取决于各节点瞬时状态,帧的精确到达次序会漂;
- 调度抖动:非硬实时 OS 的调度延迟是随机变量;
- 温度等物理效应:硬件行为随温度漂移,实验室空调都是变量。
承认这一点,才能谈在线模式真正保留的三样东西:
- 证据真实性:日志采自真实总线,证据链的源头是真的;
- 统计判定稳定性:单次不可复现,但 Wilson 下界、SPRT 判定在统计意义上稳定——判定引擎本来就是为噪声世界设计的(第 1–3 篇);
- 判定逻辑一致性:在线离线跑的是同一个判定引擎、同一份阈值推导书,不是两套班子。
一句话:离线给你”同一输入同一输出”,在线给你”不同输出、同一套判定”。前者靠确定性,后者靠统计学。
五、闭环:capture once, replay forever
两种模式不是两座孤岛,它们之间有一条传送带:
在线实采一次 ──► 保存为回放文件 ──► 离线永久回放
▲ │
└──────── 修复后在线验证 ◄── 离线回归通过
现场出的 bug,抓包一次,从此它变成离线场景库里的一份回归资产——每次版本迭代,这个 bug 都要被重新处刑一遍。场景库因此越用越厚:不是工程师凭空编出来的,是真实世界一份份”贡献”的。
六、架构含义:模式切换是配置,不是重写
对架构师最关键的一句:同一份场景 YAML、同一个判定引擎、同一种证据格式,横跨两种模式。 切换模式改的是时钟源和数据源两个配置,不是代码。这意味着:
- 场景资产零分叉:离线开发的场景直接上线跑;
- 判定资产零分叉:阈值推导书两模式通用;
- 证据资产零分叉:报告格式一致,审核方学习一次即可。
七、落点
双模时钟在 Cyclone Core 里是已实现的核心机制(虚拟时钟 + 1ms 确定性调度,已支持;在线模式适配 SocketCAN/UDP,已支持)。场景包里每个场景都标注了它跑在哪种模式下——下载下来,从离线的一致性跑起,再把同一份场景接到真实总线上。
测试平台最深的架构决策不是选真还是选假,而是想清楚:什么必须确定(判定逻辑与证据链),什么可以随机(物理世界)——然后用时钟把两者隔开。
附:名词解释(按出场顺序)
| 名词 | 通俗解释 |
|---|---|
| 双模时钟 | 平台支持虚拟时钟与真实时钟两种时间源的设计 |
| 虚拟时钟 | 时间由平台按固定步长注入(而非物理流逝),世界完全受控 |
| 离线模式 | 虚拟时钟 + 数据回放:封闭世界,比特级可复现 |
| 在线模式 | 真实时钟 + 实时总线:真实时序与真实硬件行为 |
| 比特级一致 | 同输入重跑,日志逐字节相同、判定必然相同 |
| 内容寻址的 PASS | 输入哈希相同则判定结果可直接复用缓存,CI 不必重跑 |
| seed 矩阵 | 同一场景用多个随机种子批量执行,供统计判定 |
| 泄漏点 | 真实世界中破坏比特级一致性的物理噪声来源(共五类) |
| 总线仲裁 | CAN 总线上多节点同时发帧时按优先级裁决的机制,结果随瞬时状态漂移 |
| ppm | 百万分之一;晶振频率偏差的常用单位 |
| capture once, replay forever | 在线抓包一次、离线永久回放的资产闭环 |
| SocketCAN | Linux 下的 CAN 总线网络接口框架 |
(产品能力以 Cyclone Core 当前版本为准。)
下一步:
下载场景包亲手跑一遍