Advanced+Apple+Debugging(9)

在前面的章节中, 你已经学习了为命令创建别名并将它们保存在lldbinit文件中.不幸的是, 命令别名有一些局限性.
用这种方法创建的别名如果你用来执行静态命令会表现的很好, 但是有时候你想在命令中输入一些内容以便得到一些有用的输出.
用命令别名的本质是用实际的命令替换了别名. 如果你想在命令的中间输入一些东西, 比如一条获取对象实例的类的命令, 提供需要输入的对象?
一种极其拙劣的解决方案就是用命令别名做下面的事情(请永远不要这样做):

(lldb) po id $INPUT = @"input test";
(lldb) command alias getcls po -l objc -O -- [$INPUT class]
这在LLDB中创建了一个叫做$INPUT的临时变量然后使用$INPUT获取类.但是这种方式很拙劣. 你不得不每次都要重新声明一个$INPUT, 这完全违背了使用快捷命令的初衷.
然而, 不要绝望 - 这里有一种优雅的解决方案可以支持输入.

命令正则

在LLDB中command regex命令实际上很像命令别名, 除非你可以为输入的将会作为命令的一部分执行的内容提供一个正则表达式.
command regex带来了一种输入语法类似于下面的样子:

s/<regex>/<subst>/
这是一个普通的正则表达式. 它是以s/开头的, 这指定了一个流编辑器将输入的内容作为替换命令.<regex>是将要被替代的部分, <subst>部分是用来替代的部分.

注意:这种语法衍生自sed终端命令. 知道这点是很重要的, 因为如果你尝试使用高级模式, 你可以查看sed的主页来看看在替换格式语法中可能是哪些内容.
到了看一个具体例子的时间了. 打开Signals的Xcode项目. 构建并运行, 然后将应用程序暂停在调试器中. 一旦LLDB的控制台出现并准备接受输入, 在LLDB中输入下面的内容:

(lldb) command regex rlook 's/(.+)/image lookup -rn %1/'
你输入的这条命令会使你的 image正则搜索更简单. 你已经创建了一个叫做rlook的新命令. 这条新命令会获取rlook后面的所有东西并用image lookup -rn作为它的前缀. 这条命令做了这样一件事情, 用image lookup -rn %1替换可以匹配一个或多个字符的正则表达式(圆括号) 内的所有内容. %1表示这匹配到的内容.
所以, 例如, 你输入了这样的内容:

rlook FOO
LLDB实际执行的的是下面的内容:

image lookup -rn FOO
现在你就可以通过仅仅输入rlook 来替代输入长长的image lookup -rn命令.
但是等一下, 现在变得好一点了!假如与rl字符没有冲突, 你也可以用rl字符替代. 你可以指定任何命令, 可以是内置的或者你自己的, 只要是与其他命令没有冲突的前缀.
这就意味着你可以使用更简洁的输入来搜索viewDidLoad方法. 现在来试一下:

(lldb) rl viewDidLoad
这回提取出当前可执行文件中所有模块中的viewDidLoad的实现. 尝试将搜索范围限定在viewDidLoadAPP中:

(lldb) rl viewDidLoad Signals
现在你已经确信了这条命令, 讲下面这行代码添加到~/.lldbinit文件中:

command regex rlook 's/(.+)/image lookup -rn %1/'
注意:实现一个正则命令的最佳方式是在程序运行中用LLDB实现. 这可以让你在命令正则中重复声明(在你对它不满意的时候重新声明)并且可以在不重启LLDB的情况下测试它.如果你对命令很满意, 那么将它添加到~/.lldbinit文件中以便LLDB每次启动的时候都可以使用.
从现在开始rlook就变得可用了, 你再也不用输入令人头疼的image lookup -rn的完整命令了.太棒了!

执行复杂的逻辑

是时候把命令的正则表达式提升一个等级了.实际上你可以用一条命令的别名来执行多条指令. 现在LLDB应该依然在暂停状态, 输入下面这条新的命令:

