LLDB 調試

LLDB是 XCode 內置的爲咱們開發者提供的調試工具。
LLDB 能夠提供的服務:
  • 容許你在程序的特定時刻暫停它;
  • 容許你查看變量的值;
  • 執行自定的指令;
  • 按照你所認爲合適的步驟來操做程序的進展。

語法

<command> [<subcommond>]
惟一匹配原則:假如根據前n個字母已經能惟一匹配到某個命令,則只寫前n個字母等效於寫下完整的命令。
每個 LLDB 命令都包含着 0 個到多個子命令,而且可能具備一個到多個可選參數。
--:表示命令(或子命令)的結束,以及輸入的開始。
express -h +17,讓人很困惑,究竟是以 -h 爲標識,僅僅執行 +17,仍是計算 17 和 h 的差值。因此用 -- 表示標識的結束和輸入的開始。若是想要 -h 做爲標識,就用 express -h -- +17。若是是要計算他們的差值,就使用 express -- -h +17。由於通常不使用標識的狀況比較多,因此 express -- 就有了一個簡寫方式,那就是 print。
輸入 help print 能夠看到。
[]:表示可選。

經常使用命令

help

列舉出全部的命令,能夠經過 help <command> 來了解更多細節。
例如:help thread help print 等。

apropos

有的時候,可能並不能徹底記得某個命令,只記得某個關鍵詞。就可使用 apropos 搜索到相關命令信息。
例如:(lldb) apropos stop-hook

print

  • 打印值
根據惟一匹配原則,你可使用 prin,pri,甚至 p。可是不能使用 pr,由於 LLDB 不能消除和 process 的歧義(幸運的是 p 並無歧義)。
  • 打印對象
通常打印對象,會出現 $0 = 0x00007fdb9b71b3e0, 可是咱們想看到的是對象的 description 方法的結果。
因此使用 -o,告訴 expression 命令以 對象(object)的方式來打印結果。
expression -o -- 有個別名,就是 po (print object)的縮寫。
 
po [[UIWindow keyWindow] recursiveDescription] 來檢查層次結構。
它能夠以文本形式打印出 完整的視圖層次結構
能夠用 po [[[UIWindow keyWindow] rootViewController] _printHierarchy] 來檢查視圖控制器。
  • 打印變量
能夠給 print 指定不一樣的打印格式。都是以 print/<fmt> 或者 p/<fmt> 格式書寫。
(lldb) p 16 -> 16
(lldb) p/x 16 //十六進制 -> 0x10
(lldb) p/t 16 //二進制 -> 0b00000000000000000000000000010000
(lldb) p/t (char)16 -> 0b00010000
p/c 打印字符
p/s 打印字符串(以'\0' 結尾的字符串)

expression

執行指定表達式,並將結果打印出來。經常使用於改變調試器中的值,即改變程序中的值。
完整語法:
express <cmd -options> -- <expr>
<cmd -options>:命令選項,使用默認便可;
<expr >:要執行的表達式
調試過程當中,咱們常常用到假設某個變量是某個值,就不須要代碼寫死了;只需打個斷點,執行以下便可,很是方便。
例:expr a = 10
 
在 lldb 中聲明一個對象,爲了下文中可使用聲明的變量,變量必須以 美圓符開頭。
(lldb) e int $a = 2
(lldb) p $a * 19 -> 38
 
(lldb) e NAArray *$array = @[@"Sturday", @"Sunday"]
(lldb) p [$array count] ->2
(lldb) po [[$array objectAtIndex:0] uppercaseString] -> SATURDAY
注:lldb 沒法肯定返回的類型

thread backtrace & bt

打印線程堆棧信息
完整語法:
thread backtrace [-c <count>] [-s <frame-index>] [-e <boolean>]
-c:設置打印堆棧的幀數
-s:設置從哪一個幀開始打印
-e:是否顯示額外的回溯
實際上這些命令選項通常不須要使用。
當發生 crash 時,可使用
(lldb) thread backtrace //查看棧幀信息
該命令還有一個別名: bt
bt all:打印全部的 thread 堆棧
 

thread return

thread return [<expr>]
該命令能夠接受一個表達式,調用命令後直接從當前的 frame 返回表達式的值。
//它有一個可選參數,在執行時它會把可選參數加載進返回寄存器裏,而後馬上執行返回命令,跳出當前棧幀。這意味這函數剩餘的部分不會被執行。這會給 ARC 的引用計數形成一些問題,或者會使函數內的清理部分失效。可是在函數的開頭執行這個命令,是個很是好的隔離這個函數,僞造返回值的方式 。
 
