在构建旨在重放执行的确定性虚拟机(VM)时,开发者面临一个根本性的挑战:如何一致地表示时间。标准的墙钟时间不适用于重放,因为程序中两个点之间经过的时间取决于外部因素,如宿主机 CPU 负载、缓存状态和其他运行中的进程。如果客户机程序的决策取决于当前时间,使用宿主机的墙钟会导致程序在每次运行时表现不同,从而破坏确定性的核心要求。
为了解决这个问题,Rewind 虚拟机利用了一种通过客户机指令流而非宿主机时钟来衡量进展的机制。虽然有人可能会考虑使用原始指令计数,但 Rewind 的作者证明了这是不可靠的。在涉及简单循环的测试中,指令计数发生变化,因为硬件计数器捕捉到了客户机程序本身无法看到的外部事件——例如网络数据包、处理器间中断(IPIs)和缺页中断。相比之下,条件分支计数保持稳定,因为它仅根据客户机的内部逻辑递增,使其成为更可靠的“确定性时间”代理。

这个表情包说明了选择使用条件分支作为时间主要指标而非原始指令计数的决策。

此视觉效果解释了为什么原始指令计数无法提供确定性时间代理的技术原因。
Rewind 为这种测量提供了两种主要模式:“退出时间”和“计数器时间”。退出时间仅在客户机执行系统调用(syscall)时推进时钟,将所有内部计算视为瞬时完成。虽然这种方法简单,但它可能无法重现与超时或特定竞态条件相关的错误,因为它将所有工作挤压到同一个时间片中。计数器时间则为客户机每完成一个条件分支推进一纳秒,提供了更接近现实世界的近似值。尽管计数器时间仍与实际的宿主机墙钟不同(例如,一个在宿主机上耗时 14.2 秒的任务被测量为 11.7 秒),但其一致性确保了相同的程序在每次重放中始终产生相同的结果。

这张图片作为 Rewind 虚拟机通过客户机内部逻辑衡量进展方法的总结。