i 19:Xcode調(diào)試器LLDB

相信大家肯定都有過為了調(diào)試而添加打印變量厉斟,或者使用直接常量代替函數(shù)調(diào)用結(jié)果,或者更改判斷條件以進入某特定分支的調(diào)試經(jīng)歷,但每次更改代碼都需要重新編譯形葬,重新來過一遍却汉,但其實本可以不用這樣,因為我們有調(diào)試器荷并,而且除了監(jiān)視變量的值合砂,還有很多它可以做的。

LLDB

LLDB是一個開源的以REPL為特性源织,并可以配置C++和python插件的調(diào)試器翩伪。它集成在xcode中,并在窗口底部的控制臺中運行谈息。調(diào)試器可以暫停程序執(zhí)行缘屹,觀察變量,執(zhí)行自定義指令侠仇,并掌控程序的執(zhí)行轻姿。如果對GDB比較熟悉的話,GDB-TO-LLDB?這個份指引應(yīng)該對你了解LLDB的指令有極大助益逻炊,如果安裝了Chisel這個LLDB插件的話互亮,調(diào)試會更有趣。

基礎(chǔ)操作

程序在斷點處暫停的時候余素,console會打開供輸入命令:

help

最簡單的命令即是help豹休,它會列出所有命令,如果忘記了help本身桨吊,help help試試

print?

用來打印值威根,由于 LLDB的前綴匹配,所以也可以使用prin,pri,但不可以用pr,因為還有一個命令是process视乐,但可以用p代表print洛搀。

注意到結(jié)果里面會有$n的字樣,帶$前綴的標識屬于LLDB命名空間佑淀,我們可以利用這個特性為自己服務(wù)

expression

修改變量值

注:有一個需要注意的是留美,如果使用中文字符串常量的時候,由于LLDB解析器的bug渣聚,會報錯An Objective-C constant string's string initializer is not an array独榴,需要使用[NSString stringWithUTF8String:]

list命令

list 行號 ? ? ? 顯示行號開始的數(shù)行代碼(默認10行)

list 函數(shù)名 ? ?顯示函數(shù)名為中心的前后十行代碼

list不帶參數(shù),接著上一次list命令

print 命令

如果執(zhí)行 p count = 18,會發(fā)現(xiàn)除了打印18之后奕枝,count的值也會變成18.它與expression count = 18執(zhí)行結(jié)果相同棺榔。不同的地方在于print 命令沒有參數(shù)。

想想 e -h +17 這行命令隘道,如果將-h理解為flag的話症歇, +17看起來并不像輸入郎笆。如果理解為計算17與h的差值,那這個連字符看起來很讓人困惑忘晤。

幸運的是宛蚓,使用--來分隔flag與其后的輸入,實際上e -- 的縮寫是print设塔。

打印對象

如果使用print objects,則輸入看起來十分冗長:(NSString *) $7 = 0x0000000104da4040 @"red balloons"凄吏,如果打印更復(fù)雜的結(jié)構(gòu) p @[ @"foo", @"bar" ],輸出可能像這樣(NSArray *) $8 = 0x00007fdb9b71b3e0 @"2 objects"闰蛔,而實際上我們想看對象的description痕钢,所以需要將對象按對象輸出,使用-O 選項序六。

同樣幸運的是任连,e -O --的別名是po (print object)

打印變量

可以在print命令中指定很多種格式,其形式像這樣print/<fmt>,或者p/<fmt>例诀,默認格式是這樣随抠,p 16 > 16,十六進制形式輸出 p/x 16 > 0x10, 二進制 (t代表two) p/t 16 > 0b10000

還可以用p/c輸出字符繁涂,p/s輸出c字符串拱她,這里是完整的輸出格式

變量

在lldb中可以使用變量以減少過多的typing,但在LLDB中定義的變量必須以$開頭爆土,比如e int $i = 1,e NSArray* $a = @[@"Saturday",@"Sunday"], p [$a count], po [[$a objectAtIndex:0] uppercaseString]

而如果輸入這條命令p [[$a objectAtIndex:$i] characterAtIndex:0],得到的結(jié)果是

error: no known method '-characterAtIndex:'; cast the message send to the method's return type

error: 1 errors parsing expression

因為LLDB無法識別涉及到的類型椭懊,這種情況有時候會發(fā)生,可以這樣處理

p (char)[[$a objectAtIndex:$i] characterAtIndex:0] ?> 'M'

或者p/d (char)[[$a objectAtIndex:$i] characterAtIndex:0] > 77

