【Linux】——调试器-gdb的使用

news2024/9/21 22:36:48

序言:

  • 本期,我将带领大家学习的关于linux下的调试器gdb的使用,废话不多说跟着我一起去看看吧!!


目录

前言

(一)背景介绍

1、debug模式和release模式

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

3、初步见识

1️⃣readelf

(二)调试代码

1、命名大全

2、具体演示

0️⃣行号显示

1️⃣断点设置

2️⃣逐过程(逐语句)演示

3️⃣强制执行

4️⃣跳转到下一断点

(三)总结


前言

在之前的学习中,我们学习了关于linux下环境开发相关的工具,主要讲解了以下几个:

  • 💨【yum】—— 是Linux下非常常用的一种包管理器,进行软件安装;
  • 💨【vim】—— vim的使用使我们学会了如何编辑一个代码文本;
  • 💨【gcc/g++】—— gcc是一个功能强大的编译器集合,可以帮助开发人员将源代码转换为可执行二进制文件,并提供各种选项和参数来控制编译过程;
  • 💨【make/Makefile】—— 帮助我们自动化构建代码文本。

紧接着我们面临的一个问题就是我们已经有了可以编写代码的环境还可以编写代码,但是如何对一段代码去进行调试呢?这就是本期我们将要解决的问题。对于代码调试就需要使用到 Linux下的调试器gdb。在Linux环境下调试跟我们在vs下调试是不一样的,在vs下我们是通过图形化页面进行操作,而在Linux环境下则是纯文本的操作。


(一)背景介绍

1、debug模式和release模式

在Linux环境下,Debug和Release是两种不同的构建模式。它们的主要区别在于编译器优化、符号表和调试信息等方面。

  • ⚔️ 【Debug】—— 调试版本
  • ⚔️ 【Release】—— 发布版本

Debug模式

  1. Debug模式通常被用于开发阶段,其目的是为了方便开发人员进行代码调试和错误定位;
  2. 在Debug模式下,编译器会保留符号表和调试信息,生成未优化的可执行文件;
  3. 这样可以使得程序在运行时更容易被调试,开发人员可以使用调试器(如gdb)进行单步跟踪和变量监视等操作。

Release模式

  1. Release模式则用于产品发布阶段,其目的是为了生成可执行文件的最终版本,以提高程序运行效率
  2. 在Release模式下,编译器会开启各种优化选项,如去除未使用的代码、内联函数、循环展开等,以提高程序的执行效率;
  3. 同时,也会删除符号表和调试信息,减小可执行文件的大小。

区别

在Linux环境下,Debug和Release模式的区别在于:

  1. Debug模式会生成未优化的可执行文件,并保留符号表和调试信息
  2. 而Release模式则会开启各种优化选项,删除符号表和调试信息,生成可执行文件的最终版本。


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

首先大家需要知道的是在Linux下,Release模式和Debug模式的可执行文件是不同的。

  1. Debug模式生成的可执行文件包含了符号表和调试信息,可以被调试器(如gdb)使用进行调试;
  2. 而Release模式生成的可执行文件则去除了符号表和调试信息,因此不能使用调试器进行调试。

这样做主要出于两个原因:

安全性

  • 如果将符号表和调试信息包含在发布版本中,黑客可能会利用这些信息来发动攻击,因此在发布版本中删除这些信息是一个基本的安全措施。

可执行文件大小

  • 包含符号表和调试信息的可执行文件比没有这些信息的可执行文件要大得多。对于发布版本,我们希望可执行文件越小越好,这有助于提高程序启动速度和运行效率。

综上所述,在Linux下,我们通常在Debug模式下进行程序的开发和调试,然后在Release模式下进行最终的编译和发布。这样可以保证程序在开发过程中易于调试和定位问题,同时在发布时也能够提供更加高效和安全的可执行文件。


3、初步见识

接下来,我带大家简单的看看在Linux下调试的时候是否如上述我所说的一样

  • 以下是本次调试所要使用到的代码
#include <stdio.h>

int addToTop(int top)
{
    printf("enter addToTop\n");
    int sum = 0;
    for(int i = 1; i <= top; i++)
    {
        sum += i;
    }
    printf("quit addToTop\n");
    return sum;
}

int main()
{
    int top = 100;

    int result = addToTop(top);
    printf("result:%d\n", result);
    return 0;
}
  • Makefile中的内容:
