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

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

一、前言

  • 学习了【vim】知道了如何编辑一个代码文本
  • 学习了【gcc】知道了如何编译一个代码文本
  • 学习了【make/Makefile】知道了如何自动化构建一个代码文本

但是如何对一段代码去进行调试呢,此时就要使用到Linux下的调试器gdb了。对于这个调试器来说,不像是VS中那样的图形化界面形式,而是采用纯命令行的形式进行调试。可能我的讲解会比较晦涩难懂,在学习的过程中主要是会一些gdb下基本的操作即可

二、调试版本与发布版本

1、见见gdb

下面是本次调试所要使用到的代码

  1 #include <stdio.h>
  2 
  3 int AddToTop(int top)
  4 {
  5     printf("Enter AddToTop\n");
  6 
  7     int count = 0;
  8     for(int i = 1;i <= top; ++i)
  9     {
 10        count += i;
 11     }
 12 
 13     printf("Quit AddToTop\n");                                                                         
 14     return count;
 15 }
 16 
 17 int main(void)
 18 {
 19     int top = 100;
 20     int ret = AddToTop(top);
 21 
 22     printf("ret = %d\n", ret);
 23     return 0;
 24 }

下面是Makefile中的内容,用于自动化编译

  1 mytest:test.c
  2     gcc -o mytest test.c -std=c99                                                                      
  3 .PHONY:clean
  4 clean:
  5     rm -rf mytest

注:-std=c99表示以c99的标准来编译代码


  • 如果要进入gdb开始调试,那直接gdb + 可执行程序即可
  • 不过进去之后发现似乎有一些奇怪的内容,【no debugging symbols found】,翻译过来就是没有调试信息。那这是为何呢?是gdb出问题了吗?

image.png

  • 先不要着急,如果有经常调试的通过就可以知道只有在【DeBug】的环境下才会有我们想要的调试信息,所以可以初步推断这可能不是一个【DeBug】版本的可执行程序
  • 先使用q(quit)退出gdb

让我们先看下去,了解一下其他的知识再来解决这个问题

2、程序员与测试人员

接下去我们就来说说有关【DeBug】和【Release】版本的不同之处

📚【Debug】—— 调试版本📚【Release】—— 发布版本

  • 在使用VS的时候我们可以直接使用鼠标来进行操作,当前程序以DeBug或者是Release的形式进行运行,那么运行出来的可执行程序版本也是不同的,我们程序员在编写代码后运行一般是使用【DeBug】环境进行运行。因为在企业里写软件项目,将代码写完后==程序员自己要做简单的测试,保证代码没有问题==
  • 当程序员自己测试完没有问题之后,就会将这个可执行程序给到测试人员进行测试,而且会给出自己的单元测试报告。对于测试人员来说所处的模式是【Release】,也就是将来客户要使用的这款软件的发布版本
  • 当测试在测的过程中,一定会发现一些问题。此时测试人员就会把报告再打回研发部。研发部做修改重新生成Release版本的可行性程序给到测试人员继续测试
  • 最后只有当测试通过了,再将生成的【单元测试报告】与产品经理进行核对之后没有问题,那这个软件才可以真正地面向市场👉

3、为什么Release不能调试但DeBug可以调试❓

其实对于我们刚才直接make自动化生成的可执行程序是通过gcc直接编译产生得到的,它是一个【Release】版本的可执行程序,因此无法进行调试

  • 若是我们想要使用gcc/g++去生成一个可执行程序时,默认是【Release】版本的,而不是【DeBug】
  • 但若是我们想要去生成一个【DeBug】版本的可执行程序也是可以的,只需要修改一下我们的Makefile即可,给gcc后面带上一个-g的命令选项,此时再去make一下的话生成的就是【DeBug】版本的了

  • 为了之前的【Release】版本不被覆盖,我们将其重命名一下为mytest-release
  • 在生成【DeBug】版本后一样对其进行一个重命名为mytest-debug

image.png

  • 通过观察上图中两个可执行文件的大小便可以发现虽然它们都是可执行程序,但是容量大小却不一样,这是为什么呢❓
  • 因为以Release版本发布的软件是给客户的,客户是不需要调试信息
  • 往可执行程序里添加很多的调试信息意味着软件的体积会变大
  • 一方面,用户下载需要时间了
  • 另一方面,用户下载好之后将软件启动、运行都需要更多的时间,体验不好。一般能不加就不加
  • 但是对于DeBug来说会自动加调试信息,容量体积比Release大

但是就这么说说太抽象了,我们得看看这个可执行文件里的调试信息究竟是怎样的💻

readelf -S mytest-debug

image.png

