断点设置与管理:调试的“刹车系统”
调试这事儿,说白了就是让程序停下来,看看它到底在干嘛。而断点,就是让程序停下来的“刹车”。我做了这么多年嵌入式开发,可以说,断点用得溜不溜,直接决定了调试效率的高低。今天咱们就聊聊GDB里那些跟断点相关的核心操作。
break:最基础的断点
break命令,简写b,是咱们最常用的。用法很简单:
(gdb) break main # 在main函数入口停
(gdb) break 42 # 在当前文件的第42行停
(gdb) break file.c:100 # 在file.c的第100行停
(gdb) break func_name # 在某个函数入口停
我个人习惯在函数入口处先下一个断点,然后单步跟踪。这样能快速了解函数的整体流程。记得有一次我调试一个网络协议栈,函数嵌套特别深,我就在每个关键函数入口都下了断点,配合backtrace看调用栈,问题很快就定位了。
list命令先看看源码,再下断点。
tbreak:用完即走的临时断点
tbreak就是临时断点。它跟break的区别在于:断点命中一次后,自动删除。这玩意儿在循环里特别有用。
(gdb) tbreak 88 # 在第88行下一个临时断点
(gdb) tbreak main # 在main函数下一个临时断点
你想想看,有时候你只想在某处停一次,看看某个变量的值。如果用break,还得手动删,多麻烦。tbreak就是为这种场景设计的。我经常在调试初始化代码时用,因为初始化只执行一次,用临时断点刚刚好。
condition:带条件的断点
条件断点,就是只有满足某个条件时,程序才会停下来。这个功能在循环里简直是神器。
(gdb) break 42 if i == 100 # 当i等于100时,在第42行停下
(gdb) condition 1 i > 50 # 给断点编号1添加条件:i大于50
为什么要用条件断点?举个例子,你有一个循环跑10000次,但只在第5000次时出问题。如果没有条件断点,你得手动按5000次continue,手都按酸了。有了条件断点,一次搞定。
我曾经调试过一个内存泄漏问题,就是在malloc调用处下了条件断点,条件是分配大小超过1MB。这样只抓大块分配,很快就找到了泄漏点。
watch:观察点的妙用
观察点,也叫数据断点。它不是让程序在某个代码位置停下,而是当某个变量的值发生变化时停下。
(gdb) watch my_var # 当my_var的值改变时停下
(gdb) watch my_var == 100 # 当my_var等于100时停下(硬件支持)
(gdb) rwatch my_var # 当my_var被读取时停下(硬件支持)
(gdb) awatch my_var # 当my_var被读取或写入时停下
观察点特别适合追踪那些“莫名其妙被改掉”的变量。我记得有一次,一个全局变量在某个地方被意外修改了,但代码量太大,根本不知道是谁干的。我就在那个变量上下了watch,程序一跑,GDB立刻告诉我是在哪个文件哪一行改的。嗯,那一刻的感觉,就像在黑暗里突然开了灯。
catch:捕获异常和事件
catch命令用来捕获特定事件,比如C++异常、系统调用、信号等。
(gdb) catch throw # 捕获C++异常的抛出
(gdb) catch syscall write # 捕获write系统调用
(gdb) catch signal SIGSEGV # 捕获段错误信号
这个功能在嵌入式开发中特别有用。比如你怀疑某个信号处理有问题,直接catch signal SIGUSR1,程序收到信号时就会停下,你可以看看当时的堆栈和变量状态。
info breakpoints:查看所有断点
断点下多了,难免会忘记。这时候info breakpoints(简写i b)就派上用场了。
(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x4004e0 main
2 breakpoint keep y 0x400520 func
3 hw watchpoint keep y my_var
输出里会显示断点编号、类型、状态、地址和位置。我每次调试前都会先i b看一眼,确认断点状态,避免浪费时间。
delete/disable/enable:管理断点
断点可以删除、禁用、启用。这三个命令各有用途:
| 命令 | 作用 | 示例 |
|---|---|---|
delete |
永久删除断点 | delete 1 删除编号1的断点 |
disable |
临时禁用,保留编号 | disable 2 禁用编号2的断点 |
enable |
重新启用被禁用的断点 | enable 2 启用编号2的断点 |
我个人习惯用disable而不是delete。为什么呢?因为有时候你只是暂时不想用某个断点,但后面可能还会用到。删了还得重新下,多麻烦。禁用就方便多了,想用的时候enable一下就行。
delete了,结果得重新一个个下。后来我就养成了习惯:不确定要不要删的,一律disable。
知识体系总览
下面这张图,把断点相关的核心概念串起来了。你可以把它当作一个快速参考。
断点这东西,用好了就是调试利器,用不好就是浪费时间。我建议你多练练条件断点和观察点,这两个在实际项目中用得最多,也最能提升效率。嗯,今天就先聊到这儿,下次咱们接着讲单步执行和查看变量。