mytest:test.c
	gcc -o mytest test.c  -std=c99
.PHONY:clean
clean:
	rm -f mytest

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


有了上述的代码之后,如果要进入gdb开始调试,只需 gdb + 可执行文件 即可实现;

 解释说明

  1.  从上述大家可以发现,当我们执行相应的指令之后,在显示出的内容中出现了【no debugging symbols found)】 这样的字眼,大概意思就是没有调试信息。
  2. 因此,我们不难看出这可能不是一个【DeBug】版本的可执行程序,就验证了我们讨论的话题——默认是在releas版本下执行的。

那么如何解决这个问题呢?

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

  • 紧接着我们再去执行相关指令,看最终的结果是否还会出现上述那样的情况:
  • 运行之后,我们可以发现此时则没有上述的显示无调试信息的字眼了

 

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

 


上述我们只是简单的说明一点,那就是调试是在debug环境下进行的。此时,大家可能会有这样的疑惑,上述所说的我也能懂,但是在调试信息这个层面上 加和不加调试信息有区别呢?

接下来,我就给演示一下这个可执行文件里的调试信息究竟是怎样的:

  • 首先,如果我们想读取一个二进制文件的一个构成,此时就需要一个指令——【readelf】

1️⃣readelf

简单介绍

  1. readelf 是Linux下的一个命令行工具,用于查看可执行文件、共享库和目标文件的详细信息
  2. 它可以显示这些文件的ELF头部、节(Section)头部、符号表、重定位表等相关信息。

readelf命令的常见用法如下

 💨 查看ELF头部信息

  • 使用 “-h” 选项可以查看可执行文件、共享库或目标文件的ELF头部信息,包括文件类型、目标体系结构、入口地址等。
  • 例如:
  •  readelf -h mytest
    

 💨 查看节(Section)头部信息

  • 使用 “-S” 选项可以查看可执行文件、共享库或目标文件的所有节头部信息。
  • 例如:
  •  readelf -S mytest
    

 💨 查看符号表

  • 使用 “-s” 选项可以查看可执行文件、共享库或目标文件的符号表。符号表是一个记录了程序中函数、变量等符号信息的表格,可以帮助调试器进行调试。
  • 例如:
  •  readelf -s mytest
    

 💨 查看重定位表

  1. 使用 “-r” 选项可以查看可执行文件、共享库或目标文件的重定位表。重定位表是一个记录了需要进行地址重定位的符号信息的表格,用于在程序加载时修改符号地址。
  2. 例如:
  3.  readelf -r mytest
    

⚔️ 除此之外,readelf还有许多其他选项和用法,可以根据需要进行深入学习和掌握 ⚔️ 


接下来,根据上述对 readelf 的介绍,我们执行以下指令:

 readelf -S mytest_debug

  • 此时上述可执行文件中的所有内容有30几条,若是我们只想查看一些关于debug的调试信息,就要对这些东西进行一个筛选;
  • 此时就可以使用【grep】命令来进行一个筛选,便可以查看到所有的debug调试信息(以后大家做信息筛选的时候就可以用到这个指令)

 

 

其实,对于【可执行文件】它是一个二进制文件,若是查看它的源码就可以发现里面都是一堆乱码:

 

【小结】

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

(二)调试代码

有了上述的基本认识之后,接下来我们就可以上手去进行操作了。

1、命名大全

在正式的进行代码前,我们需要知道基本的调试命名吧!以下是我整理的关于调试相关的基本指令:

  • list/l 行号:显示binFile源代码,接着上次的位置往下列,每次列10行。
  • list/l 函数名:列出某个函数的源代码。
  • r或run:运行程序。
  • n 或 next:单条执行。
  • s或step:进入函数调用
  • break(b) 行号:在某一行设置断点
  • break 函数名:在某个函数开头设置断点
  • info break :查看断点信息。
  • finish:执行到当前函数返回,然后停下来等待命令
  • print(p):打印表达式的值,通过表达式可以修改变量的值或者调用函数
  •  p 变量:打印变量值。
  • set var:修改变量的值
  • continue(或c):从当前位置开始连续而非单步执行程序
  • run(或r):从开始连续而非单步执行程序
  • delete breakpoints:删除所有断点
  • delete breakpoints n:删除序号为n的断点
  • disable breakpoints:禁用断点
  • enable breakpoints:启用断点
  • info(或i) breakpoints:查看当前设置了哪些断点
  • display 变量名:跟踪查看一个变量,每次停下来都显示它的值
  • undisplay:取消对先前设置的那些变量的跟踪
  • until X行号:跳至X行
  • breaktrace(或bt):查看各级函数调用及参数
  • info(i) locals:查看当前栈帧局部变量的值
  • quit:退出gdb

