一、背景知识
我们在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 的全部内容,同学们要多上手,边操作边学习。本章的内容就讲到这里,希望同学们多多支持,如果有不对的地方欢迎大佬指正,谢谢!
版权归原作者 世间是否此山最高 所有, 如有侵权,请联系我们删除。