GDB 断点调试:文件、条件与临时断点
介绍 GDB 的编译调试信息、文件和行号断点、临时断点、条件断点,以及常用的执行和查看命令。
断点是使用 GDB 定位程序问题的基础。程序运行到断点时会暂停,开发者可以查看变量、调用栈和线程状态,再决定单步执行还是继续运行。
本文使用 Ubuntu 22.04 环境中的 GDB 12.1 和 GCC 11.3 进行示例说明。GDB 的命令在其他 Linux 平台通常也适用。
一、准备调试信息
准备一个简单的 main.cpp:
#include <iostream>
int main() {
for (int i = 0; i < 100; ++i) {
std::cout << i << '\n';
}
return 0;
}
使用 -g 或 -g3 生成调试信息。调试阶段通常使用 -O0,避免优化改变变量和源代码行之间的对应关系:
g++ -std=c++17 -g3 -O0 -Wall -Wextra -pedantic main.cpp -o main
启动 GDB:
gdb ./main
也可以在启动时直接设置要调试的可执行文件:
(gdb) file ./main
二、设置断点
2.1 根据函数设置
(gdb) break main
break 可以缩写为 b。如果程序中存在重载函数,GDB 可能会列出多个候选位置,需要选择具体的断点位置。
2.2 根据文件和行号设置
(gdb) break main.cpp:5
在当前文件中也可以直接指定行号:
(gdb) break 5
断点需要位于编译器生成了调试行信息的代码位置。若行号没有对应的可执行代码,GDB 可能会将断点调整到附近的代码行,或者暂时将断点设为 pending。
2.3 临时断点
如果只想让断点生效一次,可以使用 tbreak,它也可以缩写为 tb:
(gdb) tbreak main.cpp:5
(gdb) run
程序第一次命中临时断点后,该断点会自动删除。它适合只需要观察一次初始化过程的场景。
三、运行和查看状态
(gdb) run # 从头运行程序
(gdb) continue # 继续运行到下一个断点
(gdb) next # 单步执行,不进入函数内部
(gdb) step # 单步执行并进入函数
(gdb) finish # 执行到当前函数返回
(gdb) until 20 # 执行到当前文件第 20 行附近
常用查看命令:
(gdb) print i # 打印表达式,简写为 p
(gdb) display i # 每次暂停时自动打印 i
(gdb) info locals # 查看当前栈帧的局部变量
(gdb) info args # 查看当前函数参数
(gdb) list # 查看源代码,简写为 l
(gdb) backtrace # 查看调用栈,简写为 bt
不再需要自动显示某个表达式时,可以先通过 display 输出的编号查询,再删除:
(gdb) info display
(gdb) undisplay 1
四、条件断点
当循环次数很多时,不需要每次都暂停,可以为断点增加条件:
(gdb) break main.cpp:5 if i == 50
(gdb) run
程序会在 i 等于 50 时暂停。条件断点也可以在断点创建后设置:
(gdb) info breakpoints
(gdb) condition 2 i == 50
这里的 2 是断点编号,需要根据 info breakpoints 的实际输出替换。
五、管理断点
(gdb) info breakpoints # 查看断点、观察点及编号
(gdb) disable 2 # 暂时禁用断点
(gdb) enable 2 # 重新启用断点
(gdb) delete 2 # 删除指定断点
(gdb) delete # 删除全部断点
断点可以使用 commands 配置命中后的自动操作。例如只打印变量而不进入交互暂停:
(gdb) commands 2
> silent
> printf "i = %d\n", i
> continue
> end
六、一个常用调试流程
遇到一个需要定位的问题时,可以按下面的顺序操作:
- 使用
-g3 -O0重新编译程序。 - 在入口函数或怀疑出错的函数设置断点。
- 使用
run启动程序。 - 使用
next、step和print缩小问题范围。 - 使用
backtrace检查调用路径。 - 对循环变量或关键状态设置条件断点。
- 找到原因后删除或禁用临时断点,继续验证修复结果。
如果程序使用了较高优化级别,变量可能被优化掉、合并或移动,GDB 可能提示变量不可用。此时应先在 -O0 下完成定位,再使用目标优化级别验证性能和最终行为。
七、总结
- 使用
-g生成调试信息,调试阶段通常同时使用-O0。 break设置普通断点,tbreak设置只生效一次的临时断点。- 使用
break file:line if condition设置条件断点。 next不进入函数,step会进入函数,finish执行到当前函数返回。- 使用
info breakpoints、disable、enable和delete管理断点。