LLDB淺析


現LLDB調試器已經成爲Xcode工程中默認的調試器。它與LLVM編譯器一起,帶給我們更豐富的流程控制和數據檢測的調試功能。LLDB爲Xcode提供了底層調試環境,其中包括內嵌在Xcode IDE中的位於調試區域的控制面板。


一、LLDB命令結構

<noun> <verb> [-options [option-value]] [argument [argument...]]

<command> [<subcommand> [<subcommand>...]] <action> [-options [option-value]] [argument [argument...]]

  1. <command>(命令)和<subcommand>(子命令):LLDB調試器對象的名稱。命令和子命令按層級結構來排列:一個特定的命令對象爲跟隨其的子命令對象創建一個上下文,子命令又爲其子命令創建一個上下文,依此類推。
  2. <action>:我們想在組合的調試器對象(前面的命令序列)的上下文中執行的一些操作。
  3. <options>:行爲修改器(action modifiers)。通常帶有一些值。
  4. <argument>:根據使用的命令的上下文來表示各種不同的東西。例如,使用process launch命令啓動進程,該上下文中的參數就是您在命令行中輸入的參數,就好像你在調試會話之外調用進程一樣。

LLBD命令行解析是在命令執行之前完成的,因此在所有命令中都是統一的。基本命令的命令語法非常簡單,參數、選項和選項值都用空格分隔,並且雙引號用於保護參數中的空格。如果需要在參數中使用反斜槓或雙引號,可以在參數中使用反斜槓。這使得命令語法更加規範

如下所示:

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

二、命令選項

LLDB中的命令選項有規範形式(canonical,也稱爲“discoverable”)和縮寫形式(abbreviated)。

規範形式:

expression –object-description – foo

縮寫形式:

e -0 – foo

別名:

po foo

選項可以放在命令行的任何位置,但如果參數以“-”開頭,則必須通過添加選項終止來告訴LLDB您已經完成了當前命令的選項:“–”,例如,如果要啓動進程並提供“進程啓動”“command the”–stop at entry“選項,但是您希望將要啓動的進程使用參數”-program_arg value“啓動,您將鍵入:

(lldb) process launch --stop-at-entry -- -program_arg value


三、基礎

執行命令(Execution Commands)

流程控制

在這裏插入圖片描述
從左到右,四個按鈕分別是:continue,step over,step into,step out。

  1. continue
    取消程序的暫停,允許程序正常執行 (要麼一直執行下去,要麼到達下一個斷點)。
    LLDB中使用process continue命令。
(lldb) process continue 
(lldb) continue 
(lldb) c
  1. step over
    以黑盒的方式執行一行代碼。如果所在這行代碼是一個函數調用,那麼就不會跳進這個函數,而是會執行這個函數,然後繼續。
    LLDB中使用thread step-over命令。
(lldb) thread step-over 
(lldb) next 
(lldb) n 
  1. step in
    如果你確實想跳進一個函數調用來調試或者檢查程序的執行情況。當前行不是函數調用時,next和step效果是一樣的。
    在LLDB中使用thread step-in命令。
(lldb) thread step-in 
(lldb) step 
(lldb) s
  1. step out
    如果你曾經不小心跳進一個函數,但實際上你想跳過它,常見的反應是重複的運行step over直到函數返回。其實這種情況,step out會繼續執行到下一個返回語句 (直到一個堆棧幀結束) 然後再次停止。
    在LLDB中使用thread step-out命令。
(lldb) thread step-out 
(lldb) finish 

process

使用參數啓動進程

(lldb) process launch -- <args> 
(lldb) run <args> 
(lldb) r <args>

取消程序的暫停

(lldb) process continue 
(lldb) continue 
(lldb) c

thread

在當前選定的線程中執行源級單步。

(lldb) thread step-in 
(lldb) step 
(lldb) s

在當前選定的線程中執行源級別單步操作。

(lldb) thread step-over 
(lldb) next 
(lldb) n 

在當前選定的線程中執行指令級單步。(遇到彙編函數,會進入彙編函數內部)

(lldb) thread step-inst 
(lldb) si

在當前選定的線程中執行指令級別的單步操作。

(lldb) thread step-inst-over 
(lldb) ni

