LLDB命令库HMLLDB介绍

2,146 阅读37分钟

LLDB命令库HMLLDB介绍

中文版介绍的更新有延迟,建议优先查看GitHub里的README

2024年1月更新内容

新增:autodsym,trace-step-over-instruction,adrp,cbt,edisassemble,reference。 更新:rr,fclass,fsubclass,fmethod,ivarsinfo,methods,bpframe。

2023年3月更新内容

新增命令:ivarsinfo、bpframe、bpmessage、bpmethod、rc、rr、tracefunction、traceinstruction、pfont。

前言

和大多数人一样,我首次接触到的LLDB命令是其自带的po命令,用于打印对象。后来我学习了更多的命令才发觉LLDB的强大,尤其是可用于动态修改的expression命令,我至今依然在大量使用,如自动填充账号密码,log控制,mock控制,自动打印UIViewController的生命周期等等。
不限于此,苹果还提供了Scripting Bridge API,简称SB API,可用Python脚本控制LLDB,其中Facebook的chisel项目和Derek Selander的LLDB项目是比较知名LLDB命令库,其中大部分功能都是基于SB API。不过这两个项目还存在很多问题,对我影响较大的就是更新不及时和命令互斥,另外我自己也有一些想法,于是也开发了一个LLDB命令库,即HMLLDB,首要目的是提升开发和调试效率。

特色

  • 无侵入,绿色版,项目无需修改,git看不到任何记录
  • 所有命令均支持真机,大部分命令支持模拟器
  • 所有命令均支持Objective-C和Swift项目
  • 一些命令提供了应用内的交互式的UI

系统要求

  • Xcode 15.1
  • 64位模拟器或真机,iOS 13.0 +
  • 部分命令需要Debug模式(或者Optimization Level设置为[-O0]/[-Onone])

安装

项目源码:github.com/chenhuimao/…

  1. 下载源码,强烈建议clone仓库,方便pull代码保持最新的版本。一些命令很可能要随着Xcode版本的更新而调整,我都会及时适配。
  2. 打开(或创建)~/.lldbinit文件(".lldbinit"是完整的文件名,没有多余后缀,放在当前的用户目录下,即~),在末尾新增这一行命令:
command script import <path>

其中<path>是项目里HMLLDB.py这个文件的绝对路径,比如我电脑这一行的是:
command script import /Users/pal/Desktop/gitProjects/HMLLDB/commands/HMLLDB.py

  1. 重启Xcode,运行你自己的iOS项目,点击Pause program execution进入LLDB调试模式,输入命令help,如果看到有下文介绍的命令,表明安装成功。

命令介绍

主要命令的简要说明:

CommandDescription
autodsym给当前模块添加调试符号文件
deletefile删除沙盒里指定的文件
pbundlepath打印主bundle的路径
phomedirectory打印沙盒的路径,即"~"
fclass查找并打印所有包含指定字符串的类名
fsubclass查找并打印一个类的所有子类
fsuperclass查找并打印一个类的父类
fmethod在所有方法列表中查找并打印指定方法,也可以打印指定类的方法列表
methods相当于调用[input _methodDescription],打印类的所有方法
properties相当于调用[input _propertyDescription],打印类的所有property
ivars相当于调用[input _ivarDescription],打印类实例的ivar
ivarsinfo打印指定类的ivars的信息
bpframe设置一个断点,只有匹配指定堆栈/地址时才暂停
bpmessage给指定类的指定方法设置一个断点,即使这个方法是其父类的实现也有效
bpmethod设置一个断点,当前线程执行到下一个OC方法的实现时暂停(必须是通过objc_msgSend调用的方法)
cbt基于fp/lr寄存器,完整展示当前线程调用栈
rc打印通用寄存器的改变(和上一次执行此方法时对比)
rrlldb自带register read命令的别名,新增选项打印栈寄存器sp指向地址的值
reference扫描machO获取指定地址所有的引用信息
adrp快速获取adrp指令的计算结果
edisassembledisassemble命令增强版本
tracefunction基于指令,一步步追踪函数跳转,直到命中下个断点停止
traceinstruction追踪指令,直到命中下个断点停止
trace-step-over-instruction以step-over的形式追踪指令
pfont打印设备支持的所有字体名称
plifecycle用于打印UIViewController的生命周期
redirectstdout/stderr重定向
push找到一个UINavigationController,然后push一个指定的UIViewController
showhud在keyWindow上展示一个debug视图,显示内存占用,CPU使用率,主线程FPS
showfps已被showhud命令替代,仅供学习参考
sandboxpresent一个沙盒浏览器,具有系统分享、删除文件功能
inspect查看当前页面的UIView对象。还能看到常见的类如UILabel的关键属性值
request自动打印http/https请求
environment用于诊断当前的环境
...

和系统LLDB命令一样,表中所有命令均支持通过help <command>查看语法和用例,例如help fmethod输出如下:

(lldb) help fmethod
     Find the method.  Expects 'raw' input (see 'help raw-input'.)

Syntax: fmethod

    Syntax:
        fmethod <methodName>  (Case insensitive.)
        fmethod [--class] <className>

    Options:
        --class/-c; Find all method in the class

    Examples:
        (lldb) fmethod viewdid
        (lldb) fmethod viewDidLayoutSubviews
        (lldb) fmethod -c UITableViewController

    This command is implemented in HMClassInfoCommands.py

例子

接下来我将介绍命令具体用法。其中有演示效果的,来源于开源项目Kingfisher里的demo,没有改动一行代码。这些例子用于文章介绍,实际使用的时候,用help <command>命令查找用法更方便。
建议点击Pause program execution主动进入LLDB调试模式执行下面的命令,而不是用命中断点的方式执行命令(部分命令除外)。

autodsym

lldb命令target symbols add [<symfile>]需要指定符号文件的地址。我经常需要重签调试各个发布版本,手动输入符号表太麻烦了,所以写了autodsym命令,执行此命令就可以省略符号文件路径的输入。建议新增一个启动符号的断点(如UIApplicationMain)用于自动执行此命令。

# 以下两个命令效果相同。注意Xcode需要有访问路径的权限,autodsym并不提醒授权
(lldb) autodsym
(lldb) target symbol add /path/to/dSYM

deletefile

开发中常常需要删除沙盒中的某些文件以重置状态,deletefile命令可以快速删除指定路径的文件夹。有些数据还留在内存中,所以建议删除后立即重新运行项目。这是我最经常调用的命令之一。

# 删除沙盒里所有的文件
(lldb) deletefile -a

