四、gdb调试工具
gdb主要提供的功能:
监视程序中变量的值的变化
设置断点,使程序在指定的代码行上暂停执行,便于观察
单步执行代码
分析崩溃程序产生的core文件
使用命令:
gdb filename 装入可执行文件进行调试
注:由于调试需要加入调试信息,在编译是需要加入-g选项或-ggdb3选项
调试:
# gcc filename 进入调试环境
(gdb) break main 在main的下一行代码处加入断点
(gdb) break n 在第n行加入断点
(gdb) run 运行程序
(gdb) step / s 单步执行,
(gdb) print 表达式 打印变量的值如 print input 打印变量input的值
(gdb) print 变量=表达式 设置变量的值
(gdb) print 开始表达式@要打印的连续内存空间大小 打印一段内存的内容
(gdb) display 变量值 执行到断点是会显示变量值,可以观察表达式的值的变化(需要在表达式(变量)作用范围内,一旦出了变量的作用范围不再显示变量值)
(gdb) next 与step相区别,next不能跟踪到函数内部,也即跳入与跳过的区别。
(gdb) quit 退出调试环境
使用代码:
#include <stdio.h>
int getinput( void);
void printmessage(int counter, int input);
int main(void)
{
int counter = 0;
int input = 0;
for(counter = 0; counter <= 200; counter++)
{
input = getinput();
if(input == -1)
end(0);
printmessage(counter, input);
}
return 0;
}
int getinput(void )
{
int input;
printf("Enter an integer, or use -1 to exit:");
scanf("%d", &input);
return input;
}
void printmessage(int counter, int input)
{
static int lastnum = 0;
counter ++;
printf("For number %d, you enterd %d(%d more than last time)\n", counter, input, input-lastnum);
lastnum = input;
}
显示数据命令:
显示命令最常用的是display 和print命令,另外还有printf 和 set命令
程序如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct TAG_datastruct{
char * string;
int checksum;
}datastruct;
datastruct * getinput( void);
void printmessage(datastruct * todisp);
int main(void)
{
int counter = 0;
int maxval = 0;
datastruct * svalues[200];
for(counter = 0; counter < 200; counter++)
{
svalues[counter] = getinput();
if(!svalues[counter])
break;
maxval = counter;
}
printmessage(svalues[maxval/2]);
return 0;
}
datastruct * getinput( void)
{
char input[80];
datastruct * instruct;
int counter;
printf("Enter a string, or leave blank when done:");
fgets(input, 79, stdin);
input[strlen(input)-1] = 0;
if(strlen(input) == 0)
return NULL;
instruct = malloc(sizeof(datastruct));
instruct->string = strdup(input);
instruct->checksum = 0;
for(counter = 0; counter < strlen(instruct->string); counter++)
{
instruct->checksum += instruct->string[counter];
}
return instruct;
}
void printmessage(datastruct * todisp)
{
printf("This structure has a checksum of %d.Its string is:\n", todisp->checksum);
puts(todisp->string);
}
$ gdb test2 进入调试环境
(gdb) run 运行程序
(gdb) print svalues 打印一个数组(svalues是一个结构体数组)
(gdb) print svalues[0]->checksum 打印结构体的一个成员
(gdb) print input 打印一个字符串数组,
(gdb) print input@20 打印input地址处20个内存单元的内容
(gdb) print input[0] 打印input数组的第一个元素
(gdb) finish 强制结束调试
内存检查命令:
x/format address format为显示内存单元个数和显示方式组成:如x/2c 以字符形式显示两个内存单元
(gdb) x/2c 0x9813008
(gdb) x/2c instruct
(gdb) x/2c instruct->string
x/2x 以十六进制形式显示 x/2o 以八进制形式显示 x/2d 以十进制形式显示
printf命令:
printf “%2.2s\n”, (char*)0x981e018
printf “%2.2s\n”, instruct->string
%2.2s\n 第一个2表示最多输出2个单元第二个2表示从0x981e018开始的两个
set命令在程序调试过程中设置变量的值。
使用断点:
(gdb) break test2.c:21 在test2.c文件的第21行设置断点
(gdb) break 23 在调试文件的第23行设置断点
(gdb) break printmessage 在调试文件的breakmessage函数处设置断点
查看当前调试程序的断点:
info break(b)
编号 类型 可用 地址 断点信息
Num Type Disp Enb Address What
1 breakpoint keep y 0x080484b9 in main at test2.c:15
2 breakpoint keep n 0x08948630 in printmessage at test2.c:57
run 到断点处,执行continue命令恢复程序运行。
continue cont 两个命令作用相同
cont 2 跳过两次断点
(gdb) bbreak 32 在32行加入一个临时断点,只能断点一次
(gdb) enable 3 将断点3设置为可用
(gdb) disable 3 将断点3设置为不可用
(gdb) delete 2 将断点2删除
(gdb) clear 行号 clear 23 将23行的断点清除掉。注意此处是行号,而delete使用的是断点编号。
使用观察窗口:
(gdb) watch counter>15
当表达式满足条件时,显示变量的值
查看栈信息:
backtrace 或 bt 查看栈信息
(gdb) bt 查看当前调用栈的所有信息
(gdb) backtrace <n> 打印栈顶n层的栈信息
(gdb) bt <-n> 打印栈底n层的栈信息
(gdb) frame <n> n为一个从0开始证书,栈中的编号,frame 0 表示栈顶 f 1 表示第二层
(gdb) frame 或 f 查看当前栈的信息
(gdb) info frame 给出更为详细的当前栈信息
info args 显示出当前函数中所有局部变量及值
info locals 显示函数中所有局部变量及值
info catch 显示当前函数中的异常处理信息
查看源程序:
gdb可以打印出来所调试的源代码,在程序编译时需要加入-g参数。
(gdb) list <linenum> 打印linenum行的代码
(gdb) list <function> 打印function函数的源程序
(gdb) list 显示当前行后面的源程序
(gdb) list - 显示当前行前面的源程序
通过set listsize <count> 设置一次显示的源码行数
show listsize 查看当前listsize的设置
(gdb) list <first>, <last> 显示从first行到last行的代码
注:一路list,当到达文件结尾时,再输入list命令出现已经到结尾的提示,使用list num可以将list设置到num行开始显示。
forward-search <regexp> regexp为正则表达式,前向搜索regexp表达的字符串
search <regexp> regexp为正则表达式,前向搜索regexp表达的字符串
reverse-search <regexp> 反向搜索字符串
搜索字符串时,对于源文件,只编译进了文件名称,没有目录名,可以通过directory进行设置。
(gdb) directory <dirname …> 可以使用 “:”隔开多个目录名称
show directories 显示源文件的搜索路径
info line 可以显示源代码在内存中的地址,info line 后可以跟行号,函数名,文件名:行号,文件名:函数名等。
查看执行中变量内容:
(gdb) print *array@len 显示数组的内容
输出格式化
print </f> <expr> 其中f表示格式字符串
f取值:
x 十六进制 d 十进制 u 十进制无符号整型
o 八进制 t 二进制格式 a 十六进制格式
c 字符格式 f 浮点数格式
如:
(gdb) p /c i 将i按照字符形式输出
(gdb) p /f i 将i按照浮点数形式打印出来
examine( 或 x) 查看内存 x /<n/f/u> <addr> 参考前面总结
display <expr> 自动显示表达式的值
display /<fmt> <expr> 自动显示表达式的值
display /<fmt> <expr> 自动显示表达式的值
expr 为变量或表达式,fmt为格式
undisplay <dnums …>
delete display <dnums …> dnums为设置好的自动显示的编号,也即显示表达式的编号,可以通过info display来查看自动显示的信息
disable display <dnums …>
enalble display <dnums …> 使得显示可用和不可用
设置显示选项:
set print address
set print address on 打开地址输出,当程序显示函数信息时,会显示函数的参数地址
set print address off 关闭函数的参数地址显示
show print address 查看当前地址显示选项是否打开
同样 array 为显示数组
elements 显示数组的元素个数信息
null-stop 遇到结束符时停止显示
pretty 显示结构体每个元素占一行
union 显示结构体,是否显示其内的联合体数据
object C++对象显示
static-members 显示静态成员
vtbl 比较规整的格式来显示虚函数表
改变程序执行:
修改变量值 x=4 直接修改变量x的值为4
为了防止和系统的变量冲突,在设置变量值时,加上var 即 set var x= 4
跳转执行:
jump <linespec> 指定下一个语句的运行点,linespec可以是文件的行号,file:line,可以是+num便宜格式
jump <address> address 代表代码行的内存地址
也可以通过 set $pc = 0x456 通过设置pc值来改变程序运行
产生信号量:
signal <signal> Linux系统的信号量从1~15,<signal>的取值也在这个范围。
强制返回:
return
return <expression> 强制返回一个值,expression
强制调用函数:
call <expr> 表达式
分析core文件:
程序执行前,执行$ ulimit –c unlimited 命令,使得自己拥有core dump的权限。
编译程序,直接执行(不要在gdb下执行),出现错误,就可以使用gdb分析core.*文件,看错出现位置。
其中使用 bt命令可以查看栈的情况,使用frame命令(frame statcknum)查看指定栈帧的内容。
By Andy @ 2012-07-12 17:06