gdb调试工具

参考及学习文档

2.安装与启动gdb

  1. gdb -v 检查是否安装成功,未安装成功则安装(必须确保编译器已经安装,如 gcc) → 可参考(http://c.biancheng.net/view/8130.html)
  2. 启动 gdb
    1. gdb test_file.exe 来启动 gdb 调试, 即直接指定需要调试的可执行文件名
    2. 直接输入 gdb 启动,进入 gdb 之后采用命令 file test_file.exe 来指定文件名
    3. 如果目标执行文件要求出入参数(如 argv[] 接收参数),则可以通过三种方式指定参数:
      1. 在启动 gdb 时,gdb --args text_file.exe 
      2. 在进入gdb 之后,运行 set args param_1
      3. 在 进入 gdb 调试以后,run param_1 或者 start para_1

3.常用命令

  • 以下以 test_file.c 作为源程序例子的名字,test_file.exe 作为可执行文件例子的名字, 以param_1 作为参数的例子的名字。
  • (gdb) 表示是在 gdb 调试模式下运行
  • 一般常用的方法有两种,即 3.1打断点调试 和 3.2单步调试
  • list(l): 列出源代码
  • quit(q): 退出 gdb 调试模式
  • 进入 gdb 之后,输入 help 可以查看所有命令的使用说明

3.1 打断点调试

  • 查看断点信息: info breakpoint [n] 或者 info break [n] 用于查看断点的信息, n参数 作为可选参数,为某个断点的编号,表示查看指定断点而非全部断点。
  • 删除断点
    • (gdb) clear location
      • 参数 location 通常为某一行代码的行号或者某个具体的函数名。当 location 参数为某个函数的函数名时,表示删除位于该函数入口处的所有断点。
    • (gdb) delete [breakpoints] [num]
      • breakpoints 参数可有可无,num 参数为指定断点的编号,其可以是 delete 删除某一个断点,而非全部。
  • 禁用断点
    • disable [breakpoints] [num...]
      • breakpoints 参数可有可无;num... 表示可以有多个参数,每个参数都为要禁用断点的编号。如果指定 num...,disable 命令会禁用指定编号的断点;反之若不设定 num...,则 disable 会禁用当前程序中所有的断点。
  • 激活断点
    • enable [breakpoints] [num...]                        激活用 num... 参数指定的多个断点,如果不设定 num...,表示激活所有禁用的断点
    • enable [breakpoints] once num…                 临时激活以 num... 为编号的多个断点,但断点只能使用 1 次,之后会自动回到禁用状态
    • enable [breakpoints] count num...      临时激活以 num... 为编号的多个断点,断点可以使用 count 次,之后进入禁用状态
    • enable [breakpoints] delete num…               激活 num.. 为编号的多个断点,但断点只能使用 1 次,之后会被永久删除。

break(b): 打的是普通断点,打断点有两种形式

  • (gdb) break location     // b location,location 代表打断点的位置

    linenum linenum 是一个整数,表示要打断点处代码的行号。要知道,程序中各行代码都有对应的行号,可通过执行 l(小写的 L)命令看到。
    filename:linenum filename 表示源程序文件名;linenum 为整数,表示具体行数。整体的意思是在指令文件 filename 中的第 linenum 行打断点。
    + offset
    - offset
    offset 为整数(假设值为 2),+offset 表示以当前程序暂停位置(例如第 4 行)为准,向后数 offset 行处(第 6 行)打断点;-offset 表示以当前程序暂停位置为准,向前数 offset 行处(第 2 行)打断点。
    function function 表示程序中包含的函数的函数名,即 break 命令会在该函数内部的开头位置打断点,程序会执行到该函数第一行代码处暂停。
    filename:function filename 表示远程文件名;function 表示程序中函数的函数名。整体的意思是在指定文件 filename 中 function 函数的开头位置打断点。
  • (gdb) break ... if cond   // b .. if cond,代表如果 cond 条件为true,则在 “...” 处打断点
    • 通过借助 condition 命令为不同类型断点设置条件表达式,只有当条件表达式成立(值为 True)时,相应的断点才会触发从而使程序暂停运行。

tbreak: tbreak 命令可以看到是 break 命令的另一个版本,tbreak 和 break 命令的用法和功能都非常相似,唯一的不同在于,使用 tbreak 命令打的断点仅会作用 1 次,即使程序暂停之后,该断点就会自动消失。

rbreak: 和 break 和 tbreak 命令不同,rbreak 命令的作用对象是 C、C++ 程序中的函数,它会在指定函数的开头位置打断点。

  • (gdb) tbreak regex
    • regex 代表一个正则表达式,会在匹配到的函数的内部的开头位置打断点
  • tbreak 命令打的断点和 break 命令打断点的效果是一样的,会一直存在,不会自动消失。

watch: 此命令打的是观察断点,可以监控某个变量或者表达式的值。只有当被监控变量(表达式)的值发生改变,程序才会停止运行。

  • (gdb) watch cond
    • cond 代表的就是要监控的变量或者表达式
  • rwatch 命令:只要程序中出现读取目标变量(表达式)的值的操作,程序就会停止运行;
  • awatch 命令:只要程序中出现读取目标变量(表达式)的值或者改变值的操作,程序就会停止运行。

catch: 捕捉断点的作用是,监控程序中某一事件的发生,例如程序发生某种异常时、某一动态库被加载时等等,一旦目标时间发生,则程序停止执行。

  • (gdb) catch event

    event 事件
    含 义
    throw [exception] 当程序中抛出 exception 指定类型异常时,程序停止执行。如果不指定异常类型(即省略 exception),则表示只要程序发生异常,程序就停止执行。
    catch [exception] 当程序中捕获到 exception 异常时,程序停止执行。exception 参数也可以省略,表示无论程序中捕获到哪种异常,程序都暂停执行。
    load [regexp]
    unload [regexp]

    其中,regexp 表示目标动态库的名称,load 命令表示当 regexp 动态库加载时程序停止执行;unload 命令表示当 regexp 动态库被卸载时,程序暂停执行。regexp 参数也可以省略,此时只要程序中某一动态库被加载或卸载,程序就会暂停执行。

    PS: 可以先通过 lld text_file.exe 命令查看此执行文件所加载的程序动态库。

tcatch: tcatch 命令和 catch 命令的用法几乎相同,唯一不同之处在于,对于目标事件,catch 命令的监控是永久的,而 tcatch 命令只监控一次,也就是说,只有目标时间第一次触发时,tcath 命令才会捕获并使程序暂停,之后将失效。

GDB的 condition 命令

  • 针对三种断点设置条件
  • (gdb) condition bnum expression
    • bnum 表示断点的编号,expression 表示条件表达式
    • 用于为 bnum 编号的断点添加或修改 expression 条件表达式
  • (gdb) condition bnum
    • 用于删除 bnum 编号断点的条件表达式,使其变成普通的无条件断点

GDB ignore命令

  • ignore 命令也可以使一个断点成为条件断点,但这里的“条件”并非自定义的表达式,而仅为一个整数,它用来表示该断点失效的次数。
  • 即,ignore 命令可以使目标断点暂时失去作用,当断点失效的次数超过指定次数时,断点的功能会自动恢复。
  • 语法形式
    • ignore bnum count
      • 参数 bnum 为某个断点的编号;参数 count 用于指定该断点失效的次数。

3.2 单步调试

run(r)

continue(c)

next(n)

  • 命令格式: (gdb) next count
    • count 表示单步执行多少行代码,默认为 1 行
  • 其最大的特点是当遇到包含调用函数的语句时,无论函数内部包含多少行代码,next 指令都会一步执行完。也就是说,对于调用的函数来说,next 命令只会将其视作一行代码

step(s)

  • (gdb) step count
    • 参数 count 表示一次执行的行数,默认为 1 行。
  • 通常情况下,step 命令和 next 命令的功能相同,都是单步执行程序。不同之处在于,当 step 命令所执行的代码行中包含函数时,会进入该函数内部,并在函数第一行代码处停止执行。

until(u)

  • (gdb) until
    • 不带参数的 until 命令,可以使 GDB 调试器快速运行完当前的循环体,并运行至循环体外停止。注意,until 命令并非任何情况下都会发挥这个作用,只有当执行至循环体尾部(最后一行代码)时,until 命令才会发生此作用;反之,until 命令和 next 命令的功能一样,只是单步执行程序
  • (gdb) until location
    • 参数 location 为某一行代码的行号

3.3 查看变量的值

print(p)

  • p num_1
    • 参数 num_1 用来代指要查看或者修改的目标变量或者表达式
  • 它的功能就是在 GDB 调试程序的过程中,输出或者修改指定变量或者表达式的值

display

  • (gdb) display expr
  • (gdb) display/fmt expr
    • expr 表示要查看的目标变量或表达式;参数 fmt 用于指定输出变量或表达式的格式

      /fmt
      功 能
      /x 以十六进制的形式打印出整数。
      /d 以有符号、十进制的形式打印出整数。
      /u 以无符号、十进制的形式打印出整数。
      /o 以八进制的形式打印出整数。
      /t 以二进制的形式打印出整数。
      /f 以浮点数的形式打印变量或表达式的值。
      /c 以字符形式打印变量或表达式的值。
  • (gdb) undisplay num...
  • (gdb) delete display num...
    • 参数 num... 表示目标变量或表达式的编号,编号的个数可以是多个
  • (gdb) disable display num...
    • 禁用自动显示列表中处于激活状态下的变量或表达式
  • (gdb) enable display num...
    • 也可以激活当前处于禁用状态的变量或表达式
  • 和 print 命令一样,display 命令也用于调试阶段查看某个变量或表达式的值
  • 它们的区别是,使用 display 命令查看变量或表达式的值,每当程序暂停执行(例如单步执行)时,GDB 调试器都会自动帮我们打印出来,而 print 命令则不会

3.4 GDB handle 命令: 信号处理

→(gdb) handle signal mode

其中,signal 参数表示要设定的目标信号,它通常为某个信号的全名(SIGINT)或者简称(去除‘SIG’后的部分,如 INT);如果要指定所有信号,可以用 all 表示。

mode 参数用于明确 GDB 处理该目标信息的方式,其值可以是如下几个:

  • nostop:当信号发生时,GDB 不会暂停程序,其可以继续执行,但会打印出一条提示信息,告诉我们信号已经发生;
  • stop:当信号发生时,GDB 会暂停程序执行。
  • noprint:当信号发生时,GDB 不会打印出任何提示信息;
  • print:当信号发生时,GDB 会打印出必要的提示信息;
  • nopass(或者 ignore):GDB 捕获目标信号的同时,不允许程序自行处理该信号;
  • pass(或者 noignore):GDB 调试在捕获目标信号的同时,也允许程序自动处理该信号。

可以在 gdb 模式下,通过 info signals 或者 info signals <signal_name> (例如 info signals SIGINT) 查看不同 signal 的信息。

3.5 GDB frame和backtrace命令:查看栈信息

(gdb) frame spec 该命令可以将 spec 参数指定的栈帧选定为当前栈帧。spec 参数的值,常用的指定方法有 3 种:

  1. 通过栈帧的编号指定。0 为当前被调用函数对应的栈帧号,最大编号的栈帧对应的函数通常就是 main() 主函数;
  2. 借助栈帧的地址指定。栈帧地址可以通过 info frame 命令(后续会讲)打印出的信息中看到;
  3. 通过函数的函数名指定。注意,如果是类似递归函数,其对应多个栈帧的话,通过此方法指定的是编号最小的那个栈帧。

(gdb) info frame 我们可以查看当前栈帧中存储的信息

该命令会依次打印出当前栈帧的如下信息:

  • 当前栈帧的编号,以及栈帧的地址;
  • 当前栈帧对应函数的存储地址,以及该函数被调用时的代码存储的地址
  • 当前函数的调用者,对应的栈帧的地址;
  • 编写此栈帧所用的编程语言;
  • 函数参数的存储地址以及值;
  • 函数中局部变量的存储地址;
  • 栈帧中存储的寄存器变量,例如指令寄存器(64位环境中用 rip 表示,32为环境中用 eip 表示)、堆栈基指针寄存器(64位环境用 rbp 表示,32位环境用 ebp 表示)等。


除此之外,还可以使用 info args 命令查看当前函数各个参数的值;使用 info locals 命令查看当前函数中各局部变量的值。

(gdb) backtrace [-full] [n] 用于打印当前调试环境中所有栈帧的信息

其中,用 [ ] 括起来的参数为可选项,它们的含义分别为:

  • n:一个整数值,当为正整数时,表示打印最里层的 n 个栈帧的信息;n 为负整数时,那么表示打印最外层 n 个栈帧的信息;
  • -full:打印栈帧信息的同时,打印出局部变量的值。

3.6 GDB编辑和搜索源码

GDB edit命令:编辑文件

  • (gdb) edit [location]
  • (gdb) edit [filename] : [location]
    • location 表示程序中的位置。这个命令表示激活文件的指定位置,然后进行编辑。
    • 如果遇到报错 "bash: /bin/ex: 没有那个文件或目录", 因为 GDB 的默认编辑器是 ex , 则需要指定编辑器,如 export EDITOR=/usr/bin/vim or export EDITOR=/usr/bin/vi

GDB search命令:搜索文件

  • search <regexp>
  • reverse-search <regexp>
    • 第一项命令格式表示从当前行的开始向前搜索,后一项表示从当前行开始向后搜索。其中 regexp 就是正则表达式,正则表达式描述了一种字符串匹配的模式,可以用来检查一个串中是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串。很多的编程语言都支持使用正则表达式。

 

posted @ 2022-02-12 13:26  算是一个初学者  阅读(339)  评论(0编辑  收藏  举报