小笨狼與LLDB的故事

LLDB的Xcode默認(rèn)的調(diào)試器,它與LLVM編譯器一起疚脐,帶給我們更豐富的流程控制和數(shù)據(jù)檢測(cè)的調(diào)試功能。平時(shí)用Xcode運(yùn)行程序邢疙,實(shí)際走的都是LLDB棍弄。熟練使用LLDB,可以讓你debug事半功倍

LLDB基礎(chǔ)知識(shí)

LLDB控制臺(tái)

Xcode中內(nèi)嵌了LLDB控制臺(tái)疟游,在Xcode中代碼的下方呼畸,我們可以看到LLDB控制臺(tái)。



LLDB控制臺(tái)平時(shí)會(huì)輸出一些log信息颁虐。如果我們想輸入命令調(diào)試蛮原,必須讓程序進(jìn)入暫停狀態(tài)。讓程序進(jìn)入暫停狀態(tài)的方式主要有2種:

  1. 斷點(diǎn)或者watchpoint: 在代碼中設(shè)置一個(gè)斷點(diǎn)(watchpoint)另绩,當(dāng)程序運(yùn)行到斷點(diǎn)位置的時(shí)候儒陨,會(huì)進(jìn)入stop狀態(tài)
  2. 直接暫停,控制臺(tái)上方有一個(gè)暫停按鈕笋籽,上圖紅框已標(biāo)出蹦漠,點(diǎn)擊即可暫停程序

LLDB語(yǔ)法

在使用LLDB之前,我們來(lái)先看看LLDB的語(yǔ)法车海,了解語(yǔ)法可以幫助我們清晰的使用LLDB:

<command> [<subcommand> [<subcommand>...]] <action> [-options [option-value]] [argument [argument...]]

一眼看上去可能比較迷茫笛园,給大家解釋一下:

  1. <command>(命令)和<subcommand>(子命令):LLDB調(diào)試命令的名稱。命令和子命令按層級(jí)結(jié)構(gòu)來(lái)排列:一個(gè)命令對(duì)象為跟隨其的子命令對(duì)象創(chuàng)建一個(gè)上下文侍芝,子命令又為其子命令創(chuàng)建一個(gè)上下文研铆,依此類推。
  2. <action>:執(zhí)行命令的操作
  3. <options>:命令選項(xiàng)
  4. <arguement>:命令的參數(shù)
  5. []:表示命令是可選的州叠,可以有也可以沒(méi)有

舉個(gè)例子棵红,假設(shè)我們給main方法設(shè)置一個(gè)斷點(diǎn),我們使用下面的命令:

breakpoint set -n main

這個(gè)命令對(duì)應(yīng)到上面的語(yǔ)法就是:

  1. command: breakpoint 表示斷點(diǎn)命令
  2. action: set 表示設(shè)置斷點(diǎn)
  3. option: -n 表示根據(jù)方法name設(shè)置斷點(diǎn)
  4. arguement: mian 表示方法名為mian

原始(raw)命令

LLDB支持不帶命令選項(xiàng)(options)的原始(raw)命令留量,原始命令會(huì)將命令后面的所有東西當(dāng)做參數(shù)(arguement)傳遞。不過(guò)很多原始命令也可以帶命令選項(xiàng)哟冬,當(dāng)你使用命令選項(xiàng)的時(shí)候楼熄,需要在命令選項(xiàng)后面加--區(qū)分命令選項(xiàng)和參數(shù)。

e.g: 常用的expression就是raw命令浩峡,一般情況下我們使用expression打印一個(gè)東西是這樣的:

(lldb) expression count
(int) $2 = 4

當(dāng)我們想打印一個(gè)對(duì)象的時(shí)候可岂。需要使用-O命令選項(xiàng),我們應(yīng)該用--將命令選項(xiàng)和參數(shù)區(qū)分:

(lldb) expression -O -- self
<ViewController: 0x7f9000f17660>

唯一匹配原則

LLDB的命令遵循唯一匹配原則:假如根據(jù)前n個(gè)字母已經(jīng)能唯一匹配到某個(gè)命令翰灾,則只寫(xiě)前n個(gè)字母等效于寫(xiě)下完整的命令缕粹。
e.g: 前面提到我設(shè)置斷點(diǎn)的命令稚茅,我們可以使用唯一匹配原則簡(jiǎn)寫(xiě),下面2條命令等效:

breakpoint set -n main
br s -n main

~/.lldbinit

LLDB有了一個(gè)啟動(dòng)時(shí)加載的文件~/.lldbinit平斩,每次啟動(dòng)都會(huì)加載亚享。所以一些初始化的事兒,我們可以寫(xiě)入~/.lldbinit中绘面,比如給命令定義別名等欺税。但是由于這時(shí)候程序還沒(méi)有真正運(yùn)行晚凿,也有部分操作無(wú)法在里面玩瘦馍,比如設(shè)置斷點(diǎn)。

LLDB命令

expression

expression命令的作用是執(zhí)行一個(gè)表達(dá)式燥筷,并將表達(dá)式返回的結(jié)果輸出荆责。expression的完整語(yǔ)法是這樣的:

expression <cmd-options> -- <expr>
  1. <cmd-options>:命令選項(xiàng)亚脆,一般情況下使用默認(rèn)的即可,不需要特別標(biāo)明濒持。
  2. --: 命令選項(xiàng)結(jié)束符键耕,表示所有的命令選項(xiàng)已經(jīng)設(shè)置完畢,如果沒(méi)有命令選項(xiàng)柑营,--可以省略
  3. <expr>: 要執(zhí)行的表達(dá)式

說(shuō)expression是LLDB里面最重要的命令都不為過(guò)屈雄。因?yàn)樗軐?shí)現(xiàn)2個(gè)功能。

  • 執(zhí)行某個(gè)表達(dá)式官套。
    我們?cè)诖a運(yùn)行過(guò)程中酒奶,可以通過(guò)執(zhí)行某個(gè)表達(dá)式來(lái)動(dòng)態(tài)改變程序運(yùn)行的軌跡。
    假如我們?cè)谶\(yùn)行過(guò)程中奶赔,突然想把self.view顏色改成紅色惋嚎,看看效果。我們不必寫(xiě)下代碼站刑,重新run另伍,只需暫停程序,用expression改變顏色绞旅,再刷新一下界面摆尝,就能看到效果

    // 改變顏色
    (lldb) expression -- self.view.backgroundColor = [UIColor redColor]
    // 刷新界面
    (lldb) expression -- (void)[CATransaction flush]
    
    
  • 將返回值輸出温艇。
    也就是說(shuō)我們可以通過(guò)expression來(lái)打印東西堕汞。
    假如我們想打印self.view:

    (lldb) expression -- self.view
    (UIView *) $1 = 0x00007fe322c18a10

p & print & call

一般情況下臼朗,我們直接用expression還是用得比較少的绣否,更多時(shí)候我們用的是pprint段磨、call。這三個(gè)命令其實(shí)都是expression --的別名(--表示不再接受命令選項(xiàng)债蜜,詳情見(jiàn)前面原始(raw)命令這一節(jié))

  1. print: 打印某個(gè)東西寻定,可以是變量和表達(dá)式
  2. p: 可以看做是print的簡(jiǎn)寫(xiě)
  3. call: 調(diào)用某個(gè)方法。

表面上看起來(lái)他們可能有不一樣的地方精耐,實(shí)際都是執(zhí)行某個(gè)表達(dá)式(變量也當(dāng)做表達(dá)式)向胡,將執(zhí)行的結(jié)果輸出到控制臺(tái)上。所以你可以用p調(diào)用某個(gè)方法淮捆,也可以用call打印東西
e.g: 下面代碼效果相同:

(lldb) expression -- self.view
(UIView *) $5 = 0x00007fb2a40344a0
(lldb) p self.view
(UIView *) $6 = 0x00007fb2a40344a0
(lldb) print self.view
(UIView *) $7 = 0x00007fb2a40344a0
(lldb) call self.view
(UIView *) $8 = 0x00007fb2a40344a0
(lldb) e self.view
(UIView *) $9 = 0x00007fb2a40344a0

根據(jù)唯一匹配原則攀痊,如果你沒(méi)有自己添加特殊的命令別名。e也可以表示expression的意思棘街。原始命令默認(rèn)沒(méi)有命令選項(xiàng),所以e也能帶給你同樣的效果

po

我們知道险污,OC里所有的對(duì)象都是用指針表示的,所以一般打印的時(shí)候蚁飒,打印出來(lái)的是對(duì)象的指針,而不是對(duì)象本身弦蹂。如果我們想打印對(duì)象。我們需要使用命令選項(xiàng):-O脑漫。為了更方便的使用,LLDB為expression -O --定義了一個(gè)別名:po

(lldb) expression -- self.view
(UIView *) $13 = 0x00007fb2a40344a0
(lldb) expression -O -- self.view
<UIView: 0x7fb2a40344a0; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7fb2a4018c80>>
(lldb) po self.view
<UIView: 0x7fb2a40344a0; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7fb2a4018c80>>

還有其他很多命令選項(xiàng),不過(guò)我們一般用得比較少碳却,所以我就不具體的一一介紹了馍资,如果想了解,在LLDB控制臺(tái)上輸入:help expression即可查到expression所有的信息

thread

thread backtrace & bt

有時(shí)候我們想要了解線程堆棧信息建钥,可以使用thread backtrace
thread backtrace作用是將線程的堆棧打印出來(lái)。我們來(lái)看看他的語(yǔ)法

thread backtrace [-c <count>] [-s <frame-index>] [-e <boolean>]

thread backtrace后面跟的都是命令選項(xiàng):

-c:設(shè)置打印堆棧的幀數(shù)(frame)
-s:設(shè)置從哪個(gè)幀(frame)開(kāi)始打印
-e:是否顯示額外的回溯
實(shí)際上這些命令選項(xiàng)我們一般不需要使用。
e.g: 當(dāng)發(fā)生crash的時(shí)候馋吗,我們可以使用thread backtrace查看堆棧調(diào)用

(lldb) thread backtrace
* thread #1: tid = 0xdd42, 0x000000010afb380b libobjc.A.dylib`objc_msgSend + 11, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
    frame #0: 0x000000010afb380b libobjc.A.dylib`objc_msgSend + 11
  * frame #1: 0x000000010aa9f75e TLLDB`-[ViewController viewDidLoad](self=0x00007fa270e1f440, _cmd="viewDidLoad") + 174 at ViewController.m:23
    frame #2: 0x000000010ba67f98 UIKit`-[UIViewController loadViewIfRequired] + 1198
    frame #3: 0x000000010ba682e7 UIKit`-[UIViewController view] + 27
    frame #4: 0x000000010b93eab0 UIKit`-[UIWindow addRootViewControllerViewIfPossible] + 61
    frame #5: 0x000000010b93f199 UIKit`-[UIWindow _setHidden:forced:] + 282
    frame #6: 0x000000010b950c2e UIKit`-[UIWindow makeKeyAndVisible] + 42

我們可以看到crash發(fā)生在-[ViewController viewDidLoad]中的第23行,只需檢查這行代碼是不是干了什么非法的事兒就可以了。

LLDB還為backtrace專門(mén)定義了一個(gè)別名:bt崇堰,他的效果與thread backtrace相同,如果你不想寫(xiě)那么長(zhǎng)一串字母特幔,直接寫(xiě)下bt即可:

(lldb) bt

thread return

Debug的時(shí)候,也許會(huì)因?yàn)楦鞣N原因村刨,我們不想讓代碼執(zhí)行某個(gè)方法打洼,或者要直接返回一個(gè)想要的值炫惩。這時(shí)候就該thread return上場(chǎng)了芭毙。

thread return [<expr>]

thread return可以接受一個(gè)表達(dá)式粘咖,調(diào)用命令之后直接從當(dāng)前的frame返回表達(dá)式的值。

e.g: 我們有一個(gè)someMethod方法,默認(rèn)情況下是返回YES路呜。我們想要讓他返回NO

我們只需在方法的開(kāi)始位置加一個(gè)斷點(diǎn)烦感,當(dāng)程序中斷的時(shí)候晌该,輸入命令即可:

(lldb) thread return NO

效果相當(dāng)于在斷點(diǎn)位置直接調(diào)用return NO;,不會(huì)執(zhí)行斷點(diǎn)后面的代碼

c & n & s & finish

一般在調(diào)試程序的時(shí)候,我們經(jīng)常用到下面這4個(gè)按鈕:

用觸摸板的孩子們可能會(huì)覺(jué)得點(diǎn)擊這4個(gè)按鈕比較費(fèi)勁蚜锨。其實(shí)LLDB命令也可以完成上面的操作晨抡,而且如果不輸入命令如捅,直接按Enter鍵烈涮,LLDB會(huì)自動(dòng)執(zhí)行上次的命令。按一下Enter就能達(dá)到我們想要的效果窖剑,有木有頓時(shí)感覺(jué)逼格滿滿的<崆ⅰ!西土!
我們來(lái)看看對(duì)應(yīng)這4個(gè)按鈕的LLDB命令:

  1. c/ continue/ thread continue: 這三個(gè)命令效果都等同于上圖中第一個(gè)按鈕的讶舰。表示程序繼續(xù)運(yùn)行
  2. n/ next/ thread step-over: 這三個(gè)命令效果等同于上圖第二個(gè)按鈕跳昼。表示單步運(yùn)行
  3. s/ step/ thread step-in: 這三個(gè)命令效果等同于上圖第三個(gè)按鈕。表示進(jìn)入某個(gè)方法
  4. finish/ step-out: 這兩個(gè)命令效果等同于第四個(gè)按鈕彻亲。表示直接走完當(dāng)前方法踪旷,返回到上層frame

thread其他不常用的命令

