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 nn 为某一行代码的行号,该命令会使程序运行至第 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的值含义
linenumlinenum 是一个整数,表示要打断点处代码的行号。要知道,程序中各行代码都有对应的行号,可通过执行 l(小写的 L)命令看到。
filename:linenumfilename 表示源程序文件名;linenum 为整数,表示具体行数。整体的意思是在指令文件 filename 中的第 linenum 行打断点。
+ offset - offsetoffset offset 为整数(假设值为 2),+offset 表示以当前程序暂停位置(例如第 4 行)为准,向后数 offset 行处(第 6 行)打断点;-offset 表示以当前程序暂停位置为准,向前数 offset 行处(第 2 行)打断点
functionfunction 表示程序中包含的函数的函数名,即 break 命令会在该函数内部的开头位置打断点,程序会执行到该函数第一行代码处暂停。
filename:functionfilename 表示远程文件名;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;
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