前言
上篇《GDB 调试指南 - 启动调试 https://www.yanbinghu.com/2019/01/08/62137.html 》我们讲到了 GDB 启动调试的多种方式, 分别应用于多种场景. 今天我们来介绍一下断点设置的多种方式.
为何要设置断点
在介绍之前, 我们首先需要了解, 为什么需要设置断点. 我们在指定位置设置断点之后, 程序运行到该位置将会 "暂停", 这个时候我们就可以对程序进行更多的操作, 比如查看变量内容, 堆栈情况等等, 以帮助我们调试程序.
查看已设置的断点
在学习断点设置之前, 我们可以使用 info breakpoints 查看已设置断点:
- 1info breakpoints
- 2Num Type Disp Enb Address What
- 31 breakpoint keep y 0x00000000004005fc in printNum2 at test.c:17
- 4 breakpoint already hit 1 time
- 52 hw watchpoint keep y a
- 6 breakpoint already hit 1 time
- 7 ignore next 3 hits
它将会列出所有已设置的断点, 每一个断点都有一个标号, 用来代表这个断点. 例如, 第 2 个断点设置是一个观察点, 并且会忽略三次.
断点设置
断点设置有多种方式, 分别应用于不同的场景. 借助示例程序进行一一介绍:
- //test.c
- #include<stdio.h>
- void printNum(int a)
- {
- printf("printNum\n");
- while(a > 0)
- {
- printf("%d\n",a);
- a--;
- }
- }
- void printNum2(int a,int num)
- {
- printf("printNum\n");
- while(a > num && a>0)
- {
- printf("%d\n",a);
- a--;
- }
- }
- int div(int a,int b)
- {
- printf("a=%d,b=%d\n",a,b);
- int temp = a/b;
- return temp;
- }
- int main(int argc,char *argv[])
- {
- printNum2(12,5);
- printNum(10);
- div(10,0);
- return 0;
- }
编译:
1gcc -g -o test test.c
注意, 编译时需要带上 - g 参数, 具体原因参见《GDB 调试指南 - 启动调试 https://www.yanbinghu.com/2019/01/08/62137.html 》.
微信公众号 [编程珠玑] : 专注但不限于分享计算机编程基础, Linux,C 语言, C++, 算法, 数据库等编程相关[原创] 技术文章, 号内包含大量经典电子书和视频学习资源. 欢迎一起交流学习, 一起修炼计算机 "内功", 知其然, 更知其所以然.
根据行号设置断点
1b 10 #break 可简写为 b
或者
1
b test.c:10
程序运行到第 10 行的时候会断住.
根据函数名设置断点
同样可以将断点设置在函数处:
1b printNum
程序在调用到 printNum 函数的时候会断住.
根据条件设置断点
假设程序某处发生崩溃, 而崩溃的原因怀疑是某个地方出现了非期望的值, 那么你就可以在这里断点观察, 当出现该非法值时, 程序断住. 这个时候我们可以借助 gdb 来设置条件断点, 例如:
1break test.c:23 if b==0
当在 b 等于 0 时, 程序将会在第 23 行断住.
它和 condition 有着类似的作用, 假设上面的断点号为 1, 那么:
1condition 1 b==0
会使得 b 等于 0 时, 产生断点 1. 而实际上可以很方便地用来改变断点产生的条件, 例如, 之前设置 b==0 时产生该断点, 那么使用 condition 可以修改断点产生的条件.
根据规则设置断点
例如需要对所有调用 printNum 函数都设置断点, 可以使用下面的方式:
1rbreak printNum*
所有以 printNum 开头的函数都设置了断点. 而下面是对所有函数设置断点:
- # 用法: rbreak file:regex
- rbreak .
- rbreak test.c:. #对 test.c 中的所有函数设置断点
- rbreak test.c:^print #对以 print 开头的函数设置断点
设置临时断点
假设某处的断点只想生效一次, 那么可以设置临时断点, 这样断点后面就不复存在了:
1tbreak test.c:l0 #在第 10 行设置临时断点
跳过多次设置断点
假如有某个地方, 我们知道可能出错, 但是前面 30 次都没有问题, 虽然在该处设置了断点, 但是想跳过前面 30 次, 可以使用下面的方式:
1ignore 1 30
其中, 1 是你要忽略的断点号, 可以通过前面的方式查找到, 30 是需要跳过的次数. 这样设置之后, 会跳过前面 30 次. 再次通过 info breakpoints 可以看到:
- 1Num Type Disp Enb Address What
- 21 breakpoint keep y 0x00000000004005e8 in printNum2 at test.c:16
- 3 ignore next 30 hits
根据表达式值变化产生断点
有时候我们需要观察某个值或表达式, 知道它什么时候发生变化了, 这个时候我们可以借助 watch 命令. 例如:
1watch a
这个时候, 让程序继续运行, 如果 a 的值发生变化, 则会打印相关内容, 如:
- Hardware watchpoint 2: a
- Old value = 12
- New value = 11
但是这里要特别注意的是, 程序必须运行起来, 否则会出现:
1No symbol "a" in current context.
因为程序没有运行, 当前上下文也就没有相关变量信息.
rwatch 和 awatch 同样可以设置观察点前者是当变量值被读时断住, 后者是被读或者被改写时断住.
禁用或启动断点
有些断点暂时不想使用, 但又不想删除, 可以暂时禁用或启用. 例如:
- disable #禁用所有断点
- disable bnum #禁用标号为 bnum 的断点
- enable #启用所有断点
- enable bnum #启用标号为 bnum 的断点
- enable delete bnum #启动标号为 bnum 的断点, 并且在此之后删除该断点
断点清除
断点清除主要用到 clear 和 delete 命令. 常见使用如下:
- clear #删除当前行所有 breakpoints
- clear function #删除函数名为 function 处的断点
- clear filename:function #删除文件 filename 中函数 function 处的断点
- clear lineNum #删除行号为 lineNum 处的断点
- clear f:lename:lineNum #删除文件 filename 中行号为 lineNum 处的断点
- delete #删除所有 breakpoints,watchpoints 和 catchpoints
- delete bnum #删除断点号为 bnum 的断点
总结
本文介绍了常见的断点设置方法, 断点设置之后, 可以便于我们后期观察变量, 堆栈等信息, 为进一步的定位与调试做准备.
最新内容: GDB 调试指南 - 断点设置 https://www.yanbinghu.com/2019/02/24/44483.html
来源: https://www.cnblogs.com/bianchengzhuji/p/10445882.html