xcode調試利器 LLDB-深度命令文檔

什么是 LLDB狂芋?

LLDB(Low Level Debugger) is a next generation, high-performance debugger. It is built as a set of reusable components which highly leverage existing libraries in the larger LLVM Project, such as the Clang expression parser and LLVM disassembler.

LLDB is the default debugger in Xcode on Mac OS X and supports debugging C, Objective-C and C++ on the desktop and iOS devices and simulator.

All of the code in the LLDB project is available under the standard LLVM License, an open source "BSD-style" license.

基礎

LLDB 是一個有著 REPL 的特性的調試器榨馁,這說明他和Linux數(shù)據(jù)庫有一樣的"套路"。當一般不是手動把help命令禁止掉的時候帜矾,就可以通過help查找所有想要使用的命令翼虫。

現(xiàn)在iOSxcode,安卓android studio 都有LLDB模塊支持屑柔。學好LLDB,各種難搞的問題都能找到一些可以原由蛙讥。

print

你可能還注意到了,結果中有個 $3灭衷。 實際上你可以使用它來指向這個結果次慢。試試 print $3 + 1,你會看到 100翔曲。任何以美元符開頭的東西都是存在于 LLDB 的命名空間的迫像,它們是為了幫助你進行調試而存在的。

lldb_001.png

expression

如果想改變一個值怎么辦瞳遍?你或許會猜 modify闻妓。其實這時候我們要用到的是 expression 這個方便的命令。

lldb_002.png
lldb_003.png

這不僅會改變調試器中的值掠械,實際上它改變了程序中的值由缆。從現(xiàn)在開始,我們將會偷懶分別以pe來代替printexpression猾蒂。

什么是 print 命令

考慮一個有意思的表達式:p count = 18均唉。如果我們運行這條命令,然后打印 count 的內容肚菠。我們將看到它的結果與 expression count = 18 一樣舔箭。

和 expression 不同的是,print 命令不需要參數(shù)蚊逢。比如 e -h +17 中层扶,你很難區(qū)分到底是以 -h 為標識,僅僅執(zhí)行 +17 呢烙荷,還是要計算 17 和 h 的差值镜会。連字符號確實很讓人困惑,你或許得不到自己想要的結果终抽。

幸運的是稚叹,解決方案很簡單。用 -- 來表征標識的結束拿诸,以及輸入的開始扒袖。如果想要 -h 作為標識,就用 e -h -- +17亩码,如果想計算它們的差值季率,就使用 e -- -h +17。因為一般來說不使用標識的情況比較多描沟,所以 e -- 就有了一個簡寫的方式飒泻,那就是 print鞭光。

輸入 help print

'print' is an abbreviation for 'expression --'.   
(print是 `expression --` 的縮寫)

打印對象

(lldb) p @[ @"foo", @"bar" ]
(NSArray *) $8 = 0x00007fdb9b71b3e0 @"2 objects" 

實際上,我們想看的是對象的 description 方法的結果泞遗。我么需要使用 -O (字母 O惰许,而不是數(shù)字 0) 標志告訴 expression 命令以 對象 (Object) 的方式來打印結果。

(lldb) e -O -- $8
<__NSArrayI 0x7fdb9b71b3e0>(
foo,
bar
)

幸運的是史辙,e -o -- 有也有個別名汹买,那就是 po (print object 的縮寫),我們可以使用它來進行簡化:

(lldb) po $8
<__NSArrayI 0x7fdb9b71b3e0>(
foo,
bar
)
(lldb) po @"lunar"
lunar
(lldb) p @"lunar"
(NSString *) $13 = 0x00007fdb9d0003b0 @"lunar"

Thread Return

調試時聊倔,還有一個很棒的函數(shù)可以用來控制程序流程:thread return 晦毙。它有一個可選參數(shù),在執(zhí)行時它會把可選參數(shù)加載進返回寄存器里耙蔑,然后立刻執(zhí)行返回命令见妒,跳出當前棧幀。這意味這函數(shù)剩余的部分不會被執(zhí)行甸陌。這會給 ARC 的引用計數(shù)造成一些問題须揣,或者會使函數(shù)內的清理部分失效。但是在函數(shù)的開頭執(zhí)行這個命令钱豁,是個非常好的隔離這個函數(shù)返敬,偽造返回值的方式 。

斷點

我們都把斷點作為一個停止程序運行寥院,檢查當前狀態(tài)劲赠,追蹤 bug 的方式。但是如果我們改變和斷點交互的方式秸谢,很多事情都變成可能凛澎。

