3. GDB断点详解:设置断点、条件断点、临时断点、断点管理、断点的高级用法

断点这东西,说白了就是调试的基石。没有断点,你只能眼睁睁看着程序跑飞,然后一脸懵。我刚开始用GDB那会儿,只会最简单的b main,后来踩的坑多了,才慢慢摸透断点的各种玩法。今天咱们就把断点彻底聊透。

3.1 设置断点的基本姿势

最常用的就是break命令,简写b。你可以按函数名、行号、文件名+行号来设断点。

(gdb) b main              # 在main函数入口
(gdb) b 42                # 在当前文件第42行
(gdb) b foo.c:128         # 在foo.c的第128行
(gdb) b my_function       # 在任意文件的my_function

我个人习惯用b file.c:line这种形式,尤其是项目大了以后,同名函数很容易撞车。你想想看,如果两个文件都有init(),不加文件名GDB会给你设两个断点,有时候自己都忘了哪个是哪个。

小技巧: 用info files先看看当前加载了哪些源文件,再精确设断点,省得设错地方。

3.2 条件断点:只在需要时停下

条件断点是我在项目中用得最多的。比如循环里只想看第1000次迭代的情况,总不能手动按1000次c吧?

(gdb) b 42 if i == 1000
(gdb) b my_function if x > 0 && y < 0
(gdb) b file.c:200 if strcmp(name, "error") == 0

我曾经调试过一个内存泄漏问题,只在某个特定大小的分配时才会触发。用条件断点b malloc if size == 4096,一下子就定位到了问题。要是没有条件断点,你得在malloc里断住几百次,手都按酸了。

注意: 条件表达式里的变量必须是当前作用域可见的。如果变量不在当前栈帧,GDB会报错。另外,条件表达式如果写复杂了,会影响调试性能——每次断点命中都要计算一次。

3.3 临时断点:用完即弃

临时断点用tbreak,它只生效一次。断点命中后自动删除,特别适合那种「我就想看一眼这里,然后继续跑」的场景。

(gdb) tbreak 88
(gdb) tbreak my_function

我经常在调试某个函数时,临时想看看调用栈,就在函数入口设个tbreak,然后bt看一下,再c继续。省得后面还要手动清理断点。

3.4 断点管理:别让断点失控

项目大了,断点一多就容易乱。我见过同事调试时设了20多个断点,最后自己都搞不清哪个是哪个。这时候管理命令就派上用场了。

命令 作用
info breakpoints 列出所有断点,带编号和状态
disable 2 禁用编号2的断点(不删除)
enable 2 重新启用
delete 2 删除编号2的断点
clear main 删除main函数处的断点

我个人习惯给断点加注释,方便记忆:

(gdb) b main
(gdb) comment 1 "这是程序入口,检查初始化"
(gdb) b 200 if errno != 0
(gdb) comment 2 "检查错误处理分支"

嗯,这里要注意:comment命令在旧版GDB里可能不支持,建议用GDB 10以上版本。

3.5 断点的高级用法

玩到高级阶段,断点就不只是「停住」那么简单了。你可以让断点执行一系列命令,甚至自动打印变量然后继续跑。

3.5.1 断点命令列表

(gdb) b 42
(gdb) commands
  > print x
  > print y
  > continue
  > end

这样每次命中第42行,GDB会自动打印x和y的值,然后继续执行。我在调试一个网络协议栈时,用这个技巧在每个包处理函数入口打印包类型和长度,省得一次次手动敲命令。

3.5.2 硬件断点 vs 软件断点

GDB默认用软件断点,就是在指令里插入int 3。但有些场景必须用硬件断点:

  • 调试只读内存(比如Flash里的代码)
  • 调试自修改代码
  • 某些嵌入式平台不支持软件断点
(gdb) hbreak 0x40001000   # 在地址处设硬件断点
(gdb) info breakpoints    # 看类型列,hw表示硬件断点

3.5.3 观察点:数据变化自动断

观察点(watchpoint)是断点的变种,它监视某个变量或内存地址,一旦值发生变化就停下。

(gdb) watch my_var          # 监视变量
(gdb) watch *(int*)0x7fff   # 监视内存地址
(gdb) rwatch my_var         # 读时断住
(gdb) awatch my_var         # 读写都断

我曾经用watch抓过一个野指针问题。有个全局变量莫名其妙被改写了,我在它上面设了观察点,程序跑起来后立刻断在了一个不该写它的函数里。嗯,那感觉就像破案一样爽。

核心要点:
  • 条件断点用if,避免无效中断
  • 临时断点tbreak,用完即焚
  • 断点管理用info/disable/enable/delete
  • 高级玩法:commands、硬件断点、观察点

最后说一句:断点不是越多越好。我见过有人设了30个断点,结果程序根本跑不动。精确定位,少而精,才是调试的真谛。

GDB断点知识体系 断点 (Breakpoint) 设置断点 b main / b 42 / b file.c:128 条件断点 b 42 if i == 1000 临时断点 tbreak 88 / tbreak func 断点管理 info / disable / enable / delete 高级用法 commands / hbreak / watch 断点命令列表 (commands) 硬件断点 (hbreak) 观察点 (watch) 图:GDB断点五大分类及高级用法展开

可可精品资料库