【Linux】Linux下调试器gdb的使用

news2024/11/20 7:14:58

👑作者主页:@安 度 因
🏠学习社区:StackFrame
📖专栏链接:Linux

文章目录

  • 一、前言
  • 二、铺垫
  • 三、指令集和使用
    • 1、指令集
    • 2、演示
  • 四、结语

如果无聊的话,就来逛逛 我的博客栈 吧! 🌹

一、前言

前几篇 Linux 博客中,我们分别学习了与编辑、编译、自动化构建代码、上传代码的工具。而今天,我们将学习最后一个工具 —— Linux 下的调试器 gdb 。

而 gdb 这个调试工具的指令有很多,且在 Linux 下并没有图形化界面,所以 gdb 调试的体验并不是很好。

所以对于 gdb ,我们的学习目标就是会常用的操作。对于生僻的操作,我们简单介绍,对于常用的操作,我们进行演示。做到学习了前几篇博客之后能在 Linux 上,进行简单的线上开发。

这样也减少了我们学习的成本。

好了,我们这就开始今天的学习。
在这里插入图片描述

二、铺垫

在讲解使用之前,我们先进行一下铺垫:

程序的发布方式有两种,debug 模式和 release 模式 ,分别是调试版本和发布版本。

debug 模式是程序员在自主编写代码时的模式,在 debug 模式下是 含有调试信息 的;而 release 模式下是 不含调试信息 的 ,且 release 进行了各种优化,方便用户使用。

在 Linux 的 gcc/g++ 编译器下编译的代码往往都是 release 版本 。其实对于这点也很容易想通,我们平常的开发一般是在 ide 上进行,当产品发布时再到 Linux 上进行线上发布,且测试人员测的也是 release 版本的代码。所以为什么是 release 版本就很容易想通了。

事实胜于雄辩,我们再证明一下

调试代码:

#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 res = addToTop(top);
    printf("result = %d\n", res);
    
    return 0;
}

Makefile:

test:test.c
		gcc test.c -o test -std=c99
.PHONY:clean
clean:
		rm -f test

(注:在循环内定义变量为 c99 标准,所以编译时需要加上 -std=c99 )

Release

编译一下文件,并将生成可执行程序改名为 test-release

gdb test-release 进入调试:

image-20230114183749973

no debugging symbols found:没有调试信息

而 release 版本是没有调试信息的,没有调试信息就无法调试。

按 quit 退出调试。

在 Linux 中,编译时添加 -g 选项,就是让 gcc/g++ 编译时以 debug 形式编译

Debug

所以修改一下 Makefile

gcc test.c -o test -g -std=c99

然后继续进行编译,并将编译出的内容改名为 test-debug :

image-20230114183610740

gdb-test-debug 开始调试:

image-20230114184134800

这时就可以调试了,验证完毕,按 quit 退出调试。

对比生成文件大小

image-20230114184444371

debug 的文件,比 release 大。就是因为里面包含了调试信息等内容。

拓展

readelf 指令:读取可执行程序的二进制构成,可执行程序遵守二进制排布规则:elf

readelf -S test-debug | grep -i debug 忽略大小写筛一下含有 debug 的信息:

image-20230114185138425

当然如果直接查看 test-debug 的二进制排布的话,也可以看到 .data .bss .rodata 等字段,这些也都是我们的老熟人:已初始化全局数据区、未初始化全局数据区以及常量字符串所在的只读全局数据区。

由于图比较大,我就不放出来了,大家感兴趣可以下去查看一下。

好了,讲了这么多,我们的铺垫总结起来无非就是三点

  1. 程序有两种发布方式:debug 模式 和 release模式
  2. Linux 下 gcc/g++ 编译处的二进制程序默认是 release 模式
  3. 要启用 gdb 调试,必须在编译生成可执行程序时加上 -g 选项

三、指令集和使用

1、指令集

