GDB使用介绍

转载请注明出处:http://blog.csdn.net/horkychen 

GDB使用介绍

Linux下最强大的Debug工具就是GDB了,许多IDE都集成了GDB进行调试。使用源代码级调试能够更直接的进行调试,效率明显高于输出Log信息。但目前无论是Mac下的XCode,还是Linux下的其它集成工具,对于调试库函数都是相当困难的,如果直接使用GDB这些问题就迎刃而解。我们首先来探讨一下GDB的基础知识。

 

GDB调试流程

 

GDB调试依赖于编译器输出的调试信息,所以进行调试前必须确定GCC输出了调试信息。

1.生成符号文件

使用GCC编译时需要生成相应的调试信息,编译时可以使用-g选项:

<<详细内容参GCC Manual>> Section 3.9

-g  表示将Symbol Table以系统原生格式直接生成到可执行文件中

 -g选项最好不要同-O一起使用,因为代码经过优化后有时会同源代码差异很大,可能找不到指定的变量等等。

-ggdb  表示将专门为GDB调试使用生成调试信息,它会包括很多GDB的扩展信息

其它的Symbol Table的格式还有COFF,DWARF,Stabs. (Mac OS默认为DWARF. DWARF也是基于COFF实现[Common Object File Format])

 

输出的调试信息的多少由三个等级:

-g[level]  默认为2

  0  表示不生成任何调试信息

  1  表示生成最少的调试信息,不提供局部变量及源代码行列等信息。

  2  标准模式

  3  2而言,包含了宏定义等额外信息

 

其它同调试相关的编译选项还有:

-p 生成额外的代码用于输出profile信息,用于另一个工具程序使用:prof

--coverage 用于统于代码的覆盖率

--ftest-coverage 类似上面的--coverage

-d*  用于Dump一些有用的信息,详细内容参GDB Manual.

 

 

2.启动GDB进行调试

下面的过程,我都以下面的工程进行解释:

 目标程序: text2bin

 源代码:text2bin.c

 功能: 将文本文件转成二进制文件

  使用方法: ./text2bin txt_file_name  [offset]

                    txt_file_name为源文本文件名

                    offset指定忽略左侧多少字节

 

A.调试应用程序

(1)启动  

  直接在命令行下输入gdb ./text2bin 或者运行gdb后输入file ./text2bin都可以加载指定的应用程序.

GDB会显示加载Symbols的过程,注意如果没有出现加载text2bin的调试信息的过程,就是表明无法获取调试信息!

Reading symbols for shared libraries ... done

Reading symbols from /Horky/Project/WINBASE/WINBASE/TextToRaw/text2bin...

warning: UUID mismatch detected between:

    /Horky/Project/WINBASE/WINBASE/TextToRaw/text2bin

 

(2)设置断点

  *除了断点外还有Watchpoints(观测点)Catchpoints (异常捕捉点)

  输入bbreak加上断点位置或断点函数名,

  b main  #main函数入口设置断点

  b text2bin.c:50  在源代码第50行设置断点

 

 如果需要查看断点信息可以使用指令:

 info breakpoints

 

 清除所有断点使用指令:

 clear

 清除特定的断点使用指令:

 clear text2bin.c:50

  clear main

 

 在调试过程可以使用disableenable开关某个特定的断点.enable 2 disable 2开关第2个断点。

在使用info b查看断点时,注意其中Enb栏位的变化。

 

对于观测点(Watchpoints),是指在某个条件下触发的断点,text2bin77:

Buffer2[nCount++] = ConvertTextToInt(sData);

 

我们要查看当nCount10时的运行状况,我们可以通过下面的步骤完成:

 a. 执行b 77,返回这个断点号是3

 b. 执行condition 3 nCount=10

 过程如下:

(gdb) b 77

Breakpoint 3 at 0x1c73: file text2bin.c, line 77.

(gdb) condition 3 nCount=10

(gdb) info breakpoints

Num Type           Disp Enb Address    What

1   breakpoint     keep y   0x00001e1e in ConvertTextToInt at text2bin.c:124

    breakpoint already hit 1 time

2   breakpoint     keep y   0x00001e25 in ConvertTextToInt at text2bin.c:125

3   breakpoint     keep y   0x00001c73 in main at text2bin.c:77

    stop only if nCount = 10

 

这样就可以控制当nCount10时在77行处中断.

 

如果在调试时,需在下面若干行代码后追加一个断点,在指定位置可以使用偏移量来指定断点,

