1.怎么调试程序
在linux下,我们通常用gcc来编译链接程序,用gdb来调试程序。在用gcc生成程序的时候,用-g选项来使程序可以调试:
[email protected]:~/gdb$ gcc -g -Wall gdbtest.c -o gdbtest
然后在用如下命令调试程序:
[email protected]:~/gdb$ gdb gdbtest
2.gdb的基本命令介绍
run | 在gdb中运行你的程序,一般会在你设置的断点地方停止。 |
start | 也是在gdb中运行程序,不过默认实在程序的main函数停止。 |
break | 用break命令在程序中来设置断点,方便程序的调试。 |
continue | continue继续执行程序,直到遇到下一个断点。 |
step | step单步调试程序,遇到有函数调用的地方,会跳进该函数,继续单步调试。 |
next | next也是单步调试程序,但是在函数的调用的地方,不会跳进该函数,二回继续执行。 |
list | list列出当前的源代码,如果参数是函数名,就会列出该函数的定义。 |
用来打印变量。 | |
display | 用来显示变量的。 |
backtrace | 用来显示函数的调用栈。 |
3.设置断点的基本方法
break linenumber b linenumber |
在当前文件的第linenumber行设置一个断点。 |
break functionname b functionname |
在当前文件的functionname函数设置一个断点 |
break filename:linenumber b filename:linenumber |
在filename文件第linenumber行设置一个断点。 |
break filename:functionname b filename:functionname |
在filename文件的functionname函数设置一个断点。 |
(gdb) l
38 int x = atoi(argv[1]);
39 int y = atoi(argv[3]);
40 printf("x = %d, y =
%d.\n", x , y);
41
42 if (0 == strcmp("+",
argv[2])) {
43 int result = add(x, y);
44 printf("%s %s %s = %d.\n", argv[1], argv[2], argv[3],
result);
45 }
46
47 if (0 ==
strcmp("-", argv[2])) {
在43的一行设置一个断点:
(gdb) b
43
Breakpoint 2 at 0x80485dc: file gdbtest.c,
line 43.
在add函数设置一个断点:
(gdb) b
add
Breakpoint 3 at 0x8048500: file gdbtest.c,
line 7.
在sub函数设置一个断点:
(gdb) b
sub
Breakpoint 4 at 0x804850d: file gdbtest.c,
line 12.
在div函数设置一个断点:
(gdb) b
div
Breakpoint 5 at 0xb7e487e0: file div.c, line
56.
用info break(i b)查看当前设置的断点信息:
(gdb) info
break
Num Type Disp Enb Address
What
2 breakpoint keep y 0x080485dc in main at
gdbtest.c:43
3 breakpoint keep y 0x08048500 in add at
gdbtest.c:7
4 breakpoint keep y 0x0804850d in sub at
gdbtest.c:12
5 breakpoint keep y 0xb7e487e0 in div at
div.c:56
用delete删除设置的断点(删除断点的方法设置的时候都差不多):
(gdb) delete
5
(gdb) i
b
Num Type Disp Enb Address
What
2 breakpoint keep y 0x080485dc in main at
gdbtest.c:43
3 breakpoint keep y 0x08048500 in add at
gdbtest.c:7
4 breakpoint keep y 0x0804850d in sub at
gdbtest.c:12
第5个断点没有了。
直接用delete删除所有设置的断点:
(gdb) delete
删除所有断点吗? (y or n)
n
disable命令使某个断点失效:
(gdb) disable
3
(gdb) i
b
Num Type Disp Enb Address
What
2 breakpoint keep y 0x080485dc in main at
gdbtest.c:43
3 breakpoint keep n 0x08048500 in add at gdbtest.c:7
4
breakpoint keep y 0x0804850d in sub at gdbtest.c:12
enable命令是某个断点有效:
(gdb) enable
3
(gdb) i
b
Num Type Disp Enb Address
What
2 breakpoint keep y 0x080485dc in main at
gdbtest.c:43
3 breakpoint keep
y 0x08048500 in add at gdbtest.c:7
4
breakpoint keep y 0x0804850d in sub at gdbtest.c:12
只执行disable命令就是让所有断点失效:
(gdb) disable
(gdb) i b
Num Type
Disp Enb Address What
2 breakpoint keep n 0x080485dc in main at gdbtest.c:43
3 breakpoint keep n 0x08048500 in add at
gdbtest.c:7
4 breakpoint keep n 0x0804850d in sub at gdbtest.c:12
只执行enable命令就是让所有断点有效:
(gdb) enable
(gdb) i b
Num Type
Disp Enb Address What
2 breakpoint keep y 0x080485dc in main at gdbtest.c:43
3 breakpoint keep y 0x08048500 in add at
gdbtest.c:7
4 breakpoint keep y 0x0804850d in sub at gdbtest.c:12
4.gdb打印变量
在gdb可以用print/p命令来打印变量,打印的方式可以分为以下几种:
x | 按十六进制格式显示变量。 |
d | 按十进制格式显示变量。 |
u | 按十六进制格式显示无符号整型 |
o | 按八进制格式显示变量 |
t | 按二进制格式显示变量 |
a | 按十六进制格式显示变量 |
c | 按字符格式显示变量 |
f | 按浮点数格式显示变量 |
(gdb) l
1 #include
<stdio.h>
2
3 int
main(void)
4 {
5 char
a[100];
6 int i;
7
8 for (i = 0; i < 100; i++) {
9 a[i] =
i;
10 }
11 return 0;
12 }
(gdb) start
Temporary breakpoint
1 at 0x8048447: file print.c, line 4.
Starting program:
/home/test/gdb/print
Temporary breakpoint 1, main () at
print.c:4
4 {
(gdb) u
11
main () at print.c:12
12
return 0;
以十六进制打印数组a的内容,会显示很多:
(gdb) p/x
a
$1 = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
0x16, 0x17, 0x18,
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c,
0x2d, 0x2e, 0x2f,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43,
0x44, 0x45, 0x46,
0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d,
0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a,
0x5b, 0x5c, 0x5d,
0x5e, 0x5f, 0x60, 0x61, 0x62,
0x63}
可以用p *[email protected] count打印数组array_name的前count个元素。
以十六进制指定打印数组的前面十个元素:
(gdb) p/x
*[email protected]
$2 = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6,
0x7, 0x8, 0x9}
在c语言中经常有很多struct自定义变量,在程序的执行的可以通过whatis命令和ptype这俩个命令来查看变量类型。
(gdb) l
-15
1 #include <stdio.h>
2
3 typedef
struct listnode
4 {
5 int value;
6 struct listnode
*next;
7 }Node;
8
9 Node node1 = {1, NULL};
10
(gdb) start
Temporary breakpoint 1 at
0x80483f3: file struct.c, line 13.
Starting program:
/home/test/gdb/struct
Temporary breakpoint 1, main () at struct.c:13
13 Node node2 = {2, NULL};
(gdb) n
15 return
0;
查看node1的变量类型,没有到结构体,只显示类型:
(gdb) whatis
node1
type = Node
查看node2的变量类型,看到结构体里面都有些什么:
(gdb) ptype
node2
type = struct listnode
{
int value;
struct listnode
*next;
}
gdb中使用“x
”命令来打印内存的值,格式为“x/nfu
”。含义为以
addrf
格式打印从addr
开始的n
个长度单元为u
的内存值。参数具体含义如下:
a)n:输出单元的个数。
b)f:是输出格式。比如x
是以16进制形式输出,o
是以8进制形式输出,等等。
c)u:标明一个单元的长度。b
是一个byte
,h
是两个byte
(halfword),w
是四个byte
(word),g
是八个byte
(giant
word)。
(gdb) l
1 #include
<stdio.h>
2
3 int main(void)
4 {
5 char a[100];
6 int i;
7
8 for (i
= 0; i < 100; i++) {
9 a[i] = i;
10 }
(gdb) start
Temporary breakpoint 1 at
0x8048447: file print.c, line 4.
Starting program: /home/test/gdb/print
Temporary breakpoint 1, main () at print.c:4
4
{
(gdb) n
8 for (i = 0; i <
100; i++) {
(gdb) n
9 a[i] = i;
(gdb)
n
8 for (i = 0; i < 100; i++) {
(gdb) u 11
main () at
print.c:12
12 return 0;
以16进制格式打印数组前a
16个byte的值:
(gdb) x/16xb
a
0xbfffedf8: 0x00 0x01 0x02 0x03
0x04 0x05 0x06 0x07
0xbfffee00: 0x08 0x09 0x0a
0x0b 0x0c 0x0d 0x0e 0x0f
以无符号10进制格式打印数组a
前16个byte的值:
(gdb) x/16ub
a
0xbfffedf8: 0 1 2 3 4 5 6
7
0xbfffee00: 8 9 10 11 12 13 14
15
以16进制格式打印数组a
前16个word(4个byte)的值:
(gdb)
x/16xw a
0xbfffedf8:
0x03020100 0x07060504 0x0b0a0908 0x0f0e0d0c
0xbfffee08:
0x13121110 0x17161514 0x1b1a1918 0x1f1e1d1c
0xbfffee18:
0x23222120 0x27262524 0x2b2a2928 0x2f2e2d2c
0xbfffee28:
0x33323130 0x37363534 0x3b3a3938