第二十章:有選項和參數(shù)的橋接腳本(一)

當(dāng)你在創(chuàng)建一個自定義的調(diào)試命令的時候, 你經(jīng)常在你的命令上用一些選項或者參數(shù). 只有一種方法可以執(zhí)行一項任務(wù)的自定義LLDB命令是讓人厭惡的只會一招的小馬駒.
在這一章中, 你將會學(xué)習(xí)如何傳一個可選的參數(shù)作為你自定義命令的參數(shù)來修改你自定義LLDB腳本中的功能和邏輯.
你將會繼續(xù)使用你在前面的章節(jié)中創(chuàng)建的bar(break-after-regex)命令工作. 在這一章中, 你將會通過在你的腳本中添加邏輯處理選項來完成bar命令.
在這一章的結(jié)尾, bar命令將會有邏輯來處理下面的可選參數(shù):
? Non-regular expression search: 使用-n或者--non_regex選項將會導(dǎo)致bar命令用一個非正在表達(dá)式斷點搜索. 這個選項不會帶任何額外的參數(shù).
? Filter by module: 使用-m或者--module選項將會僅僅只搜索特定模塊中的斷點. 這個選項期望有一個額外的指明模塊名字的參數(shù).
? Stop on condition: 使用-c或者--condition選項, 在步出當(dāng)前函數(shù)之后bar命令將會執(zhí)行給定的條件. 如果為True, 執(zhí)行將會停止. 如果為False, 執(zhí)行將會繼續(xù). 這個選項期望一個額外的將會被執(zhí)行而且是作為一個Objective-CBOOL執(zhí)行的字符串代碼作為參數(shù).
這一章將會是內(nèi)容稠密但又很有趣的一章. 確保你手中已經(jīng)有了一杯咖啡!

設(shè)置

如果你已經(jīng)讀過了之前的章節(jié)而且你的bar命令是可以工作的, 然后你可以繼續(xù)使用那個腳本并且可以忽略這一部分內(nèi)容. 否則, 打開本章資源中的start文件夾, 然后復(fù)制BreakAfterRegex.py 文件到~/lldb文件夾中. 確保你的~/.lldbinit文件中已經(jīng)有了在前面的章節(jié)中添加的下面這行內(nèi)容:

command script import ~/lldb/BreakAfterRegex.py

如果你對于這個名師是否成功的加載到LLDB中有什么困惑, 只需要在終端中簡單的新開一個LLDB實例:

lldb

然后查看bar的文檔:

(lldb) help bar
圖片.png

如果你得到了一個錯誤, 它就沒有成功的加載到LLDB中; 但是如果你的到了文檔字符串, 那就表明成功的加載到了LLDB中.

RWDevCon項目

在本章中, 你將會使用一個叫做RWDevcon的APP. 它是一個直播APP, 可以在APP Store中下載https://itunes.apple.com/us/app/rwdevcon-the-tutorial-conference/id958625272?mt=8.

圖片.png

這個APP是RWDevcon引用的同伴APP, https://www.rwdevcon.com/, 這是一個查看在Ray Wenderlich生氣之前你可以拍多少下Ray Wenderlich的肩膀. 嘗試運行它!我個人的最好值是37!
在這個項目中, 我已經(jīng)建了一個分支84167c68, 這個分支可以starter文件夾中找到. 然而, 你可以在https://github.com/raywenderlich/RWDevCon-App這個網(wǎng)站上找到更早的版本.
找到start文件夾然后打開, 構(gòu)建, 然后運行這個應(yīng)用程序. 看一個了解一下這個項目.
這里不需要瀏覽任何的源代碼. 在bar命令的幫助下, 你可以瀏覽只能斷點查詢到的不同的有趣的事物.
但是在我們做之前, 讓我們先討論一下如何讓bar命令變得更加強大.

Python模塊optparse

