【项目管理】GDB调试
gdb(GNU Debugger) 是 Linux 和嵌入式开发中最常用的调试工具之一,可以用来调试 C/C++ 程序、排查崩溃、分析程序流程等。在嵌入式开发中,gdb 还可以通过远程调试(gdbserver)调试目标设备上的程序。
这篇文章是 gdb 调试的入门指南,包括常见命令、使用场景和一些高级技巧。
安装 gdb
:
sudo apt-get update
sudo apt-get install gdb
基本使用流程
编译时开启调试信息
在使用 gdb 调试程序之前,必须在编译时启用调试信息。确保编译时加上 -g
参数:
gcc -g -o my_program my_program.c
启动 gdb
启动 gdb 并加载可执行文件:
gdb ./my_program
gdb a.out
gdb app#调试完毕后退出
q: qiut
执行调试
进入 gdb 后,可以通过命令控制程序执行和调试流程:
- 设置断点。
- 单步调试。
- 查看变量值、堆栈信息等。
常用命令
程序控制
命令 | 功能 |
---|---|
run 或 r | 启动程序并在 main() 函数处暂停。 |
start | $12 |
continue 或 c | 继续运行程序直到下一个断点或程序结束。 |
quit 或 q | 退出 gdb 调试器。 |
kill | 终止当前调试的程序。 |
断点管理
命令 | 功能 |
---|---|
break [行号/函数名] | 在指定行号或函数入口处设置断点。 |
info breakpoints | 查看所有断点。 |
delete [断点编号] | 删除指定断点(不加编号则删除所有断点)。 |
disable [编号] | 禁用某个断点(不删除)。 |
enable [编号] | 启用某个断点。 |
condition [编号] 条件 | 为断点设置条件(满足条件时才会触发)。 |
单步调试
命令 | 功能 |
---|---|
step 或 s | 单步执行代码(进入函数内部)。 |
next 或 n | 单步执行代码(跳过函数调用)。 |
finish 或 fi | 执行完当前函数,返回调用函数处。 |
until [行号] | 执行到指定行号处。 |
jump [行号] | 使程序从当前要执行的代码处,直接跳转到指定位置处继续执行后续的代码。 |
变量和内存查看
命令 | 功能 |
---|---|
print [变量名] 或 p [变量名] | 打印变量值(支持表达式)。 |
set variable [变量名]=值 | 修改变量值。 |
info locals | 查看当前函数中的所有局部变量。 |
info variables | 查看程序中定义的变量。 |
x/[格式][单位] 地址 | 查看内存内容(如 x/4xw 查看 4 个字)。 |
格式说明:
- 格式:x(十六进制)、d(十进制)、c(字符)、s(字符串)。
- 单位:
- b:字节(1 byte)。
- h:半字(2 bytes)。
- w:字(4 bytes)。
- g:双字(8 bytes)。
查看代码和堆栈
命令 | 功能 |
---|---|
list 或 l | 查看当前代码(默认显示 10 行)。 |
list [行号/函数名] | 查看指定行号或函数的代码。 |
backtrace 或 bt | 显示函数调用堆栈。 |
frame [编号] | 切换到指定堆栈帧。 |
info frame | 查看当前堆栈帧的信息。 |
以下是示例代码的调试界面(仅截部分作为参考):
高级功能
条件断点
为断点设置触发条件,只有条件满足时程序才会暂停:
break 42 if x > 10# 示例:在第 42 行设置断点,当变量 x > 10 时触发。
观察点(Watchpoint)
观察某个变量的值是否发生变化:
watch x
- 程序运行时,如果 x 的值发生改变,程序会暂停。
打印调用栈
当程序崩溃时(如发生 Segmentation Fault
),可以使用以下命令打印调用栈,定位问题代码:
bt
调试核心转储文件
如果程序崩溃并生成了核心转储文件(core dump
),可以使用 gdb
查看崩溃时的状态:
- 启用
core dump
:
ulimit -c unlimited
- 调试
core
文件:
gdb ./my_program core
- 使用
bt
命令查看崩溃时的调用栈。
远程调试(gdbserver)
远程调试用于嵌入式开发,调试运行在目标设备上的程序。
步骤:
- 目标设备 上安装并启动
gdbserver
:
gdbserver :1234 ./my_program
- 目标设备上的程序将在
1234 端口
等待调试器连接。
- 主机 上启动
gdb
并连接到目标设备:
gdb ./my_program
target remote <目标设备IP>:1234
- 调试流程与普通
gdb
调试相同。
常见调试场景
1. 程序崩溃的调试
触发崩溃后,使用 bt
查看调用栈,定位崩溃的代码位置。
检查变量值是否异常:
p x
2. 内存越界问题
使用 watch
命令观察数组边界或指针的值:
watch array[index]
3. 性能问题
设置断点查看某个函数是否被频繁调用:
break function_name
- 使用
info locals
和x
命令检查不必要的内存分配或操作。
gdb 调试技巧
1. 使用 .gdbinit 文件
在项目目录下创建 .gdbinit
文件,可以预先配置常用命令。例如:
set pagination off
set print pretty on
2. 命令别名
为常用命令创建别名。例如:
define bmainbreak main
end
- 输入
bmain
时会自动设置断点到main()
。
3. 保存调试会话
将调试命令保存到脚本文件中,以便快速重现调试流程:
gdb -x commands.gdb
其中 commands.gdb
文件内容示例:
break main
run
bt
嵌入式开发中的 gdb 应用
在嵌入式开发中,gdb
通常用于调试运行在目标硬件上的程序,以下是常见场景:
- 调试裸机程序:
- 使用硬件调试器(如 JTAG)连接目标设备,通过
gdb
检查寄存器和内存状态。
- 使用硬件调试器(如 JTAG)连接目标设备,通过
- 调试 RTOS 应用:
- 设置断点到任务切换点,观察任务调度是否正常。
- 调试内核模块:
- 使用
kgdb
调试 Linux 内核模块。
- 使用
综上。gdb 是非常强大的调试工具,通过熟练掌握其命令和技巧,可以快速定位和解决程序中的问题。无论是调试普通 C/C++ 程序还是嵌入式设备中的驱动程序,gdb 都是不可或缺的工具。建议从常用命令入手,逐步学习高级功能,并结合实际项目练习,提升调试效率。
以上。仅供学习与分享交流,请勿用于商业用途!转载需提前说明。
我是一个十分热爱技术的程序员,希望这篇文章能够对您有帮助,也希望认识更多热爱程序开发的小伙伴。
感谢!