(lldb) command regex -- tv 's/(.+)/expression -l objc -O -- @import
QuartzCore; [%1 setHidden:!(BOOL)[%1 isHidden]]; (void)[CATransaction
flush];/'
这是一条复杂而有用的命令, 将会创建一个名字叫tv (toggle view)的命令, 可用与当调试器暂停的时候在UIView (或者 NSView)之间切换.
这条命令由三行代码组成:

@import QuartzCore, 在调试器的地址空间中导入QuartzCore框架. 这是必要的因为只有在你声明了代码之后调试器才理解你在执行什么. 你需要执行QuartzCore框架中的代码, 之前还没有导入, 但是现在已经导入了.
[%1 setHidden:!(BOOL)[%1 isHidden]];, 在视图的可见于不可见之间切换, 这取决于之前的状态是可见的还是不可见的. 注意isHidden不知道返回值的类型, 因此你需要将它转换为Objective-C中的Bool值.
最后一行代码是, [CATransactionflush], 它会刷新CATransaction队列. 调试器中控制UI也就是通常所说的屏幕在调试器继续执行之前不会反映出任何改变. 然而, 这个方法将会更新LLDB中执行的结果而不需要continue就能展示视觉上的改变.
注意:由于输入参数的限制, 多行输入是不允许的, 所以你不得不将所有的指令都放在一行代码中. 在手工控制正则命令的时候这很难看但却很有必要.然而, 如果你曾经在Objective-C/Swift的源代码中做过这些, 那么苹果上帝可能用很多次的审核严厉的惩罚过你!
如果LLDB依然在暂停状态, 执行新创建的tv命令:

(lldb) tv [[[UIApp keyWindow] rootViewController] view]
page100image13312.png

查看模拟器里确认一下视图已经消失了.
现在只需要简单的在LLDB中按下Enter, LLDB就会重复你之前执行的最后一条指令.视图会刷新到之前的状态.
现在你已经实现了tv的命令, 将它添加到~/.lldbinit文件中.
command regex -- tv 's/(.+)/expression -l objc -O -- @import QuartzCore;
[%1 setHidden:!(BOOL)[%1 isHidden]]; (void)[CATransaction flush];/'
链接正则输入

