GDB使用指北:速查指南

本笔记基于Marc Haisenko (marc@darkdust.net)于2007年制作的GDB速查表整理而成。

同时包含了在学习课程cs300时的一些体悟,也许可以算一篇学习笔记?

本来应该在一年前写完的,没想到拖着拖着拖到现在了。果然博客只有在刚刚建完的时候才有最大的热情去写。

调试能力是编程过程中不可或缺的技能。固然,如今的许多IDE已经将调试简单化了,但是在某些场景,特别是命令行环境下,传统的调试工具仍然有其使用价值。GDB(GNU Debugger)便是如此。

为什么需要GDB?

在开发C/C++程序时,我们经常会遇到程序崩溃、逻辑错误等问题。虽然printf大法好用,但在复杂程序中,它显得力不从心。GDB作为GNU项目下的标准调试器,能让我们:

  • 在程序崩溃时查看状态
  • 设置断点精确控制执行流程
  • 检查变量和内存内容
  • 跟踪函数调用栈
  • 甚至修改程序运行时状态

是的,这些功能现代的IDE都能够做到。但在CLI环境下,GDB无疑是更优雅的选择。谁没有过All In Terminal的梦想?与此同时,更加直接和传统的使用方式也让它有利于促进语言的学习。C/C++的特性在GDB下得以很好的展现。这或许也是众多著名cs公开课青睐它的原因之一。

启动

1
2
3
gdb <program> [core dump]       # 带可选core dump启动GDB
gdb --args <program> <args...> # 传递参数启动GDB
gdb --pid <pid> # 附加到正在运行的进程

运行控制

1
2
3
set args <args...>  # 设置程序参数
run # 运行程序
kill # 终止运行中的程序

断点管理

1
2
3
4
5
break <where>          # 设置断点
delete <breakpoint#> # 删除断点
clear # 删除所有断点
enable <breakpoint#> # 启用断点
disable <breakpoint#> # 禁用断点

断点位置

  • function_name - 函数名
  • line_number - 当前源文件行号
  • file:line_number - 指定文件的行号

观察点

1
2
watch <where>                     # 设置观察点
delete/enable/disable <watchpoint#> # 管理观察点

条件断点

1
2
break/watch <where> if <condition>  # 条件触发
condition <breakpoint#> <condition> # 修改现有条件

堆栈检查

1
2
3
backtrace        # 显示调用栈 (或 where)
backtrace full # 显示调用栈及局部变量
frame <frame#> # 选择操作的栈帧

单步执行

1
2
3
4
step     # 执行下一行(进入函数)
next # 执行下一行(不进入函数)
finish # 继续执行直到当前函数返回
continue # 继续正常执行

变量与内存

1
2
3
4
print/format <what>     # 打印变量/内存
display/format <what> # 每次单步后自动打印
undisplay <display#> # 移除display
x/nfu <address> # 打印内存

内存打印格式

  • n: 打印单元数(默认1)
  • f: 格式字符
  • u: 单元大小(b=字节, h=半字, w=字, g=巨字)

格式字符

  • a: 指针
  • c: 字符
  • d: 有符号十进制
  • f: 浮点数
  • o: 八进制
  • s: C字符串
  • t: 二进制
  • u: 无符号十进制
  • x: 十六进制

变量表示

  • expression: 任意C表达式
  • file_name::variable_name: 静态变量
  • function::variable_name: 函数内变量
  • {type}address: 指定类型的内存地址
  • $register: 寄存器内容($esp, $ebp, $eip)

线程操作

1
thread <thread#>  # 选择操作的线程

程序操作

1
2
set var <variable_name>=<value>  # 修改变量值
return <expression> # 强制函数立即返回

源代码管理

1
2
3
4
5
directory <directory>        # 添加源文件搜索目录
list # 显示源代码
list <filename>:<function> # 显示特定函数
list <filename>:<line> # 显示特定行
set listsize <count> # 设置list显示行数

信号处理

1
2
handle <signal> <options>  # 信号处理设置
# options: (no)print, (no)stop, (no)pass

信息查询

1
2
3
4
5
6
7
8
9
10
11
disassemble [<where>]      # 反汇编
info args # 当前函数参数
info breakpoints # 断点信息
info display # display信息
info locals # 局部变量
info sharedlibrary # 已加载共享库
info signals # 信号处理信息
info threads # 线程列表
show directories # 源文件搜索目录
show listsize # list显示行数
whatis <variable_name> # 变量类型

技巧

  1. Core dump定位gdb program core 精确定位崩溃点
  2. 动态调试:运行中附加进程 gdb --pid $(pgrep -n myapp)
  3. 内存快照x/16xb &myvar 查看变量内存布局
  4. 绕过函数finish + return 0 快速跳过有问题的函数
  5. 自动显示display/x $eax 每次停顿时自动显示寄存器
  6. 其他技巧
    • 使用display自动监控关键变量
    • 条件断点break if避免频繁中断
    • x/10xw $esp快速查看栈内容
    • finish快速跳出当前函数
    • return修改函数返回值进行测试

命令速查表

类别 命令 用途
执行 run 启动程序
断点 b main 在main函数设置断点
检查 p var 打印变量值
内存 x/4xw $esp 查看栈顶4字
bt 显示调用栈
线程 thread 2 切换到线程2
修改 set var i=10 修改变量值