# 删除~/Documents文件夹
(lldb) deletefile -d

# 删除~/Library文件夹
(lldb) deletefile -l

# 删除~/tmp文件夹
(lldb) deletefile -t

# 删除~/Library/Caches文件夹
(lldb) deletefile -c

# 删除~Library/Preferences文件夹
(lldb) deletefile -p

# 删除沙盒内指定路径的文件
(lldb) deletefile -f path/to/fileOrDirectory

pbundlepath & phomedirectory

# 打印APP的主bundle的路径
(lldb) pbundlepath
[HMLLDB] /Users/pal/Library/Developer/CoreSimulator/Devices/D90D74C6-DBDF-4976-8BEF-E7BA549F8A89/data/Containers/Bundle/Application/84AE808C-6703-488D-86A2-C90004434D3A/Kingfisher-Demo.app

# 打印APP沙盒的路径
(lldb) phomedirectory
[HMLLDB] /Users/pal/Library/Developer/CoreSimulator/Devices/D90D74C6-DBDF-4976-8BEF-E7BA549F8A89/data/Containers/Data/Application/3F3DF0CD-7B57-4E69-9F15-EB4CCA7C4DD8

# 如果是在模拟器上运行,可以加上-o选项用Finder打开
(lldb) pbundlepath -o
(lldb) phomedirectory -o

fclass & fsubclass & fsuperclass & fmethod

这几个命令针对Swift做了优化,输入Swift类可以省略命名空间。
虽然Xcode自带有相关功能,但有时反应慢,而且Xcode不支持查看二进制库的类。

fclass查找并打印所有包含指定字符串的类名,不区分大小写。

语法:
fclass <class_name> [-p <protocol>]

(lldb) fclass NormalLoadingViewController
[HMLLDB] Waiting...
[HMLLDB] Count: 1 
Kingfisher_Demo.NormalLoadingViewController (0x102148fa8, Kingfisher-Demo)

# 不区分大小写,包含匹配
(lldb) fclass image
[HMLLDB] Waiting...
[HMLLDB] Count: 672
Kingfisher.ImageLoadingProgressSideEffect (0x10124ae18, Kingfisher)
Kingfisher.GIFAnimatedImage (0x10124a3e0, Kingfisher)
...
Kingfisher_Demo.DetailImageViewController (0x1009068e8, Kingfisher-Demo)
Kingfisher_Demo.AVAssetImageGeneratorViewController (0x100904568, Kingfisher-Demo)
...
UIImagePickerController (0x1ea918240, UIKitCore)
_UIStackedImageContainerView (0x1ea90fac8, UIKitCore)
...

# 选项 -p:找出遵守协议的类
# 例子:找出遵守UICollectionViewDelegate协议的类,且名称包含controller
(lldb) fclass controller -p UICollectionViewDelegate
[HMLLDB] Waiting...
[HMLLDB] Count: 16
UIActivityContentViewController (0x224739570, ShareSheet)
UIPrintPreviewViewController (0x225b5f408, PrintKitUI)
UIDebuggingSpecViewController (0x2252cfeb8, UIKitCore)
UIDebuggingInformationHierarchyViewController (0x2252cfc60, UIKitCore)
UICollectionViewController (0x2245d4e50, UIKitCore)
SUIKSearchResultsCollectionViewController (0x2256e82b8, Preferences)
Kingfisher_Demo.InfinityCollectionViewController (0x10079ac18, Kingfisher-Demo)
Kingfisher_Demo.HighResolutionCollectionViewController (0x100799ed0, Kingfisher-Demo)
Kingfisher_Demo.OrientationImagesViewController (0x100799cc8, Kingfisher-Demo)
Kingfisher_Demo.ImageDataProviderCollectionViewController (0x100799bb0, Kingfisher-Demo)
Kingfisher_Demo.IndicatorCollectionViewController (0x100799328, Kingfisher-Demo)
Kingfisher_Demo.ProcessorCollectionViewController (0x100799178, Kingfisher-Demo)
Kingfisher_Demo.NormalLoadingViewController (0x1007989b8, Kingfisher-Demo)
_UISearchSuggestionsListViewController (0x2252dcb40, UIKitCore)
_UIAlertControllerTextFieldViewController (0x2245d13b8, UIKitCore)
UIActivityGroupViewController (0x224739520, ShareSheet)

# 例子:找出所有遵守UICollectionViewDelegate协议的类
(lldb) fclass -p UICollectionViewDelegate
[HMLLDB] Waiting...
[HMLLDB] Count: 30
...

fsubclass查找并打印一个类的所有子类。

(lldb) fsubclass UICollectionViewController
[HMLLDB] Waiting...
[HMLLDB] Subclass count: 10 
Kingfisher_Demo.InfinityCollectionViewController (0x102cf2c18, Kingfisher_Demo)
Kingfisher_Demo.HighResolutionCollectionViewController (0x102cf1ed0, Kingfisher_Demo)
...

fsuperclass查找并打印一个类的父类。

(lldb) fsuperclass UIButton
[HMLLDB] UIButton : UIControl : UIView : UIResponder : NSObject

(lldb) fsuperclass KingfisherManager
[HMLLDB] Kingfisher.KingfisherManager : Swift._SwiftObject

fmethod在方法列表中查找并打印方法,常用来查找一个方法在哪些类中被实现。在Swift项目中用处较小。

# 全局查找含clear的方法,不区分大小写
(lldb) fmethod clear
[HMLLDB] Waiting...
[HMLLDB] Methods count: 3725 
(-) clearMemoryCache (0x10340c174, Kingfisher)
	Type encoding:v16@0:8
	Class:Kingfisher.ImageCache
(-) _clearAllSpecifiers (0x1c7f1959c, Preferences)
	Type encoding:v16@0:8
	Class:PSSpecifierDataSource
(-) _clearCells (0x2160258c0, ScreenReaderOutput)
	Type encoding:v16@0:8
	Class:SCRO2DBrailleCanvas
...

# 选项-c:打印指定类的方法列表,区分大小写
(lldb) fmethod -c ImageCache
[HMLLDB] Waiting...
[HMLLDB] Class: Kingfisher.ImageCache (0x10348de18, Kingfisher)
Instance methods count: 3. Class method count: 0.
(-) backgroundCleanExpiredDiskCache (0x10340dd6c, Kingfisher)
	Type encoding:v16@0:8
(-) cleanExpiredDiskCache (0x10340c84c, Kingfisher)
	Type encoding:v16@0:8
