GDB调试

源程序编译

源程序:test1.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 #include <stdio.h>
int func(int n)
{
int sum = 0, i;
for (i = 0; i < n; ++i) {
sum += i;
}
return sum;
}

int main() {
int i;
long result = 0;
for (int i = 0; i < 100; ++i) {
result += i;
}
printf("result[1-100] = %d /n", result);
printf("result[1-250] = %d /n", func(250));
}

编译生成执行文件:(Linux下)gcc -g -o test1 test1.c -Wall,加上-g选项。

GDB调试案例

使用GDB调试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
linux:~/$ gdb test1	<---------- 启动GDB
GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2) 7.4-2012.04
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
For bug reporting instructions, please see:
<http://bugs.launchpad.net/gdb-linaro/>...
Reading symbols from /home/linux/OSTEP/Homework/14.第十四章-插叙_内存操作api/code/test1...done.
(gdb) l <-------------------- l命令相当于list,从第一行开始例出原码。
4 int sum = 0, i;
5 for (i = 0; i < n; ++i) {
6 sum += i;
7 }
8 return sum;
9 }
10
11 int main() {
12 int i;
13 long result = 0;
(gdb) <-------------------- 直接回车表示,重复上一次命令
14 for (i = 0; i < 100; ++i) {
15 result += i;
16 }
17 printf("result[1-100] = %d /n", (int)result);
18 printf("result[1-250] = %d /n", func(250));
19 return 0;
20 }
(gdb) break 12 <-------------------- 设置断点,在源程序第12行处。
Breakpoint 1 at 0x804841a: file test1.c, line 12.
(gdb) break func <-------------------- 设置断点,在函数func()入口处。
Breakpoint 2 at 0x80483ea: file test1.c, line 4.
(gdb) info break <-------------------- 查看断点信息。
Num Type Disp Enb Address What
1 breakpoint keep y 0x0804841a in main at test1.c:12
2 breakpoint keep y 0x080483ea in func at test1.c:4
(gdb) r <--------------------- 运行程序,run命令简写
Starting program: /home/linux/OSTEP/Homework/14.第十四章-插叙_内存操作api/code/test1

