🪐🪐🪐欢迎来到程序员餐厅💫💫💫
主厨:邪王真眼
主厨的主页:Chef‘s blog
所属专栏:青果大战linux
总有光环在陨落,总有新星在闪烁
gdb是什么
gdn是linux下的一个开源的命令行调试器,它可以帮助程序员在调试程序时跟踪程序运行过程中的错误。它可以用于C、C++、Fortran、汇编语言等多种编程语言。通过GDB,你可以在程序运行时中断程序的执行,查看和修改变量的值,设置断点,单步执行代码,打印函数调用栈等操作。
基于gdb强大的调试能力,我们可以快速定位程序运行过程中的错误。现在就让我们来学习它的使用吧。
调试信息
#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
readelf -S test1.exe |grep debug
以上指令,管道左侧用于输出可执行文件内的内容,右侧用于筛选含有debug字段,最后该指令什么也没有输出,说明gcc默认生成的可执行文件不包含debug信息,无法被调试。
- 得出结论:
gcc默认发布的是release版本,要想发布debug版本,必须在源代码生成二进制程序的时候, 加上 -g 选项
gcc -o test1.exe.debug test1.c -std=c99 -g
文件正常生成并且运行正确
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
查看代码信息
- 指令:l n
以第n行为中心,显示10行代码
- 指令l
从上次显示的代码结尾开始,显示10行
断点:
- 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
- Num那一列是断点的编号
- type那一列是类型(断点)
- Enb表示断点关闭或启动状态
- 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也可以
开始运行程序,直到程序结束或遇到断点
我们先把断点关了
(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中
- info(i) locals: