在這一段剩下的章節(jié)中, 你將會(huì)聚焦于Python腳本上.
正如前一章中指出的, image lookup -rn
命令正在被淘汰的路上. 是時(shí)候來創(chuàng)建一個(gè)漂亮的腳本來顯示內(nèi)容了.
下面是你現(xiàn)在用image lookup -rn
命令能夠獲取到的內(nèi)容:
當(dāng)你學(xué)完這一章之后, 你將會(huì)有一個(gè)可以查的清除的叫做
lookup
的腳本.此外, 你將會(huì)給
lookup
命令添加一組參數(shù)來為新的搜索添加一些提示.
腳本創(chuàng)建的自動(dòng)化
包含在本章starter目錄下的這個(gè)項(xiàng)目是兩個(gè)能讓你在更輕松的創(chuàng)建LLDB腳本是Python 腳本. 這兩個(gè)腳本如下:
? generate_new_script.py: 這將會(huì)用你給的名字創(chuàng)建一個(gè)新的綱要腳本然后將它粘貼到generate_new_script
所在的同一個(gè)目錄下.
? lldbinit.py: 這個(gè)腳本將會(huì)枚舉它所在的目錄下的所有腳本(以.py
結(jié)尾的文件)然后嘗試將它們加載到LLDB中. 此外, 如果這個(gè)目錄下有.txt
拓展名的文件, LLDB將會(huì)嘗試通過command import
加載這些文件的內(nèi)容.
將本章starter目錄下的兩個(gè)文件粘貼到你的~/lldb/
目錄里.
把這些文件放在正確的位置之后, 進(jìn)到你的~/.lldbinit文件里然后添加下面一行代碼:
command script import ~/lldb/lldbinit.py
這將會(huì)加載lldbinit.py文件, lldbinit.py文件會(huì)枚與它同目錄下的所有的.py
文件和.txt
文件容納后將這些文件加載到LLDB中. 這就意味著從現(xiàn)在開始, 只需要簡單的將腳本添加到~/lldb
目錄下面就可以在LLDB啟動(dòng)的時(shí)候自動(dòng)加載到LLDB中.
創(chuàng)建lookup command
將你的新工具設(shè)置完成之后, 打開一個(gè)終端窗口. 啟動(dòng)一個(gè)新的LLDB實(shí)例:
lldb
正如期望的那樣, 你會(huì)迅速的看到LLDB的問候語.
確保你已經(jīng)存在的LLDB腳本沒有任何的編譯錯(cuò)誤:
(lldb) reload_script
如果輸出了一些錯(cuò)誤, 是時(shí)候試一下你的新命令了__generate_script(**generate_new_script.py **文件里實(shí)現(xiàn)的命令).
在LLD中, 輸入:
(lldb) __generate_script lookup
如果一切都如我們期望的那樣, 你將會(huì)得到一些類似下面的輸出:
Opening "/Users/derekselander/lldb/lookup.py"...
此外, 一個(gè)Finder窗口將會(huì)彈出來向你展示當(dāng)前文件所在的位置. 你可以用這些腳本做的事情是十分瘋狂的, 是吧?
將Finder窗口保留幾秒鐘--不要關(guān)閉它. 回到LLDB終端窗口中容納后使用reload_script命令.
因?yàn)?code>lookup.py腳本與lldbinit.py
腳本在同一個(gè)目錄下并且你剛剛重新加載了~/.lldbinit
中的內(nèi)容, 現(xiàn)在你要試一下lookup.py
文件是否正常工作. 輸入下面這個(gè)命令:
(lldb) lookup
你將會(huì)得到類似下面的輸出:
Hello! the lookup command is working!
現(xiàn)在你可以創(chuàng)建并使用至少兩個(gè)LLDB自定義命令. 耶, 你可以在一個(gè)命令里設(shè)置所有的內(nèi)容, 但是我現(xiàn)在手動(dòng)控制我腳本的加載.
lldbinit目錄結(jié)構(gòu)建議
我自己的lldbinit
文件結(jié)構(gòu)也許是有參考價(jià)值的. 盡管這一部分的內(nèi)容不是必要的, 但是更多的是建議你如何組織你所有的LLDB的自定義腳本和內(nèi)容.
我更傾向于保持我的~/.lldbinit
文件盡可能簡單并且使用一個(gè)像lldbinit.py
一樣的腳本加載特定目錄下的所有內(nèi)容. Facebook的fblldb.py做了同樣的事情(具有同樣的作用). 如果你有興趣的話就把它下載下來看看.
我將那個(gè)目錄添加到版本控制里這樣在我需要將同樣的邏輯轉(zhuǎn)移到不同的電腦上的時(shí)候, 或者我做錯(cuò)了事情需要回滾的時(shí)候就很方便.
例如, 我實(shí)際的~/.lldbinit
文件(在實(shí)際工作中而不是寫這本書的時(shí)候)僅僅包含下面的內(nèi)容:
command script import /Users/derekselander/lldb_repo/lldb_commands/
lldbinit.py
command script import /Users/derekselander/chisel/chisel/fblldb.py
lldb_repo
是一個(gè)公有的git倉庫https://github.com/DerekSelander/lldb, 這個(gè)倉庫里包含了一些為你想工程設(shè)計(jì)的LLDB腳本.
我同樣有Facebook的Chisel在版本控制上, 因此無論什么時(shí)候那些開發(fā)者更新了一些內(nèi)容, 發(fā)布了一些有趣的版本, 我只需要pull一下Chisel倉庫 https://github.com/facebook/chisel的最新版本我就可以擁有下次運(yùn)行LLDB時(shí)需要的一切, 或者通過reload_script
重新加載我的腳本.
在我的lldb_commands
目錄里, 我所有的Python腳本都作為兩個(gè)text文件存放起來. 一個(gè)text文件叫做cmds.txt并保存著我所有的command regex
和commandalias
. 另一個(gè)text文件叫做settings.txt, 這個(gè)文件是我用來補(bǔ)充一些LLDB設(shè)置的.
例如, 此刻我的settings.txt
文件的內(nèi)容如下:
settings set target.skip-prologue false
settings set target.x86-disassembly-flavor intel
在這本書前面的章節(jié)你已經(jīng)將這些設(shè)置添加到你的~/.lldbinit
文件里, 但是我更愿意將這些LLDB的設(shè)置與我自定義的LLDB命令分開, 以便我檢索~/.lldbinit
文件里的命令的時(shí)候不會(huì)丟失.
然而, 在這本書里, 我選擇將每一章的內(nèi)容都單獨(dú)作為一個(gè)腳本安裝.這就意味著你需要將內(nèi)容手動(dòng)的添加到你的~/.lldbinit
文件里以便你知道發(fā)什么事情. 當(dāng)你看完這本書的時(shí)候你應(yīng)該審視新的結(jié)構(gòu)實(shí)現(xiàn), 因?yàn)榘凑战ㄗh的這種布局做有幾個(gè)好處. 這些好處如下:
- 調(diào)用
reload_script
僅顯示這個(gè)命令~/.lldbinit
正在加載; 它不會(huì)顯示子腳本被加載了. 例如 這將會(huì)反饋lldbinit.py
被加載了, 到那時(shí)不會(huì)反饋lldbinit.py
文件本身被加載的內(nèi)容.這讓創(chuàng)建腳本變的更容易因?yàn)槲医?jīng)常用reload_script
作為檢查我最后使用的腳本的任何錯(cuò)誤消息的方式. 執(zhí)行reload_script
后產(chǎn)生的輸出越少, 在控制臺(tái)中需要檢查的錯(cuò)誤就越少. - 正如你注意到的,
~/.lldbinit
文件里的內(nèi)容越少你i就可以越輕松的在不同電腦間切換, 尤其是將它的內(nèi)容添加到版本控制之后. - 最后, 用這樣的實(shí)現(xiàn)盡可能簡單的添加新的腳本. 只需要將它們添加到
lldbinit.py
文件所在的目錄里然后下一次它就會(huì)被加載. 備選方案是你手動(dòng)的將腳本的路徑添加到~/.lldbinit
文件里, 如果你經(jīng)常做這件事的話你會(huì)覺得它很煩人.
在本章中你將使用將腳本粘貼到~/lldb
目錄下的方式然后將它們加載到LLDB中的這種實(shí)現(xiàn)策略去保存腳本...這一種方法更好一點(diǎn), 對吧?
實(shí)現(xiàn)lookup 命令
正如你在前一章中看到的, 在lookup
命令的背后foundation其實(shí)相當(dāng)簡單. 最主要的秘密是使用SBTarget的FindGlobalFunctions
API. 在那之后, 你所需要做的就是用你喜歡的方式格式化這些輸出.
你將繼續(xù)使用Allocator項(xiàng)目, 在本章的starter目錄下可以找到這個(gè)項(xiàng)目.
打開這個(gè)項(xiàng)目, 構(gòu)建并在iPhone 7 plus模擬器上運(yùn)行. 你將會(huì)使用這個(gè)項(xiàng)目測試本章中你新的lookup
命令的查詢功能.
項(xiàng)目運(yùn)行起來之后, 暫停這個(gè)應(yīng)用程序并進(jìn)到LLDB中.
我的記憶有點(diǎn)模糊不清了. 這個(gè)FindGlobalFunctions
需要指定哪個(gè)參數(shù)? 在LLDB中輸入下面內(nèi)容:
(lldb) script help(lldb.SBTarget.FindGlobalFunctions)
你將會(huì)得到下面的輸出, 輸出展示了方法的簽名:
FindGlobalFunctions(self, *args) unbound lldb.SBTarget method
FindGlobalFunctions(self, str name, uint32_t max_matches, MatchType
matchtype) -> SBSymbolContextList
因?yàn)樗且粋€(gè)Python的類, 所以你可以忽略第一個(gè)self參數(shù). str
參數(shù)的名字叫做name將會(huì)是你lookuo查詢的內(nèi)容. max_matches
指出你想要觸發(fā)的最大數(shù)量. 如果你指定的數(shù)字是0, 它將會(huì)返回所有可用的匹配. matchType 參數(shù)是一個(gè)lldb Python枚舉值, 用這個(gè)枚舉值你可以執(zhí)行不同類型的搜索, 比如正則或非正則.
因?yàn)檎齽t搜索是唯一能行的通的方法, 所以你將要使用LLDB的枚舉值lldb.eMatchTypeRegex.其他的枚舉值可以在https://lldb.llvm.org/python_reference/_lldb%27-module.html#eMatchTypeRegex上找到. 是時(shí)候?qū)⑦@些功能實(shí)現(xiàn)在lookup.py腳本上了. 用你最愛的編輯器打開~/lldb/lookup.py. 在handle_command的末尾找到下面的代碼:
# Uncomment if you are expecting at least one argument
# clean_command = shlex.split(args[0])[0]
result.AppendMessage('Hello! the lookup command is working!')
刪除上面的代碼, 用下面的代碼替代它, 確保你的縮進(jìn)保持原狀:
#1
clean_command = shlex.split(args[0])[0]
#2
target = debugger.GetSelectedTarget()
#3
contextlist = target.FindGlobalFunctions(clean_command, 0, lldb.eMatchTypeRegex)
#4
result.AppendMessage(str(contextlist))
下面是對上面代碼的解讀:
- 包含傳到這個(gè)腳本里的命令的干凈的版本, 使用你在第二十章看到的同樣的魔法.
- 通過
SBDebugger
抓取SBTarget
實(shí)例. - 將
clean_command
傳遞給FindGlobalFunctions
API并調(diào)用FindGlobalFunctions
. 你傳入了0用了設(shè)置了結(jié)果沒有數(shù)量上的上限并且傳入了eMatchTypeRegex
類型的匹配來使用正則表達(dá)式搜索. - 你正在將
contextlist
轉(zhuǎn)換到一個(gè)Pythonstr
然后將它添加到SBCommandReturnObject
.
回到Xcode, 用LLDB控制臺(tái)重新加載腳本的內(nèi)容:
(lldb) reload_script
試著運(yùn)行一下lookup
命令. 還記得你在上一章中研究的DSObjectiveCObject
類嗎? 通過LLDN提取出相關(guān)的所有內(nèi)容:
lookup DSObjectiveCObject
你將會(huì)得到一些實(shí)際上看起來比image lookup -rnDSObjectiveCObject更糟糕的輸出:
使用LLDB的腳本命令來弄清楚進(jìn)一步瀏覽了哪一個(gè)API:
(lldb) script k = lldb.target.FindGlobalFunctions('DSObjectiveCObject',0, lldb.eMatchTypeRegex)
這個(gè)命令將會(huì)重復(fù)lookup.py
腳本所做的事情并且將SBSymbolContextList
實(shí)例的值賦值給k
. 在瀏覽API名字的時(shí)候我是短變量名字的粉絲--如果你沒有注意到的話.
瀏覽SBSymbolContextList
的文檔:
(lldb) gdocumentation SBSymbolContextList
這條命令會(huì)提取出SBSymbolContextList
實(shí)現(xiàn)的或者重寫的搜索方法. 這里會(huì)有很多方法. 但是請將注意力放在__iter__
和__getitem__
.
這對你的腳本來說是一個(gè)好事, 因?yàn)檫@意味著
SBSymbolContextList
是可迭代以及可索引的. 幾秒鐘之前, 你剛剛通過LLDB把一個(gè)SBSymbolContextList
的實(shí)例賦值給了名字叫做k
的變量.在LLDB控制臺(tái)中, 使用索引來抓取
k
對象里的子項(xiàng).
(lldb) script k[0]
這相當(dāng)于輸入了script k.__getitem__(0)
.
你將會(huì)得到一些類似下面的輸出:
<lldb.SBSymbolContext; proxy of <Swig Object of type
'lldb::SBSymbolContext *' at 0x113a83780> >
很好理解! SBSymbolContextList
持有一個(gè)由SBSymbolContext
的組成的數(shù)組.
使用print
命令獲取這個(gè)SBSymbolContext
的上下文:
(lldb) script print k[0]
你的輸出與我的可能不同, 但是我獲取到了代表[DSObjectiveCObject setLastName:]
的SBSymbolContext
, 像下面這樣:
Module: file = "/Users/derekselander/Library/Developer/Xcode/
DerivedData/Allocator-czsgsdzfgtmanrdjnydkbzdmhifw/Build/Products/Debug-
iphonesimulator/Allocator.app/Allocator", arch = "x86_64"
CompileUnit: id = {0x00000000}, file = "/Users/derekselander/iOS/dbg/s4-
custom-lldb-commands/22. Ex 1, Improved Lookup/projects/final/Allocator/
Allocator/DSObjectiveCObject.m", language = "objective-c"
Function: id = {0x100000268}, name = "-[DSObjectiveCObject
setLastName:]", range = [0x0000000100001c00-0x0000000100001c37)
FuncType: id = {0x100000268}, decl = DSObjectiveCObject.h:33,
compiler_type = "void (NSString *)"
Symbol: id = {0x0000001e}, range =
[0x0000000100001c00-0x0000000100001c40), name="-[DSObjectiveCObject
setLastName:]"
你將會(huì)使用屬性或者getter方法從SBSymbolContext
里抓取函數(shù)的名字.
做這件事情最簡單的方式是通過SBSymbolContext
的symbol屬性抓取SBSymbol
.這里的SBSymbol
里包含一個(gè)name屬性, 這個(gè)屬性會(huì)返回一個(gè)讓你快樂的Python字符串.
在你的LLDB控制臺(tái)中驗(yàn)證一下這個(gè)邏輯:
(lldb) script print k[0].symbol.name
在我這里, 我收到了下面的內(nèi)容:
-[DSObjectiveCObject setLastName:]
這些信息足夠構(gòu)建出你的腳本了. 你將會(huì)迭代SBSymbolContextList
里的內(nèi)一個(gè)子項(xiàng)并打印出它找到的函數(shù)名字.
回到lookup.py
腳本的頭部并修改handle_command
函數(shù)的內(nèi)容. 找到下面的代碼:
#3
contextlist = target.FindGlobalFunctions(clean_command, 0, lldb.eMatchTypeRegex)
#4
result.AppendMessage(str(contextlist))
用下面的代碼來替換(縮進(jìn)要正確!):
contextlist = target.FindGlobalFunctions(clean_command, 0,
lldb.eMatchTypeRegex)
output = ''
for context in contextlist:
output += context.symbol.name + '\n\n'
result.AppendMessage(output)
現(xiàn)在你正在迭代SBSymbolContextList
里返回的所有SBSymbolContext
, 提取出函數(shù)的名字并用兩個(gè)換行符來分割它們.
回到Xcode中, 然后重新加載你的腳本:
(lldb) reload_script
然后在LLDB中測試一下你更新以后的lookup
命令:
(lldb) lookup DSObjectiveCObject
你將會(huì)得到一些比之前更漂亮的輸出:
-[DSObjectiveCObject setLastName:]
-[DSObjectiveCObject .cxx_destruct]
-[DSObjectiveCObject setFirstName:]
-[DSObjectiveCObject eyeColor]
-[DSObjectiveCObject init]
-[DSObjectiveCObject lastName]
-[DSObjectiveCObject setEyeColor:]
-[DSObjectiveCObject firstName]
這就是全部的好東西, 但是我想看看這些函數(shù)在進(jìn)程里的什么位置. 我想將所有的函數(shù)組合起來放到一個(gè)特定的模塊里(一個(gè)SBModule), 當(dāng)它們被打印出來的時(shí)候通過將模塊的名字和這個(gè)模塊被調(diào)用的次數(shù)放在頭部分割開來.
回到lookup.py文件中. 現(xiàn)在你要?jiǎng)?chuàng)建兩個(gè)新的函數(shù).
第一個(gè)函數(shù)叫做generateFunctionDictionary
, 這個(gè)函數(shù)將會(huì)帶一個(gè)SBBreakpointContextList
參數(shù)并且生成一個(gè)Python 列表的字典. 這些字典包含每個(gè)模塊的keys. 字典中的value, 將會(huì)是每一個(gè)被調(diào)用的SBSymbolContext
的Python列表.
第二個(gè)函數(shù)叫做generateOutput, 這個(gè)函數(shù)將會(huì)沿著你從OptionParser
實(shí)例收到的options
解析這些你剛才創(chuàng)建的字典.這個(gè)方法將會(huì)返回一個(gè)字符串并打印才控制臺(tái)中.
在lookup.py腳本中的handle_command
函數(shù)下面開始按照下面的方式實(shí)現(xiàn)generateModuleDictionary
函數(shù):
def generateModuleDictionary(contextlist):
mdict = {}
for context in contextlist: #1
key = context.module.file.fullpath
#2
if not key in mdict:
mdict[key] = []
#3
mdict[key].append(context)
return mdict
下面是對代碼的解讀:
- 從
SBSymbolContext
里開始, 你正在抓取SBModule
(module), 然后是SBFileSpec
(file), 然后是fullPath的Python字符串并且將它賦值給名字叫做key的變量. 抓取fullPath
是重要的(取而代之的是, say,SBFileSpec
的basename屬性, 因?yàn)檫@里同樣的basename可能有多個(gè)模塊). - 那個(gè)
mdict
變量準(zhǔn)備保存發(fā)現(xiàn)的所有符號(hào), 通過模塊隔開. 字典里的key將會(huì)是模塊的名字, value將會(huì)是在那個(gè)模塊里發(fā)現(xiàn)的符號(hào)的數(shù)組.在這一行, 你正在檢查這個(gè)字典是否已經(jīng)包含了這個(gè)模塊的列表. 如果沒有, 一個(gè)空的數(shù)組會(huì)被設(shè)置到這個(gè)模塊的key上. - 你正在將
SBSymbolContext
實(shí)例添加到這個(gè)模塊合適的列表里. 你可以放心的假設(shè)mdict
變量里的每一個(gè)key, 這里將至少有一個(gè)或者多個(gè)SBSymbolContext
實(shí)例.
注意: 一個(gè)獲取唯一的key的更簡單的方法是使用`SBModule`里的**__str__()**方法(以及LLDB Python模塊中的每一個(gè)類). 當(dāng)你在其中一個(gè)對象上調(diào)用Python的**print**的時(shí)候就會(huì)被調(diào)用. 然而, 如果你只依靠`__str__()`你不會(huì)學(xué)習(xí)進(jìn)程里的所有這些類, 屬性和方法
在generateModuleDictionary
函數(shù)下面, 實(shí)現(xiàn)generateOutput函數(shù):
def generateOutput(mdict, options, target): #1
output = ''
separator = '*' * 60 + '\n' #2
for key in mdict:
#3
count = len(mdict[key])
firstItem = mdict[key][0]
#4
moduleName = firstItem.module.file.basename
output += '{0}{1} hits in {2}\n{0}'.format(separator,
#5
for context in mdict[key]:
query = ''
query += context.symbol.name
query += '\n\n'
output += query
return output
下面是對代碼的解讀:
-
output變量將會(huì)是返回的包含傳給
SBCommandReturnObject
的所有內(nèi)容字符串. - 枚舉
mdict
字典中發(fā)現(xiàn)的所有的key. - 這將會(huì)抓取array里子項(xiàng)的數(shù)量以及每一個(gè)數(shù)組里的第一個(gè)子項(xiàng). 稍后你將使用這些信息查詢模塊的名字.
- 你正在抓取模塊名字以便應(yīng)用到每一段的頭部輸出.
- 這將會(huì)迭代Python數(shù)組中搜索的
SBSymbolContext
然后將名字添加到output
變量.
在你測試這個(gè)腳本之前還有最后一點(diǎn)要做.
補(bǔ)充handle_command函數(shù)里的代碼以便它可以使用你剛剛創(chuàng)建的這兩個(gè)新的方法. 找到下面的代碼:
output = ''
for context in contextlist:
output += context.symbol.name + '\n\n'
并用下面的代碼代替:
mdict = generateModuleDictionary(contextlist)
output = generateOutput(mdict, options, target)
你知道做什么.回到Xcode中; 重新加載LLDB中的內(nèi)容:
(lldb) reload_script
檢查你新改善的lookup
命令:
(lldb) lookup DSObjectiveCObject
你將會(huì)得到一些類似下面的輸出:
************************************************************
8 hits in Allocator
************************************************************
-[DSObjectiveCObject setLastName:]
-[DSObjectiveCObject .cxx_destruct]
-[DSObjectiveCObject setFirstName:]
-[DSObjectiveCObject eyeColor]
-[DSObjectiveCObject init]
-[DSObjectiveCObject lastName]
-[DSObjectiveCObject setEyeColor:]
-[DSObjectiveCObject firstName]
酷. 然后查看所有壹initWith開頭并且包含兩個(gè)參數(shù)的 Objective-C 方法.
(lldb) lookup initWith(\w+\:){2,2}\]
你會(huì)得到加載到Allocator
進(jìn)程里的所有的公有模塊和私有模塊的方法.
為lookup添加選項(xiàng)
你要保持選項(xiàng)很好很簡單而且只實(shí)現(xiàn)兩個(gè)不需要其他參數(shù)的選項(xiàng).
你需要實(shí)現(xiàn)下面的內(nèi)容:
? 添加加載地址到每一個(gè)查詢上. 這是理想如果你想知道函數(shù)在內(nèi)存中的實(shí)際位置.
? 僅僅提供一個(gè)模塊的簡介. 不要產(chǎn)生函數(shù)名字, 只列出每一個(gè)模塊調(diào)用的數(shù)量.
__generate_script
為在lookup.py文件底部發(fā)現(xiàn)的generateOptionParser
方法添加了一些占位符. 在generateOptionParser函數(shù)中, 改變這個(gè)函數(shù)以便它包含下面的代碼:
def generateOptionParser():
usage = "usage: %prog [options] code_to_query"
parser = optparse.OptionParser(usage=usage, prog="lookup")
parser.add_option("-l", "--load_address",
action="store_true",
default=False,
dest="load_address",
help="Show the load addresses for a particular hit")
parser.add_option("-s", "--module_summary",
action="store_true",
default=False,
dest="module_summary",
help="Only show the amount of queries in the module")
return parser
這里不需要深入解讀這些代碼因?yàn)槟阋呀?jīng)在前面的章節(jié)學(xué)過這些東西. 你已經(jīng)創(chuàng)建了兩個(gè)支持的選項(xiàng), -s, 或者--module_summary以及-l, 或者--load_address.
首先你要實(shí)現(xiàn)加載地址的選項(xiàng). 在generateOutput函數(shù)中, 找到以context in mdict[key]:
開頭的for循環(huán)迭代SBSymbolContext
的代碼.
按照下面的方式修改for循環(huán):
for context in mdict[key]:
query = ''
#1
if options.load_address:
#2
start = context.symbol.addr.GetLoadAddress(target) end = context.symbol.end_addr.GetLoadAddress(target) #3
startHex = '0x' + format(start, '012x')
endHex = '0x' + format(end, '012x')
query += '[{}-{}]\n'.format(startHex, endHex)
query += context.symbol.name
query += '\n\n'
output += query
下面是這些代碼做的事情:
- 你添加了一個(gè)條件句來判斷
load_address
選項(xiàng)時(shí)候被設(shè)置了. 如果被設(shè)置了, 這將添加內(nèi)容到輸出上. - 這種從
SBSymbolContext
到SBSymbol
(symbol屬性)到SBAddress
(addr或者end_addr)然后通過GetLoadAddress
獲取Pythonlong
. 這里實(shí)際有一個(gè)load_addr
變量給SBAddress
, 但是我已經(jīng)及時(shí)的發(fā)現(xiàn)了一個(gè)bug, 因此我已經(jīng)默認(rèn)使用GetLoadAddress
API替代. 這個(gè)方法期望SBTarget
作為一個(gè)輸入?yún)?shù). - 在你有了Python long的起始表達(dá)式和結(jié)束表達(dá)式之后, 你已經(jīng)講他們格式化的很漂亮并且用Python的format函數(shù)組合在一起. 如果需要會(huì)補(bǔ)上0, 注意它應(yīng)該是12個(gè)數(shù)字的長度, 并且將它格式化為十六進(jìn)制.
保存你的工作成果并且重新查看Xcode和LLDB控制臺(tái). 重新加載.
(lldb) reload_script
測試一下你的新選項(xiàng):
(lldb) lookup -l DSObjectiveCObject
你將會(huì)得到一些類似下面的輸出:
************************************************************
8 hits in Allocator
************************************************************
[0x0001099d2c00-0x0001099d2c40]
-[DSObjectiveCObject setLastName:]
[0x0001099d2c40-0x0001099d2cae]
-[DSObjectiveCObject .cxx_destruct]
在這個(gè)列表中的一個(gè)地址上設(shè)置一個(gè)斷點(diǎn)看看它是否與函數(shù)相匹配.
按照下面的方式做, 用你列表中的地址替換下面的地址:
(lldb) b 0x0001099d2c00
Breakpoint 3: where = Allocator`-[DSObjectiveCObject setLastName:] at
DSObjectiveCObject.h:33, address = 0x00000001099d2c00
做得好!你已經(jīng)實(shí)現(xiàn)了一個(gè)多選項(xiàng)的命令!
在最后重新看一下generateOutput.找到下面這一行代碼:
moduleName = firstItem.module.file.basename
在這一行代碼的后面加上下面的內(nèi)容:
if options.module_summary:
output += '{} hits in {}\n'.format(count, moduleName)
continue
這寫代碼簡單的添加了每一個(gè)模塊調(diào)用的次數(shù)并且跳過了了添加實(shí)際符號(hào).
就是這些.沒有更多的代碼.保存, 然后回到Xcode中重新加載你的腳本:
(lldb) reload_script
測試一下你的module_summary:
(lldb) lookup -s viewWillAppear
你將會(huì)得到一些類似下面的代碼:
46 hits in UIKit
1 hits in WebKit
4 hits in Allocator
就是這些!你已經(jīng)做完了!你已經(jīng)從草稿中創(chuàng)建了一個(gè)更強(qiáng)大的腳本. 在后面的章節(jié)中你將使用這個(gè)腳本搜索代碼. 當(dāng)你制定一個(gè)寬泛的搜索范圍的然后想將范圍進(jìn)一步縮小的時(shí)候summary
選項(xiàng)是一個(gè)強(qiáng)大的工具.
我們?yōu)槭裁匆獙W(xué)這些?
這里還有更多的選項(xiàng)可以添加到lookup
命令上. 你可以通過在SBSymbolContext
的SBFunction
(通過function屬性)的后面創(chuàng)建一個(gè)-S
或者-Swift_only
選型
去訪問GetLanguage()API.
當(dāng)你實(shí)現(xiàn)了之后, 你也應(yīng)該添加一個(gè)-m或者--module選項(xiàng)過濾出某個(gè)模塊的內(nèi)容.
如果你想看看還有哪些選項(xiàng)可用, 可以在https://github.com/DerekSelander/LLDB/blob/master/lldb_commands/lookup.py查看我實(shí)現(xiàn)的lookup
命令.
享受添加的這些選項(xiàng)吧!