XCODE GDB這個是老版本xcode,新版的是lldb

此文下半部分爲轉載:在此感謝原創者。html

---------------------shell

關於調試異常崩潰:小程序

通常崩潰是由內存使用錯誤致使的,要麼多了,要麼少了。xcode

xcode的調試提示能夠知道是什麼緣由致使的崩潰。app

xcodeproduct àedit scheme à diagnostics enable Zombie objects Malloc Stack 選中, 若是是內存釋放錯誤,則gdb會提示release dealloc object框架

而後能夠用斷點縮小錯誤範圍, 在可能出現錯誤的地方用單步調試, 當執行到有錯誤代碼時, gdb會再次提示 release dealloc object編輯器

其實XCODE內嵌GDB,那個 lldb就是gdb!函數

-----------------------工具

 

http://blog.csdn.net/ch_soft/article/details/7005998學習

關於GDB

對於大多數Cocoa程 序員來講,最經常使用的debugger莫過於Xcode自帶的調試工具了。而實際上,它正是gdb的一個圖形化包裝。相對於gdb,圖形化帶來了不少便利, 但同時也缺乏了一些重要功能。並且在某些狀況下,gdb反而更加方便。所以,學習gdb,瞭解一下幕後的實質,也是有必要的。

gdb能夠經過終端運行,也能夠在Xcode的控制檯調用命令。本文將經過終端講述一些gdb的基本命令和技巧。

首先,咱們來看一個例子:

#import

int main(int argc, char **argv)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSLog(@"Hello, world!");
[pool release];

return 0;
}


糟糕,程序居然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
9NSLog("Hello, world! x = %@", x);
這回不只是函數名,連出錯的那行代碼也打印出來了。可是,咱們還可使用list(簡寫爲l)命令,打印出更多信息:
ps: 若是須要回到棧列表。可使用down命令。