LLDB Python腳本中最可愛的事情是你可以發(fā)揮Python的所有力量以及它所有的模塊.
在Python 2.7中在解析現(xiàn)象和參數(shù)時這里有三個值得查看的非常相關(guān)的模塊: getopt, getopt, 和argparse. getopt是一種地級別的而optparse是正在淘汰的路上因為它在Python 2.7之后的版本被廢棄了. 不幸的是argparse在設(shè)計的時候幾乎是和Python的sys.argv一起使用的-- sys.argv是不能在Python 的LLDB命令腳本中使用的.這就意味著optparse將會是你的go-to選項. Facebook的Chisel, Apple自己自定義的LLDB腳本, 而我全部使用這個模塊. 因此, 它有一點是事實上的解析參數(shù)的標(biāo)準(zhǔn).
optparse模塊可以讓你定義一個OptionParser類型的實例, 一個負(fù)責(zé)解析所有參數(shù)的類. 在這個類工作的時候, 你需要聲明你的命令支持的參數(shù)和選項. 這產(chǎn)生了一種情況因為可選的參數(shù)可能為那個特定的選項帶有額外的值或者沒帶額外的值.
看一個簡單的例子. 思考下面的代碼:

some_command woot -b 34 -a "hello world"

這個命令的名字叫做some_command. 但是傳到這個命令里的參數(shù)和選項是什么呢?
如果你沒有給這個解析器任何的上下文, 然后這個句子就可能是模棱兩可的. 這個解析器不知道-b或者-a選項是否應(yīng)該帶一個參數(shù). 例如, 這個解析器可能會認(rèn)為這個命令傳入了三個參數(shù):[woot, 34, hello world], 和兩個沒有參數(shù)的選項-b, -a. 然兒, 如果解析器期望-b或者-a帶一個參數(shù), 解析器就會將[woot作為參數(shù), 34作為-b的參數(shù)而hello world作為-a的參數(shù).
讓我們更深入到optparse函數(shù)中, 然后看一看我們?nèi)绾问褂盟鼇硖幚眍愃频那闆r.

添加沒有參數(shù)的選項

伴隨著你需要告訴解析器哪些參數(shù)是期望的, 是時候添加你的第一個可以修改bar命令功能的選項來應(yīng)用到?jīng)]有使用正則表達(dá)式的SBBreakpoint, 而不是使用一個普通的正則表達(dá)式.
這個參數(shù)將會通過一個Python的boolean值返回, 這個選項不需要參數(shù). 存在的這個選項就是你需要檢測的boolean值的所有信息. 如果參數(shù)存在, 然后它的值就會是True. 否則, 它的值就是False.
有些腳本的作者會為這個boolean值設(shè)計一個指明必要的參數(shù)的boolean選項而且這個選項的默認(rèn)值可能是True也可能是False如果這個選項沒有賦值的話.
例如, 下面的命令帶了一個選項, -f沒有帶參數(shù):

some_command -f

這將會被轉(zhuǎn)換為:

some_command -f1

這真不是我的風(fēng)格. 但是如果你正在為廣泛的用戶寫腳本的話你可能要考慮這種設(shè)計方式, 因為它給了用戶更詳細(xì)的目的.
好了, 閑聊夠了. 讓我們?nèi)崿F(xiàn)這個解析器的內(nèi)容吧.
打開BreakAfterRegex.py然后在這個文件的頂部添加下面的import語句:

import optparse
import shlex

optparse 模塊包含OptionParser 類, OptionParser 類可以解析指令中額外輸入的任何內(nèi)容.
shlex模塊有一個好用的小Python函數(shù), 這個函數(shù)可以方便的分割參數(shù),并將參數(shù)應(yīng)用到您的指令中, 同時保持字符串不變.
例如, 思考下面的Python代碼:

import shlex
command = '"hello world" "2nd parameter" 34'
shlex.split(command)

這將會產(chǎn)生下面的輸出:

['hello world', '2nd parameter', '34']

這里返回一個python的list, 元素內(nèi)容是解析后的python的string.
但是在你去用split方法之前, 你需要創(chuàng)建一個解析器.
BreakAfterRegex.py文件的底部, 添加如下代碼:

