一直以來在console中使用最基本的print與po命令來調(diào)試程序,通過這兩個(gè)命令可以應(yīng)付大多數(shù)的情況下的調(diào)試需求,但是有時(shí)候需要知道更多地信息就需要lldb的更多命令。
LLDB介紹
隨著Xcode 5的發(fā)布,LLDB調(diào)試器已經(jīng)取代了GDB,成為了Xcode工程中默認(rèn)的調(diào)試器。它與LLVM編譯器一起,帶給我們更豐富的流程控制和數(shù)據(jù)檢測(cè)的調(diào)試功能。LLDB為Xcode提供了底層調(diào)試環(huán)境,其中包括內(nèi)嵌在Xcode IDE中的位于調(diào)試區(qū)域的控制面板,在這里我們可以直接調(diào)用LLDB命令。

LLDB命令結(jié)構(gòu)
LLDB命令的語法有其通用結(jié)構(gòu),通常是以下形式的:
<command> [<subcommand> [<subcommand>...]] <action> [-options [option-value]] [argument [argument...]]
其中:
(command)和(subcommand):LLDB調(diào)試命令的名稱。命令和子命令按層級(jí)結(jié)構(gòu)來排列:一個(gè)命令對(duì)象為跟隨其的子命令對(duì)象創(chuàng)建一個(gè)上下文,子命令又為其子命令創(chuàng)建一個(gè)上下文,依此類推。
(action):我們想在前面的命令序列的上下文中執(zhí)行的一些操作。
options:行為修改器(action modifiers)。通常帶有一些值。
argument:根據(jù)使用的命令的上下文來表示各種不同的東西。
LLBD命令行的解析操作在執(zhí)行命令之前完成。上面的這些元素之間通過空格來分割,如果某一元素自身含有空格,則可以使用雙引用。而如果元素中又包含雙引號(hào),則可以使用反斜杠;或者元素使用單引號(hào)。如下所示:
(lldb) command [subcommand] -option "some \"quoted\" string"
也可以表示為:
(lldb) command [subcommand] -option 'some "quoted" string'
這種命令解析設(shè)計(jì)規(guī)范了LLDB命令語法,并對(duì)所有命令做了個(gè)統(tǒng)一。
例如一個(gè)簡單的LLDB命令,設(shè)置文件test.c的斷點(diǎn)在第12行輸入
(lldb) breakpoint set --file test.c --line 12
命令選項(xiàng)
LLDB中的命令選項(xiàng)有規(guī)范形式和縮寫形式兩種格式。以設(shè)置斷點(diǎn)的命令breakpoint set為例,以下列表了其部分選項(xiàng)的格式,其中括號(hào)中的是規(guī)范形式:
breakpoint set
-M <method> ( --method <method> )
-S <selector> ( --selector <selector> )
-b <function-name> ( --basename <function-name> )
-f <filename> ( --file <filename> )
-l <linenum> ( --line <linenum> )
-n <function-name> ( --name <function-name> )
…
各選項(xiàng)的順序是任意的。如果后面的參數(shù)是以”–“開頭的,則在選項(xiàng)后面添加”—“作為選項(xiàng)的終止信號(hào),以告訴LLDB我們處理的選項(xiàng)的正確位置。如下命令所示:
(lldb) process launch --stop-at-entry -- -program_arg_1 value -program_arg_2 value
如上所示,命令的選項(xiàng)是—stop-at-entry,參數(shù)是-program_arg_1和-program_arg_2,我們使用”—“將選項(xiàng)與參數(shù)作一下區(qū)分。
原始命令
LLDB命令解析器支持”原始(raw)“命令,即沒有命令選項(xiàng),命令字符串的剩余部分未經(jīng)解析就傳遞給命令。例如,expression就是一個(gè)原始命令。
不過原始命令也可以有選項(xiàng),如果命令字符串中有虛線,則在命令名與命令字符串之間放置一個(gè)選項(xiàng)結(jié)束符(—)來表明沒有命令標(biāo)記。
我們可以通過help命令的輸出來查看一個(gè)命令是否是原始命令。
命令補(bǔ)全(Command Completion)
LLDB支持源文件名,符號(hào)名,文件名,等等的命令補(bǔ)全(Commmand Completion)。終端窗口中的補(bǔ)全是通過在命令行中輸入一個(gè)制表符來初始化的。Xcode控制臺(tái)中的補(bǔ)全與在源碼編輯器中的補(bǔ)全方式是一樣的:補(bǔ)全會(huì)在第三個(gè)字符被鍵入時(shí)自動(dòng)彈出,或者通過Esc鍵手動(dòng)彈出。
一個(gè)命令中的私有選項(xiàng)可以有不同的完成者(completers)。如breakpoint中的—file 選項(xiàng)作為源文件的完成者,—shlib 選項(xiàng)作為當(dāng)前加載的庫的完成者,等等。這些行為是特定的,例如,如果指定—shlib ,且以—file 結(jié)尾,則LLDB只會(huì)列出由—shlib 指定的共享類庫。
Python腳本
對(duì)于高級(jí)用戶來說,LLDB有一個(gè)內(nèi)置的Python解析器,可以通過腳本命令來訪問。調(diào)試器中的所有特性在Python解析器中都可以作為類來訪問。這樣,我們就可以使用LLDB-Python庫來寫Python函數(shù),并通過腳本將其加載到運(yùn)行會(huì)話中,以執(zhí)行一些更復(fù)雜的調(diào)試操作。
在命令行中調(diào)試程序
通常我們都是在Xcode中直接使用LLDB調(diào)試器,Xcode會(huì)幫我們完成很多操作。當(dāng)然,如果我們想讓自己看著更Bigger,或者想了解下調(diào)試器具體的一些流程,就可以試試直接在終端使用LLDB命令來調(diào)試程序。在終端中使用LLDB調(diào)試器,我們需要了解以下內(nèi)容:
1.加載程序以備調(diào)試
2.將一個(gè)運(yùn)行的程序綁定到LLDB
3.設(shè)置斷點(diǎn)和觀察點(diǎn)
4.控制程序的執(zhí)行
5.在調(diào)試的程序中導(dǎo)航
6.檢查狀態(tài)和值的變量
7.執(zhí)行替代代碼
了解在終端中這些操作是如何進(jìn)行的,可以幫助我們更深入的了解調(diào)試器在Xcode中是如何運(yùn)作的。
LLDB常用命令
expr
可以在調(diào)試時(shí)動(dòng)態(tài)執(zhí)行指定表達(dá)式,并將結(jié)果打印出來。常用于在調(diào)試過程中修改變量的值。
在斷點(diǎn)的控制臺(tái)中使用:expr var=value 來對(duì)表達(dá)式做動(dòng)態(tài)改變
這個(gè)命令也可以新聲明一個(gè)變量對(duì)象,
例如: expr int $b=2 p $b
call
call即是調(diào)用的意思。一般只在不需要顯示輸出,或是方法無返回值時(shí)使用call。在ViewController里設(shè)置斷點(diǎn),然后在程序中斷的時(shí)候輸入下面的命令:
call [self.view setBackgroundColor:[UIColor redColor]]
繼續(xù)運(yùn)行程序,看看view的背景顏色是不是變成紅色的了!在調(diào)試的時(shí)候靈活運(yùn)用call命令可以起到事半功倍的作用。
bt
打印調(diào)用堆棧,加all可打印所有thread的堆棧。
image
image 命令可用于尋址,有多個(gè)組合命令。比較實(shí)用的用法是用于尋找棧地址對(duì)應(yīng)的代碼位置。
我們還可以用它來查找可執(zhí)行文件或共享庫的原始地址,這一點(diǎn)還是很有用的,當(dāng)我們的程序崩潰時(shí),我們可以使用這條命令來查找崩潰所在的具體位置,如下所示:
NSArray *array = @[@1, @2];
NSLog(@"item 3: %@", array[2]);
代碼在運(yùn)行后會(huì)拋出如下異常:
test[18122:76474] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 2 beyond bounds [0 .. 1]'
*** First throw call 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: terminating with uncaught exception of type NSException
根據(jù)以上信息,我們可以判斷崩潰位置是在main.m文件中,要想知道具體在哪一行,可以使用以下命令:
(lldb) image lookup --address 0x0000000100000de0
Address: test[0x0000000100000de0] (test.__TEXT.__text + 384)
Summary: test`main + 384 at main.m:23```
image命令還有許多其它功能,使用help來查看其他用法。
##### print命令
簡寫形式:prin / pri / p。但是不能用pr表示,因?yàn)闀?huì)和process混淆。
實(shí)際上,如果在console中輸入“help print”,就會(huì)得到*’print’ is an abbreviation for ‘expression --‘* 這句話,也就是說print實(shí)際上就相當(dāng)于 expression -- 。這里就很容易理解下面命令是如何工作的,p _lastPoiID=20,這行命令表達(dá)式會(huì)被執(zhí)行。
打印變量的值可以使用print命令,該命令如果打印的是簡單類型,則會(huì)列出簡單類型的類型和值。如果是對(duì)象,還會(huì)打印出對(duì)象指針地址,如下所示:
(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這樣的符號(hào),我們可以將其看作是指向?qū)ο蟮囊粋€(gè)引用,我們?cè)诳刂泼姘逯锌梢灾苯邮褂眠@個(gè)符號(hào)來操作對(duì)應(yīng)的對(duì)象,這些東西存在于LLDB的全名空間中,目的是為了輔助調(diào)試。
上面的print命令會(huì)打印出對(duì)象的很多信息,如果我們只想查看對(duì)象的值的信息,則可以使用po(print object的縮寫)命令,如下所示:
(lldb) po str
abc
當(dāng)然,po命令是”exp -O —“命令的別名,使用”exp -O —”能達(dá)到同樣的效果。
###簡稱和別名
很多時(shí)候,*LLDB*完整的命令是很長的。比如前面所說的image lookup --address
這個(gè)組合命令。為了方便日常的使用,提高效率,*LLDB*命令也提供通過簡稱的方式調(diào)用命令。還是這個(gè)命令,我們用簡稱就可以寫為im loo -a,是不是簡單多了。
如果你是從gdb時(shí)代就開始使用調(diào)試器的,你會(huì)發(fā)現(xiàn),有些命令如p、call等命令和*gdb*下是一致的。其實(shí)這些命令是*LLDB*一些命令的別名,比如p是frame variable的別名,p view實(shí)際上是frame variable view。除了系統(tǒng)自建的*LLDB*別名,你也可以自定義別名。比如下面這個(gè)命令
command alias ioa image lookup --address %1
將前面所介紹過的一個(gè)命令image lookup --address添加了一個(gè)ioa
的別名。然后執(zhí)行下面的命令:
(lldb) ioa 0x0000000100004af8
Address: ControlStyleDemo[0x0000000100004af8] (ControlStyleDemo.__TEXT.__text + 13288)
Summary: ControlStyleDemo`-[RootViewController viewDidLoad] + 312 at RootViewController.m:53
#####幫助系統(tǒng)
LLDB幫助系統(tǒng)讓我們可以了解LLDB提供了哪些功能,并可以查看LLDB命令結(jié)構(gòu)的詳細(xì)信息。熟悉幫助系統(tǒng)可以讓我們?cè)L問幫助系統(tǒng)中中命令文檔。
我們可以簡單地調(diào)用help命令來列出LLDB所有的頂層命令。如下所示:
(lldb) help
The following is a list of built-in, permanent debugger commands:
_regexp-attach -- Attach to a process id if in decimal, 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 <linenum> is in decimal and <address> is
in hex.
_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 …</address></linenum>```
如果help后面跟著某個(gè)特定的命令,則會(huì)列出該命令相關(guān)的所有信息,我們以breakpoint set為例,輸出信息如下:
(lldb) help breakpoint set
Sets a breakpoint or set of breakpoints in the executable.
Syntax: breakpoint set <cmd-options>
Command Options Usage:
breakpoint set [-Ho] -l <linenum> [-s <shlib-name>] [-i <count>] [-c <expr>] [-x <thread-index>] [-t <thread-id>] [-T <thread-name>] [-q <queue-name>] [-f <filename>] [-K <boolean>]
breakpoint set [-Ho] -a <address-expression> [-s <shlib-name>] [-i <count>] [-c <expr>] [-x <thread-index>] [-t <thread-id>] [-T <thread-name>] [-q <queue-name>]
breakpoint set [-Ho] -n <function-name> [-s <shlib-name>] [-i <count>] [-c <expr>] [-x <thread-index>] [-t <thread-id>] [-T <thread-name>] [-q <queue-name>] [-f <filename>] [-K <boolean>] [-L <language>]
breakpoint set [-Ho] -F <fullname> [-s <shlib-name>] [-i <count>] [-c <expr>] [-x <thread-index>] [-t <thread-id>] [-T <thread-name>] [-q <queue-name>] [-f <filename>] [-K <boolean>]
… and so forth …</boolean></filename></queue-name></thread-name></thread-id></thread-index></expr></count></shlib-name></fullname></language></boolean></filename></queue-name></thread-name></thread-id></thread-index></expr></count></shlib-name></function-name></queue-name></thread-name></thread-id></thread-index></expr></count></shlib-name></address-expression></boolean></filename></queue-name></thread-name></thread-id></thread-index></expr></count></shlib-name></linenum></cmd-options>```
幫助系統(tǒng)能讓我們快速地了解一個(gè)LLDB命令的使用方法。經(jīng)常使用它,可以讓我們更快地熟悉LLDB的各項(xiàng)功能。
參考:
[About LLDB and Xcode](https://developer.apple.com/library/ios/documentation/IDEs/Conceptual/gdb_to_lldb_transition_guide/document/Introduction.html)
[淺談LLDB調(diào)試器](http://www.cocoachina.com/ios/20150126/11021.html)
[LLDB調(diào)試命令初探](http://www.starfelix.com/blog/2014/03/17/lldbdiao-shi-ming-ling-chu-tan/)