断点进阶:条件断点、临时断点、硬件断点、断点命令列表
断点这东西,说白了就是让程序在指定位置停下来。但实际调试中,你不可能每次都手动敲 b main 然后一步步走。真正的高手,会用条件断点、临时断点、硬件断点,再配合 commands 命令列表,让调试效率翻倍。
我个人习惯是:能用条件断点解决的,绝不用手动跳过。能用临时断点的,绝不留垃圾断点。嗯,咱们一个一个来看。
条件断点:只在满足条件时停下
条件断点,就是给断点加个「门禁」。只有条件为真,程序才停。否则直接忽略。
我在项目中遇到过一个大坑:一个循环跑了 10 万次,第 99999 次才出 bug。如果不用条件断点,你得手动按 99999 次 continue…… 想想就崩溃。
语法很简单:
(gdb) break main.c:42 if i == 99999
(gdb) b func if x > 0 && y < 100
你也可以先设普通断点,再追加条件:
(gdb) break main.c:42
(gdb) condition 1 i == 99999
这里的 1 是断点编号。用 info break 可以查看所有断点及其编号。
if foo() == 0,每次断点命中都会调用 foo(),如果 foo() 有全局变量修改,调试结果可能不准。
临时断点:用完即焚
临时断点,就是一次性的。命中一次后自动删除。非常适合「我就想看一眼这里,看完就走」的场景。
语法:
(gdb) tbreak main.c:42
(gdb) tb func
我曾经在调试一个中断服务函数时,只想确认它是否被触发一次。用 tbreak 设好,命中后自动消失,省得手动清理。你想想看,如果忘了删普通断点,下次调试时突然停住,那得多懵。
临时断点也支持条件:
(gdb) tbreak main.c:42 if count == 5
命中一次,条件满足,断点自动消失。干净利落。
硬件断点:对付 Flash 和只读内存
普通断点是通过修改内存指令实现的(把指令替换成 trap)。但有些区域是只读的,比如 Flash、ROM、或者被 MPU 保护的内存。这时候就得用硬件断点。
硬件断点依赖 CPU 的调试寄存器,数量有限(通常 4-8 个)。但好处是:不修改内存,不影响代码执行。
用法:
(gdb) hbreak main.c:42
(gdb) hbreak func if flag == 1
info break 可以查看哪些是硬件断点(类型显示为 "hw breakpoint")。
什么时候必须用硬件断点?
- 代码在 Flash 或 ROM 中运行
- 调试 Bootloader 或早期初始化代码
- 代码段被 MPU/MMU 设置为只读
- 需要调试自修改代码(self-modifying code)
断点命令列表(commands):让断点自动干活
这是我最喜欢的功能。没有之一。你可以给断点绑定一组命令,命中后自动执行。比如:自动打印变量、自动修改变量、自动继续运行。
语法:
(gdb) break main.c:42
(gdb) commands
> print x
> print y
> continue
> end
这样每次命中断点,GDB 自动打印 x 和 y,然后继续运行。你完全不用手动操作。
我在项目中用过最爽的场景:调试一个内存泄漏,我在 malloc 和 free 上各设一个断点,用 commands 记录调用栈和地址,然后自动继续。跑完一轮后,用脚本分析日志,直接定位到未匹配的 malloc。
commands 里可以写什么?
- 任何 GDB 命令:print、set、continue、step 等
- 条件判断:用
if控制执行路径 - 调用外部脚本:
python print("hello") - 静默模式:
silent可以抑制断点命中的默认输出
举个例子,我想在循环中只打印第 5 次到第 10 次的值:
(gdb) break loop.c:10
(gdb) commands
> silent
> if i >= 5 && i <= 10
> print i
> print array[i]
> end
> continue
> end
silent 让输出更干净,只显示你关心的内容。
知识体系图
下面这张图总结了断点进阶的四个核心概念及其关系:
避坑指南
我曾经在调试一个实时系统时,用了条件断点,结果每次命中都要评估表达式,导致系统时序被打乱,中断响应超时。后来改用硬件断点 + commands 的 silent 模式,才解决问题。
还有一次,我在 commands 里写了 continue,但忘了加 silent,结果每次命中都打印一堆信息,终端刷屏刷了 10 分钟…… 嗯,从那以后我养成了习惯:只要 commands 里有 continue,第一行一定是 silent。
最后说一句:条件断点和 commands 的组合,是 GDB 调试的「核武器」。你花 10 分钟写好 commands,可能省下 2 小时的手动操作。这笔账,怎么算都划算。