zoukankan      html  css  js  c++  java
  • GDB程序启动和断点设置

    GDB程序启动和断点设置

    前面章节介绍了如何启动GDB调试器,本节介绍如何在 GDB 调试器中启动(运行)程序,启动程序过程中的一些注意事项 以及借助 GDB 调试器在程序中的某个地方设置断点。

    程序启动

    根据不同场景的需要,GDB 调试器提供了多种方式来启动目标程序,其中最常用的就是 run 指令,其次为 start 指令。也就是说,run 和 start 指令都可以用来在 GDB 调试器中启动程序,它们之间的区别是:

    (1)、默认情况下,run 指令会一直执行程序,直到执行结束。如果程序中手动设置有断点,则 run 指令会执行程序至第一个断点处;

    (2)、start 指令会执行程序至 main() 主函数的起始位置,即在 main() 函数的第一行语句处停止执行。

    不仅如此,在进行 run 或者 start 指令启动目标程序之前,还可能需要做一些必要的准备工作,大致包括以下几个方面:

    (1)、如果启动 GDB 调试器时未指定要调试的目标程序,或者由于各种原因 GDB 调试器并为找到所指定的目标程序,这种情况下就需要再次手动指定;

    (2)、有些 C 或者 C++ 程序的执行,需要接收一些参数(程序中用 argc 和 argv[] 接收);

    (3)、目标程序在执行过程中,可能需要临时设置 PATH 环境变量;

    (4)、默认情况下,GDB 调试器将启动时所在的目录作为工作目录,但很多情况下,该目录并不符合要求,需要在启动程序手动为 GDB 调试器指定工作目录。

    (5)、默认情况下,GDB 调试器启动程序后,会接收键盘临时输入的数据,并将执行结果会打印在屏幕上。但 GDB 调试器允许对执行程序的输入和输出进行重定向,使其从文件或其它终端接收输入,或者将执行结果输出到文件或其它终端。

    示例程序:

    #include<stdio.h>
    int main(int argc,char* argv[])
    {
        FILE * fp;
        if((fp = fopen(argv[1],"r")) == NULL){
            printf("file open fail");
        }
        else{
            printf("file open true");
        }
        return 0;
    }

    命令行打开GDB后,是位于当前目录下,假设我们在root的家目录启动gdb。则在执行 main 之前,有以下几项要做:

    1) 首先,对于已启动的 GDB 调试器,我们可以先通过 l (小写的 L)指令验证其是否已找到指定的目标程序文件:

    可以看到,对于找不到目标程序文件的 GDB 调试器,l 指令的执行结果显示“无法加载符号表”。这种情况下,我们就必须手动为其指定要调试的目标程序,例如:

    可以看到,通过借助 file 命令,则无需重启 GDB 调试器也能指定要调试的目标程序文件。

     2) 通过分析 main.c 中程序的逻辑不难发现,要想其正确执行,必须在执行程序的同时给它传递一个目标文件的文件名。总的来说,为 GDB 调试器指定的目标程序传递参数,常用的方法有 3 种:

    (1)、启动 GDB 调试器时,可以在指定目标调试程序的同时,使用 --args 选项指定需要传递给该程序的数据。仍以 main程序为例:

    [root@all c]# gdb -args main a.txt

    整个指令的意思是:启动 GDB 调试器调试 main.exe 程序,并为其传递 "a.txt" 这个字符串(其会被 argv[] 字符串数组接收)。

    (2)、GDB 调试器启动后,可以借助 set args 命令指定目标调试程序启动所需要的数据。仍以 main 为例:

    (gdb) set args a.txt

    该命令表示将 "a.txt" 传递给将要调试的目标程序。

    (3)、还可以使用 run 或者 start 启动目标程序时,指定其所需要的数据。例如:

    (gdb) run a.txt
    (gdb) start a.txt3)

    3)默认情况下,GDB 调试器的工作目录为启动时所使用的目录。当然,GDB 调试器提供有修改工作目录的指令,即 cd 指令。例如,将 GDB 调试器的工作目录修改为 /root/c,则执行指令为:

    (gdb) cd /root/c

    4) 某些场景中,目标调试程序的执行还需要临时修改 PATH 环境变量,此时就可以借助 path 指令,例如:

    (gdb) path /temp/demo
    Executable and object file path: /root/c:/usr/local/sbin:/usr/local/bin...

    此修改方式只是临时的,退出 GDB 调试后会失效。

    5) 默认情况下,GDB 调试的程序会接收 set args 等方式指定的参数,同时会将输出结果打印到屏幕上。而通过对输入、输出重定向,可以令调试程序接收指定文件或者终端提供的数据,也可以将执行结果输出到文件或者某个终端上。

    例如,将 main 文件的执行结果输出到 a.txt 文件中,执行如下命令:

    (gdb) run > a.txt

    在 GDB 调试的工作目录下就会生成一个 a.txt 文件,其中存储的即为 main 的执行结果。

    总的来说,只有将调试程序所需的运行环境搭建好后,才能使用 run 或者 start 命令开始调试。如下是一个完整的实例,演示了 GDB 调试 mian之前所做的准备工作[root@all c]# pwd <--显示当前工作路径

    [root@all c]# ls       <-- 显示当前路径下的文件
    a.txt  main.c  main.exe
    [root@all c]# cd ~  <-- 进入 home 目录
    [root@all ~]# gdb -q      <-- 开启 GDB 调试器
    (gdb) cd /root/c            <-- 修改 GDB 调试器的工作目录
    Working directory /root/c.
    (gdb) file main               <-- 指定要调试的目标文件
    Reading symbols from main...
    (gdb) set args a.txt               <-- 指定传递的数据
    (gdb) run                               <-- 运行程序
    Starting program: /root/c/main a.txt
    file open true
    Program exited normally.

     设置断点

    在 GDB 调试器中对  C/C++ 程序打断点,最常用的就是 break 命令,有些场景中还会用到 tbreak 或者 rbreak 命令,本节将对这 3 个命令的功能和用法做详细的讲解。以一下一段C代码演示断点的使用:

    #include<stdio.h>
    int main(int argc,char* argv[])
    {
        int num = 1;
        while(num<100)
        {
            num *= 2;
        }
        printf("num=%d",num);
        return 0;
    }

    GDB break命令

    break 命令(可以用 b 代替)常用的语法格式有以下 2 种。

    1、(gdb) break location     // b location
    2、(gdb) break ... if cond   // b .. if cond

    1) 第一种格式中,location 用于指定打断点的具体位置,其表示方式有多种,如表 1 所示。

    表 1 location 参数的表示方式
    location 的值含 义
    linenum linenum 是一个整数,表示要打断点处代码的行号。要知道,程序中各行代码都有对应的行号,可通过执行 l(小写的 L)命令看到。
    filename:linenum filename 表示源程序文件名;linenum 为整数,表示具体行数。整体的意思是在指令文件 filename 中的第 linenum 行打断点。
    + offset
    - offset
    offset 为整数(假设值为 2),+offset 表示以当前程序暂停位置(例如第 4 行)为准,向后数 offset 行处(第 6 行)打断点;-offset 表示以当前程序暂停位置为准,向前数 offset 行处(第 2 行)打断点。
    function function 表示程序中包含的函数的函数名,即 break 命令会在该函数内部的开头位置打断点,程序会执行到该函数第一行代码处暂停。
    filename:function filename 表示远程文件名;function 表示程序中函数的函数名。整体的意思是在指定文件 filename 中 function 函数的开头位置打断点。

    2) 第二种格式中,... 可以是表 1 中所有参数的值,用于指定打断点的具体位置;cond 为某个表达式。整体的含义为:每次程序执行到 ... 位置时都计算 cond 的值,如果为 True,则程序在该位置暂停;反之,程序继续执行。

    如下演示了以上 2 种打断点方式的具体用法:

    [root@all c]# gdb main -q
    Reading symbols from /root/c/main...done.
    (gdb) l
    1    #include<stdio.h>
    2    int main(int argc,char* argv[])
    3    {
    4        int num = 1;
    5        while(num<100)
    6        {
    7            num *= 2;
    8        }
    9        printf("num=%d",num);
    10        return 0;
    (gdb) 
    11    }
    (gdb) b 4                                         # 程序第4行打断点
    Breakpoint 1 at 0x4004d3: file main.c, line 4.
    (gdb) r                                           # 运行程序,到第4行暂停
    Starting program: /root/c/main 
    
    Breakpoint 1, main (argc=1, argv=0x7fffffffe278) at main.c:4
    4        int num = 1;
    Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.149.el6.x86_64
    (gdb) b +1                                        # 在第4行的基础上,在第5行代码处打断点
    Breakpoint 2 at 0x4004da: file main.c, line 5.
    (gdb) c                                           # 继续执行程序,到第5行暂停
    Continuing.
    
    Breakpoint 2, main (argc=1, argv=0x7fffffffe278) at main.c:5
    5        while(num<100)
    (gdb) b 7 if num>10                               # 如果 num > 10 ,在第7行打断点
    Breakpoint 3 at 0x4004dc: file main.c, line 7.
    (gdb) c                                           # 继续执行
    Continuing.
    
    Breakpoint 3, main (argc=1, argv=0x7fffffffe278) at main.c:7
    7            num *= 2;                            # 程序在第7行暂停
    (gdb) p num                                       # p 命令查看 num 的当前值
    $1 = 16

     GDB tbreak命令

    tbreak 命令可以看到是 break 命令的另一个版本,tbreak 和 break 命令的用法和功能都非常相似,唯一的不同在于,使用 tbreak 命令打的断点仅会作用 1 次,即使程序暂停之后,该断点就会自动消失。

    tbreak 命令的使用格式和 break 完全相同,有以下 2 种:

    1、(gdb) tbreak location
    2、(gdb) tbreak ... if cond

    仍以 main 为例,如下演示了 tbreak 命令的用法:

    [root@all c]# gdb main -q
    Reading symbols from /root/c/main...done.
    (gdb) tbreak 7 if num>10
    Temporary breakpoint 1 at 0x4004dc: file main.c, line 7.
    (gdb) r
    Starting program: /root/c/main 
    
    Temporary breakpoint 1, main (argc=1, argv=0x7fffffffe278) at main.c:7
    7            num *= 2;
    Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.149.el6.x86_64
    (gdb) p num
    $1 = 16
    (gdb) c
    Continuing.
    num=128
    Program exited normally.
    (gdb)

    可以看到,自num=16开始,后续循环过程中 num 的值始终大于 10,则num>10表达式的值永远为 True,理应在第 7 行暂停多次。但由于打断点采用的是 tbreak 命令,因此断点的作用只起 1 次。

    GDB rbreak命令

    和 break 和 tbreak 命令不同,rbreak 命令的作用对象是 C、C++ 程序中的函数,它会在指定函数的开头位置打断点。

    tbreak 命令的使用语法格式为:

    (gdb) tbreak regex

    其中 regex 为一个正则表达式,程序中函数的函数名只要满足 regex 条件,tbreak 命令就会其内部的开头位置打断点。tbreak 命令打的断点和 break 命令打断点的效果是一样的,会一直存在,不会自动消失。

    这里我们对 main.c 源文件的程序做如下修改:

    (gdb) l      <-- 显示源码
    1 #include<stdio.h>
    2 void rb_one(){
    3    printf("rb_one
    ");
    4 }
    5 void rb_second(){
    6   printf("rb_second");
    7 }
    8 int main(int argc,char* argv[])
    9 {
    10     rb_one();
    (gdb)
    11     rb_second();
    12     return 0;
    13 }
    (gdb) rbreak rb_*       <--匹配所有以 rb_ 开头的函数
    Breakpoint 1 at 0x1169: file main.c, line 2.
    void rb_one();
    Breakpoint 2 at 0x1180: file main.c, line 5.
    void rb_second();
    (gdb) r
    Starting program: /home/ubuntu64/demo/main.exe
    
    Breakpoint 1, rb_one () at main.c:2
    2 void rb_one(){
    (gdb) c
    Continuing.
    rb_one
    
    Breakpoint 2, rb_second () at main.c:5
    5 void rb_second(){
    (gdb) c
    Continuing.
    rb_second[Inferior 1 (process 7882) exited normally]
    (gdb)

    可以看到,通过执行rbreak rb_*指令,找到了程序中所有以 tb_* 开头的函数,并在这些函数内部的开头位置打上了断点(如上所示,分别为第 2 行和第 5 行)。

  • 相关阅读:
    轨迹预测-运动递归函数
    Mandelbrot集合及其渲染
    如何检测一个圆在多个圆内?
    【转】三十分钟掌握STL
    【转】如何理解c和c++的复杂类型声明
    有1,2,3一直到n的无序数组,排序
    归并排序
    希尔排序
    快速排序
    冒泡排序
  • 原文地址:https://www.cnblogs.com/jkin/p/13828295.html
Copyright © 2011-2022 走看看