2、具体演示

大家看到上述的指令那么多,就产生了恐惧的心理,其实大家不要害怕,Linux本来就是命令行的形式,我们多敲上个几遍,自然而然的就记住了。

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

 

0️⃣行号显示

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

  • 当我们想从开头显示时,即【L 0】或者是【L 1】的话那就是从第一行开始往下列10行的内容(其余的同理)

 

  • 若是想要看到我们所写的全部代码,只需要多按几次 【enter】键即可

 

 


1️⃣断点设置

  • break(b) 行号:在某一行设置断点

  • info break :查看断点信息。

 

  • run(或r):从开始连续而非单步执行程序(无端点直接运行结束)

  •  delete breakpoints n:删除序号为n的断点

 

 

 

  •  break 函数名:在某个函数开头设置断点

  •  delete breakpoints:删除所有断点

注意: 

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

 

  • disable breakpoints:禁用断点

 

  • enable breakpoints:启用断点

  • disable breakpoints n:使一个断点无效

 

 

 


2️⃣逐过程(逐语句)演示

为了方便演示,我退出调试后在重新来过。

  • n 或 next:逐过程单条执行【相当于F10,为了查找是哪个函数出错了】

在vs下不知道是否可以区分逐过程和逐语句之间的差别,如果忘了大家下去自己试试看具体是怎么样的。

现在,我要做的就是不进入我们定义的【addToTop】函数里面去,直接把这个函数跑完

 

  • s或step:进入函数调用 ,逐语句执行【相当于F11】

  • 紧接着可以就继续【n】,然后进行逐过程调试,来到for循环中,那么逐过程也就是变量i的累加和计数器count的累加,所以会反复执行;
  • 后面我没有再按【n】了,但是依旧会执行上面的步骤,因为gdb会自动化记忆你上一次执行过的命令,所以若是不想再敲了,直接【enter】即可

 在vs中,会有一个对话框,当我们想看哪个变量都可以从上观察到。那在Linux下怎么查看程序中【sum】以及【i】的值呢?

但是这样是不是觉得很“嘚儿”呀看着,本来在linux 下就是纯命令跟以前用的图形化比起来,我想查变量是想一直看到它的变化,而不是像现在这样。那有没有办法呢?其实是有的。

  • display 变量名:跟踪查看一个变量,每次停下来都显示它的值

 

  • undisplay:取消对先前设置的那些变量的跟踪

  •  bt看到底层函数调用的过程

AddToTop()  和 主函数 main() 函数呈现这样的关系,可以通过【bt】这个指令来查看函数压栈的过程

  •  until X行号:跳至X行

当前在for循环内容执行累加的逻辑,但若是我们一直这么执行下去,就会非常耗时,【until】其实起到直接结束当前循环的作用,即进行指定行号跳转

 

  • print(p):打印表达式的值,通过表达式可以修改变量的值或者调用函数

 


3️⃣强制执行

  • finish:执行到当前函数返回,然后停下来等待命令

 💨  在Linux下的gdb中,我们可以使用【finsh】指令来直接使一个函数执行完毕;

 💨 从下图我们可以看到,首先【s】进到函数内部,接下去我直接使用【finish】,可以看到它直接回到了调用函数的位置,returned了一个返回值

 


4️⃣跳转到下一断点

  • continue(或c):从当前位置开始连续而非单步执行程序

💨 在VS中,我们要直接跳转到下一个断点处只修要按下F5即可,那在gdb中该如何操作呢,只需要敲个【c】就可以了

 


(三)总结

到此,便是关于调试器gdb的全部内容了。接下来,我们简单回顾一下本期都学到了什么

  1. 首先,我们通过对比在vs下调试与linux下调试的区别,引出了可执行程序的【DeBug】版本和【Release】版本,并指出了默认情况下是release版本,我们也通过具体的代码来带大家验证了这一现象。我们要加上一个-g命令选项使其在make之后生成一个【DeBug】版本的可执行程序,这样就可以进行调试了;
  2. 接着我们就介绍了很多相关的指令,并且通过一一举例来大家认识这些指令。小伙伴们不要害怕,只要多加练习,自然就会熟记于心的。

