GDB调式工具学习笔记---断点
在前文,作者学习到了GDB调试工具单步执行和跟踪函数调用的功能,但是很多时候我们会面临这么一种局面,那就是我们代码量超级大,而且我们已经可以通过经验大概确定BUG出现的范围,只是不确定具体位置,此时如果只是使用之前的技巧,从main函数开始一步一步的往下执行,作者选择回家种地,开个玩笑,因为作者种地都不会。。。咳咳,所以断点的出现就很有必要,我们只需要在我们怀疑的代码段之前打上一个断点,gdb调试工具运行到断点的时候,会暂停住等待我们的下一步指令,下面就让我们一起来学习,出来吧,就是你了——皮卡丘。。。(GDB之断点)。
2.断点
2.1 示例代码
还是熟悉的地点,还是熟悉的配方,今天老八。。。今天作者给大家表演一个手撕代码,兄弟们,奥里给。
#include <stdio.h>
int main(void)
{
int sum = 0, i = 0;
char input[5];
while(1)
{
scanf("%s", input);
for(i=0; input[i]!='\0'; i++)
{
sum = sum * 10 + input[i] - '0';
}
printf("res = %d\n", sum);
}
return 0;
}
上述代码的功能,作者说了一堆,其实就是将字符串数字转换成整型数字,即"123"===>123。我们编译运行一下,看看输出结果。
zz@ubuntu:~/Project/gdb_study$ vim demo2.c
zz@ubuntu:~/Project/gdb_study$ zz@ubuntu:~/Project/gdb_study$ gcc -g demo2.c -o demo2
zz@ubuntu:~/Project/gdb_study$ ./demo2
123
res = 123
123
res = 123123
234
res = 123123234
结果一目了然,第一次完全符合我们的预期,但是第二次以后,结果就偏离正常轨道了,这是怎么肥四呢,大佬请闭嘴,因为作者该登场了。
2.2 常用命令
2.2.1 display和undisplay
跟踪显示某个变量的值,使用后者取消跟踪
zz@ubuntu:~/Project/gdb_study$ gdb demo2
...
(gdb) start
Temporary breakpoint 1 at 0x1169: file demo2.c, line 4.
Starting program: /home/zz/Project/gdb_study/demo2
Temporary breakpoint 1, main () at demo2.c:4
4 {
(gdb) i locals
sum = 1431654528
i = 21845
input = "\377\377\177\000"
(gdb) n
5 int sum = 0, i = 0;
(gdb) i locals
sum = 1431654528
i = 21845
input = "\377\377\177\000"
(gdb) display sum
1: sum = 1431654528
(gdb) n
10 scanf("%s", input);
1: sum = 0
还是熟悉的和书上写得不一样,但是没关系,不影响我们分析,可以看到,程序开始会停留在main函数的’{'处,此时函数局部参数都刚被分配内存,但是值还未被初始化,这是不是说明,即使一个函数的局部参数在定义的时候初始化了,也还是先分配内存在赋值,而不是分配内存的时候就把数值写入了,值得思考,作者回去查一下。
2.2.2 break(b)
给程序添加断点
(gdb) l
5 int sum = 0, i = 0;
6 char input[5];
7
8 while(1)
9 {
10 scanf("%s", input);
11 for(i=0; input[i]!='\0'; i++)
12 {
13 sum = sum * 10 + input[i] - '0';
14 }
(gdb) break 10
Breakpoint 2 at 0x555555555192: file demo2.c, line 10.
这里我们使用break 10
命令将断点打在程序的第10行,等待下一步命令。
(gdb) break 10
Breakpoint 2 at 0x555555555192: file demo2.c, line 10.
(gdb) continue
Continuing.
123
res = 123
Breakpoint 2, main () at demo2.c:10
10 scanf("%s", input);
1: sum = 123
2.2.3 continue(c)
连续运行
(gdb)
10 scanf("%s", input);
1: sum = 0
(gdb) b 10
Breakpoint 2 at 0x555555555192: file demo2.c, line 10.
(gdb) continue
Continuing.
123
res = 123
Breakpoint 2, main () at demo2.c:10
10 scanf("%s", input);
1: sum = 123
(gdb) continue
Continuing.
234
res = 123234
Breakpoint 2, main () at demo2.c:10
10 scanf("%s", input);
1: sum = 123234
这里我们可以看到,运行continue
命令之后,程序继续向下执行,因为处于while循环之中,所以在此来到了断点处sum的值变为了123,再次执行命令,错误就出现了,这是聪明的我们可以一下就知道了bug出现的地方,咳咳,心机之娃一直摸你肚子,是你sum变量初始化。没错,我们可以清晰的发现第一次运行的之前,sum的值为0,但是执行第二次循环的时候,sum的值为第一次的结果,并未重新赋值为0,这导致了程序的错误,那么为了验证我们的猜想,我们手动将sum设置为0,看看结果是否正确。
(gdb) set var sum=0
(gdb) continue
Continuing.
2345
res = 2345
Breakpoint 2, main () at demo2.c:10
10 scanf("%s", input);
1: sum = 2345
果然如我们所料,一切尽在掌握中。
2.2.4 disable
暂时禁用某些断点
随着我们进行深入的调试,断点会越来越多,程序运行的时候和便秘一样,搞得我们十分难受,我们希望可以暂时停用一些断点,此时我们可以下怒用info breakpoints
命令展示所有的断点,然后使用disable breakpoints 断点号
命令暂时停用某些断点。
(gdb) i breakpoints
Num Type Disp Enb Address What
2 breakpoint keep y 0x0000555555555192 in main at demo2.c:10
breakpoint already hit 3 times
3 breakpoint keep y 0x00005555555551b3 in main at demo2.c:13
(gdb) disable breakpoints 2
(gdb) continue
Continuing.
123
Breakpoint 3, main () at demo2.c:13
13 sum = sum * 10 + input[i] - '0';
1: sum = 2345
(gdb)
可以看到在10行的2号断点被我们禁用,执行continue
命令后,gdb停在了位于13行的3号断点处。
2.2.6 enble
重新启用断点
人生如戏,断点和恋爱一样,当你有了备胎的时候,你以为原配没用了,你把他抛在一边不管不顾,可是突然有天你发现,对象还是原配好,怎么办,你舔着脸去和原配说:我最爱的还是你,我们和好吧。你会得到两个大嘴巴子,但是断点不会这么对你,断点还爱你。
(gdb) enable breakpoints 2
(gdb) i breakpoints
Num Type Disp Enb Address What
2 breakpoint keep y 0x0000555555555192 in main at demo2.c:10
breakpoint already hit 3 times
3 breakpoint keep y 0x00005555555551b3 in main at demo2.c:13
breakpoint already hit 1 time
(gdb) disable breakpoints 3
(gdb) continue
Continuing.
res = 2345123
Breakpoint 2, main () at demo2.c:10
10 scanf("%s", input);
1: sum = 2345123
(gdb)
可以看到,这里我们和2号断点重归于好,至于3号断点,我们将它暂时禁用,你问我为什么不永远删除,这我们没法回答你,你懂的。
2.2.5 delete
删除断点
当我们十分确定某个断点是没用的时候,我们要做一个渣男,无情的将它抛弃,相当的无情!!!
(gdb) delete breakpoints 3
(gdb) i breakpoints
Num Type Disp Enb Address What
2 breakpoint keep y 0x0000555555555192 in main at demo2.c:10
breakpoint already hit 4 times
(gdb)
2.2.6 run
你删除了小三、小四,哭着说要重新做人,人生不能重来,但是程序可以,命令run
可以让程序重新从头运行。
(gdb) i locals
sum = 2345123
i = 3
input = "123\000"
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/zz/Project/gdb_study/demo2
Breakpoint 2, main () at demo2.c:10
10 scanf("%s", input);
1: sum = 0
(gdb)
我们可以看到,命令运行之后,果然给你一次重来的机会,重新停在了2号断点出
2.2.7 if
你说你见惯了风雨,玩够了,现在想找个人结婚,安安稳稳,原配被你伤透了,咬牙切齿说除非太阳从西面出来,但是我们可爱的断点不会,它会跑到你面前红着脸,低着头望着脚尖说if sum != 0
(gdb) delete breakpoints 2
(gdb) i breakpoints
No breakpoints or watchpoints.
(gdb) break 10 if sum != 0
Breakpoint 4 at 0x555555555192: file demo2.c, line 10.
(gdb) continue
Continuing.
123
res = 123
Breakpoint 4, main () at demo2.c:10
10 scanf("%s", input);
1: sum = 123
可以看到,非常简单,你的断点妹妹简直是白给,断点再一次生效,程序再一次停在了断点处,但是你非常的坏,你想挑逗下断点妹妹,你将sum的值手动设置为0,我劝你善良。
(gdb) set var sum=0
(gdb) continue
Continuing.
0
res = 0
q
res = 65
Breakpoint 4, main () at demo2.c:10
10 scanf("%s", input);
1: sum = 65
(gdb)
程序不知道停在了哪里,但是可以肯定的是,没有陷入死循环,因为作者用top
命令监控了cpu和内存的使用情况,并没有爆满,过了一段时间也没有。
2.3 小结
好了,到此该总结以下渣男都用了哪些命令
命令 | 函数名 |
---|---|
break | 设置断点(break 行号:在某一行设置断点;break 函数名:在某个函数开头设置断点) |
break…if… | 当if后面的条件成立,设置断点 |
continue | 从当前开始连续运行 |
delete | 删除断点 |
display | 跟踪显示某个变量 |
undisable | 取消跟踪某个变量 |
disable | 暂时停用某个断点 |
enable | 重新启用某个断点 |
run | 从头运行程序 |