当我们面对一堆二进制乱码措手不及的时候,给大家提到过一个东西叫做readelf,其实它是Linux中的一个指令,可以用来读取【elf】格式的文件

  • 加上-S的命令选项以==符号表==的形式来进行读取这个文件,就以一个列表的形式展现出了这个带调试信息的可执行程序
  • 下面展现几个比较常见的。例如这里的
  • .rodata就是read only data即只读全局数据区
  • .data就是已初始化全局数据区
  • .bss就是未初始化全局数据区

image.png

  • 不过这些呢是这个可执行文件中的所有内容,若是我们只是先要查看一些debug的调试信息,就要对这些东西进行一个筛选才行
  • 此时就可以使用到grep命令来进行一个筛选。便可以查看到所有的debug调试信息了

image.png

  • 上面是查看【DeBug】版本下的调试信息,在【Release】版本有没有呢
  • 我们也是读取并搜寻一下这个文件便可以发现对于【Release】版本来说是不存在调试信息的,所以什么都没有被打印出来

image.png【总结一下】

  • 程序的发布方式有两种,debug模式和release模式
  • Linux gcc/g++出来的二进制程序,默认是release模式
  • 要使用gdb调试,必须在源代码生成二进制程序的时候, 加上-g选项

好,说到这里,对于调试相关背景就全部讲完了,接下去我们正式进入【gdb】的学习:keyboard:

三、使用gdb调试代码

1、指令集汇总

因为这个调试器是在Linux环境下的,是纯命令行模式,所以会有很多的指令,做好心里准备:cry:

注:()括号里面是该指令的全称

  • l(list) 行号/函数名 —— 显示对应的code,每次10行
  • r(run) —— F5【无断点直接运行、有断点从第一个断点处开始运行】
  • b(breakpoint) + 行号 —— 在那一行打断点
  • b 源文件:函数名 —— 在该函数的第一行打上断点
  • b 源文件:行号 —— 在该源文件中的这行加上一个断点吧
  • info b —— 查看断点的信息
    breakpoint already hit 1 time【此断点被命中一次】
  • d(delete) + 当前要删除断点的编号 —— 删除一个断点【不可以d + 行号】
  • 若当前没有跳出过gdb,则断点的编号会持续累加
  • d + breakpoints —— 删除所有的断点
  • disable b(breakpoints) —— 使所有断点无效【默认缺省】
  • enable b(breakpoints) —— 使所有断点有效【默认缺省】
  • disable b(breakpoint) + 编号 —— 使一个断点无效【禁用断点】
  • enable b(breakpoint) + 编号 —— 使一个断点有效【开启断点】
  • 相当于VS中的空断点
  • enable breakpount —— 使一个断点有效【开启断电】
  • n(next) —— 逐过程【相当于F10,为了查找是哪个函数出错了】
  • s(step) —— 逐语句【相当于F11,】
  • bt —— 看到底层函数调用的过程【函数压栈】
  • set var —— 修改变量的值
  • p(print) 变量名 —— 打印变量值
  • display —— 跟踪查看一个变量,每次停下来都显示它的值【变量/结构体..】
  • undisplay + 变量名编号 —— 取消对先前设置的那些变量的跟踪

排查问题三剑客🗡

  • until + 行号 ——  进行指定位置跳转,执行完区间代码
  • finish —— 在一个函数内部,执行到当前函数返回,然后停下来等待命令
  • c(continue) —— 从一个断点处,直接运行至下一个断点处【VS下不断按F5】

2、命令演示

看了上面的这些命令后,相信你一定回到了刚开始学习Linux指令的时候那种恐惧感,不过没关系,我会一一地演示这些指令,让你在看完本文后有一个基本的调试能力💪

  • 首先我们进入到gdb,然后它会等待我们输入指令

image.png

⌨ 行号显示

l(list) 行号/函数名 —— 显示对应的code,每次10行

  • 首先若是直接【L】的话便会随机显示出该源文件中的随机10行内容,这不是我们想要的

image.png

  • 若是【L 0】或者是【L 1】的话那就是从第一行开始往下列10行的内容
  • 注意这里的L是小写,而且与数字之间要有一个空格

image.png

  • 接下去若是想要看到我们所写的全部代码,只需要多Enter几次就可以了,gdb会自动记忆你上次敲入的指令

image.png

⌨ 断点设置

b + 行号 —— 在那一行打断点

image.pngb 源文件:函数名 —— 在该函数的第一行打上断点

image.png

b 源文件:行号 —— 在该源文件中的这行加上一个断点

image.png

⌨ 查看断点信息

info b —— 查看断点的信息

  • 若是直接执行【info】的话,出来的就是所有的调试信息

image.png

  • 但若是我们只想查看一下所打的断点的信息,那就在后面加个b/breakpoint

image.png来简要介绍一下断点的一些字段信息

  • Num —— 编号
  • Type —— 类型
  • Disp —— 状态
  • Enb —— 是否可用
  • Address —— 地址
  • What —— 在此文件的哪个函数的第几行

