gdb使用
前言
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
直接返回,指定返回值为expression
u
退出当前循环体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
然后再按n
1
单窗口模式,显示一个窗口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
切换为栈帧地址addr
up 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 的强大。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 LuosBlog!