(-) clearMemoryCache (0x10340c174, Kingfisher)
	Type encoding:v16@0:8

methods & properties & ivars

相当于调用NSObject的私有方法_methodDescription_propertyDescription_ivarDescription。上文提到的其他LLDB命令库也有这些命令,但HMLLDB针对Swift类做了优化,省去了输入命名空间,并优化了报错信息。

# 语法:
methods [--short] <className/classInstance>
properties <className/classInstance>
ivars <Instance>

(lldb) methods NormalLoadingViewController
[HMLLDB] <Kingfisher_Demo.NormalLoadingViewController: 0x10d55ffa8>:
in Kingfisher_Demo.NormalLoadingViewController:
	Instance Methods:
		- (id) collectionView:(id)arg1 cellForItemAtIndexPath:(id)arg2; (0x10d523f30, Kingfisher-Demo)
		- (long) collectionView:(id)arg1 numberOfItemsInSection:(long)arg2; (0x10d522a20, Kingfisher-Demo)
		- (void) collectionView:(id)arg1 willDisplayCell:(id)arg2 forItemAtIndexPath:(id)arg3; (0x10d523af0, Kingfisher-Demo)
		- (void) collectionView:(id)arg1 didEndDisplayingCell:(id)arg2 forItemAtIndexPath:(id)arg3; (0x10d522cb0, Kingfisher-Demo)
		- (id) initWithCoder:(id)arg1; (0x10d522960, Kingfisher-Demo)
...

# 这3个命令只能用于NSObject的子类
(lldb) methods KingfisherManager
[HMLLDB] KingfisherManager is not a subclass of NSObject

ivarsinfo

打印指定类的ivars的信息,我一般用于查看offset。

# 语法:
ivarsinfo <className>
(lldb) ivarsinfo UIView
[HMLLDB] UIView (0x22658d3b8, UIKitCore)
_constraintsExceptingSubviewAutoresizingConstraints
	typeEncoding:@"NSMutableArray"
	offset:16 hex:0x10
_cachedTraitCollection
	typeEncoding:@"UITraitCollection"
	offset:24 hex:0x18
_animationInfo
	typeEncoding:@"UIViewAnimationInfo"
	offset:32 hex:0x20
...

bpframe

设置一个断点,只有匹配指定堆栈/地址时才暂停。
注意:即使命中断点后没有暂停,耗时也是非常高的。建议不要设置高频符号或地址。

# 语法:
bpframe [--one-shot] <symbol or address> <stack keyword 1> <stack keyword 2> ... <stack keyword n>

# 当命中"setupChildViewControllers:"符号,且堆栈包含"otherFunction"关键词时才暂停
(lldb) bpframe setupChildViewControllers: otherFunction

# 当命中"setupChildViewControllers:"符号,且堆栈包含"function_1""function_2"关键词时才暂停
(lldb) bpframe setupChildViewControllers: function_1 function_2

# 当命中"0x1025df6c0"地址,且堆栈包含"0x19261c1c0""0x19261bec0"地址时才暂停
(lldb) bpframe 0x1025df6c0 0x19261c1c0 0x19261bec0

# 当命中"0x1025df6c0"地址,且堆栈包含"otherFunction"关键词和"0x19261bec0"地址时才暂停
(lldb) bpframe 0x1025df6c0 otherFunction 0x19261bec0

# --one-shot/-o; 当触发第一次暂停后,自动删除断点
(lldb) bpframe -o setupChildViewControllers: otherFunction
(lldb) bpframe -o 0x1025df6c0 otherFunction

bpmessage

给指定类的指定方法设置一个断点,即使这个方法是其父类的实现也有效。

语法:
    bpmessage -[<class_name> <selector>]
    bpmessage +[<class_name> <selector>]
    
例子:
    (lldb) bpmessage -[MyModel release]
    (lldb) bpmessage -[MyModel dealloc]

chisel里的bmessage也有相同的功能,它是通过条件断点实现的。也就是说兄弟类或父类对象也会命中断点,只是没有暂停程序,这会非常耗时。
HMLLDBbpmessage是通过runtime实现的,当指定类的方法是其父类实现时,会给指定类添加一个方法和实现,这个实现里直接再调用父类的实现,所以非常高效。上述的例子,给指定类的release或dealloc方法添加断点,就是最佳实践。

bpmethod

设置一个断点,当前线程执行到下一个OC方法的实现时暂停(必须是通过objc_msgSend调用的方法)。
当调试汇编代码时,遇到objc_msgSend会非常麻烦,常常需要查看对应方法的实现,而想要跳到对应方法的实现,操作有点繁琐,这个命令解决了这个痛点。

# 我想进入(step into)到对应方法的实现,而不是进入(step into)objc_msgSend的实现
0x10075a574 <+64>: bl     0x10075a95c       ; symbol stub for: objc_msgSend
# 解决方案
(lldb) bpmethod
[HMLLDB] Target thread index:1, thread id:1692289.
[HMLLDB] Done! You can continue program execution.
# --continue/-c; 当执行此命令后,程序自动继续执行
Continue program execution after executing bpmethod
(lldb) bpmethod -c

cbt

cbt命令:基于fp/lr寄存器,完整展示当前线程调用栈。
Xcode的"Debug Navigator"和bt命令:基于libunwind.dylib库,展示线程的调用栈。
某些情况下,基于libunwind.dylib库回溯可能会丢失调用帧,而arm64架构应用实际执行过程是依据fp/lr寄存器的,基于这个原因,开发了cbt命令。
注意:此命令只支持arm64架构设备。

rc

打印通用寄存器的改变(和上一次执行此方法时对比)。

# 首次执行
(lldb) rc
[HMLLDB] Get registers for the first time.

