目录
一、其他调试代码的工具
1.strace 、lsof工具也可以调试。
二、GDB调试
1、调试准备
在使用gdb调试之前,我们需要在使用gcc编译的时候加上 -g选项, 如果你没有-g选项,运行的时候可以看到gdb提示你不能调试。同时对于多个文件,你都要加上-g
2、开始调试
假设我编译好的文件名为test, 使用gdb ./test 开启调试。我的文件树如下图所示:
3、调试命令
1.运行程序
我们使用(run or r or start)三个命令可以运行程序, r是run命令的缩写,这个命令就像你运行 ./test 可执行文件一样,顺便说一下这个test 与加了 -g选项的test 是不一样的。运行start命令,会使程序停止在 main函数入口(如果遇到No debugging symbols found in ** 问题的时候,需要看一下-g选项是否生效了, 同时如果你移动了文件,然后在编译,但是你之前编译的中间文件没有删除,你直接make,在使用gdb的时候也会出现这个问题,删掉旧的中间文件就行了)。如下图:
我的makefile如下:
all:test
test.o:test.c
gcc -g $^ -c -o $@
sestack.o:sestack.c
gcc -g $^ -c -o $@
test:test.o sestack.o
gcc -g $^ -o $@
2.退出gdb
使用quit or q来退出gdb
3.传参
有时候,我们需要通过命令行去传参数,可以使用set args 参数1 参数2 参数3 ...来给程序传参数。
4.查看代码
你只能查看当前文件的代码, 如果需要查看其他文件的代码,则需切换到其他的文件。使用list or l 来查看当前文件的代码,你可以通过set listsize 20 来修改你每次需要显示的行数为20。如下图:
当前我在test
接下来我们切换到sestack.c,你可以看到我使用了list 、 list 行号、 list 函数名、 list 文件名:行号。
5.设置或删除断点及相关操作
有两种断点,一种是常规断点,一种是条件断点。使用break 行号or b 行号 来设置断点。
或者是使用他们在一个函数处设置断点。
常规断点的使用,如下图所示:
条件断点的使用如下图所示:
在设置完断点后我们可以通过info b 或者 i b来查看断点的信息。
我们可以通过delete or del or d + 断点的编号来删除断点。或者d num1-num4
我们还可以通过disable(dis) 或者 enable(ena) 来设置断点的有效和无效。(观察Enb这一行)
6.继续运行
使用continue or c来继续的向下运行。
7.运行中打印某些值及其类型
使用print or p 来打印我们想打印的值。同时我们还可以设置我们打印时的输出格式。如下表所示:
我们可以使用ptype 来打印某个变量的类型。
8.自动的打印某些值和信息及其相关操作
我们使用display可以来展示我们经常需要打印的值。我们也可以通过info display来展示我们需要自动打印的信息。
我们可以通过undisplay 编号 或者delete display 编号来删除, 当然这里可以展示不打印使用disable 不打印, 使用enable 来打印:
9.单步调试
我们可以用step or s来进行单步调试(s可以进入到函数体的内部),值得注意的时当我们使用单步调试进入一个函数的时候,我们必须使用finish 来跳出函数。
如果通过 s 单步调试进入到函数内部,想要跳出这个函数体, 可以执行 finish 命令。如果想要跳出函数体必须要保证函数体内不能有有效断点,否则无法跳出(待实践)。
我们也可以使用next or n来向下运行,但是next不会进入函数体内。
如果我们想跳出某个循环体 ,我们可以使用until ,但是要满足一下的条件:
- 要跳出的循环体内部不能有有效的断点
- 必须要在循环体的开始 / 结束行执行该命令
10.设置变量的值
有时候我们需要设置某个变量的值,来查看程序的行为。我们可以使用set val 变量名=值来设置某个变量的值。
可以看到我把s指针设置为null后报错了。
11.gdb的图形界面命令
1. 用layout src命令显示源代码窗口。
2.用backtrace命令显示当前调试环境中所有栈帧的信息。栈帧是函数调用时产生的一段内存空间,用于存储函数的参数、局部变量、返回地址等信息。backtrace命令可以帮助你追踪程序执行的路径,找出哪个函数导致了错误或者异常。
3.用layout split显示源代码和汇编代码两个窗口,方便查看每条源代码对应的汇编指令。你可以用方向键或者空格键在不同的窗口中切换。
4.用tui disable退出图形界面。
12.用命令查看指针的信息
p /x *p可以查看指针p的信息(如果指针是t那就是 p /x *t,注意/x是输出格式为16进制,如果是/s ,那表示字符串),只要指针是有效的,即指向了一个已分配的内存地址。(下面的待验证)如果指针是无效的,比如是空指针,或者是野指针,那么p /x *p命令会报错,提示无法访问该地址。你可以用info proc mappings命令来查看进程的内存映射,以及哪些地址是有效的。
你可以通过p /x *p->**,来打印(比如p是个结构体指针)结构体成员信息,注意如果p前面没有*,这是打印地址。(见前面的打印信息小结)。
13.x命令检测内存中的相关内容
使用的格式是 x/nfu address
其中 n是一个正整数,表示需要显示的内存单元的个数,也就是说从当前地址向后显示几个内存单元的内容,一个内存单元的大小由后面的u定义。 f 表示显示的格式 。u 表示从当前地址往后请求的字节数。 addr是一个必须的参数,表示要查看的内存地址。可以是一个寄存器名,一个变量名,一个表达式,或者一个常数(只要它能被gdb解释为一个有效的内存地址)。