走出當前選擇的幀的。

(lldb) thread step-out 
(lldb) finish 

立即從當前選定的幀返回,並帶有可選的返回值。

(lldb) thread return <RETURN EXPRESSION>

運行直到我們達到第12行或控制離開當前函數(行數只能在該函數之內)

(lldb) thread until 12

斷點命令(Breakpoint Commands)

管理斷點

在這裏插入圖片描述

在Xcode的左側面板,有一組按鈕。其中一個看起來像斷點。點擊它打開斷點導航,這是一個可以快速管理所有斷點的面板。
在LLDB中使用breakpoint list命令。

(lldb) breakpoint list 
(lldb) br l

可以點擊單個斷點來開啓或關閉。
在LLDB中使用breakpoint enable 和breakpoint disable 。

(lldb) breakpoint enable <breakpointID> 
(lldb) br e <breakpointID>
(lldb) breakpoint disable <breakpointID> 
(lldb) br disable <breakpointID>

顯示斷點詳細信息

(lldb) breakpoint list -verbose
(lldb) br l -v

實例

(lldb) br l
Current breakpoints:
1: name = 'UIApplicationMain', locations = 1, resolved = 1, hit count = 1

  1.1: where = UIKitCore`UIApplicationMain, address = 0x0000000108d52791, resolved, hit count = 1 

2: names = {'objc_exception_throw', '__cxa_throw'}, locations = 2, resolved = 2, hit count = 0

  2.1: where = libobjc.A.dylib`objc_exception_throw, address = 0x000000010548a705, resolved, hit count = 0 
  2.2: where = libc++abi.dylib`__cxa_throw, address = 0x00000001076df089, resolved, hit count = 0 

3: file = '/Users/lixingdong/workSpace/lldbTest/lldbTest/AppDelegate.m', line = 23, exact_match = 0, locations = 1, resolved = 1, hit count = 1

  3.1: where = lldbTest`-[AppDelegate application:didFinishLaunchingWithOptions:] + 70 at AppDelegate.m:23, address = 0x0000000104b6c5e6, resolved, hit count = 1 

4: file = '/Users/lixingdong/workSpace/lldbTest/lldbTest/AppDelegate.m', line = 29, exact_match = 0, locations = 1, resolved = 1, hit count = 0

  4.1: where = lldbTest`-[AppDelegate testExecutionCommands] + 23 at AppDelegate.m:29, address = 0x0000000104b6c647, resolved, hit count = 0 

解釋一下輸出的信息

3: file = '/Users/lixingdong/workSpace/lldbTest/lldbTest/AppDelegate.m', line = 23, exact_match = 0, locations = 1, resolved = 1, hit count = 1

1.冒號前面的數字(3)是斷點組的組號。
2.file = ‘/Users/lixingdong/workSpace/lldbTest/lldbTest/AppDelegate.m’, line = 29 斷點的生成方式.
3.locations = 1 該組有幾個斷點。
4.resolved = 1 該組可用的斷點的數量。
5.hit count = 1 該組斷點被段過的次數。每一次程序在斷點中暫停一次這個計數+1。

3.1: where = lldbTest`-[AppDelegate application:didFinishLaunchingWithOptions:] + 70 at AppDelegate.m:23, address = 0x0000000104b6c5e6, resolved, hit count = 1 
  1. 3.1,3組號,1是組裏編號,這個斷點是第三組中的第一個斷點。
  2. lldbTest是工程名稱。
  3. -[AppDelegate application:didFinishLaunchingWithOptions:] 是斷點所在類的函數。
    • 70指的是該斷點所在指令在其所在棧空間的偏移量(十進制)。
  4. AppDelegate.m:23 表示該斷點所在文件行數。
  5. address = 0x0000000104b6c5e6 表示該斷點所在的物理地址。
  6. resolved 表示是否可用,不可用是unresolved。
  7. hit count = 1 表示該斷點攔截次數。

疑問

LLDB的斷點命令都是臨時生效?
user級別斷點和project級別斷點,使用breakpoint list查看不出區別?

操作斷點

