在软件开发过程中,调试是一个不可或缺的环节。随着技术的发展,调试工具也变得越来越强大。其中,条件断点(conditional breakpoints)是调试工具中的一项重要功能,它允许开发者在满足特定条件时触发断点。本文将分享作者在使用条件断点调试DOS设备驱动程序时的个人经验,探讨其在开发中的实用性以及可能的性能影响。
条件断点允许开发者设置一个断点,该断点只有在满足特定条件时才会触发。这些条件可以是变量的值、其他断点的状态等。虽然本文不会再次详细介绍这些功能,因为它们已经在微软的文章中被详细描述,但希望通过分享使用经验,来评估它们在日常开发工作中的实际效用。
在调试一个加载到两个不同段(0x0CEE和0x0B8C)的DOS设备驱动程序时,使用了DOSBox-X,这是一个在Visual Studio2019和Visual Studio 2022下编译良好的工具。目标是找出什么导致指令指针在这两个段之间变化,例如,当CS:IP从0x0CEE:XXXX变化到0x0B8C:XXXX,反之亦然。
如果没有条件断点,这可以通过添加if语句来实现,这些语句会在满足这些条件时写入相关的日志消息,并通过在这些新添加的语句上设置断点来触发。这些更改可以在core_normal.cpp的CPU_Core_normal_Run(void)函数中进行(大约在第162行),并在OutputDebugStringA处设置断点:
if (条件) {
OutputDebugStringA("日志消息");
}
一旦满足正确的条件,断点就会立即触发。通过使用监视窗口,可以检查那一刻x86寄存器(AX,BX,CX,DX)的值。
在Visual Studio的较新版本中,这个过程更简单,因为不需要修改代码。触发条件和要写入的消息可以直接在断点本身中设置。
这会立即生效,并且一旦触发断点,日志消息就会打印到输出窗口。然而,也意识到模拟DOS机器现在启动得非常慢,DOSBox达到“Starting MS-DOS”阶段需要将近120秒,而之前不到5秒。调整各种DOSBox速度设置并没有帮助,但禁用条件断点后,原始执行速度得以恢复。通过在DOSBox中启动DOS程序之前启用断点来解决这个问题,但这并没有太大帮助,因为执行速度仍然会立即减慢。手动修改代码的原始方法并没有减慢启动速度。
经过进一步研究,找到了原因。CPU_Core_Normal_Run()不是一个普通的函数。忽略其他情况(例如,指令缓存等),CPU_Core_Normal_Run()会在DOSBox反汇编的指令准备好执行时被调用。原始的8088速度为4.77MHz(大约0.2 MIPS),这意味着这个函数每秒将被执行超过200,000次。在默认的DOSBox配置中模拟486,这个函数每秒将被调用数百万次。很可能,添加条件断点引入了一些处理开销,这只有在函数被调用次数过多时才会变得明显。