Xcode調(diào)試之LLDB介紹

一直以來在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命令。


Xcode 控制臺(tái)
Xcode 控制臺(tái)

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/)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • [轉(zhuǎn)]淺談LLDB調(diào)試器文章來源于:http://www.cocoachina.com/ios/20150126/...
    loveobjc閱讀 2,735評(píng)論 2 6
  • 隨著Xcode 5的發(fā)布,LLDB調(diào)試器已經(jīng)取代了GDB,成為了Xcode工程中默認(rèn)的調(diào)試器。它與LLVM編譯器一...
    隨風(fēng)飄蕩的小逗逼閱讀 1,462評(píng)論 0 0
  • LLDB的Xcode默認(rèn)的調(diào)試器,它與LLVM編譯器一起,帶給我們更豐富的流程控制和數(shù)據(jù)檢測(cè)的調(diào)試功能。平時(shí)用Xc...
    CoderSC閱讀 1,510評(píng)論 0 2
  • Xcode 上的lldb LLDB 調(diào)試工具,gdb替代品;LLVM : Low Level Virtual Ma...
    helinyu閱讀 1,069評(píng)論 0 2
  • iOS調(diào)試之LLDB Xcode內(nèi)嵌了LLDB控制臺(tái),在Xcode代碼編輯區(qū)的下方。shift + cmd + y...
    comst閱讀 1,671評(píng)論 0 3

友情鏈接更多精彩內(nèi)容