假设当前目录下共有以下3个文件,接下来会利用以下代码来演示GDB的调试过程。
├── main.cpp
├── tool.h
└── tools.cpp
文件内容分别为:
tools.h:函数声明
1 |
|
tools.cpp:函数定义
1 |
|
main.cpp:主函数
1 |
|
以上代码只是为了演示之用,并无特别之处。
编译方式
1 | // -g 添加调试信息 -Wall 输出所有警告信息,例如定义了从未使用过的变量 |
启动
调试可执行程序
1 | gdb main |
调试 core 文件,core 文件是程序运行过程中出现Segmentation fault (core dumped)错误时,程序停止运行时产生的。core文件是程序运行状态的内存映象。使用gdb调试core文件,可以帮助我们快速定位程序出现段错误的位置。可执行程序编译时应加上-g编译选项,生成调试信息。
1 | gdb <program> <core dump file> |
调试服务程序
1 | $gdb <program> <PID> |
设置和获取参数
有时需要在执行程序时输入额外的参数,例如:像下面这样执行程序test
1 | ./main 5 |
调试时,往往直接执行$ gdb main
那么怎么在gdb中输入该程序的参数呢?
1 | (gdb) set args 5 |
还可以查看程序的参数
1 | (gdb) show args |
查看代码
GDB提供了两种查看源码的方式,分别是根据行号和函数名查看。除了可以查看本文件源码,还可以查看其他文件的源码。
本文件
本文件指的是该程序对应的main函数所在文件,即main.cpp
1 | (gdb) l # 每执行1次显示10行,再执行1次显示次10行 |
其他文件
共同编译的所有文件中,除了main函数所在文件的其它文件。在这里,除了main.cpp即tools.cpp
1 | (gdb) l tools.cpp:15 # 在tools.cpp中,显示第15行附近的代码 |
设置和获取显示行数
1 | (gdb) show list # 显示行数 |
断点
可以在一次调试中设置1个或多个断点,下一次只需让程序自动运行到设置断点位置,便可在上次设置断点的位置中断下来,极大的方便了操作,同时节省了时间。
可以根据行号,函数名设置断点,还可根据条件设置断点(一般用于循环中)
本文件
1 | (gdb) b 10 # 将第10行设置为断点 |
其他文件
1 | b tools.cpp:12 # 将tools.cpp的第12行设置为断点 |
设置条件断点
条件断点一般用于循环中
本文件
1 | 设置i==2时,第18行为断点 |
其他文件
1 | 设置tools.cpp文件内,i==5时,第22行为断点 |
查看和删除断点
1 | (gdb) i b # 显示所有断点 |
设置断点无效和有效
1 | clear n # 清除第n行的断点 |
运行
有两种运行方式,一种是从主函数开始运行,一种是运行到第1个断点处
从主函数运行
程序从main函数开始
1 | (gdb) run |
运行到第1个断点处
程序停在第一个断点处
1 | (gdb) start |
执行流控制
1 | c/continue # 向下运行到下一个断点处 |
打印变量
1 | p 变量名 # 打印变量值,可以答应在当前作用域之内的变量名 |
查询
自动变量操作
可以在每次执行时都打印该变量的值,常用于循环体中
1 | display 变量名 # 自动打印指定变量的值 |
运行时信息
1 | where/bt # 当前运行的堆栈列表; |
设置变量值
1 | set var 变量名=变量值 # 可以临时改变该变量的值 |
多窗口调试
使用 layout 可以同时在多个窗口调试代码
1 | layout src # 显示源代码窗口 |
多进程调试
GDB 默认调试的是父进程,使用以下命令切换调试的进程
1 | set follow-fork-mode child # 调试子进程 |
GDB 可以同时调试一个进程,也可以同时调试多个进程,使用以下命令设置:
1 | set detach-on-fork <mode> # 当 mode 为 on 时,表示程序只调试一个进程(可以是父进程、子进程)。当 mode 为 off 时,父子进程都在gdb的控制之下,其中一个进程正常的调试,另一个会被设置为暂停状态。 |
GDB 将每一个被调试程序的执行状态记录在一个名为 inferior 的结构中。一般情况下一个 inferior 对应一个进程,不同的 inferior 有不同的地址空间。inferior 有时候会在进程没有启动的时候就存在。
1 | info inferiors # 显示所有的 inferior,当前调试的进程前有 "*"。 |
切换进程使用命令 inferior
1 | inferior <num> # 切换到 num 号进程 |
设置捕获点中断,调用 fork 函数时将产生中断
1 | catch fork |
查看内存
命令格式:例如 x/<n/f/u> <addr>
例如:
1 | (gdb) x/3cb 0x8049098 |
x(examine)为命令的简称,用于检查内存数据。
命令的意思是:查看从内存地址 0x8049098 开始的数据,每字节为单位(b),以ASCII字母形式显示(c),显示 3 个单位的数据(3)。
下面对 3cb 三个字符对应的含义进行讲解:
3
表示显示的单位数据个数,可根据需要自定义,和第三个字符参数有关。
c
用什么格式来解析这些二进制位,因为内存本质上是二进制位,它可被解释为多种含义,例如:0111 0101,可表示十进制数 117,也可以表示十六进制数 75,也可以表示字符 K(ASCII 字母)
可用的格式字符如下:
c 按字符格式显示变量。
x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十进制格式显示无符号整型。
o 按八进制格式显示变量。
t 按二进制格式显示变量。
a 按十六进制格式显示地址,并显示距离前继符号的偏移量(offset)。常用于定位未知地址(变量)。
f 按浮点数格式显示变量。
b
表示从当前地址往后请求的位宽大小。如果不指定的话,GDB默认是4个bytes。也可以指定以下字符
b表示单字节,h表示双字节,w表示四字 节,g表示八字节。
其它工具
cgdb
cgdb可以看作gdb的界面增强版,用来替代gdb的 gdb -tui。cgdb主要功能是在调试时进行代码的同步显示。