【gdb】- 断点调试

水深无声 2022-05-30 06:49 504阅读 0赞

断点调试实例:

  1. #include <stdio.h>
  2. int main(void)
  3. {
  4. int sum = 0, i = 0;
  5. char input[5];
  6. while (1){
  7. scanf("%s",input);
  8. for(i = 0;input[i] != '\0'; i++)
  9. sum = sum*10 + input[i] - '0';
  10. printf("input=%d\n", sum);
  11. }
  12. return 0;
  13. }

这个程序的作用是:首先从键盘读入一串数字存到字符数组input中,然后转换成整型存到sum中,然后打印出来,一直这样循环下去。scanf(“%s”, input);这个调用的功能是等待用户输入一个字符串并回车,scanf把其中第一段非空白(非空格、Tab、换行)的字符串保存到input数组中,并自动在末尾添加’\0’。接下来的循环从左到右扫描字符串并把每个数字累加到结果中,例如输入是”2345”,则循环累加的过程是(((0*10+2)*10+3)*10+4)*10+5=2345。注意字符型的’2’要减去’0’的ASCII码才能转换成整数值2。下面编译运行程序看看有什么问题:

  1. [zhangsan@localhost study-c]$ gcc -Wall -g gdb2.c -o gdb2
  2. [zhangsan@localhost study-c]$ ./gdb2
  3. 123
  4. input=123
  5. 345
  6. input=123345

第一次是对的,第二次就不对。可是这个程序我们并没有忘了赋初值,不仅sum赋了初值,连不必赋初值的i都赋了初值。下面来调试:

  1. [zhangsan@localhost study-c]$ gdb gdb2
  2. GNU gdb (GDB) 7.6.1
  3. Copyright (C) 2013 Free Software Foundation, Inc.
  4. ...
  5. Reading symbols from /home/zhangsan/study-c/gdb2...done.
  6. (gdb) start
  7. Temporary breakpoint 1 at 0x40053c: file gdb2.c, line 6.
  8. Starting program: /home/zhangsan/study-c/gdb2
  9. Temporary breakpoint 1, main () at gdb2.c:6
  10. 6 int sum = 0, i = 0;

有了上一次的经验,sum被列为重点怀疑对象,我们可以用display命令使得每次停下来的时候都显示当前sum的值,然后继续往下走:

  1. (gdb) display sum
  2. 1: sum = 0
  3. (gdb) n
  4. 10 scanf("%s",input);
  5. 1: sum = 0
  6. (gdb)
  7. 123
  8. 11 for(i = 0;input[i] != '\0'; i++)
  9. 1: sum = 0

undisplay命令可以取消跟踪显示,变量sum的编号是1,可以用undisplay 1命令取消它的跟踪显示。这个循环应该没有问题,因为上面第一次输入时打印的结果是正确的。如果不想一步一步走这个循环,可以用break命令(简写为b)在第10行设一个断点(Breakpoint):

  1. (gdb) l
  2. 6 int sum = 0, i = 0;
  3. 7 char input[5];
  4. 8
  5. 9 while (1){
  6. 10 scanf("%s",input);
  7. 11 for(i = 0;input[i] != '\0'; i++)
  8. 12 sum = sum*10 + input[i] - '0';
  9. 13 printf("input=%d\n", sum);
  10. 14 }
  11. 15 return 0;
  12. (gdb) break 10
  13. Breakpoint 2 at 0x40054a: file gdb2.c, line 10.

break命令的参数也可以是函数名,表示在某个函数开头设断点。现在用continue命令(简写为c)连续运行而非单步运行,程序到达断点会自动停下来,这样就可以停在下一次循环的开头:

  1. (gdb) continue
  2. Continuing.
  3. 123
  4. input=123
  5. Breakpoint 2, main () at gdb2.c:10
  6. 10 scanf("%s",input);
  7. 1: sum = 123

然后输入新的字符串准备转换:

  1. (gdb) n
  2. 234
  3. 11 for(i = 0;input[i] != '\0'; i++)
  4. 1: sum = 123