thread 還有一些不經常使用的命令
thread jump:直接讓程序跳到某一行。ARC 下編譯器實際插入了很多 retain,release命令,跳過一些代碼不執行極可能會形成對象內存混亂髮生 crash。
thread list:列出全部的線程。
thread select:選擇某個線程。
thread until:傳入一個 line 參數,讓程序執行到這行的時候暫停。
thread info:輸出當前線程的信息。

watchpoint

觀察變量或者屬性

這是一個很是有用的東西,咱們常常遇到,某一個變量,不知道何時值被改掉了,就可使用這個東西去定位:
(lldb) watchpoint set variable self->_string
不能使用點語法

watchpoint set expression (觀察地址)

若是想觀察某個地址,可使用 watchpoint set expression
例如:先拿到 _model 的地址,而後對地址設置一個 watchpoint
(lldb) p &_model
(Modek **) $3 = 0x00007fe0dbf23280
(lldb) watchpoint set expression 0x00007fe0dbf23280
Watchpoint created: Watchpoint 1: addr = 0x7fe0dbf23280 size = 8 state = enabled type = w
    new value: 0
 
watchpoint command
跟 breakpoint 相似,watchpoint 也能夠添加命令
watchpoint command add (添加觀察點)
例如:
(lldb) watchpoint set variable xxx
Watchpoint created: Watchpoint 1: addr = 0x7fe4e1444760 size = 8 state = enabled type = w
    watchpoint spec = '_string'
    new value: 0x0000000000000000
watchpoint 的 id 是1.
watchpoint command add -o 'bt' 1
在 watchpoint 停下來時,打印了它的線程信息
 
也能夠添加多條命令:
(lldb) watchpoint command add 1
Enter your debugger command(s).  Type 'DONE' to end.
> bt
> continue
> DONE
 
watchpoint command list (某觀察點全部命令)
watchpoint command list 1
列出某個 watchpoint 的全部 command
 
watchpoint command delete (觀察點刪除)
watchpoint command delete 1
刪除某個 watchpoint 全部的 command
 
watchpoint list (觀察點列表)
查看當前全部的 watchpoint,使用 watchpoint list
 
 
watchpoint disable (觀察點失效)
讓某個 watchpoint 失效
 
watchpoint enable (觀察點生效)
使某個 watchpoint 生效
 
 
watchpoint delete index
刪除某個 watchpoint。
不指定 index,則刪除全部的 watchpoint。
 

target modules & image:查找地址對應的文件位置

LLDB 給 target modules 取了個別名 image。
 
image lookup -address
查找這個地址具體對應的文件位置。
好比:
TLLDB[25086:246169] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArray0 objectAtIndex:]: index 1 beyond bounds for empty NSArray'
*** First throw call stack:
(
    0   CoreFoundation                      0x000000010accde65 __exceptionPreprocess + 165
    1   libobjc.A.dylib                     0x000000010a746deb objc_exception_throw + 48
    2   CoreFoundation                      0x000000010ac7c395 -[__NSArray0 objectAtIndex:] + 101
    3   TLLDB                               0x000000010a1c3e36 -[ViewController viewDidLoad] + 86
    4   UIKit                               0x000000010b210f98 -[UIViewController loadViewIfRequired] + 1198
    5   UIKit                               0x000000010b2112e7 -[UIViewController view] + 27
咱們能夠看到數組越界了,可是 objectAtIndex: 的代碼在哪呢?
使用 image lookup
(lldb) image lookup -a 0x000000010a1c3e36
      Address: TLLDB[0x0000000100000e36] (TLLDB.__TEXT.__text + 246)
      Summary: TLLDB`-[ViewController viewDidLoad] + 86 at ViewController.m:32
 
image lookup -name
查找一個方法 或者 符號的信息
例如: 第三方 SDK 有一個 Dictionary 的 catagory,和咱們本身的 catagory 衝突了,可是不知道在哪一個 .a 裏面。
使用 image lookup -n dictionaryWithXMLString: 便可。
 
image lookup -type
查看一個類型
image lookup -t Model
全部的屬性,實例變量都會打出來
 
target stop-hook
使用 LLDB debug,大多數時候須要讓程序 stop。該命令能夠在每次 stop 的時候去執行一些命令。
 
target stop-hook add & display
例如:每次程序 stop 時,都用命令打印當前 frame 的全部變量。能夠添加一個 stop-hook
(lldb) target stop-hook add -o "frame variable"
-o:--one-liner,表示添加一條命令。
 
LLDB 提供了一個更簡便的命令:display。下面這兩條等同
(lldb) target stop-hook add -o "p self.view"
(lldb) display self.view
 
target stop-hook list
能夠查看全部的 stop-hook
 
target stop-hook delete & undisplay
刪除某個 stop-hook
(lldb) target stop-hook delete index
(lldb) undisplay index
 
target stop-hook disable/enable
使某個 stop-hook 失效/生效。
 
 
 
 
參考文章
相關文章
相關標籤/搜索