【Linux】调试器 gdb / cgdb

目录

  • 一、什么是gdb?
  • [二、gdb 的使用](#二、gdb 的使用)
    • [2.1 启动 gdb](#2.1 启动 gdb)
    • [2.2 常见命令的使用](#2.2 常见命令的使用)
      • [2.2.1 list / l 命令](#2.2.1 list / l 命令)
      • [2.2.2 run / r 命令](#2.2.2 run / r 命令)
      • [2.2.3 break / b 命令](#2.2.3 break / b 命令)
      • [2.2.4 info 和 delete / d 命令](#2.2.4 info 和 delete / d 命令)
      • [2.2.5 逐过程 next / n 和 逐语句 step / s](#2.2.5 逐过程 next / n 和 逐语句 step / s)
      • [2.2.6 point / p 和 display 和 undisplay](#2.2.6 point / p 和 display 和 undisplay)
      • [2.2.7 until 和 finish 命令](#2.2.7 until 和 finish 命令)
      • [2.2.7 continue / c 命令](#2.2.7 continue / c 命令)
      • [2.2.8 disable 和 undisable](#2.2.8 disable 和 undisable)
      • [2.2.9 backtrace / bt 命令](#2.2.9 backtrace / bt 命令)
  • 三、调试的常见技巧
    • [3.1 watch](#3.1 watch)
    • [3.2 set var](#3.2 set var)
    • [3.3 条件断点](#3.3 条件断点)
      • [3.3.1 添加条件断点](#3.3.1 添加条件断点)
      • [3.3.2 在已有断点基础上修改其为条件断点](#3.3.2 在已有断点基础上修改其为条件断点)
  • 四、调试的本质

个人主页:矢望

个人专栏:C++LinuxC语言数据结构

一、什么是gdb?

它是 Linux 系统下最强大、最核心的命令行调试工具。gdb 和其它命令一样就是一个普通的命令。

gdb 在大多数 Linux 发行版中通常需要手动安装sudo yum install gdb

当执行gdb --version时出现以下内容证明已安装。

当你启动gdb时会进入它的交互界面,输入quit / Ctrl + d就可以退出。

二、gdb 的使用

现在准备了一份从1~100求和的程序code.c。和与之匹配的Makefile

Makefile

2.1 启动 gdb

当我们make编译形成可执行程序code,再进行gdb code时,它会有这样的信息说明:

上面说 code 没有包含调试信息,所以 gdb 无法显示源代码、变量名等信息。这是为什么呢?

程序状态分为两种模式,分别是 DebugReleaseDebug版本包含调试信息,是程序员在开发期间所处的模式,而release版本是给用户提供的,它的体积小,不包含调试信息,运行速度最快。

这和上面有什么联系呢?当然有,上面图片的信息说明我们的可执行程序是release版本,这是因为gcc/g++编译代码,默认模式是release模式

如何证明它是release版本的呢?可执行程序不仅仅是二进制的集合,它的内部是有固定的格式的,这个固定格式叫做ELF

readelf -S code可以读取它内部的格式信息的细节,我们从中过滤一下debug调试信息。

从上图的结果,就可以知道,code内部没有调试信息,它就是release版本的。

那么如何形成debug版本的呢?很简单,在gcc/g++编译时加上-g选项。

从上图中可执行程序文件大小也可以看到,带有调试信息的会更大。以下是调试信息:

所以一般在开发时,我们都会把-g选项带上

2.2 常见命令的使用

2.2.1 list / l 命令

l无参数作用:显示源代码,从上次位置开始,每次列出10

l 文件名:行号作用:显示指定行号附近的代码 。当只有一个文件时,文件名可省略。

l 函数名作用:显示指定函数的源代码

l 开始行,结束行作用:显示指定行号范围的代码

注意:以上执行后按回车都会继续显示后面的代码,直到显示结束

2.2.2 run / r 命令

作用:从程序开始连续执行

2.2.3 break / b 命令

b 行号作用: 在指定行设置断点

如上图,打完断点,此时运行,它就会在断点处停下来。

由上图我们可以发现gdb很不直观,这里推荐使用cgdbCGDB 是一个基于 GDB 的增强版调试器,它提供了分屏界面 - 上方显示源代码,下方是 GDB 命令窗口,用户体验比纯命令行 GDB 好很多,安装命令sudo yum install cgdb

此时再次打断点就可以在上面的源代码区域找到。

注意:上方是源代码窗口,下方是gdb窗口。如何退出:1、按 Esc 键切换到源代码窗口,然后输入 :q 并按回车。2、按 i 键切换到 GDB 命令窗口,然后输入 quit / Ctrl+d

如果你不小心在cgdb交互界面触碰了鼠标的滑轮,导致再按按键cgdb界面没了反应,这是因为可能触碰了cgdb的模式切换,按一下i就重新进入了cgdb命令窗口

2.2.4 info 和 delete / d 命令

info break/b查看当前所有断点的信息
delete/d n删除序号为n的断点

从上图中,我们可以看出当创建断点并删除之后,再次创建断点,断点序号就不再从1开始了,在一次调试过程中它是一个递增的序列

2.2.5 逐过程 next / n 和 逐语句 step / s

next/n:逐过程,执行下一行代码,不进入函数内部,对标F10

step/s:逐语句,执行下一行代码,会进入函数内部,对标F11

注意:当执行完n或者s命令后你还想要执行下一行命令,可以继续输入n或者s,此外gdb会自动保存上一次执行的命令,此时你按回车也可以达到想要的效果

2.2.6 point / p 和 display 和 undisplay

我们在Windows下进行调试代码时,是可以在搜索框查看变量的值的呀,cgdb也一样可以。

p 表达式:打印表达式的值。p 变量:打印指定变量的值。

但我们要是每一步都想查看怎么办,这还要每一步都自己敲吗?

可以使用displaydisplay 变量名:跟踪显示指定变量的值

undisplay 编号:取消对指定编号变量的跟踪显示

2.2.7 until 和 finish 命令

until 行号执行到指定行号 。它解决了这样的痛点:当你在循环内部单步调试时,不想一步步执行几十次甚至几百次循环迭代。

finish执行完当前函数返回然后停止 。假设你的程序运行过程中发生了报错,你想看看是不是这个函数的程序执行引起的,此时你就可以进入到这个函数的内部并finish

如上图,就直接执行结束了finish函数

2.2.7 continue / c 命令

continue/c 的作用是:让暂停的程序继续运行,直到遇到下一个断点、程序结束或发生异常

2.2.8 disable 和 undisable

disable:临时禁用断点(断点保留但不会触发)。
enable:重新启用被禁用的断点。

cpp 复制代码
disable/enable 2-5 //禁用/启用2到5号断点
disable breakpoints //禁用所有断点
enable breakpoints //启用所有被禁用的断点

disable:

enable

2.2.9 backtrace / bt 命令

bt 命令显示程序当前暂停时刻的函数调用链 ,告诉你:当前执行到哪个函数,这个函数是被谁调用的,调用路径是怎样的(从 main 开始的完整轨迹)。

三、调试的常见技巧

3.1 watch

执行时监视一个表达式(如变量)的值。如果监视的表达式在程序运行期间的值发生变化,GDB 会暂停程序的执行,并通知使用者

例如我要在Sum函数内部监视result的值:

如果你认为有一些变量不应该修改,或者你怀疑它修改导致了问题,你可以watch它,如果变化了,就会通知你

3.2 set var

set var 允许你在程序暂停时动态修改变量的值

假设我在进行求和的时候,i初始值不是从1开始的,我们的start初始化时是-10,并且我们还不知道。

如上图,我们在调试过程中发现i的初始值不正确,我们之前的做法是退出cgdb,并且修改源代码,但在某些场景下,我们并不知道这次的修改是不是让程序正确。

现在有了set var我们可以暂时看看修改后的运行结果。我们可以看看i初始为1的结果。

如上图,我们可以确认问题就出在i的初始值上,此时就可以退出cgdb,并修改源代码了

3.3 条件断点

3.3.1 添加条件断点

cpp 复制代码
//在第 9 行的地方新增断点
//并且满足特殊条件i == 50才会被触发
b 9 if i == 50

3.3.2 在已有断点基础上修改其为条件断点

condition 命令是 GDB 中用于为断点添加条件的工具

cpp 复制代码
condition 2 i == 50
// 2 是已有断点的编号
// i == 50,是给已有断点添加的条件

四、调试的本质

调试的本质是找到问题 ,这是gdb的核心价值,至于发现问题分析问题 都是人的工作。gdb给我们提供的主要作用是找到问题,辅助作用是帮助我们分析问题,它提供辅助数据。

GDB 放大了开发者的调试能力,但无法替代开发者的思考过程。 这就是为什么说"工具再好,也要看谁在用"

总结:
以上就是本期博客分享的全部内容啦!如果觉得文章还不错的话可以三连支持一下,你的支持就是我前进最大的动力!
技术的探索永无止境! 道阻且长,行则将至!后续我会给大家带来更多优质博客内容,欢迎关注我的CSDN账号,我们一同成长!
(~ ̄▽ ̄)~

相关推荐
last demo2 小时前
MariaDB 数据库管理
linux·运维·服务器·数据库·php·mariadb
生信大表哥2 小时前
Python单细胞分析-基于leiden算法的降维聚类
linux·python·算法·生信·数信院生信服务器·生信云服务器
码上上班4 小时前
ubuntu 安装ragflow
linux·运维·ubuntu
HIT_Weston4 小时前
38、【Ubuntu】【远程开发】拉出内网 Web 服务:构建静态网页(一)
linux·前端·ubuntu
XH-hui4 小时前
【打靶日记】HackMyVm 之 hunter
linux·网络安全·hackmyvm·hmv
xu_yule5 小时前
Linux_15(多线程)线程安全+线程互斥(加锁)+死锁
linux·运维·服务器
wa的一声哭了6 小时前
并行计算 PCAM方法学
linux·运维·服务器·arm开发·python·spring·django
Dovis(誓平步青云)6 小时前
《拆解 Linux 进程与线程:三个维度讲透二者的区别与联系》
linux·运维·服务器
车传新6 小时前
Linux
linux·运维