執(zhí)行流控制

命中斷點時步势,調(diào)試條上的4個按鈕可供控制程序的執(zhí)行流

執(zhí)行流控制按鈕

其意義從左到右分別是繼續(xù),越過背犯,進入函數(shù)體坏瘩,退出函數(shù)體

繼續(xù)按鈕的作用同LLDB的process continue,簡寫為continue,或者c

越過按鈕的作用相當于執(zhí)行完當前行的指令漠魏,就算當前當為函數(shù)調(diào)用倔矾,也不進入函數(shù)體,對應(yīng)LLDB中的thread step-over,next 或者n

進入函數(shù)體按鈕的作用相當于LLDB中的 thread step-in,step和s柱锹,而如果當前行非函數(shù)調(diào)用哪自,則其與thread step-over 表現(xiàn)是相同的

退出函數(shù)體的作用在于能夠一直執(zhí)行到當前函數(shù)return之后再中斷到調(diào)試器,即將當前棧幀彈出之后再停止禁熏。對應(yīng)LLDB中的命令finish

frame info

輸出當前代碼行及其所在在源代碼文件

線程return

thread return是控制執(zhí)行流的另一利器壤巷,其可攜帶選項參數(shù),其中將參數(shù)加載進返回寄存器瞧毙,并立即執(zhí)行return命令胧华,并跳出當前棧幀寄症,而這也意味著當前函數(shù)中剩余的語句不會被執(zhí)行。這樣做導(dǎo)致ARC的引用計數(shù)和追蹤方面的問題或者在函數(shù)尾所做的清理等被跳過矩动,但如果在進入函數(shù)體之后馬上執(zhí)行有巧,則其在假裝函數(shù)已經(jīng)執(zhí)行方面的作用還是很大的。

斷點篇


斷點示例代碼段

LLDB列舉斷點: breakpoint list/br li

enable/disable 斷點: breakpoint enable <breakpointID>/breakpoint disable <breakpointID>

設(shè)置斷點:(在Xcode中可以在編輯區(qū)的代碼行首點擊添加斷點悲没,以及鼠標拖拽斷點到代碼行首區(qū)釋放以移除斷點)

breakpoint set:? 比如breakpoint set -f main.m -l 16

由于b是_regexp-break的簡寫篮迎,所以breakpoint的縮寫為br

但其實使用b設(shè)置斷點,LLDB在通常情況下也是可以識別的示姿,比如b main.m:17同樣可以設(shè)置斷點成功

其實也可以使用符號(C函數(shù)來設(shè)置斷點,不用指定行號),比如

(lldb) b isEven

(lldb) br s -F isEven

這樣可以使得調(diào)用此函數(shù)時會在其入口暫停柑潦,也可以使用oc 方法設(shè)置斷點

(lldb) breakpoint set -F "-[NSArray objectAtIndex:]"

(lldb) b -[NSArray objectAtIndex:]

(lldb) breakpoint set -F "+[NSSet setWithObject:]"

(lldb) b +[NSSet setWithObject:]

如果想創(chuàng)建一個符號斷點,可以在斷點導(dǎo)航頁中點擊“+”

breakpoint navigator

同時在右鍵每個現(xiàn)有斷點時都會出現(xiàn)包含edit breakpoint項的菜單峻凫,點擊之后

edit breakpoint

Add Action的動作在如下詳細介紹

Breakpoint Actions

這個功能很有用渗鬼,可以在斷點發(fā)生之后,馬上執(zhí)行你所定義的動作之后再將控制權(quán)交給你荧琼,即lldb命令行譬胎。action支持多行Debugger command,shell command,log message,使用lldb而非UI操作的方法為

lldb編輯斷點

Continuing after Evaluation

在編輯斷點的UI中命锄,options項Automatically continue after evaluation actions勾選可以使得在斷點處執(zhí)行完預(yù)設(shè)action之后馬上恢復(fù)運行堰乔,就像沒斷點一樣。

Full Execution in the Debugger

還有一個特性是可以在debugger中運行任何C/Objective-C/C++/Swift命令脐恩,一點不足之外是不能創(chuàng)建新的函數(shù)镐侯,即不能創(chuàng)建新類,block,函數(shù)驶冒,帶虛方法的C++類等苟翻,除此之外都可以。

比如可以分配數(shù)字節(jié)的空間

lldb分配內(nèi)存空間

不過要注意骗污,分配空間的時候崇猫,其作用域是與當前棧幀中當前代碼行所在的作用域相同的,所以要盡量避免因作用域問題引起的執(zhí)行異常

