现在的位置: 首页 > 综合 > 正文

GDB的调试技巧

2013年02月11日 ⁄ 综合 ⁄ 共 4152字 ⁄ 字号 评论关闭

转载:http://www.cocoachina.com/bbs/read.php?tid=66525

原作地址:http://www.mikeash.com/pyblog/friday-qa-2011-06-17-gdb-tips-and-tricks.html 

 
作者:mikeash 
 
这是作者Friday Q&A 中的一篇。觉得很好于是翻译了一下。 
 
\*********************************************/ 
 
 
 
关于GDB 
 
对于大多数Cocoa程序员来说,最常用的debugger莫过于Xcode自带的调试工具了。而实际上,它正是gdb的一个图形化包装。相对于gdb,图形化带来了很多便利,但同时也缺少了一些重要功能。而且在某些情况下,gdb反而更加方便。因此,学习gdb,了解一下幕后的实质,也是有必要的。 
 
gdb可以通过终端运行,也可以在Xcode的控制台调用命令。本文将通过终端讲述一些gdb的基本命令和技巧。 
 
首先,我们来看一个例子: 
 
    #import <Foundation/Foundation.h>  
 
    int main(int argc, char **argv)  
    {  
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];  
        NSLog(@"Hello, world!");  
        [pool release];  
 
        return 0;  
    } 
 
我们把文件命名为test.m,然后编译: 
 
    gcc -g -framework Foundation test.m 
 
准备工作已经完成。现在我们可以开始调试了。只要把要调试的文件名作为参数,启动gdb: 
 
    gdb a.out 
 
gdb启动后会输出很多法律声明之类的信息。无视它们,最后我们看到一个提示: 
 
    (gdb) 
 
成功!现在debugger和刚才编译好的程序都被装载了。不过,现在程序还没有开始运行。因为gdb在程序开始前把它暂停了,好让我们有机会设置调试参数。这次我们不需要做特别设置,所以马上开始运行吧: 
 
    (gdb) run  
 
    Starting program: /Users/mikeash/shell/a.out  
    Reading symbols for shared libraries .++++....................... done  
    2011-06-16 20:28:53.658 a.out[2946:a0f] Hello, world!  
 
    Program exited normally.  
    (gdb) 
 
糟糕,程序竟然exited normally了(==|||)。这可不行,我们得让他崩溃才行。所以我们给这个小程序添加一个bug: 
 
    int x = 42;  
    NSLog("Hello, world! x = %@", x); 
nice。这样一来程序就会漂亮地崩溃了: 
 
    (gdb) run  
 
    Starting program: /Users/mikeash/shell/a.out  
    Reading symbols for shared libraries .++++....................... done  
 
    Program received signal EXC_BAD_ACCESS, Could not access memory.  
    Reason: 13 at address: 0x0000000000000000  
    0x00007fff84f1011c in objc_msgSend ()  
    (gdb) 
 
如果我们是在shell中直接运行的程序,在崩溃后就会回到shell。不过现在我们是通过gdb运行的,所以现在并没有跳出。gdb暂停了我们的程序,但依然使之驻留在内存中,让我们有机会做调试。 
 
首先,我们想知道具体是哪里导致了程序崩溃。gdb已经通过刚才的输出告知了我们: 函数objc_msgSend就是祸之根源。但是这个信息并不足够,因为这个objc_msgSend是objc运行时库中的函数。我们并不知道它是怎么调用的。我们关注的是我们自己的代码。 
要知道这一点,我们需要得到当前进程的函数调用栈的情况,以此回溯找到我们自己的方法。这时我们需要用到backtrace命令,一般简写为bt: 
 
    (gdb) bt  
    #0 0x00007fff84f1011c in objc_msgSend ()  
    #1 0x00007fff864ff30b in _CFStringAppendFormatAndArgumentsAux ()  
    #2 0x00007fff864ff27d in _CFStringCreateWithFormatAndArgumentsAux ()  
    #3 0x00007fff8657e9ef in _CFLogvEx ()  
    #4 0x00007fff87beab3e in NSLogv ()  
    #5 0x00007fff87beaad6 in NSLog ()  
    #6 0x0000000100000ed7 in main () at test.m:10 
 
现在我们可以看到,程序在test.m的第10行,调用NSLog方法时崩溃了。接下来我们想看一下这次调用的详细信息。这时我们要用到up命令。up命令可以在栈的各层之间跳转。本例中,我们的代码main是#6: 
 
    (gdb) up 6  
    #6 0x0000000100000ed7 in main () at test.m:10  
    9       NSLog("Hello, world! x = %@", x); 
这回不仅是函数名,连出错的那行代码也打印出来了。但是,我们还可以使用list(简写为l)命令,打印出更多信息: 
ps: 如果需要回到栈列表。可以使用down命令。 
 
    (gdb) l  
    5    
    6   int main(int argc, char **argv)  
    7   {  
    8       NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];  
    9       int x = 42;  
    10      NSLog("Hello, world! x = %@", x);  
    11      [pool release];  
    12       
    13      return 0;  
    14  } 
 
啊,整个代码都被列出来了。虽然我们用编辑器打开test.m文件然后找到第10行也可以打到同样效果,但显然没有上面的方法更有效率。(当然没有Xcode自带的那个快就是了) 
 
好了,现在我们再来看看这个bug(虽然是我们自己弄出来的)。很明显,在格式化字符串前少加了一个@。我们改正它,并重新运行一遍程序: 
 
    (gdb) run  
    Starting program: /Users/mikeash/shell/a.out  
    Reading symbols for shared libraries .++++....................... done  
 
    Program received signal EXC_BAD_ACCESS, Could not access memory.  
    Reason: KERN_INVALID_ADDRESS at address: 0x000000000000002a  
    0x00007fff84f102b3 in objc_msgSend_fixup ()  
    (gdb) bt  
    #0 0x00007fff84f102b3 in objc_msgSend_fixup ()  
    #1 0x0000000000000000 in ?? () 
 
啊咧,程序还是崩溃了。更杯具的是,栈信息没有显示出这个objc_msgSend_fixup方法是从哪里调用的。这样我们就没法用上面的方法找到目标代码了。这时,我们只好请出一个debugger最常用的功能:断点。 
 
在gdb中,设置断点通过break命令实现。它可以简写为b。有两种方法可以确定断点的位置:传入一个已定义的符号,或是直接地通过一个file:line对设置位置。 
现在让我们在main函数的开始处设置一个断点: 
 
    (gdb) b test.m:8  
    Breakpoint 1 at 0x100000e8f: file test.m, line 8. 
 
debugger给了我们一个回应,告诉我们断点设置成功了,而且这个断点的标号是1。断点的标号很有用,可以用来给断点排序&停用&启用&删除等。不过我们现在不需要理会,我们只是接着运行程序: 
 
    (gdb) run  
    The program being debugged has been started already.  
    Start it from the beginning? (y or n) y  
    Starting program: /Users/mikeash/shell/a.out  
 
    Breakpoint 1, main (argc=1, argv=0x7fff5fbff628) at test.m:8  
    8       NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
debugger在在我们期望的地方停下了。现在我们使用next(简写n)命令单步调试程序,看看它到底是在哪一行崩溃的: 
 
    (gdb) n  
    9       int x = 42;  
    (gdb)  
    10      NSLog(@"Hello, world! x = %@", x);  
    (gdb) 

抱歉!评论已关闭.