用GDB调试程序

用GDB调试程序
一、GDB概述 2
二、使用GDB 2
三、GDB的命令概貌 2
四、在GDB中运行程序 2
五、暂停/恢复程序运行 2
1、设置断点(Break Point) 2
2、设置观察点(WatchPoint) 3
3、设置捕捉点(CatchPoint) 3
4、维护停止点 3
5、停止条件维护 4
6、恢复程序运行和单步调试 4
7、线程(Thread Stops) 4
六、查看栈信息 5
七、查看源程序 6
1、显示源代码 6
2、搜索源代码 6
3、指定源文件的路径 6
八、查看运行时的数据 6
1、表达式 6
2、程序变量 6
3、数组 7
4、输出格式 7
6、自动显示 7
九、改变程序的执行 7
1、修改变量的值 7
2、强制函数返回 7
3、强制调用函数 8

一、GDB概述
GDB 是GNU开源组织发布的一个强大的UNIX下的程序调试工具。
二、使用GDB
在用GDB调试C/C++的程序时,首先在编译时,我们必须要把调试信息加入到可执行文件中。使用编译器(cc/gcc/g++)的 -g 参数可以做到这一点。如:
cc -g test.c –o test 或 g++ -g test.cpp –o test
启动GDB的方法:
gdb program也就是我们的执行文件,一般在当前目录下。
三、GDB的命令概貌
启动gdb后,就可以使用gdb的命令开始调试程序了,使用help命令可查看gdb的命令,命令格式为:help。
GDB的命令很多,gdb把其分成许多个种类。help命令,将列出gdb的命令种类,如果要看种类中的命令,可以使用help 命令。如 help list ,查看有关list 命令使用方法。
list linenum :显示程序第linenum行的周围的源程序
list first,last: 显示从first行到last行之间的源代码。
list func:显示函数名为func的函数的源程序
list : 显示当前行后面的源程序
list – :显示当前行前面的源程序
注:一般打印的是当前行的上5行和下5行,如果显示函数时上2行下8行,默认总显示行数为10行。当然,我们可以定制显示的范围,使用下面命令可以设置一次显示源程序的函数。
set listsize num //num为设置一次显示源代码的函数;
show listsize //查看当前listsize的设置
GDB中,输入命令时,可以不用打全命令,只用打命令的前几个字符就可以了,当然,命令的前几个字符应该要标志着一个唯一的命令。按下两次tab键后,gdb会把我们的函数补充完整。按两次tab键后补齐函数名的功能在调试C++程序时,可以查看到C++中的所有重载函数及参数。
要退出gdb,只用发quit或简称q就可以了。
四、在GDB中运行程序
在gdb中,运行程序使用r或run命令。
五、暂停/恢复程序运行
调试程序中,暂停程序运行是必须的,GDB可以方便地暂停程序的运行。 当进程被gdb停住时,你可以使用info program来查看程序是否在运行,进程号,被暂停的原因。
1、设置断点(Break Point)
gdb用break命令来设置断点,有以下几种设置断点的方法:
1> break 在进入指定函数时停住。C++中可以使用class::function或function(type,type)格式来指定函数名。
2> break 在指定行号处停住。
3> break +offset/-offset在当前行号的前面或后面的offset行停住。Offset为自然数。
4>break filename:function 在源文件filename的function函数的入口处停住。
5>break *address 在程序运行的内存地址处停住。
6>break 不带参数时,表示在下一条指令处停住。
7>break…if …可以是上述的参数,condition表示条件,在条件成立时停住。比如在循环体,可以设置break if i=100,表示当i为100时停住程序。
查看断点信息,可以使用info命令,如下所示:(n表示断点号)
info breakpoints [n]
info break [n]
2、设置观察点(WatchPoint)
观察点一般来观察某个表达式(变量也是一种表达式)的值是否有变化了,如果有变化,马上停住程序。有以下几种方法来设置观察点:
watch 为表达式(变量)expr设置一个观察点。一旦表达式值有变化时,马上停住程序。
rwatch expr被读时,停住程序。
awatch 当表达式(变量)的值被读或被写时,停住程序。
info watchpoints 列出当前所设置的所有的观察点
3、设置捕捉点(CatchPoint)
你可以设置捕捉点来捕捉程序运行时的一些事件。如:加载共享库(动态链接库)或是C++的异常。设置捕捉点的格式为:
catch
当event发生时,停住程序。event可以是下面的内容:
1> throw 一个C++抛出的异常。(throw为关键字)
2> catch一个C++捕捉到的异常。(catch 为关键字)
3> exec 调用系统调用exec时。(exec为关键字,目前此功能只在HP-UX下有用)
4> fork 调用系统调用fork时。(fork 为关键字,目前此功能只在HP-UX下有用)
5> vfork 调用系统调用vfork时。(vfork为关键字,目前此功能只在HP-UX下有用)
6> load 或 load 载入共享库(动态链接库)时。(load为关键字,目前此功能只在HP-UX下有用)
7> unload 或 unload 卸载共享库(动态链接库)时。(unload为关键字,目前此功能只在HP-UX下有用)
tcatch
只设置一次捕捉点,当程序停住以后,此点将自动删除。
4、维护停止点
在GDB中,如果觉得已定义好的停止点没有用了,可以使用,delete、clear、disable、enable这几个命令来维护。
clear 清除所有的已定义的停止点。
clear 清除对应函数停止点
clear 清除所有设置在函数上的停止点。
clear 清除指定行处的停止点
clear 清除所有设置在指定行上的停止点。
delete [breakpoints] [range…]
删除指定的断点,breakpoints为断点号。如果不指定断点号,则表示删除所有的断点。range 表示断点号的范围(如:3-7)。其简写命令为d。
比删除更好的一种方法是disable停止点,disable了的停止点,GDB不会删除,当你还需要时,enable即可,就好像回收站一样。
disable [breakpoints] [range…]
disable所指定的停止点,breakpoints为停止点号。如果什么都不指定,表示disable所有的停止点。简写命令是dis.
enable [breakpoints] [range…]
enable所指定的停止点,breakpoints为停止点号。
enable [breakpoints] once range…
enable所指定的停止点一次,当程序停止后,该停止点马上被GDB自动disable。
enable [breakpoints] delete range…
enable所指定的停止点一次,当程序停止后,该停止点马上被GDB自动删除。
5、停止条件维护
在GDB中可以设置一个条件,当条件成立时,程序自动停止。一般来说,使用 if关键词设置停止条件,if后面跟断点条件。条件设置好后,我们可以用condition命令来修改断点的条件。(只有break和watch命令支持if, catch目前暂不支持if)
condition 修改断点号为bnum的停止条件为expression。
condition 清除断点号为bnum的停止条件。
还有一个比较特殊的维护命令ignore,你可以指定程序运行时,忽略停止条件几次。格式如下: ignore
表示忽略断点号为bnum的停止条件count次。
6、恢复程序运行和单步调试
当程序被停住了,可用continue命令恢复程序的运行直到程序结束或下一个断点到来。也可以使用step或next命令单步跟踪程序。
continue [ignore-count] 或c [ignore-count] 或 fg [ignore-count]
恢复程序运行,直到程序结束,或是下一个断点到来。ignore-count表示忽略其后的断点次数。continue,c,fg三个命令都是一样的意思
step
单步跟踪,如果有函数调用,他会进入该函数。进入函数的前提是,此函数被编译有debug信息。很像VC等工具中的step in。后面可以加count也可以不加,不加表示一条条地执行,加表示执行后面的count条指令,然后再停住。
next
同样单步跟踪,如果有函数调用,他不会进入该函数。很像VC等工具中的step over。后面可以加count也可以不加。
finish
运行程序,直到当前函数完成返回。并打印函数返回时的堆栈地址和返回值及参数值等信息。
7、线程(Thread Stops)
如果程序是多线程的话,可以定义断点是否在所有的线程上,或是在某个特定的线程。
break thread break thread if …
linespec 指定了断点在源程序的行号,threadno指定了线程的ID,注意,这个ID是GDB分配的,你可以通过“info threads”命令来查看正在运行程序中的线程信息。如果你不指定thread 则表示你的断点设在所有线程上面。
六、查看栈信息
当程序被停住了,你需要做的第一件事就是查看程序是在哪里停住的。当你的程序调用了一个函数,函数的地址,函数参数,函数内的局部变量都会被压入“栈”(Stack)中。下面是一些查看函数调用栈信息的GDB命令:
backtrace 或bt
打印当前的函数调用栈的所有信息。如:
(gdb) bt
#0 func (n=250) at tst.c:6
#1 0x08048524 in main (argc=1, argv=0xbffff674) at tst.c:30
#2 0x400409ed in __libc_start_main () from /lib/libc.so.6
从上可以看出函数的调用栈信息:__libc_start_main –> main() –> func()
backtrace 或 bt
n是一个正整数,表示只打印栈顶上n层的栈信息。
backtrace <-n> 或 bt <-n>
-n表一个负整数,表示只打印栈底下n层的栈信息。
如果你要查看某一层的信息,你需要在切换当前的栈,一般来说,程序停止时,最顶层的栈就是当前栈,如果你要查看栈下面层的详细信息,首先要做的是切换当前栈。
frame 或 f
n是一个从0开始的整数,是栈中的层编号。比如:frame 0,表示栈顶,frame 1,表示栈的第二层。
up 表示向栈的上面移动n层,可以不打n,表示向上移动一层。
down 表示向栈的下面移动n层,可以不打n,表示向下移动一层。
查看当前栈层的信息,你可以用以下GDB命令:
frame 或 f
会打印出这些信息:栈的层编号,当前的函数名,函数参数值,函数所在文件及行号,函数执行到的语句。
info frame 或 info f
这个命令会打印出更为详细的当前栈层的信息,只不过,大多数都是运行时的内内地址。比如:函数地址,调用函数的地址,被调用函数的地址,目前的函数是由什么样的程序语言写成的、函数参数地址及值、局部变量的地址等等。如:
(gdb) info f
Stack level 0, frame at 0xbffff5d4:
eip = 0x804845d in func (tst.c:6); saved eip 0x8048524
called by frame at 0xbffff60c
source language c.
Arglist at 0xbffff5d4, args: n=250
Locals at 0xbffff5d4, Previous frame’s sp is 0x0
Saved registers:
ebp at 0xbffff5d4, eip at 0xbffff5d8
info args 打印出当前函数的参数名及其值。
info locals 打印出当前函数中所有局部变量及其值。
info catch 打印出当前的函数中的异常处理信息。
七、查看源程序
1、显示源代码
详细在第三部分GDB的命令概貌。
2、搜索源代码
GDB提供了源代码搜索的命令:
forward-search
search
向前面搜索。
reverse-search
全部搜索。
其中,就是正则表达式,也是一个字符串的匹配模式。
3、指定源文件的路径
某些时候,用-g编译过后的执行程序中只是包括了源文件的名字,没有路径名。GDB提供了可以让你指定源文件的路径的命令,以便GDB进行搜索。
directory
dir
加一个源文件路径到当前路径的前面。如果你要指定多个路径,UNIX下你可以使用“:”,Windows下你可以使用“;”。
directory
清除所有的自定义的源文件搜索路径信息。
show directories
显示定义了的源文件搜索路径。
八、查看运行时的数据
当程序被停住时,可以使用print命令(简写为p),或是同义命令inspect来查看当前程序的运行数据。print命令的格式是:
print 或 print /
是表达式,是你所调试的程序的语言的表达式(GDB可以调试多种编程语言),是输出的格式,比如,如果要把表达式按16进制的格式输出,那么就是/x。
1、表达式
print和许多GDB的命令一样,可以接受一个表达式,GDB会根据当前的程序运行的数据来计算这个表达式,可以是当前程序运行中的const常量、变量、函数等内容。
在表达式中,有几种GDB所支持的操作符如下:
@ 是一个和数组有关的操作符,在后面会有更详细的说明。
:: 指定一个在文件或是一个函数中的变量。
{} 表示一个指向内存地址的类型为type的一个对象。
2、程序变量
在GDB中,你可以随时查看以下三种变量的值:
1、全局变量(所有文件可见的)
2、静态全局变量(当前文件可见的)
3、局部变量(当前Scope可见的)
3、数组
为了查看一段连续的内存空间的值。如数组的一段,或是动态分配的数据的大小。你可以使用GDB的“@”操作符,“@”的左边是第一个内存的地址的值,“@”的右边则你你想查看内存的长度。如 int *array = (int *) malloc (len * sizeof (int));
在GDB调试过程中,可用如下命令显示出这个动态数组的取值:
(gdb) p *array@len
$1 = {2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40}
如果是静态数组的话,可以直接用print数组名,就可以显示数组中所有数据的内容了。
4、输出格式
一般来说,GDB会根据变量的类型输出变量的值。但也可以自定义GDB的输出的格式。x 按十六进制格式显示变量。 d 按十进制格式显示变量。
u 按十六进制格式显示无符号整型。 o 按八进制格式显示变量。
t 按二进制格式显示变量.。 a 按十六进制格式显示变量。
c 按字符格式显示变量。 f 按浮点数格式显示变量。
如:(gdb) p i
$21 = 101
(gdb) p /a i
$22 = 0x65
6、自动显示
可以设置一些自动显示的变量,当程序停住时,或是在单步跟踪时,这些变量会自动显示。相关的GDB命令是display。
格式为:display 或display/ 或display/
expr是一个表达式,fmt表示显示的格式,addr表示内存地址。
与display相对应的 GDB命令:
undisplay delete display
删除自动显示,dnums意为所设置好了的自动显式的编号。如果要同时删除几个,编号可以用空格分隔,如果要删除一个范围内的编号,可以用减号表示(如:2-5)
九、改变程序的执行
当GDB挂上被调试程序时,就可以根据自己的调试思路,来动态地在GDB中更改当前被调试程序的运行线路或是其变量的值。
1、修改变量的值
在GDB中,修改被调试程序运行时的变量值,使用GDB的print命令即可完成。如: (gdb) print x=4
有时候,可能变量和GDB中的参数冲突,为此,可以使用set var命令来设置变量,如:(gdb) set var width=47
2、强制函数返回
如果调试断点在某个函数中,并还有语句没有执行完。则可使用return命令强制函数忽略还没有执行的语句并返回。语法如下:
return 或return
使用return命令取消当前函数的执行,并立即返回,如果指定了,那么该表达式的值会被认作函数的返回值。
3、强制调用函数
call
表达式中可以是一函数,以此达到强制调用函数的目的。并显示函数的返回值,如果函数返回值是void,那么就不显示。