Linux下GDB简明教程(包括GUI模式)
我们在linux下调试程序时,一般都会用到gdb,本文主要介绍一下gdb常用的操作命令以及TUI模式的使用方式。
一 常用操作命令
假设我们有段程序叫main.c,代码如下,
#include <stdio.h>
typedef struct {
char name[8];
int age;
int birthYear;
} student;
int func(int data1, int data2)
{
int temp = 100;
return temp + data1 + data2;
}
int main(void)
{
student obj = {
.name = "Lucy",
.age = 2018,
.birthYear = 1990
};
printf("name: %s\n", obj.name);
printf("age: %d\n", obj.age);
printf("birthYear: %d\n", obj.birthYear);
int ret = func(200, 300);
printf("ret: %d\n", ret);
return 0;
}
对main.c进行编译,并开启-g选项生成调试信息,这样gdb才可以调试程序
gcc -g main.c
编译ok后生成a.out。我们开始调试,在terminal下输入gdb ./a.out
,打印如下,
此时处于命令模式,我们可以输入相关命令来进行调试。下面列出常用的调试命令,
- b xx,设置断点(b是breakpoint的缩写),可以是
b 函数名
,即在指定函数的起始处设置断点,也可以是b 行号
,即在指定代码行设置断点。 - r,运行的意思(r是run的缩写),一般用来代码开始运行,或者重新运行(如果调试到一半又想从头开始运行)
- c,继续执行(c是continue的缩写),当执行r运行到某个断点后,后面想继续执行到下一个断点或者把剩下代码执行完毕,就可以使用c
- n,next的意思,执行当前行代码
- s,step的意思,当一行代码里有函数调用,那么执行s会跳入函数里执行,如果没有函数调用,那么效果和n相同
- info xx,查看一些信息,如断点或者局部变量,分别是
info b
和info locals
- p xx,p是print的缩写,打印某个变量的值
- set xx,设置某个变量的值
- x 指定大小 起始内存地址,即查看起始内存地址上指定大小的内存里的值。如x /3b 0x11223344,就是查看以0x11223344开始的3个字节的值,也可以是x /3w 0x11223344,就是查看以0x11223344开始的3个word的值
- bt,backtrace的缩写,回溯,当使用s进入某个函数后,输入bt可以打印该函数的栈帧
- list,在命令行下显示源码,可以是
list
或者list 行号
,后者是以指定行号为基准,显示该行号前后的代码 - q,quit的意思,即退出gdb调试
有了以上知识后,我们来看下如何调试,
起始
输入b main
并回车,意思是在main函数起始处打断点,然后输入r
并回车,开始执行程序,程序会在main开始处停下来,因为这边设置了断点。
这套操作是调试的基础,因为程序运行都从main函数开始执行,所以我们都会从main开始调试。
执行代码
此时输入n
并回车,程序就会执行一行代码。每输入一个n并回车,都会执行一行代码。如果一行代码有函数调用,那么输入s
就会进入函数体内,如果输入n
则直接把被调函数执行完。
查看变量
代码里有个结构体变量obj,我们来查看下它的信息,输入p obj
,
查看结构体里的成员则是p obj.name
,p obj.age
查看obj或obj内的成员变量的地址,
可以看到age的地址减去obj的地址是8,正好是name占据的内存空间。如果我们想用比较raw的方式查看内存空间里的值,可以使用x
,
查看到的值是0x0400,即1024。
或者使用&
符号来直接取地址
修改变量
修改obj里成员变量的值,使用set obj.age=1024
这个命令就是直接修改内存里的值,这样如果后续代码没有对这个变量重新赋值,那么后面这个变量的值就是我们手动设置的值了。
如果想使用比较raw的方式来修改内存地址上的值,可以按如下操作,
使用了*
这个解引用符号
查看断点和局部变量
如果想删除某个断点,就是用delete Num
,这个Num就是使用info查看到的索引(最左侧的Num一列)
查看局部变量是查看当前函数内的局部变量,使用info locals
就可以了。
查看栈帧
代码里调用了func,我们先给func打个断点,然后使用c
让代码运行到该断点,然后输入bt
来查看
这里可以看到2个栈帧,一个是func的,一个是main的,因为func是被main调用的。
小结
以上介绍了gdb的一些常用操作,对于命令的使用方法也可以在gdb命令行下使用help 命令
来查看帮助信息。
二 TUI模式
经过第一节,我们知道了gdb的一些常用操作方式,但是这种使用方法都是在命令行下进行的,使用起来非常不方便,假如我们想在某行打断点,需要使用list来显示源码,很麻烦。
基于此,gdb提供了一种可视化的调试模式,即TUI(Text User Interface的缩写),这个模式使用了curses库来进行图形界面显示。
首先执行gdb ./a.out
,然后Ctrl+x+a,即可进入到TUI模式,
此时啥都没有,我们可以按照第一节的方式输入b main
和r
,
可以看到源码显示在上面窗口,命令输入在下面窗口。中间是状态行,表示当前gdb调试的目标属于native的(即本地调试,还有一种是远程调试),进程号是2694,当前调用函数是main,当前位于19行,PC值是0x5555555546d2
如果此时按下Ctrl+x+2,那么就会出现汇编窗口
如果我们连续按下Ctrl+x+2就会发现还会显示寄存器窗口,但是源码显示没有了。
这是咋回事呢?因为在TUI模式下,总共有4种窗口
- 命令窗口
- 源码窗口
- 汇编窗口
- 寄存器窗口
其中命令窗口肯定要显示出来的,这是我们debug的输入,对于另外3个窗口,如果我们输入Ctrl+x+2就可以显示其中2个窗口,这个数字2意思就是除了命令窗口外还可以同时再显示2个窗口。连续按下Ctrl+x+2就会出现这三个窗口的两两组合。
如果我们想除了命令窗口外只显示一个窗口,那么可以按下Ctrl+x+1,这个数字1就是只显示一个窗口(除了命令窗口外)。
对于源码窗口,我们可以使用PageUp,PageDown和4个方向键来查看源码。
TUI模式下有时显示会出现混叠现象,此时按下Ctrl+l(是小写的L)可以进行刷新。
断点设置
断点设置是TUI模式带来的很方便的特性,如我们在main函数开始处设置了断点,
在19行出现B+>
,假如我们在27行也设置个断点,在命令行下输入b 27
,
此时在27行出现b+
,跟19行显示的不一样。他们的具体意义如下,
B
表示断点处代码已经运行至少一次b
表示断点处代码还没有运行到+
表示该断点处于enable状态-
表示该断点处于disable状态
那么如何disable一个断点呢,
使用info b
来查看断点信息,然后对于想要disable的断点,直接输入disable Num
就可以了,也可以看到27行的b+
变成了b-
,如果想enable断点,就输入enable Num
。
三 总结
本文主要讲述了gdb的常用命令和gdb的TUI模式,文中介绍的常用命令在TUI下都可以使用,这样2者结合起来会更加方便。
另外,读者也可以参考Richard Stallman编写的《Debugging with gdb》,里面详尽描述了gdb的使用方法。
如果有写的不对的地方,希望能留言指正,谢谢阅读。
更多推荐
所有评论(0)