【Linux】gdb调试器的使用

文章目录

一、gdb简介

二、调试前的准备

1、生成调试文件

2、启动 gdb 

三、gdb 使用方法 

1、查看源代码

2、设置 / 查看断点(多种方式设置断点)

方法一

方法二

方法三

3、run 

4、删除断点、断点无效

5、逐过程调试(以函数为单位)

6、逐语句调试

7、查看调用链

8、查看变量值

单次查看

长显示

9、指定运行到某行

10、 执行完当前函数

11、从一个断点运行到另一个断点

四、gdb 指令总结


一、gdb简介

        GDB 全称“GNU symbolic debugger”,是 Linux 下常用的程序调试器。发展至今,GDB 已经迭代了诸多个版本,当下的 GDB 支持调试多种编程语言编写的程序,包括 C、C++、Go 等等。实际场景中,GDB 更常用来调试 C 和 C++ 程序。一般来说,GDB主要帮助我们完成以下四个方面的功能:

  1. 启动你的程序,可以按照你的自定义的要求随心所欲的运行程序。
  2. 在某个指定的地方或条件下暂停程序。
  3. 当程序被停住时,可以检查此时你的程序中所发生的事。
  4. 在程序执行过程中修改程序中的变量或条件,将一个bug产生的影响修正从而测试其他bug。

二、调试前的准备

