目录
1. 什么是gdb
2. gdb的使用
2.1 生成调试信息
2.2 gdb的基本用法
2.2.1 启动gdb
2.2.2 gdb退出
2.2.3 列出源码
2.2.4 运行程序
2.2.5 断点
2.2.6 逐过程调试和单步调试
2.2.7 显示变量的值
2.2.8 修改变量的值
2.2.9 跳转到指定的行
2.2.10 跳到下一个断点
gdb是一个调试器,能够帮助开发人员诊断和修复程序中的错误
gdb可以在程序运行时暂停程序并检查程序状态,例如检查变量的值、执行栈跟踪和查看内存内容,并且gdb支持多种编程语言,包括C、C ++、Objective-C、Fortran和Ada等。gdb也可以在终端命令行中使用,也可以与多种集成开发环境(IDE)集成使用。gdb是一个强大而灵活的工具,可用于调试各种类型的程序,从简单的命令行应用程序到复杂的多线程网络应用程序都能够通过gdb调试
gdb具有如下优点:
报错解决:
如果在后续使用gdb时出现如下报错
Missing separate debuginfos, use: debuginfo-install glibc-2.17-326.el7_9.x86_64
解决方法如下:
[base-debuginfo]
name=CentOS-$releasever - DebugInfo
baseurl=http://debuginfo.centos.org/$releasever/$basearch/
gpgcheck=0
enabled=0
protect=1
priority=1
Linux下的ELF可执行目标文件由一系列的section组成,其中有一个section为.debug,.debug节通常是一些调试工具(如gdb)使用的关键节之一,它们可以解析这些信息以提供调试信息和帮助调试程序
在Linux下,程序的发布方式有两种,debug模式和release模式,通过gcc/g++编译形成的可执行文件默认是release模式的,也就是说不能够进行调试,因此要使用gdb调试,必须在源代码生成二进制程序的时候, 加上 -g 选项,才会在生成的可执行目标文件中带上.debug节
对于已经生成的源文件,可以通过下面的命令查看目标文件中的信息
readelf -S [filename] | grep -i debug
// readelf 用于读取和显示 ELF (Executable and Linkable Format) 文件信息
// -S 显示 ELF 文件的所有节(section)的信息
// grep -i 选项表示忽略大小写
以下面的test.c文件为示例
#include
// 求 0-n 的和
int AddToN(int n)
{
int res = 0;
for (int i = 0; i <= n; i++)
{
res += i;
}
return res;
}
int main()
{
int n, res;
scanf("%d", &n);
res = AddToN(n);
printf("res: %d\n", res);
return 0;
}
gcc test.c -o test 生成 test文件,readelf后发现没有用于调试的.debug section
接下来在编译时带上 -g 选项,会发现生成的test.debug文件大小明显更大
这些内容就是调试信息,因此在使用gdb进行调试前,一定要确保在编译时带上 -g 选项
gdb可以在终端命令行中使用,也可以与多种集成开发环境(IDE)集成使用,这里只介绍在命令行中的基本使用
直接在命令行上输入gdb,后面接上要调试的文件名即可
gdb binFile
启动gdb成功之后,最后一行信息显示如下,说明调试信息读取成功,可以进行调试
Reading symbols from /home/hqs/code/April/3/test.debug...done.
ctrl + d 或 输入 quit 后按回车
list 列出当前文件的源代码,默认列出前10行 (list 可以简写为 l)
选项
list [n], +:从指定行号n开始,列出该行后面的源代码
list [function_name]:列出指定函数的源代码
list [sline], [eline]:列出指定行号范围内的源代码,比如list 10 20 显示10-20行之间的代码
list -:列出上一个list命令列出的源代码的下一部分
list +:列出当前行后面的源代码
如果想要从第一行开始打印,可以使用以下命令,一次默认打印10行
list 1
如果要继续往后看,可以使用 list + 命令或者直接按回车,因为gdb会记住你上一次的操作,当你没有输入时按回车,gdb就重复你上一次的操作,这里就是list,会自动将后10行的内容显示出来
查看第n行的源代码可以用下面的命令,gdb会以n为中心,显示出前5行和后4行代码,共计10行
list n
要运行程序,直接输入run即可,程序会一直运行到断点处停下来,如果没有断点则直接运行结束
run (简写为 r)
还有另外一组命令可以在gdb中运行程序
start:启动程序并停在程序的入口处
continue:继续运行程序
新增断点
直接run会运行到程序退出不方便调试,为了利于调试和观察我们需要在程序的某些地方加上断点,好让程序在一个便于观察的地方停下来,使用的命令如下linenum是要设置断点的行号
break linemun (break简写为 b)
b 10 就是在第10行设置一个断点
设置好断点后可以查看已经设置的断点,命令如下
info break (简写为 info b)
删除断点
delete n (delete简写为 d,n表示断点编号,因此通常配合info来使用)
d 2 表示删除第二个断点
在打断点时,gdb会默认给每一个断点一个编号,因此在删除断点的时候,只能根据编号删除
打开和关闭断点
disable breakpoint_number (breakpoint_number 是要关闭的断点编号)
enable breakpoint_number (breakpoint_number 是要打开的断点编号)
关闭断点不等于删除断点,它跟类似于将这个断点暂时禁用,以后有需要可以随时打开它,所以在info b时还能够看到这个断点存在的信息,之不过其Enb字段由 y(yes) 变成了 n(no)
在完成了断点的设置后,就可以让程序停在断点处,然后一步一步调试来观察程序,这里可以分为两种方式
逐过程
逐过程调试会执行下一行,当遇到函数调用时,会一次性执行完该函数,也就是说不会进到函数体内部,其命令为
next (简写为 n)
这里将断点打在第19行,输入r运行,程序停在第19行,按下n直接跳转到第20行
单步调试(逐语句)
单步调试时同样会执行下一行,当遇到函数调用时,会进入改函数体内部,其命令为
step (简写为 s)
调用堆栈
在单步调试时,可以从一个函数体内部进到另一个函数体内部,此时我们所处的栈帧位置也发生了变化,如果需要查看堆栈信息,可以使用如下命令
backtrace (简写为 bt)
结束堆栈
当进入到另一个函数体之后,如果想跳出这个函数体回到调用的地方,使用如下命令
finish
临时查看
当需要查看变量的值时,有如下一些做法
print (简写为 p)
在gdb中启动程序并进入调试模式
在程序执行到需要查看变量的位置时,可以使用break或b命令在该位置设置断点
使用run或r命令运行程序,程序将在断点处停止
使用print或p命令查看变量的值
查看变量:
print var_name // var_name是要查看的变量名称
查看数组或结构体变量的值:
print *array@len // 显示数组array的前len个元素的值 print struct_name // 显示结构体struct_name的成员变量值
查看指针
print *pointer_name // 查看指针指向的值 print pointer_name // 查看指针本身
长显示
在调试程序时,经常需要反复查看某个变量的值,而不希望每次都输入一遍print命令。这时可以使用gdb的display命令,该命令会在每次程序停止时自动显示指定变量的值
display
每指定一个变量,这个变量都会带上一个编号,如果要关闭某个变量的长显示,使用下面的命令
undisplay display_number (display_number是使用display命令时分配的编号)
当需要对某个变量的值进行修改时,使用如下的命令
set variable_name = new_value
比如 set i = 10 就是将变量i的值设为10
这里将 i 设为10, 下一步不满足循环条件,直接跳出循环
在调试程序时,有时候需要让程序执行到指定的代码行之前或之后停止,可以使用gdb的until命令
until line_number (line_number是要执行到的行号)
until 15 表示直接跳到15行执行(前面的执行流跑完)
在gdb中,c命令用于继续执行程序,直到下一个断点或程序结束。
continue (简写为 c)
具体操作步骤如下:
在gdb中启动程序并进入调试模式
使用break或b命令设置(多个)断点
使用run或r命令运行程序,程序将在第一个断点处停止
在停止时,可以使用print命令查看变量的值,使用step或s命令逐行执行程序,使用next或n命令跳过函数调用等
如果想要继续执行程序,可以使用c命令