前言
gdb 功能
- 动态改变程序的执行环境。
 - 自定义启动运行需要调试的程序。
 - 在指定位置使用条件表达式设置断点。
 - 在程序暂停时观察代码内变量值的变化。
 
开始前准备
主要是生成调试信息,在编译的时候,添加 -g 选项来生成调试信息,以此来使用 gdb 的调试
gdb 外部指令
gdb 可执行文件名启动 gdb 调试gdb --help查看指令帮助man gdb查看 gdb 手册gdb -x xxx在 gdb 启动时运行一些脚本gdb --version查看 gdb 版本
gdb 内部指令
开始运行
r/run开始运行,会一直运行,直到断点start开始运行,在main处会停下来starti开始运行,在第一条指令处停下来r/run param开始运行,会一直运行,直到断点,后面的param就是可执行文件后面需要跟的参数
运行中指令
c/continue继续运行,一直运行,直到下一个断点c/continue n继续运行,一直运行,会忽略n个断点s/step单步,不进入函数内部si/stepi单步进入,进入函数内部,是在机器层面的,单步到下一个机器指令n/next单步,进入函数内部,是源码层面的下一行ni/nexti单步,是机器指令层面的,单步到下一个机器指令finish函数会继续执行完,并且打印返回值,然后等待接下来的指令return函数不会继续执行下面的指令,直接返回return expression直接返回,指定返回值为expressionu退出当前循环体step, next, stepi, nexti后面加次数都表示执行一定次数该指令
调试中变量相关
info args打印当前函数的参数名及值info locals打印当前函数中所有局部变量及其值set var arg=val设置变量的值ptype arg查看arg的类型disable mem addr禁用mem命令定义的内存区域enable mem addr启用mem命令定义的内存区域wha/whatis arg显示变量的类型show arg可以显示历史中变量的最后十个值
断点和监视点
断点
b func_name在函数处添加断点b *func_name在函数处添加断点,这里使用*的主要是为了使断点设置到汇编语言层的函数开头b +n在当前位置的n个偏移处添加断点b *addr在地址addr处添加断点b filename: n在文件filename的n行号处添加断点b filename:func_name在文件filename的func_name函数处添加断点info b查看断点信息ignore n nums忽略n号断点nums次disable n临时禁用n号断点enable n重新启用n号断点enable n once重新启用n号断点,中断一次之后禁用断点enable n delete重新启用n号断点,中断一次之后删除断点clear func_name/n/*addr/filename:n/filename:function删除已经指定的断点break point if condition测试给定条件,如果条件为真就暂停运行condition n删除指定断点的出发条件condition n con为指定断点添加出发条件
断点命令
可以定义在断点中断后执行的命令,一般使用要在定义断点之后执行
1  | b main  | 
其中 n 就是断点号,也就是在对应断点号之后执行命令, command 需要与 end 对应结束
监视点
watch expression表达式发生变化时暂停运行awatch expression表达式被访问/被改变时暂停运行rwatch expression表达式被访问时暂停运行info watchpoints查看当前设置的观察点delete删除所有断点和监视点delete n删除n号断点或者监视点
窗口指令
layout src显示源码layout asm显示汇编代码layout split显示源代码和汇编代码layout regs显示寄存器layout next切换到下一个布局模式layout prev切换到上一个布局模式info win显示窗口大小focus next聚焦到下一个窗口focus prev聚焦到上一个窗口focus cmd聚焦到命令行focus src聚焦到源码窗口focus asm聚焦到汇编代码窗口focus regs聚焦到寄存器窗口refresh刷新所有窗口winheight name +/- line调整name窗口高度reg寄存器cmd命令行src源码asm汇编代码
update更新源代码窗口和当前执行点ctrl-l刷新窗口ctrl-x + n也就是先按下ctrl-x然后再按n1单窗口模式,显示一个窗口2双窗口模式,显示两个窗口a回到传统模式,也就是退出layout
寄存器指令
i/info reg/register显示寄存器的值,不包括浮点寄存器和向量寄存器的内容i/info all-register/reg显示所有寄存器的内容i regsiter/reg reg_name显示reg_name寄存器的内容p/print $reg显示对应寄存器reg的值set var $reg=n设置寄存器reg的值为n
栈帧操作
bt/backtrace查看栈回溯信息,执行该栈回溯命令后,会显示程序执行到什么位置,包含哪些帧等信息。每一帧都有一个编号,从 0 开始。0 表示当前正在执行的函数,1 表示调用当前函数的函数,以此类推。栈回溯是倒序排列的bt/backtrace n只显示开头n个栈帧bt/backtrace -n只显示最后n个栈帧bt/backtrace full打印栈帧并打印局部变量的值bt/backtrace full n打印开头的n个栈帧并打印局部变量的值bt/backtrace full -n打印结尾的n个栈帧并打印局部变量的值f/frame n切换栈帧为n号栈帧f/frame addr切换为栈帧地址addrup n切换栈帧为向上n个栈帧,跳到上一层函数down n切换栈帧为向下n个栈帧,跳到下一层函数info frame显示栈帧的信息info frame n显示n号栈帧的信息info stack查看堆栈使用情况whe/where查看调用的堆栈disas/disassemble反汇编当前的函数或者指定函数
显示指令
p/print /格式 arg/$reg用于指定显示格式x /格式 addr用指定格式显示addr内存中的内容- 格式
x显示为 16 进制数字d显示为 10 进制数字u显示为无符号 10 进制数字o显示为 8 进制数字t显示为 2 进制数字 twoa显示为地址c显示为字符f显示为浮点小数s显示为字符串i显示为机器语言(仅在显示内存的X命令中可以使用)
 x /NFU addr其中N表示向下显示多少个,F表示格式,如上,U表示单位大小b字节h半字w字g双字
set print elements n设置打印的字节长度为n,其中n=0时表示不受限制l/list n显示代码,默认显示 10 行,指定显示n行代码display arg/$reg跟踪打印变量值或者寄存器值undisplay n取消跟踪第n个跟踪变量的值info display查看跟踪的变量号和对应的变量disable display n禁用n号跟踪变量enable display n启用n号跟踪变量
反向调试
record用于开始记录程序反向调试所需要的信息,其中包括保存程序的每一步每一行的结果等信息,而且反向调试只能到执行这条指令的位置,如果没有执行这条指令是没有办法进行反向调试的record stop停止反向调试,反向调试的记录停止,就可以开始正常运行了rc/reverse-continue开始反向运行,直到碰到一个断点rs/reverse-step反向运行到上一次被执行的源代码行,会进入函数rsi/reverse-stepi反向运行程序到上一条机器指令rn/reverse-next反相运行到上一次被执行的源代码行,不会进入函数rni/reverse-nexti反相运行到上一条机器指令reverse-finish反向运行程序回到调用当前函数的地方set exec-direction [forward|reverse]设置程序运行方向,就可以使用stop和continue等指令来执行反向的调试指令
抓取和释放进程
detach将正在调试的进程释放掉,不再进行调试,该进程会继续执行attach pid可以调试已经启动的进程,或者调试陷入死循环而无法返回的控制台进程
结束
q/quit退出当前调试k/kill杀死当前正在调试的进程,不会退出,可以重新使用开始运行指令开始
其它一些操作
edit编辑文件或者函数find dir xxx寻找dir路径下名为xxx的文件dir用于指定源文件目录print-object arg显示目标信息sharelibrary加载共享的符号
gdb 初始化文件
linux 环境下初始化文件为 .gdbinit 文件,会在用户的主目录下寻找一个名为 .gdbinit 的文件,如果存在该文件,gdb 就会在启动之前将其作为命令文件运行,初始化文件和命令文件运行顺序如下
~/.gdbinit- 运行命令选项
 ./.gdbinit- 通过 
-x选项给出的命令文件gdb -x xxx 
该文件的定义应遵循以下格式
1  | define <command>  | 
在 .gdbinit 中可以写 gdb 的内部指令,gdb 会执行该指令的
1  | layout src  | 
但是一般来说,当在一个非 home 目录下添加一个 .gdbinit 时,就必须要在 home 目录下添加一个 .gdbinit 文件,并且此文件中必须声明
1  | add-auto-load-safe-path xxx/.gdbinit  | 
后记
虽然 gdb 调试很强大,但是没有一个交互的页面其实也是有点难受的,但是 gdb 调试适用性很广。现在大部分都可以直接使用 vscode+cmake 来调试,其实很方便,能把所有变量都放在侧边栏以便调试确实很不错。但是丝毫不影响 gdb 的强大。