linux下开发gdb调试

操作系统:centos7

yum install gdb //安装gdb调试工具

太长不看版
(注意,如果针对cmake项目,需要把编译模式从release改成debug,否则无法定位到准确的行数)

进入gdb debugger界面==>gdb filename 示例:gdb helloworld.cpp
查看segmentation fault 位置:
run
backtrace
加断点: break filename:line
示例:break helloworld.cpp:10
加条件断点: break filename:line if (condition)
示例:break helloworld.cpp:10 if (i ==10)
查看局部变量: print(variable)
示例:print i
运行 run
继续运行 continue
运行下一个指令 next
进入一个函数体 step into 
观察一个变量 watch 

手把手教学版
先上一段充满bug的代码:

#include <stdio.h>
#include <stdlib.h>
// this function is 100% correctly implemented

void print_heart();
void times_two(int *num) {
// calculate 2*num
for (int i = 0; i < *num; i++) {
*num++;
}
}




int main(int argc, char *argv[]) {
// parse arguments
if (argc != 2) {
printf("usage: %s <number>\n", argv[0]);
return 1;
}
print_heart();

// allocate memory
int *number = malloc(sizeof(int));
if (number = NULL)
return 1;
*number = atoi(argv[0]);

// calcualte and print result
times_two(number);
printf("Result: %d\n", *number);

// free memory
free(number);
return 0;
}

void print_heart() {

int i, j;
for (i = 0; i < 3;i++)
{
for (j = 0; j < 5-2*i;j++)
{
printf(" ");
}
for (j = 0; j < 5+4*i;j++)
{
printf("❤");
}
for (j = 0; j < 9-4*i;j++)
{
printf(" ");
}
for (j = 0; j < 5+4*i;j++)
{
printf("❤");
}
printf("\n");
}
for (i = 0; i < 3; i++)
{
for (j = 0; j < 29;j++)
printf("❤");
printf("\n");

}
for (i = 0; i < 6+ 1; i++)
{
for (j = 0; j < 2*i+1; j++)
printf(" ");
for (j = 0; j < 27-4*i; j++)
printf("❤");
printf("\n");
}
for (i = 0; i <1; i++)
{
for (j = 0; j < 14; j++)
printf(" ");
for (j = 0; j < 1; j++)
printf("❤");
printf("\n");
}



}

  


这个代码的作用是打印一个骚气的爱心,然后,将输入的数字×2.(显然,刚开始,代码不能实现这样的功能)

用gcc编译改代码:

gcc -g -std=c99 -o bug bug.c

#run
./bug 3
你会发现有core dump,所以,接下来开始debug

 

启动gdb

gdb --args ./bug 10
这时,你会发现,Terminal左边出现了(gdb)

然后输入“run”

 

从终端可以看到,在28行出现错误,然后对应代码;

 

28行涉及到的变量包括number,和右边的参数。我们可以用gdb显示变量的数值,所以来看看number吧。

 

通过检查,发现number指向了一个nullptr,这显然不是我们期望的。再来看看argv[0], 发现它是一个

 

我们希望的是把输入char改成int类型,但是,发现它不是这样的。所以,对代码修改。

 

重新编译,使用gdb

 

从结果上看,还是一样的错误。

但是,这时候如果打印会发现;

 

所以,右边的数值是我们期待的,去除了一个bug。。。只是左边还是一个nullptr。

因为程序一旦碰到bug结束,就要重新运行,所以调试的时候,我们希望一步一步,就需要加breakpoint。在之前的代码里,在28行出现了问题,所以我们需要在这之前添加断点,我们关注的变量是number,因此,我选择了在25行添加断点。

 

注意,断点打在25行意味着,25行还没有被执行!

我们执行next,来跑下一行代码,对应的就是25行,这时候,可以发现,和我们的预期一样,number是一个有效地址。继续next,然后在28行,地址重新变为了0。

 

所以问题其实就找到了。在26行的判断语句中,我们把number重新赋值为null。所以,赶紧修改, 重新编译

 

继续在28行打断点,然后next break bug.c 28

 

发现,*number = 10, 现在正常了

 

然后,自信的continue

 

但是,结果是不对的。。。重新输入run,next。

通过step,我们可以进入函数,

 

输入list,我们可以在终端查看代码

print i 会发现 i对应的是一个奇怪的数字,因为还没有初始化。

接着,next

 

这时候,print 就得到了 0

 

通过操作,我们发现,在for循环中,增加的是地址,而我们希望的是,增加地址对应的数的大小。

所以,修改代码。

 

接下来重新编译,然后这次我们在函数循环的开始处加断点,这时候,我们观察num对应的地址,没有改变,说明改变是生效可行的。

 

我们假设程序是正确的,直接continue,这时候,其实还是naive了。结果是错的。

 

重新run,继续debug,

我们在循环开始的地方加上断点,因为我们想知道进入循环之前的状态

 

我们加上一个条件断点,因为我们关心当在循环中满足条件时,为什么没有正常退出。我们关注×num最后一次做加法的情况,也就是×num=19==》后来测试发现,在下一行观察number和i似乎更有效。。。。

 

这时候,其实结果很明显了

 

懂得都懂,不懂的也不方便多说,这行水太深。我们对程序进行修改。

 

还有一个重要功能:

backtrace: 可以查看代码嵌套的过程。并且,非常适合找到segmentation对应的行数。

-------------------------------------------------------------------------------------------------------

更新: 当程序启动后,依旧另外启动终端,将GDB介入,进行debug,方法如下:

首先利用关键词找到需要debug的程序的Process ID。

ps -ef | grep [keyword]
然后,执行

sudo gdb -p 29925
#将29925替换为你要找的pid
这样,你就可以介入进行debug了

转载:https://blog.csdn.net/m0_47096428/article/details/116721465

 

posted @ 2022-11-09 20:24  dongzhaosheng73  阅读(79)  评论(0编辑  收藏  举报