隨著Xcode 5的發(fā)布,LLDB調(diào)試器已經(jīng)取代了GDB宿亡,成為了Xcode工程中默認的調(diào)試器常遂。它與LLVM編譯器一起,帶給我們更豐富的流程控制和數(shù)據(jù)檢測的調(diào)試功 能挽荠。LLDB為Xcode提供了底層調(diào)試環(huán)境克胳,其中包括內(nèi)嵌在Xcode IDE中的位于調(diào)試區(qū)域的控制面板,在這里我們可以直接調(diào)用LLDB命令圈匆。如圖1所示:
圖1:位于Xcode調(diào)試區(qū)域的控制臺
在本文中漠另,我們主要整理一下LLDB調(diào)試器提供給我們的調(diào)試命令,更詳細的內(nèi)容可以查看The LLDB Debugger跃赚。
LLDB命令結(jié)構(gòu)
在使用LLDB前笆搓,我們需要了解一下LLDB的命令結(jié)構(gòu)及語法,這樣可以盡可能地挖掘LLDB的潛能纬傲,以幫助我們更充分地利用它满败。
LLDB命令的語法有其通用結(jié)構(gòu),通常是以下形式的:
1
?[?[...]]??[-options?[option-value]]?[argument?[argument...]]
其中:
(命令)和(子命令):LLDB調(diào)試命令的名稱叹括。命令和子命令按層級結(jié)構(gòu)來排列:一個命令對象為跟隨其的子命令對象創(chuàng)建一個上下文算墨,子命令又為其子命令創(chuàng)建一個上下文,依此類推汁雷。
:我們想在前面的命令序列的上下文中執(zhí)行的一些操作净嘀。
:行為修改器(action modifiers)报咳。通常帶有一些值。
:根據(jù)使用的命令的上下文來表示各種不同的東西面粮。
LLBD命令行的解析操作在執(zhí)行命令之前完成少孝。上面的這些元素之間通過空格來分割,如果某一元素自身含有空格熬苍,則可以使用雙引用稍走。而如果元素中又包含雙引號,則可以使用反斜杠柴底;或者元素使用單引號婿脸。如下所示:
1
2(lldb)?command?[subcommand]?-option"some?\"quoted\"?string"
(lldb)?command?[subcommand]?-option'some?"quoted"?string'
這種命令解析設(shè)計規(guī)范了LLDB命令語法,并對所有命令做了個統(tǒng)一柄驻。
命令選項
LLDB中的命令選項有規(guī)范形式和縮寫形式兩種格式狐树。以設(shè)置斷點的命令breakpoint set為例,以下列表了其部分選項的格式鸿脓,其中括號中的是規(guī)范形式:
1
2
3
4
5
6
7
8breakpoint?set
-M??(?--method??)
-S??(?--selector??)
-b??(?--basename??)
-f??(?--file??)
-l??(?--line??)
-n??(?--name??)
…
各選項的順序是任意的抑钟。如果后面的參數(shù)是以”–“開頭的,則在選項后面添加”—“作為選項的終止信號野哭,以告訴LLDB我們處理的選項的正確位置在塔。如下命令所示:
1
(lldb)?process?launch?--stop-at-entry?--?-program_arg_1?value?-program_arg_2?value
如上所示,命令的選項是—stop-at-entry拨黔,參數(shù)是-program_arg_1和-program_arg_2蛔溃,我們使用”—“將選項與參數(shù)作一下區(qū)分。
原始命令
LLDB命令解析器支持”原始(raw)“命令篱蝇,即沒有命令選項贺待,命令字符串的剩余部分未經(jīng)解析就傳遞給命令。例如零截,expression就是一個原始命令麸塞。
不過原始命令也可以有選項,如果命令字符串中有虛線涧衙,則在命令名與命令字符串之間放置一個選項結(jié)束符(—)來表明沒有命令標記喘垂。
我們可以通過help命令的輸出來查看一個命令是否是原始命令。
命令補全(Command Completion)
LLDB 支持源文件名绍撞,符號名,文件名得院,等等的命令補全(Commmand Completion)傻铣。終端窗口中的補全是通過在命令行中輸入一個制表符來初始化的。Xcode控制臺中的補全與在源碼編輯器中的補全方式是一樣的:補 全會在第三個字符被鍵入時自動彈出祥绞,或者通過Esc鍵手動彈出非洲。
一個命令中的私有選項可以有不同的完成者(completers)鸭限。如 breakpoint中的—file 選項作為源文件的完成者,—shlib 選項作為當前加載的庫的完成者两踏,等等败京。這些行為是特定的,例如梦染,如果指定—shlib 赡麦,且以—file 結(jié)尾,則LLDB只會列出由—shlib 指定的共享類庫帕识。
Python腳本
對于高級用戶來說泛粹,LLDB有一個內(nèi)置的Python 解析器,可以通過腳本命令來訪問肮疗。調(diào)試器中的所有特性在Python解析器中都可以作為類來訪問晶姊。這樣,我們就可以使用LLDB-Python庫來寫 Python函數(shù)伪货,并通過腳本將其加載到運行會話中们衙,以執(zhí)行一些更復雜的調(diào)試操作。
在命令行中調(diào)試程序
通常我們都是在Xcode中直接使用LLDB調(diào)試器碱呼,Xcode會幫我們完成很多操作蒙挑。當然,如果我們想讓自己看著更Bigger巍举,或者想了解下調(diào)試器具體的一些流程脆荷,就可以試試直接在終端使用LLDB命令來調(diào)試程序。在終端中使用LLDB調(diào)試器懊悯,我們需要了解以下內(nèi)容:
1.加載程序以備調(diào)試
2.將一個運行的程序綁定到LLDB
3.設(shè)置斷點和觀察點
4.控制程序的執(zhí)行
5.在調(diào)試的程序中導航
6.檢查狀態(tài)和值的變量
7.執(zhí)行替代代碼
了解在終端中這些操作是如何進行的蜓谋,可以幫助我們更深入的了解調(diào)試器在Xcode中是如何運作的。下面我們分步來介紹一下炭分。
指定需要調(diào)試的程序
首先我們需要設(shè)置需要調(diào)試的程序桃焕。我們可以使用如下命令做到這一點:
1
2$?lldb?/Projects/Sketch/build/Debug/Sketch.app
Current?executable?set?to'/Projects/Sketch/build/Debug/Sketch.app'(x86_64).
或者在運行l(wèi)ldb后,使用file命令來處理捧毛,如下所示:
1
2
3$?lldb
(lldb)?file?/Projects/Sketch/build/Debug/Sketch.app
Current?executable?set?to'/Projects/Sketch/build/Debug/Sketch.app'(x86_64).
設(shè)置斷點
在設(shè)置完程序后观堂,我們可能想設(shè)置一點斷點來調(diào)試程序。此時我們可以使用breakpoint set命令來設(shè)置斷點呀忧,這個命令簡單师痕、直觀,且有智能補全而账,接下來我們看看它的具體操作胰坟。
如果想在某個文件中的某行設(shè)置一個斷點,可使用以下命令:
1
(lldb)?breakpoint?set?--file?foo.c?--line?12
如果想給某個函數(shù)設(shè)置斷點泞辐,可使用以下命令:
1
(lldb)?breakpoint?set?--name?foo
如果想給C++中所有命名為foo的方法設(shè)置斷點笔横,可以使用以下命令:
1
(lldb)?breakpoint?set?--method?foo
如果想給Objective-C中所有命名為alignLeftEdges:的選擇器設(shè)置斷點竞滓,則可以使用以下命令:
1
(lldb)?breakpoint?set?--selector?alignLeftEdges:
我們可以使用—shlib 來將斷點限定在一個特定的可執(zhí)行庫中:
1
(lldb)?breakpoint?set?--shlib?foo.dylib?--name?foo
看吧,斷點設(shè)置命令還是很強大的吹缔。
如果我們想查看程序中所有的斷點商佑,則可以使用breakpoint list命令,如下所示:
1
2
3
4(lldb)?breakpoint?list
Current?breakpoints:
1:?name?='alignLeftEdges:',?locations?=?1,?resolved?=?1
1.1:?where?=?Sketch`-[SKTGraphicView?alignLeftEdges:]?+?33?at?/Projects/Sketch/SKTGraphicView.m:1405,?address?=?0x0000000100010d5b,?resolved,?hit?count?=?0
從上面的輸出結(jié)果可以看出厢塘,一個斷點一般有兩部分:
1.斷點的邏輯規(guī)范茶没,這一部分是用戶提供給breakpoint set命令的。
2.與規(guī)范匹配的斷點的位置俗冻。
如上所示礁叔,通過”breakpoint set —selector alignLeftEdges:“設(shè)置的斷點,其信息中會顯示出所有alignLeftEdges:方法的位置迄薄。
breakpoint list命令輸出列表顯示每個邏輯斷點都有一個整數(shù)標識琅关,如上所示斷點標識為1。而每個位置也會有一個標識讥蔽,如上所示的1.1涣易。
輸出列表中另一個信息是斷點位置是否是已解析的(resolved)。這個標識表示當與之相關(guān)的文件地址被加載到程序進行調(diào)試時冶伞,其位置是已解析的新症。例如,如果在共享庫中設(shè)置的斷點之后被卸載了响禽,則斷點的位置還會保留徒爹,但其不能再被解析。
不管是邏輯斷點產(chǎn)生的所有位置芋类,還是邏輯斷點解析的任何特定位置隆嗅,我們都可以使用斷點觸發(fā)命令來對其進行刪除、禁用侯繁、設(shè)置條件或忽略計數(shù)操作胖喳。例如,如果我們想添加一個命令贮竟,以在LLDB命中斷點1.1時打印跟蹤棧丽焊,則可以執(zhí)行以下命令
1
2
3
4(lldb)?breakpoint?command?add?1.1
Enter?your?debugger?command(s).?Type'DONE'to?end.
>?bt
>?DONE
如果想更詳細地了解”breakpoint command add”命令的使用,可以使用help幫助系統(tǒng)來查看咕别。
設(shè)置觀察點
作為斷點的補充技健,LLDB支持觀察點以在不中斷程序運行的情況下監(jiān)測一些變量。例如惰拱,我們可以使用以下命令來監(jiān)測名為global的變量的寫操作雌贱,并在(global==5)為真時停止監(jiān)測:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40(lldb)?watch?setvarglobal
Watchpoint?created:?Watchpoint?1:?addr?=?0x100001018?size?=?4?state?=?enabled?type?=?w
declare?@'/Volumes/data/lldb/svn/ToT/test/functionalities/watchpoint/watchpoint_commands/condition/main.cpp:12'
(lldb)?watch?modify?-c'(global==5)'
(lldb)?watch?list
Current?watchpoints:
Watchpoint?1:?addr?=?0x100001018?size?=?4?state?=?enabled?type?=?w
declare?@'/Volumes/data/lldb/svn/ToT/test/functionalities/watchpoint/watchpoint_commands/condition/main.cpp:12'
condition?='(global==5)'
(lldb)?c
Process?15562?resuming
(lldb)?about?to?write?to'global'...
Process?15562?stopped?and?was?programmatically?restarted.
Process?15562?stopped?and?was?programmatically?restarted.
Process?15562?stopped?and?was?programmatically?restarted.
Process?15562?stopped?and?was?programmatically?restarted.
Process?15562?stopped
*?thread#1:?tid?=?0x1c03,?0x0000000100000ef5?a.out`modify?+?21?at?main.cpp:16,?stop?reason?=?watchpoint?1
frame#0:?0x0000000100000ef5?a.out`modify?+?21?at?main.cpp:16
13
14??????static?void?modify(int32_t?&var)?{
15??????????++var;
->?16??????}
17
18??????int?main(int?argc,?char**?argv)?{
19??????????int?local?=?0;
(lldb)?bt
*?thread#1:?tid?=?0x1c03,?0x0000000100000ef5?a.out`modify?+?21?at?main.cpp:16,?stop?reason?=?watchpoint?1
frame#0:?0x0000000100000ef5?a.out`modify?+?21?at?main.cpp:16
frame#1:?0x0000000100000eac?a.out`main?+?108?at?main.cpp:25
frame#2:?0x00007fff8ac9c7e1?libdyld.dylib`start?+?1
(lldb)?framevarglobal
(int32_t)?global?=?5
(lldb)?watch?list?-v
Current?watchpoints:
Watchpoint?1:?addr?=?0x100001018?size?=?4?state?=?enabled?type?=?w
declare?@'/Volumes/data/lldb/svn/ToT/test/functionalities/watchpoint/watchpoint_commands/condition/main.cpp:12'
condition?='(global==5)'
hw_index?=?0??hit_count?=?5?????ignore_count?=?0
(lldb)
可以使用help watchpoint來查看該命令的使用。
使用LLDB來啟動程序
一旦指定了調(diào)試哪個程序,并為其設(shè)置了一些斷點后帽芽,就可以開始運行程序了。我們可以使用以下命令來啟動程序:
1
2
3(lldb)?process?launch
(lldb)?run
(lldb)?r
我 們同樣可以使用進程ID或進程名來連接一個已經(jīng)運行的程序翔冀。當使用名稱來連接一個程序時导街,LLDB支持—waitfor選項。這個選項告訴LLDB等待下 一個名稱為指定名稱的程序出現(xiàn)纤子,然后連接它搬瑰。例如,下面3個命令都是用于連接Sketch程序(假定其進程ID為123):
1
2
3(lldb)?process?attach?--pid?123
(lldb)?process?attach?--name?Sketch
(lldb)?process?attach?--name?Sketch?--waitfor
啟動或連接程序后控硼,進程可能由于某些原因而停止泽论,如:
1
2
3
4
5
6(lldb)?process?attach?-p?12345
Process?46915?Attaching
Process?46915?Stopped
1?of?3?threads?stoppedwithreasons:
*?thread#1:?tid?=?0x2c03,?0x00007fff85cac76a,?where?=?libSystem.B.dylib`__getdirentries64?+?10,
stop?reason?=?signal?=?SIGSTOP,?queue?=?com.apple.main-thread
注意“1 of 3 threads stopped with reasons:”及其下面一行。在多線程環(huán)境下卡乾,在內(nèi)核實際返回控制權(quán)給調(diào)試器前翼悴,可能會有多個線程命中同一個斷點。在這種情況下幔妨,我們可以在停止信息中看到所有因此而停止的線程鹦赎。
控制程序
啟動程序后,LLDB允許程序在到達斷點前繼續(xù)運行误堡。LLDB中流程控制的命令都在thread命令層級中古话。如下所示:
1
2
3(lldb)?threadcontinue
Resuming?thread?0x2c03inprocess?46915
Resuming?process?46915
另外,還有以下命令:
1
2
3
4
5(lldb)?thread?step-in//?The?same?as?"step"?or?"s"?in?GDB.
(lldb)?thread?step-over//?The?same?as?"next"?or?"n"?in?GDB.
(lldb)?thread?step-out//?The?same?as?"finish"?or?"f"?in?GDB.
(lldb)?thread?step-inst//?The?same?as?"stepi"?/?"si"?in?GDB.
(lldb)?thread?step-over-inst//?The?same?as?"nexti"?/?"ni"?in?GDB.
LLDB還提供了run until line按步調(diào)度模式锁施,如:
1
lldb)?thread?until?100
這條命令會運行線程陪踩,直到當前frame到達100行。如果代碼在運行的過程中跳過了100行悉抵,則當frame被彈出棧后終止執(zhí)行肩狂。
查看線程狀態(tài)
在進程停止后,LLDB會選擇一個當前線程和線程中當前幀(frame)基跑。很多檢測狀態(tài)的命令可以用于這個線程或幀婚温。
為了檢測進程的當前狀態(tài),可以從以下命令開始:
1
2
3
4
5(lldb)?thread?list
Process?46915?state?is?Stopped
*?thread#1:?tid?=?0x2c03,?0x00007fff85cac76a,?where?=?libSystem.B.dylib`__getdirentries64?+?10,?stop?reason?=?signal?=?SIGSTOP,?queue?=?com.apple.main-thread
thread#2:?tid?=?0x2e03,?0x00007fff85cbb08a,?where?=?libSystem.B.dylib`kevent?+?10,?queue?=?com.apple.libdispatch-manager
thread#3:?tid?=?0x2f03,?0x00007fff85cbbeaa,?where?=?libSystem.B.dylib`__workq_kernreturn?+?10
星號(*)表示thread #1為當前線程媳否。為了獲取線程的跟蹤棧栅螟,可以使用以下命令:
1
2
3
4
5
6
7
8
9
10
11
12
13(lldb)?thread?backtrace
thread#1:?tid?=?0x2c03,?stop?reason?=?breakpoint?1.1,?queue?=?com.apple.main-thread
frame#0:?0x0000000100010d5b,?where?=?Sketch`-[SKTGraphicView?alignLeftEdges:]?+?33?at?/Projects/Sketch/SKTGraphicView.m:1405
frame#1:?0x00007fff8602d152,?where?=?AppKit`-[NSApplication?sendAction:to:from:]?+?95
frame#2:?0x00007fff860516be,?where?=?AppKit`-[NSMenuItem?_corePerformAction]?+?365
frame#3:?0x00007fff86051428,?where?=?AppKit`-[NSCarbonMenuImpl?performActionWithHighlightingForItemAtIndex:]?+?121
frame#4:?0x00007fff860370c1,?where?=?AppKit`-[NSMenu?performKeyEquivalent:]?+?272
frame#5:?0x00007fff86035e69,?where?=?AppKit`-[NSApplication?_handleKeyEquivalent:]?+?559
frame#6:?0x00007fff85f06aa1,?where?=?AppKit`-[NSApplication?sendEvent:]?+?3630
frame#7:?0x00007fff85e9d922,?where?=?AppKit`-[NSApplication?run]?+?474
frame#8:?0x00007fff85e965f8,?where?=?AppKit`NSApplicationMain?+?364
frame#9:?0x0000000100015ae3,?where?=?Sketch`main?+?33?at?/Projects/Sketch/SKTMain.m:11
frame#10:?0x0000000100000f20,?where?=?Sketch`start?+?52
如果想查看所有線程的調(diào)用棧,則可以使用以下命令:
1
(lldb)?thread?backtrace?all
查看調(diào)用棧狀態(tài)
檢查幀參數(shù)和本地變量的最簡便的方式是使用frame variable命令:
1
2
3
4
5
6
7(lldb)?frame?variable
self?=?(SKTGraphicView?*)?0x0000000100208b40
_cmd?=?(struct?objc_selector?*)?0x000000010001bae1
sender?=?(id)?0x00000001001264e0
selection?=?(NSArray?*)?0x00000001001264e0
i?=?(NSUInteger)?0x00000001001264e0
c?=?(NSUInteger)?0x00000001001253b0
如果沒有指定任何變量名篱竭,則會顯示所有參數(shù)和本地變量力图。如果指定參數(shù)名或變量名,則只打印指定的值掺逼。如:
1
2(lldb)?frame?variable?self
(SKTGraphicView?*)?self?=?0x0000000100208b40
frame variable命令不是一個完全的表達式解析器吃媒,但它支持一些簡單的操作符,如&,*,–>,[]。這個數(shù)組括號可用于指針赘那,以將指針作為數(shù)組處理刑桑。如下所示:
1
2
3
4
5
6
7
8
9(lldb)?frame?variable?*self
(SKTGraphicView?*)?self?=?0x0000000100208b40
(NSView)?NSView?=?{
(NSResponder)?NSResponder?=?{
...
(lldb)?frame?variable?&self
(SKTGraphicView?**)?&self?=?0x0000000100304ab
(lldb)?frame?variable?argv[0]
(char?const?*)?argv[0]?=?0x00007fff5fbffaf8"/Projects/Sketch/build/Debug/Sketch.app/Contents/MacOS/Sketch"
frame variable命令會在變量上執(zhí)行”對象打印”操作。目前募舟,LLDB只支持Objective-C打印祠斧,使用的是對象的description方法。
如果想查看另外一幀拱礁,可以使用frame select命令琢锋,如下所示:
1
2(lldb)?frame?select?9
frame#9:?0x0000000100015ae3,?where?=?Sketch`function1?+?33?at?/Projects/Sketch/SKTFunctions.m:11
小結(jié)
以上所介紹的命令可以讓我們在終端中直接調(diào)試程序。當然呢灶,很多命令也可以在Xcode中直接使用吴超。這些命令可以讓我們了解程序運行的狀態(tài),當然有些狀態(tài)可以在Xcode中了解到鸯乃。建議在調(diào)試過程中鲸阻,可以多使用這些命令。
如果想了解這一過程中使用的各種命令飒责,可以查看蘋果的官方文檔赘娄。
在Xcode中調(diào)試程序
對于我們?nèi)粘5拈_發(fā)工作來說,更多的時候是在Xcode中進行調(diào)試工作宏蛉。因此上面所描述的流程遣臼,其實Xcode已經(jīng)幫我們完成了大部分的工作,而且很多東西也可以在Xcode里面看到拾并。因此揍堰,我們可以把精力都集中在代碼層面上。
在蘋果的官方文檔中列出了我們在調(diào)試中能用到的一些命令嗅义,我們在這重點講一些常用的命令屏歹。
打印
打印變量的值可以使用print命令,該命令如果打印的是簡單類型之碗,則會列出簡單類型的類型和值蝙眶。如果是對象,還會打印出對象指針地址褪那,如下所示:
1
2
3
4
5
6
7
8(lldb)?print?a
(NSInteger)?$0?=?0
(lldb)?print?b
(NSInteger)?$1?=?0
(lldb)?print?str
(NSString?*)?$2?=?0x0000000100001048?@"abc"
(lldb)?print?url
(NSURL?*)?$3?=?0x0000000100206cc0?@"abc"
在輸出結(jié)果中我們還能看到類似于$0,$1這樣的符號幽纷,我們可以將其看作是指向?qū)ο蟮囊粋€引用,我們在控制面板中可以直接使用這個符號來操作對應的對象博敬,這些東西存在于LLDB的全名空間中友浸,目的是為了輔助調(diào)試。如下所示:
1
2
3
4(lldb)?exp $0?=?100
(NSInteger)?$9?=?100
(lldb)?p?a
(NSInteger)?$10?=?100
另外$后面的數(shù)值是遞增的偏窝,每打印一個與對象相關(guān)的命令收恢,這個值都會加1武学。
上面的print命令會打印出對象的很多信息,如果我們只想查看對象的值的信息伦意,則可以使用po(print object的縮寫)命令火窒,如下所示:
1
2(lldb)?po?str
abc
當然,po命令是”exp -O —“命令的別名驮肉,使用”exp -O —”能達到同樣的效果沛鸵。
對于簡單類型,我們還可以為其指定不同的打印格式缆八,其命令格式是print/,如下所示:
1
2(lldb)?p/x?a
(NSInteger)?$13?=?0x0000000000000064
格式的完整清單可以參考Output Formats疾捍。
expression
在 開發(fā)中奈辰,我們經(jīng)常會遇到這樣一種情況:我們設(shè)置一個視圖的背景顏色,運行后發(fā)現(xiàn)顏色不好看乱豆。嗯奖恰,好吧,在代碼里面修改一下宛裕,再編譯運行一下瑟啃,嗯,還是不好 看揩尸,然后再修改吧~~這樣無形中浪費了我們大把的時間蛹屿。在這種情況下,expression命令強大的功能就能體現(xiàn)出來了岩榆,它不僅會改變調(diào)試器中的值错负,還 改變了程序中的實際值。我們先來看看實際效果勇边,如下所示:
1
2
3
4
5(lldb)?exp?a?=?10
(NSInteger)?$0?=?10
(lldb)?exp?b?=?100
(NSInteger)?$1?=?100
2015-01-25?14:00:41.313?test[18064:71466]?a?+?b?=?110,?abc
expression命令的功能不僅于此犹撒,正如上面的po命令,其實際也是”expression -O —“命令的別名粒褒。更詳細使用可以參考Evaluating Expressions识颊。
image
image命令的用法也挺多,首先可以用它來查看工程中使用的庫奕坟,如下所示:
1
2
3
4
5
6
7
8
9(lldb)?image?list
[??0]?432A6EBF-B9D2-3850-BCB2-821B9E62B1E0?0x0000000100000000?/Users/**/Library/Developer/Xcode/DerivedData/test-byjqwkhxixddxudlnvqhrfughkra/Build/Products/Debug/test
[??1]?65DCCB06-339C-3E25-9702-600A28291D0E?0x00007fff5fc00000?/usr/lib/dyld
[??2]?E3746EDD-DFB1-3ECB-88ED-A91AC0EF3AAA?0x00007fff8d324000?/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation
[??3]?759E155D-BC42-3D4E-869B-6F57D477177C?0x00007fff8869f000?/usr/lib/libobjc.A.dylib
[??4]?5C161F1A-93BA-3221-A31D-F86222005B1B?0x00007fff8c75c000?/usr/lib/libSystem.B.dylib
[??5]?CBD1591C-405E-376E-87E9-B264610EBF49?0x00007fff8df0d000?/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
[??6]?A260789B-D4D8-316A-9490-254767B8A5F1?0x00007fff8de36000?/usr/lib/libauto.dylib
......
我們還可以用它來查找可執(zhí)行文件或共享庫的原始地址祥款,這一點還是很有用的,當我們的程序崩潰時执赡,我們可以使用這條命令來查找崩潰所在的具體位置镰踏,如下所示:
1
2NSArray?*array?=?@[@1,?@2];
NSLog(@"item?3:?%@",?array[2]);
這段代碼在運行后會拋出如下異常:
1
2
3
4
5
6
7
8
9
102015-01-25?14:12:01.007?test[18122:76474]?***?Terminating?app?due?to?uncaught?exception'NSRangeException',?reason:'***?-[__NSArrayI?objectAtIndex:]:?index?2?beyond?bounds?[0?..?1]'
***?Firstthrowcall?stack:
(
0???CoreFoundation??????????????????????0x00007fff8e06f66c?__exceptionPreprocess?+?172
1???libobjc.A.dylib?????????????????????0x00007fff886ad76e?objc_exception_throw?+?43
2???CoreFoundation??????????????????????0x00007fff8df487de?-[__NSArrayI?objectAtIndex:]?+?190
3???test????????????????????????????????0x0000000100000de0?main?+?384
4???libdyld.dylib???????????????????????0x00007fff8f1b65c9?start?+?1
)
libc++abi.dylib:?terminatingwithuncaught?exception?of?type?NSException
根據(jù)以上信息,我們可以判斷崩潰位置是在main.m文件中沙合,要想知道具體在哪一行奠伪,可以使用以下命令:
1
2
3(lldb)?image?lookup?--address?0x0000000100000de0
Address:?test[0x0000000100000de0]?(test.__TEXT.__text?+?384)
Summary:?test`main?+?384?at?main.m:23
可以看到跌帐,最后定位到了main.m文件的第23行,正是我們代碼所在的位置绊率。
我們還可以使用image lookup命令來查看具體的類型谨敛,如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28(lldb)?image?lookup?--type?NSURL
Best?match?foundin/Users/**/Library/Developer/Xcode/DerivedData/test-byjqwkhxixddxudlnvqhrfughkra/Build/Products/Debug/test:
id?=?{0x100000157},?name?="NSURL",?byte-size?=?40,?decl?=?NSURL.h:17,?clang_type?=?"@interface?NSURL?:?NSObject{
NSString?*?_urlString;
NSURL?*?_baseURL;
void?*?_clients;
void?*?_reserved;
}
@property?(?readonly,getter?=?absoluteString,setter?=?,nonatomic?)?NSString?*?absoluteString;
@property?(?readonly,getter?=?relativeString,setter?=?,nonatomic?)?NSString?*?relativeString;
@property?(?readonly,getter?=?baseURL,setter?=?,nonatomic?)?NSURL?*?baseURL;
@property?(?readonly,getter?=?absoluteURL,setter?=?,nonatomic?)?NSURL?*?absoluteURL;
@property?(?readonly,getter?=?scheme,setter?=?,nonatomic?)?NSString?*?scheme;
@property?(?readonly,getter?=?resourceSpecifier,setter?=?,nonatomic?)?NSString?*?resourceSpecifier;
@property?(?readonly,getter?=?host,setter?=?,nonatomic?)?NSString?*?host;
@property?(?readonly,getter?=?port,setter?=?,nonatomic?)?NSNumber?*?port;
@property?(?readonly,getter?=?user,setter?=?,nonatomic?)?NSString?*?user;
@property?(?readonly,getter?=?password,setter?=?,nonatomic?)?NSString?*?password;
@property?(?readonly,getter?=?path,setter?=?,nonatomic?)?NSString?*?path;
@property?(?readonly,getter?=?fragment,setter?=?,nonatomic?)?NSString?*?fragment;
@property?(?readonly,getter?=?parameterString,setter?=?,nonatomic?)?NSString?*?parameterString;
@property?(?readonly,getter?=?query,setter?=?,nonatomic?)?NSString?*?query;
@property?(?readonly,getter?=?relativePath,setter?=?,nonatomic?)?NSString?*?relativePath;
@property?(?readonly,getter?=?fileSystemRepresentation,setter?=??)?const?char?*?fileSystemRepresentation;
@property?(?readonly,getter?=?isFileURL,setter?=?,readwrite?)?BOOL?fileURL;
@property?(?readonly,getter?=?standardizedURL,setter?=?,nonatomic?)?NSURL?*?standardizedURL;
@property?(?readonly,getter?=?filePathURL,setter?=?,nonatomic?)?NSURL?*?filePathURL;
@end"
可以看到,輸出結(jié)果中列出了NSURL的一些成員變量及屬性信息滤否。
image命令還有許多其它功能脸狸,具體可以參考Executable and Shared Library Query Commands。
流程控制
流程控制的命令實際上我們在上一小節(jié)已經(jīng)講過了藐俺,在Xcode的控制面板中同樣可以使用這些命令炊甲,在此不在重復。
命令別名及幫助系統(tǒng)
LLDB有兩個非常有用的特性欲芹,即命令別名及幫助卿啡。
命令別名
我們可以使用LLDB的別名機制來為常用的命令創(chuàng)建一個別名,以方便我們的使用菱父,如下命令:
1
(lldb)?breakpoint?set?--file?foo.c?--line?12
如果在我們的調(diào)試中需要經(jīng)常用到這條命令颈娜,則每次輸入這么一長串的字符一定會很讓人抓狂。此時浙宜,我們就可以為這條命令創(chuàng)建一個別名官辽,如下所示:
1
(lldb)?command?alias?bfl?breakpoint?set?-f?%1?-l?%2
這樣,我們只需要按如下方式來使用它即可:
1
(lldb)?bfl?foo.c?12
是不是簡單多了粟瞬?
我 們可以自由地創(chuàng)建LLDB命令的別名集合同仆。LLDB在啟動時會讀取~/.lldbinit文件。這個文件中存儲了command alias命令創(chuàng)建的別名裙品。LLDB幫助系統(tǒng)會讀取這個初始化文件并會列出這些別名乓梨,以讓我們了解自己所設(shè)置的別名。我們可以使用”help -a”命令并在輸出的后面來查看這邊別名清酥,其以下面這行開始:
1
2...
The?following?is?a?list?of?your?current?command?abbreviations?(see'help?command?alias'formore?info):?...
如果我們不喜歡已有命令的別名扶镀,則可以使用以下命令來取消這個別名:
1
(lldb)?command?unalias?b
幫助系統(tǒng)
LLDB幫助系統(tǒng)讓我們可以了解LLDB提供了哪些功能,并可以查看LLDB命令結(jié)構(gòu)的詳細信息焰轻。熟悉幫助系統(tǒng)可以讓我們訪問幫助系統(tǒng)中中命令文檔臭觉。
我們可以簡單地調(diào)用help命令來列出LLDB所有的頂層命令。如下所示:
1
2
3
4
5
6
7
8
9
10
11
12(lldb)?help
The?following?is?a?list?of?built-in,?permanent?debugger?commands:
_regexp-attach????--?Attach?to?a?process?idifindecimal,?otherwise?treat?the
argument?as?a?process?name?to?attach?to.
_regexp-break--?Set?a?breakpoint?using?a?regular?expression?to?specify?the
location,?where??isindecimal?and??is
inhex.
_regexp-bt????????--?Show?a?backtrace.??An?optional?argument?is?accepted;if
that?argument?is?a?number,?it?specifies?the?number?of
frames?to?display.??If?that?argument?is'all',?full
backtraces?of?all?threads?are?displayed.
…?and?so?forth?…
如果help后面跟著某個特定的命令辱志,則會列出該命令相關(guān)的所有信息蝠筑,我們以breakpoint set為例,輸出信息如下:
1
2
3
4
5
6
7
8
9(lldb)?help?breakpoint?set
Sets?a?breakpoint?or?set?of?breakpointsinthe?executable.
Syntax:?breakpoint?set?
Command?Options?Usage:
breakpoint?set?[-Ho]?-l??[-s?]?[-i?]?[-c?]?[-x?]?[-t?]?[-T?]?[-q?]?[-f?]?[-K?]
breakpoint?set?[-Ho]?-a??[-s?]?[-i?]?[-c?]?[-x?]?[-t?]?[-T?]?[-q?]
breakpoint?set?[-Ho]?-n??[-s?]?[-i?]?[-c?]?[-x?]?[-t?]?[-T?]?[-q?]?[-f?]?[-K?]?[-L?]
breakpoint?set?[-Ho]?-F??[-s?]?[-i?]?[-c?]?[-x?]?[-t?]?[-T?]?[-q?]?[-f?]?[-K?]
…?and?so?forth?…
還有一種更直接的方式來查看LLDB有哪些功能揩懒,即使用apropos命令:它會根據(jù)關(guān)鍵字來搜索LLDB幫助文檔什乙,并為每個命令選取一個幫助字符串,我們以apropos file為例已球,其輸出如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19(lldb)?apropos?file
The?following?commands?may?relate?to'file':
…
log?enable?????????????????????--?Enable?loggingfora?single?log?channel.
memory?read????????????????????--?Read?from?the?memory?of?the?process?being
debugged.
memory?write???????????????????--?Write?to?the?memory?of?the?process?being
debugged.
platform?process?launch????????--?Launch?anewprocess?on?a?remote?platform.
platform?select????????????????--?Create?a?platformifneeded?and?select?it?as
the?current?platform.
plugin?load????????????????????--?Import?a?dylib?that?implements?an?LLDB
plugin.
process?launch?????????????????--?Launch?the?executableinthe?debugger.
process?load???????????????????--?Load?a?shared?library?into?the?current
process.
source?????????????????????????--?A?set?of?commandsforaccessing?source?file
information
…?and?so?forth?…
我們還可以使用help來了解一個命令別名的構(gòu)成臣镣。如:
1
2
3(lldb)?help?b
…
'b'is?an?abbreviationfor'_regexp-break'
help命令的另一個特性是可以查看某個具體參數(shù)的使用辅愿,我們以”break command add”命令為例:
1
2
3
4(lldb)?helpbreakcommand?add
Add?a?set?of?commands?to?a?breakpoint,?to?be?executed?whenever?the?breakpoint?is?hit.
Syntax:?breakpoint?command?add??
etc...
如果想了解以上輸出的參數(shù)的作用,我們可以在help后面直接指定這個參數(shù)(將其放在尖括號內(nèi))來查詢它的詳細信息忆某,如下所示:
1
2
3(lldb)?help?
?--?Breakpoint?IDs?consist?major?and?minor?numbers;?the?major
etc...
幫助系統(tǒng)能讓我們快速地了解一個LLDB命令的使用方法点待。經(jīng)常使用它,可以讓我們更快地熟悉LLDB的各項功能弃舒,所以建議多使用它癞埠。
總結(jié)
LLDB 帶給我們強大的調(diào)試功能,在調(diào)試過程中充分地利用它可以幫助我們極大地提高調(diào)試效率聋呢。我們可以不用寫那么多的NSLog來打印一大堆的日志苗踪。所以建議在日 常工作中多去使用它。當然削锰,上面的命令只是LLDB的冰山一角徒探,更多的使用還需要大家自己去發(fā)掘,在此只是拋磚引玉喂窟,做了一些整理。
參考