在Xcode中可以通過在源碼頁面器的滾槽上點擊來創建斷點。通過把斷點拖拽出滾槽,然後釋放鼠標來刪除斷點。
也可以點擊斷點欄左側的 + 按鈕創建斷點,在斷點導航頁選擇斷點,然後按下刪除鍵刪除。
在這裏插入圖片描述

創建

在LLDB中使用breakpoint set命令。

可以使用縮寫形式 br。雖然 b 是一個完全不同的命令 (_regexp-break 的縮寫),但恰好也可以實現和上面同樣的效果。

給某個函數設置斷點(私有方法亦可設置)

(lldb) breakpoint set --name main 
(lldb) br s -n main 
(lldb) b main
(lldb) breakpoint set --name "-[NSString stringWithFormat:]" 
(lldb) b -[NSString stringWithFormat:]

某個文件中的某行設置一個斷點

(lldb) breakpoint set --file test.c --line 12 
(lldb) br s -f test.c -l 12 
(lldb) b test.c:12

給C++中所有命名爲main的方法設置斷點

(lldb) breakpoint set --method main 
(lldb) br s -M main

Objective-C中所有命名爲count的選擇器設置斷點

(lldb) breakpoint set --selector count 
(lldb) br s -S count

使用–shlib 來將斷點限定在一個特定的可執行庫中

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

通過函數名稱上的正則表達式設置斷點

(lldb) breakpoint set --func-regex regular-expression 
(lldb) br s -r regular-expression

編輯

在這裏插入圖片描述

  1. Symbol:
    符號斷點。填入想設置斷點的方法 (例如:-[NSException raise],-號是實例方法,+號是類方法)
  2. Module
    填入要設置斷點的方法或函數是否在位於dylib中,默認不填。
  3. Condition
    填入條件。返回值必須爲BOOL類型。確定類類型isKindOfClass:,確定對象在繼承體系中的位置的isMemberOfClass:,判斷一個對象是否能接收某個特定消息的respondsToSelector:,判斷一個對象是否遵循某個協議的conformsToProtocol:,以及提供方法實現地址的methodForSelector:
  4. Ignore
    忽略前n次不執行斷點,優先級在condition之上
  5. Action
    在這裏插入圖片描述
  • AppleScript
    內置的一種功能強大的腳本語言。
  • Capture GPU Frame
    捕捉動畫幀速。
  • Debugger Command
    調試器命令。
    可輸入LLDB相關命令。
  • Log Message
    日誌信息。
    %B會打印斷點的名字,%H會打印斷點的調用次數,@@中間可以輸入表達式。打印內容直接輸入。
  • Shell Command
    終端命令。
  • Sound
    播放聲音。
  1. Options
    勾選Automatically continue after evaluating actions之後程序會在斷點產生後繼續運行。這個屬性是相當有用的,可以輸入調試信息至於不暫停程序。

刪除

在LLDB中使用breakpoint delete命令。

(lldb) breakpoint delete 1 
(lldb) br del 1

breakpoint

在名爲main的所有函數上設置斷點。

(lldb) breakpoint set --name main 
(lldb) br s -n main 
(lldb) b main

在文件test.c的第12行中設置斷點。

(lldb) breakpoint set --file test.c --line 12 
(lldb) br s -f test.c -l 12 
(lldb) b test.c:12

給C++中所有命名爲main的方法設置斷點。

(lldb) breakpoint set --method main 
(lldb) br s -M main

在Objective-C函數-[NSString stringWithFormat:]中設置斷點。

(lldb) breakpoint set --name "-[NSString stringWithFormat:]" 
(lldb) b -[NSString stringWithFormat:]

在其選擇器爲count的所有Objective-C方法中設置斷點

(lldb) breakpoint set --selector count 
(lldb) br s -S count

通過函數名稱上的正則表達式設置斷點。

(lldb) breakpoint set --func-regex regular-expression 
(lldb) br s -r regular-expression

通過源文件內容上的正則表達式設置斷點。

(lldb) breakpoint set --name foo --condition '(int)strcmp(y,"hello") == 0' 
(lldb) br s -n foo -c '(int)strcmp(y,"hello") == 0'

列出所有斷點。

(lldb) breakpoint list 
(lldb) br l

刪除斷點。

(lldb) breakpoint delete 1 
(lldb) br del 1

觀察點命令(Watchpoint Commands)

