winston 发表于 2012-2-8 22:08:57

使用gdb进行调试

今天介绍一下gdb,如果你是在UNIX平台下做软件,你会发现GDB这个调试工具有比VC、BCB的图形化调试器更强大的功能。
GDB主要帮忙你完成下面四个方面的功能:
    1、启动你的程序,可以按照你的自定义的要求随心所欲的运行程序。
    2、可让被调试的程序在你所指定的调置的断点处停住。(断点可以是条件表达式)
    3、当程序被停住时,可以检查此时你的程序中所发生的事。
    4、动态的改变你程序的执行环境。
  一般来说GDB主要调试的是C/C++的程序。要调试C/C++的程序,首先在编译时,我们必须要把调试信息加到可执行文件中。使用编译器(cc/gcc/g++)的 -g 参数可以做到这一点。如:
    > cc -g hello.c -o hello
    > g++ -g hello.cpp -o hello
  如果没有-g,你将看不见程序的函数名、变量名,所代替的全是运行时的内存地址。当你用-g把调试信息加入之后,并成功编译目标代码以后,让我们来看看如何用gdb来调试他。  
  启动GDB的方法有以下几种:
    1、gdb <program>
       program也就是你的执行文件,一般在当前目录下。
    2、gdb <program> core
       用gdb同时调试一个运行程序和core文件,core是程序非法执行后core dump后产生的文件。
    3、gdb <program> <PID>
       如果你的程序是一个服务程序,那么你可以指定这个服务程序运行时的进程ID。gdb会自动attach上去,并调试他。program应该在PATH环境变量中搜索得到。
  在启动了gdb后,就可以开始使用gdb中命令了。如果希望查看都有哪些命令,可以输入help进行查询。
  下面简单演示几个小例子:
  示例一:在进入函数func时,设置一个断点。可以敲入break func,或是直接就是b func
    (gdb) b func
    Breakpoint 1 at 0x8048458: file hello.c, line 10.

  示例二:敲入b按两次TAB键,你会看到所有b打头的命令:
    (gdb) b
    backtracebreak      bt
  示例三:l(l命令相当于list,从第一行开始例出原码)
  示例四:r(运行程序) n(下一步) c(Continuing)
  示例五:p i 查看变量i的值
  示例六:bt 查看函数堆栈

  
  需要特别说明的是,在gdb中运行程序时,使用run命令,可能需要做以下的操作。
  1、程序运行参数。
    set args 可指定运行时参数。(如:set args 10 20 30 40 50)
    show args 命令可以查看设置好的运行参数。
  2、运行环境。
    path <dir> 可设定程序的运行路径。
    show paths 查看程序的运行路径。
    set environment varname [=value] 设置环境变量。如:set env USER=hchen
    show environment 查看环境变量。  
  3、工作目录。
    cd <dir> 相当于shell的cd命令。
    pwd 显示当前的所在目录。

  4、程序的输入输出。
    info terminal 显示你程序用到的终端的模式。
    使用重定向控制程序输出。如:run > outfile
    tty命令可以指写输入输出的终端设备。如:tty /dev/ttyb



  以上就是对gdb简单的一个介绍,以后如果有机会会更详细的介绍各种功能。请大家多多指教。
