1、什么是GDB?
GDB 是 GNU 开源组织发布的一个强大的 UNIX 下调试程序工具。大家知道命令行的强大就是在于,其可以形成执行序列,形成脚本。UNIX 下的软件全是命令行的,这给程序开发提供了极大的便利,命令行软件的优势在于,他们可以非常容易的集成在一起,使用几个简单的已有工具的命令,就可以做出一个非常强大的功能。
2、GDB主要功能是什么?
(1)启动你的程序,可以按照你自定义的要求随心所欲的运行程序。
(2)可以让调试程序在你所指定的位置的断点处停止。
(3)当程序停止时,可以检查此时你的程序中所发生的事情。
(4)动态的改变你程序的执行环境。
3、一个简单代码示例
源程序:test_gdb.c
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
14 int main(void)
15 {
16 int i;
17 long result = 0;
18 for(i=1; i<=100; i++)
19 {
20 result += i;
21 }
22
23 printf("result[1-100] = %d \n", result);
24 printf("result[1-250] = %d \n", func(250));
25 return 0;
26 }
(1)编译代码,得到可执行文件
gcc -g test_gdb.c -o test_gdb
(2)启动gdb 调试
gdb test_gdb
(3)查看代码的命令
list
(4)设置断点
break 断点所处行号/具体函数名
如:break 15 ---在第十五行设置断点 break func ---在func()处设置断点
(5)查看设置的断点信息
info break
(6)运行调试
run
(7)继续执行下一行
next ---单条语句执行
(8)程序跳过断点继续执行
continue
(9)查看函数栈使用情况
bt
(10)退出gdb调试
quit
4、进一步认识GDB
4.1、关于 -g命令参数
一般来说 GDB 主要调试的是 C/C++程序。要调试 C/C++程序,首先在编译时,我们必须要把调试信息加到可执行文件中。使用编译器(cc/gcc/g++)的-g 参数可以做到这一点,如:
$ cc –g hello.c –o hello
$ g++ -g hello.cpp –o hello
如果没有加入-g,你将看不见程序的函数名,变量名,所代替的全是运行的内存地址。当你用-g 把调试信息加入之后,并成功编译目标代码以后,就可以在调试时看到具体的调试信息。
4.2、启动GDB的几种方式
(1)gdb programer
programer为编译生成的可执行文件,且位于当前可执行目录;此方式较为常用。
(2) gdb <program> core
用 gdb 同时调试一个运行程序和 core 文件,core 是程序非法执行后 core dump 后产生的文件。
(3) gdb <program> <PID>
如果你的程序是一个服务程序,那么你可以指定这个服务程序运行时的进程 ID。gdb会自动 attach 上去,并调试它。program 应该在 PATH 环境变量中搜索到。
4.3、GDB启动开关
GDB 启动时,可以加上一些 GDB 的启动开关,详细的开关可以用 gdb –help 来查看。
gdb 的命令很多,gdb 把之分成很多种类。help 命令只是列出了 gdb 的命令种类,如果要看种类中的命令,使用 help <class>命令,如:help breakpoints,查看设置断点的所有命令。也可以直接 help <command>来查看命令的帮助。
4.4、GDB命令自动补全功能
gdb 中,输入命令时,可以不用打全命令,只用打命令的前几个字符就可以了,当然,命令的前几个字符要标志着一个唯一的命令,在 linux 下,可以敲击两次 TAB 键来补齐命令的全称,如果有重复的,gdb 会把其列出来。
4.5、调试已运行程序的两种方式
方式1:在 UNIX 下用 ps 查看正在运行的程序的 PID(进程 ID),然后用 gdb<program> PID 格式挂接正在运行的程序。
方式2:先用 gdb<program>关联上源代码,并进行 gdb,在 gdb 中用 attach 命令来挂接进程的PID,并用 detach 来取消挂接的程序。
4.6、借助GDB暂停程序运行的几种方式
断点(breakpoint)、观察点(watchpoint)、 捕捉点(catchpoint)、信号(signals)、线程停止(thread stops)。
(1)断点
break<function> 在进入指定函数时停住。C++中可以使用 class::function 或 function(type,type)格式来指定函数名。
break<linenum> 在指定行号停住。
break +offset 当前行号的 前面offset行停止。
break -offset 当前行号的后面的offset行停止。
break filename:linenum 在源文件 filename 的 linenum 行处停住。
break filename:function 在源文件 filename 的 function 函数的入口处停住。
break *address 在程序运行的内存地址处停住。
break break 命令没有参数时,表示在下一条指令处停住。
查看断点时,可使用命令 info 命令,如下所示:(注:n 表示断点号)
info breakpoints[n]
info break[n]
(2)捕捉点
我们可以设置捕捉点来捕捉程序运行时的一些事件。如:载入共享库(动态链接库)或是 C++的异常,设置捕捉点的格式为:
catch <event>
当 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 <libname> 载入共享库(动态链接库)时。(load 为关键字,
目前此功能只在 HP-UX 下有用)
7、 unload 或 unload <libname> 卸载共享库(动态链接库)时。(unload 为关
键字,目前此功能只在 HP-UX 下有用)
tcatch<event>
只设置一次捕捉,当程序停住后,断点被自动删除。
4.7、维护停止点
上面说了如何设置程序的停止点,GDB 中的停止点也就是上述的三类。在 GDB 中,如果你觉得已定义好的停止点没有用了,你可以使用 delete、clear、disable、enable 这几个命令来进程维护。
clear
清楚所有已定义的停止点。
4.8、 恢复程序运行和单步调试
(1)当程序被停住后,你可以用 continue 命令恢复程序的运行直到程序结束,或下一个断点的到来。也可以使用 step 或 next 命令单步跟踪程序。
continue [ignore-count]
c [ignore-count]
fg [ignore-count]
恢复程序运行,直到程序结束,或是下一个断点到来。Ignore-count 表示忽略其后的断点次数。continue,c,fg 三个命令都是一样的意思。
(2)step<count> 和 next<count>
单步跟踪,如果有函数调用,他会进入该函数。进入函数的前提是,此函数被编译有 debug 信息。像 VC 等工具中的 step in后面可以加 count,也可以不加,不加表示一条一条地执行,加表示执行后面的 count 条指令,然后再停住 。
next<count> 同样单步跟踪,如果有函数调用,他不会进入函数。
4.9、多线程中使用GDB(难点)
如果你的程序时多线程的话,你可以定义你的断点是否在所有的线程上,或是在某个特定的线程。GDB 很容易帮你完成这一工作。
break<linespec>thread<threadno>
break<linespec>thread<threadno>if…
注明:linespec 指定了断点是这在源程序的行号。Threadno 指定了线程 ID,注意,这个ID 是 GDB 分配的,你可以通过“info threads”命令来查看正在运行程序中的线程信息。如果你不指定 thread<threadno>则表示你的断点设在所有的线程上面。还可
以为某个线程指定断点条件。
当程序被 GDB 停住时,所有的运行线程都会被停住。这方便你查看运行程序的总体情况。而在你恢复程序运行时,所有的线程也会被恢复运行。哪怕是主进程在被单步调试时。
4.10 查看栈信息
当程序调用了一个函数,函数的地址,函数的参数,函数内的局部变量都会被压入“栈(stack)”中。我,我们可以使用 GDB 命令来查看当前的栈中的信息。下面是一些查看函数调用栈信息的 GDB 命令:
(1)backtrace
(2)bt
如果你要查看某一层的信息,你需要切换当前的栈,一般来说,程序停止时,最顶层的栈就是当前的栈,如果你要查看栈下面层的详细信息,首先要做的是切换当前栈。
frame<n>
f<n>
n 是一个从 0 开始的整数,是栈中的层编号。比如:frame 0,表示栈顶,frame 1,表示栈的第二层。
up<n>
表示向栈的上面移动 n 层,可以不打 n,表示向上移动一层。
down<n>
表示向栈的下面移动 n 层,可以不打 n,表示向下移动一层。
info frame
info f
这个命令会打印出更为详细的当前栈层的信息,只不过,大多数都是运行时的内存地址 。比如函数的地址,调用函数的地址,被调用函数的地址,目前的函数是由什么样的程序语言写成的、函数参数地址及值,局部变量的地址等等。
4.11 GDB中查看源码
GDB 可以打印出所调试程序的源代码,当然,在程序编译时一定要加上-g 参数,把源程序信息编译到执行文件中。不然就看不到源程序了。当程序停下来以后,GDB 会报告程序停在了那个文件的第几行上,你可以用 list 命令来打印程序的源代码。还是来看一看查看源代码的 GDB 命令吧。
(1)list<linenum>
显示程序第 linenum 行周围的源程序。
(2)list<function>
显示函数名为 function 的函数的源程序。
(3)list
显示当前行后面的源程序
(4)list -
显示当前行前面的源程序。
(5)show listsize
查看当前 listsize 的设置。
(6)调整 listsize的默认值
set listsize <count>
4.12、查看运行时的数据
在你调试程序时,当程序被停住时,可以使用 print 命令(简写命令为 p),或是同义命令inspect 来查看当前程序的运行数据。print 命令的格式是:
print<expr>
print/<f><expr>
<expr>是表达式,是你所调试的程序的语言的表达式(GDB 可以调试多种编程语言),<f>是输出的格式,比如,如果要把表达式按 16 进制的格式输出,那么就是/x。
4.13、环境变量
你可以在 GDB 的调试环境中定义自己的变量,用来保存一些调试程序中的运行数据。要定义一个 GDB 的变量很简单。使用 GDB 的 set 命令。GDB 的环境变量和 UNIX 一样,也是以$起头。如:set $foo = *object_ptr
使用环境变量时,GDB 会在你第一次使用时创建这个变量,而在以后的使用中,则直接对其赋值。环境变量没有类型,你可以给环境变量定义任一的类型。包括结构体和数组。
show convenience
该命令查看当前所设置的所有的环境变量。
4.14、CDB中查看寄存器的值
要查看寄存器的值,很简单,可以使用如下命令:
info registers
查看寄存器的情况(除了浮点寄存器)。
info all-registers
查看所有寄存器的情况(包括浮点寄存器)。
info registers<regname…>
查看所有指定的寄存器的情况。
寄存器中放置了程序运行时的数据,比如程序当前运行的指令地址(ip),程序的当前堆栈地址(sp)等等。你同样可以使用 print 命令来访问寄存器的情况,只需要在寄存器名字前加一个$符号就可以来看。如:p $eip。
4.15、强制函数返回
如果你的调试断点在某个函数中,并还有语句没有执行完。你可以使用 return 命令强制函数忽略还没有执行的语句并返回。
return
return <expression>
使用 return 命令取消当前函数的执行,并立即返回,如果指定了<expression>,那么该表达式的值会作为函数的返回值。
4.16、强制调用函数
call<expr>
表达式中可以是函数,以此达到强制调用函数的目的。并显示函数的返回值,如果函数返回值是 void,那么就不显示。
另一个相似的命令也可以完成这一功能——print,print 后面可以跟表达式,所以也可以用他来调用函数,print 和 call 不同的是,如果函数返回 void,call 则不显示,而 print 显示函数返回值,并把该值存入历史数据中。
5、GDB支持的编程语言
GDB 支持下列语言:C,C++,Fortran,PASCAL,Java,Chill,assembly 和 Modula-2。一般说来,GDB 会根据你所调用的程序来确定当时的调试语言,比如:发现文件后缀名为“.c”的,GDB 会认为是 C 程序。文件后缀名为“.c,.cc,.cp,.cpp,.cxx,.c++”的 ,GDB 会认为是 C++程序。而后缀是“.f,.F”的,GDB 会认为是 Fortran 程序,还有,后缀
名如果是“.s,.S”,GDB 会认为是汇编语言。
也就是说,GDB 会根据你所调试的程序的语言,来设置自己的语言环境,并让 GDB 的命令跟着语言环境的改变而改变。比如一些 GDB 命令需要用到表达式或变量时,这些表达式或变量的语法,完全是根据当前的语言环境而改变。如果你当前的程序是由几种不同语言一同编译成的,在调试过程中,GDB 也能根据不同的语言自动的切换语言环境。这种跟着语言环境而改变的功
能,真是体贴开发人员的一种设计。
下面是几个相关于 GDB 语言环境的命令:
(1)show language
(2)info frame
查看当前函数的程序语言。
(3)info source
查看当前文件的程序语言。
如果 GDB 没有检测出当前的程序语言,那么我们也可以手动设置当前的程序语言,使用set language 命令即可做到。
6、后记
GDB本身功能比较强大,本文只是简明扼要的描述了一些基本的或常用的功能及参数,其他功能需要在实际使用过程中不断学习。