【软件开发底层知识修炼】十四 快速学习GDB调试一 入门使用
发布日期:2021-07-01 00:06:09
浏览次数:2
分类:技术文章
本文共 2908 字,大约阅读时间需要 9 分钟。
前面几篇文章学习了链接器相关的内容。现在开始来学习GDB调试。我们的目的是通过这几篇文章将GDB调试完全学会。
文章目录
1 为什么需要GDB
- 什么是GDB?
- GNU项目中的调试器(gnu debuger)
- 能够跟踪程序的执行,也能够恢复程序执行前的状态
- 为什么需要GDB?
- 软件不是一次性开发完的,是软件就一定有bug,所以需要调试工具来定位bug
- 调试是软件开发过程中不可或缺的部分
2 GDB 的常规应用
- 自定义程序启动的方式(指定影响程序运行的参数)
- 设置条件断点(在条件满足时暂停程序的执行,一般为循环中的语句或者递归调用中的语句)
- 回溯检查导致程序异常的原因(一般是通过分析核心转储文件-core文件)
- 动态改变程序执行流(定位问题的辅助方式)
GDB的启动方式有哪些?
- 直接启动
- gdb
- gdb test.out
- gdb test.out core
- 动态连接
- gdb test.out pid
在给出gdb具体的调试代码步骤之前,我们先来看看两个应用示例:
- GDB应用示例一
- 其中设置命令行参数这里,如果了解main函数的参数的话应该知道,如果不了解命令行参数,请参考这篇文章:
- 还有一点就是下面两种载入目标程序的命令是一样的效果
- 其他的用法都很好理解,我们不再赘述
- GDB应用示例二
- 上述各个命令的使用方法也很好理解,我们只需要注意下图中的两种启动方式同样是等效的:
3 GDB调试程序实例
下面就开始使用具体的例子来详细解读GDB调试的过程
我们调试的两个代码为:
- test.c
#include#include extern int* g_pointer; // 外部变量extern void func(); // 外部函数void test_1(){ printf("test_1() : %p\n", test_1);}void test_2(){ printf("test_2() : %p\n", test_2);}void test_3(){ printf("test_3() : %p\n", test_3);}int main(int argc, char *argv[]){ typedef void(TFunc)(); TFunc* fa[] = { test_1, test_2, test_3}; int i = 0; printf("main() : begin...\n"); for(i=0; i 1); // 如果argc大于1,则执行睡眠函数 } printf("g_pointer = %p\n", g_pointer); func(); printf("main() : end...\n"); return 0;}
- func.c
#includeint* g_pointer;void func(){ *g_pointer = (int)"D.T.Software"; //注意,这里是出错的地方,g_pointer是指向0地址,但是在这里却对0地址赋值 return;}
- GDB使用初探-----下面我们暂时先不调试,先熟悉熟悉几个命令是如何使用的。
- 首先对上述程序编译并且运行:
- gcc func.c test.c -o test.out
- ./test.out
- 毫无疑问,程序肯定会产生错误,如下图:
- 这是在意料之中的,毕竟在func.c程序中,我们对0地址进行写内容了。
- 那么现在我们开始使用gdb来定位出错误,在开启gdb调试之前,需要在编译源程序的时候加上-g选项,并将程序的崩溃信息转储的core文件(这在这篇文章中有讲解过)。
- gcc -g test.c func.c -o test.out //重新编译加上调试信息
- ulimit -c unlimited //让程序在崩溃时产生core文件
- ./test.out //重新运行看看是否产生core文件
- 很明显核心已转出,生成了core文件----注意,这是GDB调试需要用的文件
- 输入命令:gdb test.out core 进行调试:
- 从这里我们甚至都直接看到了产生段错误的地方就在func.c程序中的第7行,func函数中出的问题。
- 输入quit命令退出当前gdb调试
- 输入命令:gdb 进行调试:
- 输入gdb后再gdb调试模式下输入:file test.out
- 然后字gdb调试模式下输入run,显示结果最后部分如下:
- 通过上面的动态图我们很容易发现,程序执行的很快,瞬间就到了段错误那里。我们回到test.c程序中,会发现这段代码中: for(i=0; i<100; i++) { fa[i%3] (); sleep(argc > 1); // 如果argc大于1,则执行睡眠函数 } 由于sleep参数中argc的参数为1(只有./test.out这个参数),所以不会睡眠。
- 但是我们可以在gdb中进行设置参数,输入命令set args D.T.SoftWare:
- 就像下面的动态图一样:
- 很明显,我们的程序运行起来变得慢很多,这是因为我们加了一个命令行参数D.T.SoftWare,现在命令行参数就有两个,一个是可执行程序test.out,一个是D.T.SoftWare(如果这里不明白命令行参数的意思,请参考这篇文章:)
- 最后,输入ctrl + c可以终止程序的执行,再输入continue可以继续执行刚刚被终止的程序。
- gdb动态连接到一个正在执行的程序,然后对其进行调试
- 还是直接看下面的动图,更加好理解:
- 在右边的终端我们运行程序的时候加一个参数D.T.SoftWare这样可以让上述的for循环中的sleep开启,让程序执行的慢一点
- 程序执行起来后,在左边的终端首先输入ps aux查看我们的程序的pid
- 然后sudo gdb 开启gdb,这里加上sudo以root模式开启,是因为动态连接正在运行的程序的话就需要以root模式
- 开启gdb进入gdb模式后,使用attach pid (这里的pid根据你自己查到的pid写)连接到我们运行的程序。
- 在我们连接到程序的一瞬间,发现程序的执行停止了(使用continue可以继续程序的执行,当程序运行到段错误那里,gdb可以发现错误),说明已经连接到运行中的程序,现在可以使用gdb对它进行调试了。怎么调试随你意,上面我们也说了几种简单的调试方法。
4 总结
本文只是介绍GDB调试的几种简单用法。下一篇文章会学习GDB的断点调试。
本文章参考狄泰软件学院相关课程
想学习的可以加狄泰软件学院群, 群聊号码:199546072学习探讨加个人(可以免费帮忙下载CSDN资源):
qq:1126137994 微信:liu1126137994转载地址:https://lyy-0217.blog.csdn.net/article/details/85148552 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
不错!
[***.144.177.141]2024年04月12日 00时47分26秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
记一次曲折的Debug经历
2019-04-30
HDU-2838 Cow Sorting(树状数组)
2019-04-30
基于SSM的兼职论坛系统的设计与实现
2019-04-30
基于java的ssm框架就业信息管理系统的设计
2019-04-30
如何用同期群分析模型提升留存?(Tableau实战)
2019-04-30
Oracle字符串分隔符替换(替换奇数个或偶数个)
2019-04-30
Oracle 利用 UTL_SMTP 包发送邮件
2019-04-30
Oracle的pfile和spfile的一点理解和笔记
2019-04-30
java实现稀疏数组及将稀疏数组存入硬盘中
2019-04-30
2021-05-18
2019-04-30
libuv实现ping包发送和接收
2019-04-30
基础架构系列篇-NGINX部署VUE
2019-04-30
基础架构系列篇-系统centos7安装kafka
2019-04-30
软件质量的8个特性
2019-04-30
2021年不可错过的17种JS优化技巧(一)
2019-04-30
在 Vue 中用 Axios 异步请求API
2019-04-30
MySQL进阶查询(SELECT 语句高级用法)
2019-04-30
Mysql 之主从复制
2019-04-30
【NLP学习笔记】中文分词(Word Segmentation,WS)
2019-04-30