Linux | 调试器GDB的详细教程【纯命令行调试】-2

简介: Linux | 调试器GDB的详细教程【纯命令行调试】

⌨ 运行 / 调试

r(run) —— F5【无断点直接运行、有断点从第一个断点处开始运行】

  • 首先若是将断点删除掉,使用【r】指令运行的话就会直接运行到程序结束

image.png

  • 再加上断点去运行的话就会在打的断点处停下来

image.png

⌨ 逐过程和逐语句

n(next) —— 逐过程【相当于F10,为了查找是哪个函数出错了】

  • 可以看到,我从第一个断点处也就是20行的位置开始执行,按下【n】之后因为在其后即22行有一个断点,此时就会直接运行到断点处

image.pngs(step) —— 逐语句【相当于F11,一次走一条代码,可进入函数,同样的库函数也会进入】

  • 此时我们按下【s】,也就相当于是【step】,让程序一步一步地走,继而进入了AddToTop这个函数,若是你在printf()语句要执行时按下【s】的话gdb就会进入printf()库函数内部去执行,这里就不展示了

image.png

  • 接下去我们可以就继续【n】,然后进行逐过程调试,来到for循环中,那么逐过程也就是==变量i的累加和计数器count的累加==,所以会反复执行(通过图中最左侧可以看出是第8行和第10行在反复执行
  • 可以看到后面我没有再按【n】了,但是依旧会执行上面的步骤,这点上面也有提到过,因为gdb会自动化记忆你上一次执行过的命令,所以若是不想再敲了,直接Enter就可以了

image.png

⌨ 打印 / 追踪变量

p(print) 变量名 —— 打印变量值

  • 都执行了那么多次了,不知道【i】和【count】发生了怎样的变化,将它们打印出来看看吧💻

image.png

  • 通过继续执行【n】,然后再去打印就可以发现i的值和count的值发生了变化

image.png

但是你不觉得这样每次去打印会显得很繁琐吗,那一定会的,所以我们有更好的办法💡

display —— 跟踪查看一个变量,每次停下来都显示它的值【变量/结构体..】

image.png

  • 我们也可以去追踪一下这两个变量的地址,不过可以看到对于地址来说是不会发生改变的

image.pngundisplay + 变量名编号 —— 取消对先前设置的那些变量的跟踪

  • 但是呢,每次都追踪打印这么多内容又太多了,我想把它们取消了可以吗?答:当然是可以的
  • 既然有display,那就有undisplay

image.png

⌨ 查看函数调用

bt —— 看到底层函数调用的过程【函数压栈】

  • 通过仔细观察刚才追踪的4个变量最左侧的编号,就可以看到它们的排列的顺序是倒着的。因为变量i和变量count是我们先追踪的,它们的地址是我们后追踪的,所以可以看出这很像是一个==压栈的过程==

image.png

  • 其实不仅是对于它们,AddToTop函数和main函数也呈现这样的关系。此时我们就可以通过【bt】这个指令来查看函数压栈的过程,此时便可以看到因为

image.png

⌨ 修改变量的值

set var —— 修改变量的值

  • 对于这个修改变量的值,很像是在VS里调试之前设置的那种条件断点,可以使调试开始后直接运行到此断点处。不过对于【set var】而言是在调试过程中进行设置

image.png

我们也可以到VS中来看一下条件断点是如何设置的

在右击选择【条件】后,就可以输入你本次执行代码想要从哪个条件开始,然后按下Enter,启动调试之后就会从你设置的条件处开始执行

排查问题三剑客🗡

掌握了上面的这些,你就可以在Linux下调一些简单的代码了,不过想做到高效地进行调试,就需要学习一下【三剑客】

⌨ 指定行号跳转

until + 行号 ——  进行指定位置跳转,执行完区间代码

  • 可以看到,当前在for循环内容执行累加的逻辑,但若是我们一直这么执行下去,就没有时间排错了,除了上面的哪一种【set var】之外,还有一种方法其实起到直接结束当前循环的作用,那就是进行==指定行号跳转==
  • 通过观察下图可以看到,当我们运行了until 13之后,程序直接就给出了我们最终的结果count,而且即将要执行最后的打印语句,说明我们跳转成功了

image.png

  • 不过呢,在我使用这个【until】的时候,遇到了一个bug,此时我在循环内部,也就是第10行的位置打了一个断点,用过VS调试器的都知道一直按F5就可以立即执行下一次循环。
  • 我在上面是没有打上断点的,所以使用【until】的时候也没有出现问题,但是在下面我在循环内部设置了一个断点,此时再去使用【until 13】进行跳转的时候就出现问题了,并没有马上跳转到指定的行号,而是接着执行循环中的内容

image.png

⌨ 强制执行函数

finish —— 在一个函数内部,执行到当前函数返回,然后停下来等待命令

  • 有时候我们会有这样的需求,在初步排查的时候推断可能是某个函数内部的逻辑出了问题,但是呢又不想一步步地进到函数内部进行调试,==在VS中其实很简单,只需要在函数下方设个断点,然后F5直接运行到断点处即可==
  • 但是在Linux下的gdb中,我们可以使用【finsh】指令来直接使一个函数执行完毕。从下图我们可以看到,首先【s】进到函数内部,接下去我直接使用finish,可以看到它直接回到了调用函数的位置,returned了一个返回值

image.png

  • 然后可以看到,在获取到返回值后,也就直接进行了printf打印

image.png

⌨ 跳转到下一断点

c(continue) —— 从一个断点处,直接运行至下一个断点处【VS下不断按F5】

  • 这点也是我刚才在上面有提到过的,在VS中,我们要直接跳转到下一个断点处只修要按下F5即可,那在gdb中该如何操作呢,你需要敲个【c】就可以了
  • 从下图我们可以看出,对于这个指令的用处可谓是非常大,当我处于第一个断点也就是20行的时候,直接敲下【c】,就可以运行到第二个断点处也就是第10行。之后若反复敲【c】,因为这是一个单语句的循环,所以循环的下一次还是会执行到此处。==上面的这两个功能就和我们在VS中用的F5是一个道理==

image.png

四、实战演练:Swap Two Numbers

:book:纸上得来终觉浅,绝知此事要躬行🔨 ————————————

以下是本次调试所要使用到的代码,相信你一定非常熟悉了,也就是使用指针交换两数,如果想看细节讲解可以看看我的函数章节

  1 #include <stdio.h>
  2 
  3 void swap(int* x, int* y)
  4 {
  5     int t = *x;
  6     *x = *y;
  7     *y = t;                                                                                            
  8 }
  9 int main(void)
 10 {
 11     int a = 10;
 12     int b = 20;
 13 
 14     printf("a = %d, b = %d\n", a, b);
 15 
 16     swap(&a, &b);
 17 
 18     printf("a = %d, b = %d\n", a, b);
 19     return 0;
 20 }
  • 首先我们在程序第16行设置上一个断点,然后【r】从第15行开始运行

image.png

  • 然后我们使用【s】进入到swap函数中,因为我首先不想调试,想先立马看看运行结果,但是此时又已经进入调试了,那么我们就可以使用到【finish】来立马执行完这个函数,然后观察一下结果
  • 可以看到,最后打印出结果的时候a和b的值确实发生了交换

image.png

  • 既然清楚了二者会进行一个交换,接下去我们就逐语句【n】进行一个单步追踪吧
  • 因为提前看了执行结果,所以我们要重新开始调试,按下【r】即可,它会询问你是否需要重新开始调试,选择y之后就可以重新从16行开始进行调试

image.png

  • 首先通过【display】记录一下两个变量的值和地址

image.png

  • 接着按【s】进入到swap函数里,追踪一下指针x和指针y的内容,也就是它们所存放的地址,就可以看到,函数内部已经接受到了这两个变量的地址

image.png

  • 但是我们后面不想再查看它们了,只需要看值是否被修改,此时就可以取消对它们的跟踪【undisplay】

image.png

  • 然后对我们要观察的值变化继续做一个追踪
  • 并且在执行完第一个语句t = *x时,临时变量t中已经存放了变量a的值,也就是指针所指向的那块空间中的值

image.png

  • 接下去执行*x = *y,此时*x中的值就发生了变化,因为指针x可以直接找到变量a的地址,所以可以对其中的内容做修改,就变为了20

image.png

  • 接下去执行*y = t,同理,指针y可以直接找到变量b的地址,所以可以对其中的内容做修改,将原本保存在临时变量t中的10赋值给到*y,也就修改了其中的内容

image.png

  • 再按【n】的话这个swap函数就执行结束了,回到了main函数,就可以清楚地看到函数内部的修改带动了函数外部值的变化,真正地通过【传址调用】交换了两个数

image.png

敲黑板:重点来了

  • 但是我不仅想让你看到这些,更加重要的一点不知你有没有发现,刚才我们在main函数中追踪的变量a和变量b的值与地址,到了swap()中就不见了,这是为什么呢?
  • 其实就是因为两个函数的函数栈帧不同,变量a和变量b位于main函数的函数栈帧中;而指针变量x和指针变量y位于swap函数的函数栈帧中,是互不相干的,临时变量除了函数栈帧就会被销毁【如果听不懂这些请看反汇编深挖【函数栈帧】的创建和销毁
  • 但是因为在swap()内部接受到了外部两个变量的地址,所以在函数内部就可以通过这两个指针找到这两个变量的地址,从而对这两个空间中的内容做一个修改的操作

通过以上的实验演练,你是否对调试器GDB的使用更上一层楼🐧

五、总结与提炼

最后来总结一下本文所学习的内容:book:

  • 在本文的开始,我们在初见GDB的时候发现了【no debugging symbols】的报错信息,于是便谈到了一个可执行程序的【DeBug】版本和【Release】版本,知道了对于前者来说是我们程序员使用的环境,是存在调试信息的;对于后者来说是测试人员所处的环境,是不存在调试信息的。而对于gcc/g++而言默认生成的可执行程序就是【Release】版本的,因此我们要加上一个-g命令选项使其在make之后生成一个【DeBug】版本的可执行程序,这样就可以进行调试了
  • 接着我们正式进入到了调试器GDB的使用,介绍了很多相关的指令,这些都是我整理出来的常见指令,其实对于GDB来说还有很多指令,但是真正常用的也就这些,==只要你认真地看下来,将它们都使用熟练了,相信你的调试功底会大幅度提升==,对你在VS中的调试也是有所帮助的
  • 最后,在学习了GDB的许多调试指令后,我们便进行了【Swap Two Numbers】的实战演练,不仅巩固了C语言中有关传址调用以及函数栈帧的相关指知识,而且使我们对于调试器GDB的使用更上一层楼↑

以上就是本文要介绍的所有内容,感谢您的阅读:heart:

相关实践学习
阿里云图数据库GDB入门与应用
图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。 GDB由阿里云自主研发,具备如下优势: 标准图查询语言:支持属性图,高度兼容Gremlin图查询语言。 高度优化的自研引擎:高度优化的自研图计算层和存储层,云盘多副本保障数据超高可靠,支持ACID事务。 服务高可用:支持高可用实例,节点故障迅速转移,保障业务连续性。 易运维:提供备份恢复、自动升级、监控告警、故障切换等丰富的运维功能,大幅降低运维成本。 产品主页:https://www.aliyun.com/product/gdb
相关文章
|
25天前
|
弹性计算 安全 Linux
阿里云服务器ECS安装宝塔Linux面板、安装网站(新手图文教程)
本教程详解如何在阿里云服务器上安装宝塔Linux面板,涵盖ECS服务器手动安装步骤,包括系统准备、远程连接、安装命令执行、端口开放及LNMP环境部署,手把手引导用户快速搭建网站环境。
|
2月前
|
NoSQL 关系型数据库 Linux
ERPNext 搭建教程:Linux 一键部署与维护
ERPNext 是一款开源免费的企业资源计划系统,适用于中小企业信息化管理。基于 Python 和 Frappe 框架开发,支持财务、销售、人力、库存等模块,具备高度可定制性。本文介绍如何通过 Websoft9 在 Linux 下快速部署 ERPNext,并提供环境配置、系统维护等实用建议,适合开发者和企业用户快速上手。
355 7
ERPNext 搭建教程:Linux 一键部署与维护
|
6月前
|
安全 Linux 开发工具
【Linux】vim使用与配置教程
Vim是一款功能强大的文本编辑器,广泛应用于Linux环境,是开发者和系统管理员的必备工具。本文介绍了Vim的基本操作与简单配置,涵盖命令模式、插入模式和底行模式的使用方法,以及光标定位、复制粘贴、搜索替换等常用技巧。同时,文章还提供了实用的分屏操作和代码注释方法,并分享了通过`.vimrc`文件进行个性化配置(如显示行号、语法高亮、自动缩进等)的技巧,帮助用户提升文本编辑效率。掌握这些内容,能让Vim更好地服务于日常工作与开发需求。
425 3
|
1月前
|
Ubuntu 网络协议 Unix
Linux教程(Ubuntu为蓝本)之Linux介绍篇
SuSE嫁到了Novell,SCO继续顶着骂名四处强行“化缘”, Asianux, MandrakeSoft也在五年中首次宣布季度赢利。3月,SGI宣布成功实现了Linux操作系统支持256个Itanium 2处理器。[1-2]
|
1月前
|
Ubuntu Linux 数据安全/隐私保护
Win10安装Linux子系统教程!如何在Win10系统中安装Ubuntu!
登录系统后,输入cd /返回上一级,然后再输入“ls”查看一下系统文件目录,看看对不对!
|
2月前
|
Java Linux 网络安全
Linux云端服务器上部署Spring Boot应用的教程。
此流程涉及Linux命令行操作、系统服务管理及网络安全知识,需要管理员权限以进行配置和服务管理。务必在一个测试环境中验证所有步骤,确保一切配置正确无误后,再将应用部署到生产环境中。也可以使用如Ansible、Chef等配置管理工具来自动化部署过程,提升效率和可靠性。
284 13
|
4月前
|
NoSQL Linux 编译器
GDB符号表概念和在Linux下获取符号表的方法
通过掌握这些关于GDB符号表的知识,你可以更好地管理和理解你的程序,希望这些知识可以帮助你更有效地进行调试工作。
188 16
|
3月前
|
Java Linux 开发工具
Linux下版本控制器(SVN) -命令行客户端
Linux下版本控制器(SVN) -命令行客户端
81 3
|
4月前
|
Linux 数据安全/隐私保护
使用Linux命令行接入无线网络Wi-Fi的示例。
现在,你已经使用命令行成功地连接到 Wi-Fi 网络了。这两个示例涵盖了用 `nmcli` 和 `wpa_supplicant` 连接无线网络的常见场景,让你能够不依赖图形化界面来完成这个任务。在日常使用中熟练掌握这些基本操作能增强你对 Linux 系统的理解,帮助你更有效地处理各种问题。
172 12
|
3月前
|
关系型数据库 Linux 数据库
Linux系统安装Postgre和Postgis教程
本文详细介绍了PostgreSQL/PostGIS的卸载与安装步骤。卸载部分涵盖Docker、Yum/RPM及源码编译安装的清理方法,包括停止服务、删除容器/包、清理残留文件和环境变量等操作,并强调卸载前需备份数据库数据。安装部分提供在线yum安装和离线源码编译两种方式,前者简单快捷,后者需准备依赖(如gcc、readline-devel等)、创建用户组、初始化数据库及配置访问规则。每步均附带命令示例,确保操作清晰明确。
521 0