XCode中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 
          NSLog("Hello, world! x = %@", x);
這回不僅是函數名,連出錯的那行代碼也打印出來了。但是,我們還可以使用list(簡寫爲l)命令,打印出更多信息:
ps: 如果需要回到棧列表。可以使用down命令。

    (gdb) l 
      
      int main(int argc, char **argv) 
      { 
          NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
         

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章