動(dòng)態(tài)調(diào)試
- 將程序運(yùn)行起來,通過下斷點(diǎn),打印等方式,查看參數(shù),返回值,函數(shù)調(diào)用流程等。
Xcode調(diào)試App的原理
- Xcode編譯器的發(fā)展歷程:GCC -> LLVM
- Xcode調(diào)試器的發(fā)展歷程:GDB -> LLDB
- debugServer剛開始時(shí)存放在Mac的Xcode里面,其路徑為:/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/13.3/DeveloperDiskImage.dmg/usr/bin/debugServer
- 當(dāng)Xcode識(shí)別到真機(jī)設(shè)備時(shí),Xcode會(huì)自動(dòng)將debugServer安裝到真機(jī)上,安裝路徑為:/Developer/usr/bin/debugServer
- Xcode調(diào)試的局限性:一般情況下,只能調(diào)試 通過Xcode安裝的App
動(dòng)態(tài)調(diào)試任意App
debugServer的權(quán)限問題
- 默認(rèn)情況下,真機(jī)設(shè)備路徑下:/Developer/usr/bin/debugServer的debugServer缺少一定的權(quán)限,只能調(diào)試通過Xcode安裝的App,無法調(diào)試其他App;
- 若希望調(diào)試其他App,需要對(duì)debugServer重新簽名,簽上兩個(gè)調(diào)試相關(guān)的權(quán)限,如下所示:
- get-task-allow
- task_for_pid_allow
如何給debugServer簽上權(quán)限
- 手機(jī)必須越獄;
- 通過IFunBox,定位到/Developer/usr/bin/debugServer文件路徑下,將debugServer文件拷貝到Mac桌面;
- Mac需要安裝
ldid簽名工具,利用ldid指令導(dǎo)出debugServer文件以前的簽名權(quán)限文件debugServer.entitlements,終端指令為:ldid -e debugServer > debugServer.entitlements - 然后在導(dǎo)出的權(quán)限文件
debugServer.entitlements中,手動(dòng)添加get-task-allow與task_for_pid_allow這兩個(gè)權(quán)限選項(xiàng); - 然后終端執(zhí)行
ldid SdebugServer.entitlements debugServer,即將權(quán)限文件debugServer.entitlements簽到debugServer文件中; - 由于/Developer/usr/bin/debugServer文件路徑是只讀的,不能操作此文件路徑下的文件,所以將剛重新簽名權(quán)限的debugServer文件,拷貝到
/usr/bin文件路徑下; - 最后終端輸入:
chmod +x /usr/bin/debugServer賦予操作權(quán)限;
讓debugServer附加到某個(gè)App進(jìn)程
- 終端在iPhone環(huán)境下,執(zhí)行命令:
debugserver *: 端口號(hào) -a 進(jìn)程名稱 -
*: 端口號(hào):使用iPhone的某個(gè)端口號(hào)啟動(dòng)debugserver服務(wù)(只要不是保留端口號(hào)就可以) -
-a 進(jìn)程名稱:輸入App的進(jìn)程名稱或者進(jìn)程ID
在Mac上啟動(dòng)LLDB,遠(yuǎn)程連接iPhone上的debugserver服務(wù)
- 啟動(dòng)LLDB:在Mac環(huán)境下,輸入
lldb - 連接debugserver服務(wù):在Mac環(huán)境下,輸入
process connect connect ://手機(jī)的ip地址:debugserver服務(wù)端口號(hào) - 使用LLDB指令讓目標(biāo)程序繼續(xù)執(zhí)行:在Mac環(huán)境下,輸入
c - 接下來就可以使用LLDB指令調(diào)試目標(biāo)App了。
LLDB指令
-
help:幫助提示,直接輸入help,會(huì)打印出所有LLDB指令; -
help LLDB指令:例如help breakpoint,會(huì)打印出breakpoint的所有子命令,例如上面的set子命令; -
expression 命令選項(xiàng) -- 執(zhí)行表達(dá)式,例如expression -O -- self.view-
--命令選項(xiàng)結(jié)束符,表示命令選項(xiàng)已經(jīng)設(shè)置完畢,如沒有命令選項(xiàng)可以直接省略; -
expression -- 對(duì)象,expression 對(duì)象,e 對(duì)象,print 對(duì)象,p 對(duì)象,call 對(duì)象這些指令的效果一樣,打印目標(biāo)對(duì)象; -
expression -O -- 對(duì)象與po 對(duì)象的效果一樣; - 總結(jié):
expression不僅僅是打印,后面還能跟表達(dá)式,執(zhí)行表達(dá)式; -
expression self.view.layer.backgroundColor = [UIColor redColor].CGColor設(shè)置控制器的顏色為紅色,可以在運(yùn)行中動(dòng)態(tài)調(diào)試;
-
-
thread backtrace:打印當(dāng)前函數(shù)的調(diào)用堆棧,可簡(jiǎn)寫為bt; -
thread return:讓當(dāng)前函數(shù)直接返回某個(gè)值,不會(huì)執(zhí)行斷點(diǎn)后面的代碼; -
frame variable:打印出當(dāng)前棧楨函數(shù)內(nèi)部的所有變量;
流程控制
-
thread continue:跳過當(dāng)前斷點(diǎn),繼續(xù)執(zhí)行,如果有下一個(gè)斷點(diǎn)會(huì)卡住在下一個(gè)斷點(diǎn),可簡(jiǎn)寫為continue或c -
thread step-over:?jiǎn)尾綀?zhí)行;如果遇到函數(shù)調(diào)用,會(huì)直接過掉即將函數(shù)調(diào)用看成單行代碼執(zhí)行,可簡(jiǎn)寫為next或n -
thread step-in:?jiǎn)尾綀?zhí)行,如果遇到函數(shù)調(diào)用,會(huì)進(jìn)入到函數(shù)內(nèi)部執(zhí)行,可簡(jiǎn)寫為step或s -
thread step-out:直接執(zhí)行完當(dāng)前函數(shù)的所有代碼,返回到上一個(gè)函數(shù),可簡(jiǎn)寫為finish - 上面的四個(gè)指令與Xcode中的按鈕對(duì)應(yīng)關(guān)系如下:

Snip20210707_13.png
-
thread step-inst-over:匯編指令級(jí)別的單步執(zhí)行,可簡(jiǎn)寫為nexti或ni -
thread step-inst:匯編指令級(jí)別的單步執(zhí)行,如果遇到函數(shù)調(diào)用,會(huì)進(jìn)入到函數(shù)內(nèi)部執(zhí)行,可簡(jiǎn)寫為stepi或si
普通斷點(diǎn)操作
-
breakpoint set -r 正則表達(dá)式:例如breakpoint set -r test,給函數(shù)名包含test字符串的所有函數(shù)添加斷點(diǎn); -
breakpoint set -s 動(dòng)態(tài)庫(kù) -n 函數(shù)名:給指定動(dòng)態(tài)庫(kù)的執(zhí)行函數(shù)添加斷點(diǎn); -
breakpoint set -a 函數(shù)內(nèi)存地址:給對(duì)應(yīng)的內(nèi)存地址的函數(shù)添加斷點(diǎn); -
breakpoint list:打印出工程中的所有斷點(diǎn); -
breakpoint disable 斷點(diǎn)編號(hào):禁用指定斷點(diǎn); -
breakpoint enable 斷點(diǎn)編號(hào):?jiǎn)⒂弥付〝帱c(diǎn); -
breakpoint delete 斷點(diǎn)編號(hào):刪除指定斷點(diǎn); -
breakpoint command add 斷點(diǎn)編號(hào):給指定的斷點(diǎn)添加額外的執(zhí)行指令;如下所示:

Snip20210707_15.png
- 當(dāng)執(zhí)行到第18行時(shí),斷點(diǎn)斷住,然后控制臺(tái)執(zhí)行下面的指令,

Snip20210707_16.png
breakpoint set -n "-[ViewController touchesBegan:withEvent:]"breakpoint command add 2expression self.view.layer.backgroundColor = [UIColor redColor].CGColor過掉當(dāng)前斷點(diǎn),點(diǎn)擊屏幕,就會(huì)執(zhí)行在
touchesBegan函數(shù)停住,并執(zhí)行上面添加的三條指令;breakpoint command list 斷點(diǎn)編號(hào):打印指定斷點(diǎn)添加的所有執(zhí)行指令;breakpoint command delete 斷點(diǎn)編號(hào):刪除指定斷點(diǎn)添加的所有執(zhí)行指令;
內(nèi)存斷點(diǎn)
-
watchpoint set variable self->_age:監(jiān)聽成員變量_age的內(nèi)存,只要_age發(fā)生變化就會(huì)斷住; -
watchpoint set expression 對(duì)象的內(nèi)存地址:功能同上; -
watchpoint list:打印工程中所有的內(nèi)存斷點(diǎn) -
watchpoint delete 內(nèi)存斷點(diǎn)編號(hào):刪除指定內(nèi)存斷點(diǎn); -
watchpoint disable 內(nèi)存斷點(diǎn)編號(hào):禁用指定內(nèi)存斷點(diǎn); -
watchpoint enable 內(nèi)存斷點(diǎn)編號(hào):?jiǎn)⒂弥付▋?nèi)存斷點(diǎn); -
watchpoint command add 內(nèi)存斷點(diǎn)編號(hào):給指定的內(nèi)存斷點(diǎn)添加額外的執(zhí)行指令; -
watchpoint command list 內(nèi)存斷點(diǎn)編號(hào):打印指定內(nèi)存斷點(diǎn)添加的所有執(zhí)行指令; -
watchpoint command delete 內(nèi)存斷點(diǎn)編號(hào):刪除指定內(nèi)存斷點(diǎn)添加的所有執(zhí)行指令;
與模塊鏡像相關(guān)
-
image list:打印工程中引用的所有的模塊,也就是動(dòng)態(tài)庫(kù)文件,也可稱之為鏡像; -
image lookup -t 類型:查看某個(gè)類型的信息,例如:image lookup -t int -
image lookup -a 內(nèi)存地址:根據(jù)內(nèi)存地址查找在模塊中的位置,例如崩潰的代碼行定位; -
image lookup -n 符號(hào)或函數(shù)名:查找某個(gè)符號(hào)或者函數(shù)的位置;