設置觀察點

有時候我們會關心類的某個屬性什麼時候被人修改了,最簡單的方法當然就是在setter的方法打斷點,或者在@property的屬性生命行打上斷點。這樣當對象的setter方法被調用時,就會觸發這個斷點。
在這裏插入圖片描述
當然這麼做是有缺點的,對於直接訪問內存地址的修改,setter方法的斷點並沒有辦法監控得到,因此我們需要用到watchpoint命令。

    _watchPointInteger = 8;

watchpoint命令在XCode的GUI中也可以直接使用,當程序暫停時,我們能對當前程序棧中的變量設置watchpoint。值得注意的是,watchpoint是直接設置到該變量所在的內存地址上的,所以當這個變量釋放了後,watchpoint仍然是對這個地址的內存生效的。
在這裏插入圖片描述
受限於CPU的支持程度,在intel平臺上,提供了4個slot供watchpoint使用,所以同時只可設置最多4個watchpoint,arm上是2個。

在LLDB中使用用watchpoint命令(不能使用點語法,因爲是getter函數)

(lldb) wa set variable self->_watchPointInteger
Watchpoint created: Watchpoint 1: addr = 0x6000033da7e8 size = 8 state = enabled type = w
    watchpoint spec = 'self->_watchPointInteger'
    new value: 1
(lldb) wa modify -c 'self->_watchPointInteger == 5'

Watchpoint 1 hit:
old value: 1
new value: 5
(lldb) wa list
Number of supported hardware watchpoints: 4
Current watchpoints:
Watchpoint 1: addr = 0x6000033da7e8 size = 8 state = enabled type = w
    watchpoint spec = 'self->_watchPointInteger'
    old value: 1
    new value: 5
    condition = 'self->_watchPointInteger == 5'

watchpoint

在寫入變量時在變量上設置觀察點。

(lldb) watchpoint set variable global_var 
(lldb) wa s v global_var 

在寫入時在內存位置設置觀察點。 如果未指定“-x byte_size”,則要監視的區域的大小默認爲指針大小。 此命令採用原始輸入,作爲表達式計算,返回指向區域開頭的無符號整數,位於’–'選項終止符之後。

(lldb) watchpoint set expression -- my_ptr 
(lldb) wa s e -- my_ptr

在觀察點上設置條件。

(lldb) watch set var global 
(lldb) watchpoint modify -c '(global==5)' 
(lldb) c 
... 
(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) watchpoint list 
(lldb) watch l 

刪除觀察點。

(lldb) watchpoint delete 1 
(lldb) watch del 1 

檢查變量(Examining Variables)

查看調用棧狀態

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

(lldb) frame variable 
(lldb) fr v 

如果沒有指定任何變量名,則會顯示所有參數和本地變量。

(lldb) frame variable
(AppDelegate *) self = 0x000060000123ba00
(SEL) _cmd = "application:didFinishLaunchingWithOptions:"
(UIApplication *) application = 0x00007f8cb6c02780
(NSDictionary *) launchOptions = nil

如果指定參數名或變量名,則只打印指定的值。

(lldb) frame variable self
(AppDelegate *) self = 0x000060000123ba00

frame variable命令不是一個完全的表達式解析器,但它支持一些簡單的操作符,如&,*,->,[]。這個數組括號可用於指針,以將指針作爲數組處理。
frame variable命令會在變量上執行”對象打印”操作。目前,LLDB只支持Objective-C打印,使用的是對象的description方法。

(lldb) frame variable *self
(AppDelegate) *self = {
  _window = 0x00007f8cb6c2a620
  _stepInString = 0x0000000108c74070 @"stepInString"
  _stepOutString = 0x0000000108c740b0 @"12345"
  _watchPointInteger = 5
  _watchPointView = 0x00007f8cb8904320
}
(lldb) frame variable launchOptions[0]
(NSObject) launchOptions[0] = <parent is NULL>

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

