LLDB是一款高性能的调试器,它是一组可重用的组件,高度利用了大型LLVM项目中的现有库,例如Clang表达式解析器和LLVM反汇编器。LLDB是Xcode默认的调试器,支持调试C、OC、C++开发的一系列桌面应用,和手机应用。
相信所有使用Xcode开发工具开发人人员对LLDB并不陌生,Xocde为我们提供了方便快捷的操作导航区域,并在当我们我们键入LLDB命令的时候有友好的提示功能,但是大部分开发人员只是依赖几个常用的调试命令进行一些简单的信息打印,今天通过这篇文章我们简单的对一些常用的LLDB命令进行一个汇总,同时介绍一些LLDB命令更高级的用法,来帮助我们在开发过程中进行一些更快,更准的调试工作,提升开发调试效率。
<command> [<subcommand> [<subcommand>...]] <action> [-options [option-value]] [argument [argument...]]
command: 命令名称 subcommand: 子命令 action: 行为,可选 option:命令选项,可选 argument: 该命令携带的参数,可选
举例:
breakpoint set -n main
command:breakpoint 断点命令 action:set 设置断点 option:-n 表根据方法 name 设置断点 arguement:mian 表示方法名为 mian
说明:
LLDB命令的行为action为可选,当我们不需要使用命令行为的时候,命令后面所有内容视为参数处理,当我们使用命令行为的时候,需要使用--来区分参数和命令行为,如下:
- expression obj/value //打印一个对象的地址,或者输出一个值类型变量的值
- expression -o --obj //调用一个对象description方法
语法:help command
正如我们平时在终端使用其他命令行一样,LLDB也存在help命令,我们可以通过help 命令查看某一个命令的具体用法
示例:
(lldb) help expression
Evaluate an expression on the current thread. Displays any returned value
with LLDB's default formatting. Expects 'raw' input (see 'help
raw-input'.)
Syntax: expression <cmd-options> -- <expr>
Command Options Usage:
expression [-AFLORTgp] [-f <format>] [-G <gdb-format>] [-a <boolean>] [-j <boolean>] [-X <source-language>] [-v[<description-verbosity>]] [-i <boolean>] [-l <source-language>] [-t <unsigned-integer>] [-u <boolean>] [-d <none>] [-S <boolean>] [-D <count>] [-P <count>] [-Y[<count>]] [-V <boolean>] [-Z <count>] -- <expr>
expression [-AFLORTgp] [-a <boolean>] [-j <boolean>] [-X <source-language>] [-i <boolean>] [-l <source-language>] [-t <unsigned-integer>] [-u <boolean>] [-d <none>] [-S <boolean>] [-D <count>] [-P <count>] [-Y[<count>]] [-V <boolean>] [-Z <count>] -- <expr>
expression [-r] -- <expr>
expression <expr>
-A ( --show-all-children )
Ignore the upper bound on the number of children to show.
.
.
.
语法:expression
: 命令选项 -- :命令选项结束符号,表示当前命令选项已经结束,如果没有命令选项,可省略 :需要执行的表达式
当我们在调试项目的时候,有时候想在当前断点处动态的去执行一个方法或者一个表达式,来改变当前调试环境的上下文状态,使用这个命令,我们就不必须执行添加代码,重新编译执行的操作,大大节省调试时间。
示例:
expression self.refreshFlag = YES //
我们也可以通过此命令进行一些打印操作,查看某些对象一些内存信息和内容。
示例:
(lldb) expression -O -- arr //和指令 po的效果一样
<__NSArrayI 0x600001f6cf00>(
1,
2,
3
)
(lldb) expression arr //和指令print、p、call的效果一样
(__NSArrayI *) $2 = 0x0000600001f6cf00 @"3 elements"
(lldb)
语法:thread
用于在当前进程中的一个或多个线程上操作的命令。通常我们使用此命令进行一些函数调用堆栈的操作和断点所在函数的执行逻辑。此部分指令在Xcode调试导航部分都有提供相应的快捷键,也是我们日常频繁使用的指令,了解这些快捷键的指令实现有助于我们在进行一些崩溃定位和逆向研究时更加灵活的使用LLDB。
示例:
(lldb) thread backtrace //显示线程调用堆栈。默认为当前
线程.
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
* frame #0: 0x000000010552fdb7 DebugTestProject`-[ViewController function3](self=0x00007ff3be006da0, _cmd="function3") at ViewController.m:43:5
frame #1: 0x000000010552fd95 DebugTestProject`-[ViewController function2](self=0x00007ff3be006da0, _cmd="function2") at ViewController.m:39:5
frame #2: 0x000000010552fd55 DebugTestProject`-[ViewController function1](self=0x00007ff3be006da0, _cmd="function1") at ViewController.m:34:5
frame #3: 0x000000010552fcb9 DebugTestProject`-[ViewController touchesBegan:withEvent:](self=0x00007ff3be006da0,
(lldb) thread continue //继续执行当前目标流程,过掉当前断点
(lldb) thread step-in //单步调用,步进调用函数中
(lldb) thread step-over //跨级调用,如果当前为函数调用,跨过当前函数,直接下一步,对应step-in
(lldb) thread step-out //完成当前堆栈帧的执行并停止
返回
(lldb) thread step-inst-over //指令级别的单步执行,以每一条汇编指令为单位执行
(lldb) thread step-inst //指令级别的单步调用,步进调用函数中,以每一条汇编指令为单位执行
(lldb) continue //thread continue简写
(lldb) c //thread continue简写
(lldb) next //thread step-over简写
(lldb) n //thread step-over简写
(lldb) finish //thread step-out简写
(lldb) step //thread step-in简写
(lldb) s //thread step-in简写
(lldb) nexti //thread step-inst-over 简写
(lldb) i //thread step-inst-over 简写
(lldb) step //thread step-inst 简写
(lldb) s //thread step-inst 简写
语法:breakpoint[
设置断点操作在我们代码调试过可以帮助我们准确的定位代码执行过程中某一个执行上下文中,通过对所设置断点位置变量的分析来定位一些问题,Xcode已经为我们提供了方便快捷的断点设置操作面板,此处我们详细介绍一些这些操作的底层实现原理,有助于我们在进行一些接触不到源码的静态库或者动态库调试工作。
示例:
(lldb) breakpoint set //在可执行文件中设置一个或一组断点
(lldb) breakpoint set -n function1 //按函数名设置断点,为所有该函数创建断点
Breakpoint 2: where = DebugTestProject`-[ViewController function1] + 23 at ViewController.m:33:5, address = 0x000000010cce0d37
(lldb) breakpoint set -n "[ViewController function1]" //按函数名设置断点,为某一个类的该函数创建断点
Breakpoint 3: where = DebugTestProject`-[ViewController function1] + 23 at ViewController.m:33:5, address = 0x000000010cce0d37
(lldb) breakpoint set -r 正则表达式 //可通过正则表达式匹配函数
Breakpoint 2: 3 locations.
(lldb) breakpoint set -s 动态库 -n 函数名 //可以在动态库中设置断点,
Breakpoint 2: 3 locations.
(lldb) breakpoint list //列处当前所有断点的详细信息
Current breakpoints:
1: file = '/Users/ylc/Documents/DebugTest/DebugTestProject/DebugTestProject/ViewController.m', line = 29, exact_match = 0, locations = 1, resolved = 1, hit count = 1
1.1: where = DebugTestProject`-[ViewController touchesBegan:withEvent:] + 181 at ViewController.m:29:6, address = 0x00000001008c4c95, resolved, hit count = 1
2: regex = 'ylcfuncti', locations = 3, resolved = 3, hit count = 0
2.1: where = DebugTestProject`-[ViewController ylcfunction3] + 23 at ViewController.m:43:5, address = 0x00000001008c4da7, resolved, hit count = 0
2.2: where = DebugTestProject`-[ViewController ylcfunction1] + 23 at ViewController.m:33:5, address = 0x00000001008c4d27, resolved, hit count = 0
2.3: where = DebugTestProject`-[ViewController ylcfunction2] + 23 at ViewController.m:38:5, address = 0x00000001008c4d67, resolved, hit count = 0
(lldb) breakpoint delete 断点号 //删除某一个指定的断点,如果不指定断点号,默认删除所有断点
(lldb) breakpoint disable 断点编号 //禁用某一个断点,指定断电编号
(lldb) breakpoint enable 断点编号 //启用某一个断点,指定断点编号
下面着重介绍一下 breakpoint command调试命令,此命令用于在某一个断点处插入一段自定义指令集,当执行到该断点处时,插入的调试指令集段会被执行,此调试命令可用于在不重新编译代码的情况下修改当前某一个方法内的逻辑,且此逻辑可被重复执行,同时也可以查看或者删除某一段指令集,恢复之前的代码逻辑,方便灵活,节省调试时间。
示例:
(lldb) breakpoint set -n "[ViewController ylcfunction1]"
Breakpoint 2: where = DebugTestProject`-[ViewController ylcfunction1] + 23 at ViewController.m:34:5, address = 0x000000010bc0ed27
(lldb) breakpoint command add 2 //在某一断点处添加一段指令集
Enter your debugger command(s). Type 'DONE' to end.
> p self.view.backgroundColor = [UIColor redColor]
> p arr = @[@"1", @"2", @"3"]
> DONE
(lldb) breakpoint command list 2 //查看某一断点处的指令集
Breakpoint 2:
Breakpoint commands:
p self.view.backgroundColor = [UIColor redColor]
p arr = @[@"1", @"2", @"3"]
(lldb) breakpoint command delete 2 //删除某一断点处的指令集
(lldb) breakpoint command list 2
Breakpoint 2 does not have an associated command.
(lldb)
语法:watchpoint
watchpoint是另外一种断点,我们称之为内存断点,之所以称之为内存断点,是因为我们可以对指定的内存发生读、写时进行暂停查看。通过此断点我们可以对已知属性或者变量进行断点,查看对当前地址的操作线程。
示例:
(lldb) watchpoint set variable self->_refreshFlag //对某一个变量进行内存改变的断点
Watchpoint created: Watchpoint 1: addr = 0x7fca58007510 size = 1 state = enabled type = w
watchpoint spec = 'self->_refreshFlag'
new value: false
Watchpoint 1 hit:
old value: false
new value: true
(lldb) watchpoint set expression &self->_mail //对某一个变量的内存地址进行一个断点,当内存地址内容发生改变时,进行断点
Watchpoint created: Watchpoint 1: addr = 0x7feb61509908 size = 8 state = enabled type = w
new value: 0x0000000000000000
Watchpoint 1 hit:
old value: 0x0000000000000000
new value: 0x0000000000000064
(lldb) watchpoint list //当前所有的内存断点
Number of supported hardware watchpoints: 4
Current watchpoints:
Watchpoint 1: addr = 0x7feb61509908 size = 8 state = enabled type = w
old value: 0x0000000000000000
new value: 0x0000000000000064
(lldb) watchpoint disable 断点编号 //禁用某一个断点,默认全部禁用
All watchpoints disabled. (1 watchpoints)
(lldb) watchpoint enable 断点编号 //启用某一个断点,默认全部启用
All watchpoints enabled. (1 watchpoints)
(lldb) watchpoint command add 断点编号 //为某一个断点添加指令集,同breakpoint command add 用法
Enter your debugger command(s). Type 'DONE' to end.
> DONE
(lldb) watchpoint command list 断点编号 //查看某一个断点指令集,同breakpoint command list 用法
Watchpoint 1 does not have an associated command.
(lldb) watchpoint command delete 断点编号 //删除某一个断点指令集,同breakpoint command delete 用法
语法:target modules
用于访问一个或多个目标模块信息的命令。我们通常可以通过使用此命令查看当前进程加载使用了哪些动态库,查看某一个类对象的内存详情,在逆向开发时也可以通过该命令获取某个方法或者变量的真实地址。
示例:
(lldb) image list //查看当前进程加载使用了哪些动态库
[ 0] 913F3F08-E800-39F4-8522-E62CA4C9D808 0x00000001030c9000 /Users/ylc/Library/Developer/Xcode/DerivedData/DebugTestProject-bvenrimdwabnwpbyzbxxkbkfhmls/Build/Products/Debug-iphonesimulator/DebugTestProject.app/DebugTestProject
[ 1] 1D318D60-C9B0-3511-BE9C-82AFD2EF930D 0x0000000109d4c000 /usr/lib/dyld
[ 2] C0AB2891-0E84-3F48-9A45-C079C2864222 0x00000001030dd000 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/dyld_sim
...
(lldb) image lookup -t NSArray //查看某一个类对象的内存详
Best match found in /Users/ylc/Library/Developer/Xcode/DerivedData/DebugTestProject-bvenrimdwabnwpbyzbxxkbkfhmls/Build/Products/Debug-iphonesimulator/DebugTestProject.app/DebugTestProject:
id = {0x7fffffff000002a7}, name = "NSArray", byte-size = 8, decl = NSArray.h:17, compiler_type = "@interface NSArray : NSObject
@property(readonly, getter = count, setter = <null selector>) NSUInteger count;
@end"
虽然Xcode和其他开发集成工具已经为我们提供了常用调试指令的操作面板,但是通过对LLDB调试指令的语法和使用场景的探究,能更好的帮助我们理解LLDB指令调试原理,灵活组合和使用LLDB调试指令,可以更好的帮助我们进行代码调试工作,提升开发效率,后续会不断完善丰富本文,收纳更多LLDB指令的用法和使用场景。