File/file 装入想要调试的可执行文件 run(r) 执行当前被调试的程序 kill(k) 终止正在调试的程序 quit(q) 退出gdb shell 使用户不离开gdb就可以执行Linux的shell命令 backtrace(bt) 回溯跟踪(当对代码进行调试时,run后出现错误,则可以使用bt命令查出详细的错误信息 frame n 定位到发生错误的代码段,n为backtrace命令的输出结果中的行号(位于行首)。
(1) 设置断点(break)
break <function> 在进入指定函数时被停住。C++可以使用class::function或function<type,type>格式来指定函数名。 //在该处停止(断点),该处不执行,下同
break <linenum> 在指定行号停住
break filename:linenum 在源文件filename的linenum行处停止
break filename:function 在源文件filename的function函数的入口处停止
break 没有参数,表示在下一条指令处停止
break *address 在程序运行的内存地址处停止
break与step结合使用时,step(s)确认后,执行上一次显示的未执行命令,并且显示出将要执行的下一行程序。使用单步调试命令step来跟踪程序,它一次只执行程序中的一行代码。
info break(i b) 命令 可以显示所有断点的信息
(2) 查看运行时的数据
在调试程序的过程中,需要查看程序中某些表达式或变量的值,以判断程序运行是否正确。
- print命令(p)
在调试程序时,当程序被停住时(如在断点处),可以使用print(p)命令或其同义命令inspect来查看当前程序的运行数据。
print <expr> //输出表达式的值
print /<f> <expr> //按某种格式输出表达式的值,如/x 则为16进制
(gdb) print n1
$1 = 4
(gdb) inspect n1
$2 = 4
(gdb) print n1
$3 = 4
(gdb) print n2
$4 = 5
(gdb) print $2
$5 = 4 //$2的值
(gdb) print $ //$5的值
$6 = 4
(gdb) print $ $ //$5的值 注意之间没有空格 这是防止CSDN编辑器转义而加上的空格
$7 = 4
(gdb) print $ $6 //$4的值
$8 = 5
每一个print都会被gdb记录下来,并且会以$1、$2、$3······这样的方式为每一个print命令编号。于是,可以使用这个编号访问以前的表达式。
另外要注意print命令的表达式中两个具有特殊意义的符号:$、$ $。print $表示显示当前序号的前一个序号的值;$ $表示给定序号的前两个序号,如果未给定序号,则默认当前序号为给定序号。
另外,info local命令可以显示当前本地的所有变量的值。
print命令的功能除了打印表达式或变量的值以外,还有对变量进行赋值和打印内存中某个变量开始的一段区域的内容。
- gdb的数据输出格式
x 十六进制格式 d 十进制格式 u 十六进制格式无符号整型
o 八进制格式 t 二进制格式 a 十六进制格式(等价x)
c ASCII字符格式 f 浮点数格式 s字符串格式 i指令地址(指令文件)
p /x n1 //以十六进制格式显示n1的值
- 自动显示命令display
当程序停住时,或单步跟踪时,这些变量会自动显示。
display <expr> display /<f> <expr>
display /<f> <addr> addr表示内存地址
display /i $pc $pc为gdb的环境变量,表示指令的地址,/i则表示输出格式为机器指令码,也就是汇编。该句指令表示,当程序停下后,就会出现源代码和机器指令码相对应的情形。 即输出当前指令的地址(程序运行到当前处),以机器指令码的格式输出,从而出现源代码和机器指令码(汇编代码)相对应的情形。
- 查看内存examine(x)
examine(简写为x)指令可以查看内存地址中的值,格式:
x /<n/f/u> <addr>
n、f、u为可选的参数,可以独立使用,也可以联合使用。
n为一个正整数,表示显示内存的长度,即从当前地址向后显示几个地址的内容。
f表示显示的格式(即地址所指内容以什么样的格式显示出),s为字符串,如果所指的内容为指令地址,则为i。
u表示从当前地址往后请求的字节数,如果不指定则默认为4个Bytes。u参数可以用下面的字符代替:b表示单字节,h表示双字节,w表示四字节,g表示八字节。
<addr>表示一个内存地址
x /4uh 0x48723 // 从内存地址0x48723读取内容,h表示以双字节为1个单位,4表示4个单位,u表示按16进制显示。
- gdb的环境变量
可以在gdb的调试环境中定义自己的变量,用来保存一些调试程序中的运行数据。set命令用于定义gdb的环境变量,gdb的环境变量与Linux一样,都是以$起始。
set $foo=*object_ptr
第一次使用环境变量时,需要创建这个变量(set),以后使用直接对其赋值即可,环境变量没有类型,可以给环境变量定义任意的类型,包括结构体和数组。
在gdb的调试过程中,show convenience 命令用于查看当前设置的所有环境变量
- 查看寄存器
在调试程序的过程中,有时需要查看某些寄存器中的值。寄存器存放了程序运行时的数据,比如程序当前运行时的指令地址(IP),程序的当前堆栈地址(SP)等。可以使用info命令来查看寄存器中的值。
info registers //查看寄存器的情况(不包括浮点寄存器)
info all-registers //查看所有寄存器的情况(包括浮点寄存器)
info registers<name1,name2,······> //查看指定寄存器的情况(name表示寄存器名)
也可以使用print命令来访问寄存器的情况,只需要在寄存器名字前加一个$就可以了,如:print $ip。
- 查看源程序list(l)
在程序的调试过程中,有时需要查看源程序的内容,以及源代码在内存中的情况。用list命令可以显示程序的源代码。
list linenum 显示程序第linenum行周围的源程序
list filename:linenum 显示某个.c文件中的第linenum行周围的源程序(对于多个源文件的编译)
list function 显示函数名为function的函数的源程序
list filename:function
list 显示当前行后面的源程序
list - 显示当前行前面的源程序
list first,last 显示从first行到last行之间的源代码
list ,last 显示从当前行到last行之间的源代码
可以使用info line命令来查看源代码在内存中的地址,info line命令后面也可以跟行号、函数名、文件名:行号、文件名:函数名等,从而显示指定的源代码在内存中的地址。如要显示zsx.c源文件中calculate( )函数在内存中的地址:
info line zsx.c:calculate
(3) 改变程序的执行
修改变量的值。print命令还可以修改被调试程序中运行时的变量值。如:
print x=9
跳转执行。可以修改程序的执行顺序,让程序执行随意跳跃。
jump <linespec> <linespec>可以是文件的行号,可以是file:linenum格式,表示下一条运行语句从哪里开始。
jump <address> <address>是代码行的内存地址
注意jump命令不会改变当前的程序栈中的内容。
程序运行时,有一个寄存器用于保存当前代码所在的内存地址,所以jump命令也就是改变了这个寄存器中的值。可以使用set $pc来更改跳转执行的地址:set $pc=0x485。
(4) 具体事例
15 for (i = 0; i < len; ++i)
(gdb) b 15 if i==5
Breakpoint 1 at 0x4008d3: file ../src/main.c, line 15.
(gdb) b 27
Breakpoint 2 at 0x400936: file ../src/main.c, line 27.
(gdb) i b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00000000004008d3 in main at ../src/main.c:15
stop only if i==5
2 breakpoint keep y 0x0000000000400936 in main at ../src/main.c:27
//断点编号(id) 断点类型 断点是否可用(y表示可用,n表示不可用) 断点地址 断点的详细信息
(5) 总结(重点内容)
运行程序:start(开始运行,只执行一步就停住);run(开始运行,在断点处停住);continue(c)继续运行,到下一个断点处停止,step(s)单步执行,进入函数内部;next(n)单步执行,不进入函数内部;u跳出循环体,执行循环体后面的第一个语句。
set var i=10 //将变量i的值设为10,比如在一个循环体中,i为控制变量,当单步执行时,i依次增加,如果想让变量i循环到10时,才停住,则可以:set var i = 10。
gdb调试:
1. 启动gdb
start -- 只执行一步
n -- next
s -- step(单步) -- 可以进入到函数体内部
c - continue -- 直接停在断点的位置
2. 查看代码:
l -- list
l 10(或者函数名)
l filename:行号(或者函数名)
3. 设置断点:
设置当前文件断点:
b -- break
b 10(或函数名)
设置指定文件断点:
b fileName:行号(或函数名)
设置条件断点:
b 10 if value==19
删除断点:
d 断点编号
获取编号:i b
4. 查看设置的断点
info break i b
5. 开始 执行gdb调试
执行一步操作: start
继续执行: n s
执行多步, 直接停在断点处: continue
5. 单步调试
进入函数体内部: s
从函数体内部跳出: finish(如果在循环处有断点, 需要将断点删掉)
不进入函数体内部:n
退出当前循环: u //该退出是指,直接一次性执行完该循环体
6. 查看变量的值: p -- print
7. 查看变量的类型: ptype 变量名
8. 设置变量的值: set var 变量名 = 赋值 //注意,同理,不是硬性的
9. 设置追踪变量
display
取消追踪变量
undisplay 编号
获取编号: info display
10. 退出gdb调试
quit