序:原文 Dancing in the Debugger — A Waltz with LLDB
聲明:譯文有一部分參考自:與調(diào)試器共舞 - LLDB 的華爾茲
前言
你是否曾經(jīng)為試圖理解尼的代碼和打印一個(gè)變量的值而感到苦惱靴寂?
NSLog(@"%@", whatIsInsideThisThing);
或者跳過(guò)一個(gè)函數(shù)調(diào)用來(lái)簡(jiǎn)化程序的行為?
NSNumber *n = @7; // theFunctionThatShouldReallyBeCalled();
或短路一個(gè)邏輯檢查?
if (1 || theBooleanAtStake) { ... }
抑或偽造一個(gè)函數(shù)的實(shí)現(xiàn)?
int calculateTheTrickyValue {
return 9;
/*
Figure this out later.
...
}
并且每次都必須重新編譯只锭,重新運(yùn)行嗎?
構(gòu)建軟件是復(fù)雜的叶沛,并且bug總是會(huì)出現(xiàn)图贸。常見(jiàn)的修復(fù)周期是修改代碼新症,編譯罢低,再次運(yùn)行坯钦,并希望變的最好。
其實(shí)并不需要這樣益咬。您可以使用調(diào)試器哈!即使你已經(jīng)知道如何使用調(diào)試器來(lái)檢查一個(gè)變量,可是調(diào)試器可以做的遠(yuǎn)不止這些幽告。
本文打算挑戰(zhàn)你對(duì)調(diào)試的認(rèn)知梅鹦,更詳細(xì)地解釋了一些你可能不知道的基本原理,然后給你展示一些有趣的例子冗锁。讓我們隨著舞曲開(kāi)始旋轉(zhuǎn)起來(lái)齐唆,看看我們會(huì)以何種水平結(jié)束。
LLDB
LLDB 是一個(gè)有著 REPL
特性冻河,并內(nèi)置 C++
和 Python
插件的開(kāi)源調(diào)試器箍邮。該調(diào)試器捆綁在Xcode內(nèi)部,并內(nèi)置于Xcode窗口底部的控制臺(tái)面板里叨叙。調(diào)試器允許您在程序運(yùn)行的特定時(shí)刻暫停程序锭弊,來(lái)檢查變量的值,執(zhí)行自定義的指令擂错,然后按照你所認(rèn)為合適的步驟來(lái)操作程序的進(jìn)展味滞。(這里 是調(diào)試器如何工作的大體介紹。)
之前尼很有可能使用過(guò)調(diào)試器钮呀,即使只是在Xcode窗口頁(yè)面添加斷點(diǎn)剑鞍。但是有一些小竅門(mén),可以使你有一些很漂亮而又酷爽的事情去做爽醋。GDB to LLDB 參考的是一個(gè)偉大的可用命令的鳥(niǎo)瞰圖蚁署,你還有可能想要安裝 Chisel ,一個(gè)可以使調(diào)試器更加有趣的 LLDB 插件的開(kāi)源合輯蚂四。
與此同時(shí)光戈,讓我們以 在調(diào)試器中如何打印一個(gè)變量的值 來(lái)開(kāi)始這場(chǎng)華爾茲的旅程吧。
基礎(chǔ)知識(shí)
這里是一段打印一個(gè)字符串的簡(jiǎn)單示例小程序证杭。注意田度,在第16
行添加了一個(gè)斷點(diǎn):
程序會(huì)在第16
行被暫停運(yùn)行,并且控制臺(tái)會(huì)被打開(kāi)解愤,允許我們和調(diào)試器交互镇饺。那我們應(yīng)該輸入些什么呢?
help
最簡(jiǎn)單的命令是 help
送讲,這一指令將會(huì)列出所有的命令奸笤。如果你忘記某條命令是做什么的或者想了解更多的命令,你可以使用 help
命令查看更多的細(xì)節(jié)哼鬓,例如 help print
或 help thread
监右。如果你忘記了 help
命令是做什么的?你可以使用 help help
命令异希,但如果你知道的足夠多健盒,也許你還沒(méi)有完全忘記命令是做什么的。
使用 print
命令打印一個(gè)值是很簡(jiǎn)單的。
LLDB 實(shí)際上會(huì)做前綴匹配扣癣,因此尼也可以使用
prin
惰帽、 pri
、 p
命令父虑,但是不可以使用 pr
该酗,因?yàn)?LLDB 不能把它和 process
命令做區(qū)分(幸運(yùn)的是,p
并沒(méi)有歧義)士嚎。你可能還會(huì)注意到呜魄,結(jié)果中有個(gè)
$0
。實(shí)際上你可以使用它來(lái)代指這個(gè)結(jié)果莱衩。試試 print $0 + 7
爵嗅,你會(huì)看到 45
。任何以美元符開(kāi)頭的東西都是存在于 LLDB 的命名空間的膳殷,它們是為了幫助你進(jìn)行調(diào)試而存在的操骡。
expression
如果想修改一個(gè)值怎么辦?這里使用 expression
命令赚窃。
這一命令不僅僅修改了調(diào)試器中的值册招,實(shí)際上還修改了程序中的值。如果你在這個(gè)基礎(chǔ)上繼續(xù)執(zhí)行程序勒极,將會(huì)打印
83 huang qimeng
是掰,神奇吧!從現(xiàn)在開(kāi)始辱匿,我們下面會(huì)使用這兩個(gè)命令的簡(jiǎn)化形式
p
键痛、 e
。
什么是 print 命令
這里有一個(gè)有趣的表達(dá)式:p count = 18
匾七,如果我們執(zhí)行這個(gè)命令并打印count
的值絮短,我們會(huì)看到結(jié)果和這個(gè)表達(dá)式 expression count = 18
是一個(gè)吊樣的。
和expression
不同的是昨忆,print
不需要參數(shù)丁频,比如e -h +17
到底是以-h
為標(biāo)識(shí),僅僅執(zhí)行+17
呢邑贴,還是計(jì)算17
和h
的差值呢席里?連字符確實(shí)讓人困惑,你可能得不到尼想要的結(jié)果拢驾。
幸運(yùn)的是解決方式很簡(jiǎn)單奖磁,使用--
表示標(biāo)識(shí)的結(jié)束,和輸入的開(kāi)始繁疤,比如以-h
作為標(biāo)識(shí)咖为,就要用e -h -- +17
秕狰,如果想計(jì)算它們的差值,就使用e -- -h +17
躁染,一般來(lái)說(shuō)封恰,不使用標(biāo)識(shí)的情況比較普遍,所以e --
就有了一個(gè)簡(jiǎn)寫(xiě)的方式褐啡,即print
。
輸入help print
鳖昌,然后向下滾動(dòng)备畦,就會(huì)發(fā)現(xiàn):
'print' is an abbreviation for 'expression --'.
(print是 `expression --` 的簡(jiǎn)寫(xiě))
打印對(duì)象
輸入:
p objects
然后輸出是一堆奇怪的東西:
(NSString *) $7 = 0x0000000104da4040 @"huang qimeng"
如果我們嘗試打印結(jié)構(gòu)更復(fù)雜的對(duì)象,結(jié)果甚至?xí)悖?/p>
(lldb) p @[ @"foo", @"bar" ]
(NSArray *) $8 = 0x00007fdb9b71b3e0 @"2 objects"
實(shí)際上许昨,我們想看的是對(duì)象的 description
方法的結(jié)果懂盐。我么需要使用 -O
(注意
是字母 O,而不是數(shù)字 0) 標(biāo)志告訴expression
命令以對(duì)象 (Object)
的方式來(lái)打印結(jié)果糕档。
(lldb) e -O -- $8
<__NSArrayI 0x7fdb9b71b3e0>(
foo,
bar
)
幸運(yùn)的是莉恼,e -o --
也有個(gè)別名,即po
(print object
的縮寫(xiě))速那,我們可以這樣使用:
(lldb) po $8
<__NSArrayI 0x7fdb9b71b3e0>(
foo,
bar
)
(lldb) po @"lunar"
lunar
(lldb) p @"lunar"
(NSString *) $13 = 0x00007fdb9d0003b0 @"lunar"
打印變量
可以為print
指定不同的打印格式俐银。他們都以print/<fmt>
或者簡(jiǎn)化的p/<fmt>
格式書(shū)寫(xiě)。例子如下:
默認(rèn)格式:
(lldb) p 16
16
十六進(jìn)制:
(lldb) p/x 16
0x10
二進(jìn)制(t
代表two
):
(lldb) p/t 16
0b00000000000000000000000000010000
(lldb) p/t (char)16
0b00010000
你也可以使用p/c
打印字符端仰,或者p/s
打印以空終止的字符串*char **捶惜,這里是輸出格式的完整說(shuō)明。
變量
現(xiàn)在你已經(jīng)可以打印對(duì)象和簡(jiǎn)單類型的變量了荔烧,以及如何使用expression
命令在調(diào)試器中修改他們了吱七。~~此處廢話不翻譯~~,不過(guò)為了能使用聲明的變量鹤竭,變量必須以美元符號(hào)$
開(kāi)頭踊餐。
(lldb) e int $a = 2
(lldb) p $a * 19
38
(lldb) e NSArray *$array = @[ @"Saturday", @"Sunday", @"Monday" ]
(lldb) p [$array count]
2
(lldb) po [[$array objectAtIndex:0] uppercaseString]
SATURDAY
(lldb) p [[$array objectAtIndex:$a] characterAtIndex:0]
error: no known method '-characterAtIndex:'; cast the message send to the method's return type
error: 1 errors parsing expression
糟了~,LLDB無(wú)法分辨涉及的類型(注:返回的類型)臀稚,這種事情經(jīng)常出現(xiàn)吝岭,給個(gè)說(shuō)明就好了:
(lldb) p (char)[[$array objectAtIndex:$a] characterAtIndex:0]
'M'
(lldb) p/d (char)[[$array objectAtIndex:$a] characterAtIndex:0]
77
變量使調(diào)試器的使用變得更容易了,你想不到吧烁涌???