gdb 命令 #
gdb
(GNU 调试器)是一个功能强大的调试工具,用于调试 C、C++和其他编程语言编写的程序。它允许开发者检查程序在运行时的内部状态,设置断点,查看变量值,以及逐步执行代码。
语法 #
gdb [选项] [程序 [核心文件/进程ID]]
常用选项 #
选项 | 描述 |
---|---|
-h, --help |
显示帮助信息 |
-v, --version |
显示版本信息 |
-q, --quiet |
不显示介绍和版权信息 |
-s 文件 |
从指定文件读取符号表 |
-e 程序 |
指定要执行的程序 |
-c 文件 |
指定核心转储文件 |
-p PID |
附加到指定进程 ID |
-x 文件 |
从文件执行 GDB 命令 |
-d 目录 |
添加源文件搜索路径 |
-n, --nx |
不执行初始化文件(.gdbinit) |
-batch |
批处理模式,执行完命令后退出 |
-cd 目录 |
将工作目录更改为指定目录 |
-f, --fullname |
输出完整文件名和行号 |
-b 波特率 |
设置串行线路的波特率 |
-tty 设备 |
使用设备作为程序的标准输入和输出 |
基本 GDB 命令 #
1. 启动和退出 #
命令 | 描述 |
---|---|
gdb 程序 |
启动 GDB 并加载程序 |
gdb -c core 程序 |
使用核心转储文件启动 GDB |
gdb --pid=PID |
附加到正在运行的进程 |
quit 或 q |
退出 GDB |
kill |
终止正在调试的程序 |
2. 运行和停止 #
命令 | 描述 |
---|---|
run 或 r [参数] |
开始执行程序,可选传递命令行参数 |
start |
运行程序,在 main 函数处停止 |
continue 或 c |
继续执行程序 |
step 或 s |
单步执行,进入函数调用 |
next 或 n |
单步执行,不进入函数调用 |
finish |
执行到当前函数返回 |
until 或 u [位置] |
执行到指定位置 |
advance [位置] |
继续执行到指定位置 |
attach 进程ID |
附加到正在运行的进程 |
detach |
从当前附加的进程分离 |
3. 断点和观察点 #
命令 | 描述 |
---|---|
break 或 b [位置] |
在指定位置设置断点 |
tbreak [位置] |
设置临时断点(触发一次后删除) |
watch 表达式 |
设置观察点,当表达式的值改变时停止 |
rwatch 表达式 |
设置读观察点,当表达式被读取时停止 |
awatch 表达式 |
设置访问观察点,当表达式被读取或写入时停止 |
catch 事件 |
在指定事件发生时停止(如异常) |
info breakpoints |
列出所有断点和观察点 |
delete 或 d [编号] |
删除指定编号的断点或观察点 |
disable [编号] |
禁用指定编号的断点或观察点 |
enable [编号] |
启用指定编号的断点或观察点 |
clear [位置] |
删除指定位置的断点 |
condition 编号 表达式 |
为断点添加条件 |
ignore 编号 次数 |
忽略断点指定次数 |
4. 检查程序状态 #
命令 | 描述 |
---|---|
backtrace 或 bt |
显示所有栈帧 |
frame 或 f [编号] |
选择栈帧 |
info frame |
显示当前栈帧的详细信息 |
up |
选择上一层栈帧 |
down |
选择下一层栈帧 |
info locals |
显示当前栈帧的局部变量 |
info args |
显示当前函数的参数 |
info registers |
显示寄存器的值 |
disassemble |
反汇编当前函数 |
list 或 l [位置] |
显示源代码 |
print 或 p 表达式 |
显示表达式的值 |
display 表达式 |
每次停止时显示表达式的值 |
undisplay [编号] |
取消自动显示 |
whatis 表达式 |
显示表达式的类型 |
ptype 表达式 |
显示表达式的详细类型信息 |
info threads |
显示所有线程 |
thread 编号 |
切换到指定线程 |
5. 修改程序状态 #
命令 | 描述 |
---|---|
set variable 变量=值 |
修改变量的值 |
set 表达式=值 |
修改表达式的值 |
return [表达式] |
强制从当前函数返回 |
jump 位置 |
跳转到指定位置执行 |
signal 信号 |
向程序发送信号 |
call 函数(参数) |
调用函数 |
6. 其他命令 #
命令 | 描述 |
---|---|
shell 命令 |
执行 shell 命令 |
source 文件 |
从文件执行 GDB 命令 |
define 命令名 |
定义新的 GDB 命令 |
document 命令名 |
为自定义命令添加文档 |
set 参数 值 |
设置 GDB 参数 |
show 参数 |
显示 GDB 参数的值 |
help [命令] |
显示帮助信息 |
apropos 关键字 |
搜索相关命令 |
save breakpoints 文件 |
保存断点到文件 |
save history 文件 |
保存命令历史到文件 |
位置指定方式 #
在 GDB 中,可以通过多种方式指定位置:
- 函数名:
break main
- 行号:
break 42
- 文件:行号:
break file.c:42
- 文件:函数:
break file.c:main
- +偏移:
break +1
(当前行之后的第一行) - -偏移:
break -1
(当前行之前的第一行) - 地址:
break *0x12345678
- 偏移量:
break *main+20
高级用法 #
1. 条件断点 #
break 位置 if 条件
例如:
break 42 if i == 100
2. 命令列表 #
可以为断点指定一系列命令,当断点触发时自动执行:
break 位置
commands
命令1
命令2
...
end
例如:
break 42
commands
print i
print j
continue
end
3. 捕获异常 #
catch throw # 捕获所有C++异常
catch catch # 捕获C++异常处理
catch assert # 捕获失败的断言
catch syscall # 捕获系统调用
4. 打印设置 #
set print array on # 漂亮地打印数组
set print pretty on # 漂亮地打印结构
set print object on # 显示对象的派生类型
set print static-members off # 不显示静态成员
set print elements 100 # 限制显示的数组元素数量
5. 自定义 GDB 命令 #
define 命令名
命令1
命令2
...
end
例如:
define plist
set $n = $arg0->next
while $n != 0
print *$n
set $n = $n->next
end
end
6. 调试多线程程序 #
info threads # 显示所有线程
thread 编号 # 切换到指定线程
set scheduler-locking on # 只运行当前线程
set scheduler-locking off # 允许所有线程运行
set scheduler-locking step # 在单步执行时只运行当前线程
thread apply all 命令 # 对所有线程应用命令
7. 调试共享库 #
info sharedlibrary # 显示已加载的共享库
set auto-solib-add on # 自动加载共享库符号
sharedlibrary 正则表达式 # 加载匹配的共享库符号
8. 调试核心转储 #
gdb 程序 core # 使用核心转储文件启动GDB
generate-core-file # 为当前程序生成核心转储
9. 远程调试 #
target remote 主机:端口 # 连接到远程GDB服务器
10. 反向调试 #
record # 开始记录执行历史
record stop # 停止记录
reverse-continue # 反向继续执行
reverse-step # 反向单步执行
reverse-next # 反向单步执行(不进入函数)
reverse-finish # 反向执行到函数调用点
实用示例 #
1. 基本调试会话 #
$ gdb ./myprogram
(gdb) break main
(gdb) run
(gdb) next
(gdb) print variable
(gdb) continue
(gdb) quit
2. 调试段错误 #
$ gdb ./myprogram
(gdb) run
Program received signal SIGSEGV, Segmentation fault.
(gdb) backtrace
(gdb) frame 2
(gdb) list
(gdb) print *ptr
3. 条件断点 #
(gdb) break 42 if i == 100
(gdb) run
4. 观察变量 #
(gdb) watch global_var
(gdb) run
Hardware watchpoint 1: global_var
Old value = 0
New value = 42
5. 检查数据结构 #
(gdb) print *node
(gdb) print node->next->data
(gdb) ptype struct Node
6. 调试多线程程序 #
(gdb) info threads
(gdb) thread 2
(gdb) bt
(gdb) thread apply all bt
7. 使用断点命令 #
(gdb) break 42
(gdb) commands
> silent
> print i
> print j
> continue
> end
(gdb) run
8. 调试内存问题 #
(gdb) set environment MALLOC_CHECK_=2
(gdb) run
9. 保存和恢复断点 #
(gdb) save breakpoints breakpoints.txt
(gdb) source breakpoints.txt
10. 使用 GDB 脚本 #
创建script.gdb
文件:
set pagination off
break main
run
next 5
print variable
continue
quit
然后运行:
$ gdb -x script.gdb ./myprogram
调试 C++特性 #
1. 设置断点在重载函数 #
(gdb) break 'MyClass::method(int)'
(gdb) break 'MyClass::method(std::string)'
2. 查看 STL 容器 #
(gdb) print myvector
(gdb) print myvector._M_impl._M_start[0]
(gdb) print mymap['key']
3. 调试模板 #
(gdb) break 'MyTemplate<int>::method'
4. 查看虚函数表 #
(gdb) p *((void ***)obj)[0]@10
调试内存问题 #
1. 检查内存泄漏 #
GDB 本身不是内存泄漏检测工具,但可以与 Valgrind 等工具结合使用:
$ valgrind --leak-check=full --vgdb=yes --vgdb-error=0 ./myprogram
$ gdb ./myprogram
(gdb) target remote | /usr/lib/valgrind/../../bin/vgdb
2. 检查野指针 #
(gdb) watch *pointer
(gdb) run
3. 检查缓冲区溢出 #
(gdb) break function
(gdb) run
(gdb) print buffer
(gdb) x/20x buffer
常见问题排查 #
1. 没有调试信息 #
错误:
No debugging symbols found in ./myprogram
解决方案:
# 使用-g编译程序
gcc -g -o myprogram myprogram.c
2. 源代码找不到 #
错误:
No such file or directory.
解决方案:
(gdb) directory /path/to/source
3. 优化代码调试困难 #
问题:变量值不正确或行号混乱
解决方案:
# 编译时禁用优化
gcc -g -O0 -o myprogram myprogram.c
4. 无法设置断点 #
错误:
Function "function" not defined.
解决方案:
# 确保函数名正确,对于C++可能需要使用完整的名称
(gdb) break Namespace::Class::function
5. 调试共享库 #
问题:无法看到共享库中的符号
解决方案:
(gdb) set auto-solib-add on
(gdb) sharedlibrary
GDB 图形界面 #
1. GDB TUI 模式 #
GDB 内置的文本用户界面:
(gdb) tui enable
或启动时使用:
gdb -tui ./myprogram
TUI 常用命令:
(gdb) layout src # 显示源代码窗口
(gdb) layout asm # 显示汇编窗口
(gdb) layout split # 同时显示源代码和汇编
(gdb) layout regs # 显示寄存器窗口
(gdb) focus cmd # 焦点切换到命令窗口
(gdb) focus src # 焦点切换到源代码窗口
(gdb) refresh # 刷新显示
2. 外部前端 #
- DDD:数据显示调试器,提供数据结构可视化
- Eclipse:通过 CDT 插件提供 GDB 集成
- Visual Studio Code:通过扩展提供 GDB 支持
- Emacs:通过 GUD 模式集成 GDB
- gdbgui:基于浏览器的 GDB 前端
GDB 配置文件 #
GDB 启动时会读取以下配置文件:
/etc/gdb/gdbinit
:系统范围的初始化文件~/.gdbinit
:用户主目录中的初始化文件./.gdbinit
:当前目录中的初始化文件
常用的.gdbinit
配置:
# 启用漂亮打印
set print pretty on
set print array on
set print array-indexes on
# 禁用分页
set pagination off
# 保存历史命令
set history save on
set history filename ~/.gdb_history
set history size 10000
# 自动反汇编
define hook-stop
disassemble $pc-8, $pc+16
end
# 自定义命令
define phead
print *(void **)($arg0)
end
与其他调试工具的比较 #
工具 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
GDB | 功能强大、灵活、支持多种语言 | 命令行界面学习曲线陡峭 | C/C++程序、系统级调试 |
LLDB | 现代设计、C++API、与 Clang 集成 | 某些平台支持不如 GDB | macOS/iOS 开发、使用 LLVM 工具链 |
Visual Studio 调试器 | 图形界面、易用性高 | 仅限 Windows 平台 | Windows 应用程序开发 |
Valgrind | 内存错误检测、性能分析 | 运行速度慢 | 内存泄漏和访问错误检测 |
strace/ltrace | 轻量级、跟踪系统调用和库调用 | 功能有限 | 快速诊断系统交互问题 |
提示和技巧 #
1. 使用简写命令 #
大多数 GDB 命令都有简写形式:
b
代替break
c
代替continue
n
代替next
s
代替step
p
代替print
bt
代替backtrace
2. 使用命令历史 #
- 上下箭头键浏览历史命令
Ctrl+R
搜索历史命令
3. 使用 Python 扩展 #
GDB 支持 Python 脚本,可以扩展功能:
# 在.gdbinit中
python
def print_list(val):
result = []
node = val
while node != 0:
result.append(node['data'])
node = node['next']
return result
class LinkedListPrinter:
def __init__(self, val):
self.val = val
def to_string(self):
return "LinkedList with elements: " + str(print_list(self.val))
def lookup_type(val):
if str(val.type) == 'LinkedList *':
return LinkedListPrinter(val)
return None
gdb.pretty_printers.append(lookup_type)
end
4. 使用断点条件和命令 #
(gdb) break file.c:123 if i > 100
(gdb) commands
> silent
> print i
> print j
> continue
> end
5. 使用宏 #
(gdb) macro expand MACRO(arg)
6. 使用反向调试 #
(gdb) record
(gdb) continue
# 程序崩溃
(gdb) reverse-stepi
(gdb) reverse-next
7. 保存调试会话 #
(gdb) save breakpoints bp.txt
(gdb) save history history.txt
8. 使用 GDB 仪表板 #
安装 GDB 仪表板(一个增强的 GDB 界面):
wget -P ~ https://git.io/.gdbinit
9. 调试优化代码 #
(gdb) set debug-file-directory /path/to/debug/files
(gdb) set substitute-path /build/path /local/path
10. 使用 GDB 脚本自动化调试 #
gdb -batch -ex "run" -ex "bt" -ex "quit" ./myprogram