除了硬件断点外,软件断点也可用于中断调试器。仅当代码放置在 RAM 中时,调试器才支持这一点。这通常根本不实用。
作为一种生活黑客,可以做的是制作breakpoint()
包含硬件断点的函数。现在,无论在哪里调用此函数,都会激活断点:
void __attribute__ ((noinline)) breakpoint()
{
__asm("NOP"); // <---- set a hardware breakpoint here!
// hello, please Step Out to go to caller location (ex: press Shift-F11)
}
void main()
{
int x = 1;
breakpoint(); // break into the debugger
printf("%d\n", x);
x += 2;
breakpoint(); // break into the debugger, again
printf("%d\n", x);
}
调试器现在将停止在breakpoint()
. 要查看断点的实际位置,必须先跳出。
这种技术为单步等交互释放了硬件断点,并且可用的 4 个断点通常就足够了。
笔记:
- 该
breakpoint()
函数的另一种选择是使用__asm("BKPT #0")
,它进入调试器。不幸的是,没有办法跳过这条指令(在 STM32/GDB 上测试),所以它实际上就像一条 HALT 指令。它可用于在故障条件或未使用的中断中放置断点。
- 省略时,该
breakpoint()
功能似乎只工作一次__asm("NOP");
- 关于STM8,它特别有一个支持字节更新的闪存,所以它可以以非常类似于RAM的方式工作。调试器可以根据需要使用它来插入软断点。
- 不过,STM8 只有 2 个断点寄存器,它们可能专门用于单步执行。
- 其他更强大的 ARM Cortex MCU 可以有 6 或 8 个硬件断点。
- GDB(和其他调试器)在处理断点的方式上可能会更聪明一些。例如,当您在一个函数中有多个断点时,通常不可能在到达某个断点之前先到达某个断点。在某些常见的调试场景中,这可能会有很长的路要走。