Breakpoint 1, main () at test1.c:13 <---------- 在断点处停住。
13 long result = 0;
(gdb) n <--------------------- 单条语句执行,next命令简写。
14 for (i = 0; i < 100; ++i) {
(gdb) n
15 result += i;
(gdb)
14 for (i = 0; i < 100; ++i) {
(gdb)
15 result += i;
(gdb) c <--------------------- 继续运行程序,continue命令简写。
Continuing.

Breakpoint 2, func (n=250) at test1.c:4
4 int sum = 0, i;
(gdb) n
5 for (i = 0; i < n; ++i) {
(gdb) p i <--------------------- 打印变量i的值,print命令简写。
$1 = -1207961320
(gdb) n
6 sum += i;
(gdb) n
5 for (i = 0; i < n; ++i) {
(gdb) p i
$2 = 0
(gdb) n
6 sum += i;
(gdb) n
5 for (i = 0; i < n; ++i) {
(gdb) p sum
$3 = 1
(gdb) p i
$4 = 1
(gdb) bt <--------------------- 查看函数堆栈。
#0 func (n=250) at test1.c:5
#1 0x08048461 in main () at test1.c:18
(gdb) finish <--------------------- 退出函数。
Run till exit from #0 func (n=250) at test1.c:5
0x08048461 in main () at test1.c:18
18 printf("result[1-250] = %d /n", func(250));
Value returned is $5 = 31125
(gdb) n
19 return 0;
(gdb) c <--------------------- 继续运行。
Continuing.
result[1-100] = 4950 /nresult[1-250] = 31125 /n[Inferior 1 (process 3516) exited normally] <----程序输出。程序退出,调试结束。
(gdb) q <--------------------- 退出gdb。
linux:~/$

GDB 常用命令

启动 GDB

编译:一般来说GDB主要调试的是C/C++的程序。要调试C/C++的程序,首先在编译时,我们必须要把调试信息加到可执行文件中。使用编译器(cc/gcc/g++)的 -g 选项可以做到这一点。如:

1
2
cc -g hello.c -o hello
g++ -g hello.cpp -o hello

启动GDB:gdb <program>,program也就是你的执行文件,一般在当前目录下。

GDB 交互命令

GDB 命令概貌

启动gdb后,就你被带入gdb的调试环境中,就可以使用gdb的命令开始调试程序了,gdb的命令可以使用help命令来查看:

image-20211023204012383

如上所示,gdb的命令很多,gdb把之分成许多个种类。help命令只是例出gdb的命令种类,如果要看种类中的命令,可以使用help <class> 命令,如:help breakpoints,查看设置断点的所有命令。也可以直接help 来查看命令的帮助。

gdb中,输入命令时,可以不用打全命令,只用打命令的前几个字符就可以了,当然,命令的前几个字符应该要标志着一个唯一的命令,在Linux下,你可以敲击两次TAB键来补齐命令的全称,如果有重复的,那么gdb会把其例出来。

如:b func => break func

暂停 / 恢复程序运行

设置断点(BreakPoint)

break <function>:在进入指定函数时停住。C++中可以使用class::function或function(type,type)格式来指定函数名。

break <linenum>:在指定行号停住。

break filename:linenum:在源文件filename的linenum行处停住。

break:没有参数时,表示在下一条指令处停住。

break ... if <condition>:…可以是上述的参数,condition表示条件,在条件成立时停住。比如在循环境体中,可以设置break if i=100,表示当i为100时停住程序。

TODO:

#####设置观察点(WatchPoint)

#####设置捕捉点(CatchPoint)

#####维护停止点

GDB中的停止点也就是上述的三类(断点、观察点、捕捉点)。在GDB中,如果你觉得已定义好的停止点没有用了,你可以使用delete(删除指定的断点)、clear(清除定义的停止点)、disable(disable停止点,而不会删除)、enable(enable停止点)这几个命令来进行维护。

#####为停止点设定运行命令

恢复程序运行:当程序被停住了,你可以用continue命令恢复程序的运行直到程序结束,或下一个断点到来。也可以使用step或next命令单步跟踪程序。

continue [ignore-count]:恢复程序运行,直到程序结束,或是下一个断点到来。ignore-count表示忽略其后的断点次数。continue,c,fg三个命令都是一样的意思。

单步调试:

next <count>:单步跟踪,如果有函数调用,他不会进入该函数。很像VC等工具中的step over。后面可以加count也可以不加,不加表示一条条地执行,加表示执行后面的count条指令,然后再停住。

step <count>:单步跟踪,如果有函数调用,他会进入该函数。进入函数的前提是,此函数被编译有debug信息。很像VC等工具中的step in。后面可以加count也可以不加,不加表示一条条地执行,加表示执行后面的count条指令,然后再停住。

查看栈信息:当程序被停住了,你需要做的第一件事就是查看程序是在哪里停住的。当你的程序调用了一个函数,函数的地址,函数参数,函数内的局部变量都会被压入“栈”(Stack)中。你可以用GDB命令来查看当前的栈中的信息。

backtracebt:打印当前的函数调用栈的所有信息,如下, 可以看出函数的调用栈信息是main() -> func()

1
2
3
(gdb) bt
#0 func (n=250) at test1.c:5
#1 0x08048461 in main () at test1.c:18

查看源代码:

打印表达式:

查看运行信息:

TODO:

#####分割窗口:

更强大的工具

cgdb可以看作gdb的界面增强版,用来替代gdb的 gdb -tui。cgdb主要功能是在调试时进行代码的同步显示,这无疑增加了调试的方便性,提高了调试效率。界面类似vi,符合unix/linux下开发人员习惯;如果熟悉gdb和vi,几乎可以立即使用cgdb。

GDB命令汇总

image-20240102230124482

image-20240102230153765

参考

用GDB调试程序(一)首选,更具体和全面的系列专栏。

Linux工具 - 1. gdb 调试利器

其他:GDB中应该知道的几个调试方法

The End


GDB调试
http://franktjp.com/2021/10/23/GDB调试(一)/
作者
Franktjp
发布于
2021年10月23日
许可协议