GDB 5分钟快速入门
一个简单快速的GDB
使用入门,直接上手使用进行调试。
GCC/G++ 编译文件
在使用gdb
进行调试之前,需要使用GCC
的编译器编译可执行文件,我们以C++
代码为例,使用g++
进行编译,此时应该带上编译选项-g
确保生成调试信息,点击此处查看具体信息
GNU/GCC Debugging-Options
。
$ g++ main.cc -g -o main
// main.cc
#include <iostream>
int main() {
std::cout << "Hello\n";
return 0;
}
GDB Text User Interface (TUI)
其实gdb
也是有UI的,一种终端接口,直接在终端中显示源文件、程序输出、程序寄存器等。点击此处查阅文档
GDB Text User Interface
。在使用时gdb
,添加上选项-tui
即可。如果直接使用gdb execution_file
,那么也可以输入tui enable
切换进入tui
模式。
$ gdb main -tui
开始调试
如果你正确使用了g++
的-g
选项,那么你应该可以在gdb
打开后看见源代码在上方,命令窗口在下方。如下图,颜色不同仅仅是终端配置区别。这里我没有指定输出的可执行文件名,所以是a.out
。
打上断点
我们可以看见各代码的行数,在gdb
中,使用break
,或简单地使用b
打断点,例如我们在main
函数处打断点则会在该函数的下面一行添加一个断点。我们也可以用delete
或者clear
清除断点,前者用于指定某个断点,后者则全部清空。
$ b main
# or you can also specific lines
$ b 4
运行
使用run
或者r
,将直接运行至当前的第一个断点处,此时可以继续使用step
或者s
步过,也可以使用stepi
或者si
步入。可以看见直接输入s
后代码框左边的箭头>
指向了return 0
,并且在命令行看见了运行结果。
此时当我们输入
si
的话,则需要layout asm
查看汇编,因为这时候步入到主函数返回。我们看见当前的汇编恰好对应ret
。
监视变量
现在我们修改一下源代码,如下并重新编译调试。
// main.cc
int foo(int& x){
x = 10;
return x * x;
}
int main(){
int val = 90;
foo(val);
return 0;
}
我们使用b main
,b foo
在两个函数的位置打上断点,通过watch
监视变量。此时则需要先run
程序,让程序走到需要监视的变量被初始化的作用域,才能成功监视。我们先r
。然后再watch val
。可以看见我们成功停在了main
函数,同时也成功watch
了val
这个变量。
打印变量值
我们再不断s
,直到程序断在foo
函数——由于我们在foo
打了断点,所以s
不会直接步过foo
,当我们经过了x = 10
的赋值语句,此时命令行会提醒我们val
的值变化。
我们也可以使用print
或者p
打印x
,即p x
,此时的作用域下只能打印x
。也可以通过p/x
指定打印的格式,例如p/x
就是按照十六进制打印。p/x x
或p/t x
。如果要打印变量地址的话,则p &x
即可,当然,由于我们的程序中x
是左值引用,所以p x
的时候,就会显示出其指向的地址,我们的程序是0x7fffffffd4b4
。也可以用p *0x7fffffffd4b4
打印出该地址的值。
- x 按十六进制格式显示变量。
- d 按十进制格式显示变量。
- u 按十六进制格式显示无符号整型。
- o 按八进制格式显示变量。
- t 按二进制格式显示变量。
- a 按十六进制格式显示变量。
- c 按字符格式显示变量。
- f 按浮点数格式显示变量。
大家应该察觉到了,当我们目前在foo
函数内时,p val
是无法打印出main
函数内的值的,此时应该使用p 'main'::val
进行打印。如果你有多个文件,也可以通过p 'file2.cc::value'
打印file2.cc
当中的某个值。这个也适用于各种打印方法,比如watch
。
对于数组类型,我们需要通过p *array@len
进行打印,其中len
是指定的个数。例如我们将arr
的前5个元素打印出来p *arr@5
。
自动显示变量
我们每次都使用p
来显示变量太麻烦了,使用watch
的话也只能在变量改变的时候显示,在各种ide
里调试的时候可以实时显示当前的每一个变量,太酷了😎,其实我们也可以用display
来监视,在该变量存在的作用域内,每次操作都会打印一次被display
的变量。
退出调试
使用q
然后y
退出
Python
自动化的GDB
脚本
我们可以直接使用python
编写gdb
脚本,让我们每次直接运行该脚本,免去调试时重复输入的痛苦😻。例如我们使用gdb -x debug.py -tui
进行调试,会自动帮我们执行这些指令。点击这里查看
GDB Python API
# debug.py
import gdb
gdb.execute("file ./a.out")
gdb.execute("b main" )
gdb.execute("r")
gdb.execute("display 'main'::val")
一点启示
如果我们可以自动根据当前作用域display
所有变量并绘制一份UI
······