(lldb) frame select 1
frame #1: 0x000000010ce50bde UIKitCore`-[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 280
UIKitCore`-[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:]:
    0x10ce50bde <+280>: cmpb   $0x0, -0x29(%rbp)
    0x10ce50be2 <+284>: setne  %r13b
    0x10ce50be6 <+288>: andb   %al, %r13b
    0x10ce50be9 <+291>: movl   -0x38(%rbp), %r15d

frame

顯示當前幀的參數和局部變量。

(lldb) frame variable 
(lldb) fr v 

顯示當前幀的局部變量。

(lldb) frame variable --no-args 
(lldb) fr v -a 

顯示局部變量“bar”的內容。

(lldb) frame variable bar 
(lldb) fr v bar 
(lldb) p bar 

顯示格式爲十六進制的局部變量“bar”的內容。

(lldb) frame variable --format x bar 
(lldb) fr v -f x bar 

target

顯示全局變量“baz”的內容。

(lldb) target variable baz 
(lldb) ta v baz 

顯示當前源文件中定義的全局/靜態變量。

(lldb) target variable 
(lldb) ta v 

每次停止時顯示變量“argc”和“argv”。

(lldb) target stop-hook add --one-liner "frame variable argc argv" 
(lldb) ta st a -o "fr v argc argv" 
(lldb) display argc 
(lldb) display argv

僅當你在main函數中停止時,才顯示變量“argc”和“argv”。

(lldb) target stop-hook add --name main --one-liner "frame variable argc argv" 
(lldb) ta st a -n main -o "fr v argc argv"

僅當您在名爲MyClass的c類中停止時,才顯示變量“* this”。

(lldb) target stop-hook add --classname MyClass --one-liner "frame variable *this" 
(lldb) ta st a -c MyClass -o "fr v *this"

評價表達式(Evaluating Expressions)

打印

打印值很簡單;只要試試 print 命令:

(lldb) expr (int) printf ("Print nine: %d.", 4 + 5) 
or using the print alias: 
(lldb) print (int) printf ("Print nine: %d.", 4 + 5) 

結果中有個1使1。實際上你可以使用它來指向這個結果。另外後面的數值是遞增的,每打印一個與對象相關的命令,這個值都會加1

(lldb) p self.stepOutString
(__NSCFConstantString *) $1 = 0x0000000106999090 @"stepOutString"

賦值

如果想改變一個值怎麼辦?你或許會猜 modify。其實這時候我們要用到的是 expression 這個方便的命令。

(lldb) print self.watchPointInteger
(NSInteger) $2 = 1
(lldb) expression  self.watchPointInteger=8
(NSInteger) $3 = 8
(lldb) print self.watchPointInteger
(NSInteger) $4 = 8

打印對象

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

(lldb) expr -o -- [SomeClass returnAnObject] 
or using the po alias: 
(lldb) po [SomeClass returnAnObject] 

打印字符串

(lldb) po self.stepOutString
12345

(lldb) p self.stepOutString
(__NSCFConstantString *) $6 = 0x00000001069990b0 @"12345"
(lldb) 

打印數組

(lldb) p @[ @"foo", @"bar" ]
(__NSArrayI *) $7 = 0x00006000018d56c0 @"2 elements"
(lldb) po $7
<__NSArrayI 0x6000018d56c0>(
foo,
bar
)

expr

評估當前幀中的廣義表達式。

(lldb) expr (int) printf ("Print nine: %d.", 4 + 5) 
or using the print alias: 
(lldb) print (int) printf ("Print nine: %d.", 4 + 5) 

爲便利變量創建和賦值。

(lldb) expr unsigned int $foo = 5 

打印對象的ObjC的"description(描述)"。

(lldb) expr -o -- [SomeClass returnAnObject] 
or using the po alias: 
(lldb) po [SomeClass returnAnObject] 

打印表達式結果的動態類型。

(lldb) expr -d 1 -- [SomeClass returnAnObject] 
(lldb) expr -d 1 -- someCPPObjectPtrOrReference 
or set dynamic type printing to be the default: (lldb) settings set target.prefer-dynamic run-target

調用函數使您可以在函數中的斷點處停止。

(lldb) expr -i 0 -- function_with_a_breakpoint()

調用崩潰的函數,並在函數崩潰時停止。

(lldb) expr -u 0 -- function_which_crashes() 

檢查線程狀態(Examining Thread State)

查看線程狀態

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

(lldb) thread list

Process 59154 stopped
* thread #1: tid = 0x56c99e, 0x0000000106997294 lldbTest`-[AppDelegate application:didFinishLaunchingWithOptions:](self=0x0000600000dea280, _cmd="application:didFinishLaunchingWithOptions:", application=0x00007fb230c00bf0, launchOptions=0x0000000000000000) at AppDelegate.m:28, queue = 'com.apple.main-thread', stop reason = breakpoint 4.1
  thread #3: tid = 0x56ca07, 0x00000001099b15be libsystem_kernel.dylib`__workq_kernreturn + 10
  thread #4: tid = 0x56ca09, 0x00000001099b31b2 libsystem_kernel.dylib`__psynch_cvwait + 10, name = 'JavaScriptCore bmalloc scavenger'
  thread #5: tid = 0x56ca0a, 0x00000001099afc2a libsystem_kernel.dylib`mach_msg_trap + 10, name = 'WebThread'
  thread #6: tid = 0x56ca0b, 0x00000001099afc2a libsystem_kernel.dylib`mach_msg_trap + 10, name = 'com.apple.uikit.eventfetch-thread'
  thread #7: tid = 0x56ca0d, 0x00000001099f2dec libsystem_platform.dylib`_platform_memmove$VARIANT$Haswell + 268, queue = 'com.apple.root.utility-qos'
