你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> 淺談LLDB調試器

淺談LLDB調試器

編輯:IOS開發基礎

隨著Xcode 5的發布,LLDB調試器已經取代了GDB,成為了Xcode工程中默認的調試器。它與LLVM編譯器一起,帶給我們更豐富的流程控制和數據檢測的調試功能。LLDB為Xcode提供了底層調試環境,其中包括內嵌在Xcode IDE中的位於調試區域的控制面板,在這裡我們可以直接調用LLDB命令。如圖1所示:

圖1:位於Xcode調試區域的控制台

lldb_in_xc5_command_window_2x.png

在本文中,我們主要整理一下LLDB調試器提供給我們的調試命令,更詳細的內容可以查看The LLDB Debugger。

LLDB命令結構

在使用LLDB前,我們需要了解一下LLDB的命令結構及語法,這樣可以盡可能地挖掘LLDB的潛能,以幫助我們更充分地利用它。

LLDB命令的語法有其通用結構,通常是以下形式的:

 [ [...]]  [-options [option-value]] [argument [argument...]]

其中:

(命令)和(子命令):LLDB調試命令的名稱。命令和子命令按層級結構來排列:一個命令對象為跟隨其的子命令對象創建一個上下文,子命令又為其子命令創建一個上下文,依此類推。

:我們想在前面的命令序列的上下文中執行的一些操作。

:行為修改器(action modifiers)。通常帶有一些值。

:根據使用的命令的上下文來表示各種不同的東西。

LLBD命令行的解析操作在執行命令之前完成。上面的這些元素之間通過空格來分割,如果某一元素自身含有空格,則可以使用雙引用。而如果元素中又包含雙引號,則可以使用反斜槓;或者元素使用單引號。如下所示:

(lldb) command [subcommand] -option "some \"quoted\" string"
(lldb) command [subcommand] -option 'some "quoted" string'

這種命令解析設計規范了LLDB命令語法,並對所有命令做了個統一。

命令選項

LLDB中的命令選項有規范形式和縮寫形式兩種格式。以設置斷點的命令breakpoint set為例,以下列表了其部分選項的格式,其中括號中的是規范形式:

breakpoint set
   -M  ( --method  )
   -S  ( --selector  )
   -b  ( --basename  )
   -f  ( --file  )
   -l  ( --line  )
   -n  ( --name  )
…

各選項的順序是任意的。如果後面的參數是以”–“開頭的,則在選項後面添加”—“作為選項的終止信號,以告訴LLDB我們處理的選項的正確位置。如下命令所示:

(lldb) process launch --stop-at-entry -- -program_arg_1 value -program_arg_2 value

如上所示,命令的選項是—stop-at-entry,參數是-program_arg_1和-program_arg_2,我們使用”—“將選項與參數作一下區分。

原始命令

LLDB命令解析器支持”原始(raw)“命令,即沒有命令選項,命令字符串的剩余部分未經解析就傳遞給命令。例如,expression就是一個原始命令。

不過原始命令也可以有選項,如果命令字符串中有虛線,則在命令名與命令字符串之間放置一個選項結束符(—)來表明沒有命令標記。

我們可以通過help命令的輸出來查看一個命令是否是原始命令。

命令補全(Command Completion)

LLDB支持源文件名,符號名,文件名,等等的命令補全(Commmand Completion)。終端窗口中的補全是通過在命令行中輸入一個制表符來初始化的。Xcode控制台中的補全與在源碼編輯器中的補全方式是一樣的:補全會在第三個字符被鍵入時自動彈出,或者通過Esc鍵手動彈出。

一個命令中的私有選項可以有不同的完成者(completers)。如breakpoint中的—file 選項作為源文件的完成者,—shlib 選項作為當前加載的庫的完成者,等等。這些行為是特定的,例如,如果指定—shlib ,且以—file 結尾,則LLDB只會列出由—shlib 指定的共享類庫。

Python腳本

對於高級用戶來說,LLDB有一個內置的Python解析器,可以通過腳本命令來訪問。調試器中的所有特性在Python解析器中都可以作為類來訪問。這樣,我們就可以使用LLDB-Python庫來寫Python函數,並通過腳本將其加載到運行會話中,以執行一些更復雜的調試操作。

在命令行中調試程序

通常我們都是在Xcode中直接使用LLDB調試器,Xcode會幫我們完成很多操作。當然,如果我們想讓自己看著更Bigger,或者想了解下調試器具體的一些流程,就可以試試直接在終端使用LLDB命令來調試程序。在終端中使用LLDB調試器,我們需要了解以下內容:

1.加載程序以備調試

2.將一個運行的程序綁定到LLDB