# 执行step over汇编指令后,再次执行"rc"命令
(lldb) rc
0x10431a3cc <+16>:  mov    x1, x2
        x1:0x000000010431aa94 -> 0x000000010490be50
        pc:0x000000010431a3cc -> 0x000000010431a3d0  Demo`-[ViewController clickBtn:] + 20 at ViewController.m:24

rr

lldb自带register read命令的别名,新增选项打印栈寄存器sp指向地址的值。

# 系统命令"register read"的别名
(lldb) rr

# 相当于"register read -a"
(lldb) rr -a

# 打印寄存器的同时,同时打印[sp, (sp + offset)]地址的值
(lldb) rr -s 64
General Purpose Registers:
        x0 = 0x000000016dc24e48
        x1 = 0x0000000000000000
        x2 = 0x0000000129d0fd70
        x3 = 0x0000000281a30000
        x4 = 0x0000000281a30000
        x5 = 0x0000000281a30000
        x6 = 0x0000000000000000
        x7 = 0x000000016dc24aae
        x8 = 0x0000000000000006
        x9 = 0x0000000000000002
       x10 = 0x000000013a0efd77
       x11 = 0x01ff00012d00b800
       x12 = 0x0000000000000042
       x13 = 0x000000012d00bc10
       x14 = 0x00000001ba0ec000
       x15 = 0x0000000213521b88  (void *)0x0000000213521b60: UIButton
       x16 = 0x00000001d31fa170  libobjc.A.dylib`objc_release
       x17 = 0x00000002162b2f90  (void *)0x00000001d31fa170: objc_release
       x18 = 0x0000000000000000
       x19 = 0x0000000281a30000
       x20 = 0x0000000129d0fd70
       x21 = 0x00000001021de9d0  "clickBtn:"
       x22 = 0x0000000129d0a7a0
       x23 = 0x00000001021de9d0  "clickBtn:"
       x24 = 0x0000000213536800  UIKitCore`UIApp
       x25 = 0x0000000000000000
       x26 = 0x00000002047c18ff  
       x27 = 0x0000000281a30000
       x28 = 0x0000000000000001
        fp = 0x000000016dc24e60
        lr = 0x00000001021de170  Demo`-[ViewController clickBtn:] + 52 at ViewController.m:29
        sp = 0x000000016dc24e30
        pc = 0x00000001021de178  Demo`-[ViewController clickBtn:] + 60 at ViewController.m:31:1
      cpsr = 0x80001000

0x16dc24e30: 0x0000000281a30000
0x16dc24e38: 0x000000016dc24e48
0x16dc24e40: 0x0000000000000000
0x16dc24e48: 0x0000000129d0fd70
0x16dc24e50: 0x00000001021de9d0 "clickBtn:"
0x16dc24e58: 0x0000000129d0a7a0
0x16dc24e60: 0x000000016dc24e90
0x16dc24e68: 0x00000001bcd84f1c UIKitCore`-[UIApplication sendAction:to:from:forEvent:] + 100
0x16dc24e70: 0x0000000281a30000

(lldb)rr x0 sp -s 0x10
[HMLLDB] register read x0 sp
    x0 = 0x0000000000000000
    sp = 0x000000016fb2cdf0
0x16fb2cdf0: 0x000000010110b8b0
0x16fb2cdf8: 0x00000001002e5008 "clickBtn:"
0x16fb2ce00: 0x0000000101137b80

reference

扫描machO获取指定地址所有的引用信息。
类似Hopper Disassembler软件的References to功能,但暂时只支持基于b/bl指令的查询,不过速度比Hopper Disassembler快很多,后者需要分析的信息太多了。

语法:
reference <address> <image_name>

例子:
(lldb) reference 0x12345678 MyApp
(lldb) reference 0x12345678 UIKitCore