/ 代表的就是或者,比如 list / l 就是两者都可以,后一个通常为简写,一般使用简写。

  • gdb file :开始调试
  • list/l 行号:显示源代码,接着上次的位置往下列,每次列10行。list 通常简写为 l 。
  • list 0/1 :从源代码的第一行开始显示代码,每次列 10 行
  • list/l 函数名:列出某个函数的源代码,10行
  • r / run:运行程序,类似于 vs 上的 f5 ,如果有断点就停在第一个断点处,否则直接执行程序
  • n / next:单条执行。也被叫做 逐过程 ,和 vs 上的 f5 一样,遇到函数会直接把函数执行完
  • s /step:进入函数调用。也被叫做 逐语句 ,按住会进入函数,包括库函数
  • break / 行号:在某一行设置断点
  • break 函数名:在某个函数开头设置断点
  • info breakpoints/break/b :查看断点信息,查看的断点以编号形式排布,从 1 开始。
  • finish:执行到当前函数返回,然后停下来等待命令
  • print / p:打印表达式的值,通过表达式可以修改变量的值或者调用函数
  • p 变量:打印变量值,类比于短暂监视窗口
  • set var:修改变量的值。常用语循环,比如跳转到第 n 次循环
  • continue / c:从当前位置开始连续而非单步执行程序。说白了就是从一个断点处,直接运行到下一个断点处,类比于 vs 上的 f5 在断点进行跳转
  • delete breakpoints:删除所有断点
  • delete breakpoints n:删除序号为 n 的断点
  • disable breakpoints:禁用断点,改变断点使能
  • enable breakpoints:启用断点
  • display 变量名:跟踪查看一个变量,每次停下来都显示它的值(监视窗口常显示
  • undisplay 编号:取消对先前设置的那些变量的跟踪,若使用 undisplag 则全部取消
  • until 行号:在函数内进行指定位置跳转,执行完区间代码
  • breaktrace / bt:查看各级函数调用及参数。说白了就是查看调用链
  • info / i locals:查看当前栈帧局部变量的值
  • quit:退出gdb调试

提示:gdb 会记住最近一次的指令,比如上次 l 0 进行翻阅查看,之后一直回车就可以显示出内容。

2、演示

list / l :显示内容

image-20230114191507198

显示函数部分的话,只要求显示片段含有函数名。

list 是查看内容的指令,它不影响实际的调试指令,也就是说 list 显示的内容并不会干扰到当前的调试次序。

断点操作

b 行号 和 b 函数名:打断点

image-20230114200657854

info breakpoints/break/b :查看断点信息

image-20230114200811642

解释:

  • num:断点编号,从 1 开始
  • enb:代表着断点使能。y代表打开;n代表关闭
  • what:说明断点是什么,在哪个函数中,在哪个文件,第几行。

过渡步骤:运行 r 跳转到断点,在断点处停下来

此刻 info b 查看断点信息:

image-20230114201059089

显示信息:breakpoint already hit 1 time 代表断点命中一次

d 断点编号:删除断点(是编号不是行号) d break :删除所有断点

image-20230114201928604

d break 可以删除所有断点,同理 d breakpoints 和 break breakpoints 也都对

删除时会询问是否删除,y 代表 yes ,n 代表 no

disable b num :关闭断点使能 enable b num :打开断点使能

image-20230114202555673

分别显示了断点打开和关闭的过程

info b :可以直接关闭所有的断点使能

这一过程就像 vs 上设置空断点一样

info :查看 info 可以查阅的信息

image-20230114202818218

逐过程和逐语句

next/n :逐过程,一步可以走掉一个函数,相当于 vs 中的 f10

image-20230114203003091

一次走过了函数,函数中打印的内容也会显示出来

还会显示当前行数,以及当前行的内容

s :逐语句,一次走一条代码,可进入函数,同样的库函数也会进入

image-20230114203413197

调用一个函数,就要把函数调用的数据进行压栈

调试位置变成整个代码的第一行

addToTop函数第一行为整个程序的第五行

显示行号和内容

bt :查看调用链

image-20230114203812146

看到一个函数调用的过程,压栈的过程,main函数在栈底,addtotop就像被压栈了,被压到栈顶

查询数据和常显示数据

p 变量名/地址:暂时查询变量

image-20230114204014126

使用完该指令之后,会给变量一个对应的编号

这个数据只是短暂显示,下次执行指令会消失

display 变量名:常显示变量的数据

image-20230114204215523

常显示变量数据,类似于 vs 中的监视窗口,display 显示的数据伴随着调试过程一直存在

且会按照 display 的顺序给上相应的编号,显示为倒序显示

undisplay 编号 :取消变量的显示

image-20230114204507762

undisplay 取消显示后,每次执行不会显示刚刚取消的变量

until 行号:不打断点,在函数内进行指定位置跳转,执行完区间代码

image-20230114204725418

刚刚还在执行循环中的内容,使用 until 直接跳转到 11 行,把循环执行完毕

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

假设当前已经进入行数,想要直接跑完这个函数,函数中没有任何断点,跑完之后就停下来:

image-20230114205115282

这样就直接跑完了函数,并显示了返回结果

再次 n 就是返回主函数,并执行完调用函数的语句,返回下一行

continue / c:从一个断点处,直接运行至下一个断点

image-20230114205519388

跳转到下一个断点处,并把跳转过程中打印的内容显示出来

set var :修改变量的值,很适用与循环跳转到指定循环次

image-20230114210027203

在循环中使用 set var i=100 ,直接跳转到 i=99 时的循环次

四、结语

到这里本篇文章就到此结束了。

其实调试本质上就是查找问题的过程,调试完成再根据查找到的问题,修改程序,随后再进行测试,看问题是否已经解决

而调试一个程序员必备的技能,我们在日常写代码时,就要慢慢培养自己调试和快速定位错误,即排错的能力。

所以专门讲一下调试器的使用还是很有必要的。

但是对于 gdb ,这个调试器其实使用起来还是挺有难度的。一是因为它没有图形化界面;二是因为它指令繁杂。所以使用 gdb 调试只需要掌握基本就好,看完文章也就差不多了。

而我们的日常开发和调试还是在平常用的 I D E IDE IDE 上,所以不必担心。

建议看完文章的小伙伴也可以下去实操一下,只有操作过才知道会不会。

如果觉得 a n d u i n anduin anduin 写的不错的话,可以 点赞 + 收藏 + 评论 支持一下哦!我们下期见~

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

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

相关文章

通信原理与MATLAB(十三):AMI的编解码

目录1.AMI的的编解码原理1.1 AMI编码原理1.2 AMI解码原理2.AMI编解码的代码3.AMI编解码结果图4.AMI的误码率曲线4.1 原理4.2 AMI的误码率曲线代码4.3 误码率曲线图1.AMI的的编解码原理 1.1 AMI编码原理 如下图所示&#xff0c;AMI的编码原理:将原始码元的1转换成1,0转换成-1。…

快过年了,用Python康康哪一家足浴店可以带朋友去玩.....

人生苦短&#xff0c;我用Python 首先肯定是去正经足浴店&#xff0c; 毕竟一年出差也不少&#xff0c; 大家都很辛苦&#xff0c; 好不容易放假了&#xff0c; 约上好兄弟一起去放松放松~ 所需环境 python 3.8 解释器pycharm 编辑器 所需模块 requests 数据来源分析 …

Silane-PEG-NH2 氨基聚乙二醇硅烷 NH2-PEG-Silane结构式

英文名称&#xff1a;Silane-PEG-NH2 Silane-PEG-Amine 中文名称&#xff1a;硅烷-聚乙二醇-氨基 分子量&#xff1a;1k&#xff0c;2k&#xff0c;3.4k&#xff0c;5k&#xff0c;10k&#xff0c;20k。。。 存储条件&#xff1a;-20C&#xff0c;避光&#xff0c;避湿 用 途…

2022年度总结,迎接2023

目录 我和CSDN的2022 初次见面&#xff1a; 你我的成长&#xff1a; 博客&#xff1a; 比赛&#xff1a; 我和CSDN的2023 我和CSDN的2022 初次见面&#xff1a; CSDN你好啊&#xff01;我跟你的初次见面在于2022年4月2日&#xff01;&#xff01;&#xff01; 这这半年内…

【算法5.1】背包问题 - 01背包 (至多最大价值、至少最小价值)

目录 至少模板和至多模板的两大区别 1、至多模板 2、至少模板 2. 01背包 - 至多模板 - 体积至多j&#xff0c;总价值最大 1、朴素做法 - 二维dp 2、优化 - 一维dp 4700. 何以包邮&#xff1f; - 至少模板 - 价值至少j&#xff0c;总价值最小 至少模板和至多模板的两大区…

list容器与vector容器的区别

vector与list都是STL中非常重要的序列式容器&#xff0c;它们都存放在namespace std命名空间中&#xff0c;由于俩个容器的底层结构不同&#xff0c;导致其特性不同 一、底层实现结构不同 vector本质是一段动态连续的顺序表&#xff0c;而list底层是一个双向循环链表 二、访…

Ubuntu多硬盘luks全盘加密自动解锁(硬件变更后失效)的方法

简介大家都知道&#xff0c;Linux现在用Luks全盘加密一直有一个痛点&#xff0c;就是每次开机都需要输入解密硬盘的密码&#xff0c;之后又要输入用户密码&#xff0c;非常的麻烦&#xff01;本文正是为了解决这个问题诞生的&#xff01;本文多硬盘加密带来的效果是&#xff0c…

Redis持久化——RDB机制详解

在运行情况下&#xff0c;Redis 以数据结构的形式将数据维持在内存中&#xff0c;为了让这些数据在 Redis 重启之后仍然可用&#xff0c;需要将数据写入持久存储 持久化是指将数据写入持久存储&#xff0c;例如固态磁盘(SSD) Redis 提供了一系列持久化选项。这些包括&#xff1…

Java-黑马Java学习作业-day07综合练习

学习视频链接&#xff1a;黑马Java学习视频 文章目录练习一&#xff1a;飞机票练习二&#xff1a;打印素数练习三&#xff1a;验证码练习四&#xff1a;复制数组练习五&#xff1a;评委打分练习六&#xff1a;数字加密练习七&#xff1a;数字解密练习八&#xff1a;抽奖解法一:…

【C++11】—— 可变参数模板

目录 一、可变参数模板概念以及定义方式 二、参数包的展开 1. 递归函数方式展开参数包 2. 逗号表达式展开参数包 三、STL容器中的empalce相关接口函数 一、可变参数模板概念以及定义方式 在c11之前&#xff0c;类模板和函数模板只能含有固定数量的模板参数&#xff0c;c11…

JavaScript高级 ES5 面向对象原型继承

原型以及ES5中实现继承1. 对象和函数的原型1. 普通对象的原型 [[prototype]]2. 函数的原型 prototype2. new、constructor1. new 操作符2. constructor属性3. 将方法放到原型上4. 创建对象的内存表现5. 重写原型对象3. 原型链的查找顺序4. 原型链实现的继承5. 借用构造函数继承…

深入URP之Shader篇10: 深度值专题(1)

之前研究Unlit shader的时候就遇到一些Z值相关的问题&#xff0c;一笔带过了&#xff0c;比如ComputeFogFactor中的UNITY_Z_0_FAR_FROM_CLIPSPACE。今天就把URP Shader中出现的Z相关的问题做一个专题一起研究下。 深度缓冲的方向和UNITY_REVERSED_Z 先说这个关于z的宏&#x…

nacos:服务注册与发现

导入SpringCloudAlibaba相关的依赖&#xff0c;并在父工程将依赖进行管理 <dependencyManagement> <dependencies> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-s…

Java EE|多线程代码实例之定时器与线程池

文章目录&#x1f534;定时器什么是定时器以及开发中的作用标准库中的定时器定时器的实现&#x1f534;线程池什么是线程池标准库中的线程池创建一个线程池ThreadPoolExecutor构造方法解析线程池的实现&#x1f534;定时器 什么是定时器以及开发中的作用 程序中的定时器功能与…

【互联网大厂机试真题 - 华为】九宫格

题目描述 九宫格是一款广为流传的游戏,起源于河图洛书。游戏规则是:1到9九个数字放在3x3的格子中,要求每行、每列以及两个对角线上的三数之和都等于15. 在金麻名著《射雕英雄传》中黃蓉曾给九宫格的一种解法,口诀:戴九恩一,左三右七,二四有肩,八六为足,五居中央。解法…

【云原生进阶之容器】第四章Operator原理4.3节--Operator模式

1 Operator概述 1.1 诞生背景 Kubernetes实际是期望状态管理器。先在Kubernetes中指定应用程序期望状态(实例数,磁盘空间,镜像等),然后它会尝试把应用维持在这种状态。Kubernetes的控制平面运行在Master节点上,它包含数个controller以调和应用达到期望状态: 检查当前的…

【阶段三】Python机器学习30篇:机器学习项目实战:智能推荐系统的基本原理与计算相似度的常用方法

本篇的思维导图: 智能推荐系统模型 智能推荐系统属于非监督式学习,是机器学习一个非常重要的应用领域,它能带来的经济价值往往是直接且非常可观的。 智能推荐系统的基本原理 智能推荐系统的应用场景 互联网每天都在产生海量信息,用户行为数据呈现爆发式增长…

PyTorch - 常见神经网络

文章目录LeNetAlexNetDropoutAlexNet 网络结构torchvision中的AlexNet的实现ZFNetVGG-NetsVGG 各网络VGG-16 网络结构GoogLeNet代码实现ResNetDenseNetRNNLSTMGRULeNet 1998年&#xff0c;由 LeCun 提出用于手写数字识别任务只有5层结构&#xff1b;目前看来不输入深度学习网络…

智能售货机系统帝可得

智能售货机 概述项目使用springcloudalibaba中提供的短信服务图形验证码生成多端登录/网关统一鉴权对象存储服务代码的自动填充微服务集成emq&#xff0c;发送emq工单业务流 接收工单 拒绝工单 运维工单补货工单使用xxl-job进行任务调度lkd集成xxl-job自动创建维修工单自动…

设计模式_结构型模式 -《代理模式》

设计模式_结构型模式 -《代理模式》 笔记整理自 黑马程序员Java设计模式详解&#xff0c; 23种Java设计模式&#xff08;图解框架源码分析实战&#xff09; 结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式&#xff0c;前者采用继承…