0


【Linux】调试器 - gdb 的使用

一、背景知识

我们在Linux上编写代码后,也希望能够同 vs 上一样可以进行代码调试,这时我们就需要一个调试工具,名为 gdb 。

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

二、debug 与 release

1、生成两种版本的可执行程序

我们首先编写一个简单的累加程序:

补充知识:因为老版的 c89 或 c90 标准的C语言语法不支持在 for 语句的括号内定义变量,因此我们需要在 Makefile 中的依赖方法里增加一个选项: -std=c99 。

程序编写完成后,我们使用 gcc/g++ 编译出的可执行程序默认是 release 版本的。为了方便区分,我们先把第一次编译出的可执行程序重命名为 mytest-release 。


让 gcc/g++ 以 debug 方式编译程序:

gcc -g 

我们修改一下 Makefile:

此时,我们再次编译出的可执行程序就是 debug 版本的了。为了方便区分,我们把第二次编译出的可执行程序重命名为 mytest-debug 。

debug版本和release版本的可执行程序都是能够正常执行的。

2、debug 与 release 的区别

为什么 debug 版本的程序可以调试,而 release 版本的不可以呢?

这是因为以 release 版本发布的软件是给客户使用的,而客户不需要调试信息。如果在程序里增加了大量的调试信息,不仅该程序的体积会变大,而且运行速度也会变慢,给客户的使用体验不好。

而 debug 版本是给程序员使用的,程序员需要通过调试信息来调试程序。所以在程序里增加调试信息是非常有必要的。


读取可执行程序的二进制构成的指令:

readelf -S [可执行程序]

搜索调试信息的指令:

readelf -S [可执行程序] | grep debug

查看 mytest-debug 的调试信息:

查看 mytest-release 的调试信息:

可以非常直观的看到 debug 版本的可执行程序里包含调试信息,而 release 版本的可执行程序里没有包含。

三、gdb 的使用

1、调试指令与指令集

gdb [debug版本可执行程序]

当出现如上字样时,gdb调试的准备工作就已经完成。

指令集:

  • list / l 行号:显示可执行程序源代码,接着上次的位置往下列,每次列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

gdb会记住最近一次执行的指令。比如当我们使用指令 l 来查阅下面 10 行代码后,只需要再按 enter 键就可以继续执行 l 指令的功能。


2、源代码显示、运行与退出调试

list / l 行号:显示源代码


r / run:运行程序

因为没有设置断点,所以程序正常执行正常退出。

quit :退出调试


3、断点操作

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

break(b) 函数名:在某个函数开头设置断点

info b:查看断点

在屏幕上显示断点信息,从左到右依次为:

  • Num:断点编号。从 1 开始,依次递增
  • Enb:断点使能。y 为打开, n 为关闭
  • what:说明这个断点的位置等信息

这时,我们再输入命令 r 运行程序,程序就会在断点处停下:

同时显示断点已经被命中了一次。

disable breakpoint [断点编号]:关闭断点使能

enable breakpoint [断点编号]:打开断点使能

关闭断点使能:在不删除断点的条件下,使断点不生效。

d [断点编号] :删除指定断点

编号为 1 的断点被删除。

d break :删除所有断点


4、逐语句与逐过程

n / next :逐过程。类比到 vs 中的 F10 ,一步可以走过一个函数。

直接执行完 addToTop 函数,并且该函数中的内容也被打印出来。执行完毕后,显示当前行号及当前行内容。

s / step :逐语句。类比到 vs 中的 F11,逐条语句调试,遇到函数会跳转到函数内。

跳转到 addToTop 函数内的第一行语句,显示当前行号及当前行内容。

因为在一个程序中可能会出现 A 函数调用 B 函数,B 函数调用 C 函数的情况。所以我们有时希望看到当前程序中函数的调用链。

bt :查看当前的调用链

因为函数调用是一个压栈的过程,所以我们可以看到 addToTop 函数被压栈到了 main 函数之上。


5、调试过程中的数据监视

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

可以查看当前时刻变量的内容及地址等信息。在显示时,会给 被显示变量 一个编号,编号以 $ 开头,从 1 开始递增。 p 命令每使用一次,就打印一次变量信息。

display [变量名/地址] :常显示变量(内置类型, 结构体等自定义类型,stl)

只需要设置一次,接下来每次操作,都会显示 已设置变量 的当前时刻的信息。

undisplay [编号] :取消常显示

被取消常显示的变量,在接下来执行程序时不再被显示。


6、调试过程中快速定位问题

until [行号] :在函数内,进行指定行位置跳转,执行完区间代码

直接执行完了程序 12 行之前的所有代码,并且停留在第 12 行。

finish :进入一个函数,只执行完该函数就停下来

当已经进入一个函数后,该函数中没有任何断点。就跑完当前函数停下来等待命令。

执行完函数后,返回结果。

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

直接运行至下一个断点处,并把运行过程打印了出来。


set var n=XXX:设置某一个变量为特定的值

直接把 i 的值设为了 90。


以上就是Linux调试器 gdb 的全部内容,同学们要多上手,边操作边学习。本章的内容就讲到这里,希望同学们多多支持,如果有不对的地方欢迎大佬指正,谢谢!

标签: linux 运维 服务器

本文转载自: https://blog.csdn.net/weixin_74078718/article/details/128764014
版权归原作者 世间是否此山最高 所有, 如有侵权,请联系我们删除。

“【Linux】调试器 - gdb 的使用”的评论:

还没有评论