以上便是本文的全部内容了,感谢您的观看和支持!!!

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/594352.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

nDreams CEO:是时候打破市场对VR游戏的错误认知了

自Quest系列头显问世以来&#xff0c;VR游戏市场仿佛被注入了一剂强心针&#xff0c;发展速度远超Rift时代。从Quest 1到现在&#xff0c;已经过去4年左右&#xff0c;现在VR游戏市场又走到了哪呢&#xff1f;在GDC 2023上&#xff0c;资深VR游戏工作室和发行商nDreams公布了一…

django-vue-admin-pro 使用

地址&#xff1a; GitHub - dvadmin-pro/django-vue-admin-pro 一、准备工作 Python > 3.8.0 (推荐3.9版本) nodejs > 14.0 (推荐最新) Mysql > 5.7.0 (可选&#xff0c;默认数据库sqlite3&#xff0c;推荐8.0版本) Redis(可选&#xff0c;最新版)项目运行及部署 |…

Android crash 流程详解(一):JE

源码基于&#xff1a;Android R 0. 前言 App crash(全称为 Application crash)&#xff0c;又分 java crash 和 native crash&#xff0c;又称 java layer exception(JE) 和 native layer exception(NE)。对于 crash 在开发过程中或多或少都会遇到&#xff0c;本文将整理总结 …

Shell脚本攻略:shell函数应用

目录 一、理论 1.shell函数 2.函数传参 3.函数变量的作用范围 4.递归 5.函数位置变量与脚本位置变量区别 6.创建库 二、实验 1.实验一 一、理论 1.shell函数 &#xff08;1&#xff09;概念 将命令序列按格式写在一起&#xff0c;可方便重复使用命令序列。 ① 避免…

JetBrains的Java集成开发环境IntelliJ 2023版本在Win10系统的下载与安装配置教程

目录 前言一、IntelliJ 安装二、使用配置总结 前言 IntelliJ IDEA Ultimate是一款功能强大的Java集成开发环境&#xff08;IDE&#xff09;。它提供了丰富的功能和工具&#xff0c;可以帮助开发人员更高效地编写、调试和部署Java应用程序。 IntelliJ IDEA Ultimate的主要特点…

Benewake(北醒) 快速实现TFmini-Plus-IIC与电脑通信的操作说明

目录 1. 概述2. 测试准备2.1 工具准备2.2通讯协议转换 3. IIC通讯测试3.1 引脚说明3.2 测试步骤3.2.1 TFmini-Plus-IIC 与 PC 建立连接3.2.2 获取测距值3.2.3 更改 slave 地址 1. 概述 通过本文档的概述&#xff0c;能够让初次使用测试者快速了解测试 IIC 通信协议需要的工具以…

48. 旋转图像

48. 旋转图像 C代码&#xff1a; void rotate(int** matrix, int matrixSize, int* matrixColSize){int m matrixSize;int n matrixColSize[0];int arr[m*n];int arrTop 0;memset(arr, 0, sizeof(int) * m * n);for (int i 0; i < m; i) {for (int j 0; j < n; j) …

【Springcloud】分布式搜索elasticsearch

文章目录 1、ElasticSearch 1、ElasticSearch 先看下翻译&#xff1a; elasticsearch是一款非常强大的开源搜索引擎&#xff0c;可以帮助我们从海量数据中快速找到需要的内容 项目在运行的时候会产生海量的日志信息&#xff0c;而elasticsearch结合kibana、Logstash、Beats&am…

2023免费的苹果手机备份app软件iMazing

苹果备份APP怎么备份&#xff1f;一般情况下&#xff0c;苹果手机备份照片、短信等可以使用iCloud备份。虽然App也可以使用iCloud备份&#xff0c;但是App数据一般较多&#xff0c;需要较大的iCloud存储空间&#xff0c;而免费的iCloud存储空间只有5GB&#xff0c;很多人都不想…

[C++] 继承和多态

Be water my friend. 一.关于继承(inheritance) 基础知识&#xff1a; 继承的定义格式&#xff1a; 继承方式的比较&#xff1a; 继承中的作用域&#xff1a; 基类和派生类对象赋值转换 : 派生类的默认成员函数 关于继承的补充 如何防止继承的发生(final关键字…

