GDB调试

来源:互联网 发布:vb.net 入门 编辑:程序博客网 时间:2024/06/05 08:15

GDB是一个强大的命令行调试工具。命令行的强大就是在于:

  • 可以形成执行序列
  • 可以形成脚本shell

UNIX下的软件全是命令行的,这给程序开发提代供了极大的便利。命令行软件的优势在于,它们可以非常容易的集成在一起,使用几个简单的已有工具的命令,就可以做出一个非常强大的功能。

例如:实现自动化     可以使用shell文本包装几个简单的工具命令,让它们可以集成在一起发挥更多功能。

1. GDB概述

一般来说,GDB主要完成下面四个方面的功能:

  • 启动你的程序,可以按照你的自定义的要求运行程序。
  • 可让被调试的程序在你所指定的调置的断点处停住。(断点可以是条件表达式)
  • 当程序被停住时,可以检查此时你的程序中所发生的事。
  • 动态的改变你程序的执行环境。

2. GDB调试命令

backtrace       //显示程序中的当前位置和表示如何到达当前位置的栈跟踪(同义词:where)        breakpoint      //在程序中设置一个断点        cd              //改变当前工作目录        clear           //删除刚才停止处的断点      commands        //命中断点时,列出将要执行的命令        continue        //从断点开始继续执行      delete          //删除一个断点或监测点;也可与其他命令一起使用        display         //程序停止时显示变量和表达时        down            //下移栈帧,使得另一个函数成为当前函数       frame           //选择下一条continue        //命令的帧        info            //显示与该程序有关的各种信息        jump            //在源程序中的另一点开始运行       kill            //异常终止在gdb             //控制下运行的程序       list            //列出相应于正在执行的程序的原文件内容      next            //执行下一个源程序行,从而执行其整体中的一个函数        print           //显示变量或表达式的值        pwd             //显示当前工作目录      pype            //显示一个数据结构(如一个结构或C++类)的内容        quit            //退出gdb      run             //执行该程序      search          //在源文件中搜索正规表达式        set-variable    //给变量赋值    signal          //将一个信号发送到正在运行的进程        step            //执行下一个源程序行,必要时进入下一个函数        until           //结束当前循环      up              //上移栈帧,使另一函数成为当前函数      watch           //在程序中设置一个监测点(即数据断点)        whatis          //显示变量或函数类型Reverse-search      //在源文件中反向搜索正规表达式   

3. GDB调试

gcc是Linux或其他所有unix系统自带的编译器,它也有Windows的接口,不过在Windows上还是用Visual Studio的debugger比较简单。

假设程序文件名叫main.c,可以用以下的命令来编译它:

gcc main.c -o main //若程序没有error,生成目标文件main

如果想调试程序,需要告诉编译器:

gcc main.c -g -o main //-g 为调试命令gdb//或者gcc main.c -g -Wall -Werror -o main

这里讲一下以上编译选项:

  • -Wall –>显示所有的警告(warning all);
  • -W –> 只显示编译器认为可能出错的警告信息;
  • -w –> 忽略所有的警告信息。

一般情况,【-W】【-Wall】同时使用为佳:

gcc main.c -g -W -Wall -o main

GDB的启动、退出

启动GDB的方法有几种:

<1> gdb main  //main是执行文件,一般在当前目录下<2> gdb main core //同时调试一个运行程序和core文件//core是程序非法执行后系统为了记录错误信息而产生的文件<3> gdb main PID  //如果程序是一个服务程序,则可以指定这个服务程序     //运行时的PID,GDB会自动attach上去,并调试它。//main应该在PATH环境变量中搜索得到。

GDB启动时,可以添加一些GDB的启动开关gdb -help查看详细开关。一下列举一些常用参数:

--symbols=SYMFILE : 从指定文件中读取符号表--se=FILE : 从指定文件中读取符号表信息,并将它用在可执行文件中--core=COREFILE : 调试时系统产生的core文件--directory=DIR : 加入一个源文件的搜索路径。默认是PATH中定义的路径

退出:

kill //退出程序quit //退出GDB

运行程序

