【Linux系统】—— 调试器 gdb/cgdb的使用

【Linux系统】—— 调试器 gdb/cgdb的使用

  • 1 前置准备
  • 2 快速认识 gdb
  • 3 cgdb/gdb 的使用
    • 3.1 简单认识 cgdb
    • 3.2 打断点 / 删断点
    • 3.3 逐过程 / 逐语句
    • 3.4 查看变量
    • 3.5 快速跳转
  • 4 cgdb/gdb 调试技巧
    • 4.1 watch
    • 4.2 「set var」确定问题原因
    • 4.3 条件断点
  • 5 概念理解
  • 6 gdb/cgdb 指令一览

1 前置准备

  程序的发布方式有两种, debug 模式release 模式, Linux gcc/g++ 出来的⼆进制程序,默认是 release 模式
  程序要调试,必须是debug模式
  要使用 gdb 调试,必须在使用 gcc/g++ 源代码⽣成⼆进制程序的时候,加上 「-g」 选项,如果没有添加,程序无法被编译

  • 「-g」选项:让最后形成的可执行程序,添加调试信息 —— d e b u g debug debug 模式
$ gcc mycmd.c -o mycmd # 默认模式,不⽀持调试
$ file mycmd
mycmd: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically
linked, interpreter /lib64/ld-linux-px86-64.so.2,
BuildID[sha1]=82f5cbaada10a9987d9f325384861a88d278b160, for GNU/Linux
3.2.0, not stripped$ gcc mycmd.c -o mycmd -g # debug模式
$ file mycmd
mycmd: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically
linked, interpreter /lib64/ld-linux-x86-64.so.2,
BuildID[sha1]=3d5a2317809ef86c7827e9199cfefa622e3c187f, for GNU/Linux
3.2.0, with debug_info, not stripped

  
样例代码:


#include <stdio.h>int Sum(int s, int e)
{int result = 0;for(int i = s; i <= e; i++){   result += i;}   return result;
} int main()
{int start = 1;int end = 100;printf("I will begin\n");int n = Sum(start, end);printf("running done, result is: [%d-%d]=%d\n", start, end, n); return 0;
}

  
  

2 快速认识 gdb

g d b gdb gdb下载:

  • sudo apt install -y gdb / sudo yum install -y gdb

  

进入调试:

  • gdb 可执行文件

  

退出调试:

  • q(quit)

  
在这里插入图片描述

  

常见选项:

  • 「-q」:安静模式,不打印介绍信息和版本信息

  在这里插入图片描述


  

查看源码:

  • l(list): 查看源程序代码,默认显示 10 行,按回车键继续看余下的。 后面可以跟文件名表明要看的指定文件,默认当前文件
  • l(list) 【指定文件】

  
在这里插入图片描述
l(list) 后面加行号,查看指定行号的前后十行

  
  

3 cgdb/gdb 的使用

3.1 简单认识 cgdb

  看了 g d b gdb gdb 的基础操作,许多小伙伴都会认为 g d b gdb gdb 难用。也难怪,我调试的时候连看个源代码都那么麻烦,我还怎么调试。
  为了解决 g d b gdb gdb 难用的问题,可以使用 cgdb 进行调试

   c g d b cgdb cgdb g d b gdb gdb 的命令是一模一样的,但 c g d b cgdb cgdb 可以给我们动态呈现我们的代码

cgdb下载指令:

  • Ubuntu: sudo apt-get install -y cgdb
  • Centos: sudo yum install -y cgdb

  

cgdb 界面:
  在这里插入图片描述

  

3.2 打断点 / 删断点

开始调试(相当于VS:F5):

  • r(run):程序开始运行,遇到断点停止否则运行至结束
      在这里插入图片描述
      
    程序运行中可以再次按:「r」,从新开始调试( gdb 会出现提示,按 y 即可)

  

设置断点(相当于VS:F9):

  • b(break):在某行设置断点 使用指令: b [行号] /  b [指定文件:] [行号]  /  b [指定文件:][指定函数名]
      在这里插入图片描述
      
    :给函数名打断点实际上是给函数的第一句指令打断点

  