设置断点break <function> 在进入指定函数时停住。C++中可以使用class::function或function(type,type)格式来指定函数名。break <linenum> 在指定行号停住。  break filename:linenum 在源文件filename的linenum行处停住  break filename:function 在源文件filename的function函数的入口处停住  break *address 在程序运行的内存地址处停住  break if i=100 表示当i为100时停住程序  info breakpoints(info break) 查看断点  设置观察点(WatchPoint)观察点一般来观察某个表达式(变量也是一种表达式)的值是否有变化了,如果有变化,马上停住程序。我们有下面的几种方法来设置观察点:watch <expr> 为表达式(变量)expr设置一个观察点。一量表达式值有变化时,马上停住程序  rwatch <expr> 当表达式(变量)expr被读时,停住程序  awatch <expr> 当表达式(变量)的值被读或被写时,停住程序  info watchpoints 列出当前所设置了的所有观察点  删除观察点或断点clear 清除所有设置在函数上的停止点  clear <function> 清除所有设置在函数上的停止点  delete 删除指定的断点,breakpoints为断点号。如果不指定断点号,则表示删除所有的断点。range 表示断点号的范围(如:3-7)。其简写命令为d  disable disable所指定的停止点,breakpoints为停止点号。如果什么都不指定,表示disable所有的停止点。简写命令是dis.  enable enable所指定的停止点,breakpoints为停止点号。  恢复程序运行和单步调试当程序被停住了,你可以用continue命令恢复程序的运行直到程序结束,或下一个断点到来。也可以使用step或next命令单步跟踪程序。continue (c)恢复程序运行,直到程序结束,或是下一个断点到来  step <count> 单步跟踪,如果有函数调用,他会进入该函数.后面可以加count也可以不加,不加表示一条条地执行,加表示执行后面的count条指令,然后再停住。  next <count> 同样单步跟踪,如果有函数调用,他不会进入该函数  set step-mode on/off打开/关闭step-mode模式,于是,在进行单步跟踪时,程序不会因为没有debug信息而不停住。这个参数有很利于查看机器码  finish 运行程序,直到当前函数完成返回。并打印函数返回时的堆栈地址和返回值及参数值等信息  until 或 u 当你厌倦了在一个循环体内单步跟踪时,这个命令可以运行程序直到退出循环体.  nexti 或 ni 单步跟踪一条机器指令!一条程序代码有可能由数条机器指令完成,stepi和nexti可以单步执行机器指令。与之一样有相同功能的命令是“display/i $pc” ,当运行完这个命令后,单步跟踪会在打出程序代码的同时打出机器指令(也就是汇编代码)  查看栈信息当程序被停住了,你需要做的第一件事就是查看程序是在哪里停住的。当你的程序调用了一个函数,函数的地址,函数参数,函数内的局部变量都会被压入“栈”(Stack)中。你可以用GDB命令来查看当前的栈中的信息。backtrace 打印当前的函数调用栈的所有信息  frame <n> 查看某一层的信息  frame 会打印出这些信息:栈的层编号,当前的函数名,函数参数值,函数所在文件及行号,函数执行到的语句。  info f这个命令会打印出更为详细的当前栈层的信息,只不过,大多数都是运行时的内内地址。  info args 打印出当前函数的参数名及其值  info locals打印出当前函数中所有局部变量及其值。  info catch 打印出当前的函数中的异常处理信息。
  以上是关于gdb的一些使用说明,都很简单。下一次打算介绍一下如何查看运行过程中变量的值和内存地址,也就算是最常用的部分了,我姑且称之为高级篇,希望大家不要见笑。  注:如果希望了解更多关于gdb的知识,请看http://blog.csdn.net/haoel/article/details/2879



winston 发表于 2012-2-11 19:56:49

使用gdb进行调试高级篇

之前写过两篇科普文章,使用gdb调试程序入门篇和中级篇,今天打算把最后的一部分写完,就是所谓的高级篇。其实很简单,也没有多复杂。只是个人认为gdb的使用,掌握到这个地方,对于常规的调试需求就已经足够了。至于更高级的应用,肯定是多多益善。但是学习工具的目的就在于提高工作效率,没有必要为了技术而技术,技术永远都只是一种工具。不知道这个观点是否会被技术迷所鄙视,呵呵。不多废话了,言归正传。
查看运行时数据
在你调试程序时,当程序被停住时,你可以使用print命令(简写命令为p),或是同义命令inspect来查看当前程序的运行数据。print命令的格式是:   
     print <expr>
     print /<f> <expr>
       <expr>是表达式,是你所调试的程序的语言的表达式(GDB可以调试多种编程语言),<f>是输出的格式,比如,如果要把表达式按16进制的格式输出,那么就是/x。
有时候,你需要查看一段连续的内存空间的值。比如数组的一段,或是动态分配的数据的大小。你可以使用GDB的“@”操作符,“@”的左边是第一个内存的地址的值,“@”的右边则你你想查看内存的长度。例如,你的程序中有这样的语句:
  int *array = (int *) malloc (len * sizeof (int));
  于是,在GDB调试过程中,你可以以如下命令显示出这个动态数组的取值:
  p *array@len
  @的左边是数组的首地址的值,也就是变量array所指向的内容,右边则是数据的长度,其保存在变量len中,其输出结果,大约是下面这个样子的:   
    (gdb) p *array@len
    $1 = {2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40}  
  输出格式
  一般来说,GDB会根据变量的类型输出变量的值。但你也可以自定义GDB的输出的格式。例如,你想输出一个整数的十六进制,或是二进制来查看这个整型变量的中的位的情况。要做到这样,你可以使用GDB的数据显示格式:
   x按十六进制格式显示变量。
    d按十进制格式显示变量。
    u按十六进制格式显示无符号整型。
    o按八进制格式显示变量。
    t按二进制格式显示变量。
    a按十六进制格式显示变量。
    c按字符格式显示变量。
    f按浮点数格式显示变量。 (gdb) p i

      $21 = 101      
   
      (gdb) p/a i
      $22 = 0x65      
      (gdb) p/c
i
      $23 = 101 'e'      
      (gdb) p/f i
      $24 =
1.41531145e-43      
      (gdb) p/x i
      $25 = 0x65

      (gdb) p/t i
      $26 = 1100101
  查看内存
  你可以使用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会从指内存定的内存地址开始,读写指定字节,并把其当作一个值取出来。
    <addr>表示一个内存地址。
    n/f/u三个参数可以一起使用。例如:   
    命令:x/3uh 0x54320 表示,从内存地址0x54320读取内容,h表示以双字节为一个单位,3表示三个单位,u表示按十六进制显示。


  注:关于gdb调试更多的内容,请查看http://blog.csdn.net/haoel/article/details/2883。

页: [1]
查看完整版本: 使用gdb进行调试