(lldb) 

星號(*)表示thread #1爲當前線程。爲了獲取線程的跟蹤棧,可以使用以下命令:

(lldb) thread backtrace 
(lldb) bt

例如

(lldb) thread backtrace
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 4.1
  * frame #0: 0x0000000106997294 lldbTest`-[AppDelegate application:didFinishLaunchingWithOptions:](self=0x0000600000dea280, _cmd="application:didFinishLaunchingWithOptions:", application=0x00007fb230c00bf0, launchOptions=0x0000000000000000) at AppDelegate.m:28
    frame #1: 0x000000010ab75bde UIKitCore`-[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 280
    frame #2: 0x000000010ab775cb UIKitCore`-[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 3979
    frame #3: 0x000000010ab7cc2f UIKitCore`-[UIApplication _runWithMainScene:transitionContext:completion:] + 1623
    frame #4: 0x000000010a39b4e9 UIKitCore`__111-[__UICanvasLifecycleMonitor_Compatability _scheduleFirstCommitForScene:transition:firstActivation:completion:]_block_invoke + 866
    frame #5: 0x000000010a3a429c UIKitCore`+[_UICanvas _enqueuePostSettingUpdateTransactionBlock:] + 153
    frame #6: 0x000000010a39b126 UIKitCore`-[__UICanvasLifecycleMonitor_Compatability _scheduleFirstCommitForScene:transition:firstActivation:completion:] + 233
    frame #7: 0x000000010a39bae0 UIKitCore`-[__UICanvasLifecycleMonitor_Compatability activateEventsOnly:withContext:completion:] + 1085
    frame #8: 0x000000010a399cb5 UIKitCore`__82-[_UIApplicationCanvas _transitionLifecycleStateWithTransitionContext:completion:]_block_invoke + 795
    frame #9: 0x000000010a39995f UIKitCore`-[_UIApplicationCanvas _transitionLifecycleStateWithTransitionContext:completion:] + 435
    frame #10: 0x000000010a39ea90 UIKitCore`__125-[_UICanvasLifecycleSettingsDiffAction performActionsForCanvas:withUpdatedScene:settingsDiff:fromSettings:transitionContext:]_block_invoke + 584
    frame #11: 0x000000010a39f80e UIKitCore`_performActionsWithDelayForTransitionContext + 100
    frame #12: 0x000000010a39e7ef UIKitCore`-[_UICanvasLifecycleSettingsDiffAction performActionsForCanvas:withUpdatedScene:settingsDiff:fromSettings:transitionContext:] + 221
    frame #13: 0x000000010a3a393a UIKitCore`-[_UICanvas scene:didUpdateWithDiff:transitionContext:completion:] + 392
    frame #14: 0x000000010ab7b44e UIKitCore`-[UIApplication workspace:didCreateScene:withTransitionContext:completion:] + 515
    frame #15: 0x000000010a71fd09 UIKitCore`-[UIApplicationSceneClientAgent scene:didInitializeWithEvent:completion:] + 357
    frame #16: 0x00000001133b42da FrontBoardServices`-[FBSSceneImpl _didCreateWithTransitionContext:completion:] + 448
    frame #17: 0x00000001133bf443 FrontBoardServices`__56-[FBSWorkspace client:handleCreateScene:withCompletion:]_block_invoke_2 + 271
    frame #18: 0x00000001133beb3a FrontBoardServices`__40-[FBSWorkspace _performDelegateCallOut:]_block_invoke + 53
    frame #19: 0x0000000109616602 libdispatch.dylib`_dispatch_client_callout + 8
    frame #20: 0x0000000109619b78 libdispatch.dylib`_dispatch_block_invoke_direct + 301
    frame #21: 0x00000001133f3ba8 FrontBoardServices`__FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 30
    frame #22: 0x00000001133f3860 FrontBoardServices`-[FBSSerialQueue _performNext] + 457
    frame #23: 0x00000001133f3e40 FrontBoardServices`-[FBSSerialQueue _performNextFromRunLoopSource] + 45
    frame #24: 0x0000000107c7d721 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    frame #25: 0x0000000107c7cf93 CoreFoundation`__CFRunLoopDoSources0 + 243
    frame #26: 0x0000000107c7763f CoreFoundation`__CFRunLoopRun + 1263
    frame #27: 0x0000000107c76e11 CoreFoundation`CFRunLoopRunSpecific + 625
    frame #28: 0x00000001103f81dd GraphicsServices`GSEventRunModal + 62
    frame #29: 0x000000010ab7e81d UIKitCore`UIApplicationMain + 140
    frame #30: 0x0000000106997200 lldbTest`main(argc=1, argv=0x00007ffee9267fd0) at main.m:14
    frame #31: 0x000000010968c575 libdyld.dylib`start + 1
