第二十二章:SB?Examples, 改進(jìn)Lookup

在這一段剩下的章節(jié)中, 你將會(huì)聚焦于Python腳本上.
正如前一章中指出的, image lookup -rn命令正在被淘汰的路上. 是時(shí)候來創(chuàng)建一個(gè)漂亮的腳本來顯示內(nèi)容了.
下面是你現(xiàn)在用image lookup -rn命令能夠獲取到的內(nèi)容:

圖片.png

當(dāng)你學(xué)完這一章之后, 你將會(huì)有一個(gè)可以查的清除的叫做lookup的腳本.
圖片.png

此外, 你將會(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 regexcommandalias. 另一個(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è)好處. 這些好處如下:

  1. 調(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ò)誤就越少.
  2. 正如你注意到的, ~/.lldbinit文件里的內(nèi)容越少你i就可以越輕松的在不同電腦間切換, 尤其是將它的內(nèi)容添加到版本控制之后.
  3. 最后, 用這樣的實(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))

下面是對上面代碼的解讀:

  1. 包含傳到這個(gè)腳本里的命令的干凈的版本, 使用你在第二十章看到的同樣的魔法.
  2. 通過SBDebugger抓取SBTarget實(shí)例.
  3. clean_command傳遞給FindGlobalFunctions API并調(diào)用FindGlobalFunctions. 你傳入了0用了設(shè)置了結(jié)果沒有數(shù)量上的上限并且傳入了eMatchTypeRegex類型的匹配來使用正則表達(dá)式搜索.
  4. 你正在將contextlist轉(zhuǎn)換到一個(gè)Python str然后將它添加到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更糟糕的輸出:

圖片.png

使用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__.

圖片.png

這對你的腳本來說是一個(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ù)的名字.
做這件事情最簡單的方式是通過SBSymbolContextsymbol屬性抓取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

下面是對代碼的解讀:

  1. SBSymbolContext里開始, 你正在抓取SBModule(module), 然后是SBFileSpec(file), 然后是fullPath的Python字符串并且將它賦值給名字叫做key的變量. 抓取fullPath是重要的(取而代之的是, say, SBFileSpecbasename屬性, 因?yàn)檫@里同樣的basename可能有多個(gè)模塊).
  2. 那個(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上.
  3. 你正在將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

下面是對代碼的解讀:

  1. output變量將會(huì)是返回的包含傳給SBCommandReturnObject的所有內(nèi)容字符串.
  2. 枚舉mdict字典中發(fā)現(xiàn)的所有的key.
  3. 這將會(huì)抓取array里子項(xiàng)的數(shù)量以及每一個(gè)數(shù)組里的第一個(gè)子項(xiàng). 稍后你將使用這些信息查詢模塊的名字.
  4. 你正在抓取模塊名字以便應(yīng)用到每一段的頭部輸出.
  5. 這將會(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

下面是這些代碼做的事情:

  1. 你添加了一個(gè)條件句來判斷load_address選項(xiàng)時(shí)候被設(shè)置了. 如果被設(shè)置了, 這將添加內(nèi)容到輸出上.
  2. 這種從SBSymbolContextSBSymbol(symbol屬性)到SBAddress(addr或者end_addr)然后通過GetLoadAddress獲取Python long. 這里實(shí)際有一個(gè)load_addr變量給SBAddress, 但是我已經(jīng)及時(shí)的發(fā)現(xiàn)了一個(gè)bug, 因此我已經(jīng)默認(rèn)使用GetLoadAddressAPI替代. 這個(gè)方法期望SBTarget作為一個(gè)輸入?yún)?shù).
  3. 在你有了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命令上. 你可以通過在SBSymbolContextSBFunction(通過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)吧!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末渴邦,一起剝皮案震驚了整個(gè)濱河市易核,隨后出現(xiàn)的幾起案子洋措,更是在濱河造成了極大的恐慌,老刑警劉巖疾就,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異艺蝴,居然都是意外死亡猬腰,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門猜敢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來姑荷,“玉大人,你說我怎么就攤上這事锣枝∠崾茫” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵撇叁,是天一觀的道長供鸠。 經(jīng)常有香客問我,道長陨闹,這世上最難降的妖魔是什么楞捂? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任薄坏,我火速辦了婚禮,結(jié)果婚禮上寨闹,老公的妹妹穿的比我還像新娘胶坠。我一直安慰自己,他們只是感情好繁堡,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布沈善。 她就那樣靜靜地躺著,像睡著了一般椭蹄。 火紅的嫁衣襯著肌膚如雪闻牡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天绳矩,我揣著相機(jī)與錄音罩润,去河邊找鬼。 笑死翼馆,一個(gè)胖子當(dāng)著我的面吹牛割以,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播应媚,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼严沥,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了珍特?” 一聲冷哼從身側(cè)響起祝峻,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎扎筒,沒想到半個(gè)月后莱找,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嗜桌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年奥溺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片骨宠。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡浮定,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出层亿,到底是詐尸還是另有隱情桦卒,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布匿又,位于F島的核電站方灾,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜裕偿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一洞慎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嘿棘,春花似錦劲腿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至圆仔,卻和暖如春垃瞧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背坪郭。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留脉幢,地道東北人歪沃。 一個(gè)月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像嫌松,于是被迫代替她去往敵國和親沪曙。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355

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