时间:2021-07-01 10:21:17 帮助过:13人阅读
#ifndef _GDB_H
#define _GDB_H 1
class gdb
{
public:
explicit gdb(int v);
void overload(int one);
void overload(int one, int two);
void catch_ex(int ex); //exception
void loop();
private:
int value;
int array[10];
};
#endif
gdb.cpp
#include "gdb.h"
#include <iostream>
using namespace std;
gdb::gdb(int v)
{
value = v;
for(int i=0; i<10; i++)
{
array[i] = i;
}
}
void gdb::overload(int one)
{
cout<<"function overload with one parameter: "<<one<<endl;
}
void gdb::overload(int one, int two)
{
cout<<"function overload with two paremeters: "<<one<<" "<<two<<endl;
}
void gdb::loop()
{
int loop_array[10];
for(int i=0; i<10; i++)
{
loop_array[i] = i;
}
int v=3;
v=3;
v=4;
}
void gdb::catch_ex(int ex)
{
int e = ex;
try
{
if(e <= 0)
{
throw e;
}
else
{
cout<<"function catch_ex: "<<ex<<endl;
}
}
catch(int x)
{
cout<<"exception: "<<x<<endl;
}
}
test.cpp
#include "gdb.h"
int main()
{
gdb g(5);
g.overload(1);
g.overload(1, 2);
g.loop();
g.catch_ex(3);
g.catch_ex(-1);
return 0;
}
好了,现在开始调试。首先启动gdb,指定要调试的可执行文件。前面已经说过了,可以简单地使用gdb program来启动。或者可以首先启动gdb,然后用file命令指定要调试的文件。下面是仅启动gdb后的画面:
现在用file命令指定要调试的文件:
然后就可以用 run 或者 r 命令来运行程序。在运行之前,你可能需要为程序设定一些信息,这些信息有一下四种:
1)程序参数
可以用set args命令设定程序的参数。设定完后可以用show args查看设置的是否正确。如果set args后面不带任何参数,则向程序传递的参数为空。
2)环境
这儿的环境就是在系统/用户配置文件中设置的环境变量,像HOME, PATH之类的.GDB提供了在调试的时候改变这些变量值的方式,这样当需要的时候就不用退出gdb来重新设置.GDB提供的命令有:
a) path directory -- 将 directory 加到环境变量PATH前面. 注意 对PATH的改变只对调试的程序有效, GDB使用的PATH不会有改变.1
b) show paths -- 显示PATH的值。
c) show environment [varname] -- 显示环境变量varname的值,如果不指定varname,则显示所有环境变量的值。
d) set environment varname [= value] -- 设置环境变量varname的值为value。这个改变只是对调试的程序生效。如果不提供value,则将varname的值置为空。
e) unset environment varname -- 从环境中移除传递给程序的变量varname。
3)工作目录
在启动gdb调试程序的时候,被调试的程序会从gdb继承工作目录。当然gdb也提供了命令来修改工作目录:
a) cd directory -- 将 directory 设为新的工作目录。
b) pwd -- 显示当前工作目录。
4)标准输入输出
还没找到在windows里面这个东西有啥用,现在也没有linux可用,不好多说。有需要的自己看gdb手册吧。我简单抄一下手册吧。
在gdb中,可以将run命令的输入输出重定向到文件或者其他终端。也可以通过tty命令设置被调试程序输入输出的设备。命令格式是:
tty terminal 或者 set inferior-tty terminal.
tty 就是 set inferior-tty 的别名。
咚咚咚咚,下面正式开始!
上面我们已经启动了程序, 也知道了如何运行程序。可是如果你直接执行run命令会发现,程序直接运行结束了。如果你想在某一行或者某个函数调用的地方,或者当某个变量/表达式的值改变的时候,也或者在某些事件发生的时候--例如抛出异常、加载动态库,或者创建子进程--的时候停止程序运行,那应该怎么办呢?
有了gdb,一切就都好办了:), 利用下面这三个强大的武器,你可以任意的停止程序。小心了,大家小心了,偶要祭出这三件宝物了,它们是:
断点就是指定一个位置,使得程序运行到这个位置的时候会停下来(当然,还可以设置条件断点,当运行到指定位置时,只有满足了设置的条件,程序才会停下来),这样便于观察程序的内部状态。断点相关的命令主要有:
a)break location
在指定位置 location 处设置断点,这里的 location 可以是函数名,行号,指令地址等(关于如何指定 location ,可以看这里)。
b)break
如果不指定任何参数,break会在选定的栈帧的下一条指令处设置断点。
c)break ... if cond
设置条件断点。每次到达断点的时候都会对表达式 cond 求值,只有当结果为非0的时候程序才会在这个断点停下来。
d)tbreak args
设置一个只生效一次的断点。args跟break命令里的参数意义相同(也就是说,可以为location,为空,或者条件)。
e)hbreak args
设置硬件断点。
f)thbreak args
设置只生效一次的硬件断点。
g)rbreak regex
在所有匹配正则表达式 regex 的函数上设置断点。
h)info breakpoints [n]
i)info break [n]
j)info watchpoints [n]
上面三个命令都是列出当前的断点、观察点和捕捉点,如果指定参数n,则仅列出第n个的信息。
来试验一把吧。首先用gdb启动程序,假设我们想在test.cpp的 g.overload(1) 这一行添加一个断点,那就执行命令:b test.cpp:6,执行完后:
这时可以用info break等命令查看断点信息:
然后我们运行程序,看看有什么效果。
看到了吧,程序停在了断点所在的行。这时可以用where或者frame查看当前的栈帧信息,也可以用 print 查看一些变量或者表达式的信息,或者info args查看参数信息,等等。
其他的命令大家可以自己尝试一下。
有时候我们并不确定要在哪里加断点,例如当我们想在某个变量被改变或者被读、被写的时候让程序停下来,可能由于访问变量的地方比较多,要想每个地方都加上断点比较麻烦,而且很可能有遗漏,这时候我们就需要依赖另一个强大的命令了,也就是观察点。
观察点是一类特殊的断点,如果针对某个变量或者表达式指定一个观察点,那么当它们的值被读/写的时候,gdb会停止程序的执行。你不需要像设置断点时那样明确指定这个观察点在程序中的位置。观察点相关的命令有:
a)watch expr [thread threadnum]
对 expr 设置一个观察点。当 expr 的值被改变的时候,gdb会停止程序的运行。
如果指定了线程参数thread threadnum ,则 只有 在线程 threadnum 改变 expr 的值时,程序才会停止。
b)rwatch expr [thread threadnum]
对 expr 设置一个读观察点。当程序读 expr 的值时,gdb会停止程序的运行。
c)awatch expr [thread threadnum]
对 expr 设置一个访问观察点。当程序读或者写 expr 时,gdb会停止程序的运行。
d)info watchpoints
显示所有的断点、观察点、捕捉点。跟info break 相同。
下面再来看看观察点的使用。
首先我们设置一个断点在g.loop()这一行,然后运行到这里。step进入loop()函数。这一串命令的执行如下:
这时我们已经进入loop()函数,现在我们设置几个观察点。设置命令序列和设置完后的效果如小:
可以看到有5个停止点,其中前两个是我们设置的断点,后面三个是观察点。分别为watch, rwatch 和 awatch。另外还有一个地方就是,x86上默认是硬件观察点。
好了,来运行一下试试。
到达第一个观察点的时候程序停止运行,同时打印出了变量的旧值和新值,以及观察点的位置。
下面我们接着运行,看看遇到后面的观察点的时候又会怎样。
程序在第三个观察点停了下来(第二个观察点为读观察点),同样打印出了变量的新旧值和观察点的位置。第二个观察点由于是读观察点,而程序中没有读这个变量的地方,因此运行的时候被跳过了。为了看一下读观察点的效果,我们再设置一个读观察点:
i是循环内的迭代器,它会被复制给loop_array[i]变量,也就是会被读。可以看到,程序会停止运行,输出i的值。注意:每次对i的读操作都会使得程序停止。下面是两次执行continue命令后的输出:
观察点的内容差不多就这些了。另外有个需要注意的地方就是: watch命令设置的观察点只有在变量或者表达式的值被改变得时候才会使得程序停止运行,如果只是被写,但是值没有改变,则程序不会停止。
上面已经讲了断点、观察点,而对于某些情况,这两种停止点并不是最有效的方式。例如想在c++程序中跑出异常的时候停 止程序,这时候用断点就不够有效了,因为程序中可能好多异常处理的地方,如果一个个设置断点,那就太麻烦了(当然如果只处理某几个异常,用断点也无不可, 甚至用起来更灵活);而观察点就更不可用了。
这种情况就需要用到捕捉点了。
捕捉点也是一类特殊的断点,它可以使得程序在某种事件发生时停止运行,例如c++异常,或者加载动态库、创建子进程等。设置捕捉点的命令是catch.
catch event
其中 event 可以是:
a)throw
c++抛出异常。
b)catch
c++捕捉异常。
c)exception
Ada异常。
d)exception unhandled
程序中未处理的异常。
e)assert
失败的Ada断言。
f)exec
对exec的调用(只在HP-UX和GNU/Linux中可用)。
g)fork
对fork的调用(只在HP-UX和GNU/Linux中可用)。
h)vfork
对vfork的调用(只在HP-UX和GNU/Linux中可用)。
i)load
动态加载共享库(只在HP-UX中可用)。
j)load libname
动态加载共享库 libname (只在HP-UX中可用)。
k)unload
卸载已加载的共享库(只在HP-UX中可用)。
l)unload libname
卸载已加载的共享库 libname (只在HP-UX中可用)。
还有一个设置只生效一次的捕捉点的命令是: tcatch event 。
下面再看一下捕捉点的使用。
首先在vtest.cpp的 g.catch_ex(-1); 设置一个断点,然后运行,进入此函数。
现在我们设置一个捕捉点。继续运行:
可以看到程序在抛出异常的地方停止了。
当断点不再需要了,那就应该删除掉,否则每次执行到断点的位置程序都要停下来,会把人逼疯的。删除断点的命令有两个:clear和delete。
a)clear [ location ]
如果不指定 location ,则删除选择的栈帧中下一条要执行的指令上的任何断点。如果选择的是最内部的栈帧(也就是当前正执行的函数的栈帧),则clear会将刚刚使程序停止的断点被删除。 关于 location 的说明可以看这里。
b)delete [breakpoints] [ range ... ]
删除指定范围 range 那的所有的断点、观察点、捕捉点。如果不指定参数 range ,则会删除所有的停止点。这里的 range 指定的是断点编号区间。可以用info break查看断点信息。
如果不想删除断点,只是想暂时使它失效,则可以使用disable命令。disable命令的形式如下:
a)disable [breakpoints] [ range ...]
使指定区间 range 内的断点失效。如果不指定 range ,则所有的断点都失效。
使断点生效的命令是enable,形式有:
a)enable [breakpoints] [ range ...]
使指定区间 range 内的断点或者所有断点生效。
b)enable [breakpoints] once range...
使指定区间内的断点生效一次。
c)enable [breakpoints] delete range...
使指定区间内的断点生效一次,然后删除。
断点条件使得只有在相应的条件满足时,断点才有效。这里的条件表达式跟程序所用语言的逻辑表达式的语法相同,例如在c/c++语言里,可以用 a==b 或者 x&&y这种表达式。
断点条件可以在设置断点的时候指定,也可以在断点设置后通过condition命令来设置或者改变。 condition的形式为:
a)condition bnum expression
设置表达式 expression 为停止点 bnum 的条件。
b)condition bnum
删除停止点 bnum 的条件。
还有一个命令,可以使得gdb忽略断点的条件一定的次数,其形式为:
a)ingore bnum count
许多gdb命令都接受一个用于指定程序位置的参数。位置的指定方式有下面几种:
a) linenum
当前源文件的行号。
b) -offset
当前行前面,跟当前行间隔为 offset 的行。当前行可以这样确定:使用list命令,打印出来的最后一行就是当前行;或者对于断点命令,选定的栈帧中,程序停止执行的位置就是当前行。
c) +offset
当前行后面,跟当前行间隔为 offset 的行。
d) filename:linenum
源文件 filename 中的行 linenum。
e) function
当前源文件中的函数 function。
f) filename:function
源文件 filename 中的函数 function。
g) * address
指定程序地址 address。常用的 address 形式有:
expression -- 当前语言中有效的表达式。
funcaddr -- 函数的地址。在c/c++中就是函数名。
‘filename‘::funcaddr -- 源文件 filename 中的函数地址 funcaddr。
后面懒得写了,暂时先放一放。发现就算是抄文档,内容多了也是个很累人的活。唉,懒了,不行了...
待添加
待添加
待添加
这篇文章只是捡了GDB中最常用的一些东西,而且还只是最常用的东西中的一小部分,有兴趣或者需要的可以直接看GDB的文档,可以在这里找到。
1. 不知道是不是因为windows和linux系统的不同,用gdb启动程序后,执行show paths后输出:Executables and object file path: 。也就是说输出的值是空的。
C++@sublime GDB调试
标签: