第二十章:有选项和参数的桥接脚本(三)

给断点的回调函数传参数

是时候创建Parser-c选项了, 也叫--condition!
回到BreakAfterRegex.py文件然后找到generateOptionParser.在return parser这行代码之前正确的添加下面的代码:

parser.add_option("-c", "--condition",
                  action="store",
                  default=None,
                  dest="condition",
                  help="Only stop if the expression matches True. Can
reference return value through 'obj'. Obj-C only.")

现在你应该知道这些代码是做什么的了, 但是下面会做一个快速的总结. 在这里你创建了一个--condition选项, 它的默认值是none, 并且期待着传进来一个参数, 在help文本里有一些有趣的东西.你表明了你可以通过名字叫obj的变量引用返回值. 这就意味着你在执行代码的时候会捕获返回值并把obj赋值给它.
是时候来用一下这个新选项了. 但是等一下...先思考片刻. 你如何把选项的参数传入SBBreakpoint的回调函数 里呢?
记住, 这些回调函数被称为私有的C++ API, 并且被限制在一个特定的方法签名里.考虑一下下面这个你设置断点的声明:

breakpoint.SetScriptCallbackFunction("BreakAfterRegex.breakpointHandler")

SBBreakpoint的回调触发时, 下面的这个函数就会被调用:

def breakpointHandler(frame, bp_loc, dict):
  # method contents here

你只有SBFrame, SBBreakpointLocation和Python内部的dict可以用来传递信息.这个函数怎样才能读到你的OptionParser 实例解析出来的参数并且把它传递给另一个函数呢?函数签名是被锁定的, 仅用来提供这些参数的.
想到几个解决这个问题的方法. 你可以在SBBreakpoint中搜索替代方法或者类似的类看一下时候有API可以让你传入额外的参数.或者, 你可以试一下继承一下SBBreakpoint类并添加一些方法来传入条件选项参数, 或者你可以创建一个全局变量来传入解析的选项.如果你真的感到绝望, 你可以试着在运行时用Python的exec函数动态的创建一个方法.
不幸的是, SBBreakpoint没有APIs来处理类和回调, 全局变量通常情况下是一个糟糕的想法并且你可能因为多个端点的回调引用了同一个全局变量这样复杂的逻辑而陷入到线程问题中.
继承一个子类是行不通的, 因为Python lldb类动态生成的背后用的是C++的代码, 当你每次尝试访问传入的SBBreakpoint值时都会得到一个新的实例.此外, 有99%的可能使用exec是一个非常非常糟糕的想法.
开发者应该做什么呢?
这就意味着你将不得不使用全局变量并处理全局变量的状态.考虑一下下面的情况, 你将选项赋值给全局变量并切创建了SBBreakpoint 1. 你对SBBreakpoint 2也做了同样的事情.然而, SBBreakpoint 1被触发了并切引用了全局变量的回调被执行了.由于SBBreakpoint 2已经被创建了, 这个选项已经被修改成了与期望的不一样的值.

幸运的是, 这里有一个比使用全局方案更好的替代方案, 而且你会找到解决选项的全局状态的解决方案.

用来替代全局变量的是, 你将会穿件一个Python类, 这个Python类用一个属性来持有被传入的这个选项.
现在去定位那个全局变量的状态: 取代用属性来持有选项的是, 你将用python的dict来持有选项. 断点的一个好处是不管你删除或创建多少次, 每运行一个session每一个断点都有一个唯一的ID.这就意味着你可以使用断点的ID来作为唯一的key来为断点引用一个特定的选项集合.然后你可以将断点的ID作为key, 而把断点的选项作为value.太酷了, 对吧?
回到BreakAfterRegex.py文件的顶部, 在import语句的下面正确的添加下面的代码:

#1
class BarOptions(object):
  #2
  optdict = {}
  #3
  @staticmethod
  def addOptions(options, breakpoint):
    key = str(breakpoint.GetID())
    BarOptions.optdict[key] = options

我们一步一步的过一遍:

  1. 你声明了一个继承自object的类BarOptions.把object想象成Python中的NSObject就行了.这个类提供了一些基本的函数. 不继承自某个类在Python是绝对可行的(就像swift一样), 但是Python的某些API让继承自Object变的更友好.
  2. 你声明了一个叫做optdict的变量.如果你之前声明过实例变量, 他可能在某个init函数里.鉴于你只会用到这个类变量, 你不必为这个类设置任何初始化函数.
  3. 你同样声明了一个叫做addOptions的函数, 它是唯一将选项与断点的ID绑定的函数.

跳到breakAfterRegex函数, 然后把下面的代码正确的添加到你指定回调函数的代码(SetScriptCallbackFunction函数)之前.

BarOptions.addOptions(options, breakpoint)

在你添加了这行新的代码之后, 创建一个新的函数来判断这个条件. 在BreakAfterRegex.py的底部添加一个新的函数evaluateCondition.

def evaluateCondition(debugger, condition):
  '''Returns True or False based upon the supplied condition.
  You can reference the NSObject through "obj"'''
  #1
  res = lldb.SBCommandReturnObject()
  interpreter = debugger.GetCommandInterpreter()
  target = debugger.GetSelectedTarget()
  #2
  expression = 'expression -lobjc -O -- id obj = ((id){}); ((BOOL)
{})'.format(getRegisterString(target), condition)
  interpreter.HandleCommand(expression, res)
  #3
  if res.GetError():
    print(condition)
    print('*' * 80 + '\n' + res.GetError() + '\ncondition:' + condition)
    return False
  elif res.HasResult(): 
    #4
    retval = res.GetOutput()
    #5
    if 'YES' in retval:
      return True
  #6 
  return False 