def generateOptionParser():
  '''Gets the return register as a string for lldb
    based upon the hardware
  '''
  usage = "usage: %prog [options] breakpoint_query\n" +\
                  "Use 'bar -h' for option desc"
  #1
  parser = optparse.OptionParser(usage=usage, prog='bar')
  #2
  parser.add_option("-n", "--non_regex",
                                  #3
                                  action="store_true",
                                  #4
                                  default=False,
                                  #5
                                  dest="non_regex",
                                  #6
                                  help="Use a non-regex breakpoint instead")
  #7
  return parser

讓我們一個參數(shù)一個參數(shù)的解釋一下:

  1. (編號為#1的這一行)你創(chuàng)建了OptionParser參數(shù), 然后給他傳了一個usage 參數(shù)和prog參數(shù). 如果你搞砸了或者給了parser一個它不知道怎么處理的參數(shù), usage就會被顯示.prog選項是用來定位程序名字的.因為它解決了奇怪的小問題, 這個小問題會讓你運行-h-help來獲取所有支持的自定義命令, 所以我總是合并它. 如果prog參數(shù)不在這里, -h命令就無法正常的工作. 這是生活中的一個小謎團(tuán).
  2. (編號為#2的)這一行, 給parser添加了-n-- non_regex參數(shù).
  3. (編號為#3的這一行)action參數(shù)告知了, 這段程序運行完之后執(zhí)行什么操作. "store_true"這個選項的應(yīng)用表明parser 會存儲Python Boolean True.
  4. (編號為#4的這一行)表明如果default沒有被明確的傳入值,那么他的默認(rèn)初始值將會是false.
  5. (編號為#5的這一行)dest參數(shù)用來確定OptionParser解析你的輸入時你給出的屬性的名字.舉個列子, 思考下面的代碼, 這段代碼在指令中解析了一個帶有選項和參數(shù)的python 字符串.
command_args = shlex.split(command)
(options, args) = parser.parse_args(command_args)
options.non_regex

正如你稍后會看到的, parse_args方法產(chǎn)生了一個python的tuple,這個tuple包含兩個list其中一個list包含的是options(叫做options), 另一個list包含的是arguments(叫做args).現(xiàn)在options 變量將會包含non_regex屬性.

  1. (編號為#6的這一行)help將會給你幫助文檔. 你可以用--help選項獲取所有的參數(shù)和他們的信息.例如, 當(dāng)這些被正確的設(shè)置到bar指令中之后, 你所要做的就是通過輸入bar -h就能查看包含所有選項以及選項的作用的列表.
  2. (編號為#7的這一行)在你創(chuàng)建了OptionParser, 并且添加了-n選項之后, 你需要返回OptionParser.
    你剛才創(chuàng)建了一個方法, 這個方法可以生成OptionParser實例, 這個實例你需要開始去解析那些參數(shù).現(xiàn)在是時候來用下剛才學(xué)的知識了.
    讓我們跳回breakAfterRegex函數(shù)的開始, 移除下面這兩行代碼:
target = debugger.GetSelectedTarget()
breakpoint = target.BreakpointCreateByRegex(command)

然后在它們的位置上添加下面的代碼:

'''Creates a regular expression breakpoint and adds it.
Once the breakpoint is hit, control will step out of the
current function and print the return value. Useful for
stopping on getter/accessor/initialization methods
'''
#1
command = command.replace('\\', '\\\\')
#2
command_args = shlex.split(command, posix=False)

#3
parser = generateOptionParser()

#4
try:
  #5
  (options, args) = parser.parse_args(command_args)
except:
  result.SetError(parser.usage)
  return

target = debugger.GetSelectedTarget()

#6
clean_command = shlex.split(args[0])[0]

#7
if options.non_regex:
  breakpoint = target.BreakpointCreateByName(clean_command)
else:
breakpoint = target.BreakpointCreateByRegex(
                      clean_command)

# The rest remains unchanged

確保你的縮進(jìn)是正確的.這些應(yīng)該縮進(jìn)兩個空格, 因為它們?nèi)慷际呛瘮?shù)的一部分.
下面是這些代碼所做的事情:

  1. 當(dāng)把你的輸入給OptionParser解析的時候, 它會把slashes作為逃避字符解釋. 例如, \' 會作為 '解釋, 這就意味著在你的命令中需要規(guī)避所有的反斜杠字符.
  2. 正如你在前面幾章中學(xué)到的內(nèi)容, 傳入到你的自定義lldb腳本中的指令參數(shù)是一個python字符串. 你會透過這個變量進(jìn)入shlex.split方法去獲取一個包含python strs的python list.此外, 這里就是那個幫助對付任何包含特殊字符串(比如:破折號)的輸入posix=False的作用. 否則, OptionParser會錯誤的假定那是一個被傳入的選項. 這很重要因為Objective-C在實例方法中有破折號, 因此你不會希望破折號被錯誤的解釋為一個選項.
  3. 使用剛才新創(chuàng)建的generateOptionParser 函數(shù), 你創(chuàng)建了一個解析器來處理命令行的輸入.
  4. 解析輸入可能很容易出錯. Python通常會拋出異常來處理錯誤. 如果optparse發(fā)生了錯誤并拋出了異常, 不要吃驚. 如果你沒有在你的腳本中捕獲異常, lldb就會停止工作, 進(jìn)程也會收到重創(chuàng)!因此, 在解析過程中包含一個try-except代碼塊去防止lldb在出現(xiàn)不合理的輸入的時候出現(xiàn)假死.
  5. OptionParser類有一個parse_args方法. 你要把command_args變量傳入到這個方法里, 并且接收一個元組作為返回值. 這個元組由兩個值組成: 一個值是options, options 由所有的選項參數(shù)組成(現(xiàn)在只有non_regex這一個選項). 另外一個值args由 parser解析出來的輸入值組成.
  6. 你將捕獲的第一個參數(shù)賦值給clean_command變量. 還記得第二行提到的posix=False嗎?那個邏輯將會持有你捕獲到的用圓括號的語法保護(hù)的參數(shù). 如果你沒有將posix設(shè)置為false, 你也可以用args[0]代替, 但是你會由于不能在正則表達(dá)式中使用反斜杠語法而喪失正則表達(dá)式的強大功能.
  7. 你已經(jīng)用掉了第一個選項!你正在檢查選項non_regex的真實性, 如果是true, 你將會執(zhí)行在SBTarget中的BreakpointCreateByName去產(chǎn)生一個非正則表達(dá)式斷點. 如果non_regex的值是false(你給generateOptionParser函數(shù)傳入的default的默認(rèn)值), 然后你的腳本就會使用正則表達(dá)式搜索. 再說一次, 你所需要做的就是在bar指令的輸入中添加-n來使non_regex為true.
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1、通過CocoaPods安裝項目名稱項目信息 AFNetworking網(wǎng)絡(luò)請求組件 FMDB本地數(shù)據(jù)庫組件 SD...
    陽明AI閱讀 16,186評論 3 119
  • Xcode 上的lldb LLDB 調(diào)試工具,gdb替代品;LLVM : Low Level Virtual Ma...
    helinyu閱讀 1,066評論 0 2
  • SwiftObject類: 彎路中的彎路 但是等一下, 你已經(jīng)大概看了一下SwiftObject類, 這個類是AS...
    股金雜談閱讀 388評論 0 0
  • 那年,我第一次離開家,開始了高中生活?,F(xiàn)自我介紹一下,別人都叫我蟲子。 2016,一部電影上映了,那是我還在上高一...
    和風(fēng)不言語閱讀 436評論 2 2
  • 【來滬半年記】 今天是10月第一天,早晨一睜眼就感覺這一天必定充滿意義。不知道從什么時候開始,我越來越喜歡給日子添...
    最真心的姑娘閱讀 294評論 0 0

友情鏈接更多精彩內(nèi)容