GDB(GNU Debugger)是 Linux/Unix 环境下最强大的调试工具之一,支持 本地调试 和 远程调试(如嵌入式设备、网络服务)。以下是 GDB 调试的完整指南,涵盖基础命令、调试场景和常见问题解决。
一、GDB 基础调试流程
1. 编译时生成调试信息
使用 -g 选项编译程序,保留符号表和调试信息:
2. 启动 GDB
gdb ./program # 调试本地程序 |
gdb -q ./program # 静默模式(不显示版本信息) |
3. 常用基础命令
| 命令 | 作用 | 示例 |
|---|
break <行号/函数> | 设置断点 | break 10 或 break main |
run [args] | 启动程序(可带参数) | run --input=test.txt |
next / n | 单步执行(不进入函数) | n |
step / s | 单步执行(进入函数) | s |
continue / c | 继续运行到下一个断点 | c |
print <变量> | 打印变量值 | print x |
backtrace / bt | 查看函数调用栈 | bt |
quit / q | 退出 GDB | q |
二、高级调试技巧
1. 条件断点
在特定条件下触发断点:
break 10 if x == 5 # 当 x=5 时在第 10 行暂停 |
2. 观察点(Watchpoint)
监控变量变化时暂停:
watch x # 当 x 的值被修改时暂停 |
rwatch x # 当 x 被读取时暂停 |
3. 调用栈操作
- 查看调用栈:
bt # 显示完整调用栈 |
frame <编号> # 切换到指定栈帧(如 `frame 1`) |
- 返回上一帧:
bash
4. 修改运行时的值
直接修改内存或变量值:
set var x = 10 # 修改变量 x 的值为 10 |
set {int}0x12345678 = 5 # 修改内存地址 0x12345678 的值为 5 |
5. 跳过代码执行
- 跳转到指定行:
jump 20 # 直接跳转到第 20 行(可能引发未定义行为) |
- 强制函数返回:
finish # 执行完当前函数并返回 |
return <值> # 提前返回并设置返回值(如 `return 0`) |
三、GDB 远程调试
1. 网络调试(TCP/IP)
步骤 1:启动 gdbserver
在远程主机上运行:
gdbserver :2345 ./program # 监听所有网络接口的 2345 端口 |
# 或仅本地监听 |
gdbserver localhost:2345 ./program |
步骤 2:本地 GDB 连接
gdb ./program |
(gdb) target remote 192.168.1.100:2345 # 连接远程主机 |
常见问题
- Connection refused:检查
gdbserver 是否运行,防火墙是否放行端口。 - 路径映射错误:在 CLion 或 GDB 中设置本地/远程路径映射:
(gdb) set sysroot /remote/path # 指定远程系统根目录 |
2. 串口调试(嵌入式设备)
步骤 1:连接串口
确认设备串口路径(如 /dev/ttyUSB0 或 COM3)。
步骤 2:启动 GDB 串口调试
gdb ./program |
(gdb) target remote /dev/ttyUSB0 # Linux/macOS |
(gdb) target remote COM3 # Windows |
(gdb) set remotebaud 115200 # 设置波特率(必须匹配设备) |
常见问题
- 无权限访问串口:
sudo chmod 666 /dev/ttyUSB0 # Linux 临时授权 |
sudo usermod -aG dialout $USER # 永久授权 |
- 波特率不匹配:确保设备端和 GDB 的波特率一致(如
115200)。
四、GDB 脚本与自动化
1. 录制调试会话
(gdb) record # 开始录制 |
(gdb) reverse-next # 反向单步执行(需硬件支持) |
(gdb) record stop # 停止录制 |
2. 使用 GDB 脚本
将命令保存到文件(如 debug.gdb):
break main |
run |
next 3 |
print x |
quit |
执行脚本:
gdb -x debug.gdb ./program |
3. TUI 模式(文本用户界面)
启用分屏显示源代码和汇编:
gdb -tui ./program |
# 或动态切换 |
(gdb) layout src # 显示源代码 |
(gdb) layout asm # 显示汇编 |
五、常见问题解决
Q1: 报错 No symbols loaded
- 原因:程序未编译带调试信息(
-g 选项缺失)。 - 解决:重新编译并添加
-g:
Q2: 断点不生效
- 原因:代码优化导致断点位置无效。
- 解决:编译时关闭优化:
gcc -g -O0 main.c -o program # -O0 禁用优化 |
Q3: 调试多线程程序
- 查看所有线程:
- 切换线程:
(gdb) thread <ID> # 如 `thread 2` |
Q4: 调试核心转储文件(Core Dump)
- 生成核心转储文件:
ulimit -c unlimited # 允许生成核心文件 |
./program # 触发崩溃 |
- 用 GDB 分析:
gdb ./program core |
(gdb) bt # 查看崩溃时的调用栈 |
六、总结
| 场景 | 关键命令/操作 |
|---|
| 本地调试 | gdb ./program + break/run/print |
| 网络远程调试 | gdbserver + target remote <IP:port> |
| 串口调试 | target remote /dev/ttyUSB0 + set remotebaud |
| 多线程调试 | info threads + thread <ID> |
| 核心转储分析 | gdb ./program core + bt |
掌握这些技巧后,你可以高效调试 C/C++ 程序、嵌入式设备或网络服务。遇到复杂问题时,结合 bt、info registers 和 x/10xw <地址>(查看内存)等命令进一步分析。