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
相关文章
|
8天前
|
Linux
Linux(5)WIFI/BT调试笔记
Linux(5)WIFI/BT调试笔记
29 0
|
24天前
|
Linux
linux下搭建tftp服务器教程
在Linux中搭建TFTP服务器,需安装`tftp-server`(如`tftpd-hpa`)。步骤包括:更新软件包列表,安装`tftpd-hpa`,启动并设置开机自启,配置服务器(编辑`/etc/default/tftpd-hpa`),添加选项,然后重启服务。完成后,可用`tftp`命令进行文件传输。例如,从IP`192.168.1.100`下载`file.txt`: ``` tftp 192.168.1.100 &lt;&lt;EOF binary put file.txt quit EOF ```
31 4
|
2月前
|
Linux 编译器 程序员
【Linux 调试秘籍】深入探索 C++:运行时获取堆栈信息和源代码行数的终极指南
【Linux 调试秘籍】深入探索 C++:运行时获取堆栈信息和源代码行数的终极指南
69 0
|
3天前
|
网络协议 算法 Linux
【Linux】深入探索:Linux网络调试、追踪与优化
【Linux】深入探索:Linux网络调试、追踪与优化
|
2天前
|
Linux Python
【专栏】Linux 中某个目录中的文件数如何查看?这篇教程分分钟教会你!
【4月更文挑战第28天】在Linux中查看目录文件数的方法包括:使用`ls`结合`wc -l`,如`ls &lt;directory_path&gt; | wc -l`;使用`find`命令,如`find &lt;directory_path&gt; -type f | wc -l`;使用`tree`命令,如`tree &lt;directory_path&gt;`(可能需额外安装);以及通过编程方式,例如Python代码实现。注意权限、效率和选择适用方法以提升操作效率。本文提供了详细步骤和示例,助你轻松掌握!
|
5天前
|
NoSQL Ubuntu 测试技术
【GDB自定义指令】core analyzer结合gdb的调试及自定义gdb指令详情
【GDB自定义指令】core analyzer结合gdb的调试及自定义gdb指令详情
8 1
|
5天前
|
NoSQL Ubuntu 开发工具
【gdb调试】在ubuntu环境使用gdb调试一棵四层二叉树的数据结构详解
【gdb调试】在ubuntu环境使用gdb调试一棵四层二叉树的数据结构详解
6 1
|
8天前
|
Linux Android开发
Linux(6)CH9434 SPI调试笔记
Linux(6)CH9434 SPI调试笔记
15 0
|
20天前
|
Linux
Linux命令行快捷键
Linux命令行快捷键
|
28天前
|
安全 Linux
嵌入式Linux系统关闭串口调试信息的输出
嵌入式Linux系统关闭串口调试信息的输出
23 1