gdb 是一个强大的调试器,让我详细介绍如何使用它调试你的程序:

基本调试步骤

1. 编译时启用调试信息

# 编译时添加 -g 参数
gcc -g -o exec source.c
# 或者
g++ -g -o exec source.cpp
 
# 如果使用 CMake,确保是 Debug 模式
cmake -DCMAKE_BUILD_TYPE=Debug -S . -B build
make -C build

2. 启动 gdb 调试你的程序

# 方法1:启动 gdb 后设置参数
gdb ./exec
(gdb) set args -p p1 -p p2
(gdb) run
 
# 方法2:直接带参数启动
gdb --args ./exec -p p1 -p p2
(gdb) run
 
# 方法3:使用 run 命令时直接传参数
gdb ./exec
(gdb) run -p p1 -p p2

常用 gdb 命令

括号内为命令缩写,日常调试中通常使用缩写形式。

基本控制命令

(gdb) run                    # 运行程序 (r)
(gdb) continue              # 继续执行 (c)
(gdb) step                  # 单步执行(进入函数) (s)
(gdb) next                  # 单步执行(不进入函数) (n)
(gdb) finish                # 执行到当前函数结束
(gdb) quit                  # 退出 gdb (q)

断点相关

(gdb) break main            # 在 main 函数设置断点 (b main)
(gdb) break 25              # 在第 25 行设置断点
(gdb) break file.c:100      # 在 file.c 的第 100 行设置断点
(gdb) break func_name       # 在函数 func_name 设置断点
(gdb) info breakpoints      # 查看所有断点 (info b)
(gdb) delete 1              # 删除断点 1 (d 1)
(gdb) disable 1             # 禁用断点 1
(gdb) enable 1              # 启用断点 1
(gdb) clear                 # 清除当前行的断点

查看变量和内存

(gdb) print variable_name   # 打印变量值 (p variable_name)
(gdb) print *pointer        # 打印指针指向的值
(gdb) print array[0]        # 打印数组元素
(gdb) display variable      # 每次停止时都显示变量值
(gdb) info locals           # 显示当前函数的局部变量
(gdb) info args             # 显示当前函数的参数
(gdb) backtrace             # 显示调用栈 (bt)
(gdb) frame 0               # 切换到栈帧 0 (f 0)
(gdb) list                  # 显示当前源代码 (l)
(gdb) list 20               # 显示第 20 行附近的代码

实际调试示例

假设你要调试 ./exec -p p1 -p p2

# 启动调试
gdb --args ./exec -p p1 -p p2
 
# 在 gdb 中执行
(gdb) break main                    # 在 main 函数设断点
(gdb) run                          # 开始运行
 
# 程序停在 main 函数
(gdb) list                         # 查看源代码
(gdb) print argc                   # 查看参数个数
(gdb) print argv[0]                # 查看程序名
(gdb) print argv[1]                # 查看第一个参数 "-p"
(gdb) print argv[2]                # 查看第二个参数 "p1"
 
# 设置更多断点并继续
(gdb) break process_args           # 假设有参数处理函数
(gdb) continue                     # 继续执行到下个断点
 
# 单步调试
(gdb) step                         # 单步执行
(gdb) next                         # 下一行
(gdb) print some_variable          # 查看变量值

高级用法

条件断点

在满足特定条件时才触发断点,可大幅减少不必要的中断。

(gdb) break 50 if x == 10          # 只有当 x==10 时才停下
(gdb) condition 1 y > 100          # 为断点 1 添加条件

监视点

自动监控变量的读写操作,变量值发生变化时程序自动中断。

(gdb) watch variable_name          # 当变量值改变时停下
(gdb) rwatch variable_name         # 当变量被读取时停下
(gdb) awatch variable_name         # 当变量被读取或写入时停下

查看内存

使用 x 命令直接检查内存内容,支持十六进制、指令和字符串等多种显示格式。

(gdb) x/10i $pc                    # 查看当前指令
(gdb) x/10x address                # 以十六进制查看内存
(gdb) x/s string_pointer           # 查看字符串

多线程调试

(gdb) info threads                 # 查看所有线程
(gdb) thread 2                     # 切换到线程 2
(gdb) break function thread 2      # 在特定线程设断点

调试技巧

1. 处理段错误

gdb ./exec
(gdb) run -p p1 -p p2
# 程序崩溃后
(gdb) backtrace                    # 查看崩溃位置
(gdb) frame 1                      # 切换栈帧查看详细信息
(gdb) print *pointer               # 查看可能的空指针

2. 调试已运行的程序

# 获取进程 ID
ps aux | grep exec
 
# 附加到运行中的进程
gdb -p <process_id>

3. 批量命令

创建 .gdbinit 文件或使用 -x 参数:

# 创建命令文件 debug.gdb
echo "break main" > debug.gdb
echo "run -p p1 -p p2" >> debug.gdb
echo "continue" >> debug.gdb
 
# 使用命令文件
gdb -x debug.gdb ./exec

常见问题解决

没有调试信息

(gdb) info sources                 # 检查是否有源代码信息
# 如果没有,重新编译时加上 -g 参数

优化影响调试

# 编译时禁用优化
gcc -g -O0 -o exec source.c

TUI 模式(文本用户界面)

gdb -tui ./exec                    # 启动时开启 TUI
# 或在 gdb 中
(gdb) tui enable                   # 启用 TUI
(gdb) layout src                   # 显示源代码窗口
(gdb) layout asm                   # 显示汇编窗口