b +5 b -5,即表示在当前行的后五行及前五行位置设置断点

 

 

(3)控制调试过程

在开始时需要告诉GDB目标程序是哪一个,可以用gdb ./text2bin,也可以在启动gdb后使用指令:

file ./text2bin来指定。

运行则使用r/run指令,可以同时带上参数,

r ./expert.txt 7

 

在调试过程中需要有一系列操作控制调试的过程:

 c / continue  /fg

 *fgforeground的缩写

从断点状态恢复程序的执行.

 

 s / step [count]

 单步执行 (step in)

 

 n / next [count]

 单步执行,跳过函数 (step out)

 

 u / until [location]

 执行到某个位置。 当遇到循环时可以使用此指令方便地跳转到指定的位置。

 

 finish

 执行到当前函数结束位置为止,同时显示函数返回值

 

 backtrace

 查看当前位置的被调用路径

 

(4)监测变量及内存

简单地显示变量的值可以使用print指令直接输出.

p / print [expression]

如输出main函数中的文件名变量 p argv[1]

    输出ConvertTextToInt函数中n的值 p np ConvertTextToInt::n

 

查看内存内容时则使用x指令:

x /nfu addr  以指定格式显示内存内容

x addr  显示指定地址处理内存内容

x 显示当前数据段内容

 

如以字串形显示某内存内容的指令为:

   x /sb 0xbffff80e

以数据形式显示某内存中5个字节的内容的指令为:

  x /5db 0xbffff80e

 

设定local variable watch,用来在每条执行后显示某些变量的值,可以使用display指令来指定,:

display S

display /sb 0xbffff707

 

去除时使用undisplay #  (#display列表中的序号)

 

display后面所带的参数同x指令:

n 表示repeat count

f 表示格式,分为:

  x  十六制数据

  d  带符号之整型数据

  u  无符号整型数据

  o  八进制整型数据

  t  二进制数据

  a 以地址格式显示,包括十六进制及偏移量

  c 以字符形式输出

  f  小数位输出

  s  以字串形式输出

 

u表示大小单位:

  b                 Bytes

  h                 Halfwords

  w                 Words

  g                  Giant words (eight bytes) 

 

(5)查看源代码

使用l / list指令就可查看源代码了,

:

 l 150 查看当前代码的第150

l text2bin.c:150  查看text2bin.c150.

l main  查看main函数内容

查看时如果需要翻页,直接回车.

 

默认GDB一次显示10,我们也可以通过set linesize [count]进行调整。

 

B.如何调试动态库或静态库

当调试库函数时,需要透过主程序调用的形式来挂载,所以不能直接使用GDB对目标库进行调试,而是需要attach指定的父进程,然后再进行测试.这里有两种情况:

(1)主程序启动时自动加载库,此时使用GDB挂载时也会自动加载相应的调试信息.

(2)主程序动态加载库,对于这种情况则需要另外使用symbol filename来加载特定的调试信息。

 

*提醒:当进行多线程调试时,一定要确保能找出真正的主线程。

*断开主线程时,使用detach指令来完成

 

    

C.如何调试多线程的程序

我们在写程序时常常会有多线程的运用, 比如有些程序中读取数据及数据处理,就是通过两个线程来完成的。对

多线程进行调试最大的难点在于线程的同步问题.

GDB提供了一套指令针对多线程:

info threads

  查看当前有多少线程

thread # 

  切换到指定线程

set print thread-events on/off

  设定是否打印线程状态

当设置中断时,也可以专为某个线程设置,

 b/break [location]  thread #

即表示为#线程在location处设置断点,这样就可以进行线程别的调试。

 

 

3. 技巧

(1)GDB中如果需要调用外部程序可以使用shell [command]来完成

(2)当源代码目录被移动了,或者在另一台PC上调试,GDB不能通过Debug信息找到源代码时,可以使用directory/dir来指搜索的目录

 

