一、概述
開發(fā)調(diào)試中,常常使用LLDB加快調(diào)試,更快速簡(jiǎn)單的定位到問(wèn)題點(diǎn)。
下面是之前收集并且整理的常用命令 、插件和 使用技巧。
二、命令
關(guān)于命令的詳細(xì)用法,使用help <command> <subcommand>進(jìn)行查看
2.0、關(guān)于~/.lldbinit
LLDB有了一個(gè)啟動(dòng)時(shí)加載的文件~/.lldbinit,每次啟動(dòng)都會(huì)加載。
可以添加一些自定義命令,加載插件等等。
cat ~/.lldbinit查看文件內(nèi)容,包含以下信息:
command script import /path/to/fblldb.py
一般系統(tǒng),在~/.lldbinit默認(rèn)導(dǎo)入facebook的fblldb.py里面命令。
當(dāng)然也可以自行安裝相關(guān)插件。@import 導(dǎo)入第三方框架,可以把基礎(chǔ)框架放入到~/.lldbinit文件中
例如@import Foundation/UIKit/CoreGraphics等系統(tǒng)框架,否則lldb調(diào)試相關(guān)框架會(huì)報(bào)錯(cuò),
2.1、打印命令
p打印基礎(chǔ)數(shù)據(jù)類型;打印對(duì)象時(shí)會(huì)返回對(duì)象指針地址。
p/x 變量名打印變量的16進(jìn)制(x 代表十六進(jìn)制格式、t 代表二進(jìn)制格式)
p/t:二進(jìn)制輸出
p/c:字符編碼
p/x:16進(jìn)制輸出查看對(duì)象內(nèi)存分配
x對(duì)象:對(duì)象內(nèi)存情況
x/4gx對(duì)象:按照四段16字節(jié)輸出對(duì)象內(nèi)存情況
x/8gx對(duì)象:按照八段16字節(jié)po打印對(duì)象的desc信息frame variable打印exp命令作用同p命令
2.2、堆棧
bt打印當(dāng)前線程堆棧(backtrace的縮寫)
bt all打印所有線程的堆棧frame select 層數(shù)跳轉(zhuǎn)到某一層的堆棧
frame variable查看當(dāng)前堆棧的變量up向上查看堆棧down向下查看堆棧
2.3、線程
-
thread list列出所有的線程 -
thread select 線程編號(hào)切換線程 thread backtrace-
thread backtrace all打印所有線程的堆棧 -
thread return各種原因,不想讓代碼執(zhí)行某個(gè)方法,或者要直接返回一個(gè)想要的值。
2.4、表達(dá)式
-
e、expr、expression執(zhí)行表達(dá)式,常用的做法是,在不重新編譯的情況下,給變量賦值。
如self.view.backgroundColor = [UIColor redColor];
2.5、關(guān)于image(image是LLDB給 target modules 取的別名 )
image list列出當(dāng)前執(zhí)行的和依賴第三方的所有鏡像image lookup -n 名字查找項(xiàng)目中某個(gè)變量名、函數(shù)名等,包括第三方SDK或者靜態(tài)庫(kù)。
例如,用來(lái)查找同名的Category方法。image lookup --address 地址+偏移量等價(jià)于image lookup -a 地址+偏移量
image lookup -v -a 地址+偏移量查找完整的源代碼行信息image lookup -type 類名查看類的成員
2.6、斷點(diǎn)
調(diào)試技巧:斷點(diǎn)編輯,xcode中可以右鍵某個(gè)斷點(diǎn),可以進(jìn)行斷點(diǎn)編輯,可以設(shè)置某種條件下觸發(fā)斷點(diǎn),實(shí)際開發(fā)中蠻實(shí)用。
2.6.1、breakpoint set 斷點(diǎn)設(shè)置,其中breakpoint可以縮寫為b
-n 方法名根據(jù)方法名給當(dāng)前類設(shè)置斷點(diǎn)。
也可以根據(jù)方法名給指定類多個(gè)方法設(shè)置斷點(diǎn),如下
breakpoint set -n "-[ViewController save:]" -n "-[ViewController continueGame:]" -n "-[ViewController pauseGame:]"breakpoint set --selector 方法名給整個(gè)項(xiàng)目某個(gè)指定方法設(shè)置斷點(diǎn)-f指定文件設(shè)定
breakpoint set -f ViewController.m -n viewDidAppear:-l指定某一行設(shè)置斷點(diǎn)
breakpoint set -f ViewController.m -l 33-a給某個(gè)內(nèi)存地址設(shè)置斷點(diǎn)-c設(shè)置條件斷點(diǎn)
breadpoint set -n helloworld: -c flag==NO-r遍歷整個(gè)項(xiàng)目中的所有方法,設(shè)置斷點(diǎn)
例如breakpoint set -r Game:為項(xiàng)目所有包含Game:這個(gè)字符的所有方法,設(shè)置斷點(diǎn)
2.6.2、breakpoint command 給斷點(diǎn)設(shè)置一些命令
breakpoint command add添加命令
例如,breakpoint command add -o "po self.view" 3
其中,-o完整寫法是–one-liner,表示增加一條命令。3表示對(duì)id為3的breakpoint增加命令。breakpoint command list num查看某個(gè)斷點(diǎn)的所有命令breakpoint command delete num刪除某個(gè)斷點(diǎn)的所有命令
2.6.3、其他
-
breakpoint list查看斷點(diǎn)列表 -
breakpoint disable/enable num開啟和禁止斷點(diǎn) -
breakpoint delete num刪除某個(gè)斷點(diǎn),沒(méi)有入?yún)t刪除所有斷點(diǎn)
2.7、watchpoint 內(nèi)存斷點(diǎn)
如果說(shuō)breakpoint是對(duì)方法生效的斷點(diǎn),watchpoint就是對(duì)地址生效的斷點(diǎn)。
想要知道某個(gè)屬性什么時(shí)候被篡改了,我們?cè)撛趺崔k呢?就可以使用watchpoint。
2.7.1、設(shè)置
watchpoint set variable設(shè)置變量
例如,watchpoint set variable self->name
不接受方法,因此以下命令無(wú)效watchpoint set variable self.name,因?yàn)镺C中點(diǎn)操作符調(diào)用setter和getter方法。watchpoint set expression address觀察某個(gè)地址
2.7.2、命令
-
watchpoint command add\delete\list同breakpoint,斷點(diǎn)設(shè)置命令
2.7.3、其他
-
watchpoint list查看當(dāng)前所有 -
watchpoint enable/disable num開啟和禁止斷點(diǎn) watchpoint delete num
2.8、流程控制語(yǔ)句
-
c繼續(xù),continue -
nnext -
s進(jìn)入step in -
finish完成
2.9、targets
2.9.1、modules
-
target modules lookup訪問(wèn)一個(gè)或多個(gè)目標(biāo)模塊的信息
由于LLDB給target modules取了個(gè)別名image,所以這個(gè)命令我們又可以寫成image lookup。 如下
詳見上面的image命令
2.9.2、stop-hook
在breadpoint 和 watchpoint 斷點(diǎn)停止的時(shí)候,去執(zhí)行一些命令。
target stop-hook list查看所有的hooktarget stop-hook add添加的hook
使用-o表示添加一條命令。target stop-hook delete num刪除指定編號(hào)的hook,沒(méi)有入?yún)um則表達(dá)式全部target stop-hook disable/enable num開啟和禁用hook,沒(méi)有入?yún)um則代表全部
2.9.3、target symbols add(add-dsym)
當(dāng)我們對(duì)接framework的時(shí)候,如果只有framework代碼,沒(méi)有工程代碼,能不能debug呢?其實(shí)我們只需要拿到工程的ipa和dSYM文件,就可以debug了,通過(guò)Attach to Process,使用命令add-dsym將dSYM文件加入target,即可只debug framework,不需要工程的代碼
2.10、其他
call調(diào)用方法,不打印值;p、po也有該功能,并且會(huì)打印返回值disassemble,縮寫為dis打印當(dāng)前目標(biāo)的匯編代碼
使用例子demo
1、image list
讀取app的image進(jìn)程內(nèi)存首地址
[ 0] 67EC1881-E7CC-38BC-A49E-D6FE5E755FB0 0x000000010ebe1000
2、memory read addr
addr-符號(hào)表的符號(hào)地址,可以通過(guò)mach-o查看。

