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