程序源代码如下:

  1 #include
  2 
  3 int add(int x)
  4 {
  5   int ret=0;
  6   printf("start\n");
  7 
  8   for(int i=0;i

Makefile配置文件如下:

  1 test:test.c
  2   gcc -o test test.c -g -std=c99                                                                                                                
  3 .PHONY:clean
  4 clean:
  5   rm -f test 

1、生成调试文件

        不调试的情况下,使用 gcc 指令编译文件一般是:gcc test.c -o test  但是,如果要调试,那么就必须要加上 -g 指令。其原理用 Visual Studio Code 环境下来类比,使用该 IDE 熟悉的话便知道 发行一个程序的时候,可以有 release 版本和 debug 版本,只有我们选择 debug 版本的时候,程序才可以被调试,否则不行。这里也类似,加上 -g 就相当于生成 debug 版本的程序,不加就默认是 release 版本的。其正确指令如下:

gcc test.c -o test -g -std=c99

        同时,我们可以思考一下为什么 gcc 默认发行 release 版本的程序,其实是因为 debug 版本由于需要一些调试信息,所以就需要更大的内存,运行起来会更慢。我们可以分别生成 release 版本和 debug 版本的文件,进行对比,确实 debug 版本所占空间更大。
        当然,我们也可以通过查看生成的两个二进制文件,其里面是否包含调试信息。readefl 指令可以查看二进制文件里的一些信息。下图也可以看出,确实 debug 版本里包含调试信息。

【Linux】gdb调试器的使用_第1张图片

        此外,这里要特别说明一下,配置文件中,第一个依赖方法里面  -std=c99 的含义。c99 是 C 语言的一个标准,在这个标准里规定了 可以在循环控制条件里面定义变量,如 for(int i=0;i<10;i++) 。如果不使用   -std=c99  那么就会造成编译出错,如下,所以必须加上。
 【Linux】gdb调试器的使用_第2张图片

2、启动 gdb 

        启动 gdb 的指令非常简单,如下即可启动生成的 test 文件。

gdb test

        如下,出现这样的界面,就代表进入gdb调试,就可以使用各种指令进行调试:

【Linux】gdb调试器的使用_第3张图片

三、gdb 使用方法 

1、查看源代码

l  行号:( l 是 list 的简写),显示源代码,接着上次的位置往下列,每次列10行。在后面加上行号可以指定查看该行号附近的代码。

        测试效果如下:

(gdb) l
9	  {
10	    ret+=i;
11	  }
12	  printf("end\n");
13	  return ret;
14	}
15	
16	int main()
17	{
18	  int x=100;
(gdb) l 1
1	#include
2	
3	int add(int x)
4	{
5	  int ret=0;
6	  printf("start\n");
7	
8	  for(int i=0;i

2、设置 / 查看断点(多种方式设置断点)

方法一

b  行号 :(b 是 break 的简写),在某一行打上断点。

info b   : 查看断点。

【Linux】gdb调试器的使用_第4张图片

        可以看到,断点里面有一些信息,大部分都不难理解,比如 Num是编号,Type是种类,Address是断点在内存的位置,What是断点在程序中的位置。Disp 和 Enb 可能比较难理解。
Disp:断点执行一次之后是否有效 keep:有效 dis:无效
Enb: 当前断点是否有效 y:有效 n:无效

方法二

b 文件名:行数    :在指定的文件里面打断点。

        如下,这种情况可以在多文件调试的时候使用。
【Linux】gdb调试器的使用_第5张图片

方法三

b  (文件名 : )函数名    : 指定函数打断点。

        如下,可以指定函数名打断点,断点生成的地方就是该函数开始执行的地方。
【Linux】gdb调试器的使用_第6张图片

         在多文件的时候,可以指定文件名打断点。如下,删除两个断点之后,利用指定文件名的方式在 main函数开始处 添加断点。
【Linux】gdb调试器的使用_第7张图片

3、run 

r :(run的缩写),如果有断点,执行到断点处停,如果没有断点,执行结束。(相当于 VS 里面的 F5 。)

        如果执行 r 之后,再查看断点就会发现不一样。如下,info b 显示的信息里面,多了一行 “breakpoint already hit 1 time” 说明当前断点被命中。

【Linux】gdb调试器的使用_第8张图片

4、删除断点、断点无效

disable breakpoint 断点编号: 使断点无效。

enable breakpoint 断点编号: 使断点有效。

         如下,使用 disable 指令之后,info 信息里的 Enb 变成了 n,代表该断点不可用。使用 enable 之后,变成了 y ,表示该断点可用。当然 breakpoint 可以简写为 b 。

【Linux】gdb调试器的使用_第9张图片

d  断点编号:(delete 的缩写)删除 对应编号的断点。这里不是输入行号,是断点的编号。

        如下,之前的断点编号为1,删除之后,使用 info 指令查看是没有断点的。

5、逐过程调试(以函数为单位)

 n  :(next 的缩写)    一步步调试,但是不会进入函数内部。  ( 相当于 VS 下的 F10,遇到函数调用不会进入函数。)

        如下,设置 19 行的断点之后,按下 r 运行代码,在断点处停下,然后 n 调试,显然直接执行完了 add 函数内部的内容,打印了 start 和 end。
【Linux】gdb调试器的使用_第10张图片

6、逐语句调试

s   :(step 的缩写)  一步步调试,遇到函数调用的时候,进入函数。    ( 相当于 VS 下的F11,可以进入函数内部调试。)

       如下,重新调试后,又回到了 19 行设置的断点处,此时使用 s 调试,进入了 add 函数内部,一步步执行下去,也打印了 start (红色箭头指向处)。
【Linux】gdb调试器的使用_第11张图片

7、查看调用链

bt    : 查看函数调用过程。函数调用是一个压栈的过程,可以使用bt指令来查看。

        在进入add 函数之后,使用 bt 指令查看如下,可以看到,先调用了main 函数,然后add 函数压栈。
【Linux】gdb调试器的使用_第12张图片

8、查看变量值

单次查看

p   变量:(printf 的缩写) 打印变量的值,但是只能单次打印。 

        如下,在循环执行到某一步的时候,查看一些变量、地址的值。但是由于不能一直显示,显得非常麻烦。【Linux】gdb调试器的使用_第13张图片

长显示

display  变量名: 查看某变量的值(内置类型,结构体等都可以),类似于 VS 里面的监视,一直会显示。

undisplay  编号: 取消显示某变量。

        如下,查看了 ret 的值和地址,执行 s 之后,确实会显示这两个值。然后不想看 ret 的值之后,不可以直接 undisplay ret  ,必须要 undisplay 编号,如下红色箭头。
【Linux】gdb调试器的使用_第14张图片

9、指定运行到某行

until  行数 :使程序运行到指定行数,在函数内部使用。(VS 里面没有相关功能按键。)

        比如在一个函数内部,有循环要执行很多次,就非常麻烦,此时可以使用 until 直接跳过循环到达指定行数。如下,until 12 直接跳过了 add 函数里的循环(由于 i 是在循环内部定义的,所以出了循环就监视不到了。)
【Linux】gdb调试器的使用_第15张图片

10、 执行完当前函数

finishi  :只执行完当前函数,然后停下来。

        如下,当前在 add 函数里面,执行 finishi 指令,add函数直接执行完了,等待下一步指令。
【Linux】gdb调试器的使用_第16张图片

11、从一个断点运行到另一个断点

c   :(continue 的缩写) 当前处于某函数内部的断点 A,下一个该函数内部的断点是 B,执行continue,就会运行到断点B处。

        如下,重新 r 之后,进入了第一个断点,然后continue,执行到了第二个断点处。
【Linux】gdb调试器的使用_第17张图片

四、gdb 指令总结

        如下,是一些gdb常用指令总结,有一些是在上面有所展示,有一些没有。

list/l 行号:显示binFile源代码,接着上次的位置往下列,每次列10行。
list/l 函数名:列出某个函数的源代码。
r 或 run:运行程序。
n 或 next:单条执行。
s或step:进入函数调用
break(b) 行号:在某一行设置断点
break 函数名:在某个函数开头设置断点
info break :查看断点信息。
finish:执行到当前函数返回,然后挺下来等待命令
p 变量:打印变量值。
set var:修改变量的值
continue(或c):从当前位置开始连续而非单步执行程序
run(或r):从开始连续而非单步执行程序
delete breakpoints:删除所有断点
delete breakpoints n:删除序号为n的断点
disable breakpoints:禁用断点
enable breakpoints:启用断点
info(或i) breakpoints:参看当前设置了哪些断点
display 变量名:跟踪查看一个变量,每次停下来都显示它的值
undisplay:取消对先前设置的那些变量的跟踪
until X行号:跳至X行
breaktrace(或bt):查看各级函数调用及参数
info(i) locals:查看当前栈帧局部变量的值
quit:退出gdb

你可能感兴趣的:(Linux,linux,运维,服务器)