LV073-gdb-break
默认情况下,程序不会进入调试模式,代码会瞬间从开头执行到末尾。要想观察程序运行的内部细节(例如某变量值的变化情况),可以借助 GDB 调试器在程序中的某个地方设置断点,这样当程序执行到这个地方时就会停下来。
一、断点
1. 什么是断点
断点(BreakPoint),可以理解为障碍物,人遇到障碍物不能行走,程序遇到断点就暂停执行。
2. 怎么打断点
在 GDB 调试器中对 C、C++ 程序打断点,最常用的就是 break 命令,有些场景中还会用到 tbreak 或者 rbreak 命令。
二、break
1. 语法格式
break 命令(可以用 b 代替)常用的语法格式有以下 2 种:
(gdb) break location # b location
(gdb) break ... if cond # b ... if cond1.1 b location
location 用于指定打断点的具体位置,其表示方式有多种,如表所示:
| location 的值 | 含 义 |
|---|---|
| linenum | linenum 是一个整数,表示要打断点处代码的行号。要知道,程序中各行代码都有对应的行号,可通过执行 l(小写的 L)命令看到。 |
| filename:linenum | filename 表示源程序文件名;linenum 为整数,表示具体行数。整体的意思是在指令文件 filename 中的第 linenum 行打断点。 |
| + offset - offset | offset 为整数(假设值为 2),+offset 表示以当前程序暂停位置(例如第 4 行)为准,向后数 offset 行处(第 6 行)打断点;-offset 表示以当前程序暂停位置为准,向前数 offset 行处(第 2 行)打断点。 |
| function | function 表示程序中包含的函数的函数名,即 break 命令会在该函数内部的开头位置打断点,程序会执行到该函数第一行代码处暂停。 |
| filename:function | filename 表示远程文件名;function 表示程序中函数的函数名。整体的意思是在指定文件 filename 中 function 函数的开头位置打断点。 |
1.2 b ... if cond
... 可以是表 1中所有参数的值,用于指定打断点的具体位置;cond 为某个表达式。整体的含义为:每次程序执行到 ... 位置时都计算 cond 的值,如果为 True,则程序在该位置暂停;反之,程序继续执行。
2. 使用示例
2.1 测试程序
#include <stdio.h>
#include <unistd.h>
int main(int argc, const char *argv[]) {
/* code */
int i = 0;
printf("Hello, World! \n");
while (1) {
i++;
sleep(1);
if (i > 100) {
printf("i=%d\n", i++);
i = 0;
}
}
return 0;
}我们在ubuntu中测试,所以直接用下面的命令编译:
cd ~/workspace/c-learning/02-c-basic/21-debug
gcc 025-gdb-break.c -g2.2 调试示例
✓ sumu@virtual-machine:~/workspace/c-learning/02-c-basic/21-debug [main ≡ +1 ~0 -0 !]
$ gdb a.out -q
Reading symbols from a.out...
(gdb) l # <=== 1. 查看源码
1 #include <stdio.h>
2 #include <unistd.h>
3
4 int main(int argc, const char *argv[]) {
5 /* code */
6 int i = 0;
7 printf("Hello, World! \n");
8
9 while (1) {
10 i++;
(gdb) b 6 # <=== 2. 在第6行打断点
Breakpoint 1 at 0x119c: file 010-gdb-debug-running.c, line 6.
(gdb) r # <=== 3. 运行程序,到第6行暂停
Starting program: /home/sumu/workspace/c-learning/02-c-basic/21-debug/a.out
Breakpoint 1, main (argc=1, argv=0x7fffffffce28) at 010-gdb-debug-running.c:6
6 int i = 0;
(gdb) b +1 # <=== 4. 在第6行的基础上,在第7行打上断点
Breakpoint 2 at 0x5555555551a3: file 010-gdb-debug-running.c, line 7.
(gdb) c # <=== 5. 继续运行到第7行,然后暂停(第7行还未执行)
Continuing.
Breakpoint 2, main (argc=1, argv=0x7fffffffce28) at 010-gdb-debug-running.c:7
7 printf("Hello, World! \n");
(gdb) b 11 if i>3 # <=== 6. 如果i大于3了,就在第11行打断点,程序暂停
Breakpoint 3 at 0x5555555551b3: file 010-gdb-debug-running.c, line 11.
(gdb) c # <=== 7. 程序继续执行
Continuing.
Hello, World!
# <=== 程序在第11行暂停了
Breakpoint 3, main (argc=1, argv=0x7fffffffce28) at 010-gdb-debug-running.c:11
11 sleep(1);
(gdb) p i # <=== 8. 查看i变量的值
$1 = 4 # <=== i的值为4,大于了3,所以这里有一个断点,程序暂停在sleep(1)这一行了。
(gdb) info b # <=== 9. 查看所有的断点
Num Type Disp Enb Address What
1 breakpoint keep y 0x000055555555519c in main at 010-gdb-debug-running.c:6
breakpoint already hit 1 time
2 breakpoint keep y 0x00005555555551a3 in main at 010-gdb-debug-running.c:7
breakpoint already hit 1 time
3 breakpoint keep y 0x00005555555551b3 in main at 010-gdb-debug-running.c:11
stop only if i>3
breakpoint already hit 1 time
(gdb) c # <=== 10. 再运行一次
Continuing.
# <=== 程序再次暂停在11行
Breakpoint 3, main (argc=1, argv=0x7fffffffce28) at 010-gdb-debug-running.c:11
11 sleep(1);
(gdb) p i # <=== 11. 查看i变量的值
$2 = 5 # <=== 值为5,也是大于3的,这个断点依然存在所以
(gdb)三、tbreak
1. 语法格式
tbreak 命令可以看到是 break 命令的另一个版本,tbreak 和 break 命令的用法和功能都非常相似,唯一的不同在于,使用 tbreak 命令打的断点仅会作用 1 次,即使程序暂停之后,该断点就会自动消失。tbreak 命令的使用格式和 break 完全相同,有下面两种格式:
(gdb) tbreak location # tb location
(gdb) tbreak ... if cond # tb ... if cond其中,location、... 和 cond 的含义都和 break 命令中的参数含义相同,即表 1 也同样适用于 tbreak 命令。
2. 使用示例
2.1 测试程序
#include <stdio.h>
int main(int argc, const char *argv[]) {
int i = 1;
printf("Hello, World! \n");
while (i < 30) {
i += 2;
}
printf("i = %d\n", i);
return 0;
}我们在ubuntu中测试,所以直接用下面的命令编译:
cd ~/workspace/c-learning/02-c-basic/21-debug
gcc 026-gdb-tbreak.c -g2.2 调试示例
✓ sumu@virtual-machine:~/workspace/c-learning/02-c-basic/21-debug [main ≡ +1 ~0 -0 !]
$ gdb a.out -q
Reading symbols from a.out...
(gdb) l # <=== 1. 查看源码
1 #include <stdio.h>
2
3 int main(int argc, const char *argv[]) {
4 int i = 1;
5 printf("Hello, World! \n");
6
7 while (i < 30) {
8 i += 2;
9 }
10 printf("i = %d\n", i);
(gdb) tb 8 if i>10 # <=== 2. 使用tb打上一个端点
Temporary breakpoint 1 at 0x1191: file 026-gdb-tbreak.c, line 8.
(gdb) info b # <=== 3. 查看断点信息
Num Type Disp Enb Address What
1 breakpoint del y 0x0000000000001191 in main at 026-gdb-tbreak.c:8
stop only if i>10
(gdb) r # <=== 4. 运行到断点处停下
Starting program: /home/sumu/workspace/c-learning/02-c-basic/21-debug/a.out
Hello, World!
# <=== 程序暂停
Temporary breakpoint 1, main (argc=1, argv=0x7fffffffce28) at 026-gdb-tbreak.c:8
8 i += 2;
(gdb) p i # <=== 5. 查看i的值
$1 = 11 # <=== i=11,大于10了,所以上面断点生效,程序暂停
(gdb) info b # <=== 6. 查看断点信息
No breakpoints or watchpoints. # <=== 断点消失
(gdb) c
Continuing.
i = 31 # <=== 7. 程序运行结束
[Inferior 1 (process 13426) exited normally]
(gdb)三、rbreak
1. 语法格式
rbreak 命令的作用对象是 C、C++ 程序中的函数,它会在指定函数的开头位置打断点。rbreak 命令的使用语法格式为:
(gdb) rbreak regex # rb regex其中 regex 为一个正则表达式,程序中函数的函数名只要满足 regex 条件,rbreak 命令就会其内部的开头位置打断点。rbreak 命令打的断点和 break 命令打断点的效果是一样的,会一直存在,不会自动消失。
2. 使用示例
2.1 测试程序
#include <stdio.h>
void rb_first() {
printf("rb_first\n");
}
void rb_second() {
printf("rb_second\n");
}
int main(int argc, const char *argv[]) {
printf("Hello, World! \n");
rb_first();
rb_second();
return 0;
}我们在ubuntu中测试,所以直接用下面的命令编译:
cd ~/workspace/c-learning/02-c-basic/21-debug
gcc 027-gdb-rbreak.c -g2.2 调试示例
✓ sumu@virtual-machine:~/workspace/c-learning/02-c-basic/21-debug [main ≡ +1 ~0 -0 !]
$ gdb a.out -q
Reading symbols from a.out...
(gdb) l # <=== 1. 查看源码
1 #include <stdio.h>
2
3 void rb_first() {
4 printf("rb_first\n");
5 }
6
7 void rb_second() {
8 printf("rb_second\n");
9 }
10
(gdb) # <=== 2. 按下了Enter,显示剩下的代码(默认只显示10行)
11 int main(int argc, const char *argv[]) {
12 printf("Hello, World! \n");
13
14 rb_first();
15 rb_second();
16
17 return 0;
18 }
(gdb) rb rb_* # <=== 3. 匹配所有以 rb_ 开头的函数
Breakpoint 1 at 0x1149: file 027-gdb-rbreak.c, line 3.
void rb_first();
Breakpoint 2 at 0x1160: file 027-gdb-rbreak.c, line 7.
void rb_second();
(gdb) info b # <=== 4. 查看所有的断点,过执行rbreak rb_*指令,找到了程序中所有以 tb_* 开头的函数,并在这些函数内部的开头位置打上了断点
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000000000001149 in rb_first at 027-gdb-rbreak.c:3
2 breakpoint keep y 0x0000000000001160 in rb_second at 027-gdb-rbreak.c:7
(gdb) r
Starting program: /home/sumu/workspace/c-learning/02-c-basic/21-debug/a.out
Hello, World!
Breakpoint 1, rb_first () at 027-gdb-rbreak.c:3
3 void rb_first() {
(gdb) c
Continuing.
rb_first
Breakpoint 2, rb_second () at 027-gdb-rbreak.c:7
7 void rb_second() {
(gdb) c
Continuing.
rb_second
[Inferior 1 (process 14010) exited normally]
(gdb)