想象把斷點放在函數(shù)的開頭,然后用 thread return 命令重寫函數(shù)的行為估蹄,然后繼續(xù)塑煎。想象一下讓這個過程自動化,聽起來不錯臭蚁,不是嗎最铁?

創(chuàng)建斷點

在上面的例子中,我們通過在源碼頁面器的滾槽 16 上點擊來創(chuàng)建斷點垮兑。你可以通過把斷點拖拽出滾槽冷尉,然后釋放鼠標來刪除斷點 (消失時會有一個非常可愛的噗的一下的動畫)系枪。你也可以在斷點導航頁選擇斷點雀哨,然后按下刪除鍵刪除。

要在調試器中創(chuàng)建斷點,可以使用breakpoint set命令雾棺。

(lldb) breakpoint set -f main.m -l 16
Breakpoint 1: where = DebuggerDance`main + 27 at main.m:16, address = 0x000000010a3f6cab

也可以在一個符號 (C 語言函數(shù)) 上創(chuàng)建斷點膊夹,而完全不用指定哪一行

(lldb) b isEven
Breakpoint 3: where = DebuggerDance`isEven + 16 at main.m:4, address = 0x000000010a3f6d00
(lldb) br s -F isEven
Breakpoint 4: where = DebuggerDance`isEven + 16 at main.m:4, address = 0x000000010a3f6d00

如果你 Xcode 的 UI 上右擊任意斷點,然后選擇 "Edit Breakpoint" 的話捌浩,會有一些非常誘人的選擇放刨。

lldb_004.png

你也可以添加多個行為,可以是調試器命令尸饺,shell 命令进统,也可以是更直接的打印:

lldb_005.png

UI操作

更新UI

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

然后在調試器中改變它的背景色:
(lldb) e (void)[$myView setBackgroundColor:[UIColor blueColor]]

但是只有程序繼續(xù)運行之后才會看到界面的變化侵佃。因為改變的內容必須被發(fā)送到渲染服務中麻昼,然后顯示才會被更新奠支。

渲染服務實際上是一個另外的進程 (被稱作 backboardd)馋辈。這就是說即使我們正在調試的內容所在的進程被打斷了,backboardd 也還是繼續(xù)運行著的倍谜。

這意味著你可以運行下面的命令迈螟,而不用繼續(xù)運行程序:

(lldb) e (void)[CATransaction flush]

Push 一個 View Controller

(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]

(lldb) caflush // e (void)[CATransaction flush]

查找按鈕的 target

(lldb) po [$myButton allTargets]
{(
    <MagicEventListener: 0x7fb58bd2e240>
)}
(lldb) po [$myButton actionsForTarget:(id)0x7fb58bd2e240 forControlEvent:0]
<__NSArrayM 0x7fb58bd2aa40>(
_handleTap:
)

觀察實例變量的變化

假設你有一個 UIView,不知道為什么它的 _layer 實例變量被重寫了 (糟糕)尔崔。因為有可能并不涉及到方法答毫,我們不能使用符號斷點。相反的季春,我們想監(jiān)視什么時候這個地址被寫入洗搂。

首先,我們需要找到 _layer 這個變量在對象上的相對位置:

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

現(xiàn)在我們知道 ($myView + 8) 是被寫入的內存地址:

(lldb) watchpoint set expression -- (int *)$myView + 8
Watchpoint created: Watchpoint 3: addr = 0x7fa554231340 size = 8 state = enabled type = w
    new value: 0x0000000000000000

這被以 wivar $myView _layer 加入到 Chisel 中载弄。

非重寫方法的符號斷點

假設你想知道 -[MyViewController viewDidAppear:] 什么時候被調用耘拇。如果這個方法并沒有在MyViewController 中實現(xiàn),而是在其父類中實現(xiàn)的宇攻,該怎么辦呢惫叛?試著設置一個斷點,會出現(xiàn)以下結果

(lldb) b -[MyViewController viewDidAppear:]
Breakpoint 1: no locations (pending).
WARNING:  Unable to resolve breakpoint to any actual locations.

因為 LLDB 會查找一個符號逞刷,但是實際在這個類上卻找不到嘉涌,所以斷點也永遠不會觸發(fā)。你需要做的是為斷點設置一個條件 [self isKindOfClass:[MyViewController class]]夸浅,然后把斷點放在 UIViewController 上仑最。正常情況下這樣設置一個條件可以正常工作。但是這里不會帆喇,因為我們沒有父類的實現(xiàn)词身。

viewDidAppear: 是蘋果實現(xiàn)的方法,因此沒有它的符號番枚;在方法內沒有 self 法严。如果想在符號斷點上使用 self损敷,你必須知道它在哪里 (它可能在寄存器上肄扎,也可能在棧上无牵;在 x86 上,你可以在 $esp+4 找到它)罕模。但是這是很痛苦的溯街,因為現(xiàn)在你必須至少知道四種體系結構 (x86诱桂,x86-64,armv7呈昔,armv64)挥等。想象你需要花多少時間去學習命令集以及它們每一個的調用約定,然后正確的寫一個在你的超類上設置斷點并且條件正確的命令堤尾。幸運的是肝劲,這個在 Chisel 被解決了。這被成為 bmessage:

(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

LLDB 和 Python

LLDB 有內建的郭宝,完整的 Python 支持辞槐。在LLDB中輸入 script,會打開一個 Python REPL粘室。你也可以輸入一行 python 語句作為 script 命令 的參數(shù)榄檬,這可以運行 python 語句而不進入REPL:

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

這樣就允許你創(chuàng)造各種酷的命令。把下面的語句放到文件 ~/myCommands.py 中:

def caflushCommand(debugger, command, result, internal_dict):
  debugger.HandleCommand("e (void)[CATransaction flush]")

然后再 LLDB 中運行:

command script import ~/myCommands.py

常用命令

frame variable

查看當前棧幀里幀參數(shù)和本地變量

register read

查看寄存器的值

register read $arg1 $arg2

查看兩個變量

memory read

查看內存地址的命令

(lldb) memory read --size 8 --format A --count 10 0x0000000100004630

memory write

內存值的修改

up down

stack frame 跳棧幀

bt

顯示所有的調用棧幀衔统。該命令可用來顯示函數(shù)的調用順序鹿榜。

disassemble --frame

disassemble相關指令用于輸出某段程序的匯編代碼 匯編當前棧幀的代碼。

使用disassemble -b則會輸出帶字節(jié)信息的匯編代碼:

Refrence

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末锦爵,一起剝皮案震驚了整個濱河市舱殿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌棉浸,老刑警劉巖怀薛,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異迷郑,居然都是意外死亡枝恋,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進店門嗡害,熙熙樓的掌柜王于貴愁眉苦臉地迎上來焚碌,“玉大人,你說我怎么就攤上這事霸妹∈纾” “怎么了?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長鹃骂。 經常有香客問我台盯,道長,這世上最難降的妖魔是什么畏线? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任静盅,我火速辦了婚禮,結果婚禮上寝殴,老公的妹妹穿的比我還像新娘蒿叠。我一直安慰自己,他們只是感情好蚣常,可當我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布市咽。 她就那樣靜靜地躺著,像睡著了一般抵蚊。 火紅的嫁衣襯著肌膚如雪施绎。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天泌射,我揣著相機與錄音粘姜,去河邊找鬼鬓照。 笑死熔酷,一個胖子當著我的面吹牛,可吹牛的內容都是我干的豺裆。 我是一名探鬼主播拒秘,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼臭猜!你這毒婦竟也來了躺酒?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤蔑歌,失蹤者是張志新(化名)和其女友劉穎羹应,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體次屠,經...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡园匹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了劫灶。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片裸违。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖本昏,靈堂內的尸體忽然破棺而出供汛,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布怔昨,位于F島的核電站雀久,受9級特大地震影響,放射性物質發(fā)生泄漏趁舀。R本人自食惡果不足惜岸啡,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望赫编。 院中可真熱鬧巡蘸,春花似錦、人聲如沸擂送。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嘹吨。三九已至搬味,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蟀拷,已是汗流浹背碰纬。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留问芬,地道東北人悦析。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像此衅,于是被迫代替她去往敵國和親强戴。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內容

  • 轉載 與調試器共舞 - LLDB 的華爾茲: https://objccn.io/issue-19-2/ 推薦:i...
    F麥子閱讀 3,331評論 0 10
  • 你是否曾經苦惱于理解你的代碼挡鞍,而去嘗試打印一個變量的值骑歹? NSLog(@"%@", whatIsInsideThi...
    木易林1閱讀 954評論 0 4
  • 你是否曾經苦惱于理解你的代碼,而去嘗試打印一個變量的值墨微? NSLog(@"%@", whatIsInsideThi...
    paraneaeee閱讀 1,186評論 0 7
  • [轉]淺談LLDB調試器文章來源于:http://www.cocoachina.com/ios/20150126/...
    loveobjc閱讀 2,487評論 2 6
  • 與調試器共舞 - LLDB 的華爾茲 nangege 2014/12/19 你是否曾經苦惱于理解你的代碼道媚,而去嘗試...
    McDan閱讀 881評論 0 0