若以gdb main的方式启动GDB,GDB会在PATH路径和当前目录中搜索源文件。若不确定GDB是否找到源文件,可以用命令l/list查看是否有显示部分源代码。使用r/run运行程序前,可能需要进行一下5方面的设置:

  1. 设置运行参数
    • set args -> 指定运行时的参数,如set args 10 20 30 40
    • show args -> 查看设置好的运行参数
  2. 运行环境
    • path -> 设定程序的运行路径
    • showpaths -> 查看路径
    • set environmentVarname=value -> 设置环境变量,如set env USER=benshow
    • env [ varname ] -> 查看环境变量,不带参数则打印当前所有环境变量
  3. 工作目录
    • cd -> 相当于shell中的cd
    • pwd -> 显示当前所在目录
  4. 程序的输入、输出
    • info terminal -> 显示程序用到的终端的模式
    • 使用重定向控制输出输入,run > outfile
    • tty -> 设置输入输出使用的终端设备,如:tty/dev/tty1
  5. 调试已运行的程序
    • ps 查看正在运行的程序的PID,然后用gdb PID <process-id>格式挂接正在运行的程序
    • 先用GDB关联上源代码,并进行调试,在GDB中用attach <process-id>挂接进程PID。用delete取消挂接。

单步运行

若设置了断点,执行 run 命令后程序将停在第一次遇到的断点处,此时可用
- continue恢复运行,直到遇到下一个断点或程序结束。
- next、step单步执行:
- next -> 下一行代码,不进入函数内部
- step -> 遇到函数时,进入函数内部

//继续执行,三个命令作用一样,其中参数表示忽略后面的断点continue [ignore-count]c [ignore-count]fg [ignore-count]//单步跟踪程序,若遇到的函数被编译了有调试信息,则进入该函数//count表示执行后面的[count]条指令后停止step [count]s [count]//nextnext [count]n [count]//当前函数执行完后返回,并打印函数返回时的堆栈地址、返回值、参数值等finish//退出循环体until/u

设置断点

break function //b function,在函数处设置断点break linenum  //b linenum,在该行设置断点break +offset/-offset //在当前行的后一行/前一行设置断点break filename:linenum/function //进入文件breakif condition  //若果条件成立,暂停info breakpoints[n]  //查看断点,n 为短点号 ==info b[n]delete b[n] //删除断点

设置观察点

watch expr //为表达式(变量设置一个断点,当发生变化时停止运行)rwatch expr //表达式expr被读时,停止程序awatch expr //表达式的值被读写时,暂停程序info watchpoints[n] //查看观察点信息

设置捕捉点

可以通过设置捕捉点(陷阱)来捕捉程序运行时的一些事件,如载入共享库(动态的链接库)或是程序的异常。

catch  event  //event发生时,暂停tcatch  event  //只设置一次捕捉点,程序暂停后,捕捉点被自动清除

event可以是以下内容:

catch assert :     //捕捉ada语言的assert断言失败catch catch :      //捕捉C++收到的异常catch exception :  //捕捉ada语言的execptioncatch exec :       //捕捉exec调用catch fork :       //捕捉fork调用catch syscall :    //捕捉syscall调用catch throw :      //捕捉C++抛出异常catch vfork :      //捕捉vfork调用

维护停止点

断点、观察点和捕捉点统称为停止点,如果觉得设置的停止点没有作用了,可以使用delete、clear、disable、enable等命令来维护停止点。

clear [function/linenum/file-linenum]delete [breakpoints/range..] // ddisable [b/range…] //dis,比以上两方式更好,相当于放入回收站enable //让停止点生效

停止条件维护

针对break…if condition,condition设置好后还是可以更改的,方法如下:

//将原来bnum行的停止条件condition修改为expressioncondition bnum expression condition bnum    //清除改行的停止条件ignore bnum count //忽略改行的停止条件count次,任性!

Gdb实战

要使用GDB调试程序,必须确保被调试程序的可执行文件(.obj)附加了调试信息,为此,gcc / g++编译源码时需要使用-g选项:

$ gcc –g example.c –o example$ gdb example//or $ gdb    :file example //内部装载目标文件    :run          //gdb内置命令

如果一切顺利,程序运行到结束,gdb重新获得控制权;
如果中途崩溃,gdb会记录崩溃之前的堆栈信息,方便查找原因。

演示代码:(存在bug)

#include <stdio.h>int wib(int no1, int no2){    int result, diff;    diff = no1 - no2;    result = no1/diff;    return result;}int main(int argc, char *argv[]){    int value, div, result, total;    int i;    value = 10;    div = 6;    total = 0;    for (i=0; i<10; i++)    {        result = wib(value, div);        total += result;        div++;        value--;    }    printf("%d wibed by %d equals %d\n", value, div, total);    return 0;}

程序运行10次for循环,通过wib函数算出累加值,再打印出结果。
编译运行后,使用gdb内置命令调试代码:

(gdb)run...Program received signal SIGFPE, Arithmetic exception....in wib(no1=8, no2=8) at example.c : 8 8        result = no1/diff(gdb)