thread 相關(guān)的還有其他一些不常用的命令,這里就簡(jiǎn)單介紹一下即可碳锈,如果需要了解更多艺智,可以使用命令help thread查閱

  1. thread jump: 直接讓程序跳到某一行。由于ARC下編譯器實(shí)際插入了不少retain,release命令非竿。跳過(guò)一些代碼不執(zhí)行很可能會(huì)造成對(duì)象內(nèi)存混亂發(fā)生crash零聚。
  2. thread list: 列出所有的線程
  3. thread select: 選擇某個(gè)線程
  4. thread until: 傳入一個(gè)line的參數(shù)颂龙,讓程序執(zhí)行到這行的時(shí)候暫停
  5. thread info: 輸出當(dāng)前線程的信息

frame

前面我們提到過(guò)很多次frame(幀)浪规≌兜可能有的朋友對(duì)frame這個(gè)概念還不太了解锻霎。隨便打個(gè)斷點(diǎn)

我們?cè)诳刂婆_(tái)上輸入命令bt奠衔,可以打印出來(lái)所有的frame。如果仔細(xì)觀察拴事,這些frame和左邊紅框里的堆棧是一致的圣蝎。平時(shí)我們看到的左邊的堆棧就是frame。

frame variable

平時(shí)Debug的時(shí)候我們經(jīng)常做的事就是查看變量的值坦袍,通過(guò)frame variable命令蘑险,可以打印出當(dāng)前frame的所有變量

(lldb) frame variable
(ViewController *) self = 0x00007fa158526e60
(SEL) _cmd = "text:"
(BOOL) ret = YES
(int) a = 3

可以看到潜沦,他將self,_cmd,ret,a等本地變量都打印了出來(lái)

如果我們要需要打印指定變量,也可以給frame variable傳入?yún)?shù):

(lldb) frame variable self->_string
(NSString *) self->_string = nil

不過(guò)frame variable只接受變量作為參數(shù)肥卡,不接受表達(dá)式溪掀,也就是說(shuō)我們無(wú)法使用frame variable self.string,因?yàn)?code>self.string是調(diào)用stringgetter方法步鉴。所以一般打印指定變量揪胃,我更喜歡用p或者po

其他不常用命令

一般frame variable打印所有變量用得比較多唠叛,frame還有2個(gè)不怎么常用的命令:

frame info: 查看當(dāng)前frame的信息

(lldb) frame info
frame #0: 0x0000000101bf87d5 TLLDB`-[ViewController text:](self=0x00007fa158526e60, _cmd="text:", ret=YES) + 37 at ViewController.m:38

frame select: 選擇某個(gè)frame

