Linux gdb调试器用法
GDB是GUN开源组织发布一个强大的Unix下的调试工具,GDB主要珂帮助工程师完成下面4个功能:
.启动程序,可以按照工程师自定义的要求随心所欲的运行程序
.让被调试程序按照工程师指定的断点处停住,断点可以是条件表达式
.当程序被停住时,可以检查此时程序中所发生的事,并追索上文
.动态的改变程序的执行环境
不管是调试Linux内核空间的驱动还是调试用户空间的应用程序,掌握GDB的用法都必须是,而且,调试内核和调试应用程序使用的GDB命令完全相同,下面以代码为例讲解
int add(int a, int b)
{
return a + b;
}
main()
{
int sum[10] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
} ;
int i;
int array1[10] =
{
48, 56, 77, 33, 33, 11, 226, 544, 78, 90
};
int array2[10] =
{
85, 99, 66, 0x199, 393, 11, 1, 2, 3, 4
};
for (i = 0; i < 10; i++)
{
sum[i] = add(array1[i], array2[i]);
}
}
使用命令gcc –g gdb_example.c –o gdb_example编译上述程序,得到包含调试信息的二进制文件,执行 gdb gdb_example命令进入调试状态,记住编译程序时候加入-g,否则调试时查看函数或变量都看不到名称,只能看二进制地址。
1, list命令
在gdb运行list命令(缩写l)可以列出代码,list的具体形式包括
.list ,显示程序第linenum行周围的源程序,如
.list 显示函数名为function的函数源程序
.List,显示当前行后面的源程序
.list – 显示当前行前面的源程序
下面演示了使用gdb中的run(缩写r)、break(缩写b)、next(缩写n)命令控制程序的运行,并使用print(缩写p)命令打印程序中的变量sum的过程:
2.run 命令
在gdb,运行程序使用run命令,在程序运行前,我们可以设置如下4个方面的工作环境
.程序运行参数
set args可以指定运行参数,如:set args 10 20 30 40 50;show args可以查看设置好的运行参数
.运行环境
Path
Show environment [varname]则用于查看环境变量
.工作目录
cd
.程序的输入输出
info terminal 用于显示程序用到的终端的模式;gdb中也可以使用重定向控制程序输出,如run > outfile;
tty命令可以指定输入输出的终端设备,如:tty /dev/ttyS1。
2, break命令
.break
在进入指定函数时候停住,C++中可以使用class::function或function(type, type)格式来指定函数名
.break
指定行号停住
.break +offset/break –offset
在当前行号的前面或后面的offset行停住,offset为自然数
.break filename:linenum
在源文件filename的linenum行处停住
.break filename:function
在源文件filename的function函数处入口停住
.break *address
在程序运行的内存地址处停住
.break
break没有参数时,表示在下一条命令处停住
.break 。。。 if 《condition》
。。。可以是上述的break 、break +offset / break –offset中的参数,condition表示条件,在条件成立时停住。比如在循环体中,可以设置break if i=100,表示当i为100时停住程序
查看断点时,可以用info命令,如info breakpoints[n],n表示断点,如图
Num:表示断点的序号
Type:类型
Enb:表示断点是否在使用
What:描述断点的相关信息
3, 单步命令
在调试过程中,next命令用于单步执行,类似VC++中的step over。next的单步不会进入函数的内部,与next对应的step(缩写s)命令则在单步执行一个函数时,会进入其内部,类似VC++中的step into。下面演示了step命令的执行情况,在23行的add()函数调用处执行step会进入其内部的“return a+b;”语句:
单步执行更复杂用法的语法包括
.step
单步跟踪,如果有函数调用,则进入该函数(进入函数的前提是,此函数被编译有debug信息),step后面不加count表示一条条地执行,加表示执行后面的count条指令,然后再停住
.next
单步跟踪,如果有函数调用,它不会进入该函数。同样地,next后面不加count表示一条条地执行,加表示执行后面的count条指令,然后再停住。
.set step-mode
set step-mode on用于打开step-mode模式,这样,在进行单步跟踪时,程序不会因为没有debug信息而不停住,这个参数的设置可便于查看机器码。set step-mod off用于关闭step-mode模式
.finish
运行程序,直到当前函数完成返回,并打印函数返回时的堆栈地址和返回值及参数值等信息。
.stepi(缩写si)和nexti(缩写ni)
stepi和nexti用于单步跟踪一条机器指令,一条程序代码有可能由数条机器指令完成,stepi和nexti可以单步执行机器指令。 另外,运行“display/i $pc”命令后,单步跟踪会在打出程序代码的同时打出机器指令,即汇编代码
5,continue命令
当程序被停住后,可以使用continue命令(缩写c,fg命令同continue命令)恢复程序的运行直到程序结束,或到达下一个断点,命令格式为
-
continue [ignore-count]
- c [ignore-count]
- fg [ignore-count
ignore-count表示忽略其后多少次断点。 假设我们设置了函数断点add(),并watch i,则在continue过程中,每次遇到add()函数或i发生变化,程序就会停住,
6,print 命令
在调试程序时,当程序被停住时,可以使用print命令(缩写为p),或是同义命令inspect来查看当前程序的运行数据。print命令的格式是:
print
Print /
是表达式,是被调试的程序中的表达式,是输出的格式,比如,如果要把表达式按16进制的格式输出,那么就是/x。在表达式中,有几种GDB所支持的操作符,它们可以用在任何一种语言中,“@”是一个和数组有关的操作符,“::”指定一个在文件或是函数中的变量,“{} ”表示一个指向内存地址的类型为type的一个对象。
下面演示了查看sum[]数组的值的过程:
(gdb) print sum
$2 = {133, 155, 0, 0, 0, 0, 0, 0, 0, 0}
(gdb) next
Breakpoint 1, main () at gdb_example.c:25
25 sum[i] = add(array1[i], array2[i]);
(gdb) next
23 for (i = 0; i < 10; i++)
(gdb) print sum
$3 = {133, 155, 143, 0, 0, 0, 0, 0, 0, 0}
当需要查看一段连续内存空间的值的时间,可以使用GDB的“@”操作符,“@”的左边是第一个内存地址,“@”的右边则是想查看内存的长度。例如如下动态申请的内存:
int *array = (int *) malloc (len * sizeof (int));
在GDB调试过程中这样显示出这个动态数组的值:
P *array@len
print的输出格式包括:
• x 按十六进制格式显示变量。
• d 按十进制格式显示变量。
• u 按十六进制格式显示无符号整型。
• o 按八进制格式显示变量。
• t 按二进制格式显示变量。
• a 按十六进制格式显示变量。
• c 按字符格式显示变量。
• f 按浮点数格式显示变量。
我们可用display命令设置一些自动显示的变量,当程序停住时,或是单步跟踪时,这些变量会自动显示。 如果要修改变量,如x的值,可使用如下命令:
print x=4
当用GDB的print查看程序运行时的数据时,每一个print都会被GDB记录下来。GDB会以$1,$2,$3 …这样的方式为每一个print命令编号。我们可以使用这个编号访问以前的表达式,如$1
7,watch命令
watch一般来观察某个表达式(变量也是一种表达式)的值是否有变化了,如果有变化,马上停住程序。我们有下面的几种方法来设置观察点: watch :为表达式(变量)expr设置一个观察点。一量表达式值有变化时,马上停住程序。rwatch :当表达式(变量)expr被读时,停住程序。awatch :当表达式(变量)的值被读或被写时,停住程序。info watchpoints:列出当前所设置了的所有观察点。 下面演示了观察i并在连续运行next时一旦发现i变化,i值就会显示出来的过程:
(gdb) watch i
Hardware watchpoint 3: i
(gdb) next
for (i = 0; i < 10; i++)
(gdb) next
Hardware watchpoint 3: i
Old value = 0
New value = 1
0x0804838d in main () at gdb_example.c:23
23 for (i = 0; i < 10; i++)
(gdb) next
Breakpoint 1, main () at gdb_example.c:25
sum[i] = add(array1[i], array2[i]);
(gdb) next
for (i = 0; i < 10; i++)
(gdb) next
Hardware watchpoint 3: i
Old value = 1
New value = 2
0x0804838d in main () at gdb_example.c:23
for (i = 0; i < 10; i++)
8,examime命令
我们可以使用examine命令(缩写为x)来查看内存中的地址的值,examine命令的语法如下
x/<n/f/u>
表示一个内存地址。“x/”后的n、f、u都是可选的参数,n 是一个正整数,表示显示内存的长度,也就是说从当前地址向后显示几个地址的内容;f 表示显示的格式,如果地址所指的是字符串,那么格式可以是s,如果地址是指令地址,那么格式可以是i;u 表示从当前地址往后请求的字节数,如果不指定的话,GDB默认是4字节。u参数可以被一些字符代替:b表示单字节,h表示双字节,w表示四字节,g表示八字节。当我们指定了字节长度后,GDB会从指定的内存地址开始,读写指定字节,并把其当作一个值取出来。n、f、u这3个参数可以一起使用,例如命令“x/3uh 0x54320”表示从内存地址0x54320开始以双字节为1个单位(h)、16进制方式(u)显示3个单位(3)的内存。 ==
9,jump命令
一般来说,被调试程序会按照程序代码的运行顺序依次执行,但是GDB也提供了乱序执行的功能,也就是说,GDB可以修改程序的执行顺序,从而让程序随意跳跃。这个功能可以由GDB的jump命令:jump 来指定下一条语句的运行点。可以是文件的行号,可以是file:line格式,也可以是+num这种偏移量格式,表示下一条运行语句从哪里开始。jump
10,signal命令
使用singal命令,可以产生一个信号量给被调试的程序,如中断信号“Ctrl+C”。这非常方便于程序的调试,可以在程序运行的任意位置设置断点,并在该断点用GDB产生一个信号量,这种精确地在某处产生信号的方法非常有利于程序的调试。 signal命令的语法是:signal ,UNIX的系统信号量通常从1到15,所以取值也在这个范围。
11,return命令
如果在函数中设置了调试断点,在断点后还有语句没有执行完,这时候我们可以使用return命令强制函数忽略还没有执行的语句并返回。
return
return
上述return命令用于取消当前函数的执行,并立即返回,如果指定了,那么该表达式的值会被作为函数的返回值。
12,call命令
call命令用于强制调用某函数: call 表达式中可以一是函数,以此达到强制调用函数的目的,它会显示函数的返回值(如果函数返回值不是void)。 其实,前面介绍的print命令也可以完成强制调用函数的功能。
13,info命令
info命令可以在调试时用来查看寄存器、断点、观察点和信号等信息。要查看寄存器的值,可以使用如下命令: info registers (查看除了浮点寄存器以外的寄存器)info all-registers (查看所有寄存器,包括浮点寄存器)info registers <regname …> (查看所指定的寄存器) 要查看断点信息,可以使用如下命令:info break 列出当前所设置的所有观察点,使用如下命令:info watchpoints 查看有哪些信号正在被GDB检测,使用如下命令:info signals info handle 也可以使用info line命令来查看源代码在内存中的地址。info threads可以看多线程。info line后面可以跟行号、函数名、文件名:行号、文件名:函数名等多种形式,例如下面的命令会打印出所指定的源码在运行时的内存地址:
info line tst.c:func
display + 变量名:将该变量的内容常显示,即在变量的作用域中每次单步执行都会看到该变量的内容,对我们观察变量的内容的改变情况十分有帮助.
undisplay + n:取消显示列表中的号码n所对应的变量内容的常显示
set var x=n:将变量x的内容修改为n,这样做可以辅助我们调试程序,比方说我们先看到start=8;end=8;时程序的执行结果,我们就可以通过该命令直接修改变量的内容,这样可以使我们少单步执行几次程序就能得到我们想看到的结果。
info b num:查看第num个编号的断点的当前信息
info b:查看所有断点的信息
delete breakpoints:删除所有断点
delete breakpoints num:删除序号为num的断点
disable num:禁用序号为num的断点
enable num :启用序号为num的断点
until n:直接跳转至第n行