gdb调试工具使用手册
gdb调试工具使用手册
需要调试的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
typedef struct node{
int value;
struct node *next;
} Node;
// 尾插法插入结点
void add_before_tail(Node **head, int value) {
Node *new_node = (Node *)malloc(sizeof(Node));
new_node->value = value;
new_node->next = NULL;
if (*head == NULL) {
*head = new_node;
} else {
Node *temp = *head;
while (temp != NULL) {
temp = temp->next;
}
temp->next = new_node;
}
}
// 打印链表中的所有值
void print_list(Node *head) {
Node *current = head;
while (current != NULL) {
current = current->next;
printf("%d -> ", current->value);
}
printf("NULL\n");
}
int main() {
Node *head = NULL;
add_before_tail(&head, 1);
add_before_tail(&head, 2);
add_before_tail(&head, 3);
print_list(head);
return 0;
}这段代码是用双指针实现链表的尾插,但是运行结果是 段错误(核心已转储)
如图:
开始调试
开启gdb调试工具有两种方法
1.直接输入gdb 这样进入调试工具后需要在命令行输入需要调试的程序名
2.输入gbd + 需要调试的文件名 即可开始调试该文件
1.查看源代码
指令为 list/l 直接输入简写l即可,表示下翻源代码 如果想上翻源代码 使用指令 l -但是实际应用中一般会采用分屏的方式调试,所以该指令没什么卵用
2.打断点
使用指令
1
2
3
4
5
6 break/b [文件名:][行号][函数名]
#例如
b 20#在第二十行设置断点
b main#在main函数的开头设置断点
b main.c:20#在main.c文件的第20行设置断点
b main main.c:main#在main.c文件的main函数开头设置断点设置好断点后,可以使用如下指令来查看断点信息
1 info break/i b #该指令可以省略为 i b,但是空格不能省 即不能使用ib设置断点后的效果图
显示的信息从左往右为:
这段信息从左往右内容是:
- Num:断点的编号,一次Debug过程断点编号会从1开始,且不会重置一直累加。
- Disp(Disposition):它表示断点触发后,是否会继续触发。
- keep就表示它是一个持久的断点,只要不删除就一直存在,每次启动都会触发。
- 这个值如果是del就表示,该断点是一个一次性断点。
- 可以使用
tbreak
指令设置一次性断点,使用方式和break
一致。(tbreak,即temp break,一次性的临时断点)- Enb(enable):指示断点是否生效,可以通过
dis
指令设置该属性。- What:指示断点在源代码的哪个位置
删除断点
1 delete/d [n]#删除所有断点或者n号断点如果不想删除断点,也可以暂时让断点失效,成为一个“哑断点”,指令为:
1
2 disable/dis [n]
enable/en [n]打了断点后,就可以使用run/r指令来启动调试程序了,程序运行到断点处会暂停运行
3.GDB常用调试指令
1.逐语句/单步调试
1 step该指令遇到函数调用会进入函数,但是也会进入一些库函数的执行,这并没必要,所以一般使用逐过程来控制程序继续执行
1 next/n该指令碰到函数调用不会进入函数,而是把函数调用看作一条语句直接执行完毕
1 finish该指令可以执行完整个函数,并跳出函数
监视窗口
1
2
3
4
5
6
7
8 print/p express#后面跟一个表达式,也可以把这个功能当作linux的计算器来使用
#例如
p arr[0]
p 3.14*2
#该指令还可以用来改变变量的值,格式为
p express = val
#例如
p arr[0] = 20可以使用display命令自动展示表达式的值,使用格式为
1
2
3 display express # 每调试一步输出一次express的值
undisplay [n] # 删除所有或[n]号自动展示的表达式
info display # 显示所有自动展示的表达式信息通过命令查看参数和局部变量的值
1
2 (gdb) info args # 查看函数的参数
(gdb) info locals # 查看函数所有局部变量的值继续
continue/c 命令可以运行到逻辑上的下一个断点处
1 (gdb) c #相当于VS当中的继续功能按钮出现段错误信息:
忽略断点n次
可以用 ignore 命令来指定忽略某个断点多少次,这在调试循环的时候非常有用。使用格式如下:
1
2
3 ignore N COUNT
#例如
(gdb) ignore 1 10 # 忽略1号断点10次注意:
- 设定忽略断点次数后,还需要按
c
继续跳过断点才会生效。- 设定一次,仅生效一次。
- 跳过n次断点,那么程序会在(n + 1)次到达这个断点时停下来。
查看堆栈信息
1 bt/backtrace #查看当前调用堆栈的信息,会一直追溯到程序启动一般在报错位置使用该指令,用于查看程序执行流程以及排查相关的问题
输入命令行参数
当main函数的形参列表是
int argc, char *argv[]
时,允许可执行程序传参命令行参数。如果想要使用GDB调试带命令行参数的可执行程序,有以下两种方式可以选择:
- 在启动GDB时,使用指令
gdb --args ./a.out arg1 arg2 arg3...
即可表示传递命令行参数,其中a.out表示可执行程序的名字。- 如果已经启动了GDB,可以使用以下两种方式都可以传递命令行参数:
- 使用指令
set args arg1 arg2....
,其中指令部分是set args
,后面的部分则是参数。- 使用指令
run/r arg1 arg2
启动,也表示传递命令行参数。
4.调式Coredump文件
如果代码压根跑不起来,或者跑起来就会直接崩溃,我们还可以利用Core文件进行辅助调试。
通常情况下,程序异常终止时,会产生 Coredump 文件。Coredump 文件类似飞机上的”黑匣子”,它会保留程序”失事”瞬间的一些信息,通常包含寄存器的状态、栈调用情况等。
Coredump 文件常用于辅助分析和 Debug,下面介绍一下这种调试手段。
首先,系统默认是不会生成Core文件的,这一点可以通过以下指令查看:
1
2
3
4
5 ulimit -a #查看当前shell进程的各种资源限制,比如core文件
#默认情况下 指令输出的第一行是 core file size (block,-c)0 说明
#此时系统允许生成的core文件最大时0个字节,就是不允许生成
#使用如下指令需要用下列指令将core文件的大小设为不受限制
ulimit -c unlimited #将core文件的大小临时设置为不受限制默认情况下,上述操作可能还无法生成core文件,这是需要切换到root用户或者使用sudo权限,然后补充core文件的配置信息到目录文件中。
1
2
3
4
5 sudo vim /etc/sysctl.conf #打开配置文件
#将下列信息补进去
kernel.cor_pattern = ./core_%e_%t
#紧急着执行下列指令让配置信息生效
sudo sysctl -p配置完成后,一般如果发生了段错误就会生成对应的core文件
然后使用指令
1 gdb 可执行文件的名字 + core文件名查看报错的一些信息,此时再利用bt等指令就可以进行正常的程序调试了。