3.設置斷點和觀察點

4.控制程序的執行

5.在調試的程序中導航

6.檢查狀態和值的變量

7.執行替代代碼

了解在終端中這些操作是如何進行的,可以幫助我們更深入的了解調試器在Xcode中是如何運作的。下面我們分步來介紹一下。

指定需要調試的程序

首先我們需要設置需要調試的程序。我們可以使用如下命令做到這一點:

$ lldb /Projects/Sketch/build/Debug/Sketch.app 
Current executable set to '/Projects/Sketch/build/Debug/Sketch.app' (x86_64).

或者在運行lldb後,使用file命令來處理,如下所示:

$ lldb 
(lldb) file /Projects/Sketch/build/Debug/Sketch.app 
Current executable set to '/Projects/Sketch/build/Debug/Sketch.app' (x86_64).

設置斷點

在設置完程序後,我們可能想設置一點斷點來調試程序。此時我們可以使用breakpoint set命令來設置斷點,這個命令簡單、直觀,且有智能補全,接下來我們看看它的具體操作。

如果想在某個文件中的某行設置一個斷點,可使用以下命令:

(lldb) breakpoint set --file foo.c --line 12

如果想給某個函數設置斷點,可使用以下命令:

(lldb) breakpoint set --name foo

如果想給C++中所有命名為foo的方法設置斷點,可以使用以下命令:

(lldb) breakpoint set --method foo

如果想給Objective-C中所有命名為alignLeftEdges:的選擇器設置斷點,則可以使用以下命令:

(lldb) breakpoint set --selector alignLeftEdges:

我們可以使用—shlib 來將斷點限定在一個特定的可執行庫中:

(lldb) breakpoint set --shlib foo.dylib --name foo

看吧,斷點設置命令還是很強大的。

如果我們想查看程序中所有的斷點,則可以使用breakpoint list命令,如下所示:

(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

從上面的輸出結果可以看出,一個斷點一般有兩部分:

1.斷點的邏輯規范,這一部分是用戶提供給breakpoint set命令的。

2.與規范匹配的斷點的位置。

如上所示,通過”breakpoint set —selector alignLeftEdges:“設置的斷點,其信息中會顯示出所有alignLeftEdges:方法的位置。

breakpoint list命令輸出列表顯示每個邏輯斷點都有一個整數標識,如上所示斷點標識為1。而每個位置也會有一個標識,如上所示的1.1。

輸出列表中另一個信息是斷點位置是否是已解析的(resolved)。這個標識表示當與之相關的文件地址被加載到程序進行調試時,其位置是已解析的。例如,如果在共享庫中設置的斷點之後被卸載了,則斷點的位置還會保留,但其不能再被解析。

不管是邏輯斷點產生的所有位置,還是邏輯斷點解析的任何特定位置,我們都可以使用斷點觸發命令來對其進行刪除、禁用、設置條件或忽略計數操作。例如,如果我們想添加一個命令,以在LLDB命中斷點1.1時打印跟蹤棧,則可以執行以下命令

(lldb) breakpoint command add 1.1
Enter your debugger command(s). Type 'DONE' to end.
> bt
> DONE

如果想更詳細地了解”breakpoint command add”命令的使用,可以使用help幫助系統來查看。

設置觀察點

作為斷點的補充,LLDB支持觀察點以在不中斷程序運行的情況下監測一些變量。例如,我們可以使用以下命令來監測名為global的變量的寫操作,並在(global==5)為真時停止監測:

(lldb) watch set var global
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) frame var global
(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來啟動程序

一旦指定了調試哪個程序,並為其設置了一些斷點後,就可以開始運行程序了。我們可以使用以下命令來啟動程序:

(lldb) process launch
(lldb) run
(lldb) r

我們同樣可以使用進程ID或進程名來連接一個已經運行的程序。當使用名稱來連接一個程序時,LLDB支持—waitfor選項。這個選項告訴LLDB等待下一個名稱為指定名稱的程序出現,然後連接它。例如,下面3個命令都是用於連接Sketch程序(假定其進程ID為123):

(lldb) process attach --pid 123
(lldb) process attach --name Sketch
(lldb) process attach --name Sketch --waitfor

啟動或連接程序後,進程可能由於某些原因而停止,如:

(lldb) process attach -p 12345
Process 46915 Attaching
Process 46915 Stopped
1 of 3 threads stopped with reasons:
* 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:”及其下面一行。在多線程環境下,在內核實際返回控制權給調試器前,可能會有多個線程命中同一個斷點。在這種情況下,我們可以在停止信息中看到所有因此而停止的線程。

控制程序

啟動程序後,LLDB允許程序在到達斷點前繼續運行。LLDB中流程控制的命令都在thread命令層級中。如下所示:

(lldb) thread continue
Resuming thread 0x2c03 in process 46915
Resuming process 46915

另外,還有以下命令:

(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按步調度模式,如:

lldb) thread until 100

這條命令會運行線程,直到當前frame到達100行。如果代碼在運行的過程中跳過了100行,則當frame被彈出棧後終止執行。

查看線程狀態

在進程停止後,LLDB會選擇一個當前線程和線程中當前幀(frame)。很多檢測狀態的命令可以用於這個線程或幀。

為了檢測進程的當前狀態,可以從以下命令開始:

(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為當前線程。為了獲取線程的跟蹤棧,可以使用以下命令:

(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

如果想查看所有線程的調用棧,則可以使用以下命令:

(lldb) thread backtrace all

查看調用棧狀態

檢查幀參數和本地變量的最簡便的方式是使用frame variable命令:

(lldb) frame variable
self = (SKTGraphicView *) 0x0000000100208b40
_cmd = (struct objc_selector *) 0x000000010001bae1
sender = (id) 0x00000001001264e0
selection = (NSArray *) 0x00000001001264e0
i = (NSUInteger) 0x00000001001264e0
c = (NSUInteger) 0x00000001001253b0

如果沒有指定任何變量名,則會顯示所有參數和本地變量。如果指定參數名或變量名,則只打印指定的值。如:

(lldb) frame variable self
(SKTGraphicView *) self = 0x0000000100208b40

frame variable命令不是一個完全的表達式解析器,但它支持一些簡單的操作符,如&,*,–>,[]。這個數組括號可用於指針,以將指針作為數組處理。如下所示:

(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命令會在變量上執行”對象打印”操作。目前,LLDB只支持Objective-C打印,使用的是對象的description方法。

如果想查看另外一幀,可以使用frame select命令,如下所示:

(lldb) frame select 9
frame #9: 0x0000000100015ae3, where = Sketch`function1 + 33 at /Projects/Sketch/SKTFunctions.m:11

小結

以上所介紹的命令可以讓我們在終端中直接調試程序。當然,很多命令也可以在Xcode中直接使用。這些命令可以讓我們了解程序運行的狀態,當然有些狀態可以在Xcode中了解到。建議在調試過程中,可以多使用這些命令。

如果想了解這一過程中使用的各種命令,可以查看蘋果的官方文檔。

在Xcode中調試程序

對於我們日常的開發工作來說,更多的時候是在Xcode中進行調試工作。因此上面所描述的流程,其實Xcode已經幫我們完成了大部分的工作,而且很多東西也可以在Xcode裡面看到。因此,我們可以把精力都集中在代碼層面上。

在蘋果的官方文檔中列出了我們在調試中能用到的一些命令,我們在這重點講一些常用的命令。

打印

打印變量的值可以使用print命令,該命令如果打印的是簡單類型,則會列出簡單類型的類型和值。如果是對象,還會打印出對象指針地址,如下所示:

(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"

在輸出結果中我們還能看到類似於$0,$1這樣的符號,我們可以將其看作是指向對象的一個引用,我們在控制面板中可以直接使用這個符號來操作對應的對象,這些東西存在於LLDB的全名空間中,目的是為了輔助調試。如下所示:

(lldb) exp $0 = 100
(NSInteger) $9 = 100
(lldb) p a
(NSInteger) $10 = 100

另外$後面的數值是遞增的,每打印一個與對象相關的命令,這個值都會加1。

上面的print命令會打印出對象的很多信息,如果我們只想查看對象的值的信息,則可以使用po(print object的縮寫)命令,如下所示:

(lldb) po str
abc

當然,po命令是”exp -O —“命令的別名,使用”exp -O —”能達到同樣的效果。

對於簡單類型,我們還可以為其指定不同的打印格式,其命令格式是print/,如下所示:

(lldb) p/x a
(NSInteger) $13 = 0x0000000000000064

格式的完整清單可以參考Output Formats。

expression

在開發中,我們經常會遇到這樣一種情況:我們設置一個視圖的背景顏色,運行後發現顏色不好看。嗯,好吧,在代碼裡面修改一下,再編譯運行一下,嗯,還是不好看,然後再修改吧~~這樣無形中浪費了我們大把的時間。在這種情況下,expression命令強大的功能就能體現出來了,它不僅會改變調試器中的值,還改變了程序中的實際值。我們先來看看實際效果,如下所示:

(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命令的用法也挺多,首先可以用它來查看工程中使用的庫,如下所示:

(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 
......

我們還可以用它來查找可執行文件或共享庫的原始地址,這一點還是很有用的,當我們的程序崩潰時,我們可以使用這條命令來查找崩潰所在的具體位置,如下所示:

NSArray *array = @[@1, @2];
NSLog(@"item 3: %@", array[2]);

這段代碼在運行後會拋出如下異常:

2015-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]'
*** 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

根據以上信息,我們可以判斷崩潰位置是在main.m文件中,要想知道具體在哪一行,可以使用以下命令:

(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命令來查看具體的類型,如下所示:

(lldb) image lookup --type NSURL
Best match found in /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"

可以看到,輸出結果中列出了NSURL的一些成員變量及屬性信息。

image命令還有許多其它功能,具體可以參考Executable and Shared Library Query Commands。

流程控制

流程控制的命令實際上我們在上一小節已經講過了,在Xcode的控制面板中同樣可以使用這些命令,在此不在重復。

命令別名及幫助系統

LLDB有兩個非常有用的特性,即命令別名及幫助。

命令別名

我們可以使用LLDB的別名機制來為常用的命令創建一個別名,以方便我們的使用,如下命令:

(lldb) breakpoint set --file foo.c --line 12

如果在我們的調試中需要經常用到這條命令,則每次輸入這麼一長串的字符一定會很讓人抓狂。此時,我們就可以為這條命令創建一個別名,如下所示:

(lldb) command alias bfl breakpoint set -f %1 -l %2

這樣,我們只需要按如下方式來使用它即可:

(lldb) bfl foo.c 12

是不是簡單多了?

我們可以自由地創建LLDB命令的別名集合。LLDB在啟動時會讀取~/.lldbinit文件。這個文件中存儲了command alias命令創建的別名。LLDB幫助系統會讀取這個初始化文件並會列出這些別名,以讓我們了解自己所設置的別名。我們可以使用”help -a”命令並在輸出的後面來查看這邊別名,其以下面這行開始:

...
The following is a list of your current command abbreviations (see 'help command alias' for more info): ...

如果我們不喜歡已有命令的別名,則可以使用以下命令來取消這個別名:

(lldb) command unalias b

幫助系統

LLDB幫助系統讓我們可以了解LLDB提供了哪些功能,並可以查看LLDB命令結構的詳細信息。熟悉幫助系統可以讓我們訪問幫助系統中中命令文檔。

我們可以簡單地調用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  is in decimal and  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 …

如果help後面跟著某個特定的命令,則會列出該命令相關的所有信息,我們以breakpoint set為例,輸出信息如下:

(lldb) help breakpoint set
     Sets a breakpoint or set of breakpoints in the 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命令:它會根據關鍵字來搜索LLDB幫助文檔,並為每個命令選取一個幫助字符串,我們以apropos file為例,其輸出如下:

(lldb) apropos file
The following commands may relate to 'file':
…
log enable                     -- Enable logging for a 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 a new process on a remote platform.
platform select                -- Create a platform if needed and select it as
                                  the current platform.
plugin load                    -- Import a dylib that implements an LLDB
                                  plugin.
process launch                 -- Launch the executable in the debugger.
process load                   -- Load a shared library into the current
                                  process.
source                         -- A set of commands for accessing source file
                                  information
… and so forth …

我們還可以使用help來了解一個命令別名的構成。如:

(lldb) help b
…
'b' is an abbreviation for '_regexp-break'

help命令的另一個特性是可以查看某個具體參數的使用,我們以”break command add”命令為例:

(lldb) help break command add
Add a set of commands to a breakpoint, to be executed whenever the breakpoint is hit.
Syntax: breakpoint command add  
etc...

如果想了解以上輸出的參數的作用,我們可以在help後面直接指定這個參數(將其放在尖括號內)來查詢它的詳細信息,如下所示:

(lldb) help 
 -- Breakpoint IDs consist major and minor numbers; the major
etc...

幫助系統能讓我們快速地了解一個LLDB命令的使用方法。經常使用它,可以讓我們更快地熟悉LLDB的各項功能,所以建議多使用它。

總結

LLDB帶給我們強大的調試功能,在調試過程中充分地利用它可以幫助我們極大地提高調試效率。我們可以不用寫那麼多的NSLog來打印一大堆的日志。所以建議在日常工作中多去使用它。當然,上面的命令只是LLDB的冰山一角,更多的使用還需要大家自己去發掘,在此只是拋磚引玉,做了一些整理。

參考

The LLDB Debugger

LLDB Quick Start Guide

與調試器共舞 – LLDB 的華爾茲

LLDB調試命令初探

NSLog效率低下的原因及嘗試lldb斷點打印Log

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved