gdb是Linux下c/c++必不可少的调试工具,其强大相较于其他IDE完全有过之而无不及。本文主要介绍gdb的一些基础使用,启动调试器,设置断点,显示变量值,单步执行等。

   以下面一段小代码test.c为例,执行环境ubuntu14.04, 32位:

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 
  4 int sum;
  5 
  6 void swap(int *m, int *n)
  7 {
  8     int tmp;
  9 
 10     tmp = *m;
 11     *m  = *n;
 12     *n  = tmp;
 13 }
 14 
 15 int diff_fun(int x, int y)
 16 {
 17     int tmp_sum;
 18 
 19     if (x > y) {
 20         swap(&x, &y); 
 21     } 
 22 
 23     tmp_sum = y - x;
 24 
 25     return tmp_sum;
 26 }
 27 
 28 int main()
 29 {
 30     int a, b;
 31                                                                                                                                   
 32     a = 10;
 33     b = 5;
 34     sum = diff_fun(a, b);
 35 
 36     return sum;
 37 }

1、准备

    要使用GDB进行调试,须在gcc编译时使用-g选项,如:

     gcc -g -Wall test.c -o test

2、启动调试器

#gdb ./test    ----->执行gdb

root@zhuzhu:test_work# gdb ./test

GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1

Copyright (C) 2014 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".

Type "show configuration" for configuration details.

For bug reporting instructions, please see:

<http://www.gnu.org/software/gdb/bugs/>.

Find the GDB manual and other documentation resources online at:

<http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".

Type "apropos word" to search for commands related to "word"...

Reading symbols from ./test...done.

(gdb)

(gdb) start   ----->开始调试

Temporary breakpoint 1 at 0x8048421: file gdb.c, line 32.

Starting program: /root/work/test_work/test 


Temporary breakpoint 1, main () at gdb.c:32

32a = 10;

(gdb)


3、设置断点

  gdb设置断点可以有很多的方式,可以根据函数、行号、当前位置偏移、地址等来下断点。break写的时候也可以简写为b,如:

break 行号
break 函数名
break +偏移量
b -偏移量
b *地址

如执行:

(gdb) list      ----->列出附近行的代码

27

28int main()

29{

30int a, b;

31

32a = 10;

33b = 5;

34sum = diff_fun(a, b);

35

36return sum;

(gdb) b diff_fun ----->对diff_fun打断点

Breakpoint 2 at 0x80483eb: file gdb.c, line 19.

(gdb) b 23    ----->在23行处打断点

Breakpoint 3 at 0x8048407: file gdb.c, line 23.

(gdb) b +1    ----->暂停位置向下偏移一行处打断点

Breakpoint 4 at 0x8048428: file gdb.c, line 33.

(gdb) disas diff_fun  ----->反汇编指定函数代码,若不跟函数名,默认反汇编当前函数

Dump of assembler code for function diff_fun:

   0x080483e5 <+0>:push   %ebp

   0x080483e6 <+1>:mov    %esp,%ebp

   0x080483e8 <+3>:sub    $0x18,%esp

   0x080483eb <+6>:mov    0x8(%ebp),%edx

   0x080483ee <+9>:mov    0xc(%ebp),%eax

   0x080483f1 <+12>:cmp    %eax,%edx

   0x080483f3 <+14>:jle    0x8048407 <diff_fun+34>

   0x080483f5 <+16>:lea    0xc(%ebp),%eax

   0x080483f8 <+19>:mov    %eax,0x4(%esp)

   0x080483fc <+23>:lea    0x8(%ebp),%eax

   0x080483ff <+26>:mov    %eax,(%esp)

   0x08048402 <+29>:call   0x80483c3 <swap>

   0x08048407 <+34>:mov    0xc(%ebp),%edx

   0x0804840a <+37>:mov    0x8(%ebp),%eax

   0x0804840d <+40>:mov    %edx,%ecx

   0x0804840f <+42>:sub    %eax,%ecx

   0x08048411 <+44>:mov    %ecx,%eax

   0x08048413 <+46>:mov    %eax,-0x4(%ebp)

   0x08048416 <+49>:mov    -0x4(%ebp),%eax

   0x08048419 <+52>:leave  

   0x0804841a <+53>:ret    

End of assembler dump.

(gdb)b *0x080483e8    ----->在指定的地点打上断点

Breakpoint 5 at 0x80483e8: file gdb.c, line 16.

(gdb) i b        ----->查看所设断点

Num     Type           Disp Enb Address    What

2       breakpoint     keep y   0x080483eb in diff_fun at gdb.c:19

3       breakpoint     keep y   0x08048407 in diff_fun at gdb.c:23

4       breakpoint     keep y   0x08048428 in main at gdb.c:33

5       breakpoint     keep y   0x080483e8 in diff_fun at gdb.c:16


4、显示变量、寄存器、地址的值和单步执行

  print用来显示变量的值,简写为p

  info可以用来显示寄存器的值,简写为i

  x命令用来显示内存的内容


(gdb)run  ---->继续执行直到遇到第一个断点

The program being debugged has been started already.

Start it from the beginning? (y or n) y

Starting program: /root/work/test_work/test 


Breakpoint 4, main () at gdb.c:33

33 b = 5;

(gdb) layout split   ---->在不同窗口下查看源代码和汇编代码,及当前暂停的位置

wKioL1jZIHryKQ4gAACh3xOHeK8740.png

(gdb) p a  ----->打印变量a

$2 = 10

(gdb) 

同样p也可以给变量赋值,如:

(gdb) p a=9 ----->修改变量a的值

$3 = 9

(gdb) p a

$4 = 9

(gdb) 

(gdb) info reg   ----->显示多有寄存器的值

eax            0x1 1

ecx            0xaace997e -1429300866

edx            0xbffff614 -1073744364

ebx            0xb7fbe000 -1208229888

esp            0xbffff5d0 0xbffff5d0 --->可能会经常用到

ebp            0xbffff5e8 0xbffff5e8

esi            0x0 0

edi            0x0 0

eip            0x8048428 0x8048428 <main+13> --->可能会经常用到

eflags         0x282 [ SF IF ]

cs             0x73 115

ss             0x7b 123

ds             0x7b 123

es             0x7b 123

fs             0x0 0

gs             0x33 51

(gdb) 

其中指向栈顶的sp和程序计数器ip可能会经常用到,也可以单独显示某个寄存器,如,

(gdb) p $eip  ----->打印某个寄存器

$5 = (void (*)()) 0x8048428 <main+13>

(gdb) 


print还支持多种打印格式,如:

 x  显示为十六进制

 d  显示为十进制

 o  显示为八进制

 a  显示地址

 c  显示字符类型

 f  显示浮点小数类型

 s  显示为字符串

 i  显示为机器语言(x命令可用)

(gdb) p/c a     ---->字符形式显示变量a的值

$6 = 9 '\t'

(gdb)

(gdb) x &a    ----->显示变量a所在地址的值

0xbffff5e4: 9 '\t'

(gdb) x/i a

   0x9: Cannot access memory at address 0x9

(gdb) x/i &a   ----->显示变量a地址的汇编指令

   0xbffff5e4: or     %eax,(%eax)

(gdb) 


(gdb) c   ----->continue继续执行,简写为c,暂停在下一个断点处

Continuing.


Breakpoint 2, diff_fun (x=9, y=5) at gdb.c:19

19 if (x > y) {

(gdb) l

14

15 int diff_fun(int x, int y)

16 {

17 int tmp_sum;

18

19 if (x > y) {

20 swap(&x, &y); 

21

22

23 tmp_sum = y - x;

(gdb) 


(gdb) bt    ------>backtrace显示所有栈帧

#0  diff_fun (x=9, y=5) at gdb.c:19

#1  0x08048441 in main () at gdb.c:34

(gdb

(gdb) frame 1  ----->进入栈帧1

#1  0x08048441 in main () at gdb.c:34

34 sum = diff_fun(a, b);

(gdb) i locals 

a = 9

b = 5

(gdb) frame 0  ------>进入栈帧0

#0  diff_fun (x=9, y=5) at gdb.c:19

19 if (x > y) {

(gdb) bt

#0  diff_fun (x=9, y=5) at gdb.c:19

#1  0x08048441 in main () at gdb.c:34

(gdb) 

(gdb) bt full  ----->显示每个栈帧更全面的信息

#0  diff_fun (x=9, y=5) at gdb.c:19

        tmp_sum = 134513408

#1  0x08048441 in main () at gdb.c:34

        a = 9

        b = 5

(gdb) 


单步执行有next和step其中后者会进入函数,前者不会,加上i的话nexti、stepi,单步汇编指令,这在大型程序中较少用到。


删除断点的话,使用d 编号

(gdb) i b

Num     Type           Disp Enb Address    What

2       breakpoint     keep y   0x080483eb in diff_fun at gdb.c:19

breakpoint already hit 1 time

3       breakpoint     keep y   0x08048407 in diff_fun at gdb.c:23

4       breakpoint     keep y   0x08048428 in main at gdb.c:33

breakpoint already hit 1 time

5       breakpoint     keep y   0x080483eb in diff_fun at gdb.c:16

breakpoint already hit 1 time

(gdb) d 2   ----->delete 编号2断点

(gdb) i b

Num     Type           Disp Enb Address    What

3       breakpoint     keep y   0x08048407 in diff_fun at gdb.c:23

4       breakpoint     keep y   0x08048428 in main at gdb.c:33

breakpoint already hit 1 time

5       breakpoint     keep y   0x080483eb in diff_fun at gdb.c:16

breakpoint already hit 1 time

(gdb) 


以上是gdb的一些基本命令,接下来会介绍一些常见的应用场景。


参考资料:

gdb 调试入门,大牛写的高质量指南

debug hacks

man page