gdb是gun debugger,是gun的调试工具,功能强大。常用命令如下:
命令 |
描述 |
backtrace(或bt) |
查看各级函数调用及参数 |
finish |
连续运行到当前函数返回为止,然后停下来等待命令 |
frame(或f) 帧编号 |
选择栈帧 |
info(或i) locals |
查看当前栈帧局部变量的值 |
list(或l) |
列出源代码,接着上次的位置往下列,每次列10行 |
list 行号 |
列出从第几行开始的源代码 |
list 函数名 |
列出某个函数的源代码 |
next(或n) |
执行下一行语句 |
print(或p) |
打印表达式的值,通过表达式可以修改变量的值或者调用函数 |
quit(或q) |
退出gdb 调试环境 |
set var |
修改变量的值 |
start |
开始执行程序,停在main 函数变量定义后的第一行语句前面等待命令 |
step(或s) |
执行下一行语句,如果有函数调用则进入到函数中 |
例子演示
我们用一个例子test.c来演示如何用gdb调试c。
//test.c
#include
int add(int a, int b)
{
int c = 1;
int d = 2;
return a+b+c;
}
int main()
{
int a, b;
a = b = 0;
printf("Hello World!\n");
add(a, b);
a = 2;
return 0;
}
编译加入调试信息
要利用gdb调试c,首先要在编译c的时候加入gdb调试信息
-ggdb
选项(也可以用-g)的作用是在可执行文件中加入源代码的信息,比如可执行文件中第几条机器指令对应源代码的第几行,但并不是把整个源文件嵌入到可执行文件中,所以在调试时必须保证gdb
能找到源文件。如果源文件路径或者文件名改了,那么gdb就找不到源文件,在调试的时候是不会显示源代码的。
现在直接输入:
帮助查询
gdb提供一个类似bash的界面,
我们要查gdb命令的话,在gdb命令行中输入help即可列出命令种类。
要查具体哪一个种类的命令,比如files,直接输入 help files。
list命令(缩写l)
可以列出源代码,一次只列10行,如果想看下面的,直接回车即可(回车会自动执行上一条命令)。
start命令
直接从主函数变量定义后的第一条代码开始调试,列出的就是将要执行的代码。
单步调试
next(缩写n)是单步调试不进入函数,step(缩写s)是进入函数的单步调试。
在函数中几种查看状态的办法
backtrace
命令(简写为bt
)可以查看函数调用的栈帧:
可见当前的add
函数是被main
函数调用的,main
传进来的参数是a=1, b=0
。main
函数的栈帧编号为1,add
的栈帧编号为0。
现在可以用info
命令(简写为i
)查看add
函数局部变量的值:
这里会列出所有局部变量,由于d没有还没有赋值,是个不确定的值。
如果想查看main
函数当前局部变量的值也可以做到,先用frame
命令(简写为f
)选择1号栈帧然后再查看局部变量:
print命令
程序在运行过程中可以用print(简写为p)命令打印出变量的值,比如打印出d的值,
这里的$1
表示gdb
保存着这些中间结果,$后面的编号会自动增长,在命令中可以用$1
、$2
、$3
等编号代替相应的值
如果不想再运行这个函数了,可以使用finish命令退出函数。
如果在调试过程中发现问题了,比如变量未初始化,可以直接赋值,set var a=1改变变量的值,也可以用print命令(更习惯),print命令既可以打印出值,也可以执行语句,注意不能有分号。
设置断点
断点(BreakPoint):
在代码的指定位置中断,这个是我们用得最多的一种。设置断点的命令是break,它通常有如下方式:
-
break 在进入指定函数时停住
-
break 在指定行号停住。
-
break +/-offset 在当前行号的前面或后面的offset行停住。offiset为自然数。
-
break filename:linenum 在源文件filename的linenum行处停住。
-
break ... if ...可以是上述的参数,condition表示条件,在条件成立时停住。比如在循环境体中,可以设置break if i=100,表示当i为100时停住程序。
可以通过info breakpoints [n]命令查看当前断点信息。此外,还有如下几个配套的常用命令:
-
delete 删除所有断点
-
delete breakpoint [n] 删除某个断点
-
disable breakpoint [n] 禁用某个断点
-
enable breakpoint [n] 使能某个断点
观察点(WatchPoint):
在变量读、写或变化时中断,这类方式常用来定位bug,注意这里设置观察点的时候程序一定要运行到要观察的变量声明了之后,不然会报找不到该变量的错误。
-
watch 变量发生变化时中断
-
rwatch 变量被读时中断
-
awatch 变量值被读或被写时中断
可以通过info watchpoints [n]命令查看当前观察点信息
捕捉点(CatchPoint):
捕捉点用来补捉程序运行时的一些事件。如:载入共享库(动态链接库)、C++的异常等。通常也是用来定位bug。
捕捉点的命令格式是:catch ,event可以是下面的内容
-
throw C++抛出的异常时中断
-
catch C++捕捉到的异常时中断
-
exec 调用系统调用exec时(只在某些操作系统下有用)
-
fork 调用系统调用fork时(只在某些操作系统下有用)
-
vfork 调用系统调用vfork时(只在某些操作系统下有用)
-
load 或 load 载入共享库时(只在某些操作系统下有用)
-
unload 或 unload 卸载共享库时(只在某些操作系统下有用)
另外,还有一个tcatch ,功能类似,不过他只设置一次捕捉点,当程序停住以后,应点被自动删除。
恢复程序运行和单步调试
在gdb中,和调试步进相关的命令主要有如下几条:
-
continue 继续运行程序直到下一个断点(类似于VS里的F5)
-
next 逐过程步进,不会进入子函数(类似VS里的F10)
-
setp 逐语句步进,会进入子函数(类似VS里的F11)
-
until 运行至当前语句块结束
-
finish 运行至函数结束并跳出,并打印函数的返回值(类似VS的Shift+F11)
这些命令大部分可以简写为第一个字母,在日常使用过程中,往往只会输入第一个字符即可执行该命令,我标红的即是通常的使用方式。这几条命令使用非常频繁,并且可以带一些附加参数以实现高级功能,需要熟练掌握。