Linux下的调试器 : gdb指令详解

server/2024/10/10 11:57:57/
🪐🪐🪐欢迎来到程序员餐厅💫💫💫
          主厨:邪王真眼
主厨的主页:Chef‘s blog  
所属专栏:linux">青果大战linux
总有光环在陨落,总有新星在闪烁

gdb是什么

        gdn是linux下的一个开源的命令行调试器,它可以帮助程序员在调试程序时跟踪程序运行过程中的错误。它可以用于C、C++、Fortran、汇编语言等多种编程语言。通过GDB,你可以在程序运行时中断程序的执行,查看和修改变量的值,设置断点,单步执行代码,打印函数调用栈等操作。

       基于gdb强大的调试能力,我们可以快速定位程序运行过程中的错误。现在就让我们来学习它的使用吧。


调试信息

       程序的发布方式有两种,debug模式和release模式 ,那么我们之前gcc编译生成的可执行文件是那种类型呢?我们要知道生成的可执行文件是二进制文件,使用了ELF格式,而readelf指令就可以用来查看ELF文件的信息。
我们先创建一个test1.c文件
#include<stdio.h>
int add(int i)
{int sum=0;for(int j=0;j<=i;j++)sum+=j;return sum;
}
int main()
{int sum=0;for(int i=0;i<=10;i++){sum+=add(i);}printf("%d\n",sum);return 0;
}

现在对他进行gcc,因为我们gcc默认不支持c99的语法,所以要加上-std=c99来让他支持

gcc -o test1.exe test1.c -std=c99

可以看到文件生成了,并且可以正常运行,接下来我们来查看他的调试信息,即debug信息
readelf -S test1.exe |grep debug

        以上指令,管道左侧用于输出可执行文件内的内容,右侧用于筛选含有debug字段,最后该指令什么也没有输出,说明gcc默认生成的可执行文件不包含debug信息,无法被调试。

  • 得出结论:

       gcc默认发布的是release版本,要想发布debug版本,必须在源代码生成二进制程序的时候, 加上 -g 选项

gcc -o test1.exe.debug test1.c -std=c99 -g

文件正常生成并且运行正确

输入指令查看调试debug信息
readelf -S test1.exe.debug |grep debug

成功发现debug信息

  1 #include<stdio.h>2 int add(int i)3 {4     int sum=0;5     for(int j=0;j<=i;j++)6         sum+=j;7         return j;8 }9 int main()10 {11     int sum=0;12     for(int i=0;i<=10;i++)13     {14         sum+=add(i);15     }16     printf("%d\n",sum);17     return 0;                                                                                                                                                                                                18 }

gdb调试过程

进入gdb

输入指令

 gdb test1.exe.debug

出现一堆信息后左下角出现(gdb)说明进入了调试过程
输入:ctrl + d quit 命令即可退出

查看代码信息

  • 指令:l n

以第n行为中心,显示10行代码

  • 指令l
从上次显示的代码结尾开始,显示10行
展示:
输入 l  5,显示周围10行
再输入l,显示接下来10行,但是因为我代码不足10行所以就只显示剩下的部分
l+函数名,以该函数为中心,显示10代码
注意gdb会自动记录你最近一次的指令,如果你直接输入回车,他就会执行上一次的指令

断点:

  • b +行号
在该行设置一个断点
(gdb) b 12
Breakpoint 1 at 0x40055a: file test1.c, line 12.

gdb提醒我们 把断电设置到了test1.c文件的第12行

  • b +函数
在该函数定义的开头设置一个断点
(gdb) b add
Breakpoint 2 at 0x400524: file test1.c, line 4.

gdb提醒我们 把断电设置到了test1.c文件的第12行,因为add函数的定义是从第四行开始的

b是break的缩写,我们用break代替b也是可以的

(gdb) break 16
Breakpoint 3 at 0x40057a: file test1.c, line 16.
  • info b

显示断点信息

