GDB 调试 从入门到大概会用
简介
GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具。或许,各位比较喜欢那种图形界面方式的,像VC、BCB等IDE的调试,但如果你是在UNIX平台下做软件,你会发现GDB这个调试工具有比VC、BCB的图形化调试器更强大的功能。所谓“寸有所长,尺有所短”就是这个道理。
一般来说,GDB主要帮忙你完成下面四个方面的功能:
1、启动你的程序,可以按照你的自定义的要求随心所欲的运行程序。
2、可让被调试的程序在你所指定的调置的断点处停住。(断点可以是条件表达式)
3、当程序被停住时,可以检查此时你的程序中所发生的事。
4、动态的改变你程序的执行环境。
从上面看来,GDB和一般的调试工具没有什么两样,基本上也是完成这些功能,不过在细节上,你会发现GDB这个调试工具的强大,大家可能比较习惯了图形化的调试工具,但有时候,命令行的调试工具却有着图形化工具所不能完成的功能。让我们一一看来。
命令
GDB控制
r 开始运行程序直到断点.
continue : 继续运行程序直到下一个断点(类似于VS里的F5)
next : 逐过程步进,不会进入子函数(类似VS里的F10)
setp : 逐语句步进,会进入子函数(类似VS里的F11)
until : 运行至当前语句块结束
finish : 运行至函数结束并跳出,并打印函数的返回值(类似VS的Shift+F11)
return : 强制从当前函数返回
quit(q) 退出
kill [filename]终止正在调试的程序
load [filename] : 动态载入一个可执行文件到调试器
make : 使你能不退出 gdb 就可以重新产生可执行文件
shell [...]: 使你能不离开 gdb 就执行 UNIX shell 命令
list 显示当前位置的上下文
list [linenum]/[filename:linenum]
变量显示
print(p): 打印变量或表达式结果
print [variable]
print [function]
whatis/ptype [variable]: 显示变量的值和类型
断点控制
catch让程序在发生某种事件(fork、异常throw、异常catch、动态库加载等)的时候停止运行
eg: catch fork
break 在进入指定函数时停住,C++中可以使用class::function或function(type,type)格式来指定函数名
break [linenum] : 在指定行号停住
break [filename:linenum]: 在指定文件指定行号停下
break [function] : 在指定函数停下
break [filename:linenum]: 在指定文件的指定函数停下
break *[address] : 在程序运行的内存地址处停下
break if [condition] : condition表示条件,在条件成立时停住。比如在循环体中,可以设置break if i=100,表示当i为100时停住程序
delete [breakpoints] [range…]: 删除指定的断点,breakpoints为断点号。如果不指定断点号,则表示删除所有的断点。range 表示断点号的范围(如:3-7)。其简写命令为d,比删除更好的一种方法是disable停止点。disable了的停止点,gdb不会删除,当还需要时,enable即可,就好像回收站一样
clear [line/function]: 删除一个断点,这个命令需要指定代码行或者函数名作为参数,若不指定则删除所有的
disable [n] : 禁止断点功能,这个命令需要禁止的断点在断点列表索引值作为参数
enable [n]
xbreak 在当前函数的退出的点上设置一个断点
condition [n] [expression] : 在断点n满足expression时停止
ignore [n] [count] : 忽略某个断点制定的次数。例:ignore 4 23 忽略断点4的23次运行,在第24次的时候中断
info break[n] : 断点号为n的断点
info b : 所有断点
变量赋值与监测
watch [variable] : 使你能监视一个变量的值而不管它何时被改变
rwatch [variable] : 指定一个变量,如果这个变量被读,则暂停程序运行,在调试器中显示信息,并等待下一个调试命令。参考rwatch和watch命令
awatch [variable] : 指定一个变量,如果这个变量被读或者被写,则暂停程序运行,在调试器中显示信息,并等待下一个调试命令。参考rwatch和watch命令
set [variable] : 设置变量的值。例如:set nval=54 将把54保存到nval变量中
内存检测
x/[n/f/u] [addr]
使用examine命令(简写是x)来查看内存地址中的值。x命令的语法如下:
x/[n/f/u] [addr]
n、f、u是可选的参数。
n是一个正整数,表示显示内存的长度,也就是说从当前地址向后显示几个地址的内容。
f 表示显示的格式,参见上面。如果地址所指的是字符串,那么格式可以是s,如果 地址是指令地址,那么格式可以是i。
u 表示从当前地址往后请求的字节数,如果不指定的话,GDB默认是4个bytes。u参数可以用下面的字符来代替,b表示单字节,h表示双字节,w表示四字 节,g表示八字节。当我们指定了字节长度后,GDB会从指内存定的内存地址开始,读写指定字节,并把其当作一个值取出来。
n/f/u三个参数可以一起使用。例如:
命令:x/3uh 0x54320 表示,从内存地址0x54320读取内容,h表示以双字节为一个单位,3表示三个单位,u表示按十六进制显示。
输出格式
一般来说,GDB会根据变量的类型输出变量的值。但你也可以自定义GDB的输出的格式。例如,你想输出一个整数的十六进制,或是二进制来查看这个整型变量的中的位的情况。要做到这样,你可以使用GDB的数据显示格式:
上面的u可以替换成:
d 按十进制格式显示变量
x 按十六进制格式显示变量
a 按十六进制格式显示变量
u 按十进制格式显示无符号整型
o 按八进制格式显示变量
t 按二进制格式显示变量
c 按字符格式显示变量
f 按浮点数格式显示变量
s 按字符串格式显示变量
i 按指令地址显示变量(汇编)
h可以替换成:
b表示单字节,h表示双字节,w表示四字 节,g表示八字节
寄存器与汇编
info registers [寄存器] : 查看寄存器值,后面不跟寄存器的话就是查看所有寄存器
disassemble \m : 查看当前的汇编代码
stepi : 汇编代码的step
nexti : 汇编代码的next
set $[寄存器]=[value] :设置寄存器值
x/[n/f/u] $[寄存器] :把寄存器中的值作为地址,查看地址上的值
窗口
tui : GUI显示
layout:用于分割窗口,可以一边查看代码,一边测试:
layout src:显示源代码窗口
layout asm:显示反汇编窗口
layout regs:显示源代码/反汇编和CPU寄存器窗口
layout split:显示源代码和反汇编窗口
Ctrl + L:刷新窗口
专题
栈帧溢出
bt(Backtrace) : 查看堆栈情况 ,可以用来调试栈帧溢出的情况
frame n|f n :查看其调用函数的信息,n为栈的层次,然后可以用其他命令(info)查看此级别的变量信息
多线程多进程
1、 设置follow-fork-mode (fork追踪模式)以及detach-on-fork(指示GDB在fork之后是否断开某个进程的调试)即可。
这两个参数的设置命令分别是:set follow-fork-mode [parent|child],set detach-on-fork [on|off]。两者结合起来构成了GDB的调试模式:
follow-fork-mode detach-on-fork 说明
parent on GDB默认的调试模式:只调试主进程
child on 只调试子进程
parent off 同时调试两个进程,gdb跟主进程,子进程block在fork位置
child off 同时调试两个进程,gdb跟子进程,主进程block在fork位置
2、 利用info threads/inferiors 来查看线程/进程情况
3、 thread [n] 切换线程
4、 inferior [n] 切换进程
5、 set scheduler-locking on/off 打开/冻结 其他线程,只执行当前所在线程
为停止点设定运行命令
可以使用gdb提供的command命令来设置停止点的运行命令。也就是说,当运行的程序在被停住时,我们可以让其自动运行一些别的命令,这很有利行自动化调试。
commands [bnum]
... command-list ...
end
为断点号bnum指定一个命令列表。当程序被该断点停住时,gdb会依次运行命令列表中的命令。
例如:
break foo if x>0
commands
printf "x is %d/n",x
continue
end
断点设置在函数foo中,断点条件是x>0,如果程序被断住后,也就是一旦x的值在foo函数中大于0,gdb会自动打印出x的值,并继续运行程序。
如果要清除断点上的命令序列,那么只要简单地执行一下commands命令,并直接在输入end就行了。