(lldb) frame select 1
frame #1: 0x0000000101bf872e TLLDB`-[ViewController viewDidLoad](self=0x00007fa158526e60, _cmd="viewDidLoad") + 78 at ViewController.m:23
   20   
   21   - (void)viewDidLoad {
   22       [super viewDidLoad];
-> 23       [self text:YES];
   24       NSLog(@"1");
   25       NSLog(@"2");
   26       NSLog(@"3");

當(dāng)我們選擇frame 1的時(shí)候只嚣,他會(huì)把frame1的信息和代碼打印出來(lái)沮稚。不過(guò)一般我都是直接在Xcode左邊點(diǎn)擊某個(gè)frame艺沼,這樣更方便

breakpoint

調(diào)試過(guò)程中,我們用得最多的可能就是斷點(diǎn)了蕴掏。LLDB中的斷點(diǎn)命令也非常強(qiáng)大

breakpoint set

breakpoint set命令用于設(shè)置斷點(diǎn)障般,LLDB提供了很多種設(shè)置斷點(diǎn)的方式:

<b>使用-n根據(jù)方法名設(shè)置斷點(diǎn):<b>

e.g: 我們想給所有類中的viewWillAppear:設(shè)置一個(gè)斷點(diǎn):

    (lldb) breakpoint set -n viewWillAppear:
    Breakpoint 13: 33 locations.

<b>使用-f指定文件<b>

e.g: 我們只需要給ViewController.m文件中的viewDidLoad設(shè)置斷點(diǎn):

    (lldb) breakpoint set -f ViewController.m -n viewDidLoad
    Breakpoint 22: where = TLLDB`-[ViewController viewDidLoad] + 20 at ViewController.m:22, address = 0x000000010272a6f4

這里需要注意,如果方法未寫(xiě)在文件中(比如寫(xiě)在category文件中盛杰,或者父類文件中)挽荡,指定文件之后,將無(wú)法給這個(gè)方法設(shè)置斷點(diǎn)即供。

<b>使用-l指定文件某一行設(shè)置斷點(diǎn)<b>

e.g: 我們想給ViewController.m第38行設(shè)置斷點(diǎn)

(lldb) breakpoint set -f ViewController.m -l 38
Breakpoint 23: where = TLLDB`-[ViewController text:] + 37 at ViewController.m:38, address = 0x000000010272a7d5

<b>使用-c設(shè)置條件斷點(diǎn)<b>

e.g: text:方法接受一個(gè)ret的參數(shù)定拟,我們想讓ret == YES的時(shí)候程序中斷:

(lldb) breakpoint set -n text: -c ret == YES
Breakpoint 7: where = TLLDB`-[ViewController text:] + 30 at ViewController.m:37, address = 0x0000000105ef37ce

<b>使用-o設(shè)置單次斷點(diǎn)<b>

e.g: 如果剛剛那個(gè)斷點(diǎn)我們只想讓他中斷一次:

(lldb) breakpoint set -n text: -o
'breakpoint 3': where = TLLDB`-[ViewController text:] + 30 at ViewController.m:37, address = 0x000000010b6f97ce

breakpoint command

有的時(shí)候我們可能需要給斷點(diǎn)添加一些命令,比如每次走到這個(gè)斷點(diǎn)的時(shí)候逗嫡,我們都需要打印self對(duì)象青自。我們只需要給斷點(diǎn)添加一個(gè)po self命令株依,就不用每次執(zhí)行斷點(diǎn)再自己輸入po self

breakpoint command add

breakpoint command add命令就是給斷點(diǎn)添加命令的命令。

e.g: 假設(shè)我們需要在ViewControllerviewDidLoad中查看self.view的值
我們首先給-[ViewController viewDidLoad]添加一個(gè)斷點(diǎn)

(lldb) breakpoint set -n "-[ViewController viewDidLoad]"
'breakpoint 3': where = TLLDB`-[ViewController viewDidLoad] + 20 at ViewController.m:23, address = 0x00000001055e6004

可以看到添加成功之后延窜,這個(gè)breakpoint的id為3恋腕,然后我們給他增加一個(gè)命令:po self.view

(lldb) breakpoint command add -o "po self.view" 3

-o完整寫(xiě)法是--one-liner非迹,表示增加一條命令拜鹤。3表示對(duì)id為3breakpoint增加命令猿妈。
添加完命令之后娜睛,每次程序執(zhí)行到這個(gè)斷點(diǎn)就可以自動(dòng)打印出self.view的值了

如果我們一下子想增加多條命令瞭空,比如我想在viewDidLoad中打印當(dāng)前frame的所有變量率翅,但是我們不想讓他中斷受啥,也就是在打印完成之后躲履,需要繼續(xù)執(zhí)行念秧。我們可以這樣玩:

(lldb) breakpoint command add 3
Enter your debugger command(s).  Type 'DONE' to end.
> frame variable
> continue
> DONE

輸入breakpoint command add 3對(duì)斷點(diǎn)3增加命令牡彻。他會(huì)讓你輸入增加哪些命令,輸入'DONE'表示結(jié)束出爹。這時(shí)候你就可以輸入多條命令了

多次對(duì)同一個(gè)斷點(diǎn)添加命令庄吼,后面命令會(huì)將前面命令覆蓋

breakpoint command list

如果想查看某個(gè)斷點(diǎn)已有的命令,可以使用breakpoint command list严就。
e.g: 我們查看一下剛剛的斷點(diǎn)3已有的命令

(lldb) breakpoint command list 3
'breakpoint 3':
    Breakpoint commands:
      frame variable
      continue

可以看到一共有2條命令总寻,分別為frame variablecontinue

breakpoint command delete

有增加就有刪除,breakpoint command delete可以讓我們刪除某個(gè)斷點(diǎn)的命令
e.g: 我們將斷點(diǎn)3中的命令刪除:

(lldb) breakpoint command delete 3
(lldb) breakpoint command list 3
Breakpoint 3 does not have an associated command.

可以看到刪除之后梢为,斷點(diǎn)3就沒(méi)有命令了

breakpoint list

如果我們想查看已經(jīng)設(shè)置了哪些斷點(diǎn)渐行,可以使用breakpoint list
e.g:

(lldb) breakpoint list
Current breakpoints:
4: name = '-[ViewController viewDidLoad]', locations = 1, resolved = 1, hit count = 0
  4.1: where = TLLDB`-[ViewController viewDidLoad] + 20 at ViewController.m:23, address = 0x00000001055e6004, resolved, hit count = 0 

我們可以看到當(dāng)前只有一個(gè)斷點(diǎn),打在-[ViewController viewDidLoad]上铸董,id是4

breakpoint disable/enable

有的時(shí)候我們可能暫時(shí)不想要某個(gè)斷點(diǎn)祟印,可以使用breakpoint disable讓某個(gè)斷點(diǎn)暫時(shí)失效
e.g: 我們來(lái)讓剛剛的斷點(diǎn)4失效

(lldb) breakpoint disable 4
1 breakpoints disabled.

輸入完命令之后,顯示斷點(diǎn)已經(jīng)失效

當(dāng)我們又需要這個(gè)斷點(diǎn)的時(shí)候粟害,可以使用breakpoint enable再次讓他生效
e.g: 重新啟用斷點(diǎn)4

(lldb) breakpoint enable 4
1 breakpoints enabled.

breakpoint delete

如果我們覺(jué)得這個(gè)斷點(diǎn)以后再也用不上了蕴忆,可以用breakpoint delete直接刪除斷點(diǎn).
e.g: 刪除斷點(diǎn)4

(lldb) breakpoint delete 4
1 breakpoints deleted; 0 breakpoint locations disabled.

如果我們想刪除所有斷點(diǎn),只需要不指定breakpoint delete參數(shù)即可

(lldb) breakpoint delete 
About to delete all breakpoints, do you want to do that?: [Y/n] y
All breakpoints removed. (1 breakpoint)

刪除的時(shí)候他會(huì)提示你悲幅,是不是真的想刪除所有斷點(diǎn)套鹅,需要你再次輸入Y確認(rèn)。如果想直接刪除汰具,不需要他的提示卓鹿,使用-f命令選項(xiàng)即可

(lldb) breakpoint delete -f
All breakpoints removed. (1 breakpoint)

實(shí)際平時(shí)我們真正使用breakpoint命令反而比較少,因?yàn)閄code已經(jīng)內(nèi)置了斷點(diǎn)工具留荔。我們可以直接在代碼上打斷點(diǎn)吟孙,可以在斷點(diǎn)工具欄里面查看編輯斷點(diǎn),這比使用LLDB命令方便很多。不過(guò)了解LLDB相關(guān)命令可以讓我們對(duì)斷點(diǎn)理解更深刻杰妓。
如果你想了解怎么使用Xcode設(shè)置斷點(diǎn)肥隆,可以閱讀這篇文章《Xcode中斷點(diǎn)的威力》

watchpoint

breakpoint有一個(gè)孿生兄弟watchpoint。如果說(shuō)breakpoint是對(duì)方法生效的斷點(diǎn)稚失,watchpoint就是對(duì)地址生效的斷點(diǎn)

如果我們想要知道某個(gè)屬性什么時(shí)候被篡改了栋艳,我們?cè)撛趺崔k呢?有人可能會(huì)說(shuō)對(duì)setter方法打個(gè)斷點(diǎn)不就行了么句各?但是如果更改的時(shí)候沒(méi)調(diào)用setter方法呢吸占?
這時(shí)候最好的辦法就是用watchpoint。我們可以用他觀察這個(gè)屬性的地址凿宾。如果地址里面的東西改變了矾屯,就讓程序中斷

watchpoint set

watchpoint set命令用于添加一個(gè)watchpoint。只要這個(gè)地址中的內(nèi)容變化了初厚,程序就會(huì)中斷件蚕。

watchpoint set variable

一般情況下,要觀察變量或者屬性产禾,使用watchpoint set variable命令即可
e.g: 觀察self->_string

(lldb) watchpoint set variable self->_string
Watchpoint created: Watchpoint 1: addr = 0x7fcf3959c418 size = 8 state = enabled type = w
    watchpoint spec = 'self->_string'
    new value: 0x0000000000000000

watchpoint set variable傳入的是變量名排作。需要注意的是,這里不接受方法亚情,所以不能使用watchpoint set variable self.string妄痪,因?yàn)閟elf.string調(diào)用的是string的getter方法

watchpoint set expression

如果我們想直接觀察某個(gè)地址,可以使用watchpoint set expression
e.g: 我們先拿到_model的地址楞件,然后對(duì)地址設(shè)置一個(gè)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

我們來(lái)看看怎么給watchpoint添加命令:

首先,我們?cè)O(shè)置一個(gè)watchpoint:

(lldb) watchpoint set variable _string
Watchpoint created: Watchpoint 1: addr = 0x7fe4e1444760 size = 8 state = enabled type = w
    watchpoint spec = '_string'
    new value: 0x0000000000000000

可以看到這個(gè)watchpoint的id是1土浸。我們可以用watchpoint command add -o添加單條命令

watchpoint command add -o 'bt' 1

我們?cè)趙atchpoint停下來(lái)的時(shí)候罪针,打印了他的線程信息。

我們也可以一次添加多條命令:

(lldb) watchpoint command add 1
Enter your debugger command(s).  Type 'DONE' to end.
> bt
> continue
> DONE

可以看到watchpoint的使用方法跟breakpoint幾乎一模一樣黄伊。

watchpoint command list

我們可以用watchpoint command list列出某個(gè)watchpoint所有的command

(lldb) watchpoint command list 1
Watchpoint 1:
    watchpoint commands:
      bt
      continue

watchpoint command delete

我們也可以用watchpoint command delete刪除某個(gè)watchpoint所有的command

(lldb) watchpoint command delete 1
(lldb) watchpoint command list 1
Watchpoint 1 does not have an associated command.

watchpoint list

如果我們想看當(dāng)前所有watchpoint泪酱,可以使用watchpoint list:

(lldb) watchpoint list
Number of supported hardware watchpoints: 4
Current watchpoints:
Watchpoint 1: addr = 0x7fe9f9f28e30 size = 8 state = enabled type = w
    watchpoint spec = '_string'
    old value: 0x0000000000000000
    new value: 0x000000010128e0d0

可以看到,只有一個(gè)watchpoint毅舆。

watchpoint disable

當(dāng)我們不想讓某個(gè)watchpoint生效的時(shí)候西篓,可以用watchpoint disable:

(lldb) watchpoint disable 1
1 watchpoints disabled.

再次查看這個(gè)watchpoint愈腾,可以看到他的state已經(jīng)變?yōu)榱薲isabled

(lldb) watchpoint list
Number of supported hardware watchpoints: 4
Current watchpoints:
Watchpoint 1: addr = 0x7fe9f9f28e30 size = 8 state = disabled type = w
    watchpoint spec = '_string'
    old value: 0x0000000000000000
    new value: 0x000000010128e0d0

watchpoint enable

過(guò)了一會(huì)憋活,我們又要用這個(gè)watchpoint了,這時(shí)候可以使用watchpoint enable:

(lldb) watchpoint enable 1
1 watchpoints enabled.

watchpoint delete

如果我們覺(jué)得再也用不著這個(gè)watchpoint了虱黄,可以用watchpoint delete將他刪除:

(lldb) watchpoint delete 1
1 watchpoints deleted.
(lldb) watchpoint list
Number of supported hardware watchpoints: 4
No watchpoints currently set.

刪除之后悦即,我們可以看到watchpoint list里面已經(jīng)沒(méi)有watchpoint1

如果有很多個(gè)watchpoint,我們想全都干掉,只需要不指定具體哪個(gè)watchpoint即可:

(lldb) watchpoint delete 
About to delete all watchpoints, do you want to do that?: [Y/n] y
All watchpoints removed. (2 watchpoints)

target

target modules lookup(image lookup)

對(duì)于target這個(gè)命令辜梳,我們用得最多的可能就是target modules lookup粱甫。由于LLDB給target modules取了個(gè)別名image,所以這個(gè)命令我們又可以寫(xiě)成image lookup作瞄。

image lookup --address

當(dāng)我們有一個(gè)地址茶宵,想查找這個(gè)地址具體對(duì)應(yīng)的文件位置,可以使用image lookup --address宗挥,簡(jiǎn)寫(xiě)為image lookup -a
e.g: 當(dāng)我們發(fā)生一個(gè)crash

2015-12-17 14:51:06.301 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

我們可以看到是由于-[__NSArray0 objectAtIndex:]:超出邊界而導(dǎo)致的crash乌庶,但是objectAtIndex:的代碼到底在哪兒呢?

