GDB 高阶进阶用法

以下高阶用法如果有多个用法表示同一个意思,只保留缩写或者最简单的那个。
以下笔记是在阅读过 Debugging with GDB 20250527 版本后做的笔记,学习完毕可以声称精通 GDB。

启动与启动文件

启动文件可以位于以下地点:

$XDG_CONFIG_HOME/gdb/gdbinit 
$HOME/.config/gdb/gdbinit 
$HOME/.gdbinit # 推荐

命令 解释
gdb -q\texttt{gdb -q} 静默启动,不打印版本等信息
gdb -tui\texttt{gdb -tui} GDB启动时即打开可视化窗口
gdb -statistics\texttt{gdb -statistics} 启动和运行每条命令时打印时间和内存信息

内部命令

  • 编译时使用 -g3 可以保留 GDB宏定义的打印
命令 解释 示例
shell <command>\texttt{shell <command>} 在 gdb 内运行任意外界命令
set logging on\texttt{set logging on} 打开日志记录,默认文件为 gdb.txt\texttt{gdb.txt}
set logging file <file>\texttt{set logging file <file>} 设置日志文件名
pipe <gdb-command>\texttt{pipe <gdb-command>} | <linux-command>\texttt{<linux-command>} 将 GDB 的输出传入 linux 命令, 例如 grep
file <file_name>\texttt{file <file\_name>} 切换加载的文件
help <class>\texttt{help <class>} 查看某些命令的帮助 help status\texttt{help status}
i r\texttt{i r} 打印寄存器信息
i b\texttt{i b} 打印断点信息
i args\texttt{i args} 打印当前参数信息
start\texttt{start} 自动运行并停在程序入口点 (main)
starti\texttt{starti} 自动运行并停在汇编入口点 (_start)
set args <args>\texttt{set args <args>} 设置当前 GDB 窗口的运行参数
path <directory>\texttt{path <directory>} 设置可执行文件运行时搜索动态库的路径
ch\texttt{ch}
resta <checkpoint-id>\texttt{resta <checkpoint-id>}
设置检查点
从某检查点直接恢复 (回去调试)
i ch\texttt{i ch} 显示当前所有检查点
多进程调试
attach <process_id>\texttt{attach <process\_id>} 附加进程并调试进程的程序
infr <N>\texttt{infr <N>} 切换到第 N 个 inferior 进程接着调试
add-i\texttt{add-i}
file <other-program> <other-args>\texttt{file <other-program> <other-args>}
创建一个新的进程以多程序调试
clone-i <N>\texttt{clone-i <N>} 在当前程序基础上 clone 进程以多路径调试
remove-i <N>\texttt{remove-i <N>} 删除某进程
b <loc> inferior <inferior-id>\texttt{b <loc> inferior <inferior-id>} 将断点打在某进程的对应位置(也可以加条件 ifif)
i i\texttt{i i} 查看所有“被调试的程序实例”(一行一个进程)
多线程调试

注意,b 的断点会打在所有线程,即所有线程都会在断点处停顿

