在 “Linux基础知识(13)- GDB 调试器(一)| 安装配置和基本使用方法” 里我们完成了 GDB 的安装配置,并演示了 GDB 几个内部命令的基本使用方法,本文将演示普通断点、单步调试和查看变量。
1. 创建 C 程序
1) 代码如下
$ cd ~/
$ vim test2.c
#include <stdio.h> int testFunc() { for (int i=0; i<3; i++ ) { printf("printf i=%d\n", i); } } int main(int argc, char* argv[]) { if (argv[1] == NULL) { printf("Hello world - GDB\n"); } else { printf("Hello world - %s\n", argv[1]); } testFunc(); }
2)编译并设置调试信息
$ gcc -g test2.c -o test2
$ ./test2
Hello world - GDB printf i=0 printf i=1 printf i=2
$ ./test2 "Message"
Hello world - Message printf i=0 printf i=1 printf i=2
2. 设置普通断点
1) 使用 break 命令设置断点
使用 GDB 启动 test2 程序:
$ gdb -q test2
Reading symbols from test... (gdb) l 1 #include <stdio.h> 2 3 int testFunc() { 4 for (int i=0; i<3; i++ ) { 5 printf("printf i=%d\n", i); 6 } 7 } 8 9 int main(int argc, char* argv[]) { 10 (gdb) 11 if (argv[1] == NULL) { 12 printf("Hello world - GDB\n"); 13 } else { 14 printf("Hello world - %s\n", argv[1]); 15 } 16 17 testFunc(); 18 } 19
运行 break (简写 b) 命令在指定行设置断点,使用方法是 b 空格 行号。
(gdb) b 11
Breakpoint 1 at 0x11b4: file test2.c, line 11.
(gdb) b 17
Breakpoint 2 at 0x11f1: file test2.c, line 17.
查看断点信息:
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00000000000011b4 in main at test2.c:11
2 breakpoint keep y 0x00000000000011f1 in main at test2.c:17
说明:
NUM: 断点编号
Disp:断点执行一次之后是否还有效(keep: 有效,dis: 无效)
Enb: 当前断点是否有效(y: 有效,n: 无效)
Address:内存地址
(gdb) q
2) 使用 tbreak 命令设置断点
使用 tbreak 命令设置的断点,仅会作用 1 次,程序暂停之后,该断点就会自动消失。
使用 GDB 启动 test2 程序:
$ gdb -q test2
Reading symbols from test...
(gdb)
运行 tbreak 命令在指定行设置断点,使用方法是 tbreak 空格 行号。
(gdb) tbreak 11 Temporary breakpoint 1 at 0x11b4: file test2.c, line 11. (gdb) tbreak 17 Temporary breakpoint 2 at 0x11f1: file test2.c, line 17. (gdb) r Starting program: /home/xxx/test2 Temporary breakpoint 1, main (argc=1, argv=0x7fffffffe228) at test2.c:11 11 if (argv[1] == NULL) { (gdb) c Continuing. Hello world - GDB Temporary breakpoint 2, main (argc=1, argv=0x7fffffffe228) at test2.c:17 17 testFunc(); (gdb) c Continuing. printf i=0 printf i=1 printf i=2 [Inferior 1 (process 12928) exited normally] (gdb) info b No breakpoints or watchpoints. (gdb) q
3) 使用 rbreak 命令设置断点
rbreak 命令的作用对象是 C、C++ 程序中的函数,它会在指定函数的开头位置打断点。
语法格式:
(gdb) rbreak regex
regex 为一个正则表达式,程序中函数名只要满足 regex 条件,rbreak 命令就会在其内部的开头位置打断点。值得一提的是,rbreak 命令打的断点和 break 命令打断点的效果是一样的,会一直存在,不会自动消失。
使用 GDB 启动 test2 程序:
$ gdb -q test2
Reading symbols from test...
(gdb)
运行 rbreak 命令在指定行设置断点,使用方法是 tbreak 空格 regex。
(gdb) rbreak testFunc
Breakpoint 1 at 0x1169: file test2.c, line 3.
int testFunc();
(gdb) r
Starting program: /home/xxx/test2
Hello world - GDB
Breakpoint 1, testFunc () at test2.c:3
3 int testFunc() {
...
(gdb) q
4) 清除断点
运行 clear 命令删除某一行对应的断点,使用方法是 clear 空格 行号。
(gdb) clear 11
(gdb) clear 17
(gdb) info b
Deleted breakpoint 2 No breakpoints or watchpoints.
3. 单步调试
使用 GDB 启动 test2 程序,设置断点并运行到断点:
$ gdb -q test2
Reading symbols from test... (gdb) b 11 Breakpoint 1 at 0x11b4: file test2.c, line 11. (gdb) b 17 Breakpoint 2 at 0x11f1: file test2.c, line 17. (gdb) r Starting program: /home/xxx/test2 Breakpoint 1, main (argc=1, argv=0x7fffffffe228) at test2.c:11 11 if (argv[1] == NULL) {
1) step 命令
运行 step (简写 s) 命令单步执行,进入 testFunc() 函数:
(gdb) c Continuing. Hello world - GDB Breakpoint 2, main (argc=1, argv=0x7fffffffe228) at test2.c:17 17 testFunc(); (gdb) s testFunc () at test2.c:3 3 int testFunc() { (gdb) s 4 for (int i=0; i<3; i++ ) { (gdb) c Continuing. printf i=0 printf i=1 printf i=2 [Inferior 1 (process 12944) exited normally] (gdb) q
2) next 命令
运行 next (简写 n) 命令单步执行,跳过 testFunc() 函数:
(gdb) c Continuing. Hello world - GDB Breakpoint 2, main (argc=1, argv=0x7fffffffe228) at test2.c:17 17 testFunc(); (gdb) n printf i=0 printf i=1 printf i=2 18 } (gdb) c Continuing. [Inferior 1 (process 12950) exited normally] (gdb) q
3)until 命令
运行 until (简写 u) 命令:
(gdb) u 12 printf("Hello world - GDB\n"); (gdb) u 17 Hello world - GDB Breakpoint 2, main (argc=1, argv=0x7fffffffe228) at test2.c:17 17 testFunc();
注:不带参数的 until 命令,可以使 GDB 调试器快速运行完当前的循环体,并运行至循环体外停止。until 命令并非任何情况下都会发挥这个作用,只有当执行至循环体尾部(最后一行代码)时,until 命令才会发生此作用;反之,until 命令和 next 命令的功能一样,只是单步执行程序。
4. 查看变量
使用 GDB 启动 test2 程序:
$ gdb -q test2
Reading symbols from test...
(gdb)
设置断点和运行:
(gdb) b 11 Breakpoint 1 at 0x11b4: file test2.c, line 11. (gdb) r "message" Starting program: /home/xxx/test2 "message" Breakpoint 1, main (argc=2, argv=0x7fffffffe228) at test2.c:11 11 if (argv[1] == NULL) {
运行 print (简写 p) 命令变量的值,使用方法是 p 空格 变量:
(gdb) p argv[1] $1 = 0x7fffffffe509 "message"
运行 display 命令查看变量或表达式的值,使用方法是 display 空格 变量 (或 display/fmt 空格 变量):
(gdb) display argv[1] 1: argv[1] = 0x7fffffffe509 "message" (gdb) display/t argv[1] 2: /t argv[1] = 11111111111111111111111111111111110010100001001 (gdb) c Continuing. Hello world - message printf i=0 printf i=1 printf i=2 [Inferior 1 (process 12982) exited normally] (gdb) r "message2" Starting program: /home/xxx/test2 "message2" Breakpoint 1, main (argc=2, argv=0x7fffffffe228) at test2.c:11 11 if (argv[1] == NULL) { 1: argv[1] = 0x7fffffffe508 "message2" 2: /t argv[1] = 11111111111111111111111111111111110010100001000
注:使用 display 命令查看的目标变量或表达式,不仅在执行该命令的同时会看到目标变量的值,后续每次程序停止执行时,GDB 调试器都会将目标变量的值打印出来。
(gdb) info display Auto-display expressions now in effect: Num Enb Expression 1: y argv[1] 2: y /t argv[1]
注:使用 display 命令查看的目标变量或表达式,都会被记录在一张列表(称为自动显示列表)中,通过执行 info dispaly 命令,可以打印出这张表。列表各列的含义:
(1) Num 列,为各变量或表达式的编号,GDB 调试器为每个变量或表达式都分配有唯一的编号;
(2) Enb 列,表示当前各个变量(表达式)是处于激活状态还是禁用状态,如果处于激活状态(用 y 表示),则每次程序停止执行,该变量的值都会被打印出来;反之,如果处于禁用状态(用 n 表示),则该变量(表达式)的值不会被打印;
(3) Expression 列,表示查看的变量或表达式;
(gdb) disable display 2 (gdb) info display Auto-display expressions now in effect: Num Enb Expression 1: y argv[1] 2: n /t argv[1]
注:可以看到,编号为 2 的 argv[1] 变量的 Enb 由 y 变成了 n。处于禁用状态的变量或表达式,程序停止执行时将不再自动打印出它们的值。
(gdb) enable display 2 (gdb) info display Auto-display expressions now in effect: Num Enb Expression 1: y argv[1] 2: y /t argv[1]
注:参数 2 表示要激活的变量或表达式的编号,编号的个数可以是多个,表示一次性激活多个变量或表达式。
参数 fmt 用于指定输出变量或表达式的格式,下表时常用的一些 fmt 参数。
/fmt | 描述 |
/x | 以十六进制的形式打印出整数 |
/d | 以有符号、十进制的形式打印出整数 |
/u | 以无符号、十进制的形式打印出整数 |
/o | 以八进制的形式打印出整数 |
/t | 以二进制的形式打印出整数 |
/f | 以浮点数的形式打印变量或表达式的值 |
/c | 以字符形式打印变量或表达式的值 |
注:display 命令和 /fmt 之间不要留有空格。以 /x 为例,应写为 (gdb) display/x expr。