(lldb) 

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

(lldb) thread backtrace all 
(lldb) bt all

thread

列出程序中的線程。

(lldb) thread list

選擇線程1作爲後續命令的默認線程。

(lldb) thread select 1 
(lldb) t 1 

顯示當前線程的堆棧回溯。

(lldb) thread backtrace 
(lldb) bt

顯示所有線程的堆棧回溯。

(lldb) thread backtrace all 
(lldb) bt all

回溯當前線程的前五幀。

(lldb) thread backtrace -c 5 
(lldb) bt 5 (lldb-169 and later) 
(lldb) bt -c 5 (lldb-168 and earlier)

frame

按索引爲當前線程選擇不同的堆棧幀。

(lldb) frame select 12 
(lldb) fr s 12 
(lldb) f 12 

列出有關當前線程中當前所選幀的信息。

(lldb) frame info 

選擇調用當前堆棧幀的堆棧幀。

(lldb) up 
(lldb) frame select --relative=1

選擇當前堆棧幀調用的堆棧幀。

(lldb) down 
(lldb) frame select --relative=-1 
(lldb) fr s -r-1 

使用相對偏移量選擇不同的堆棧幀。

(lldb) frame select --relative 2 
(lldb) fr s -r2 

(lldb) frame select --relative -3 
(lldb) fr s -r-3 

可執行和共享庫查詢命令(Executable and Shared Library Query Commands)

image

image命令的用法也挺多,首先可以用它來查看工程中使用的庫,如下所示:

(lldb) image list

[  0] D96F1640-B0C5-33FC-A8BB-3290BAA51E88 0x0000000106996000 /Users/lixingdong/Library/Developer/Xcode/DerivedData/lldbTest-fkaqwvfpyrrqthbnxsglafxirqyy/Build/Products/Debug-iphonesimulator/lldbTest.app/lldbTest 
[  1] D6387150-2FB8-3066-868D-72E1B1C43982 0x000000010e532000 /usr/lib/dyld 
[  2] C3514384-926E-3813-BF0C-69FFC704E283 0x00000001069a2000 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/dyld_sim 
[  3] E5391C7B-0161-33AF-A5A7-1E18DBF9041F 0x0000000106c8b000 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/Foundation.framework/Foundation 
[  4] 177A61B3-9E02-3A09-9A98-C1C3C9AB7958 0x00000001072b1000 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/libobjc.A.dylib 
......

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

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

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