(lldb) image lookup -a 0x000000010a1c3e36
      Address: TLLDB[0x0000000100000e36] (TLLDB.__TEXT.__text + 246)
      Summary: TLLDB`-[ViewController viewDidLoad] + 86 at ViewController.m:32

根據(jù)0x000000010a1c3e36 -[ViewController viewDidLoad]里面的地址契耿,使用image lookup --address查找瞒大,我們可以看到代碼位置在ViewController.m里面的32行

image lookup --name

當(dāng)我們想查找一個(gè)方法或者符號(hào)的信息,比如所在文件位置等搪桂。我們可以使用image lookup --name透敌,簡(jiǎn)寫(xiě)為image lookup -n

e.g: 剛剛遇到的真問(wèn)題踢械,某個(gè)第三方SDK用了一個(gè)我們項(xiàng)目里原有的第三方庫(kù)酗电,庫(kù)里面對(duì)NSDictionary添加了category。也就是有2個(gè)class對(duì)NSDictionary添加了名字相同的category内列,項(xiàng)目中調(diào)用自己的category的地方實(shí)際走到了第三方SDK里面去了顾瞻。最大的問(wèn)題是,這2個(gè)同名category方法行為并不一致德绿,導(dǎo)致出現(xiàn)bug
現(xiàn)在問(wèn)題來(lái)了荷荤,怎么尋找到底是哪個(gè)第三方SDK?方法完全包在.a里面移稳。
其實(shí)只需使用image lookup -n即可:

(lldb) image lookup -n dictionaryWithXMLString:
2 matches found in /Users/jiangliancheng/Library/Developer/Xcode/DerivedData/VideoIphone-aivsnqmlwjhxapdlvmdmrubbdxpq/Build/Products/Debug-iphoneos/BaiduIphoneVideo.app/BaiduIphoneVideo:
        Address: BaiduIphoneVideo[0x00533a7c] (BaiduIphoneVideo.__TEXT.__text + 5414908)
        Summary: BaiduIphoneVideo`+[NSDictionary(SAPIXmlDictionary) dictionaryWithXMLString:] at XmlDictionary.m
         Module: file = "/Users/jiangliancheng/Library/Developer/Xcode/DerivedData/VideoIphone-aivsnqmlwjhxapdlvmdmrubbdxpq/Build/Products/Debug-iphoneos/BaiduIphoneVideo.app/BaiduIphoneVideo", arch = "armv7"
    CompileUnit: id = {0x00000000}, file = "/Users/jiangliancheng/Development/Work/iOS_ShareLib/SharedLib/Srvcs/BDPassport4iOS/BDPassport4iOS/SAPI/Extensive/ThirdParty/XMLDictionary/XmlDictionary.m", language = "Objective-C"
       Function: id = {0x23500000756}, name = "+[NSDictionary(SAPIXmlDictionary) dictionaryWithXMLString:]", range = [0x005a6a7c-0x005a6b02)
       FuncType: id = {0x23500000756}, decl = XmlDictionary.m:189, clang_type = "NSDictionary *(NSString *)"
         Blocks: id = {0x23500000756}, range = [0x005a6a7c-0x005a6b02)
      LineEntry: [0x005a6a7c-0x005a6a98): /Users/jiangliancheng/Development/Work/iOS_ShareLib/SharedLib/Srvcs/BDPassport4iOS/BDPassport4iOS/SAPI/Extensive/ThirdParty/XMLDictionary/XmlDictionary.m
         Symbol: id = {0x0000f2d5}, range = [0x005a6a7c-0x005a6b04), name="+[NSDictionary(SAPIXmlDictionary) dictionaryWithXMLString:]"
       Variable: id = {0x23500000771}, name = "self", type = "Class", location =  [sp+32], decl = 
       Variable: id = {0x2350000077e}, name = "_cmd", type = "SEL", location =  [sp+28], decl = 
       Variable: id = {0x2350000078b}, name = "string", type = "NSString *", location =  [sp+24], decl = XmlDictionary.m:189
       Variable: id = {0x23500000799}, name = "data", type = "NSData *", location =  [sp+20], decl = XmlDictionary.m:192
        Address: BaiduIphoneVideo[0x012ee160] (BaiduIphoneVideo.__TEXT.__text + 19810016)
        Summary: BaiduIphoneVideo`+[NSDictionary(XMLDictionary) dictionaryWithXMLString:] at XMLDictionary.m
         Module: file = "/Users/jiangliancheng/Library/Developer/Xcode/DerivedData/VideoIphone-aivsnqmlwjhxapdlvmdmrubbdxpq/Build/Products/Debug-iphoneos/BaiduIphoneVideo.app/BaiduIphoneVideo", arch = "armv7"
    CompileUnit: id = {0x00000000}, file = "/Users/wingle/Workspace/qqlive4iphone/iphone_4.0_fabu_20150601/Common_Proj/mobileTAD/VIDEO/Library/Third Party/XMLDictionary/XMLDictionary.m", language = "Objective-C"
       Function: id = {0x79900000b02}, name = "+[NSDictionary(XMLDictionary) dictionaryWithXMLString:]", range = [0x01361160-0x0136119a)
       FuncType: id = {0x79900000b02}, decl = XMLDictionary.m:325, clang_type = "NSDictionary *(NSString *)"
         Blocks: id = {0x79900000b02}, range = [0x01361160-0x0136119a)
      LineEntry: [0x01361160-0x01361164): /Users/wingle/Workspace/qqlive4iphone/iphone_4.0_fabu_20150601/Common_Proj/mobileTAD/VIDEO/Library/Third Party/XMLDictionary/XMLDictionary.m
         Symbol: id = {0x0003a1e9}, range = [0x01361160-0x0136119c), name="+[NSDictionary(XMLDictionary) dictionaryWithXMLString:]"
       Variable: id = {0x79900000b1e}, name = "self", type = "Class", location =  r0, decl = 
       Variable: id = {0x79900000b2c}, name = "_cmd", type = "SEL", location =  r1, decl = 
       Variable: id = {0x79900000b3a}, name = "string", type = "NSString *", location =  r2, decl = XMLDictionary.m:325
       Variable: id = {0x79900000b4a}, name = "data", type = "NSData *", location =  r2, decl = XMLDictionary.m:327

東西有點(diǎn)多蕴纳,我們只需關(guān)注里面的file這一行:

CompileUnit: id = {0x00000000}, file = "/Users/jiangliancheng/Development/Work/iOS_ShareLib/SharedLib/Srvcs/BDPassport4iOS/BDPassport4iOS/SAPI/Extensive/ThirdParty/XMLDictionary/XmlDictionary.m", language = "Objective-C"
CompileUnit: id = {0x00000000}, file = "/Users/wingle/Workspace/qqlive4iphone/iphone_4.0_fabu_20150601/Common_Proj/mobileTAD/VIDEO/Library/Third Party/XMLDictionary/XMLDictionary.m", language = "Objective-C"

可以清晰的看到,LLDB給我們找出來(lái)了這個(gè)方法的位置个粱。
當(dāng)然這個(gè)命令也可以找到方法的其他相關(guān)信息古毛,比如參數(shù)等.

image lookup --type

當(dāng)我們想查看一個(gè)類型的時(shí)候,可以使用image lookup --type都许,簡(jiǎn)寫(xiě)為image lookup -t:
e.g: 我們來(lái)看看Model的類型:

(lldb) image lookup -t Model
Best match found in /Users/jiangliancheng/Library/Developer/Xcode/DerivedData/TLLDB-beqoowskwzbttrejseahdoaivpgq/Build/Products/Debug-iphonesimulator/TLLDB.app/TLLDB:
id = {0x30000002f}, name = "Model", byte-size = 32, decl = Modek.h:11, clang_type = "@interface Model : NSObject{
    NSString * _bb;
    NSString * _cc;
    NSString * _name;
}
@property ( getter = name,setter = setName:,readwrite,nonatomic ) NSString * name;
@end
"

可以看到稻薇,LLDB把Model這個(gè)class的所有屬性和成員變量都打印了出來(lái),當(dāng)我們想了解某個(gè)類的時(shí)候胶征,直接使用image lookup -t即可

target stop-hook

我們知道塞椎,用LLDB debug,大多數(shù)時(shí)候需要讓程序stop睛低,不管用breakpoint還是用watchpoint案狠。
target stop-hook命令就是讓你可以在每次stop的時(shí)候去執(zhí)行一些命令

target stop-hook只對(duì)breakpointwatchpoint的程序stop生效服傍,直接點(diǎn)擊Xcode上的pause或者debug view hierarchy不會(huì)生效

target stop-hook add & display

假如我們想在每次程序stop的時(shí)候,都用命令打印當(dāng)前frame的所有變量骂铁。我們可以添加一個(gè)stop-hook:

(lldb) target stop-hook add -o "frame variable"
Stop hook #4 added.

target stop-hook add表示添加stop-hook吹零,-o的全稱是--one-liner,表示添加一條命令拉庵。
我們看一下灿椅,當(dāng)執(zhí)行到一個(gè)斷點(diǎn)的時(shí)候會(huì)發(fā)生什么?

- Hook 1 (frame variable)
(ViewController *) self = 0x00007fd55b12e380
(SEL) _cmd = "viewDidLoad"
(NSMutableURLRequest *) request = 0x00007fd55b1010c0

在程序stop的時(shí)候钞支,他會(huì)自動(dòng)執(zhí)行frame variable阱扬,打印出了所有的變量。

大多情況下伸辟,我們?cè)趕top的時(shí)候可能想要做的是打印一個(gè)東西麻惶。正常情況我們需要用target stop-hook add -o "p xxx",LLDB提供了一個(gè)更簡(jiǎn)便的命令display信夫。
e.g: 下面2行代碼效果相同

(lldb) target stop-hook add -o "p self.view"
(lldb) display self.view

也可以用display來(lái)執(zhí)行某一個(gè)命令窃蹋。p,e,expression是等效的。

target stop-hook list

當(dāng)添加完stop-hook之后静稻,我們想看當(dāng)前所有的stop-hook怎么辦呢警没?使用stop-hook list

(lldb) target stop-hook list
Hook: 4
  State: enabled
  Commands: 
    frame variable

Hook: 5
  State: enabled
  Commands: 
    expression self.view

Hook: 6
  State: enabled
  Commands: 
    expr -- self.view

我們可以看到,我們添加了4個(gè)stop-hook振湾,每個(gè)stop-hook都有一個(gè)id杀迹,他們分別是4,5押搪,6

target stop-hook delete & undisplay

有添加的命令树酪,當(dāng)然也就有刪除的命令。使用target stop-hook delete可以刪除stop-hook大州,如果你覺(jué)得這個(gè)命令有點(diǎn)長(zhǎng)续语,懶得敲。你也可以用undisplay

(lldb) target stop-hook delete 4
(lldb) undisplay 5

我們用target stop-hook deleteundisplay分別刪除了id為4和5的stop-hook

target stop-hook disable/enable

當(dāng)我們暫時(shí)想讓某個(gè)stop-hook失效的時(shí)候厦画,可以使用target stop-hook disable

(lldb) target stop-hook disable 8

如果我們想讓所有的stop-hook失效疮茄,只需不傳入stop-hookid即可:

(lldb) target stop-hook disable

disable就有enable,我們又想讓stop-hook生效了根暑×κ裕可以使用target stop-hook enable

(lldb) target stop-hook enable 8

同理,不傳入?yún)?shù)表示讓所有stop-hook生效

(lldb) target stop-hook enable

Extension

前幾天@兔be南玻1在微博上給出一個(gè)小技巧排嫌。LLDB中@import UIKit即可打印frame等變量(默認(rèn)情況下打不出來(lái))微博鏈接畸裳。

(lldb) p self.view.frame
error: property 'frame' not found on object of type 'UIView *'
error: 1 errors parsing expression
(lldb) e @import UIKit
(lldb) p self.view.frame
(CGRect) $0 = (origin = (x = 0, y = 0), size = (width = 375, height = 667))

由于每次run Xcode,LLDB的東西都會(huì)被清空躏率。所以每次run你都需要在LLDB中輸入e @import UIKit才能使用這個(gè)方便的功能躯畴,有點(diǎn)麻煩呀民鼓!

之后有人提出了比較方便的一個(gè)辦法薇芝。給UIApplicationMain設(shè)置一個(gè)斷點(diǎn)蓬抄,在斷點(diǎn)中添加執(zhí)行e @import UIKit


這種方法非常方便夯到,不用自己輸入了嚷缭,但是斷點(diǎn)我們可能會(huì)誤刪,而且斷點(diǎn)是對(duì)應(yīng)工程的耍贾。換一個(gè)工程又得重新打一個(gè)這樣的斷點(diǎn)阅爽。還是有點(diǎn)麻煩。有沒(méi)有更簡(jiǎn)便的方法呢荐开?

我們首先想到的是LLDB在每次啟動(dòng)的時(shí)候都會(huì)load '~/.lldbinit'文件付翁。在這里面執(zhí)行e @import UIKit不就行了么?不會(huì)被誤刪晃听,對(duì)每個(gè)工程都有效百侧!

然而想法是美好的,現(xiàn)實(shí)卻是殘酷的能扒!因?yàn)?code>UIKit這個(gè)庫(kù)是在target中佣渴。而load '~/.lldbinit'的時(shí)候target還沒(méi)創(chuàng)建。所以無(wú)法import UIKit初斑。stackoverflow詳細(xì)解釋

這時(shí)候我們又想到辛润,可不可以在'~/.lldbinit'中給UIApplicationMain設(shè)置一個(gè)斷點(diǎn),在斷點(diǎn)中添加執(zhí)行e @import UIKit呢见秤?
答案是不行砂竖。原因跟前面一樣,load '/.lldbinit'執(zhí)行時(shí)間太早鹃答。斷點(diǎn)是依賴target的晦溪,target還未創(chuàng)建,斷點(diǎn)加不上去挣跋。好事多磨三圆,道路坎坷呀~~

后來(lái)我們又想到用stop-hook行不行呢?stop-hook不依賴target避咆。一般我們p frame的時(shí)候舟肉,都需要先stop,理論上是可行的
事實(shí)證明stop-hook的方法完全ok查库。只需要在'~/.lldbinit'中添加這2條命令即可:

display @import UIKit
target stop-hook add -o "target stop-hook disable" 
  • 命令1:使用display表示在stop的時(shí)候執(zhí)行@import UIKit
  • 命令2:由于我們只需要執(zhí)行一次@import UIKit路媚,所以執(zhí)行完成之后,執(zhí)行target stop-hook disable樊销,使原有的所有stop-hook失效

這個(gè)命令有個(gè)缺陷整慎,直接點(diǎn)擊Xcode上的pausedebug view hierarchy脏款,stop-hook不會(huì)生效。正在探索有沒(méi)有更好的辦法完成@import UIKit裤园,如果你想到了撤师,可以聯(lián)系我~

target symbols add(add-dsym)

說(shuō)這個(gè)命令之前,先簡(jiǎn)單解釋一下dSYM文件拧揽。程序運(yùn)行的時(shí)候剃盾,都會(huì)編譯成二進(jìn)制文件。因?yàn)橛?jì)算機(jī)只識(shí)別二進(jìn)制文件淤袜,那為什么我們還能在代碼上打斷點(diǎn)痒谴?
這主要是因?yàn)樵诰幾g的時(shí)候Xcode會(huì)生成dSYM文件,dSYM文件記錄了哪行代碼對(duì)應(yīng)著哪些二進(jìn)制铡羡,這樣我們對(duì)代碼打斷點(diǎn)就會(huì)對(duì)應(yīng)到二進(jìn)制上积蔚。dSYM詳細(xì)資料

當(dāng)Xcode找不著dSYM文件的時(shí)候,我們就無(wú)法對(duì)代碼打斷點(diǎn)烦周,進(jìn)行調(diào)試尽爆。target symbols add命令的作用就是讓我們可以手動(dòng)的將dSYM文件添加上去。LLBD對(duì)這個(gè)命令起了一個(gè)別名: add-dsym

e.g: 當(dāng)我們對(duì)接framework的時(shí)候论矾,如果只有framework代碼教翩,沒(méi)有工程代碼,能不能debug呢贪壳?其實(shí)我們只需要拿到工程的ipa和dSYM文件饱亿,就可以debug了,通過(guò)Attach to Process闰靴,使用命令add-dsym將dSYM文件加入target彪笼,即可只debug framework,不需要工程的代碼

add-dsym ~/.../XXX.dSYM

詳細(xì)細(xì)節(jié)可以查看iOS中framework的聯(lián)調(diào)

help & apropos

LLDB的命令其實(shí)還有很多蚂且,很多命令我也沒(méi)玩過(guò)配猫。就算玩過(guò)的命令,我們也非常容易忘記杏死,下次可能就不記得是怎么用的了泵肄。還好LLDB給我們提供了2個(gè)查找命令的命令:help & apropos

help

直接在LLDB中輸入help∈缫恚可以查看所有的LLDB命令

(lldb) help
Debugger commands:

  apropos           -- Find a list of debugger commands related to a particular
                       word/subject.
  breakpoint        -- A set of commands for operating on breakpoints. Also see
                       _regexp-break.
  help              -- Show a list of all debugger commands, or give details
                       about specific commands.
  script            -- Pass an expression to the script interpreter for
                       evaluation and return the results. Drop into the
                       interactive interpreter if no expression is given.
  settings          -- A set of commands for manipulating internal settable
                       debugger variables.
  source            -- A set of commands for accessing source file information
  target            -- A set of commands for operating on debugger targets.
  thread            -- A set of commands for operating on one or more threads
                       within a running process.
  type              -- A set of commands for operating on the type system
  version           -- Show version of LLDB debugger.
  watchpoint        -- A set of commands for operating on watchpoints.
  ....(東西太多腐巢,只截取了一部分)

如果我們想看具體某一個(gè)命令的詳細(xì)用法,可以使用help <command-name>
e.g: 我們查看watchpoint命令

(lldb) help watchpoint
The following subcommands are supported:

      command -- A set of commands for adding, removing and examining bits of
                 code to be executed when the watchpoint is hit (watchpoint
                 'commmands').
      delete  -- Delete the specified watchpoint(s).  If no watchpoints are
                 specified, delete them all.
      disable -- Disable the specified watchpoint(s) without removing it/them. 
                 If no watchpoints are specified, disable them all.
      enable  -- Enable the specified disabled watchpoint(s). If no watchpoints
                 are specified, enable all of them.
      ignore  -- Set ignore count on the specified watchpoint(s).  If no
                 watchpoints are specified, set them all.
      list    -- List all watchpoints at configurable levels of detail.
      modify  -- Modify the options on a watchpoint or set of watchpoints in
                 the executable.  If no watchpoint is specified, act on the
                 last created watchpoint.  Passing an empty argument clears the
                 modification.
      set     -- A set of commands for setting a watchpoint.

apropos

有的時(shí)候玄括,我們可能并不能完全記得某個(gè)命令冯丙,如果只記得命令中的某個(gè)關(guān)鍵字。這時(shí)候我們可以使用apropos搜索相關(guān)命令信息遭京。
e.g: 我們想使用stop-hook的命令胃惜,但是已經(jīng)不記得stop-hook命令是啥樣了

(lldb) apropos stop-hook
The following built-in commands may relate to 'stop-hook':
  _regexp-display          -- Add an expression evaluation stop-hook.
  _regexp-undisplay        -- Remove an expression evaluation stop-hook.
  target stop-hook         -- A set of commands for operating on debugger
                              target stop-hooks.
  target stop-hook add     -- Add a hook to be executed when the target stops.
  target stop-hook delete  -- Delete a stop-hook.
  target stop-hook disable -- Disable a stop-hook.
  target stop-hook enable  -- Enable a stop-hook.
  target stop-hook list    -- List all stop-hooks.

可以看到使用apropos stop-hook搜索一下泞莉,即可將所有stop-hook相關(guān)命令搜索出來(lái)

常用的Debug快捷鍵

debug的時(shí)候,使用快捷鍵是一個(gè)很好的習(xí)慣船殉,我簡(jiǎn)單列舉了幾個(gè)debug的快捷鍵

功能 命令
暫停/繼續(xù) cmd + ctrl + Y
斷點(diǎn)失效/生效 cmd + Y
控制臺(tái)顯示/隱藏 cmd + shift + Y
光標(biāo)切換到控制臺(tái) cmd + shift + C
清空控制臺(tái) cmd + K
step over F6
step into F7
step out F8

End

東西有點(diǎn)多鲫趁,感謝大家耐心看完這篇文章。LLDB命令非常多捺弦,有很多LLDB命令我也沒(méi)玩過(guò)饮寞。這些命令我們不一定要完全記住孝扛,只要有個(gè)印象LLDB可以實(shí)現(xiàn)哪些功能就可以了列吼。具體用的時(shí)候再用help或者apropos查找

Reference

The LLDB Debugger
與調(diào)試器共舞 - LLDB 的華爾茲
LLDB調(diào)試器使用簡(jiǎn)介
Xcode中斷點(diǎn)的威力
dSYM詳細(xì)資料

About me

微博 QQ群:159974494

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市苦始,隨后出現(xiàn)的幾起案子寞钥,更是在濱河造成了極大的恐慌,老刑警劉巖陌选,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件理郑,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡咨油,警方通過(guò)查閱死者的電腦和手機(jī)您炉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)役电,“玉大人赚爵,你說(shuō)我怎么就攤上這事》ㄉ” “怎么了冀膝?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)霎挟。 經(jīng)常有香客問(wèn)我窝剖,道長(zhǎng),這世上最難降的妖魔是什么酥夭? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任赐纱,我火速辦了婚禮,結(jié)果婚禮上熬北,老公的妹妹穿的比我還像新娘疙描。我一直安慰自己,他們只是感情好蒜埋,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布淫痰。 她就那樣靜靜地躺著,像睡著了一般整份。 火紅的嫁衣襯著肌膚如雪待错。 梳的紋絲不亂的頭發(fā)上籽孙,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音火俄,去河邊找鬼犯建。 笑死,一個(gè)胖子當(dāng)著我的面吹牛瓜客,可吹牛的內(nèi)容都是我干的适瓦。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼谱仪,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼玻熙!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起疯攒,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤嗦随,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后敬尺,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體枚尼,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年砂吞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了署恍。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蜻直,死狀恐怖盯质,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情袭蝗,我是刑警寧澤唤殴,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站到腥,受9級(jí)特大地震影響朵逝,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜乡范,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一配名、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧晋辆,春花似錦渠脉、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春为朋,著一層夾襖步出監(jiān)牢的瞬間臂拓,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工习寸, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留胶惰,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓霞溪,卻偏偏與公主長(zhǎng)得像孵滞,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鸯匹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

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

  • [轉(zhuǎn)]淺談LLDB調(diào)試器文章來(lái)源于:http://www.cocoachina.com/ios/20150126/...
    loveobjc閱讀 2,484評(píng)論 2 6
  • 前言 LLDB是個(gè)開(kāi)源的內(nèi)置于XCode的具有REPL(read-eval-print-loop)特征的Debug...
    Noskthing閱讀 18,445評(píng)論 10 89
  • 你是否曾經(jīng)苦惱于理解你的代碼坊饶,而去嘗試打印一個(gè)變量的值? NSLog(@"%@", whatIsInsideThi...
    paraneaeee閱讀 1,184評(píng)論 0 7
  • 轉(zhuǎn)載 與調(diào)試器共舞 - LLDB 的華爾茲: https://objccn.io/issue-19-2/ 推薦:i...
    F麥子閱讀 3,327評(píng)論 0 10
  • 接單啦忽你! 剛下午三點(diǎn)幼东,對(duì)面的同事就接到了今天下班的順風(fēng)車訂單臂容,不禁喜形于色科雳。滴滴真是一個(gè)神奇的平臺(tái),供需雙方可以一...
    番茄狂想閱讀 473評(píng)論 0 2