(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x000000000040055a in main at test1.c:12
2       breakpoint     keep y   0x0000000000400524 in add at test1.c:4
3       breakpoint     keep y   0x000000000040057a in main at test1.c:16
  1. Num那一列是断点的编号
  2. type那一列是类型(断点)
  3. Enb表示断点关闭或启动状态
  4. what是断点位置
  • d+断点编号

表示删除该断点,d是delete的缩写,二者可以相互替代

展示:可以发现我们成功删除了第三个断点

(gdb) delete 3
(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x000000000040055a in main at test1.c:12
2       breakpoint     keep y   0x0000000000400524 in add at test1.c:4
  • disable+断点编号

关闭该断点,注意是关闭不是删除

展示:可以发现此时2号断点的状态变成了关闭

(gdb) disable 2
(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x000000000040055a in main at test1.c:12
2       breakpoint     keep n   0x0000000000400524 in add at test1.c:4
  • enable+断点编号

开启断点,与disable相对

可以发现此时2号断点的状态变成了开启

(gdb) enable 2
(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x000000000040055a in main at test1.c:12
2       breakpoint     keep y   0x0000000000400524 in add at test1.c:4

运行:

  • r:

run的缩写,使用run也可以

开始运行程序,直到程序结束或遇到断点

我们先把断点关了

输入r指令,发现程序直接运行到结束
(gdb) r
Starting program: /home/qingguo/project7_gdb/test1.exe.debug 
220

再把断点都打开,发现程序停在了第一个断电处

(gdb) r
Starting program: /home/qingguo/project7_gdb/test1.exe.debug Breakpoint 1, main () at test1.c:12
12	    for(int i=0;i<=10;i++)

我们继续输入r

The program being debugged has been started already.
Start it from the beginning? (y or n) 

它说程序已经启动了,是否要重新启动,可以看出r不能帮我们进入后面的程序

  • c

是continue的缩写

继续执行程序直到程序结束,或者遇到下一个断点,

(gdb) continue
Continuing.Breakpoint 2, add (i=0) at test1.c:4
4	    int sum=0;
  • 逐过程-n

我们重新执行r,再输入n

我们在第12行设置了断点,所一程序现在这里停了下来,接着输入n,从第12行进入了第14行,这是因为13行是一个上括号{,这在gdb看来是不需要进行操作的,所以跳过了

Breakpoint 1, main () at test1.c:12
12	    for(int i=0;i<=10;i++)
(gdb) n
14	        sum+=add(i);
(gdb) 

        相信大家都记得逐过程的特点是不会进入函数内部,而会把调用函数所在语句当成一条语句。我们来演示一下

(gdb) n
14	        sum+=add(i);
(gdb) nBreakpoint 2, add (i=0) at test1.c:4
4	    int sum=0;

唉?不是,哥们,怎么进去了,这怎么想都进不去吧

        其实是因为我们最开始在add函数里放了断点,所以输入n后在执行函数内容时程序会被停下来,我们去掉断点就好了

12	    for(int i=0;i<=10;i++)
(gdb) n
14	        sum+=add(i);
(gdb) 
12	    for(int i=0;i<=10;i++)
  • s逐语句调试

会进入函数内部

12	    for(int i=0;i<=10;i++)
(gdb) s
14	        sum+=add(i);
(gdb) s
add (i=3) at test1.c:4
4	    int sum=0;

finish执行到当前函数结束,有返回值还会告诉你返回值

(gdb) finish
Run till exit from #0  add (i=3) at test1.c:4
0x000000000040056d in main () at test1.c:14
14	        sum+=add(i);
Value returned is $1 = 6
  •  until +行数 

输入r后程序开始运行,在输入until+行数可以直接运行到该行

(gdb) until 14
main () at test1.c:14
14	        sum+=add(i);

查看变量:

  • p+变量名

显示该变量的值,

12	    for(int i=0;i<=10;i++)
(gdb) p i
$2 = 3
  • display+变量名

跟踪查看一个变量,每次停下来都显示它的值

(gdb) display i
5: i = 5
(gdb) display sum
6: sum = 35
(gdb) n
14	        sum+=add(i);
6: sum = 35
5: i = 6
  • undisplay+编号

取消对先前设置的那些变量的跟踪

可以发现每次显示的i和sun左边都有一个编号,一个是5一个是6,我们输入undisplay 5

(gdb) n
12	    for(int i=0;i<=10;i++)
6: sum = 84
  • set var
在程序运行时修改变量的值
(gdb) set var i=8
(gdb) p i
$5 = 8
  • breaktrace(bt)
查看各级函数调用及参数
(gdb) bt
#0  main () at test1.c:14

我们现在再main函数中,下面使用until跳转到add里

(gdb) bt
#0  add (i=0) at test1.c:4
#1  0x000000000040056d in main () at test1.c:14

可以看到#0后面是add函数,而#1后面是main函数,说明main函数里调用了add,并且我们当前处于add中

  • infoi) locals
查看当前栈帧局部变量的值
(gdb) info locals
sum = 0
(gdb) i locals
sum = 0

        我们今天学习了gdb调试时会用到的十几条指令,下去记得好好复习,才能在以后调试的时候合理使用。


🥰创作不易,你的支持对我最大的鼓励🥰
🪐~ 点赞收藏+关注 ~

e3ff0dedf2ee4b4c89ba24e961db3cf4.gif


http://www.ppmy.cn/server/43161.html

相关文章

记录一次Netty的WSS异常

概述 业务场景 应用通过 WSS 客户端连接三方接口。在高并发压测时&#xff0c;出现了请求服务器写入失败的异常&#xff0c;该异常是偶发&#xff0c;出现的概率不到千分之一&#xff0c;异常如下图所示。 问题概述 注意&#xff1a; 因为握手是通过 http 协议进行的。所以…

线程池以及日志类的实现

目录 线程池: 日志类: 可变参数以及相关函数 1.va_list 2. va_start 3. va_end 日志Log类 线程池 线程池: 是一种线程使用模式。线程过多会带来调度开销&#xff0c;进而影响缓存局部性和整体性能。而线程池维护着多个线程&#xff0c;等待着 监督管理者分配可并发执行…

剖析 OceanBase 应对高并发的技术策略

推荐一个AI网站&#xff0c;免费使用豆包AI模型&#xff0c;快去白嫖&#x1f449;海鲸AI 在当今互联网时代&#xff0c;高并发场景下的数据库处理能力成为了许多应用的关键需求。为了满足用户对快速响应和高吞吐量的期望&#xff0c;数据库系统需要采用一系列技术来优化并发性…

【计算机网络】初识Tcp协议

&#x1f4bb;文章目录 &#x1f4c4;前言Tcp基础概念Tcp 的报文格式三次握手四次挥手 Tcp的滑动窗口机制概念超时重传机制高速重传 TCP传输控制机制流量控制拥堵控制慢启动 Tcp的性能优化机制延迟应答捎带应答 &#x1f4d3;总结 &#x1f4c4;前言 TCP三次握手、四次挥手&…

lllllllllll

;llllllllllllllllllll

工作学习的电脑定时关机,定时重启,定时提醒

可以直接下载工具&#xff1a; 定时自动关机 大家好&#xff0c;&#xff01; 在我们学习与工作时&#xff0c;经常会遇到想要在完成一个任务后&#xff0c;再关闭电脑或对电脑重启&#xff0c;但这个时间点&#xff0c;操作电脑的人可能不能在电脑旁边&#xff0c;这样就需要…

点云AABB、OBB包围盒计算显示

目录 一、简介 1)AABB包围盒 2)OBB包围盒 二、计算代码 三、加载计算结果

【建议收藏】30个较难Python脚本,纯干货分享

本篇较难&#xff0c;建议优先学习上篇 &#xff1b;20个硬核Python脚本-CSDN博客 接上篇文章&#xff0c;对于Pyhon的学习&#xff0c;上篇学习的结束相信大家对于Pyhon有了一定的理解和经验&#xff0c;学习完上篇文章之后再研究研究剩下的30个脚本你将会有所成就&…