(gdb) l
5
6int main(int argc, char **argv)
7{
8NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
9int x = 42;
10NSLog("Hello, world! x = %@", x);
11[pool release];
12
13return 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
8NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
debugger在在咱們指望的地方停下了。如今咱們使用next(簡寫n)命令單步調試程序,看看它究竟是在哪一行崩潰的:

(gdb) n
9int x = 42;
(gdb)
10NSLog(@"Hello, world! x = %@", x);
(gdb)

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0x000000000000002a
0x00007fff84f102b3 in objc_msgSend_fixup ()
值得注意的是,我只鍵入了一次n命令,隨後直接敲了2次回車。這樣作的緣由是gdb把任何空輸入看成最近一次輸入命令的重複。因此這裏至關於輸入了3次n。

如今咱們能夠看到,崩潰之處依然是NSLog。緣由嘛,固然是在格式化輸出的地方用%@表示int型變量x了。咱們仔細看一下輸出信息:崩潰緣由是錯誤地 訪問了0x000000000000002a這個地址。而2a的十進制表示正是42--咱們爲x賦的值。編譯器把它看成地址了。

輸出數值

一個很重要的調試方法是輸出表達式和變量的值。在gdb中,這是經過print命令完成的。

(gdb) p x
$1 = 42

在print命令後追加/format能夠格式化輸出。/format是一個gdb的格式化字符串,比較有用的格式化字符有 x:十進制數; c:字符; a:地址等。

(gdb) p/x x
$2 = 0x2a

print-object方法(簡寫爲po)用來輸出obj-c中的對象。它的工做原理是,向被調用的對象發送名爲debugDescription的消息。它和常見的description消息很像。

舉例來講,讓咱們輸出一下autorelease pool:

(gdb) po pool


這個命令不只僅能夠輸出顯式定義的對象,也能夠輸出表達式的結果。此次咱們測試一下nsobject中debugDescription的方法簽名:


返回值是對象的表達式能夠用po命令輸出結果,那麼返回值是基本類型的方法又怎樣呢?顯然,它們是能夠用p命令輸出的。可是要當心,由於gdb並不能自動識別出返回值的類型。因此咱們在輸出前要顯式地轉換一下:

(gdb) p [NSObject instancesRespondToSelector: @selector(doesNotExist)]
Unable to call function "objc_msgSend" at 0x7fff84f100f4: no return type information available.
To call this function anyway, you can cast the return type explicitly (e.g. 'print (float) fabs (3.0)')
(gdb) p (char)[NSObject instancesRespondToSelector: @selector(doesNotExist)]
$5 = 0 '00'

你也許發現了,doesNotExist方法的返回值是BOOL,而咱們作的轉換倒是char。這是由於gdb也不能識別那些用typedef定義的類型。不只僅是你定義的,即便是Cocoa框架裏定義的也不行。

你也許已經注意到,在用p進行輸出的時侯,輸出值前面會有一個相似"$1="的前綴。它們是gdb變量。它們能夠在後面的表達式中使用,來指代它後面的 值。在下面的例子裏,咱們開闢了一塊內存,將其置零,而後釋放。在這個過程當中,咱們使用了gdb變量,這樣就不用一遍遍地複製粘貼地址了。

(gdb) p (int *)malloc(4)
$6 = (int *) 0x100105ab0
(gdb) p (void)bzero($6, 4)
$7 = void
(gdb) p *$6
$8 = 0
(gdb) p (void)free($6)
$9 = void

咱們也想把這個技巧用到對象上,但不幸的是po命令並不會把它的返回值存儲到變量裏。因此咱們在獲得一個新的對象時必須先使用p命令:

(gdb) p (void *)[[NSObject alloc] init]
$10 = (void *) 0x100105950
(gdb) po $10

(gdb) p (long)[$10 retainCount]
$11 = 1
(gdb) p (void)[$10 release]
$12 = void

檢查內存

有些時候,僅僅輸出一個數值還不能幫助咱們查找出錯誤。咱們須要一次性地打印出一整塊內存來窺視全局。這時候咱們就須要使用x命令。

x命令的格式是x/format address。其中address很簡單,它一般是指向一塊內存的表達式。可是format的語法就有點複雜了。它由三個部分組成:

第一個是要顯示的塊的數量;第二個是顯示格式(如x表明16進制,d表明十進制,c表明字符);第三個是每一個塊的大小。值得注意的是第三部分,即塊大小是用字符對應的。用b, h, w,g 分別表示1, 2, 4, 8 bytes。舉例來講,用十六進制方式,打印從ptr開始的4個4-byte塊應該這樣寫:

(gdb) x/4xw ptr

接下來舉一個比較實際的例子。咱們看一下NSObject類的內容:

(gdb) x/4xg (void *)[NSObject class]
0x7fff70adb468 : 0x00007fff70adb4400x0000000000000000
0x7fff70adb478 :0x0000000100105ac00x0000000100104ac0

接下來再看看一個NSObject實例的內容:

(gdb) x/1xg (void *)[NSObject new]
0x100105ba0:0x00007fff70adb468

如今咱們看到,在實例開頭引用了類的地址。

設置變量

有時,查看數值程度的能力仍是稍弱了一點,咱們還想可以修改變量。這也很簡單,只須要使用set命令:

(gdb) set x = 43

咱們能夠用任意表達式給一個變量賦值。好比說新建立一個對象而後賦值:

(gdb) set obj = (void *)[[NSObject alloc] init]

斷點

咱們能夠在程序的某個位置設置斷點,這樣當程序運行到那裏的時候就會暫停,而把控制權轉移給調試器。就像以前提到的,咱們用break命令來設置斷點。下面詳細地列出瞭如何設置斷點的目標:

SymbolName: 爲斷點指定一個函數名。這樣斷點就會設置在該函數上。
file.c:1234: 把斷點設置在指定文件的一行。
-[ClassName method:name:]: 把斷點設置在objc的方法上。用+表明類方法。
*0xdeadbeef: 在內存的指定位置設置斷點。這不是很經常使用,通常在沒有源碼的調試時使用。

斷點能夠用enable命令和disable命令來切換到使用和停用狀態,也能夠經過delete命令完全刪除。想要查看現有斷點的話,使用info breakpoints命令(能夠簡寫成info b,或是i b)。

另外,咱們也能夠用if命令,把斷點升級成條件斷點。顧名思義,條件斷點只會在設定的條件成真時起做用。舉例來講,下面的語句爲MyMethod添加了一個條件斷點,它只在參數等於5的時候有效:

(gdb) b -[Class myMethod:] if parameter == 5

最後,在斷點上能夠附加gdb命令。這樣,當斷點中斷時,附帶的命令會自動執行。附加命令使用commands breakpointnumber。這時gdb就會進入斷點指令輸入狀態。

斷點指令就是一個以end結尾的標準gdb指令序列。舉個例子,咱們想在每次NSLog被調用時輸出棧信息:

(gdb) b NSLog
Breakpoint 4 at 0x7fff87beaa62
(gdb) commands
Type commands for when breakpoint 4 is hit, one per line.
End with a line saying just "end".
>bt
>end

 

2)其餘

XCode 內置GDB,咱們能夠在命令行中使用 GDB 命令來調試咱們的程序。下面將介紹一些經常使用的命令以及調試技巧。

po 命令:爲 print object 的縮寫,顯示對象的文本描述(顯示從對象的 description 消息得到的字符串信息)。

好比:

上圖中,我使用 po 命令顯示一個 NSDictionary 的內容。注意在左側咱們能夠看到 dict 的一些信息:3 key/value pairs,顯示該 dict 包含的數據量,而展開的信息顯示 isa 層次體系(即class 和 metaclass結構關係)。咱們能夠右擊左側的 dict,選中「Print Description of "dict"」,則能夠在控制檯輸出 dict 的詳細信息:

[cpp] view plain copy

print?

  1. Printing description of dict:  
  2. <CFBasicHash 0x1001149e0 [0x7fff7e27ff40]>{type = immutable dict, count = 3,  
  3. entries =>  
  4.     0 : <CFString 0x100002458 [0x7fff7e27ff40]>{contents = "first"} = <CFString 0x100002438 [0x7fff7e27ff40]>{contents = "one"}  
  5.     1 : <CFString 0x100002498 [0x7fff7e27ff40]>{contents = "second"} = <CFString 0x100002478 [0x7fff7e27ff40]>{contents = "two"}  
  6.     2 : <CFString 0x1000024d8 [0x7fff7e27ff40]>{contents = "third"} = <CFString 0x1000024b8 [0x7fff7e27ff40]>{contents = "three"}  
  7. }  
  8. (gdb)   


print 命令:有點相似於格式化輸出,能夠輸出對象的不一樣信息:

如:

[cpp] view plain copy

print?

  1. (gdb) print (char *)[[dict description] cStringUsingEncoding:4]  
  2. $1 = 0x1001159c0 "{\n    first = one;\n    second = two;\n    third = three;\n}"  
  3. (gdb) print (int)[dict retainCount]  
  4. $2 = 1  
  5. (gdb)   

 

注:4是 NSUTF8StringEncoding 的值。

info 命令:咱們能夠查看內存地址所在信息

好比 "info symbol 內存地址" 能夠獲取內存地址所在的 symbol 相關信息:

[cpp] view plain copy

print?

  1. (gdb) info symbol 0x00000001000017f7  
  2. main + 343 in section LC_SEGMENT.__TEXT.__text of /Users/LuoZhaohui/Library/Developer/Xcode/DerivedData/RunTimeSystem-anzdlhiwvlbizpfureuvenvmatnp/Build/Products/Debug/RunTimeSystem  

 

好比 "info line *內存地址" 能夠獲取內存地址所在的代碼行相關信息:

 

[cpp] view plain copy

print?

  1. (gdb) info line *0x00000001000017f7  
  2. Line 62 of "/Users/LuoZhaohui/Documents/Study/RunTimeSystem/RunTimeSystem/main.m" starts at address 0x1000017f7 <main+343> and ends at 0x10000180a <main+362>.  

 

show 命令:顯示 GDB 相關的信息。如:show version 顯示GDB版本信息

[cpp] view plain copy

print?

  1. (gdb) show version  
  2. GNU gdb 6.3.50-20050815 (Apple version gdb-1708) (Mon Aug  8 20:32:45 UTC 2011)  
  3. Copyright 2004 Free Software Foundation, Inc.  
  4. GDB is free software, covered by the GNU General Public License, and you are  
  5. welcome to change it and/or distribute copies of it under certain conditions.  
  6. Type "show copying" to see the conditions.  
  7. There is absolutely no warranty for GDB.  Type "show warranty" for details.  
  8. This GDB was configured as "x86_64-apple-darwin".  


help 命令:若是忘記某條命令的語法了,可使用 help 命令名 來獲取幫助信息。如:help info 顯示 info 命令的用法。

[cpp] view plain copy

print?

  1. (gdb) help info  
  2. Generic command for showing things about the program being debugged.  
  3.   
  4. List of info subcommands:  
  5.   
  6. info address -- Describe where symbol SYM is stored  
  7. info all-registers -- List of all registers and their contents  
  8. info args -- Argument variables of current stack frame  
  9. info auxv -- Display the inferior's auxiliary vector  
  10. info breakpoints -- Status of user-settable breakpoints  
  11. info catch -- Exceptions that can be caught in the current stack frame  
  12. info checkpoints -- Help  
  13. info classes -- All Objective-C classes  
  14. ......  
  15.   
  16. Type "help info" followed by info subcommand name for full documentation.  
  17. Command name abbreviations are allowed if unambiguous.  
  18. (gdb)   

在系統拋出異常處設置斷點

有時候咱們的程序不知道跑到哪一個地方就 crash 了,而 crash 又很難重現。保守的作法是在系統拋出異常以前設置斷點,具體來講是在 objc_exception_throw處設置斷點。設置步驟爲:首先在 XCode 按 CMD + 6,進入斷點管理窗口;而後點擊右下方的 +,增長新的 Symbolic Breakpoint,在 Symbol 一欄輸入:objc_exception_throw,而後點擊 done,完成。 這樣在 Debug 模式下,若是程序即將拋出異常,就能在拋出異常處中斷了。好比在前面的代碼中,我讓 [firstObjctcrashTest]; 拋出異常。在 objc_exception_throw 處設置斷點以後,程序就能在該代碼處中斷了,咱們從而知道代碼在什麼地方出問題了。

 

相關文章
相關標籤/搜索