gdb牛刀小试
mufiye 内核新手

gdb牛刀小试

写在前面

最近时间很多,我在努力做MIT6.S081这门课的Lab,但是经常会遇到一些让我难以理解并且无从入手的bug,因此愈加萌发了学习gdb的想法。也试着去看gdb的官方文档,但是那个pdf足足有将近900页,总感觉这样看书对于一个新手不是高效的学习方法(当然对于已经使用过一段时间gdb的程序员来说,平时抽点时间细水长流地去看官方文档肯定是很棒的),因此想要找一个好点的教程,带我入门gdb,于是翻到了大佬写的教程(真的很古早,2003年,我当时看到发表时间人懵了)。本文一是想记录一下自己的学习过程,二也是想做个教程上的翻新(可能教程太早,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
#include <stdio.h>

int func(int n)
{
int sum = 0, i;
for (i = 0; i < n; i++)
{
sum += i;
}
return sum;
}

main()
{
//new added
int j=0;
j = 1;
j = 2;
j = 3;
int i;
long result = 0;
for (i = 1; i <= 100; i++)
{
result += i;
}

printf("result[1-100] = %d /n", result);
printf("result[1-250] = %d /n", func(250));
}

gdb调试

  1. 首先编译代码生成可执行文件(一定要加-g参数,这样才能把调试信息加到可执行文件中)
1
cc -g tst.c -o tst
  1. 然后启动gdb($后为bash命令)
1
2
3
4
ubuntu@VM-0-14-ubuntu:~/learn_gdb$gdb
...
...
--Type <RET> for more, q to quit, c to continue without paging--c
  1. 之后会跳出一大段关于gdb的描述,并问你是否继续,继续就输入c,退出就输入q
  2. 进入我们想要调试的文件,注意是可执行文件,所以不需要带上文件后缀类型名(gdb后为输入的命令)
1
2
(gdb) file tst
Reading symbols from tst...
  1. 输入l命令查看源代码(一次默认显示10行,想看更多就按回车)
1
(gdb) l   
  1. 设置断点(设置两个断点,第一个设置在第16行,第二个设置在函数func的入口处)

    1
    2
    (gdb) break 16
    Breakpoint 1 at 0x1187: file tst.c, line 16.
    1
    2
    (gdb) break func
    Breakpoint 2 at 0x1149: file tst.c, line 4.
  2. 查看断点的信息

    1
    2
    3
    4
    (gdb) info break   
    Num Type Disp Enb Address What
    1 breakpoint keep y 0x0000000000001187 in main at tst.c:16
    2 breakpoint keep y 0x0000000000001149 in func at tst.c:4
  3. 运行代码(r为run的缩写)

    1
    2
    3
    4
    5
    (gdb) r
    Starting program: /home/ubuntu/learn_gdb/tst

    Breakpoint 1, main () at tst.c:16
    16 int j=0;
  4. 执行单条语句(n为next的缩写)

    1
    2
    3
    4
    5
    6
    (gdb) n
    17 j = 1;
    (gdb) n
    18 j = 2;
    (gdb) n
    19 j = 3;
  5. 继续运行程序(c为continue的缩写),这时发现程序运行到了第二个断点处

    1
    2
    3
    4
    5
    (gdb) c
    Continuing.

    Breakpoint 2, func (n=21845) at tst.c:4
    4 {
  6. 继续单步执行程序

    1
    2
    3
    4
    5
    6
    (gdb) n
    5 int sum = 0, i;
    (gdb) n
    6 for (i = 0; i < n; i++)
    (gdb) n
    8 sum += i;
  7. 打印一些变量的信息(p为print的缩写)

    1
    2
    3
    4
    (gdb) p i
    $1 = 0
    (gdb) p sum
    $2 = 0
  8. 继续单步执行程序

    1
    2
    3
    4
    (gdb) n
    6 for (i = 0; i < n; i++)
    (gdb) n
    8 sum += i;
  9. 再查看一下变量的信息

    1
    2
    3
    4
    (gdb) p i
    $3 = 1
    (gdb) p sum
    $4 = 0
  10. 查看函数栈

    1
    2
    3
    (gdb) bt
    #0 func (n=250) at tst.c:8
    #1 0x00005555555551e9 in main () at tst.c:28
  11. 退出当前的函数(也就是func函数)

    1
    2
    3
    4
    5
    (gdb) finish
    Run till exit from #0 func (n=250) at tst.c:8
    0x00005555555551e9 in main () at tst.c:28
    28 printf("result[1-250] = %d /n", func(250));
    Value returned is $5 = 31125
  12. 继续运行程序(可以发现程序正常退出了)

    1
    2
    3
    (gdb) c
    Continuing.
    result[1-100] = 5050 /nresult[1-250] = 31125 /n[Inferior 1 (process 2973774) exited normally]
  13. 退出gdb调试

    1
    (gdb) q

一些疑问以及自己的解答

  1. break断点的设置是断在一个语句的之前还是之后的?(比如break 16,这个第16行的语句是执行了没有呢?)
    Answer:break断点的设置是断在一个语句之前的。也就是说第16行语句是没有执行的(可以通过print相关的变量验证)。

参考

大佬的gdb调试程序(一)

  • 本文标题:gdb牛刀小试
  • 本文作者:mufiye
  • 创建时间:2021-11-21 12:53:00
  • 本文链接:http://mufiye.github.io/2021/11/21/gdb牛刀小试/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
 评论