linux下GDB使用简单教程

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本身功能比较强大,本文只是简明扼要的描述了一些基本的或常用的功能及参数,其他功能需要在实际使用过程中不断学习。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只特立独行的程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值