提示程序line_8收到了一个算术异常中断信号,同时给信号的代码行内容。可以通过list列出错误代码旁边的十行代码。从gdb消息可以看出,第八行的除法运算出了错,而这一行中将变量no1/diff,
要查看变量的值,用gdb内置命令print。

(gdb)print no1 $2=8(gdb)print diff$3=0

由此可推断出算术异常时由除数为0的除法运算造成的。通过print no1-no2重新估算这个变量。gdb告诉我们这两个变量都为8,所以我们需要检查调用了wib的main函数,查看在什么时候发生的。

(gdb)continue   //继续执行程序

使用断点

(gdb)list main  //打印main函数开始前后的代码(gdb)break main //进入main函数时设置断点(gdb)break 25   //在25行wib()函数出设置断点Breakpoint 1 at ...: file example.c, line 25(gdb)

gdb显示已经在请求的那一行设置了1号断点

(gdb)run    //从头开始运行程序,直到gdb发生中断

此时,gdb会生成一条消息,指出在哪个断点中断,以及程序运行到何处。

(gdb)next   //单步执行

跟多断点和观察点
要知道调用wib函数之前什么时候value等于div,因此在上一示例中第25行处设置了断点,程序必须执行两次才会发生这种状况。我们只要在断点上设置一个条件就能使gdb在value==div时暂停。要设置条件,可以在设置断点时同时定义出发条件:

(gdb)break 25 if value==div Breakpoint 1 at ... : file example.c, line 25(gdb)condition 1 div==value    #修改出发条件condition 1    #修改为无条件断点

显示断点信息

(gdb) info bNum     Type           Disp Enb Address            What1       breakpoint     keep y   0x000000000040052a in main at example

了解div什么时候更改

(gdb)watch div==valueHardware watchpoint 2:div==value(gdb)

OK ,基本操作就介绍到这里,要学会怎么使用还需要自己实践。实践了才能学到根本。


以下内容截自本人根据例子编译、调试所得,可以参考

zhang@zhang:~/code$ vim example.czhang@zhang:~/code$ gcc -g example.c -o examplezhang@zhang:~/code$ gdb example  //启动gdb调试Type "apropos word" to search for commands related to "word"Reading symbols from gdb_example...done.(gdb) run   //运行程序Starting program: /home/zhang/code/gdb_example //gdb显示程序接收到一个算术异常信号,并指出出现在哪部分代码Program received signal SIGFPE, Arithmetic exception.0x000000000040053d in wib (no1=8, no2=8) at example.c:88       result = no1/diff;(gdb) print no1   //打印出变量no1的当前值$1 = 8(gdb) print diff  //diff当前值$4 = 0(gdb) continue //继续运行程序Continuing.Program terminated with signal SIGFPE, Arithmetic exception.The program no longer exists.(gdb) list   //打印出错代码的后10行3   int wib(int no1, int no2)4   {5       int result, diff;6   7       diff = no1 - no2;8       result = no1/diff;9   10      return result;11  }12  (gdb) list main //打印main函数开始前后的代码9   10      return result;11  }12  13  int main(int argc, char *argv[])14  {15      int value, div, result, total;16      int i;17  18      value = 10;(gdb) //此处只需要按一下Enter键19      div = 6;20      total = 0;21  22      for (i=0; i<10; i++)23      {24          result = wib(value, div);25          total += result;26          div++;27          value--;28      }(gdb) b 24  //在24行处设置断点Breakpoint 1 at 0x400575: file gdb_example.c, line 24.(gdb) info b //查看断点具体信息Num     Type           Disp Enb Address            What1       breakpoint     keep y   0x0000000000400575 in main at example.c:24(gdb) run //继续运行,程序会在1号断点处暂停Starting program: /home/zhang/code/example Breakpoint 1, main (argc=1, argv=0x7fffffffde38) at example.c:2424          result = wib(value, div);(gdb) next //单步执行,下一条指令25          total += result;(gdb) 26          div++;(gdb) 27          value--;(gdb) (gdb) watch div==value  //Hardware watchpoint 4: div==value(gdb) continueContinuing.Breakpoint 1, main (argc=1, argv=0x7fffffffde38) at gdb_example.c:2424          result = wib(value, div);(gdb) Continuing.Hardware watchpoint 4: div==valueOld value = 0New value = 1main (argc=1, argv=0x7fffffffde38) at gdb_example.c:2222      for (i=0; i<10; i++)(gdb) info locals value = 8div = 8result = 4total = 6i = 1(gdb) info watchNum     Type           Disp Enb Address            What4       hw watchpoint  keep y                      div==value    breakpoint already hit 1 time