addr = 第一步首地址 + mach-o的offset地址
0x10EBE9050 = 0x000000010ebe1000 + 0x00008048

- 相關(guān)內(nèi)存命令:find、history、read、region、writer
3、dis -s addr
這一步的addr是上一步紅框的倒敘(由于iOS是小端模式,內(nèi)存地址8位倒著讀)。

輸出:HelloWorld/fxNSlog:,其中 fxNSlog: 是用fishhook NSLog的替換函數(shù)。
4、操作寄存器
register read/write
三、LLDB 和 Python
3.1、插件的原理
LLDB 有內(nèi)建的,完整的 Python 支持。在LLDB中輸入 script,會(huì)打開一個(gè) Python REPL。
3.2、Chisel插件(facebook開源調(diào)試?yán)?,原理同時(shí))
常用的命令:
pviews self.view遞歸打印所有的view,并能標(biāo)示層級(jí)pvc遞歸打印當(dāng)前ViewController的層級(jí)visualize img_obj使用Mac的預(yù)覽打開一個(gè) UIImage, CGImageRef, UIView, 或 CALayer。fv & fvc var
通過(guò)類名搜索當(dāng)前內(nèi)存中存在的view和viewController實(shí)例的命令,支持正則搜索。show/hide無(wú)需重新編譯,直接顯示和隱藏view或者layercaflush重新渲染繪制界面,相當(dāng)于執(zhí)行了[CATransaction flush]方法
注意如果在動(dòng)畫過(guò)程中執(zhí)行這個(gè)命令,就直接渲染出動(dòng)畫結(jié)束的效果。
在調(diào)試界面顏色、坐標(biāo)之類的時(shí)候,可以直接在控制臺(tái)修改屬性,然后caflush就可以看到效果啦。mask/umask 和 border/unborder
這兩組命令用來(lái)標(biāo)識(shí)一個(gè)view或layer的位置時(shí)用。
mask用來(lái)在view上覆蓋一個(gè)半透明的矩形, border可以給view添加邊框。presponder self.view打印響應(yīng)者鏈
** 更多好用的用法,請(qǐng)參考help命令,值得你深入研究。 **
四、XCode調(diào)試
- 顯示匯編相關(guān),
Debug -> Debug Wokflow -> Always Show Disassembly則會(huì)顯示匯編信息。
例子:在NSLog打斷點(diǎn),如下
NSString *str = @"helloworld";
id __weak objc = str;
NSLog(@"%@", objc);

參考
LLDB調(diào)試小節(jié)
objc.io#19#與調(diào)試器共舞 - LLDB 的華爾茲
iOS中教你快速掌握LLDB調(diào)試技巧
Chisel-LLDB命令插件,讓調(diào)試更Easy