查看断点:

  • 使用指令:info b

  在这里插入图片描述
显示断点信息对应的含义:

  • Num:断点编号
  • Disp:断点执行一次之后是否有效 keep:有效 dis:无效
  • Enb: 当前断点是否有效 y:有效 n:无效
  • Address:断点内存地址
  • What:位置

  

删除断点:

  • 使用指令:d(delete) 断点编号

  
:删除断点只能根据断点编号删除,不能根据行号删除
  在这里插入图片描述
  
  2 号断点被删除后,3 号断点的编号并没有变成 2 号,新增的断点也是从 4 号开始。所以断点编号不会被修改,依次线性递增,除非退出再进入 gdb / cgdb。

  

禁用断点:
  断点是可以被使能的,即用的时候打开,不用的使用关闭(不是删除)
  为什么要禁用断点呢?我既然不用了,直接删掉它不就行了吗。每个断点打的位置都是有讲究,可能这次调试你不需要用这断点,便把断点删掉了,但下次再调试就不知道之前这个断点打在哪里了。
  正确的做法是将断点使能(使之能或使之不能),这样的断点的痕迹还在,就能找到历史的调试痕迹

  
使用指令:

  • disable 断点编号

  在这里插入图片描述
:只有打断点是行号,其他对断点的操作都是断点编号

  

打开断点:

  • 使用指令:enable 断点编号
    在这里插入图片描述

  
  

3.3 逐过程 / 逐语句

逐过程(相当于VS:F10)

  • 使用指令:n(next)

  
逐语句(相当于VS:F11)

  • 使用指令:s(step)

  
:gdb / cgdb 会自动记录最新的指令,按 “回车” 可以自动执行最新的指令

  

3.4 查看变量

临时查看变量值

  • 使用指令:p 变量名

  在这里插入图片描述

  

常久显示变量值(相当于VS的监视窗口)

  • 使用指令:display 变量名

  在这里插入图片描述

  

删除显示的变量名

  • 使用指令:undisplay 常显示的编号

  在这里插入图片描述

  

查看当前函数定义的所以临时变量

  • 使用指令:info locals

  在这里插入图片描述

  

调用栈帧:

  • 使用指令:bt
    在这里插入图片描述
Sum的函数栈帧
  

  递归类的程序我们就可以用 bt 来看其调用栈

  

3.5 快速跳转

运行至下一个断点处(相当于VS:F5)

  • 使用指令:c(continue)
    在这里插入图片描述

  

直接执行到当前函数返回处,然后停止

  • 使用指令:finish

  在这里插入图片描述

  

跳出当前循环

  • 使用指令:until 行号

  在这里插入图片描述
  
  until 可以用来跳转至指定行,常用来跳出当前确定没出错的循环。严格意义上不是跳转,而是将循环当前循环跑完了

  
  

4 cgdb/gdb 调试技巧

4.1 watch

  「watch」可以用来监视变量或者某个表达式的变化,如果监视的表达式在程序运行期间的值发⽣变化,gdb会通知使用者

在这里插入图片描述

  删除「watch」和删除断点一样,使用「d」指令

  「watch」有什么用呢?
  最主要的用途是:如果你有⼀些变量不应该修改,但是你怀疑它修改导致了问题,你可以「watch」它,如果变化了,就会通知你

  

4.2 「set var」确定问题原因

  我们故意写个错误代码:我们设置一个标志位 flag,为 1 得到正数 result;-1 得到负的 result,但现在粗心的将 flag 的值设为 0

在这里插入图片描述

  
  进入 gdb 利用「set var」调试

在这里插入图片描述

  所以我们可以用「set var」确定并更改我们出错的原因

  

4.3 条件断点

int Sum(int s, int e)
{int result = 0;for(int i = s; i <= e; i++){   result += i;}   return result * flag;
} 

  
  上述函数,如果我们想直接看 i = 10 时的各变量值怎么办,即跳过部分循环。
  在VS调试中,我们往往是,添加语句 if(i == 10),并在该语句打断点

