GDB 高阶进阶用法
以下高阶用法如果有多个用法表示同一个意思,只保留缩写或者最简单的那个。
以下笔记是在阅读过 Debugging with GDB 20250527 版本后做的笔记,学习完毕可以声称精通 GDB。
启动与启动文件
启动文件可以位于以下地点:
$XDG_CONFIG_HOME/gdb/gdbinit
$HOME/.config/gdb/gdbinit
$HOME/.gdbinit
命令 |
解释 |
gdb -q |
静默启动,不打印版本等信息 |
gdb -tui |
GDB启动时即打开可视化窗口 |
gdb -statistics |
启动和运行每条命令时打印时间和内存信息 |
内部命令
命令 |
解释 |
示例 |
shell <command> |
在 gdb 内运行任意外界命令 |
|
set logging on |
打开日志记录,默认文件为 gdb.txt |
|
set logging file <file> |
设置日志文件名 |
|
pipe <gdb-command> | <linux-command> |
将 GDB 的输出传入 linux 命令, 例如 grep |
|
file <file_name> |
切换加载的文件 |
|
help <class> |
查看某些命令的帮助 |
help status |
i r |
打印寄存器信息 |
|
i b |
打印断点信息 |
|
i args |
打印当前参数信息 |
|
start |
自动运行并停在程序入口点 (main) |
|
starti |
自动运行并停在汇编入口点 (_start) |
|
set args <args> |
设置当前 GDB 窗口的运行参数 |
|
path <directory> |
设置可执行文件运行时搜索动态库的路径 |
|
ch resta <checkpoint-id> |
设置检查点 从某检查点直接恢复 (回去调试) |
|
i ch |
显示当前所有检查点 |
|
多进程调试
attach <process_id> |
附加进程并调试进程的程序 |
|
infr <N> |
切换到第 N 个 inferior 进程接着调试 |
|
add-i file <other-program> <other-args> |
创建一个新的进程以多程序调试 |
|
clone-i <N> |
在当前程序基础上 clone 进程以多路径调试 |
|
remove-i <N> |
删除某进程 |
|
b <loc> inferior <inferior-id> |
将断点打在某进程的对应位置(也可以加条件 if) |
|
i i |
查看所有“被调试的程序实例”(一行一个进程) |
|
多线程调试
注意,b 的断点会打在所有线程,即所有线程都会在断点处停顿
i th |
打印当前线程 |
thr <N> |
切换线程 |
taas <command> |
所有线程都执行某命令 |
tfaas |
所有线程都返回 (finish) |
thr a <threads | all> <command> |
所有线程都运行某命令 |
检查点与备份
ch resta <checkpoint-id> |
设置检查点 (新进程) 从某检查点直接恢复 (回去调试) |
|
i ch |
显示当前所有检查点 |
|
d ch <N> |
删除某检查点 |
|
断点/观察点/捕捉点
b <loc> if <cond>
|
条件断点 |
b func if a == 10 |
tb <loc> |
临时断点 (只停一次) |
|
rb <regex> rb <file:regex> |
模糊匹配设置断点 (所有匹配的都是断点) |
|
i b |
打印所有断点 |
|
wa <loc> wa <loc> th <thread-id> |
设置写观察点 |
|
rw <loc> |
设置读检查点 |
|
aw |
设置读写检查点 |
|
i wat |
显示所有检查点 |
|
cat <event> |
捕获某事件 |
catch exception Program_Error catch syscall open |
clear |
删除所有断点 |
|
d br <list> |
删除断点 |
|
disa/ena br <breakpoint-id> |
禁用或者启用断点 |
|
ena once <N> |
断点 N 在下一次命中后就自动禁用 |
|
ena count <times> <N> |
断点 N 在出发一定次数后不再触发 |
|
ena del <N> |
触发后就直接删除断点 |
|
commands <breakpoint-id> <command> end |
将命令自动化附加到某断点上,到达断点时自动执行命令 |
commands 2 if x > 100 print "x is big" end continue end |
dp <loc> <exprs> |
动态断点,运行到指定位置只执行表达式但不停留 |
dprintf main.c:42, "val=%dn", val |
sa bre <filename> |
把断点存到某文件去 |
|
b <loc> th <thread-id> |
线程特定的断点 |
|
步
fg |
等同于 continue |
|
step/next/si/ni <count> |
步进/步入/指令步入/指令步进指定次数 |
|
finish |
返回父级函数 |
|
u <line-number> |
运行直到指定行号 |
|
sk |
跳过某些内容 |
(gdb) skip function <funcname> (gdb) skip file <filename> (gdb) skip range <start-addr> <end-addr> (gdb) skip delete <N> # 删除 skip 规则 (gdb) info skip # 查看当前 skip 列表 |
反向运行
仅单线程程序支持反向运行
rs |
反向单步 |
|
rc |
反向继续执行到断点 |
|
rsi |
|
|
rn |
|
|
rni |
|
|
reverse-f |
|
|
记录进程行为
该命令存在兼容性问题,很可能不支持目标 CPU
rec full |
完全记录 GDB 的行为 |
|
rec s |
停止记录 GDB 的行为 |
|
rec g <loc> |
前往特定 Log位置 |
可以是 begin,end,N |
res sa |
存储记录的 Log 到文件 |
|
res re |
恢复本地文件到 Log |
恢复的 log 必须由 save 生成 |
i rec |
显示 recording 基本信息 |
|
栈检查
bt <N> |
只打印前几层栈 |
|
bt -full |
同时打印临时变量 |
|
f <function> f <N> f <view> |
选择想要的栈帧并查看 |
|
up <N> |
跳到当前函数调用者或其他调用者 |
|
down <N> |
跳到被调用者 |
|
i f |
打印当前栈帧完整基本信息 |
|
i loc |
打印当前栈帧变量信息 |
|
frame apply <frames> <command> |
对所有或部分栈帧进行操作 |
frame apply all info args f ap a i ar f ap 2 5 print some_var f ap all -q p $sp (quiet 静默栈帧信息)
|
检查原文件
l |
打印当前执行上下文 |
|
l <linenum> l <func>
|
指定打印上下文 |
|
disas |
反汇编当前函数 |
|
disas <address> <address> disas <address> +<size> |
指定反汇编范围 |
|
disas/m |
源码和汇编对照 |
|
检查数据
l |
打印当前执行上下文 |
|
p -pretty <something> |
优雅打印 |
有些 GDB 不支持 |
explore <vars> |
递归得查看复杂数据结构 |
属于 tui扩展功能 |
@ 操作符 |
人工数组构造器 |
x/4x arr@4 p arr@4 |
:: 作用域界定符 |
|
p myfile.cpp::some_var |
{type} addr |
将内存开始的内容当做指定 type 解读 |
p struct MyType 0xdeadbeef |
x |
检查内存(支持如下输出格式) |
|
disp <expr> |
程序每次停下 (无论单步还是断点) 都自动执行表达式 (支持如下输出格式) |
display i 每次都打印 i 的值 |
i di |
显示当前 display 信息 |
|
p *$1 |
通过历史编号快速引用之前的 print 内容 |
p *$.next 打印 ptr->next 指向的结构体 p *$ $.next 打印前一个值的 next |
set <alias>=<foo> |
建立临时别名变量 |
set $foo = *object_ptr |
i r |
查看寄存器信息 |
|
x/i $ pc |
查看当前 PC 指向的指令 |
|
i fl |
查看硬件浮点单元信息 |
|
i ve |
查看硬件向量单元信息 |
|
du memory <filename> <start-addr> <end-addr> du value <filename> <expr> du registers <filename> |
将内存或者寄存器等信息打印到文件 |
|
app 用法如上 restore 用法如上 |
从文件恢复内存或寄存器等信息 |
|
gcore |
手动保存进程快照 |
|
find[/s或/n] start_addr, +len, <vars> find[/s或/n] start_addr, end_addr, <vars>
|
查找指定内存中的内容(支持 b 字节 h 半字 w 字 g 双字) |
|
输出格式: |
|
|
格式 |
含义 |
x |
十六进制(Hexadecimal) |
d |
十进制(Decimal) |
u |
无符号十进制(Unsigned decimal) |
o |
八进制(Octal) |
t |
二进制(Binary,“t”代表“two”) |
a |
地址(以符号+偏移形式显示)如:0x54320 <_initialize_vx+396> |
c |
字符(Character constant,与整数同时显示) |
f |
浮点数格式(Floating point) |
s |
字符串(尝试将值解释为字符串) |
z |
十六进制,带前导 0(zero-padded hexadecimal) |
r |
原始(raw)格式,禁用 Python pretty-printer,显示底层结构 |
i |
解析为指令进行打印 |
find /sb 0x600000, 0x601000, 0x68, 0x65, 0x6c, 0x6c, 0x6f
find /n3 0x600000, 0x700000, 0x0 # 找出前三个 NULL 字节的位置
宏
i macro <MACRO> |
显示程序 MACRO 的信息 |
前提是 -g3 编译
追溯点
tracepoint 的用法和 breakpoint 一模一样
但是实际应该用不到,暂时略过
特性 |
breakpoint |
tracepoint |
是否中断程序 |
✅ 会中断程序 |
❌ 不中断 |
是否实时查看值 |
✅ 可以立即查看 |
❌ 稍后查看(trace buffer) |
使用场景 |
本地调试 |
远程/嵌入式/高性能调试 |
是否依赖目标支持 |
❌ GDB 自身支持 |
✅ 需要 target 支持 trace |
命令 |
功能描述 |
说明 |
|
|
trace <location> |
设置追踪点(tracepoint) |
在不停止程序的前提下记录位置数据,适用于远程或嵌入式调试 |
|
|
collect <expr> |
指定采集哪些变量或表达式 |
可收集变量、寄存器(如 $regs、$locals)、结构体字段等 |
|
|
tstart |
开始 trace 会话 |
启动采样机制,程序运行时会在 tracepoints 自动记录 |
|
|
tstop |
停止 trace 会话 |
停止采样并允许分析采集到的快照 |
|
|
tfind <n> |
查找第 n 个 trace 快照 |
若不加参数则查找下一个,可用 start、end、none 等特殊参数 |
|
|
tdump |
打印当前 trace 快照采集到的数据 |
必须先用 tfind 定位快照 |
|
|
i tracepoints |
查看所有 tracepoint |
列出编号、位置、状态等信息 |
|
|
i collected |
查看当前采样点收集了哪些内容 |
仅列出采样项的名称,不包含具体值 |
|
|
actions [num] |
为某个 tracepoint 编号指定采集行为 |
可交互式输入 collect、while-stepping 等命令,以 end 结尾 |
|
|
while-stepping <count> |
步进若干次并采集 |
在触发 tracepoint 后单步执行并继续采集 |
|
|
passcount <n> |
设置某 tracepoint 命中 n 次后停止 |
自动终止 trace 会话,适用于限制记录量 |
|
|
tvariable $name = <expr> |
创建并赋值 trace 状态变量 |
可用于内部计数或条件判断,未指定值时默认为 0 |
|
|
info tvariables |
查看所有 trace 状态变量 |
包括初始值及当前值(若 trace 正在运行) |
|
|
delete tvariable [$name ...] |
删除指定 trace 状态变量 |
不带参数将全部清除 |
|
|
target tfile <filename> |
加载文件作为 trace 数据源 |
可用 tdump 查看内容,但不能运行新的 trace 会话 |
|
|
target ctf <dirname> |
加载 CTF 格式 trace 数据目录 |
用于共享或兼容其他 tracing 工具(如 LTTng)的分析数据 |
|
|
符号表检查
命令 |
功能说明 |
示例 |
i address <sym> |
显示符号 <sym> 的地址 |
i address myVar |
i symbol <addr> |
根据地址 <addr> 查找符号 |
i symbol 0x400123 |
demangle <mangled-name> |
将重整(mangled)名称还原 |
demangle _Z3fooIiEvv |
whatis [/<fmt>] <sym> |
显示符号 <sym> 的类型 |
whatis /x ptr |
ptype <type> |
打印类型 <type> 的完整定义 |
ptype struct Person |
i types [<regex>] |
列出匹配 <regex> 的所有类型 |
i types "My.*" |
i scope <sym> |
显示符号 <sym> 的作用域信息 |
i scope foo |
i source <file> |
显示源文件 <file> 的路径 |
i source main.c |
i sources |
列出所有加载的源文件 |
i sources |
i functions [<regex>] |
列出匹配 <regex> 的函数名 |
i functions "mˆain$" |
i variables [<regex>] |
列出匹配 <regex> 的变量名 |
\texttt{i variables "\^g\_.\*"} |
i modules [<regex>] |
列出所有 Fortran 模块 |
i modules "Mˆath$" |
i main |
显示程序的入口函数名 |
i main |
i classes [<regex>] |
列出匹配 <regex> 的 Objective-C 类 |
i classes "My.*" |
maint i symtabs [<regex>] |
列出符号表文件(.o/.so)信息 |
maint info symtabs ".*.o$" |
maint i psymtabs [<regex>] |
列出已加载的进程符号表信息 |
maint info psymtabs "mˆodule" |
maint i line [<regex>] |
列出行号—地址映射表 |
maint info line-table "mˆain" |
代替执行 (重点)
本部分提供了你“改变程序行为”的能力,不再只是观察和打印!
因为不用改代码就知道验证修复是否有效!
set var <var>=<val> set {<type>}<address> = <val> |
修改变量值或者地址值 |
set {int}0x83040 = 4 |
j <loc> |
直接跳到指定位置执行, 忽略部分内容如 Bug 代码(同函数内) |
|
sig <signal> |
向程序发送一个信号并继续运行 |
sig 0 表示继续运行 sig SIGINT 表示模拟 Ctrl+C 中断 |
ret <expr> |
强制返回父级函数,并可以指定返回值 |
|
call <expr> |
①调用程序中的函数 ②修改状态,如副作用函数 ③动态计算表达式 |
call printf("x = %dn", x)
|
compi code -raw -- <src> |
单行“原始”模式编译(无自动包装作用域) |
\texttt{compile code -raw -- int \_gdb\_expr_() { return 42; }} |
compi code |
进入多行编辑模式,直至输入 end 结束 |
compile code> int y = x+1;> end |
compi file <file> |
从指定文件编译并执行 |
compile file /home/me/snippet.c |
compi file -raw <file> |
原始模式下从文件编译 |
compile file -raw /home/me/snippet.c |
compi print [opts] -- <expr> |
编译并执行 <expr> ,结果按类型自动输出 |
compile print -- sin(3.14) |
compi print /f [opts] -- <expr> |
编译并执行 <expr> ,并以浮点格式输出 |
compile print /f -- cos(0.5) |
compi print [opts] -- |
进入多行编辑模式输入表达式,直至 end |
compile print> x = x+1;> end |
compi print /f [opts] -- |
多行模式下编译并以浮点格式输出 |
compile print /f> double z = sqrt(2);> end |
文件
file <file>
|
选择输入的文件 |
|
i dll |
查看动态库 |
|
调试目标
set arch <arch> set processor <processor> |
设置架构或处理器型号 |
|
tar <target> |
设置调试目标 |
可以是 core,ctf,exec,... |
远程调试
暂时略过,无法实操
自定义命令
def <command> |
自定义命令 |
可以写在. gdbinit |
echo |
输出文本,支持 C 风格转义字符(如 \n),默认不自动换行 |
\texttt{echo Hello\ world!\n} |
output |
输出表达式值(不带换行、不加入历史记录,不显示 $n =) |
output x + 5 |
output/ |
使用指定格式输出表达式值(格式同 p/) |
output/x my_var |
printf , <exprs…> |
类似 C 语言 printf,使用模板格式化多个值输出 |
\texttt{printf “foo = 0x\%x,\ bar = 0x\%x\n”, foo, bar} |
pi |
直接进入 python 交互时命令行 |
|
py <expr> |
用法和 shell 一样,后面跟 python 内容 |
|
语法 |
功能说明 |
if |
判断表达式是否为真(非 0) |
else |
可选,表达式为假时执行 |
while |
当 为真时循环执行 |
loop_break |
跳出当前 while 循环 |
loop_continue |
跳过本轮剩余命令,进入下一轮循环 |
end |
必须出现在每个块结构的结尾 |
TUI 命令
断点标记(图形提示)
符号 |
含义 |
B |
断点已命中过 |
b |
断点未命中过 |
H |
硬件断点命中过 |
h |
硬件断点未命中过 |
+ |
断点是启用状态 |
- |
断点是禁用状态 |
按键绑定 |
|
按键 |
作用 |
Control + x a |
退出 TUI 模式 |
Control + x 1 |
TUI 使用单窗口布局 |
Control + x 2 |
TUI 使用双窗口布局 |
Control + x o |
切换活跃窗口 |
Control + x s |
单键模式 (下面讲解) |
Control + l |
刷新当前页面 |
单键模式按钮 |
|
键位 |
等价命令 |
说明 |
c |
continue |
继续程序运行 |
C |
reverse-continue |
反向继续运行 |
d |
down |
堆栈向下(进入调用者) |
f |
finish |
运行直到当前函数返回 |
F |
reverse-finish |
反向运行直到函数返回 |
n |
next |
单步执行(不进入函数) |
N |
reverse-next |
反向单步执行(不进入函数) |
o |
nexti |
单步执行一条机器指令 |
O |
reverse-nexti |
反向执行一条机器指令 |
q |
— |
退出单键模式 |
r |
run |
重新启动程序 |
s |
step |
单步执行(进入函数) |
S |
reverse-step |
反向单步进入 |
i |
stepi |
单步执行一条指令(进入) |
I |
reverse-stepi |
反向执行一条指令(进入) |
u |
up |
堆栈向上(进入被调用者) |
v |
info locals |
查看局部变量 |
w |
where |
显示当前调用栈 |
命令 |
|
|
tu en tu dis |
打开关闭 TUI |
|
i win |
当前窗口信息 |
|
la <mode> |
TUI 以某种形式展示 |
next,prev,src,asm,split,regs |
foc <mode> |
聚焦哪个子 TUI 窗口 |
|
ref |
刷新窗口 |
|
tu reg group |
切换寄存器组 |
next,prev,general,float,system,vector,all |
win <name> ±<num>
|
调整窗口大小 |
|
命令行快捷键
Control + b |
向左移动一个字符 (back) |
|
Control + f |
向右移动一个字符 (forward) |
|
Control + d |
删除光标下的字符 |
|
Control + a |
移动到行首 |
|
Control + e |
移动到行尾 |
|
Control + l |
清屏 |
|
! |
开始一次历史替换 |
|
!! |
执行上一条命令 |
|
!n |
引用编号为 n 的命令 |
|
!-n |
引用向上第 n 条命令 |
|
!string |
引用最近一条以 string 开头的命令 |
|
!?string? |
引用最近一条包含 string 的命令 |
|