也可以使用x命令查看新數(shù)組的4個字節(jié)

(lldb) x/4c $str

0x7fd04a900040: monk

也可以查看數(shù)組第3個字節(jié)開始的內(nèi)容(x命令需要反引號需忿,需要地址作為參數(shù)):

(lldb) x/1w `$str + 3`

0x7fd04a900043: keys

但所有這些操作結(jié)束之后诅炉,確保釋放這些內(nèi)存,以免引起內(nèi)存泄漏(在調(diào)試器中):

(lldb) e (void)free($str)

根據(jù)以上內(nèi)容我們可以做的

在調(diào)試條中的暫停按鈕可以暫停當前app,實際是其是執(zhí)行了process interrupt屋厘,因為調(diào)試器其實一直都在執(zhí)行場景之中涕烧。雖然此時的中斷可能并沒有暫停在你熟悉的代碼上下文中,但其實可以試試這個:

(lldb) po [[[UIApplication sharedApplication] keyWindow] recursiveDescription]

更新UI

基于上述輸出汗洒,可以從UI層級中選擇一個來操作议纯,假定其地址為0xadd2e55

(lldb) e id $myView = (id)0xadd2e55

(lldb) e (void)[$myView setBackgroundColor:[UIColor blueColor]]

走到恢復(fù)app的執(zhí)行才會看到UI的變化,因為需要將這個信息通知給渲染Server顯示才會更新仲翎。

render server實際是另一個進程backboardd痹扇,但在我們在調(diào)試當前進程的時候铛漓,backboardd是沒有暫停的,所以其實我們可以執(zhí)行(lldb) e (void)[CATransaction flush]鲫构,這樣就可以在不恢復(fù)執(zhí)行的情況下看到UI更新浓恶。chisel提供了caflush來完成這個更新的功能(chisel還有更多方便的功能)

Pushing a View Controller

假設(shè)當前app的root vc為UINavigationController,則

(lldb) e id $nvc = [[[UIApplication sharedApplication] keyWindow] rootViewController](lldb) e id $vc = [UIViewController new]

(lldb) e (void)[[$vc view] setBackgroundColor:[UIColor yellowColor]]

(lldb) e (void)[$vc setTitle:@"Yay!"]

(lldb) e (void)[$nvc pushViewContoller:$vc animated:YES]

即可以添加一個子view controller,然后執(zhí)行(lldb) caflush // e (void)[CATransaction flush]结笨,就可以看見一個view controller被push進當前View Controller層級

筆者在使用presentViewController測試的時候包晰,執(zhí)行flush之后未見到UI有更新,然后執(zhí)行continue才看到vc被present進來炕吸,xcode 6.4,iOS 8.3伐憾,真機和模擬器均是這樣

Finding the Target of a Button

如果想知道$myButton相應(yīng)的監(jiān)聽action有哪些,可以這樣

(lldb) po [$myButton allTargets]{()}

(lldb) po [$myButton actionsForTarget:(id)0x7fb58bd2e240 forControlEvent:0]

<__NSArrayM 0x7fb58bd2aa40>(

_handleTap:

)

這個時候赫模,對_handleTap就想怎樣就怎樣設(shè)置斷點

觀察實例變量的更改

假定UIView的_layer被覆寫树肃,由于不涉及到方法的調(diào)用,無法設(shè)置斷點瀑罗。于是我們可以設(shè)置對某地址的寫入胸嘴,先找下_layer 變量在對象中的所在

(lldb) p (ptrdiff_t)ivar_getOffset((struct Ivar *)class_getInstanceVariable([MyView class], "_layer"))

(ptrdiff_t) $0 = 8

于是我們知道$myView+8即是我們關(guān)心的地址,于是

(lldb) watchpoint set expression -- (int *)$myView + 8

Watchpoint created: Watchpoint 3: addr = 0x7fa554231340 size = 8 state = enabled type = w

new value: 0x0000000000000000

在Chisel中斩祭,上述功能被精簡為命令wivar $myView _layer

未被重載的方法上的符號化斷點

假定想對-[MyViewController viewDidAppear:]的調(diào)用時機做監(jiān)聽劣像,但vc本身未重載此方法,如果這樣設(shè)置斷點

(lldb) b -[MyViewController viewDidAppear:]

Breakpoint 1: no locations (pending).

WARNING:? Unable to resolve breakpoint to any actual locations.

