调试工具gdb
1.1 gdb符号调试器简介
gdb是一个用来调试C和C++程序的功能强大的调试器,它能在程序运行时观察程序的内部结构和内存的使用情况。
gdb主要提供以下几种功能:
- 监视程序中变量值的变化
- 设置断点,使程序在指定的代码行上暂停执行,便于观察
- 单步执行代码
- 分析崩溃程序产生的core文件
gdb filename(执行文件名)
编译时需加上-g 或 -ggdb3 选项
1.2 gdb功能详解及其应用
1.2.1调试步骤
1.调用gdb
gdb filename
2.设置断点并调试
break functionname/linenumer
run 执行程序
step 单步跟踪程序代码
print命令:
print 表达式
print 变量=表达式
print 开始表达式@要打印连续内存空间的大小
display命令:
display 表达式
在使用display命令时,每次调试器中断程序,挂起指令都要显示变量的值
next命令:
不进入函数内部
quit命令:
退出gdb
1.2.2 显示数据命令
1. print命令与display命令
finish 执行完当前函数
2. 内存检查命令
x /format address
format指显示单元的个数以及显示的格式
例如:x/2c 0x120100fa0
3.printf 类似于c里的printf()函数
例如:printf"%2.2s\n",(char*)0x120100fa0
4.使用set命令
set variable 变量=表达式 设置变量值
1.2.3 使用断点
continue命令和cont命令(cont 数字 表示执行数字次继续)
break 行号
break 函数名
条件断点:
condition 断点编号 表达式 程序在表达式为真时中断
tbreak 行号 临时断点
(break 行号
enable delete 断点号)等效于临时上一个断点
enable 断点编号 恢复失效断点
disable 断点编号 使断点失效
delete 断点编号或表达式 清除断点(无提示信息)
clear 要清除的断点所在行号 清除断点(有提示信息)
1.2.4 使用观察窗口
watch 表达式 如果表达式中某个变量的取值超过了范围,就不能再对表达式取值了。这一点与条件断点设置不同,因为它只在代码的固定位置取值。
1.2.5 查看栈信息
backtrace
bt
bt n 栈顶上n层栈信息
bt -n 栈顶下n层栈信息
frame n
f n n是一个从0开始的整数,是栈中的层编号。比如:frame 0表示栈顶,frame 1表示栈的第二层
up n 表示向栈的上面移动n层,不输入n表示移动一层
down n 表示向栈的下面移动n层
以下为不带提示信息的移动栈层信息:
select-frame n
up-silently n
down-silently n
---------------
frame 或 f 查看当前栈层信息(包括栈层编号,当前函数名,函数参数值,函数所在文件及行号,函数执行到的语句)
info frame 或 info f 该命令会显示更为详细的当前栈层的信息,只不过大多数都是运行时的内地址。
info args 显示出当前函数的参数名及值
info locals 显示出当前函数中所有局部变量的值
info catch 显示当前函数中的异常处理信息
1.2.6 查看源程序
1. 显示源代码
list <linenum> 显示程序第linenum行的周围的源程序。
list <function> 显示函数名为function的函数的源程序
list 显示当前行后面的源程序
list - 显示当前行前面的源程序
set listsize <count> 设置一次显示源代码的行数
show listsize 查看当前listsize的设置
list <first>,<last> 显示从first行到last行之间的源代码
list ,<last> 显示当前行到last行之间的源代码
list + 向后显示源代码
一般来说,在list后面可以跟以下这些参数。
<linenum>:行号。
<+offset>:当前行号的正偏移量
<-offset>:当前行号的负偏移量
<filename:linenumber>:哪个文件的哪一行
<function>:函数名
<filename:function>:哪个文件的哪个函数
<*address>:程序运行时的语句在内存中的地址
2.搜索源代码
forward-search <regexp>
search <regexp> 向前搜索
reverse-search <regexp> 全部搜索
3.指定源文件的路径
directory <dirname ...>
dir <dirname ...>
directory 清除所有自定义源文件搜索路径信息
show directories 显示定义了源文件搜索路径
4.源代码的内存
info line 行号/函数名
info line 文件名:行号/文件名:函数名
disassemble func 查看func的汇编代码
disassemble address
1.2.7 查看运行时数据
1.表达式
@ 一个和数组有关的操作符
:: 指定一个在文件或是一个函数中的变量
{<type>} <addr> 表示一个指向内存地址<addr>的类型为type的一个对象
2.程序变量
全局变量(所有文件可见)
静态全局变量(当前文件可见的)
局部变量(当前Scope可见的)
示例:
file::variable
function::variable
p 'f2.c'::x
3.数组
例如:程序中有如下语句
int *array = (int *)malloc(len * sizeof(int));
p *array@len
如果是静态数组的话,直接用"print 数组名"就可以显示数组中所有数据的内容了。
4.输出格式
x 按十六进制格式显示变量
d 按是禁止格式显示变量
u 按十六进制格式显示无符号整形
o 按八进制格式显示变量
t 按二进制格式显示变量
a 按十六进制格式显示变量
c 按字符格式显示变量
f 按符点数格式显示变量
5.查看内存
x/<n/f/u> <addr> nfu是可选参数。
n表示显示内存的长度,
f表示显示的格式,
u表示从当前地址往后请求的字节数,如果不指定的话,gdb默认是4个bytes。
u可用以下字符来代替:b表示单字节,h双字节,w四字节,g八字节。
addr表示一个内存地址。
示例:
x/3uh 0x54320 以无符号整形双字节为单位显示从地址0x54320开始的3个长度的内存中的内容
6.自动显示
可以设置一些自动显示的变量,当程序停住时,或是在单步跟踪时,这些变量会自动的显示。
display <expr>
display/<fmt> <expr>
display/<fmt> <addr>
格式i和s同样被display支持,一个非常又用的命令是:
display /i $pc $pc是gdb的环境变量,表示指令的地址,/i则表示输出格式为机器指令码,也就是汇编。与是当程序停下后,就会出现源代码和机器码相对应的情形。
undisplay <dnums...>
delete display <dnums...>
删除自动显示,dnums意为设置好了的自动显示的编号。如果要同时删除几个编号,可以用空格分隔;如果要删除一个范围内的编号,可以用减号表示。
disable display <dnums...>
enable display <dnums...>
不删除自动显示的设置,而只是让其失效和恢复,而只是让其失效和恢复。
info diaplay
查看display设置的自动显示的信息。gdb会显示出一张表格,报告中设置了多少个自动显示设置,其中包括设置的编号,表达式以及是否enable。
7.设置显示选项
set print address on/off 显示函数参数地址 系统默认打开
show print address 查看当前地址显示选项是否打开
set print array on/off 打开/关闭数组显示,打开后数组显示时每个元素占一行;关闭时每个元素以逗号分隔。这个选项默认时关闭的。
show print array
set print elements <number-of-elements> 这个选项主要时设置数组的,如果数组太大了,就可以指定一个<number-of-elements>来指定数据显示的最大长度,当达到这个长度时,gdb就不再往下显示了。如果设置为0,则表示不限制。
show print elements
set print null-stop <on/off> 如果打开了这个选项,那么当显示字符串时,遇到结束符则停止显示。这个选项默认为off。
set print pretty on/off 选项打开时,当gdb显示结构体时会比较漂亮。
show print pretty
set print sevenbit-stirngs <on/off> 设置字符显示,是否按“\nnn”的格式显示。如果打开,则字符串或字符数据按\nnn显示,如“\065”
show print sevenbit-stirngs
set print union <on/off> 设置显示结构体时,是否显示其内的联合体数据。
show print union 查看联合体数据的显示方式
set print object <on/off> 对象选项设置。在C++中,当一个对象指针指向其派生类时,如果打开这个选项,gdb会自动按照虚方法调用的规则显示输出,如果关闭这个选项,gdb就不管虚函数表了。这个选项默认为off。
show print object 查看对象选项的设置
set print static-members <on/off> 这个选项表示,当显示一个C++对象中的内容时,是否显示其中的静态数据成员。默认为on。
show print static-members 查看静态数据成员选项设置
set print vtbl <on/off> 当此选项打开时,gdb将用比较规整的格式来显示虚函数表。其默认是关闭的。
show print vtbl 查看虚函数显示格式的选项
8.历史记录
每一个print命令都会被gdb记录下来。gdb会以$1,$2,$3......这样的方式为每一个print命令编上号。于是,可以使用这个编号访问以前的表达式,如$1。
9.gdb环境变量
可以在gdb调试环境中定义自己的变量,用来保存一些调试程序中的运行数据。
环境变量没有类型,可以给环境变量定义任意的类型,包括结构体和数组。
set $foo = *object_ptr
show convenience 查看当前所设置的所有的环境变量
(set i = 0
print bar[$i++]->contents) 这个一个比较强大的功能,环境变量与程序变量的交互使用使得程序调试更为灵活便捷。输入这样的命令后,按下Enter键,重复执行上一条语句,环境变量会自动增加,从而完成逐个输出的功能。
10.查看寄存器
info registers 查看寄存器的情况(除了符点器存器)
info all-registers 查看所有寄存器的情况
info registers <regname...> 查看指定寄存器的情况
1.2.8 改变程序的执行
1.修改变量值
print x=4
whatis width
set var width=47
2.跳转执行
jump <linespec> <linespec>可以是文件的行号,可以是file:line格式,可以是+num这种偏移量格式。表示下一条语句从哪里开始。
jump <address> 这里的<address>是代码行的内存地址。
建议在同一个函数内调转
熟悉汇编的人都知道,程序运行时有一个寄存器用于保存当前代码所在的内存地址。所以,jump命令也就是改变了这个寄存器中的值。可以使用set $pc来更改调转执行的地址。如:
set $pc=0x485
3.产生信号量
使用signal命令可以产生一个信号量给被调试的程序。如中断信号Ctrl+C。这非常方便与程序的调试,可以在程序运行的任意位置设置断点,并在该断点用gdb产生一个信号量。精确地在某处产生信号非常有利程序的调试。其语法是:
signal <signal>
Linux的系统信号量通常从1-15,所以<signal>也在这个范围。
4.强制函数返回
如果调试断点在某个函数中,还有语句没有执行完,可以使用return命令强制函数忽略还没有执行的语句并返回。
return
return expression
使用return命令取消当前函数的执行,并立即返回。如果指定了<expression>,那么该表达式的值会被当作函数的返回值。
5.强制调用函数
call <expr> 表达式中也可以是函数
另一个相似的命令也可以完成这一功能--print。print后面可以跟表达式,所以也可以用它来调用函数。print和call不同之处是,如果函数返回void,call则不显示,print则显示函数的返回值,并把该值存入历史数据中。
6.在不同语言中使用gdb
show language 查看当前语言环境
info frame 查看当前函数的程序语言
info source 查看当前文件的程序语言
set language 如果set language后面什么也不跟,可以查看gdb所支持的语言种类。可以在set language 后面加上被列出的语言名,来设置当前的语言环境。
1.2.9 core dump分析
在进行程序开发时,常常由于种种原因导致程序崩溃。这时可以使用Linux下的core dump功能。在程序崩溃时,Linux会创建一个core文件,在程序结束后,利用这个文件中的信息,在使用gdb,就能指导程序崩溃时在做些什么。但core dump功能并不是所有的程序员都可以使用的,这是因为一些发行版和一些系统的管理员在默认时禁止使用core dump。可以使用ulimit -c unlimited
这样就能使用core文件分析了。
编译生成可执行文件test,然后运行./test生成core文件
gdb test core
bt(即backtrace)命令用来打印stack frame(栈桢)指针,可以了解当前程序运行过程中的函数之间的调用关系。
使用frame命令:
frame命令是用来打印栈桢的,它使用如下格式。
frame 要打印的栈桢的编号 如果没有指定栈桢时,gdb会打印当前选择的栈桢
可以使用info frame命令查看当前所选择的栈桢,它将给出栈桢的详细信息。
1.3 其他调试工具
xxgdb 是X-Windows系统的调试工具,实际上它图形化的gdb,保留了gdb所有的特性。
1.4 小结
在软件开发中出现错误在所难免,所以程序的调试就成为软件开发中非常重要的一个环节,gdb就是Linux系统下C程序调试工具中最常用的一种。本章首先介绍了gdb的程序调试步骤,进而深入地阐述了它的功能和一些高级调用方法。最后简单介绍了另一种调试工具。