image.png

  • 最后的话就是每个断点信息的下面这块breakpoint already hit 1 time即此断点被命中1次

⌨ 删除断点

d + 当前要删除断点的编号 —— 删除一个断点【不可以d + 行号】

image.pngd + breakpoints —— 删除所有的断点

image.png

  • 此时若继续将这个20行的断点打上时,就可以发现其编号为【4】,而并不是从1开始,这是因为我们没有退出过gdb,所以会持续上一次的编号继续往下

image.png

⌨ 开启 / 禁用断点

disable b(breakpoints) —— 使所有断点无效【默认缺省】

image.pngenable b(breakpoints) —— 使所有断点有效【默认缺省】

image.pngdisable b(breakpoint) + 编号 —— 使一个断点无效【禁用断点】

image.pngenable b(breakpoint) + 编号 —— 使一个断点有效【开启断点】

image.png

其实对于禁用断点和启用断点,VS中也是有的,它叫做【空断点】。我们一起来看看

image.pngimage.png


相关实践学习
阿里云图数据库GDB入门与应用
图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。 GDB由阿里云自主研发,具备如下优势: 标准图查询语言:支持属性图,高度兼容Gremlin图查询语言。 高度优化的自研引擎:高度优化的自研图计算层和存储层,云盘多副本保障数据超高可靠,支持ACID事务。 服务高可用:支持高可用实例,节点故障迅速转移,保障业务连续性。 易运维:提供备份恢复、自动升级、监控告警、故障切换等丰富的运维功能,大幅降低运维成本。 产品主页:https://www.aliyun.com/product/gdb
相关文章
|
25天前
|
Linux 数据安全/隐私保护
适用于 Linux 的最佳命令行下载加速器
适用于 Linux 的最佳命令行下载加速器
45 3
|
2月前
|
监控 数据可视化 Ubuntu
|
22天前
|
缓存 NoSQL Linux
Linux调试
本文介绍了Linux调试、性能分析和追踪的培训资料,涵盖调试、性能分析和追踪的基础知识及常用工具。
217 6
Linux调试
|
4月前
|
NoSQL Linux C语言
Linux GDB 调试
Linux GDB 调试
66 10
|
4月前
|
安全 Linux 开发工具
探索Linux操作系统:从命令行到脚本编程
【8月更文挑战第31天】在这篇文章中,我们将一起潜入Linux操作系统的海洋,从最基础的命令行操作开始,逐步深入到编写实用的脚本。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供新的视角和实用技能。我们将通过实际代码示例,展示如何在日常工作中利用Linux的强大功能来简化任务和提高效率。准备好了吗?让我们一起开启这段旅程,探索Linux的奥秘吧!
|
4月前
|
Ubuntu Linux
内核实验(四):Qemu调试Linux内核,实现NFS挂载
本文介绍了在Qemu虚拟机中配置NFS挂载的过程,包括服务端的NFS服务器安装、配置和启动,客户端的DHCP脚本添加和开机脚本修改,以及在Qemu中挂载NFS、测试连通性和解决挂载失败的方法。
220 0
内核实验(四):Qemu调试Linux内核,实现NFS挂载
|
4月前
|
NoSQL Linux 编译器
内核实验(一):使用QEMU+GDB断点调试Linux内核代码
如何配置环境并使用QEMU虚拟机结合GDB进行Linux内核代码的断点调试,包括安装QEMU、交叉编译工具链,编译内核以及通过GDB远程连接进行调试的详细步骤。
169 0
内核实验(一):使用QEMU+GDB断点调试Linux内核代码
|
4月前
|
Linux
探索Linux操作系统:命令行与脚本编程基础
【8月更文挑战第31天】在这篇文章中,我们将一起踏上一段旅程,深入探索Linux操作系统的奥秘。通过学习命令行的使用和编写简单的脚本,你将能够更高效地与你的计算机进行交流。无论你是新手还是有经验的用户,本文都将为你打开一扇通往Linux世界的大门。准备好了吗?让我们开始吧!
|
4月前
|
运维 监控 Linux
深入理解Linux系统运维:命令行与脚本的奥秘
【8月更文挑战第30天】在Linux的世界里,命令行是运维人员的灵魂之窗。掌握命令行,就像握住了一把钥匙,能开启系统管理的宝藏箱。本文将带你走进Linux的命令行世界,通过实际代码示例,解锁那些高效管理和维护系统的秘籍。你将学到不仅仅是命令本身,更是如何将这些命令编织成强大的脚本,让日常的运维工作变得游刃有余。准备好跟随我的步伐,一起深入探索Linux命令行与脚本的奥秘吧!
|
4月前
|
Linux
Linux命令行文档查看cat、less、more、head、tail和图片查看
Linux命令行文档查看cat、less、more、head、tail和图片查看
59 0