GDB调试命令详解
概念
通过调试程序,我们可以监控程序执行的每一个细节,包括变量的值、函数的调用过程、内存中数据、线程的调度等,从而发现隐藏的错误或者低效的代码
作用
1、程序启动时,可以按照我们自定义的要求运行程序,例如设置参数和环境变量;
2、可使被调试程序在指定代码处暂停运行,并查看当前程序的运行状态(例如当前变量的值,函数的执行结果等),即支持断点调试;
3、程序执行过程中,可以改变某个变量的值,还可以改变代码的执行顺序,从而尝试修改程序中出现的逻辑错误。
安装
yum -y install gdb
使用
在编译时需要使用 gcc/g++ -g 选项编译源文件
调试命令 (缩写) | 作用 |
---|---|
(gdb) break (b) | 在源代码指定的某一行设置断点,其中xxx用于指定具体打断点位置 |
(gdb) run (r) | 执行被调试的程序,其会自动在第一个断点处暂停执行。 |
(gdb) continue (c) | 当程序在某一断点处停止后,用该指令可以继续执行,直至遇到断点或者程序结束。 |
(gdb) next (n) | 令程序一行代码一行代码的执行 |
(gdb) step(s) | 如果有调用函数,进入调用的函数内部;否则,和 next 命令的功能一样。 |
(gdb) until (u) | location 当你厌倦了在一个循环体内单步跟踪时,单纯使用 until 命令,可以运行程序直到退出循环体。 |
until n | n 为某一行代码的行号,该命令会使程序运行至第 n 行代码处停止 |
(gdb) print (p) | 打印指定变量的值,其中 xxx 指的就是某一变量名。 |
(gdb) list (l) | 显示源程序代码的内容,包括各行代码所在的行号。 |
(gdb) finish(fi) | 结束当前正在执行的函数,并在跳出函数后暂停程序的执行。 |
(gdb) return(return) | 结束当前调用函数并返回指定值,到上一层函数调用处停止程序执行。 |
(gdb) jump(j) | 使程序从当前要执行的代码处,直接跳转到指定位置处继续执行后续的代码。 |
(gdb) quit (q) | 终止调试。 |
常用命令
- break命令
1、(gdb) break location // b location
2、(gdb) break ... if cond // b .. if cond
location的值 | 含义 |
---|---|
linenum | linenum 是一个整数,表示要打断点处代码的行号。要知道,程序中各行代码都有对应的行号,可通过执行 l(小写的 L)命令看到。 |
filename:linenum | filename 表示源程序文件名;linenum 为整数,表示具体行数。整体的意思是在指令文件 filename 中的第 linenum 行打断点。 |
+ offset - offset | offset offset 为整数(假设值为 2),+offset 表示以当前程序暂停位置(例如第 4 行)为准,向后数 offset 行处(第 6 行)打断点;-offset 表示以当前程序暂停位置为准,向前数 offset 行处(第 2 行)打断点 |
function | function 表示程序中包含的函数的函数名,即 break 命令会在该函数内部的开头位置打断点,程序会执行到该函数第一行代码处暂停。 |
filename:function | filename 表示远程文件名;function 表示程序中函数的函数名。整体的意思是在指定文件 filename 中 function 函数的开头位置打断点。 |
- 删除断点
(gdb) clear location
参数location 通常为某一行代码的行号或者某个具体的函数名。当 location 参数为某个函数的函数名时,表示删除位于该函数入口处的所有断点。
delete [breakpoints] [num]
breakpoints 参数可有可无,num 参数为指定断点的编号,其可以是delete 删除某一个断点,而非全部
- 禁用断点
1、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 次,之后会被永久删除。
其中,breakpoints 参数可有可无;num…表示可以提供多个断点的编号,enable命令可以同时激活多个断点。
- 观察断点
(gdb) watch cond
1、rwatch 命令:只要程序中出现读取目标变量(表达式)的值的操作,程序就会停止运行;
2、awatch 命令:只要程序中出现读取目标变量(表达式)的值或者改变值的操作,程序就会停止运行。
- print 命令
输出或者修改指定变量或者表达式的值
(gdb) print num
(gdb) p num
(gdb) print file::variable
(gdb) print function::variable
其中 file用于指定具体的文件名,funciton 用于指定具体所在函数的函数名,variable表示要查看的目标变量或表达式。
另外,print也可以打印出类或者结构体变量的值。
- display 命令
和 print 命令一样,display 命令也用于调试阶段查看某个变量或表达式的值,它们的区别是,使用 display 命令查看变量或表达式的值,每当程序暂停执行(例如单步执行)时,GDB 调试器都会自动帮我们打印出来,而 print 命令则不会。
(gdb) display expr
(gdb) display/fmt expr
注意,display 命令和 /fmt 之间不要留有空格。以 /x 为例,应写为 (gdb)display/x expr。
GDB单步调试
- next命令
(gdb) next count //不进入函数内部```
- step命令
```bash
(gdb) step count //进入函数内部```
- until命令
1、(gdb) until
2、(gdb) until location
不带参数的 until命令,可以使 GDB调试器快速运行完当前的循环体,并运行至循环体外停止。
注意,until 命令并非任何情况下都会发挥这个作用,只有当执行至循环体尾部(最后一行代码)时,
until命令才会发生此作用;反之,until命令和 next 命令的功能一样,只是单步执行程序。
- return命令
1、不需要再一步步执行到函数返回处,希望直接执行完当前函数
2、可以指定该函数的返回值 - finish命令
finish命令会执行函数到正常退出 - jump命令
(gdb) jump location //直接跳到指定行继续执行程序
//如果 jump跳转到的位置后续没有断点,那么 GDB会直接执行自跳转处开始的后续代码
- GDB search 命令
search <regexp> //从当前行的开始向前搜索
reverse-search <regexp> //从当前行的开始向后搜索
查看堆栈信息
- backtrace 命令用于打印当前调试环境中所有栈帧的信息
(gdb) backtrace [-full] [n]
n:一个整数值,当为正整数时,表示打印最里层的 n 个栈帧的信息;n为负整数时,那么表示打印最外层n个栈帧的信息;
-full:打印栈帧信息的同时,打印出局部变量的值
注意,当调试多线程程序时,该命令仅用于打印当前线程中所有栈帧的信息。
如果想要打印所有线程的栈帧信息,应执行thread apply all backtrace命令
- frame 命令
1、根据栈帧编号或者栈帧地址,选定要查看的栈帧
(gdb) frame spec
1、通过栈帧的编号指定。0 为当前被调用函数对应的栈帧号,最大编号的栈帧对应的函数通常就是 main() 主函数;
2、借助栈帧的地址指定。栈帧地址可以通过 info frame 命令(后续会讲)打印出的信息中看到;
3、通过函数的函数名指定。注意,如果是类似递归函数,其对应多个栈帧的话,通过此方法指定的是编号最小的那个栈帧
(gdb) up n
n为整数,默认值为 1。该命令表示在当前栈帧编号(假设为 m)的基础上,选定 m+n为编号的栈帧作为新的当前栈帧
(gdb) down n
该命令表示在当前栈帧编号(假设为 m)的基础上,选定 m-n为编号的栈帧作为新的当前栈帧
2、查看当前栈帧中存储的信息
(gdb) info frame
该命令会依次打印出当前栈帧的如下信息:
1、当前栈帧的编号,以及栈帧的地址;
2、当前栈帧对应函数的存储地址,以及该函数被调用时的代码存储的地址
3、当前函数的调用者,对应的栈帧的地址;
4、编写此栈帧所用的编程语言;
5、函数参数的存储地址以及值;
6、函数中局部变量的存储地址;
7、栈帧中存储的寄存器变量,例如指令寄存器(64位环境中用 rip 表示,32为环境中用eip 表示)、
堆栈基指针寄存器(64位环境用 rbp表示,32位环境用 ebp表示)等
3、info args
查看当前函数各个参数的值
4、info locals
查看当前函数中各局部变量的值
调试正在执行的程序
如果调试正在执行中的程序,首先需要找到正在运行程序的进程号PID,之后可以用下面三个命令进行调试,进入正常的调试流程
1) gdb attach PID
2) gdb 文件名 PID
3) gdb -p PID
注意,当调试完成后,如果想令当前程序进行执行,消除调试操作对它的影响,需手动将 GDB 调试器与程序分离,分离过程分为 2 步:
执行 detach 指令,使GDB调试器和程序分离;
执行 quit(或q)指令,退出GDB调试
调试执行异常崩溃的程序
$ gdb test /home/homework/coresave/core.test1.7791.1615620408 -q
Reading symbols from /home/zhudi/project/linux/blog/gdb/test...done.
warning: core file may not match specified executable file.
[New LWP 7791]
Core was generated by `./test'.
Program terminated with signal 11, Segmentation fault.
#0 0x00000000004005bd in main () at core.cpp:5
5 *a = 2;