这条命令之所以选择使用怪诞的流编辑器输入是有原因的:这种格式可以让你轻松的在一条命令中指定多个动作. 当给出多个指令的时候, 这个正则表达式会试着匹配每一个输入.如果输入匹配到了, 匹配到的内容就会应用到指令的<subst>部分.如果输入没有匹配到指定的流, 他就会进入下一条命令并且看看正则表达式是否能匹配到那些输入.
当我们在处理内存中或者寄存器中的对象的时候使用Objective-C环境是普遍必要的. 因为, 以方括号[开始的任何语句或者@字符都是Objective-C里面的.这是因为Swift在处理内存数据的时候很难, 并且它不允许你访问寄存器, 同时在处理swift表达式的时候也不是以[或者@开始的.
你可以使用这些信息自动检测给定的输入需要什么样的环境.
让我们看一下你应该怎么去构建一个获取对象类信息的一条指令,:
• 在 Objective-C中, 你应该使用[objcObject class].
• 在 Swift 中, 你应该输入 (of: swiftObject).
在Xcode中, 在MasterViewController.viewDidLoad () - > ()处创建一个断点(不要忘记中间的空格).

page101image17376.png

构建并运行, 等待断点被触发.像往常一样, 转到调试器里.
首先, 构建出新命令getcls的Objective-C的实现.
(lldb) command regex getcls 's/(([0-9]|\$|\@|[).)/cpo [%1 class]/'
哇, 这条正则表达式让我们的眼睛都花了.我们来拆解一下:
首先, 这里有一个内部分组说在起始部分可以匹配接下来的字符:
• [0-9]的意思是可以是0-9的数字.
• \$的意思是$的字面量将会被匹配
• \@的意思是@的字面量将会被匹配
• [的意思是[的字面量将会被匹配
以上面的内容为开始的字符都会生成一个匹配. 紧跟着的.
的意思是为一个或多个字符产生一个匹配.
整体看来,这句话的意思是一个数字或者$符号或者 @符号或者[符号, 后面跟着任何字符的内容都会作为命令匹配到的结果并且运行cpo [%1 class].再说一次, %1会被正则表达式中第一个匹配到的内容替换. 在这里, 就是整条命令. 而内部匹配器(匹配一个数字, $, 等等)将会作为%2.
现在用一组命令来实验一下getcls命令看看它是如何工作的:

(lldb) getcls @"hello world"
NSCFString
(lldb) getcls @[@"hello world"]
NSSingleObjectArrayI
(lldb) getcls [UIDevice currentDevice]
UIDevice
(lldb) cpo [UIDevice currentDevice]
<UIDevice: 0x60800002b520>
(lldb) getcls 0x60800002b520
UIDevice
很神奇吧!
然而, 这只能处理Objective-C环境中用命令匹配到的引用.例如, 试一下下面的内容:

(lldb) getcls self
你会得到一个错误:

error: getcls
这是因为正则表达式没有匹配到你输入的内容. 让我们在getcls中添加一个匹配其他形式的输入. 现在在LLDB中输入下面的内容:

(lldb) command regex getcls 's/(([0-9]|\$|\@|[).*)/cpo [%1 class]/' 's/
(.+)/expression -l swift -O -- type(of: %1)/'
这看起来更复杂了, 但还不算太糟. 这条命令的第一部分与你在前面添加的一致. 但是现在你在末尾添加了另外一个正则表达式. 这一个正则表达式可以捕获所有输入, 就像你添加的rlook命令. 这里捕获到的所有内容的指令很简单叫做type(of:)用输入的内容作为参数.
用self再次执行这条命令:

(lldb) getcls self
你会得到期望中的Signals.MasterViewController输出. 鉴于, 你是在Swift环境下匹配的内容, 你可以用另外一种有趣的方法使用这条命令.

(lldb) getcls self .title
注意中间有一个空格, 而且这条命令依然起作用. 这是因为你告诉swift环境获取除了换行符以外的所有字符.
一旦, 你完成了练习并改进了getcls命令, 别忘了把它加到~/.lldbinit文件中.

命令正则的局限性

LLDB中的命令正则在创造性应用的数量上有些限制. LLDB的命令正则有一个你只能在<subst>上应用一个参数的限制. 这就意味着你那些支持多个参数的高级命令也逃不出这个限制.
幸运的是, LLDB有script bridging接口 - 一个完全由Python实现的在调试过程中用来实现高级LLDB指令的功能.在后面的章节中你会深入的学习script bridging.
但是现在, 简单的使用command alias或者command regex来满足你调试的需要.

我们为什么要学习这些?

回到本章中你已经创建并添加了语法和帮助文档的正则表达式命令.
正如前面提到的, 你将会感谢你自己提供了这些帮助文档来提醒你之前创建的命令的功能.

转载于:https://blog.51cto.com/haidragon/2126791

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Advanced Apple Debugging & Reverse Engineering, Second Edition ISBN: Learn the powerful secrets of Apple's software debugger, LLDB, that can get more information out of any program than you ever thought possible. In Advanced Apple Debugging and Reverse Engineering, you'll come to realize debugging is an enjoyable process to help you better understand software. Not only will you learn to find bugs faster, but you'll also learn how other developers have solved problems similar to yours. You'll also learn how to create custom, powerful debugging scripts that will help you quickly find the secrets behind any bit of code that piques your interest. This book is for intermediate to advanced iOS/macOS developers who are already familiar with either Swift or Objective-C and want to take their debugging skills to the next level. Topics Covered in Advanced Apple Debugging & Reverse Engineering: LLDB Max Achievement: Master LLDB and learn about its extensive list of subcommands and options. 1's and 0's: Learn the low-level components available to help extract useful information from a program, from assembly calling conventions to exploring the process of dynamically-loaded frameworks. The Power of Python: Use LLDB's Python module to create powerful custom debugging commands to introspect and augment existing programs. Nothing is Secret: Learn how to use DTrace, a dynamic tracing framework, and how to write D scripts to query anything you were ever curious about on your macOS machine. Case Studies: Quickly find and solve the real-world issues that iOS and macOS developers typically face in their day-to-day development workflow. After reading this book, you'll have the tools and knowledge to answer even the most obscure question about your code - or someone else's.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值