【Linux】调试器 - gdb 的使用

目录

一、背景知识

二、debug 与 release

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

2、debug 与 release 的区别

三、gdb 的使用

1、调试指令与指令集

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

3、断点操作

4、逐语句与逐过程

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

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


一、背景知识

我们在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 的全部内容,同学们要多上手,边操作边学习。本章的内容就讲到这里,希望同学们多多支持,如果有不对的地方欢迎大佬指正,谢谢!

 

  • 32
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 32
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 32
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

世间是否此山最高

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值