# 实际例子
(lldb) reference 0x18e9b27a0 UIKitCore
[HMLLDB] These are the scan results:
0x18d43b730: UIKitCore`-[UIControl sendAction:to:forEvent:] + 108
0x18d875624: UIKitCore`-[UITabBar _sendAction:withEvent:] + 388
0x18d87ed14: UIKitCore`-[UIToolbar _sendAction:withEvent:] + 328
0x18df4e8f0: UIKitCore`-[UIApplication _performKeyCommandInvocation:allowsRepeat:] + 280
0x18e087250: UIKitCore`-[UITableView _updateCell:withValue:] + 224
[HMLLDB] Reference count:5

注意:

  • 这个命令扫描大型machO的时候非常耗时,在我的电脑上需要100秒扫描UIKitCore。
  • 当前只支持基于b/bl指令的查询。在使用的时候需要考虑stub函数island函数

adrp

快速获取adrp指令的计算结果。
0x189aef040 <+32>: adrp x8, 348413,当遇到这行汇编指令,需要自己去计算得到x8寄存器的值太麻烦了,adrp命令能帮助我快速获取结果。

语法:
adrp <immediate> <pc address>
adrp <pc address> <adrp> <register> <immediate>
adrp <pc address> <+offset> <adrp> <register> <immediate>

例子:
(lldb) adrp 348413 0x189aef040
[HMLLDB] result: 0x1debec000, 8032010240

(lldb) adrp 0x189aef040: adrp   x8, 348413
[HMLLDB] x8: 0x1debec000, 8032010240

(lldb) adrp 0x189aef040 <+32>:  adrp   x8, 348413
[HMLLDB] x8: 0x1debec000, 8032010240

edisassemble

系统自带命令disassemble增强版本,新增了如下注解:

  • 计算adr/adrp指令的值。
  • 找到部分branch指令的真实目标。
语法:和系统命令disassemble一模一样

例子:
(lldb) edisassemble -s 0x107ad4504
(lldb) edis -a 0x107ad4504
(lldb) edis -n "-[UIDevice systemVersion]"
    

# 如下例子是系统命令disassemble和增强版本edisassemble的对比
(lldb) dis -n "-[UIDevice systemVersion]"
UIKitCore`-[UIDevice systemVersion]:
    0x1afbe9e34 <+0>:  pacibsp 
    0x1afbe9e38 <+4>:  stp    x20, x19, [sp, #-0x20]!
    0x1afbe9e3c <+8>:  stp    x29, x30, [sp, #0x10]
    0x1afbe9e40 <+12>: add    x29, sp, #0x10
    0x1afbe9e44 <+16>: adrp   x2, 329379
    0x1afbe9e48 <+20>: add    x2, x2, #0xef0            ; @"ProductVersion"
    0x1afbe9e4c <+24>: bl     0x1b0b06000               ; objc_msgSend$_deviceInfoForKey:
    0x1afbe9e50 <+28>: bl     0x1b2be97f0
    0x1afbe9e54 <+32>: mov    x19, x0
    0x1afbe9e58 <+36>: adrp   x8, 329342
    0x1afbe9e5c <+40>: add    x8, x8, #0x90             ; @"Unknown"
    0x1afbe9e60 <+44>: cmp    x0, #0x0
    0x1afbe9e64 <+48>: csel   x0, x8, x0, eq
    0x1afbe9e68 <+52>: bl     0x1b2be9ad0
    0x1afbe9e6c <+56>: mov    x20, x0
    0x1afbe9e70 <+60>: bl     0x1b2be99c0
    0x1afbe9e74 <+64>: mov    x0, x20
    0x1afbe9e78 <+68>: ldp    x29, x30, [sp, #0x10]
    0x1afbe9e7c <+72>: ldp    x20, x19, [sp], #0x20
    0x1afbe9e80 <+76>: retab  
    
(lldb) edis -n "-[UIDevice systemVersion]"
UIKitCore`-[UIDevice systemVersion]:
    0x1afbe9e34 <+0>:  pacibsp 
    0x1afbe9e38 <+4>:  stp    x20, x19, [sp, #-0x20]!
    0x1afbe9e3c <+8>:  stp    x29, x30, [sp, #0x10]
    0x1afbe9e40 <+12>: add    x29, sp, #0x10
    0x1afbe9e44 <+16>: adrp   x2, 329379                ; x2 = 0x20028c000
    0x1afbe9e48 <+20>: add    x2, x2, #0xef0            ; @"ProductVersion"
    0x1afbe9e4c <+24>: bl     0x1b0b06000               ; objc_msgSend$_deviceInfoForKey:
    0x1afbe9e50 <+28>: bl     0x1b2be97f0               ; br x16, x16 = 0x1a538ade4 objc_claimAutoreleasedReturnValue
    0x1afbe9e54 <+32>: mov    x19, x0
    0x1afbe9e58 <+36>: adrp   x8, 329342                ; x8 = 0x200267000
    0x1afbe9e5c <+40>: add    x8, x8, #0x90             ; @"Unknown"
    0x1afbe9e60 <+44>: cmp    x0, #0x0
    0x1afbe9e64 <+48>: csel   x0, x8, x0, eq
    0x1afbe9e68 <+52>: bl     0x1b2be9ad0               ; br x16, x16 = 0x1a537e148 objc_retainAutoreleaseReturnValue
    0x1afbe9e6c <+56>: mov    x20, x0
    0x1afbe9e70 <+60>: bl     0x1b2be99c0               ; br x16, x16 = 0x1a537fc28 objc_release_x19
    0x1afbe9e74 <+64>: mov    x0, x20
    0x1afbe9e78 <+68>: ldp    x29, x30, [sp, #0x10]
    0x1afbe9e7c <+72>: ldp    x20, x19, [sp], #0x20
    0x1afbe9e80 <+76>: retab  

tracefunction

基于指令,一步步追踪函数跳转,直到命中下个断点停止。
如果你设置了如下两个断点:
tracefunction.jpg 当命中了第一个断点后,执行tracefunction命令结果如下:

(lldb) tracefunction
[HMLLDB] ==========Begin========================================================
Demo`-[ViewController buttonAction] + 24 at ViewController.m:28:24 (0x100c33314)
Demo`-[ViewController buttonAction] + 24 at ViewController.m:28:24 (0x100c33314)
Demo`symbol stub for: objc_alloc_init + 8  (0x100c3e7dc)
libobjc.A.dylib`objc_alloc_init + 32   (0x1a7114f3c)
libobjc.A.dylib`_objc_rootAllocWithZone + 36 (0x1a711140c)
libobjc.A.dylib`symbol stub for: calloc + 12 (0x1a713a524)
libsystem_malloc.dylib`calloc + 20  (0x1a06a7b78)
libsystem_malloc.dylib`_malloc_zone_calloc + 84 (0x1a06aae58)
libsystem_malloc.dylib`default_zone_calloc + 32 (0x1a06a79d4)
libsystem_malloc.dylib`nanov2_calloc + 156   (0x1a06bc74c)
libsystem_malloc.dylib`nanov2_allocate + 124 (0x1a06bc160)
libsystem_malloc.dylib`nanov2_allocate + 340 (0x1a06bc238)
libsystem_malloc.dylib`symbol stub for: _platform_memset + 8   (0x1a06c3448)
libsystem_platform.dylib`_platform_memset + 208 (0x1ff580e50)
libsystem_malloc.dylib`nanov2_allocate + 460 (0x1a06bc2b0)
libsystem_malloc.dylib`nanov2_calloc + 172   (0x1a06bc75c)
libsystem_malloc.dylib`_malloc_zone_calloc + 132   (0x1a06aae88)
libobjc.A.dylib`_objc_rootAllocWithZone + 100   (0x1a711144c)
libobjc.A.dylib`objc_alloc_init + 64   (0x1a7114f5c)
libobjc.A.dylib`objc_msgSend + 76   (0x1a710df6c)
libobjc.A.dylib`-[NSObject init] (0x1a711d184)
Demo`-[ViewController buttonAction] + 48 at ViewController.m:29:1  (0x100c3332c)
[HMLLDB] ==========End========================================================
[HMLLDB] Instruction count: 295
[HMLLDB] Function count: 22
[HMLLDB] Start time: 22:57:10
[HMLLDB] Stop time: 22:57:11
Process 18247 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 5.1
    frame #0: 0x0000000100c3332c Demo`-[ViewController buttonAction](self=0x000000010120c9e0, _cmd="buttonAction") at ViewController.m:29:1
   26    
   27    - (void)buttonAction {
   28        NSObject *object = [[NSObject alloc] init];
-> 29    }
      ^
   30    
   31    
   32    
Target 0: (Demo) stopped.


# 最多打印500个函数,达到目标数量后停止执行
(lldb) tracefunction -m 500
...

可以看到打印了这个过程里所有执行到的函数,堆栈里看不到的调用过程都能一览无遗。我常在两种情况下使用这个命令,一是查看某行代码是否会调用到某个函数,二是查看某个过程(两个断点之间)是否有函数被hook了。
还有一个不常见的目的,因为有输出总共执行的指令数量(Instruction count: 295),所以可以用来判断某个过程(两个断点之间)的性能耗时。(并非准确,还要考虑多发射和数据冒险等因素)
注意:这个命令是指令级别的,所以非常非常非常耗时。只追踪一行[[UIView alloc] init]就能耗时90秒。

traceinstruction

追踪指令,直到命中下个断点停止。
如果你设置了如下两个断点:
tracefunction.jpg 当命中了第一个断点后,执行traceinstruction命令结果如下:

(lldb) traceinstruction
[HMLLDB] ==========Begin========================================================
Demo`-[ViewController buttonAction] + 24 at ViewController.m:28:24		ldr	x0, [x8, #0xc58]	(0x104b23314)
Demo`symbol stub for: objc_alloc_init		nop		(0x104b2e7d4)
Demo`symbol stub for: objc_alloc_init + 4		ldr	x16, #0x5960			; (void *)0x00000001a7114f1c: objc_alloc_init	(0x104b2e7d8)
Demo`symbol stub for: objc_alloc_init + 8		br	x16	(0x104b2e7dc)
libobjc.A.dylib`objc_alloc_init		pacibsp		(0x1a7114f1c)
libobjc.A.dylib`objc_alloc_init + 4		stp	x29, x30, [sp, #-0x10]!	(0x1a7114f20)
libobjc.A.dylib`objc_alloc_init + 8		mov	x29, sp	(0x1a7114f24)
libobjc.A.dylib`objc_alloc_init + 12		cbz	x0, 0x1a7114f40			; <+36>	(0x1a7114f28)
libobjc.A.dylib`objc_alloc_init + 16		ldr	x8, [x0]	(0x1a7114f2c)
libobjc.A.dylib`objc_alloc_init + 20		and	x8, x8, #0xffffffff8	(0x1a7114f30)
libobjc.A.dylib`objc_alloc_init + 24		ldrb	w8, [x8, #0x1d]	(0x1a7114f34)
libobjc.A.dylib`objc_alloc_init + 28		tbz	w8, #0x6, 0x1a7114f60			; <+68>	(0x1a7114f38)
libobjc.A.dylib`objc_alloc_init + 32		bl	0x1a71113e8			; _objc_rootAllocWithZone	(0x1a7114f3c)
libobjc.A.dylib`_objc_rootAllocWithZone		pacibsp		(0x1a71113e8)
libobjc.A.dylib`_objc_rootAllocWithZone + 4		stp	x20, x19, [sp, #-0x20]!	(0x1a71113ec)
libobjc.A.dylib`_objc_rootAllocWithZone + 8		stp	x29, x30, [sp, #0x10]	(0x1a71113f0)
libobjc.A.dylib`_objc_rootAllocWithZone + 12		add	x29, sp, #0x10	(0x1a71113f4)
libobjc.A.dylib`_objc_rootAllocWithZone + 16		mov	x19, x0	(0x1a71113f8)
libobjc.A.dylib`_objc_rootAllocWithZone + 20		ldrh	w20, [x0, #0x1c]	(0x1a71113fc)
libobjc.A.dylib`_objc_rootAllocWithZone + 24		and	x1, x20, #0x1ff0	(0x1a7111400)
libobjc.A.dylib`_objc_rootAllocWithZone + 28		cbz	w1, 0x1a7111450			; <+104>	(0x1a7111404)
libobjc.A.dylib`_objc_rootAllocWithZone + 32		mov	w0, #0x1	(0x1a7111408)
libobjc.A.dylib`_objc_rootAllocWithZone + 36		bl	0x1a713a518			; symbol stub for: calloc	(0x1a711140c)
libobjc.A.dylib`symbol stub for: calloc		adrp	x17, 274572	(0x1a713a518)
libobjc.A.dylib`symbol stub for: calloc + 4		add	x17, x17, #0xea0	(0x1a713a51c)
libobjc.A.dylib`symbol stub for: calloc + 8		ldr	x16, [x17]	(0x1a713a520)
libobjc.A.dylib`symbol stub for: calloc + 12		braa	x16, x17	(0x1a713a524)
libsystem_malloc.dylib`calloc		mov	x2, x1	(0x1a06a7b64)
libsystem_malloc.dylib`calloc + 4		mov	x1, x0	(0x1a06a7b68)
libsystem_malloc.dylib`calloc + 8		adrp	x0, 292189	(0x1a06a7b6c)
libsystem_malloc.dylib`calloc + 12		add	x0, x0, #0x0	(0x1a06a7b70)
libsystem_malloc.dylib`calloc + 16		mov	w3, #0x1	(0x1a06a7b74)
libsystem_malloc.dylib`calloc + 20		b	0x1a06aae04			; _malloc_zone_calloc	(0x1a06a7b78)
libsystem_malloc.dylib`_malloc_zone_calloc		pacibsp		(0x1a06aae04)
libsystem_malloc.dylib`_malloc_zone_calloc + 4		stp	x24, x23, [sp, #-0x40]!	(0x1a06aae08)
...
...
...
libobjc.A.dylib`objc_alloc_init + 36		adrp	x8, 202134	(0x1a7114f40)
libobjc.A.dylib`objc_alloc_init + 40		add	x1, x8, #0x4da	(0x1a7114f44)
libobjc.A.dylib`objc_alloc_init + 44		ldp	x29, x30, [sp], #0x10	(0x1a7114f48)
libobjc.A.dylib`objc_alloc_init + 48		autibsp		(0x1a7114f4c)
libobjc.A.dylib`objc_alloc_init + 52		eor	x16, x30, x30, lsl #1	(0x1a7114f50)
libobjc.A.dylib`objc_alloc_init + 56		tbz	x16, #0x3e, 0x1a7114f5c			; <+64>	(0x1a7114f54)
libobjc.A.dylib`objc_alloc_init + 64		b	0x1a710df20			; objc_msgSend	(0x1a7114f5c)
libobjc.A.dylib`objc_msgSend		cmp	x0, #0x0	(0x1a710df20)
libobjc.A.dylib`objc_msgSend + 4		b.le	0x1a710dff0			; <+208>	(0x1a710df24)
libobjc.A.dylib`objc_msgSend + 8		ldr	x13, [x0]	(0x1a710df28)
libobjc.A.dylib`objc_msgSend + 12		and	x16, x13, #0x7ffffffffffff8	(0x1a710df2c)
libobjc.A.dylib`objc_msgSend + 16		mov	x10, x0	(0x1a710df30)
libobjc.A.dylib`objc_msgSend + 20		movk	x10, #0x6ae1, lsl #48	(0x1a710df34)
libobjc.A.dylib`objc_msgSend + 24		autda	x16, x10	(0x1a710df38)
libobjc.A.dylib`objc_msgSend + 28		mov	x15, x16	(0x1a710df3c)
libobjc.A.dylib`objc_msgSend + 32		ldr	x11, [x16, #0x10]	(0x1a710df40)
libobjc.A.dylib`objc_msgSend + 36		tbnz	w11, #0x0, 0x1a710dfa0			; <+128>	(0x1a710df44)
libobjc.A.dylib`objc_msgSend + 40		and	x10, x11, #0xffffffffffff	(0x1a710df48)
libobjc.A.dylib`objc_msgSend + 44		eor	x12, x1, x1, lsr #7	(0x1a710df4c)
libobjc.A.dylib`objc_msgSend + 48		and	x12, x12, x11, lsr #48	(0x1a710df50)
libobjc.A.dylib`objc_msgSend + 52		add	x13, x10, x12, lsl #4	(0x1a710df54)
libobjc.A.dylib`objc_msgSend + 56		ldp	x17, x9, [x13], #-0x10	(0x1a710df58)
libobjc.A.dylib`objc_msgSend + 60		cmp	x9, x1	(0x1a710df5c)
libobjc.A.dylib`objc_msgSend + 64		b.ne	0x1a710df70			; <+80>	(0x1a710df60)
libobjc.A.dylib`objc_msgSend + 68		eor	x10, x10, x1	(0x1a710df64)
libobjc.A.dylib`objc_msgSend + 72		eor	x10, x10, x16	(0x1a710df68)
libobjc.A.dylib`objc_msgSend + 76		brab	x17, x10	(0x1a710df6c)
libobjc.A.dylib`-[NSObject init]		ret		(0x1a711d184)
Demo`-[ViewController buttonAction] + 32 at ViewController.m:28:24		mov	x8, x0	(0x104b2331c)
Demo`-[ViewController buttonAction] + 36 at ViewController.m:28:24		add	x0, sp, #0x8	(0x104b23320)
Demo`-[ViewController buttonAction] + 40 at ViewController.m:28:15		str	x8, [sp, #0x8]	(0x104b23324)
Demo`-[ViewController buttonAction] + 44 at ViewController.m:28:15		mov	x1, #0x0	(0x104b23328)
Demo`-[ViewController buttonAction] + 48 at ViewController.m:29:1		bl	0x104b2e8c4			; symbol stub for: objc_storeStrong	(0x104b2332c)
[HMLLDB] ==========End========================================================
[HMLLDB] Instruction count: 295
[HMLLDB] Start time: 22:59:34
[HMLLDB] Stop time: 22:59:36
Process 18265 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 5.1
    frame #0: 0x0000000104b2332c Demo`-[ViewController buttonAction](self=0x0000000153d0b5f0, _cmd="buttonAction") at ViewController.m:29:1
   26  	
   27  	- (void)buttonAction {
   28  	    NSObject *object = [[NSObject alloc] init];
-> 29  	}
    	^
   30  	
   31  	
   32  	
Target 0: (Demo) stopped.


# 最多打印8000行指令,达到目标数量后停止执行
(lldb) traceinstruction -m 8000
...

由于篇幅太长,中间省略了很多行指令。我一般用于查看同一个函数内部的汇编代码是如何执行的。
类似于tracefunction指令,这个指令也非常耗时

trace-step-over-instruction

lldb自带命令thread step-inst-over --count 100存在两个问题:

  • 只能看到最终结果,看不到这100条指令的过程。我需要在每次"step over instruction"后看到当前pc寄存器的地址。
  • 当遇到特定的指令如"bl","ret",表现不符合预期。

trace-step-over-instruction解决了这些问题。

语法:
trace-step-over-instruction <count>

例子:
(lldb) trace-step-over-instruction 20
UIKitCore`-[UIApplication sendAction:to:from:forEvent:]		pacibsp		(0x19b1d79d0)
UIKitCore`-[UIApplication sendAction:to:from:forEvent:] + 4		stp	x22, x21, [sp, #-0x30]!	(0x19b1d79d4)
UIKitCore`-[UIApplication sendAction:to:from:forEvent:] + 8		stp	x20, x19, [sp, #0x10]	(0x19b1d79d8)
UIKitCore`-[UIApplication sendAction:to:from:forEvent:] + 12		stp	x29, x30, [sp, #0x20]	(0x19b1d79dc)
UIKitCore`-[UIApplication sendAction:to:from:forEvent:] + 16		add	x29, sp, #0x20	(0x19b1d79e0)
UIKitCore`-[UIApplication sendAction:to:from:forEvent:] + 20		mov	x19, x5	(0x19b1d79e4)
UIKitCore`-[UIApplication sendAction:to:from:forEvent:] + 24		mov	x21, x4	(0x19b1d79e8)
UIKitCore`-[UIApplication sendAction:to:from:forEvent:] + 28		mov	x20, x3	(0x19b1d79ec)
UIKitCore`-[UIApplication sendAction:to:from:forEvent:] + 32		mov	x22, x2	(0x19b1d79f0)
UIKitCore`-[UIApplication sendAction:to:from:forEvent:] + 36		cbnz	x3, 0x19b1d7a10			; <+64>	(0x19b1d79f4)
UIKitCore`-[UIApplication sendAction:to:from:forEvent:] + 64		mov	x0, #0x2	(0x19b1d7a10)
UIKitCore`-[UIApplication sendAction:to:from:forEvent:] + 68		movk	x0, #0x10, lsl #48	(0x19b1d7a14)
UIKitCore`-[UIApplication sendAction:to:from:forEvent:] + 72		bl	0x19e8d6820	(0x19b1d7a18)
UIKitCore`-[UIApplication sendAction:to:from:forEvent:] + 76		cbz	w0, 0x19b1d7a38			; <+104>	(0x19b1d7a1c)
UIKitCore`-[UIApplication sendAction:to:from:forEvent:] + 80		mov	x0, x20	(0x19b1d7a20)
UIKitCore`-[UIApplication sendAction:to:from:forEvent:] + 84		mov	x1, x22	(0x19b1d7a24)
UIKitCore`-[UIApplication sendAction:to:from:forEvent:] + 88		mov	x2, x21	(0x19b1d7a28)
UIKitCore`-[UIApplication sendAction:to:from:forEvent:] + 92		mov	x3, x19	(0x19b1d7a2c)
UIKitCore`-[UIApplication sendAction:to:from:forEvent:] + 96		bl	0x19e8d6fc0	(0x19b1d7a30)
UIKitCore`-[UIApplication sendAction:to:from:forEvent:] + 100		b	0x19b1d7a54			; <+132>	(0x19b1d7a34)
UIKitCore`-[UIApplication sendAction:to:from:forEvent:] + 132		cmp	x20, #0x0	(0x19b1d7a54)

pfont

打印设备支持的所有字体名称。

(lldb) pfont
[HMLLDB] Family names count: 81, font names count: 274
familyNames: Academy Engraved LET
	fontName: AcademyEngravedLetPlain
familyNames: Al Nile
	fontName: AlNile
	fontName: AlNile-Bold
familyNames: American Typewriter
	fontName: AmericanTypewriter
...

plifecycle

用于打印UIViewController的生命周期。无侵入的方式,加上Xcode可以设置Console字体颜色使输出一目了然,成为了我最喜爱的命令之一。
使用方法:

  1. 新增一个符号断点Symbolic Breakpoint,在Symbol一行添加需要打印的方法,如-[UIViewController viewDidAppear:]
  2. 添加一个Action(Debugger Command),在里面新增plifecycle命令
  3. 勾上这个选项:Automatically continue after evaluating actions

具体配置如下图,我一般会用加上-i选项用来忽略一些系统UIViewController。

我个人常常启用viewDidAppear:dealloc用来以辅助开发,其余默认设置为Disable,按需启动,如下图:

实际输出打印例子如下图,区别于项目本身的输出(Target Output),如果选择了Debugger output,则输出还能集中显示,去除干扰。

但是这个命令还有待优化,一个是会导致页面切换卡顿,所以我一般只启用viewDidAppear:dealloc打印。另一个就是刚启动APP时有可能会触发下面的警告而暂停程序,如果经常出现阻碍了开发,建议按需启动。

# 遇上这个警告,要点击Xcode里的Continue program execution让程序继续跑
Warning: hit breakpoint while running function, skipping commands and conditions to prevent recursion.

另外,源码里还另外提供了其它方式去使用LLDB打印生命周期,有兴趣可以看看。

redirect

stdout/stderr重定向。

# 如果用模拟器,可以把Xcode的输出重定向到终端Terminal
# 打开终端,输入"tty"命令,就可以知道其路径了:/dev/ttys000
(lldb) redirect both /dev/ttys000
[HMLLDB] redirect stdout successful
[HMLLDB] redirect stderr successful

push

找到一个UINavigationController,然后push一个指定的UIViewController。
此命令的使用有限制,push MyViewController相当于要先执行[[MyViewController alloc] init],也就是说目标控制器的初始化构造器(Initializer)必须是无依赖的。若一个类初始化构造时需要参数或初始化后还需要传入参数,此命令可能会导致错误的显示甚至程序奔溃。

此处贴个gif演示,没有用Kingfisher演示是因为其demo中的UIViewController依赖于storyboard,不符合使用要求。

showhud & showfps

showhud命令会在keyWindow上展示一个debug视图,显示内存占用,CPU使用率,主线程FPS。

点击debug视图会present一个新的view controller(如下图),里面的功能后文会有介绍。

showfps命令已被showhud替代,项目里保留下来这个极简的工具仅供学习参考。

sandbox

present一个沙盒浏览器,可以查看主bundle目录和数据目录,具有系统分享(如隔空投送)、删除文件等功能。
这里用gif演示(首次调用命令要等待几秒,为了减少gif大小,演示的是初始化后的调用)
img7.gif 这是拉起系统分享的一个例子:
share_AirDrop.jpg

inspect

查看当前页面的UIView对象。还能看到常见的类如UILabel的关键属性值。

request

自动打印http/https请求(WKWebView除外)。
可能会多次打印同一个请求,请自行判断。
request.gif

environment

诊断当前的环境。可以看到其中一项是[Git commit hash],这也是建议clone仓库的原因之一。

(lldb) environment
[HMLLDB] [Python version] 3.8.2 (default, Nov  4 2020, 21:23:28) 
		[Clang 12.0.0 (clang-1200.0.32.28)]
[HMLLDB] [LLDB version] lldb-1200.0.44.2
		Apple Swift version 5.3.2 (swiftlang-1200.0.45 clang-1200.0.32.28)
[HMLLDB] [Target triple] x86_64h-apple-ios-simulator
[HMLLDB] [Git commit hash] 088f654cb158ffb16019b2deca5dce36256837ad
[HMLLDB] [Optimized] False: 28  True: 0
[HMLLDB] [Xcode version] 1230
[HMLLDB] [Xcode build version] 12C33
[HMLLDB] [Model identifier] x86_64
[HMLLDB] [System version] iOS 13.0

如果发生错误

通过LLDB进行JIT即时编译并不稳定,命令报错是常见的。如果出现问题,请按以下步骤依次检查。

  1. pull最新代码,核对Xcode版本,HMLLDB一般只适配最新的Xcode版本
  2. 打开~/.lldbinit文件,确保是在末尾导入的HMLLDB.py文件令其命令不被覆盖。
  3. 启动APP后,主动点击Pause program execution进入LLDB调试模式执行命令,而不是用命中断点的方式执行命令。(除了部分命令)
  4. 重启Xcode。这一般能解决绝大多数问题。
  5. 重启电脑。有些命令出错会把电脑的LLDB服务玩坏。
  6. 完成以上步骤命令依然出错。请拷贝错误内容发布到Issue,并执行environment命令,其输出也需要发布到Issue。

在我个人来看,编写LLDB复杂命令门槛高的原因主要是即时编译不稳定,可能出错原因都找不到。对比一下,SB API简直太友好了,还可以看源码帮助理解,另外我还编写了一个命令帮助理解常用SB API,读者有兴趣可以去看看源码。

存在的问题

HMLLDB和前言提到的两个项目一样,都有很多问题:

  • 如上所说,命令不稳定,实测还发现和设备型号相关。
  • 数据常驻内存。没有ARC机制帮忙插入release等代码,设置了options.SetSuppressPersistentResult(True)也没能解决问题。
  • 没有使用exe_ctx参数,多线程环境中极可能出错。虽然这些命令暂时都没有涉及多线程,但其他情况(如系统内部断点)触发的就不一定了。
  • 命令覆盖。LLDB其实有一个函数UserCommandExists支持判断已经导入的命令,可以避免自定义命令覆盖。但SB API居然没有暴露出这个方法。
  • 即时编译速度慢,越大型项目执行速度越慢。虽然有在Python中去hook OC的方法,按需执行相关代码,但还是不够快。如果调试工具使用频率很高,还是直接集成到项目中的传统方法效率更高。
  • 还没找到加载某些系统framework的方法。
  • 仅支持iOS平台。

未来的计划

毕竟是在Debug模式下的调试命令,上述问题并非急迫的问题,所以未来主要还是开发新的命令。理论上HMLLDB可以成为绿色版的FLEX,只是工程量浩大,除非我找到了方法可以真机运行时加载电脑本地路径的framework。
现有的命令大多是结合我个人经历而开发的。LLDB有足够高的权限,开发新功能除了个人需求还取决于想象力,各位读者如果有什么好的建议和想法,欢迎留言提出来。