i th\texttt{i th} 打印当前线程
thr <N>\texttt{thr <N>} 切换线程
taas <command>\texttt{taas <command>} 所有线程都执行某命令
tfaas\texttt{tfaas} 所有线程都返回 (finish)
thr a <threads\texttt{thr a <threads} | all> <command>\texttt{all> <command>} 所有线程都运行某命令
检查点与备份
ch\texttt{ch}
resta <checkpoint-id>\texttt{resta <checkpoint-id>}
设置检查点 (新进程)
从某检查点直接恢复 (回去调试)
i ch\texttt{i ch} 显示当前所有检查点
d ch <N>\texttt{d ch <N>} 删除某检查点
断点/观察点/捕捉点
b <loc> if <cond>\texttt{b <loc> if <cond>}
条件断点 b func if a == 10\texttt{b func if a == 10}
tb <loc>\texttt{tb <loc>} 临时断点 (只停一次)
rb <regex>\texttt{rb <regex>}
rb <file:regex>\texttt{rb <file:regex>}
模糊匹配设置断点 (所有匹配的都是断点)
i b\texttt{i b} 打印所有断点
wa <loc>\texttt{wa <loc>}
wa <loc> th <thread-id>\texttt{wa <loc> th <thread-id>}
设置写观察点
rw <loc>\texttt{rw <loc>} 设置读检查点
aw\texttt{aw} 设置读写检查点
i wat\texttt{i wat} 显示所有检查点
cat <event>\texttt{cat <event>} 捕获某事件 catch exception Program_Error\texttt{catch exception Program\_Error}
catch syscall open\texttt{catch syscall open}
clear\texttt{clear} 删除所有断点
d br <list>\texttt{d br <list>} 删除断点
disa/ena br <breakpoint-id>\texttt{disa/ena br <breakpoint-id>} 禁用或者启用断点
ena once <N>\texttt{ena once <N>} 断点 N 在下一次命中后就自动禁用
ena count <times> <N>\texttt{ena count <times> <N>} 断点 N 在出发一定次数后不再触发
ena del <N>\texttt{ena del <N>} 触发后就直接删除断点
commands <breakpoint-id>\texttt{commands <breakpoint-id>}
<command>\texttt{<command>}
end\texttt{end}
将命令自动化附加到某断点上,到达断点时自动执行命令 commands 2\texttt{commands 2}
if x > 100\texttt{if x > 100}
print "x is big"\texttt{print "x is big"}
end\texttt{end}
continue\texttt{continue}
end\texttt{end}
dp <loc> <exprs>\texttt{dp <loc> <exprs>} 动态断点,运行到指定位置只执行表达式但不停留 dprintf main.c:42, "val=%dn", val\texttt{dprintf main.c:42, "val=\%d\\n", val}
sa bre <filename>\texttt{sa bre <filename>} 把断点存到某文件去
b <loc> th <thread-id>\texttt{b <loc> th <thread-id>} 线程特定的断点
fg\texttt{fg} 等同于 continue
step/next/si/ni <count>\texttt{step/next/si/ni <count>} 步进/步入/指令步入/指令步进指定次数
finish\texttt{finish} 返回父级函数
u <line-number>\texttt{u <line-number>} 运行直到指定行号
sk\texttt{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\texttt{rs} 反向单步
rc\texttt{rc} 反向继续执行到断点
rsi\texttt{rsi}
rn\texttt{rn}
rni\texttt{rni}
reverse-f\texttt{reverse-f}
记录进程行为

该命令存在兼容性问题,很可能不支持目标 CPU

rec full\texttt{rec full} 完全记录 GDB 的行为
rec s\texttt{rec s} 停止记录 GDB 的行为
rec g <loc>\texttt{rec g <loc>} 前往特定 Log位置 可以是 begin,end,N\texttt{begin,end,N}
res sa\texttt{res sa} 存储记录的 Log 到文件
res re\texttt{res re} 恢复本地文件到 Log 恢复的 log 必须由 save\texttt{save} 生成
i rec\texttt{i rec} 显示 recording 基本信息
栈检查
bt <N>\texttt{bt <N>} 只打印前几层栈
bt -full\texttt{bt -full} 同时打印临时变量
f <function>\texttt{f <function>}
f <N>\texttt{f <N>}
f <view>\texttt{f <view>}
选择想要的栈帧并查看
up <N>\texttt{up <N>} 跳到当前函数调用者或其他调用者
down <N>\texttt{down <N>} 跳到被调用者
i f\texttt{i f} 打印当前栈帧完整基本信息
i loc\texttt{i loc} 打印当前栈帧变量信息
frame apply <frames> <command>\texttt{frame apply <frames> <command>} 对所有或部分栈帧进行操作 frame apply all info args\texttt{frame apply all info args}
f ap a i ar\texttt{f ap a i ar}
f ap 2 5 print some_var\texttt{f ap 2 5 print some\_var}
f ap all -q p $sp\texttt{f ap all -q p \$sp} (quiet 静默栈帧信息)
检查原文件
l\texttt{l} 打印当前执行上下文
l <linenum>\texttt{l <linenum>}
l <func>\texttt{l <func>}
指定打印上下文
disas\texttt{disas} 反汇编当前函数
disas <address> <address>\texttt{disas <address> <address>}
disas <address> +<size>\texttt{disas <address> +<size>}
指定反汇编范围
disas/m\texttt{disas/m} 源码和汇编对照
检查数据
l\texttt{l} 打印当前执行上下文
p -pretty <something>\texttt{p -pretty <something>} 优雅打印 有些 GDB 不支持
explore <vars>\texttt{explore <vars>} 递归得查看复杂数据结构 属于 tui扩展功能
@\texttt{@} 操作符 人工数组构造器 x/4x arr@4\texttt{x/4x arr@4}
p arr@4\texttt{p arr@4}
::\texttt{::} 作用域界定符 p myfile.cpp::some_var\texttt{p myfile.cpp::some\_var}
{type} addr 将内存开始的内容当做指定 type 解读 p struct MyType 0xdeadbeef\texttt{p {struct MyType} 0xdeadbeef}
x\texttt{x} 检查内存(支持如下输出格式)
disp <expr>\texttt{disp <expr>} 程序每次停下 (无论单步还是断点) 都自动执行表达式
(支持如下输出格式)
display i\texttt{display i} 每次都打印 i 的值
i di\texttt{i di} 显示当前 display 信息
p *$1\texttt{p *\$1} 通过历史编号快速引用之前的 print 内容 p *$.next\texttt{p *\$.next} 打印 ptr->next 指向的结构体
p *$ $.next\texttt{p *\$ \$.next} 打印前一个值的 next
set <alias>=<foo>\texttt{set <alias>=<foo>} 建立临时别名变量 set $foo = *object_ptr\texttt{set \$foo = *object\_ptr}
i r\texttt{i r} 查看寄存器信息
x/i $ pc\texttt{x/i \$ pc} 查看当前 PC 指向的指令
i fl\texttt{i fl} 查看硬件浮点单元信息
i ve\texttt{i ve} 查看硬件向量单元信息
du memory <filename> <start-addr> <end-addr>\texttt{du memory <filename> <start-addr> <end-addr>}
du value <filename> <expr>\texttt{du value <filename> <expr>}
du registers <filename>\texttt{du registers <filename>}
将内存或者寄存器等信息打印到文件
app\texttt{app} 用法如上
restore\texttt{restore} 用法如上
从文件恢复内存或寄存器等信息
gcore\texttt{gcore} 手动保存进程快照
find[/s或/n] start_addr, +len, <vars>\texttt{find[/s或/n] start\_addr, +len, <vars>}
find[/s或/n] start_addr, end_addr, <vars>\texttt{find[/s或/n] start\_addr, end\_addr, <vars>}
查找指定内存中的内容(支持 b 字节 h 半字 w 字 g 双字)
输出格式:
格式 含义
x\texttt{x} 十六进制(Hexadecimal)
d\texttt{d} 十进制(Decimal)
u\texttt{u} 无符号十进制(Unsigned decimal)
o\texttt{o} 八进制(Octal)
t\texttt{t} 二进制(Binary,“t”代表“two”)
a\texttt{a} 地址(以符号+偏移形式显示)如:0x54320 <_initialize_vx+396>\texttt{0x54320 <\_initialize\_vx+396>}
c\texttt{c} 字符(Character constant,与整数同时显示)
f\texttt{f} 浮点数格式(Floating point)
s\texttt{s} 字符串(尝试将值解释为字符串)
z\texttt{z} 十六进制,带前导 0(zero-padded hexadecimal)
r\texttt{r} 原始(raw)格式,禁用 Python pretty-printer,显示底层结构
i\texttt{i} 解析为指令进行打印

find /sb 0x600000, 0x601000, 0x68, 0x65, 0x6c, 0x6c, 0x6f\texttt{find /sb 0x600000, 0x601000, 0x68, 0x65, 0x6c, 0x6c, 0x6f}
find /n3 0x600000, 0x700000, 0x0    # 找出前三个 NULL 字节的位置\texttt{find /n3 0x600000, 0x700000, 0x0    \# 找出前三个 NULL 字节的位置}

i macro <MACRO>\texttt{i macro <MACRO>} 显示程序 MACRO 的信息

前提是 -g3\texttt{-g3} 编译

追溯点

tracepoint 的用法和 breakpoint 一模一样
但是实际应该用不到,暂时略过

特性 breakpoint tracepoint
是否中断程序 ✅ 会中断程序 ❌ 不中断
是否实时查看值 ✅ 可以立即查看 ❌ 稍后查看(trace buffer)
使用场景 本地调试 远程/嵌入式/高性能调试
是否依赖目标支持 ❌ GDB 自身支持 ✅ 需要 target 支持 trace
命令\texttt{命令} 功能描述 说明
trace <location>\texttt{trace <location>} 设置追踪点(tracepoint) 在不停止程序的前提下记录位置数据,适用于远程或嵌入式调试
collect <expr>\texttt{collect <expr>} 指定采集哪些变量或表达式 可收集变量、寄存器(如 $regs\texttt{\$regs}$locals\texttt{\$locals})、结构体字段等
tstart\texttt{tstart} 开始 trace 会话 启动采样机制,程序运行时会在 tracepoints 自动记录
tstop\texttt{tstop} 停止 trace 会话 停止采样并允许分析采集到的快照
tfind <n>\texttt{tfind <n>} 查找第 n\texttt{n} 个 trace 快照 若不加参数则查找下一个,可用 start\texttt{start}end\texttt{end}none\texttt{none} 等特殊参数
tdump\texttt{tdump} 打印当前 trace 快照采集到的数据 必须先用 tfind\texttt{tfind} 定位快照
i tracepoints\texttt{i tracepoints} 查看所有 tracepoint 列出编号、位置、状态等信息
i collected\texttt{i collected} 查看当前采样点收集了哪些内容 仅列出采样项的名称,不包含具体值
actions [num]\texttt{actions [num]} 为某个 tracepoint 编号指定采集行为 可交互式输入 collect\texttt{collect}while-stepping\texttt{while-stepping} 等命令,以 end\texttt{end} 结尾
while-stepping <count>\texttt{while-stepping <count>} 步进若干次并采集 在触发 tracepoint 后单步执行并继续采集
passcount <n>\texttt{passcount <n>} 设置某 tracepoint 命中 n\texttt{n} 次后停止 自动终止 trace 会话,适用于限制记录量
tvariable $name = <expr>\texttt{tvariable \$name = <expr>} 创建并赋值 trace 状态变量 可用于内部计数或条件判断,未指定值时默认为 0
info tvariables\texttt{info tvariables} 查看所有 trace 状态变量 包括初始值及当前值(若 trace 正在运行)
delete tvariable [$name ...]\texttt{delete tvariable [\$name ...]} 删除指定 trace 状态变量 不带参数将全部清除
target tfile <filename>\texttt{target tfile <filename>} 加载文件作为 trace 数据源 可用 tdump\texttt{tdump} 查看内容,但不能运行新的 trace 会话
target ctf <dirname>\texttt{target ctf <dirname>} 加载 CTF 格式 trace 数据目录 用于共享或兼容其他 tracing 工具(如 LTTng)的分析数据
符号表检查
命令\texttt{命令} 功能说明 示例
i address <sym>\texttt{i address <sym>} 显示符号 <sym> 的地址 i address myVar\texttt{i address myVar}
i symbol <addr>\texttt{i symbol <addr>} 根据地址 <addr> 查找符号 i symbol 0x400123\texttt{i symbol 0x400123}
demangle <mangled-name>\texttt{demangle <mangled-name>} 将重整(mangled)名称还原 demangle _Z3fooIiEvv\texttt{demangle \_Z3fooIiEvv}
whatis [/<fmt>] <sym>\texttt{whatis [/<fmt>] <sym>} 显示符号 <sym> 的类型 whatis /x ptr\texttt{whatis /x ptr}
ptype <type>\texttt{ptype <type>} 打印类型 <type> 的完整定义 ptype struct Person\texttt{ptype struct Person}
i types [<regex>]\texttt{i types [<regex>]} 列出匹配 <regex> 的所有类型 i types "My.*"\texttt{i types "My.*"}
i scope <sym>\texttt{i scope <sym>} 显示符号 <sym> 的作用域信息 i scope foo\texttt{i scope foo}
i source <file>\texttt{i source <file>} 显示源文件 <file> 的路径 i source main.c\texttt{i source main.c}
i sources\texttt{i sources} 列出所有加载的源文件 i sources\texttt{i sources}
i functions [<regex>]\texttt{i functions [<regex>]} 列出匹配 <regex> 的函数名 i functions "mˆain$"\texttt{i functions "\^main\$"}
i variables [<regex>]\texttt{i variables [<regex>]} 列出匹配 <regex> 的变量名 \texttt{i variables "\^g\_.\*"}
i modules [<regex>]\texttt{i modules [<regex>]} 列出所有 Fortran 模块 i modules "Mˆath$"\texttt{i modules "\^Math\$"}
i main\texttt{i main} 显示程序的入口函数名 i main\texttt{i main}
i classes [<regex>]\texttt{i classes [<regex>]} 列出匹配 <regex> 的 Objective-C 类 i classes "My.*"\texttt{i classes "My.*"}
maint i symtabs [<regex>]\texttt{maint i symtabs [<regex>]} 列出符号表文件(.o/.so)信息 maint info symtabs ".*.o$"\texttt{maint info symtabs ".*\\.o\$"}
maint i psymtabs [<regex>]\texttt{maint i psymtabs [<regex>]} 列出已加载的进程符号表信息 maint info psymtabs "mˆodule"\texttt{maint info psymtabs "\^module"}
maint i line [<regex>]\texttt{maint i line [<regex>]} 列出行号—地址映射表 maint info line-table "mˆain"\texttt{maint info line-table "\^main"}
代替执行 (重点)

本部分提供了你“改变程序行为”的能力,不再只是观察和打印!
因为不用改代码就知道验证修复是否有效!

set var <var>=<val>\texttt{set var <var>=<val>}
set {<type>}<address> = <val>\texttt{set \{<type>\}<address> = <val>}
修改变量值或者地址值 set {int}0x83040 = 4\texttt{set \{int\}0x83040 = 4}
j <loc>\texttt{j <loc>} 直接跳到指定位置执行, 忽略部分内容如 Bug 代码(同函数内)
sig <signal>\texttt{sig <signal>} 向程序发送一个信号并继续运行 sig 0\texttt{sig 0} 表示继续运行
sig SIGINT\texttt{sig SIGINT} 表示模拟 Ctrl+C 中断
ret <expr>\texttt{ret <expr>} 强制返回父级函数,并可以指定返回值
call <expr>\texttt{call <expr>} ①调用程序中的函数
②修改状态,如副作用函数
③动态计算表达式
call printf("x = %dn", x)\texttt{call printf("x = \%d\\n", x)}
compi code -raw -- <src>\texttt{compi code -raw -- <src>} 单行“原始”模式编译(无自动包装作用域) \texttt{compile code -raw -- int \_gdb\_expr_() { return 42; }}
compi code\texttt{compi code} 进入多行编辑模式,直至输入 end 结束 compile code> int y = x+1;> end\texttt{compile code}\newline\texttt{> int y = x+1;} \newline\texttt{> end}
compi file <file>\texttt{compi file <file>} 从指定文件编译并执行 compile file /home/me/snippet.c\texttt{compile file /home/me/snippet.c}
compi file -raw <file>\texttt{compi file -raw <file>} 原始模式下从文件编译 compile file -raw /home/me/snippet.c\texttt{compile file -raw /home/me/snippet.c}
compi print [opts] -- <expr>\texttt{compi print [opts] -- <expr>} 编译并执行 <expr>,结果按类型自动输出 compile print -- sin(3.14)\texttt{compile print -- sin(3.14)}
compi print /f [opts] -- <expr>\texttt{compi print /f [opts] -- <expr>} 编译并执行 <expr>,并以浮点格式输出 compile print /f -- cos(0.5)\texttt{compile print /f -- cos(0.5)}
compi print [opts] --\texttt{compi print [opts] --} 进入多行编辑模式输入表达式,直至 end compile print> x = x+1;> end\texttt{compile print}\newline\texttt{> x = x+1;} \newline\texttt{> end}
compi print /f [opts] --\texttt{compi print /f [opts] --} 多行模式下编译并以浮点格式输出 compile print /f> double z = sqrt(2);> end\texttt{compile print /f}\newline\texttt{> double z = sqrt(2);} \newline\texttt{> end}
文件
file <file>\texttt{file <file>}
选择输入的文件
i dll\texttt{i dll} 查看动态库
调试目标
set arch <arch>\texttt{set arch <arch>}
set processor <processor>\texttt{set processor <processor>}
设置架构或处理器型号
tar <target>\texttt{tar <target>} 设置调试目标 可以是 core,ctf,exec,...\texttt{core,ctf,exec,...}
远程调试

暂时略过,无法实操

自定义命令
def <command>\texttt{def <command>} 自定义命令 可以写在. gdbinit
echo \texttt{echo } 输出文本,支持 C 风格转义字符(如 \n),默认不自动换行 \texttt{echo Hello\ world!\n}
output \texttt{output } 输出表达式值(不带换行、不加入历史记录,不显示 $n =) output x + 5\texttt{output x + 5}
output/ \texttt{output/ } 使用指定格式输出表达式值(格式同 p/\texttt{p/} output/x my_var\texttt{output/x my\_var}
printf , <exprs>\texttt{printf , <exprs…>} 类似 C 语言 printf\texttt{printf},使用模板格式化多个值输出 \texttt{printf “foo = 0x\%x,\ bar = 0x\%x\n”, foo, bar}
pi\texttt{pi} 直接进入 python 交互时命令行
py <expr>\texttt{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\texttt{tu en}
tu dis\texttt{tu dis}
打开关闭 TUI
i win\texttt{i win} 当前窗口信息
la <mode>\texttt{la <mode>} TUI 以某种形式展示 next,prev,src,asm,split,regs\texttt{next,prev,src,asm,split,regs}
foc <mode>\texttt{foc <mode>} 聚焦哪个子 TUI 窗口
ref\texttt{ref} 刷新窗口
tu reg group\texttt{tu reg group} 切换寄存器组 next,prev,general,float,system,vector,all\texttt{next,prev,general,float,system,vector,all}
win <name> ±<num>\texttt{win <name> ±<num>}
调整窗口大小
命令行快捷键
Control + b 向左移动一个字符 (back)
Control + f 向右移动一个字符 (forward)
Control + d 删除光标下的字符
Control + a 移动到行首
Control + e 移动到行尾
Control + l 清屏
!\texttt{!} 开始一次历史替换
!!\texttt{!!} 执行上一条命令
!n\texttt{!n} 引用编号为 n 的命令
!-n\texttt{!-n} 引用向上第 n 条命令
!string\texttt{!string} 引用最近一条以 string\texttt{string} 开头的命令
!?string?\texttt{!?string?} 引用最近一条包含 string 的命令