问题暴露出来了,新的转换应该再次从0开始累加,而sum现在已经是123了,原因在于新的循环没有把sum归零。可见断点有助于快速跳过没有问题的代码,然后在有问题的代码上慢慢走慢慢分析,“断点加单步”是使用调试器的基本方法。至于应该在哪里设置断点,怎么知道哪些代码可以跳过而哪些代码要慢慢走,也要通过对错误现象的分析和假设来确定,以前我们用printf打印中间结果时也要分析应该在哪里插入printf,打印哪些中间结果,调试的基本思路是一样的。一次调试可以设置多个断点,用info命令可以查看已经设置的断点:

  1. (gdb) break 13
  2. Breakpoint 3 at 0x4005a2: file gdb2.c, line 13.
  3. (gdb) info breakpoints
  4. Num Type Disp Enb Address What
  5. 2 breakpoint keep y 0x000000000040054a in main at gdb2.c:10
  6. breakpoint already hit 1 time
  7. 3 breakpoint keep y 0x00000000004005a2 in main at gdb2.c:13

每个断点都有一个编号,可以用编号指定删除某个断点:

  1. (gdb) delete breakpoints 2
  2. (gdb) info breakpoints
  3. Num Type Disp Enb Address What
  4. 3 breakpoint keep y 0x00000000004005a2 in main at gdb2.c:13

有时候一个断点暂时不用可以禁用掉而不必删除,这样以后想用的时候可以直接启用,而不必重新从代码里找应该在哪一行设断点:

  1. (gdb) disable breakpoints 3
  2. (gdb) info breakpoints
  3. Num Type Disp Enb Address What
  4. 3 breakpoint keep n 0x00000000004005a2 in main at gdb2.c:13
  5. (gdb) enable 3
  6. (gdb) info breakpoints
  7. Num Type Disp Enb Address What
  8. 3 breakpoint keep y 0x00000000004005a2 in main at gdb2.c:13
  9. (gdb) delete breakpoints
  10. Delete all breakpoints? (y or n) y
  11. (gdb) info breakpoints
  12. No breakpoints or watchpoints.

gdb的断点功能非常灵活,还可以设置断点在满足某个条件时才激活,例如我们仍然在循环开头设置断点,但是仅当sum不等于0时才中断,然后用run命令(简写为r)重新从程序开头连续运行:

  1. (gdb) break 10 if sum!=0
  2. Breakpoint 4 at 0x40054a: file gdb2.c, line 10.
  3. (gdb) info breakpoints
  4. Num Type Disp Enb Address What
  5. 4 breakpoint keep y 0x000000000040054a in main at gdb2.c:10
  6. stop only if sum!=0
  7. (gdb) run
  8. The program being debugged has been started already.
  9. Start it from the beginning? (y or n) y
  10. Starting program: /home/zhangsan/study-c/gdb2
  11. 123
  12. input=123
  13. Breakpoint 4, main () at gdb2.c:10
  14. 10 scanf("%s",input);
  15. 1: sum = 123

结果是第一次执行scanf之前没有中断,第二次却中断了。总结一下本节用到的gdb命令:

gdb基本命令2:

命令 描述
break(或b) 行号 在某一行设置断点
break 函数名 在某个函数开头设置断点
break … if … 设置条件断点
continue(或c) 从当前位置开始连续运行程序
delete breakpoints 断点号 删除断点
display 变量名 跟踪查看某个变量,每次停下来都显示它的值
disable breakpoints 断点号 禁用断点
enable 断点号 启用断点
info(或i) breakpoints 查看当前设置了哪些断点
run(或r) 从头开始连续运行程序
undisplay 跟踪显示号 取消跟踪显示

发表评论

表情:
评论列表 (有 0 条评论,504人围观)

还没有评论,来说两句吧...

相关阅读

    相关 GDB/MI断点信息

    当GDB报告有关断点、跟踪点、观察点或捕获点的信息时,它使用具有以下字段的元组: `number` 断点编号。 `type` 断点的类型。对于普通断点,这将是 '断点'

    相关 GDB 调试

    GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具。或许,各位比较喜欢那种图形界面方式的,像VC、BCB等IDE的调试,但如果你是在UNIX平台下做软件,你会发现G

    相关 gdb调试

    在用gcc编程的时候可能会出bug,这时候就可以通过gdb这个工具进行调试,gdb调试的一定是直接有\.c 文件生成的bebug版本的可执行文件,否则,进入gdb之后敲入lis