int Sum(int s, int e)
{int result = 0;for(int i = s; i <= e; i++){   if(i == 10)printf("hehe"); //添加语句,并将断点打在该语句上result += i;}   return result * flag;
} 

  

在 gdb 的调试中,我们可以用条件判断来做到

  • 使用指令:b 行号 if 条件

  
例如:result += i;语句在第12行

  在这里插入图片描述
在这里插入图片描述
  
  删除条件断点的方法同样是用命令:「d」

  

给已经存在的断点设置条件

  • 使用指令:condition 断点编号 条件(注:没有if)

  在这里插入图片描述

  
  

5 概念理解

  调试的本质是什么?
  调试的本质是帮我们解决问题吗?我们仔细想一想,好像 VS 的调试器、gdb/cgdb 等工具并没有帮我们解决,它们仅仅是给程序员追踪内存、查看变量等,真正去解决问题的还是我们自己
  所以调试的第一件事情就是先找到问题。调试器就是帮我们找到问题的,找到问题后,我们要查看问题代码的上下文做排除,最后才能解决问题。解决问题并不是调试器帮我们解决问题,是我们自己解决问题。

  所以,我们用调试器调试时,最核心的就是用调试器找到问题

gdb 有很多命令是帮我们找到问题的,最核心的就是:

  • c:通过断点进行分块,以块为单位快速定位区域
  • finish:确认问题是否在当前函数内
  • until:局部区域快速执行

  
  如何快速找到问题所在呢?
  比如现在有 100 行代码,我们可以分别在第 25 行、50 行、75 行打上断点,这样我们就利用断点完成了代码的分块。让程序直接运行到各个断点处,就能知道是哪一块代码出现问题了。
  在工程项目中,往往几十上百万行代码,又如何利用断点快速找到问题的根源呢?
  我们可以现在项目的中间处打个断点,看问题出现那个部分;再在出问题部分的中间打个断点……以此类推,用二分查找的方式快速找到问题所在

  
  断点的本质是什么?
  断点的本质是对代码进行切块,以块为单位快速定位问题区

  
  

6 gdb/cgdb 指令一览

命令作用样例
list/l显示源代码,从上次位置开始,每次列出10行list / l 10
list/l 函数名列出指定函数的源代码list / l main
list / l 文件名:行号列出制定文件的源代码list / l mycode.c:1
r / run从程序开始连续执行run / r
n / next单步执行,不进入函数内部n / next
s / step单步执行,进入函数内部s/ step
break / b [文件名:]行号在指定行号设置断点break 10 / break test.c : 10
break / b 函数名在函数开头设置断点break main
info break / b查看当前所有断点的信息info break / b
finish执行到当前函数返回,然后停止finish
print / p 表达式打印表达式的值print start+end
p 变量打印制定变量的值p x
set var 变量 = 值修改变量的值set var i = 10
continue / c从当前位置开始连续执行程序continue
delete / d breakpoints删除所有断点delete breakpoints
delete / d n删除序号为 n 的断点delete / d 1
disable breakpoints禁用所有断点disable breakpoints
enable breakpoints启用所有断点enable breakpoints
info / i breakpoints查看当前设置的断点列表indo breakpoints
display 变量名跟踪显示制定变量的值(每次停止时)display x
undisplay 编号取消对指定变量的跟踪显示undisplay 1
until 行号执行到指定行号until 20
backtrace / bt查看当前执行栈的各级函数调用及参数backtrace / bt
info/i locals查看当前栈帧局部变量值info locals
quit退出gdb调试器quit

  
  
  
  


  好啦,本期关于调试器 gdb/cgdb的使用就介绍到这里啦,希望本期博客能对你有所帮助。同时,如果有错误的地方请多多指正,让我们在 Linux 的学习路上一起进步!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

llama.cpp部署 DeepSeek-R1 模型

一、llama.cpp 介绍 使用纯 C/C推理 Meta 的LLaMA模型&#xff08;及其他模型&#xff09;。主要目标llama.cpp是在各种硬件&#xff08;本地和云端&#xff09;上以最少的设置和最先进的性能实现 LLM 推理。纯 C/C 实现&#xff0c;无任何依赖项Apple 芯片是一流的——通过 A…

【Vue3】Vue 3 中列表排序的优化技巧

本文将深入探讨 Vue 3 中列表排序的优化技巧&#xff0c;帮助提升应用的性能和响应速度。 1. 避免不必要的排序 按需排序 在实际应用中&#xff0c;并非每次数据更新都需要进行排序。例如&#xff0c;当列表数据仅在特定条件下需要排序时&#xff0c;可通过条件判断来避免不…

HaProxy源码安装(Rocky8)

haproxy具有高性能、高可用性、灵活的负载均衡策略和强大的将恐和日志功能&#xff0c;是法国开发者 威利塔罗(Willy Tarreau)在2000年使用C语言开发的一个开源软件&#xff0c;是一款具 备高并发(一万以上)、高性能的TCP和HTTP负载均衡器&#xff0c;支持基于cookie的持久性&a…

调用openssl实现加解密算法

由于工作中涉及到加解密&#xff0c;包括Hash&#xff08;SHA256&#xff09;算法、HMAC_SHA256 算法、ECDH算法、ECC签名算法、AES/CBC 128算法一共涉及5类算法&#xff0c;笔者通过查询发现openssl库以上算法都支持&#xff0c;索性借助openssl库实现上述5类算法。笔者用的op…

1-13 tortoiseGit忽略文件与文件夹

前言&#xff1a; 基于本人对小乌龟操作的学习和思考&#xff0c;仅供参考 1-1 忽略问价和文件夹 有时候我们的一些文件是不想要提交&#xff0c;那么我们可以使用stash的方式给这个文件添加忽略&#xff0c;那么我们现在来给这个实际操作创建一个操作的环境。 右键选中添加到忽…

✨2.快速了解HTML5的标签类型

✨✨HTML5 的标签类型丰富多样&#xff0c;每种类型都有其独特的功能和用途&#xff0c;以下是一些常见的 HTML5 标签类型介绍&#xff1a; &#x1f98b;结构标签 &#x1faad;<html>&#xff1a;它是 HTML 文档的根标签&#xff0c;所有其他标签都包含在这个标签内&am…

PostgreSQL 的崛起与无服务器数据库的新时代

根据 2023 年 Stack Overflow 开发人员调查 &#xff0c;PostgreSQL 超越 MySQL 成为最受开发人员推崇和期望的数据库系统&#xff0c;这是一个重要的里程碑。这一转变反映了开发人员社区对 PostgreSQL 强大的功能集、可靠性和可扩展性的日益认同。 这种不断变化的格局激发了数…

Redis(高阶篇)03章——缓存双写一致性之更新策略探讨

一、反馈回来的面试题 一图你只要用缓存&#xff0c;就可能会涉及到redis缓存与数据库双存储双写&#xff0c;你只要是双写&#xff0c;就一定会有数据一致性的问题&#xff0c;那么你如何解决一致性的问题双写一致性&#xff0c;你先动缓存redis还是数据库mysql哪一个&#x…

【机器学习监督学习】:从原理到实践,探索算法奥秘,揭示数据标注、模型训练与预测的全过程,助力人工智能技术应用与发展

&#x1f31f;个人主页&#xff1a;落叶 &#x1f31f;当前专栏:机器学习专栏 目录 线性回归&#xff08;Linear Regression&#xff09; 基本概念 数学模型 优缺点 利用Python实现线性回归 逻辑回归&#xff08;Logistic Regression&#xff09; 3.2. 数学模型 优缺点 …

数据结构_前言

本次我们将进入一个新的阶段啦~ 要注意哦&#xff1a; 在学数据结构之前&#xff0c;我们要先掌握c语言中所学的指针、结构体、内存的存储这几部分&#xff0c;如果还没太掌握的话&#xff0c;那记得去复习回顾一下噢。 下面我们就一起进入数据结构的学习吧&#xff01; 知识…

VirtualBox 中使用 桥接网卡 并设置 MAC 地址

在 VirtualBox 中使用 桥接网卡 并设置 MAC 地址&#xff0c;可以按照以下步骤操作&#xff1a; 步骤 1&#xff1a;设置桥接网卡 打开 VirtualBox&#xff0c;选择你的虚拟机&#xff0c;点击 “设置” (Settings)。进入 “网络” (Network) 选项卡。在 “适配器 1” (Adapt…

【Mysql】索引

【Mysql】索引 一、索引的简介二、索引结构2.1 Hash2.2 二叉搜索树2.3 B树2.4 B树 三、索引分类3.1 主键索引3.2 普通索引3.3 唯一索引3.4 全文索引3.5 聚集索引3.6 非聚集索引3.7 索引覆盖 四、使用索引4.1 自动创建索引4.2 手动创建索引4.2.1 主键索引4.2.2 唯一索引4.2.3 普…

超全Deepseek资料包,deepseek下载安装部署提示词及本地部署指南介绍

该资料包涵盖了DeepSeek模型的下载、安装、部署以及本地运行的详细指南&#xff0c;适合希望在本地环境中高效运行DeepSeek模型的用户。资料包不仅包括基础的安装步骤&#xff0c;还提供了68G多套独立部署视频教程教程&#xff0c;针对不同硬件配置的模型选择建议&#xff0c;以…

1、Window Android 13模拟器 将编译的映像文件导入Android Studio

1、环境准备 编译环境&#xff1a;Ubuntu-18.04.5编译版本&#xff1a;android13-release下载地址&#xff1a;清华大学开源软件镜像站AOSP # 下载repo # 同步代码&#xff1a;repo init -u https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest -b android13-r…

UE5 Niagara 粒子远处闪烁解决

在UE5.2中使用Niagara粒子系统制作水特效时&#xff0c;远处出现粒子闪烁的问题&#xff0c;通常由渲染精度、深度冲突或LOD设置引起 .效果如下&#xff1a; 处理深度缓冲冲突&#xff08;Z-Fighting&#xff09; 问题原因&#xff1a;粒子与场景几何体深度值重叠导致闪烁。 …

机器学习入门实战 4 - 基本模型

&#x1f4cc; 机器学习基本模型项目实战&#xff1a;预测泰坦尼克号乘客的生存概率 &#x1f6a2; 项目背景 1912 年 4 月 15 日&#xff0c;泰坦尼克号在处女航中撞上冰山沉没&#xff0c;船上 2224 名乘客和船员中&#xff0c;仅有约 710 人生还。 哪些因素决定了生还几率&…

电子制造企业数字化转型实战:基于Odoo构建MES平台的深度解决方案

作者背景 拥有8年乙方项目经理经验、8年甲方信息化管理经验&#xff0c;主导过12个Odoo制造业项目落地&#xff0c;服务客户涵盖消费电子、汽车电子、工业设备等领域。本文基于华东某电子企业&#xff08;以下简称"A公司"&#xff09;的实战案例&#xff0c;解析行业…

Python - 爬虫利器 - BeautifulSoup4常用 API

文章目录 前言BeautifulSoup4 简介主要特点&#xff1a;安装方式: 常用 API1. 创建 BeautifulSoup 对象2. 查找标签find(): 返回匹配的第一个元素find_all(): 返回所有匹配的元素列表select_one() & select(): CSS 选择器 3. 访问标签内容text 属性: 获取标签内纯文本get_t…

排序与算法:选择排序

执行效果 选择排序的执行效果是这样的&#xff1a; 呃……看不懂吗&#xff1f;没关系&#xff0c;接着往下看介绍 算法介绍 选择排序&#xff08;Selection sort&#xff09;是一种简单直观的排序算法。选择排序的主要优点与数据移动有关。如果某个元素位于正确的最终位置…

Day4:强化学习之Qlearning走迷宫

一、迷宫游戏 1.环境已知 迷宫环境是定义好的&#xff0c;障碍物位置和空位置是已知的&#xff1b; # 定义迷宫 grid [[0, 0, 0, 1, 0],[0, 1, 0, 1, 0],[0, 1, 0, 0, 0],[0, 0, 0, 1, 0],[0, 1, 1, 1, 0] ] 2.奖励方式已知 如果碰到障碍物则得-1&#xff0c;如果到终点则…