但由于未重載此方法摧玫,所以不會有viewDidAppear的符號耳奕,故斷點不會命中。這種情況下需要設(shè)置條件 [self isKindOfClass:[MyViewController class]]诬像,并將此條件置于UIViewController之上屋群。但由于我們并未擁有UIViewController viewDidAppear的實現(xiàn),其為apple所編寫颅停,故沒有符號 谓晌;且在此方法之內(nèi)并沒有self可用。由于如果在符號化斷點中想用self, 那首先要知道self的位置(可能在寄存器中也可能在棧上癞揉,x86中可以在$esp+4中找到,但最少有4種硬件架構(gòu)啊溺欧,親)喊熟。難以想象了解每種硬件架構(gòu)的指令集和調(diào)用規(guī)則,然后再編寫在正確的父類上使用正確的條件設(shè)置斷點的命令姐刁。幸運的是芥牌,使用Chisel可以很輕松地做到:

(lldb) bmessage -[MyViewController viewDidAppear:]

Setting a breakpoint at -[UIViewController viewDidAppear:] with condition (void*)object_getClass((id)$rdi) == 0x000000010e2f4d28

Breakpoint 1: where = UIKit`-[UIViewController viewDidAppear:], address = 0x000000010e11533c

這篇文章的作者為了推廣Chisel也是夠拼的⊙﹏⊙

LLDB and Python

LLDB擁有全面而且是內(nèi)置的Python支持,如果在LLDB中輸入script聂使,會打開Python REPL(Read-Eval-Print Loop),相當于python命令行壁拉。當然也可以使用script command執(zhí)行python命令谬俄。

(lldb) script import os

(lldb) script os.system("open http://www.objc.io/")

接下來的事情就多了,可以使用py腳本文件弃理,比如~/myCommands.py

def caflushCommand(debugger, command, result, internal_dict):

debugger.HandleCommand("e (void)[CATransaction flush]")

然后在lldb中執(zhí)行

(lldb) script import ~/myCommands.py

而且溃论,也可以將上述導(dǎo)入放入/.lldbinit以在每次LLDB啟動時都執(zhí)行,Chisel所做的也都是拼接字符串并交給LLDB執(zhí)行而已痘昌。


錯誤處理

1 退出lldb repl的方法钥勋,使用 : 命令

2 一個需要注意的問題是在lldb中創(chuàng)建對象的時候,從init方法返回時辆苔,可能會報錯 cast the message send to the methods return type算灸,這時候不要慌,將對象顯式轉(zhuǎn)換為相應(yīng)的對象類型驻啤,比如 (CGRect)[[UIView alloc] init],另外也需要 (CGRect)[view frame]

因為lldb并不支持.語法方法調(diào)用菲驴,同時還需要顯式轉(zhuǎn)換以及必要時候的括號將整個對象包裹起來,以告訴lldb骑冗,我們的輸入表示的絕對是一個對象

3 如果在lldb中輸入expr -- content.text = (NSString*)[NSString stringWithFormat:@"e"]

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末赊瞬,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子沐旨,更是在濱河造成了極大的恐慌森逮,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件磁携,死亡現(xiàn)場離奇詭異褒侧,居然都是意外死亡,警方通過查閱死者的電腦和手機谊迄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門闷供,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人统诺,你說我怎么就攤上這事歪脏。” “怎么了粮呢?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵婿失,是天一觀的道長。 經(jīng)常有香客問我啄寡,道長豪硅,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任挺物,我火速辦了婚禮懒浮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘识藤。我一直安慰自己砚著,他們只是感情好次伶,可當我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著稽穆,像睡著了一般冠王。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上秧骑,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天版确,我揣著相機與錄音,去河邊找鬼乎折。 笑死绒疗,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的骂澄。 我是一名探鬼主播吓蘑,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼坟冲!你這毒婦竟也來了磨镶?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤健提,失蹤者是張志新(化名)和其女友劉穎琳猫,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體私痹,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡脐嫂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了紊遵。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片账千。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖暗膜,靈堂內(nèi)的尸體忽然破棺而出匀奏,到底是詐尸還是另有隱情,我是刑警寧澤学搜,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布娃善,位于F島的核電站,受9級特大地震影響瑞佩,放射性物質(zhì)發(fā)生泄漏会放。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一钉凌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧捂人,春花似錦御雕、人聲如沸矢沿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽捣鲸。三九已至,卻和暖如春闽坡,著一層夾襖步出監(jiān)牢的瞬間栽惶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工疾嗅, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留外厂,地道東北人。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓代承,卻偏偏與公主長得像汁蝶,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子论悴,可洞房花燭夜當晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容