4.GDB的前台程序(GDB frontend)

  使用命令行是显得不方便了,所以我们可以选择一些GDB前端程序:

  DDD   [GNU] (http://www.gnu.org/software/ddd  目前功能最为强大的GDB前端程序)  

  Nemiver  [GNOME]  (http://home.gna.org/nemiver/)

  Kdbg   [KDE] (www.kdbg.org)

  Insight  [Wirte in Tcl/Tk] (http://sourceware.org/insight)

  Emacs (不用介绍了!)

  一般的IDE也带有GDB frontend程序,XCode,KDevelop,Anjuta,Eclipse.

 

 *GDB Frontend都是通过伪终端(pseudo-terminal)的方式来实现,有兴趣可以了解一下.

 

 

 扩展GDB的功能

已经有人在通GDB进行代码覆盖率测试,事实上我们也可以通过类似GDB的方式读取Debug信息中的符号表来进行语法检查。有关Debug信息的存放,可以使用objdump -xreadelf -a来查看其中的不同,这有助更好的理解程序的结构.

 

需求是多样的,GDB本身提供了两种方式来扩展GDB,一种为组合GDB的指令,类似宏的方式,另一种方式则是功能强大的python脚本。

(1)GDB环境下使用define指令来定义一个指令,

 define localv

  info scope $arg0

 end

这样我们在使用时,想查看main函数中的所有的变量,就可以通过下面的指令完成:

(gdb) localv main

Scope for main:

Symbol argc is at the address (reg 5 + 8), length 4.

Symbol argv is at the address (reg 5 + 12), length 4.

Symbol fpSrc is at the address (reg 5 + -44), length 4.

......

 

如果这样的指令非常好用,每次调试时都定义一次不太现实。所以GDB允许将这些操作定义在一个文本文件中,然后在GDB中使用source [command_file]来执行,source /TestData/localv.cmd

在执行过程中GDB不会显示每个指令的执行结果,如果需要显示就在source-v来打开。

 

除了组织指令集外,还有另一种有用的自定义指令: Hooks. GDB允许用户指定在特定的GDB指令执行前后执行一段自定义指令。比如,如果希望在设置断点前后都显示当前断点状况,就可以定义两个如下指令:

(gdb) define hook-break

Type commands for definition of "hook-break".

End with a line saying just "end".

>info b

>end

(gdb) define hookpost-break

Type commands for definition of "hookpost-break".

End with a line saying just "end".

>info b

>end

 

然后执行break / b指令时就可以看到类似下面的输出:

(gdb) b GetFileSize

Num Type           Disp Enb Address    What

1   breakpoint     keep y   0x00001a68 in main at text2bin.c:25

2   breakpoint     keep y   0x00001e1e in ConvertTextToInt at text2bin.c:124

Breakpoint 3 at 0x1d86: file text2bin.c, line 108.

Num Type           Disp Enb Address    What

1   breakpoint     keep y   0x00001a68 in main at text2bin.c:25

2   breakpoint     keep y   0x00001e1e in ConvertTextToInt at text2bin.c:124

3   breakpoint     keep y   0x00001d86 in GetFileSize at text2bin.c:108

 

hookhookpost即表示在某个指令的前后。后面的指令一定要使用GDB指令的全写,如上面就不能写成define hook-bdefine hookpost-b

 

*如果需要更为详细的资料,请参考GDB Manual,20. Extending GDB

 

(2)GDB环境可以直接调用python,如在GDB环境下执行python print 23

使用Python编写脚本同上面定义指令集是类似的,可以执行指令python, GDB就会要求输入python脚本,并以end为结束标志。

GDB为编写Python提供了一个新的模块gdb,在脚本中可以进行引用,其中包括了几个主要的指令:

  execute command    commandGDB CLI(Command Line Interface)指令字串.

  get_parameter parameter  获取一项GDB的参数,诸如上面提到的linesize.

  write string  输出一个字串到GDB输出窗口

  flush    Flush当前GDB输出流

 

*只有当编译GDB时指定了—with-python,GDB才会支持python指令.

 

参考文档:

(1) 使用GDB进行代码覆盖率测试

       http://www.ibm.com/developerworks/cn/linux/l-cn-gdb/

(2)  使用 GDB 调试 Linux软件

       http://www.ibm.com/developerworks/cn/linux/sdk/gdb/

 (3)  掌握 Linux 调试技术

       http://www.ibm.com/developerworks/cn/linux/sdk/l-debug/

(4)  GDB调试程序

       http://docs.chinalinuxpub.com/doc/pro/gdb.html

 (5) GDB调试精粹及使用实例

       http://fanqiang.chinaunix.net/program/other/2006-07-14/4834.shtml

 (6) GDB的官方文档

       http://www.gnu.org/software/gdb/documentation/

 (7) GDB指令参考 (可以打出来方便查询)

       http://users.ece.utexas.edu/~adnan/gdb-refcard.pdf

 

posted on 2012-06-19 11:43  c语言源码  阅读(307)  评论(0编辑  收藏  举报

导航