ThreeJS教程:CSS3批量标注多个标签

推荐&#xff1a;将 NSDT场景编辑器 加入你的3D工具链 3D工具集&#xff1a; NSDT简石数字孪生 CSS3批量标注多个标签 下面下工厂为例&#xff0c;使用CSS3DRenderer批量渲染多个HTML元素标签。 CSS3渲染器基本代码 CSS3渲染器代码和上节课内容一样设置即可。 // 引入CSS3渲…

掌握哪些测试技术才能说自己已经学成了?

一、过硬的基础能力 其实所有的测试大佬都是从底层基础开始的&#xff0c;随着时间&#xff0c;经验的积累慢慢变成大佬。要想稳扎稳打在测试行业深耕&#xff0c;成为测试大牛&#xff0c;首当其冲的肯定就是拥有过硬的基础&#xff0c;所有的基础都是根基&#xff0c;后期所…

Docker利用DockerFile创建部署NVIDIA+PyTorch容器

Docker利用DockerFile创建部署NVIDIAPyTorch容器 1、创建 Dockerfile2、在 Dockerfile 中添加关键字和命令3、使用 Docker Build 命令构建镜像4、验证和测试 Docker 映像 1、创建 Dockerfile 首先在用户的主目录下创建一个名为 mycode 的文件夹&#xff0c;然后创建 Dockerfil…

马斯克:我是 Rust 粉丝,但为了性能我会毫不犹豫选择 C/C++

作为一个几乎时刻处于风口浪尖上的“网络红人”&#xff0c;特斯拉 CEO 埃隆马斯克(Elon Musk)被外界评价为“致力于从人工智能手中拯救人类”的钢铁侠。近期&#xff0c;这位大佬又因不少“出格”言论而在社交媒体上引发热议 —— 在一家 AI 公司谈论编程“error messages”的…

【备战秋招】每日一题:4月1日美团春招(二批)第四题:题面+题目思路 + C++/python/js/Go/java带注释

2023大厂笔试模拟练习网站&#xff08;含题解&#xff09; www.codefun2000.com 最近我们一直在将收集到的各种大厂笔试的解题思路还原成题目并制作数据&#xff0c;挂载到我们的OJ上&#xff0c;供大家学习交流&#xff0c;体会笔试难度。现已录入200道互联网大厂模拟练习题&…

电商 - 高并发下订单商品库存扣减方案

开发一个电商库存系统时,我们最担心的就是高并发和防超卖了 电商库存系统场景 前提:分布式系统,高并发 商品A只有100库存,现在有1000或者更多的用户购买。如何保证商品库存在高并发的场景下是安全的 高并发场景下,商品展示页上面的信息,除了库存的其他信息属于静态数据…

ClickHouse性能调优——压缩和编码算法

随着数据库数据越来越多&#xff0c;给数据存储、网络访问造成成本和负担。压缩技术节约存储空间、加速网络访问的常用解决方案&#xff0c;本文主要介绍压缩算法和ClickHouse编码技术。 压缩类型 ClickHouse协议支持LZ4和ZSTD 压缩算法&#xff0c;两者都是基于字典使用校验和…

【Linux】信号(一文学会,八千字好文深度讲解信号)

目录 1.信号的初步理解 2.信号处理 信号的产生 信号的保存 前台进程和后台进程 信号处理以及产生信号 对于信号的处理方式有三种 产生信号&#xff1a; 1.通过终端按键产生信号 2.调用系统函数向进程发信号​编辑 ​编辑 3. 由软件条件产生信号 4.硬件异常产生信…

docker私有仓库harbor部署

1. harbor简介&#xff1a; Harbor是一个用于存储和分发Docker镜像的企业级Registry服务器&#xff0c;通过添加一些企业必需的功能特性&#xff0c;例如安全、标识和管理等&#xff0c;扩展了开源Docker Distribution。作为一个企业级私有Registry服务器&#xff0c;Harbor提…

系列二、MongoDB的安装

一、传统方式安装 1.1、下载安装包 https://www.mongodb.com/try/download/community-kubernetes-operator 1.2、上传至opt目录并解压 tzr -xzvf mongodb-linux-x86_64-rhel70-5.0.18.tgz 1.3、移动mongodb安装包并重命名 mv mongodb-linux-x86_64-rhel70-5.0.18 /usr/local…