Debugging with GDB
一:
GDB是GNU开源组织发布的一个强大的程序调试工具(UNIX WINDOWS都可以)。
一般来说,GDB主要帮助你完成下面四个方面的功能:
1,启动你的程序,可以按照你的自定义的要求随心所欲的运行程序。
2,可以让被调试的程序在你所指定的位置的断点处停住。(断点也可以是条件表达式)
3,当程序被停住时,可以检查此时你的程序中所发生的事情。
4,动态的改变你程序的执行环境。
看一个简单的列子:
- 1 #include <stdio.h>
- 2
- 3 int func(int n)
- 4 {
- 5 int sum = 0, i;
- 6 for (i = 0; i < n; i++)
- 7 {
- 8 sum += i;
- 9 }
- 10 return sum;
- 11 }
- 12
- 13 main()
- 14 {
- 15 int i;
- 16 long result = 0;
- 17 for (i = 1; i <= 100; i++)
- 18 {
- 19 result += 1;
- 20 }
- 21
- 22 printf("result[1-100]=%d\n", result);
- 23 printf("result[1-500]=%d\n", func(500));
- 24 }
####
list(l)命令:
(gdb) list 1
1 #include <stdio.h>
2
3 int func(int n)
4 {
5 int sum = 0, i;
6 for (i = 0; i < n; i++)
7 {
8 sum += i;
9 }
10 return sum;
(gdb)
11 }
12
13 main()
14 {
15 int i;
16 long result = 0;
17 for (i = 1; i <= 100; i++)
18 {
19 result += 1;
20 }
(gdb)
21
22 printf(“result[1-100]=%d\n”, result);
23 printf(“result[1-500]=%d\n”, func(500));
24 }
(gdb)
Line number 25 out of range; tst.c has 24 lines.
(gdb)
####
和断点(break)有关的命令:
(gdb) break 16
Breakpoint 1 at 0x4004ce: file tst.c, line 16.
(gdb) list 16
11 }
12
13 main()
14 {
15 int i;
16 long result = 0;
17 for (i = 1; i <= 100; i++)
18 {
19 result += 1;
20 }
(gdb) info break
Num Type Disp Enb Address What
1 breakpoint keep y 0x00000000004004ce in main at tst.c:16
(gdb) break func
Breakpoint 2 at 0x40049f: file tst.c, line 5.
(gdb) list func
1 #include <stdio.h>
2
3 int func(int n)
4 {
5 int sum = 0, i;
6 for (i = 0; i < n; i++)
7 {
8 sum += i;
9 }
10 return sum;
(gdb) info break
Num Type Disp Enb Address What
1 breakpoint keep y 0x00000000004004ce in main at tst.c:16
2 breakpoint keep y 0x000000000040049f in func at tst.c:5
####
常用的最基础的gdb调试命令:
run:运行程序(运行到断点或者程序结束)
next:单条语句执行
continue:继续运行程序(必须先有run才能有continue,继续运行,运行到下一个断点或者是程序结束)
print:打印信息
bt:查看堆栈信息
finish:退出当前函数
quit:退出gdb
二:
一般来说GDB主要是调试c/c++的程序,要调试c/c++的程序,首先在编译的时候,我们必须把调试的信息加到可执行文件中。使用编译器(cc/gcc/g++)的-g参数可以做到这一点。如:
cc -g hello.c -o hello
g++ -g hello.c -o hello
gcc -g hello.c -o hello
如果没有-g,你将看不见程序的函数名、变量名、所替代的全是运行是的内存地址。让你用-g把调试信息加入之后,并成功编译目标代码以后,就可以用GDB来调试他了。
启动GDB的方法有以下几种:
1,gdb [program]
program一般是当前的可可行文件,一般在当前目录下。
2,gdb [program] core
用gdb同时调试一个运行程序和core文件,core是程序非法执行后core dump后产生的文件。
3,gdb [program] [PID]
如果你的程序是一个服务程序,那么你就可以指定这个服务程序运行时的进程ID。gdb会自动attach上去,并调试他。progr应该在PATH环境变量中搜索的到。
在GDB环境中,你可以执行UNIX的shell的命令,使用GDB的shell命令来完成:
shell [command string]
调用UNIX的shell来执行command string,环境变量shell中定义的UNIX的shell将会被用来执行command string,如果shell还没有定义,那就是用UNIX的标准shell:/bin/sh (在Windows中使用Commad.com或者cmd.exe)
还有一个GDB命令是make:
make [make-argv]
可以在GDB中执行make命令来重新build自己的程序,这个命令等价于”shell make [make-argv]”
在GDB中运行程序:
当以gdb [program] 方式启动gdb后,gdb会在PATH路径和当前目录中搜索[program]的源文件。如果要确认gdb是否读到源文件,可以使用l或者list命令,看看gdb是否能列出源代码。
调试已经运行的程序:
两种方法:
1,在UNIX下线用ps查看正在运行的程序的PID,然后用gdb [program] PID格式挂载正在运行的程序。
2,先用gdb [program]关联上源代码,并进行gdb,在gdb中用attach来挂接住进程的PID,并用detach来取消挂载的进程。
在调试程序中,暂停程序运行是必须的,GDB可以方便地暂停程序的运行。你可以设置程序在哪行停住,在什么条件下停住,在收到什么信号的时候停住等等。以便于你查看运行时的变量,已经运行时的流程。
当进程被gdb停住时,你可以使用info program来查看程序是否在运行,进程号,被暂停的原因。
在gdb中,我们可以有以下几种暂停方式:断点(breakp)、观测点(watchpoint)、捕捉点(catchpoint)、信号(signals)、线程停止(Thread Stops)。如果要回复程序的运行,可以使用continue命令。
一,设置断点breakpoint
我们用break命令来设置断点,有几个设置断点的方法:
break [function]
在进入指定函数时停住。
break [linenum]
在指定行号停住。
break +offset
break -offset
在当前行号的前面或者后面的offset行停住,offset为自然数。
break filename:linenum
在源文件filename的linenum行停住
break filename:function
在源文件filename的function函数的入口处停住。
break *address
在程序运行的内存地址处停住。
break
break命令没有参数时,表示在下一条指令处停住。
break … if condition
…可以是上述的参数,condition表示条件,在条件成立时停住。比如在循环中,可以设置break if i==100,表示当i为100时停住程序。
查看断点时,可以使用info命令,如下所示:(n表示断点号)
info breakpoints [n]
info break [n]
二,设置观察点(watchpoint)
观察点一般来观察某个表达式(变量也是一种表达式)的值是否有变化了,如果有变化,马上停住程序。有下面几种方法来设置观察点:
watch [expr]
为表达式(变量)expr设置一个观测点,一旦表达式值有变化时,马上停住程序。
rwatch [expr]
当表达式(变量)expr被读时,停住程序。
awatch [expr]
当表达式(变量)的值被读或者被写时,停住程序。
info watchpoints
列出当前所设置了的所有观察点。
三,设置捕捉点(catchpoint)
你可以设置捕捉点来捕捉程序运行时的一些事件。如:载入共享库(动态链接库)或者c++的异常。设置捕捉点的格式为:
catch [event]
当event发生时,停住程序。evnet可以是下面的内容:
1,throw一个c++抛出的异常 (throw为关键字)
2,catch一个c++捕捉到的一场 (catch为关键字)
3,exec调用系统调用exec时(exec为关键字,目前此功能只在hp-ux下有用)
4,fork调用系统调用fork时。(fork为关键字,目前此功能只在hp-ux下有用)
5,load或者load [libname]载入共享库(动态链接库)时。(load为关键字,目前此功能只在hp-ux下有用)
6,unload或unload [libname]卸载共享库(动态链接库)时。(unload为关键字,目前此功能只在hp-ux下有用)
四、维护停止点
上面说了如何设置程序的停止点,GDB中的停止点也就是上述的三类。在GDB中,如果你觉得已经定义好的停止点没有用了,你可以使用delete clear disable enable这几个命令来进行维护。
clear
清除所有的已经定义的停止点。
clear [function]
clear [filename:function]
清除在所有设置在函数上的停止点。
clear [linenum]
clear [filename:linenum]
清除所有设置在指定行上的停止点。
delete [breakpoints] [range…]
删除指定的断点,breakpoins为断点号,如果不指定断点号,则表示删除所有的断点。range表示断点号的范围(如3-7)。其简写命令为d。
比删除更好的一种方法是disable停止点,disable了的停止点,GDB不会删除,当你还需要时,enable即可,就好象回收站一样。
五,信号signals
信号是一种软中断,是一种处理异步事件的方法。一般来说,操作系统都支持许多信号。尤其是UNIX,比较重要应用程序一般都会处理信号。UNIX定义了许多信号,比如SIGINT表示中断字符信号,也就是Ctrl+C的信号,SIGBUS表示硬件故障的信号;SIGCHLD表示子进程状态改变信号;SIGKILL表示终止程序运行的信号,等等。信号量编程是UNIX下非常重要的一种技术。
GDB有能力在你调试程序的时候处理任何一种信号,你可以告诉GDB需要处理哪一种信号。你可以要求GDB收到你所指定的信号时,马上停住正在运行的程序,以供你进行调试,你可以使用GDB的handle命令来完成这一功能。
handle [signal] [keywords…]
在GDB中定义一个信号处理,信号[signal]可以以SIG开头或不以SIG开头,可以用定义一个要处理信号的范围(如:SIGIO-SIGKILL,表示处理从SIGIO到SIGKILL的信号,其中包括SIGIO,SIGIOT,SIHKILL三个信号),也可以使用关键字all来表明要处理的所有的信号。一旦被调试的程序接收到信号,运行程序马上会被GDB住,以供调试。
六,线程(Thread Stops)
如果你程序是多线程的话,你可以定义你的断点是否在所有的线上上,或是在某个特定的线程。GDB很容易帮你完成这一工作。
break [linespec] thread [threadno]
break [linespec] thread [threadno] if…
linespec指定了断点设置在的源程序的行号。threadno指定了线程的id,注意,这个id是GDB分配的,你可以通过”info threads“命令来查看正在运行程序中的线程信息。如果你不指定thread [threadno]则表示你的断点设在所有的线程上面。
七:查看