GDB调试器的用法

本文是我在学习Linux GDB调试器的时候所做的笔记,主要记录在使用GDB的时候常用的命令,以便于以后的复习参考,不正确的地方忘高手指教 。

GDB是GNU开源组织发布的一个强大的UNIX下的程序调试器,GDB主要可帮助工程师完成下面4个方面的功能

  • 启动程序,可以按照工程师自定义的要求运行程序
  • 让被调试的程序在工程师指定的断点处停住,断点可以是条件表达式
  • 当程序被停住时,可以检查此时程序中所发生事,并追踪上文
  • 动态的改变程序的运行环境

在调试内核金额调试应用程序时使用GDB命令是完全相同的

1、list命令

在GDB中运行list命令(缩写l)可以列出代码,list具体形式如下:

  • list<linenum>,显示程序第linenum行周围的源程序
  • list<function>,显示函数名为function的函数的源程序
  • list,显示当前行后面的源程序
  • list - ,显示当前行后面的源程序

2、run命令

在GDB中,运行程序使用run命令。在程序运行前,我么可以设置如下4方面的工作环境

  • 程序运行参数:用set args 可指定运行时参数,如set args 10 20 30 40 50 ;用show args命令可以查看设置好的运行参数。
  • 运行环境:用path<dir>可设定程序的运行路径;用how paths可查看程序的运行路径;用set environment varname[=value]可设置环境变量,如set env USER=David; 用show environment[varname]可查看环境变量
  • 工作目录:cd<dir>相当于shell的cd命令,pwd可显示当前所在的目录
  • 程序的输入输出:info terminal用于显示程序用到的终端的模式;在GDB总也可以使用重定向控制程序的输出,如run>outfile;用tty命令可以指定输入输出的终端设备,如tty/dev/ttyS1

3、break命令

在GDB中使用break命令来设置断点,设置断点方法如下

  • break<function>  在进入指定函数时停住,在c++中可以使用class::function或function(type,type)格式来指定函数名
  • break<linenum>  在指定行号停住
  • break+offset / break-offset  在当前行号的前面或者后面的offset出停住,offset为自然数
  • break filename:linenum  在源文件filename的function函数的入口处停住
  • break*address  在程序运行的内存地址处停住
  • break  break命令没有参数时,表示在下一条指令处停住
  • break...if<condition>  ...可以时上述的break<linenum>、break+offset / break-offset中的参数,condition表示条件,在条件成立时停住。比如在循环体中,可以设置break if i=100,表示当i为100时程序停住。查看断点时,可使用info命令,如info breakpoints[n]、info break[n]  (n表示断点号)

4、单步命令

在调试过程中,next命令用于单步执行,类似于VC++中的step over。next的单步不会进入函数的内部,与next对应的step(缩写为s)命令则在单步执行一个函数时,进入其内部,类似于VC++中的step into。单步执行的更复杂用法如下

  • step<count>  单步跟踪,如果函数有调用,则进入该函数(进入函数的前提时,此函数被编译有debug信息)。step后面不加count表示一条条执行,加count表示执行后面的count条指令,然后停住
  • next<count>  单步跟踪,如果有函数调用,他不会进入该函数,同理,next后面不加count表示一条条执行,加count表示执行后面的count条指令,然后停住。
  • set step-mode 。  set step-mode on用于打开step-mode模式,这样,在进行单步跟踪(运行step指令)时,若跨越某没有调试信息的函数,程序的执行则会在该函数的第一条指令处停住,而不会跳过真个函数。这样我们可以查看该函数的机器指令
  • finish  运行程序,直到当前函数完成返回,并打印函数返回时的堆栈地址、返回值及参数值等信息
  • until(缩写为u)一直在循环体内执行单步而退不出来是一件令人烦恼的事情,用until命令可以运行程序直到提u出循环体
  • stepi(缩写为si)和nexti(缩写为ni)stepi和nexti用于单步追踪一条机器指令。比如,一条C程序代码有可能由数条机器指令完成,stepi和nexti可以单步执行机器指令,相反,step和next时C语言级别的命令。另外,运行display/i $pc命令后,单步追踪会在打出程序代码的同时打出机器指令,即汇编代码

5、continue  当程序被停住后,可以使用continue命令(缩写为c)回复程序的运行直到程序的结束,或达到下一个断点,命令格式为:

  • continue [ignore-count]
  • c [ignore-count]
  • fg [ignore-count]

 ignore-count 表示忽略其后多少次断点。

6、print命令

在调试程序时,当程序被停住时,可以使用print命令(缩写为p),或是同义命令inspect来查看当前运行数据。print命令格式如下:

  • print <expr>
  • print /f <expr>

<expr>是表达式,也是被调试的程序中的表达式,<f>是输出的格式,比如,如果要把表达式按十六进制的格式输出,那么就是/x。在表达式中,有几种GDB所支持的操作符,他们可以用在任何一种语言中,@是一个和数组有关系的操作符,::指定一个在文件或是函数中的变量,{<type>}<addr>表示一个指向内存地址<addr>的类型为type的对象。

当需要查看一段连续的内存空间时,可以使用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>  为表达式(变量)expr被读时,停止程序运行
  • awatch<expr>  当表达式(变量)的值被读或者被写时,停止程序运行
  • info watchpoints  列出当前所设置的所有观察点

8、examine命令

我们可以使用examine命令(缩写为x)来查看内存地址中的值。examine命令的语法如下所示:

  • x/<n/f/u> <addr>

<addr>表示一个内存地址。“x/”后的n、f、u都是可选的参数,n是一个正整数,表示显示内存的长度,也就是从当前地址向后显示的哥地址的内容;f表示显示的格式,如果地址所指的是字符串,那么格式可以是s,如果地址是指令地址,那么格式可以是i;u表示从当前地址往后请求的字节数,如果不指定的话,GDB默认的是4个字节。u参数可以被一些字符代替:b表示单字节,h表示双字节,w表示4字节,g表示8字节。当我们指定了字节的长度后,GDB会从指定的内存地址开始,读写指定字节,并把其当作一个值取出来。n、f、u这3个参数可以一起使用,例如命令 x/3uh 0x54320 表示从内存地址0x54320 开始以双字节为1个单位(h)、16进制方式(u)显示3个单位(3)的内存

9、set命令

examine命令用于查询内存,而set命令用于设置内存。它的命令格式是“set *有类型的指针=value”  比如:

对如下函数进行内存地址设置,并查看

   #include <stdio.h>
   #include <stdlib.h>
   
   int main(int argc, char*argv[])
   {
           void *p = malloc(16);
           while(1);
   }

也可以直接使用地址常数:

10、jump命令

一般来说,被调试程序会按照程序代码的运行顺序依次运行,但是GDB也提供了乱序执行的功能,也就是说,GDB可以修改程序的执行顺序从而让程序随意跳跃。这个功能可以由jump命令jump<linespec>来指定下一条语句的运行点。<linespec>可以是文件的行号,可以是file:line的格式,也可以是+num这种偏移量格式,表示下一条运行语句从那里开始。

  • jump <address>   这里的<address>是指代码的内存地址

注意:jump指令不会改变当前程序栈中的内容,如果使用jump从一个函数跳转到另一个函数,当跳转到的函数运行完返回,进行出栈操作时不然会发生错误,这可能会导致意想不到的错误,因此最好只用jump在同一个函数中进行跳转

11、signal命令

使用signal命令,可以产生一个信号量给被调试的程序,如中断信号ctrl+c。于是,可以在程序运行的任意位置处设置断点,并在该断点处用GDB产生一个信号量,这种精确的在某处产生信号的方法非常有利于程序的调试的。signal命令的语法是signal<signal>,UNIX的系统信号量通常为1~15,因此<signal>的取值也在这个范围内

12、return命令

如果在函数中设置了调试断点,在断点后还有语句没有执行完,这时候我们可以使用return命令强制函数忽略还没有执行的语句并返回

  • return
  • return <expression>

上述return命令用于取消当前函数的执行,并立即返回,如果指定了<expression>,那么该表达式的值会被作为函数的返回值

13、call命令

call命令强制调用某函数

call <expr>

表达式可以是函数,以此达到强制调用函数的目的,他会显示函数的返回值(如果函数的返回值不是void)。比如下列程序执行while(1)的时候:

   #include <stdio.h>
   #include <stdlib.h>
   
   int main(int argc, char*argv[])
   {
           void *p = malloc(16);
           while(1);
   }

我们强制要求其执行strcpy()和printf(),执行效果如下:

14、

info命令可以用来在调试时查看寄存器、断点、观察点和信号信息。要查看寄存器的值,可以使用如下命令:

  • info  registers (查看除了浮点寄存器以外的寄存器)
  • info  all-registers (查看所有寄存器,包括浮点寄存器)
  • info  registers <regname ...>  (查看指定的寄存器)

查看断点信息,使用如下命令

  • info break

列出当前所设置的所有观察点,可使用如下命令

  • info watchpoints

要查看那些信号正在被GDB检测使用如下命令

  • info signals
  • info handle

也可以使用info line命令查看源代码在内存中的地址。info line后面可以跟行号、函数名、文件命名:行号、文件名:函数名等多种形式,例如下面的命令会打印出所指定的源代码在运行时的内存地址:

  • info line test.c:func

15、disassemble命令

disassemble命令用于反汇编,可用它来查看当前执行时的源代码的机器码,实际上只是把目前内存中的指令冲刷出来。

注明:本博客是根据宋宝华的l《linux设备驱动 开发详解》所编写

猜你喜欢

转载自blog.csdn.net/David_361/article/details/86548721