EdmondFrank's 时光足迹

この先は暗い夜道だけかもしれない それでも信じて進むんだ。星がその道を少しでも照らしてくれるのを。
或许前路永夜,即便如此我也要前进,因为星光即使微弱也会我为照亮前途。
——《四月は君の嘘》

Gdb的简单使用

gdb的简单使用

1.单步执行和跟踪函数调用

测试源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 #include <stdio.h>
 int add_range(int low,int high)
 {
    int i,sum;
    for (i=low;i<=high;i++)
    {
        sum = sum + i;
    }
    printf("sum = %d\n",sum);
    return sum;
 }
 int main()
 {
    int result[100];
    result[0]=add_range(1,10);
    result[1]=add_range(1,100);
    printf("result[0]=%d \n result[1]=%d \n",result[0],result[1]);
    return 0;
 }

在编译并运行以上代码后,我们会发现最后的输出结果并非如我们预料一样. 无论是result[0]或result[1]基本都是错误的结果,然而代码中的逻辑却没有任何的问题. 在这里心细的朋友可能已经发现问题的所在了. 但既然是练习gdb的使用,那么我们就加上 -g 选项,再重新编译一次代码以便生成的可执行文件可以用gdb进行调试.

$ gcc -g main.c -o main $ gdb main

注:-g选项的作用是在可执行文件中加入源代码的信息,比如可执行文件中第几条机器指令对应源代码的第几行,但并不是把整个源文件嵌入到可执行文件中,所以在调试时必须保证gdb能找到源文件

除此之外,gdb提供一个类似shell的命令行环境,上面的 (gdb) 就是提示符,在这个提示符下输 入help可以查看命令的类别.

help命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
(gdb) help
List of classes of commands:

aliases -- Aliases of other commands
breakpoints -- Making program stop at certain points
data -- Examining data
files -- Specifying and examining files
internals -- Maintenance commands
obscure -- Obscure features
running -- Running the program
stack -- Examining the stack
status -- Status inquiries
support -- Support facilities
tracepoints -- Tracing of program execution without stopping the program
user-defined -- User-defined commands

Type "help" followed by a class name for a list of commands in that class.
Type "help all" for the list of all commands.
Type "help" followed by command name for full documentation.
Type "apropos word" to search for commands related to "word".
Command name abbreviations are allowed if unambiguous.
(gdb)

list命令 list命令用于列出代码或指定函数,列出的代码行的起始位置为上一次列出的代码的结尾, 第一次使用时,默认从第一行开始.每次默认列出十行代码.list命令也可简写成l. 要是要列出指定函数的代码,直接在list后面加函数名即可.

eg: list add_range

且可以通过 show listsize 和 show listsize NUM(NUM为整数)来查询和修改每次最大列出代码行数.

在什么都不输入的情况下敲击回车,gdb默认会执行上一条命令

quit命令 退出gdb环境

start命令 开始执行程序,start命令默认停在main函数变量定义后的第一条语句,并等待下一次命令.

next命令(简写n) 单步步过

step命令(简写s) 单步步入

backtrace命令(简写bt) 查看函数调用栈帧

info命令(简写i) 查询相关信息

eg:查询add_range函数局部变量的值,i locals

print命令(简写p) 打印指定变量值

finish命令 执行到返回

介绍完以上几个简单的命令之后再返回到测试代码的调试之中.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(gdb) start
Temporary breakpoint 1 at 0x80484c7: file ./gdbtest2.c, line 13.
Starting program: /home/ef/c/gdbtest2

Temporary breakpoint 1, main () at ./gdbtest2.c:13
13    {
(gdb) n
15        result[0]=add_range(1,10);
(gdb) s
add_range (low=1, high=10) at ./gdbtest2.c:5
5     for (i=low;i<=high;i++)
(gdb) bt
#0  add_range (low=1, high=10) at ./gdbtest2.c:5
#1  0x080484de in main () at ./gdbtest2.c:15
(gdb) i locals
i = -1208074951
sum = -1208114904

在以上最后两行输出我们可以明白,程序之所以没有输出我们预期的结果, 其根本原因就是因为局部变量i和sum没有进行初始化.虽然变量i会在for循环之前进行重新赋值, 但作为累积变量的sum由于初始值不是0,导致最后的累加结果错误.

找出错误的所在后,我们即可以退出gdb,修改源码再重新进行调试;或者直接通过gdb的命令

set var sum = 0

修改局部变量sum的值,然后再寻找下一处Bug.

2.断点的使用

测试代码

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

编译以上代码并运行

1
2
3
4
5
6
$ gcc main.c -g -o main
$ ./main
123
input=123
234
input=123234

我们发现程序第一次运行是对的,但第二次却又出乎了我们意料. 于是我们也把这个程序也放入gdb中调试.

display命令 display命令可以使得每次停下来的时候都显示执行变量的值,亦称变量跟踪.

undisplay命令 取消之前设置的变量跟踪

break命令(简称b) 在指定位置设定断点,参数可以是指定行也可以是某个函数

i breakpoints 输出目前已有断点的信息

enable breakpoints NUM(NUM为断点号) 启用指定断点(NUM为空时,默认为全部断点)

disable breakpoints NUM(NUM为断点号) 禁用指定断点(NUM为空时,默认为全部断点)

delete breakpoints NUM(NUM为断点号) 删除指定断点(NUM为空时,默认为全部断点)

continue命令(简称c) 继续执行,注意,continue命令是连续运行而非单步执行.

介绍完命令后我们又再次回到调试中,首先我们执行完start命令后,使用

b 9

在sum的赋值处下一个断点,并用命令

display sum

来时刻监视变量sum的变化
这时我们发现在我们输入完"123"后,再次想输入"456"时,gdb显示 “sum = 123” ,这样我们便发现错误所在,即我们并没有在新的循环中清空sum的先值

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
(gdb) start
Temporary breakpoint 1 at 0x804848c: file ./gdbtest3.c, line 3.
Starting program: /home/ef/c/gdbtest3

Temporary breakpoint 1, main () at ./gdbtest3.c:3
3 {
(gdb) list
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);
(gdb) b 9
Breakpoint 2 at 0x80484c2: file ./gdbtest3.c, line 9.
(gdb) display sum
1: sum = 134514011
(gdb) c
Continuing.
123

Breakpoint 2, main () at ./gdbtest3.c:9
9 sum = sum*10 + input[i] - '0';
1: sum = 0
(gdb) c
Continuing.

Breakpoint 2, main () at ./gdbtest3.c:9
9 sum = sum*10 + input[i] - '0';
1: sum = 1
(gdb) c
Continuing.

Breakpoint 2, main () at ./gdbtest3.c:9
9 sum = sum*10 + input[i] - '0';
1: sum = 12
(gdb) c
Continuing.
input=123
456

Breakpoint 2, main () at ./gdbtest3.c:9
9 sum = sum*10 + input[i] - '0';
1: sum = 123
(gdb)