watch 命令在变量被写入时立即中断,依赖硬件调试寄存器,开销小且捕获所有修改路径;需先运行程序并停在有效位置再设置,支持全局/静态变量最稳定,局部变量须栈帧有效;info watchpoints 查看状态,delete/disable/enable 管理编号观察点;display 仅暂停时打印,无法拦截修改,不可替代 watch;多线程下仍有效,但需注意线程上下文和频繁中断风险。

用 watch 设置观察点,变量一改就停
直接用 watch 命令,GDB 会在变量值被写入时立即中断——不是等你单步走到那行,而是只要内存被修改就触发。它底层依赖硬件调试寄存器(x86 上是 DR0–DR3),所以开销极小,且能捕获所有写入路径,包括指针解引用、数组越界覆盖、多线程并发修改等场景。
常见错误是:没加 -g 编译,或对未初始化/已出作用域的变量设 watch,GDB 会报 Watchpoint 1 deleted because the program has left the block in which its expression is valid;还有人误用 display,它只在每次暂停时打印,不拦截修改。
- 必须先运行程序并停在某个有效位置(比如
break main后run),再执行watch var,否则 GDB 找不到变量地址 - 支持表达式,如
watch *ptr、watch arr[5],但不能含函数调用或非常量下标(watch arr[i]会报错) - 对全局/静态变量最稳定;局部变量需确保当前栈帧有效,函数返回后 watch 自动失效
info watchpoints 查看和管理观察点
设置后别忘了确认是否生效:info watchpoints 会列出编号、地址、是否启用、触发条件等。编号是后续操作的关键——删、禁用、启用都靠它。
容易忽略的是:watchpoint 默认是“写入即停”,但如果你只想在特定条件下中断(比如只当 a == 0 时才响应修改),GDB 不支持直接加 if 条件;得改用条件断点 + print + continue 组合模拟,效率低很多。
-
delete 2删除编号为 2 的观察点 -
disable 1临时关闭,避免干扰其他调试流程;enable 1恢复 - 多个 watch 共存时,GDB 按触发顺序逐个报告,不会漏掉——这点比手动
print可靠得多
为什么 display 不能替代 watch
display var 是“每次暂停时自动 print”,它完全不感知变量是否被改过。你在循环里设了 display i,GDB 只会在每个断点、step、next 后显示一次 i,而变量可能在中间被悄悄改了十几次,你根本不知道。
典型误用场景:想查 status 被谁改坏,却只设 display status,然后单步跳过几十行,最后发现值变了但不知何时变的——这等于放弃 watchpoint 最核心的能力。
-
display适合观察“随控制流推进而自然变化”的变量(比如循环计数器) -
watch才是专治“幽灵修改”的工具,尤其适合排查野指针、竞态、堆损坏 - 两者可共存:
display看上下文,watch定位篡改点
多线程下 watch 仍有效,但要注意线程切换
硬件 watchpoint 对所有 CPU 核心生效,所以哪个线程改了变量,GDB 就停在哪个线程的上下文中。这是它比软件断点+检查逻辑可靠得多的原因。
坑在于:停住后你看到的可能是另一个线程的栈帧,bt 显示的调用链未必和你预期的线程一致;而且如果多个线程高频争用同一变量,GDB 可能因频繁中断卡死,此时建议先用 disable 临时关掉,或换用 catch syscall write 等间接方式缩小范围。
- 用
info threads确认当前是哪个线程触发了 watch - 用
thread apply all bt快速扫一遍所有线程栈,看谁在操作该变量 - watch 对
volatile或内存映射 I/O 地址也有效,但某些嵌入式平台可能不支持硬件监控
info watchpoints 确认一下还在不在。