下面来解析一下:

  1. 你创建了一个SBCommandReturnObject来处理从condition参数传进来的代码.
  2. 这会创建并执行传进来的自定义表达式.注意你声明了实例变量obj并指定它从返回值里取到的类型为id. 这可以让你将返回值作为obj方便的引用. 你提供的表达式将会被指定为Objective-C 的BOOL类型, 这将会输出一个YES或者NO的值.
  3. 你将会判断返回的值, 而且如果这里包含了一个错误, 就将错误打印出来.在这个函数里你明确的返回了一个 False 或者 True, 因为你要用这个返回值决定在执行表达式的时候是停止还是继续.记住, 如果这个函数返回True, SBBreakpoint的回调函数breakpointHandler将会停止执行.如果返回的不是ture执行就会继续.
  4. 如果这里有输出的话将赋值给一个叫做retval的变量.
  5. 用这种方法教表达式解析真的让我很痛苦, 因为在下一章你会学到一个更清晰易懂的判断对象的类型SBValues. 但是现在, 你将继续使用SBCommandReturnObject并切比较你期望的输出.如果表达式的判断结果是YES, 然后停止执行.
  6. 如果执行的结果返回的是NO, 然后就继续执行并返回False.

最后一轮代码! 找到breakpointHandler函数, 在调用thread.StepOut()的下方添加下面的代码:

#1
key = str(bp_loc.GetBreakpoint().GetID())
#2
options = BarOptions.optdict[key]
#3
if options.condition: #4
  condition = shlex.split(options.condition)[0]
#5
  return evaluateCondition(debugger, condition)

我们再来解释一下:

  1. bp_locSBBreakpointLocation的一种类型. 这个类让你通过GetBreakpoint 引用初始化SBBreakpoint.从这里开始, 你就可以使用ID了, ID是一个数字. 因此, 你需要将这个数字转化为一个Python字符串并赋值给key.
  2. 这将会从类的optdict属性中取出选项然后赋值给变量options.
  3. 检查options是否是一个non-None引用.如果这里是一个有效的引用, 执行后面的逻辑.
  4. 这将会解析传到命令行中的选项condition. 又一次, 我们不得不做一些额外的工作感谢前面提到的posix=False, 因为它允许我们在选项和参数中使用反斜线和破折号.
  5. 最后, 你调用了你在前面创建的函数evaluateCondition. 你返回了函数的返回值, 这个返回值决定了代码是否继续执行.
    没有更多的python代码了!保存你的编辑然后回到Xcode中.
    再一次, 修改dlopen符号断点中的第二个action. 这一次, 改为下面的指令:
bar NSURL\(.*init

这一次断点会在NSURLs的初始化方法里触发.这些古怪的语法是必要因为NSURL的大多数初始化方法是这种类型的.
查看控制台中与HTTPS NSURL相关的输出:

屏幕快照 2018-10-10 下午7.00.43.png

看起来这个app有一些Amazon S3 的服务.使用你在bar指令中最新创建的condition选项去暂停执行当NSURL从初始化函数里面返回并且在absoluteString中包含amazon的时候.
回到dlopen符号断点中, 并且修改第二个action为下面的指令:

bar NSURL\(.*init -c '[[obj absoluteString] containsString:@"amazon"]'
屏幕快照 2018-10-10 下午7.06.21.png

构建并运行然后看会发生什么...
执行将会暂停在包含NURL...URL...的具体的某一行, 尽管他暂停在饿了swift环境中, 但是让我们正视现实, 那是一个NSURL实例.

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,306评论 4 370
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,657评论 2 307
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,928评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,688评论 0 220
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,105评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,024评论 1 225
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,159评论 2 318
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,937评论 0 212
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,689评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,851评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,325评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,651评论 3 263
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,364评论 3 244
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,192评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,985评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,154评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,955评论 2 279

推荐阅读更多精彩内容