GDB实现程序中断

中断调试是为了让程序运行时停在某一个或多个点上,然后进行调试操作,也就是分步调试程序。

为什么要分步调试程序呢?具体可以归结为两个原因:
  • 为了方便我们获取程序运行时的各个阶段的信息。通过这些信息,可以分析出程序中表达式的变化,程序的运行方式以及各部分的执行顺序。
  • 为了帮助我们找出代码中存在的问题,例如程序中使用了空指针,编译的阶段不会出现错误,程序执行的时候会报段错误的提示信息,而且还不会告诉我们错误出现的具体位置。

中断程序

在 GDB 中有很多的方法可以让程序产生中断,例如信号,断点或者是一个 GDB 的命令,然而最常见的方式就是设置断点。当程序执行到某个断点时,我们可以在该处执行一些调试的相关操作来获取信息,例如可以打印或者改变某个具体参数的值,设置一个新的断点或是删除一个断点,操作结束后程序继续执行。

设置断点

设置断点是让程序产生中断的最常用的方法,对于每个断点来说,还可以通过加上相应的控制条件来决定程序是否会继续处于中断的状态。GDB 中设置断点使用的是 break 命令。下面是 break 命令常用的一些格式。

1.在给定的位置设置断点,使用方式:

break <location>

location 可以是函数名,行号或者是一个指令的地址,实例:

(gdb) break func()     
(gdb) break 16        
(gdb) break 0x12345   

2.在不带参数的情况下,break 命令在下一条指令里设置断点,使用方式:

break

3.设置条件断点,使用方式:

break <...> if<condition>

每次断点到达时计算 condition,当且仅当表达式为真的时候会产生中断。<...>表示的是指定产生中断的位置(断点的位置),condition为条件表达式。实例:

(gdb) break 16 if i == 3

当程序中变量 i 的值等于 3 的时候,在第 16 行会中断程序。

break命令的变形格式

break 存在几种特殊的变形格式,都可以实现设置断点的作用,相关的使用如下。

1.设置一个只中断一次的断点,使用方式:

tbreak <location>

tbreak 和 break 里面的参数是一样的,断点的设置也是一样的。但是断点会在第一次执行后自动删除。

2.设置硬件辅助断点,使用方式:

hbreak <location>

hbreak 和 break 里面的参数是一样的,断点的设置也是一样的。但是断点需要硬件支持,某些硬件可能不支持此功能。其主要用途是 EPROM / ROM 代码调试,因此我们可以在不更改指令的情况下在指令处设置断点。

3.设置仅在一次停止时启用的硬件辅助断点,使用方式:

thbreak <location>

thbreak 和 break 里面的参数是一样的,断点的设置也是一样的。但是 thbreak 与 tbreak 命令一样,断点会在程序第一次停止后自动删除。此外,thbreak 与 hbreak 命令一样,断点需要硬件支持,而某些目标硬件可能不支持。

4.在与正则表达式匹配的所有函数上设置断点 ,使用方式:

rbreak <regex>

regex 表示一个正则表达式,这条命令表示在所有符合 regex 匹配的项上设置无条件断点,并且打印设置的所有断点的列表。设置断点后,它们将被视为与使用 break 命令设置的断点一样,可以删除它们,禁用它们或者以与任何其他断点相同的方式使它们成为条件。

设置捕捉点

捕捉点是一种特殊类型的断点,用来在设置在某些事件发生时中断程序,使用方式:

catch <event>

event 表示程序中发生的事件,当 catch 捕捉到被设置的事件发生,就会中断程序。<event>可以包含以下几种表示形式:
throw:C++抛出的异常。
catch:C++捕捉到的异常。
exec:exec 函数被调用时。
fork:fork 函数被调用。
vfork:vfork 函数被调用。
load:加载动态库。
unload:卸载动态库。

设置监视点

监视点也是一种特殊的断点,我们可以对一些表达式实行监视,当表达式的值发生改变就会中断程序。使用方式:

(r/a)watch <condition>

condition表示为表达式,表达式可以是一个变量的值或者是由操作符绑定的一个或多个变量。当<condition> 被访问、改变时中断程序运行,其中 r 表示访问的时候中断程序,a 表示的是访问或者改变的时候中断程序。

中断调试程序

为了方便对程序的综合调试(包含设置断点,设置监视点,设置捕捉点),这次调试的对象为 C++ 的程序,参数通过命令行传递。

#include <iostream>
#include <stdlib.h>
using namespace std;
 
double dev(int a,int b)
{
    if(b == 0)
    { 
        throw "this is not can";
    } 
    return a / b;
}
 
int main(int argc,char *argv[])
{
    int arr[2] = {};
    arr[0] = atoi(argv[1]);
    arr[1] = atoi(argv[2]);
    double z = 0;
    try
    { 
        z = dev(arr[0],arr[1]);
    }catch(const char *msg)
    { 
        cerr<<msg<<endl;
    } 
 
    return 0;
}

编译文件:

g++ -g test.cpp -o test

进入gdb命令行调试文件:

gdb test

相关的调试操作如下:

(gdb) break dev(int, int)            //设置函数名断点
Breakpoint 1 at 0xc08: file test.cpp, line 7.
(gdb) break 18                   //设置行号断点
Breakpoint 2 at 0xc80: file test.cpp, line 18.
(gdb) catch throw                 //设置捕获点断点
Catchpoint 3 (throw)
(gdb) info breakpoints             //显示断点信息
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000000c08 in dev(int, int)
                                                   at test.cpp:7
2       breakpoint     keep y   0x0000000000000c80 in main at test.cpp:18
3       breakpoint     keep y   0x0000000000000ac0 exception throw
(gdb) run 10 0                   //通过命令行参数传递
Starting program: /home/wjc/hsxy/lianxi/10/test/a.out 10 0
 
Breakpoint 2, main (argc=0, argv=0x7ffff7de59a0 <_dl_fini>) at test.cpp:18
18            arr[1] = atoi(argv[2]);                //第一个断点程序中断
(gdb) p a
$1 = {i = {0, 1045149306}, d = 1.2904777690891933e-08}
(gdb) p b
$2 = {i = {0, 1068498944}, d = 0.0625}
(gdb) continue
Continuing.
 
Breakpoint 1, dev (a=10, b=0) at test.cpp:7
7               if(b == 0)                       //第二个断点程序中断
(gdb) p a                               //打印变量的值
$3 = 10
(gdb) p b
$4 = 0
(gdb) continue
Continuing.
 
Catchpoint 3 (exception thrown), 0x00007ffff7adeced in __cxa_throw ()
   from /usr/lib/x86_64-linux-gnu/libstdc++.so.6       //捕获抛出异常
(gdb) continue
Continuing.
this is not can                                //运行结果显示
[Inferior 1 (process 5014) exited normally]

注意:通常 GDB 提供的消息可以显示程序大量的状态,我们也可以使用命令打印显示请求的信息,命令为“ info program”。显示的信息主要包括:程序是否在执行的状态,是什么进程,以及为什么会产生中断。