2019-08-06 22:00:34.724090+0800 lldbTest[59567:5748056] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndexedSubscript:]: index 2 beyond bounds [0 .. 1]'
*** First throw call stack:
(
	0   CoreFoundation                      0x000000010143d1bb __exceptionPreprocess + 331
	1   libobjc.A.dylib                     0x00000001009db735 objc_exception_throw + 48
	2   CoreFoundation                      0x00000001013894ec _CFThrowFormattedException + 194
	3   CoreFoundation                      0x00000001014bfb00 +[__NSArrayI allocWithZone:] + 0
	4   lldbTest                            0x00000001000bc311 -[AppDelegate testImageCommands] + 241
	5   lldbTest                            0x00000001000bc0ae -[AppDelegate application:didFinishLaunchingWithOptions:] + 110
......
(lldb) image lookup --address 0x1ec4 
(lldb) im loo -a 0x1ec4 

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

(lldb) image lookup --address 0x00000001000bc311
      Address: lldbTest[0x0000000100001311] (lldbTest.__TEXT.__text + 945)
      Summary: lldbTest`-[AppDelegate testImageCommands] + 241 at AppDelegate.m:45
(lldb) image lookup --address 0x00000001014bfb00
      Address: CoreFoundation[0x00000000001aab00] (CoreFoundation.__TEXT.__text + 1741536)
      Summary: CoreFoundation`+[__NSArrayI allocWithZone:]

可以使用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 = <null selector>,nonatomic ) NSString * absoluteString;
@property ( readonly,getter = relativeString,setter = <null selector>,nonatomic ) NSString * relativeString;
@property ( readonly,getter = baseURL,setter = <null selector>,nonatomic ) NSURL * baseURL;
@property ( readonly,getter = absoluteURL,setter = <null selector>,nonatomic ) NSURL * absoluteURL;
@property ( readonly,getter = scheme,setter = <null selector>,nonatomic ) NSString * scheme;
@property ( readonly,getter = resourceSpecifier,setter = <null selector>,nonatomic ) NSString * resourceSpecifier;
@property ( readonly,getter = host,setter = <null selector>,nonatomic ) NSString * host;
@property ( readonly,getter = port,setter = <null selector>,nonatomic ) NSNumber * port;
@property ( readonly,getter = user,setter = <null selector>,nonatomic ) NSString * user;
@property ( readonly,getter = password,setter = <null selector>,nonatomic ) NSString * password;
@property ( readonly,getter = path,setter = <null selector>,nonatomic ) NSString * path;
@property ( readonly,getter = fragment,setter = <null selector>,nonatomic ) NSString * fragment;
@property ( readonly,getter = parameterString,setter = <null selector>,nonatomic ) NSString * parameterString;
@property ( readonly,getter = query,setter = <null selector>,nonatomic ) NSString * query;
@property ( readonly,getter = relativePath,setter = <null selector>,nonatomic ) NSString * relativePath;
@property ( readonly,getter = fileSystemRepresentation,setter = <null selector> ) const char * fileSystemRepresentation;
@property ( readonly,getter = isFileURL,setter = <null selector>,readwrite ) BOOL fileURL;
@property ( readonly,getter = standardizedURL,setter = <null selector>,nonatomic ) NSURL * standardizedURL;
@property ( readonly,getter = filePathURL,setter = <null selector>,nonatomic ) NSURL * filePathURL;
@end"

摘要

https://developer.apple.com/library/archive/documentation/IDEs/Conceptual/gdb_to_lldb_transition_guide/document/Introduction.html
http://lldb.llvm.org/use/tutorial.html
https://southpeak.github.io/2015/01/25/tool-lldb/
https://huos3203.github.io/2018/09/05/調試/使用LLDB獨立調試APP/
https://www.jianshu.com/p/e48b9912b011
https://www.jianshu.com/p/39fb3d4a1368
https://www.bbsmax.com/A/6pdDEQLlzw/
https://blog.csdn.net/xuhen/article/details/77747456
https://www.jianshu.com/p/2d4083d27653
https://www.jianshu.com/p/d6a0a5e39b0e
https://objccn.io/issue-19-2/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章