一、概述
LLDB全称 [ Low Level Debugger ], 默认内置于Xcode中的动态调试工具。标准的 LLDB 提供了一组广泛的命令,旨在与熟悉的 GDB 命令兼容。 除了使用标准配置外,还可以很容易地自定义 LLDB 以满足实际需要。
语法结构
<command> [<subcommand> [<subcommand>...]] <action> [-options [option-value]] [argument [argument...]]
乍一看,有点懵,其实是这样的:
<command>(命令)和<subcommand>(子命令):LLDB调试命令的名称。
命令和子命令按层级结构来排列:一个命令对象为跟随其的子命令对象创建一个上下文,子命令又为其子命令创建一个上下文,依此类推。
<action>:执行命令的操作
<options>:命令选项
<arguement>:命令的参数
[]:表示命令是可选的,可以有也可以没有
例如:
breakpoint set -n viewDidLoad
这个命令对应到上面就是:
command: breakpoint 表示断点命令
action: set 表示设置断点
option: -n 表示根据方法name设置断点
arguement: viewDidLoad 表示方法名为mian
二、LLDB调试命令
调试时,添加断点,暂停运行时,此时就可以在 Xcode 下方的控制台使用 lldb 调试:
常用调试命令:
- 打印某个变量或对象:
po / print / p / call / e 变量名/ 对象
po 命令只是打印数值,同于 NSLog 打印对象,而 print、p、call 命令还打印了变量的类型。
如图:
- 打印某个变量或对象:
在打印变量的值的时候,我们还可以使用 print/<fmt> 或者简化的 p/<fmt>指定打印格式,例如打印十六进制:
p/x 变量名
x 代表十六进制格式、t 代表二进制格式,其他格式类型请点击这里查看。
如果需查看内存数据:可以在输出窗口采用gdb命令:x /nfu <addr>
n表示要显示的内存单元的个数
-----------------------------------------
f表示显示方式, 可取如下值:
x 按十六进制格式显示变量
d 按十进制格式显示变量
u 按十进制格式显示无符号整型
o 按八进制格式显示变量
t 按二进制格式显示变量
a 按十六进制格式显示变量
i 指令地址格式
c 按字符格式显示变量
f 按浮点数格式显示变量
-----------------------------------------
u表示一个地址单元的长度:
b表示单字节
h表示双字节
w表示四字节
g表示八字节
-------------------------------------------
例如x/16xb self
会显示self指针地址内容,16个字节,16进制
- expression 命令(简写 expr/e/p)
从前面的命令列表可以看到 print、p、po、call 都是 expression 命令的简写,而 expression命令的作用是执行一个表达式,并将表达式返回的结果输出。 常用于调试时修改当前线程上变量的值,也就是说我们可以利用它们更改变量的值,而不需要修改代码再重新编译就可以看到效果,简写为 expr
例如我们将 number 的值修改为100:
也可以用于修改某属性的值,例如:
// 修改颜色
expression self.view.backgroundColor = [UIColor purpleColor]
// 刷新界面
expression -- (void)[CATransaction flush]
也可作为打印命令:
expression -- self.view
我们知道,OC里所有的对象都是用指针表示的,所以一般打印的时候,打印出来的是对象的指针,而不是对象本身。如果我们想打印对象。我们需要使用命令选项:-O。为了更方便的使用,LLDB为expression -O --定义了一个别名:po
p (expression)也可用于调试时,动态注入代码,像正常写代码一样,比较强大的。
-
call 命令 调用某个方法
相当于在 lldb 中实时执行所添加的代码
例如: 调试界面时,查看某个控件的位置等,可以直接设置一下背景,而不用重新运行才能看到。
call self.topIV.backgroundColor = [UIColor orangeColor]
-
call 命令 调用某个方法
-
d 反汇编当前目标中的指定指令。
默认为当前线程和当前函数
堆栈框架。可以查看当前方法的汇编代码,包括:内存地址、所占字节、汇编指令等,无意间发现的。(应该是 dis 命令的简写)
如图:
-
d 反汇编当前目标中的指定指令。
- image 命令
由于LLDB给 target modules 取了个别名 image,所以 target modules lookup 这个命令我们又可以写成 image lookup。
- (1)image lookup --address/a 寻址,定位异常代码位置
当我们有一个地址,想查找这个地址具体对应的文件位置,可以使用 image lookup --address ,简写为 image lookup -a
当程序崩溃的时候,可以通过 image lookup --address 内存地址,来精确定位崩溃的具体位置,很实用。
例如:
NSString *testStr = @"12345";
NSString *subString = [testStr substringToIndex:10];
运行越界崩溃:
具体的调用堆栈信息居然没有,很神奇吧?但是不同非凡的 Me 两次居然就找到了,呵呵
- (2)image lookup –name
当我们想查找一个方法或者符号的信息,比如所在文件位置等。我们可以使用image lookup --name,简写为image lookup -n。
适用于查找同名方法,不只是自己写的方法, 系统的和第三方的 .a 都可以搜索得到,而工程中的搜索是办不到的,
例如,随便找的:
- (3)image lookup --type查看类型
当我们想查看一个类型的时候,可以使用image lookup --type,简写为image lookup -t:
定义代码如下:
@interface ViewController ()
{
NSString *address;
float height;
}
/** Description */
@property (nonatomic, copy) NSString *name;
/** Description */
@property (nonatomic, assign) int age;
@end
打印信息如下:
可以看到,LLDB把 ViewController 这个class的所有属性和成员变量都打印了出来,当我们想了解某个类的时候,直接使用image lookup -t即可
image list 查看项目所调用的库,第一个为Mach-o 其余为相关的库,相当于 Windows 的镜像。
-
- thread 查看线程堆栈信息
- thread backtrace & bt 查看堆栈调用,效果是一样的,如下:
- up 查看上一步的堆栈调用信息,如下:
down 查看下一步的堆栈调用信息,类似 up
frame select 编号,跳转至指定堆栈查看,源码和汇编(系统的或打包的),定位某个方法的具体实现。
堆栈信息的编号,指定查看:
- frame variable 查看方法的所有参数。
注:三个参数因为 OC 中每个方法默认都有的两个隐式参数,第三个才为我们传的参数,此时也可以通过 p 修改相关的数据。
修改,上面的 up 和 down 只是查看,thread return 则是让代码回滚到上一步返回,也不会执行,相当于代码中的 return。
-
breakpoint 断点(代码断点)
断点可能是在我们调试过程中用的最多的功能了,但是我们不一定都了解断点的各种用法。
-
breakpoint 断点(代码断点)
手动打断点时:
- 断点的编辑,如:
1.设置触发条件
这个功能也比较实用,调试过程中,可以跟踪数据或界面等的变化,自动触发。
-
设置触发事件,可以看到有以下几种:
-
可以试一下,探索一下。
断点 lldb 调试命令:
- breakpoint set 设置断点
- 使用-n根据方法名给当前类设置断点:
breakpoint set -n viewWillAppear:
给 viewWillAppear 方法设置一个断点:
set 是子命令
-n 是选项 是--name 的缩写!
- 使用-n根据方法名给指定类多个方法设置断点:
breakpoint set -n "-[ViewController save:]" -n "-[ViewController continueGame:]" -n "-[ViewController pauseGame:]"
注意:"" 和参数的 :,简写 b -n 方法名,或 b 方法名
b -n "-[ViewController touchesBegan:withEvent:]"
b pauseGame:
- 给整个项目某个指定方法设置断点:
breakpoint set --selector 方法名
如:给 touchesBegan:withEvent: 设置断点
breakpoint set --selector touchesBegan:withEvent:
包含系统的方法,
- 使用-f指定文件和方法
breakpoint set -f ViewController.m -n viewDidAppear:
给ViewController.m文件中的viewDidAppear 方法设置断点:
注意方法后面的 :冒号,不添加无效果。
shift +Command + j 调转至当前类文件处
- 使用-l指定文件某一行设置断点
breakpoint set -f ViewController.m -l 33
在 ViewController.m 的 33 行设置一个断点。
breakpoint set --file ViewController.m --selector touchesBegan:withEvent:
给 ViewController.m 的 touchesBegan:withEvent: 方法添加断点。
这些都可以通过上面手动来添加,只不过是两种方式罢了。
给某个内存地址设置断点
b -a 地址
查看断点列表
$breakpoint list删除
breakpoint delete 删除所有断点
$breakpoint delete 组号(一组的断点)
breakpoint delete 组分号
breakpoint delete 1.1 不会删除该断点,会禁用该断点禁用/启用
breakpoint disable 禁用
简写:break dis
breakpoint enable 启用
简写:break en
禁用某一个断点
breakpoint disable 4.1
- 遍历整个项目中的所有方法包含 Game: 这个字符的所有方法
breakpoint set -r Game:
流程控制
继续执行
$continue c单步运行,将子函数当做整体一步执行
$n next-
单步运行,遇到子函数会进去
$s- lldb 调试时,按住 control 进入汇编指令,流程控制。
watchpoint 内存断点
- watchpoint 内存断点,查看某个值的变化。
watchpoint delete 删除内存断点
同 breakpoint 用法。break command add 断点编号,执行至该断点时做相应操作。
如下:
执行结果:
- break command delete 4
- break command list
同 breakpoint 用法。
stop-hook
让你在每次stop的时候去执行一些命令,(除了Debug 控制台中的暂停和 Debug View hierarchy 查看)只对breadpoint,watchpoint 有效。
- 添加一条断点指令,打印变量,只要程序停止就会执行。
target stop-hook add -o "frame variable"
-o one line 一行 感觉像输出 -output 简写
- 查看方法实现,仅限于在源码中调试
target stop-hook add -o "frame select"
"" 中可以添加任意想要查看的 指令,eg: p ...
每次 stop 时,结果如下:
- 查看所有的 stop-hook 指令
target stop-hook list
删除 stop-hook 指令
删除指定标号的
target stop-hook delete 1
删除所有的
target stop-hook delete
有一点特殊的:
undisplay 3
移除指定标号的 stop-hook 指令同 delete
同 breakpoint 用法,help target stop-hook 查看相关的命令。
.lldbinit
以上都是在 lldb 调试时,手动添加配置指令。
- 自动配置
在用户目录下找到 .lldbinit 文件,没有的话就新建一个,可以添加一条指令测试一下,如下:
target stop-hook add -o "frame variable"
注:用 Mac 的文本编辑工具创建编辑后保存不要使用任何格式,多信息的会有很多附加的设置信息,如:
这样就可以实现断点无须手动输入指令查看,但是 .lldbinit 一般用于导入配置文件,更能方便的使用 LLDB 的功能。
常用命令
- p
- b -[xxx xxx]
- x memory read 的缩写
- register read
- po
- image list
注:其实上面的所有命令,都可以通过 help 命令查看其相关的所有指令及用法,
如:thread help 就可以查看 thread 相关的所有命令
直接在 lldb 中 help 就可以看到所有的可操作的功能的列表和一些当前基本的命令
如下:
(lldb) help
Debugger commands:
apropos -- List debugger commands related to a word or subject.
breakpoint -- Commands for operating on breakpoints (see 'help b' for
shorthand.)
bugreport -- Commands for creating domain-specific bug reports.
command -- Commands for managing custom LLDB commands.
disassemble -- Disassemble specified instructions in the current
target. Defaults to the current function for the
current thread and stack frame.
expression -- Evaluate an expression on the current thread. Displays
any returned value with LLDB's default formatting.
frame -- Commands for selecting and examing the current thread's
stack frames.
gdb-remote -- Connect to a process via remote GDB server. If no host
is specifed, localhost is assumed.
gui -- Switch into the curses based GUI mode.
help -- Show a list of all debugger commands, or give details
about a specific command.
kdp-remote -- Connect to a process via remote KDP server. If no UDP
port is specified, port 41139 is assumed.
language -- Commands specific to a source language.
log -- Commands controlling LLDB internal logging.
memory -- Commands for operating on memory in the current target
process.
platform -- Commands to manage and create platforms.
plugin -- Commands for managing LLDB plugins.
process -- Commands for interacting with processes on the current
platform.
quit -- Quit the LLDB debugger.
register -- Commands to access registers for the current thread and
stack frame.
script -- Invoke the script interpreter with provided code and
display any results. Start the interactive interpreter
if no code is supplied.
settings -- Commands for managing LLDB settings.
source -- Commands for examining source code described by debug
information for the current target process.
target -- Commands for operating on debugger targets.
thread -- Commands for operating on one or more threads in the
current process.
type -- Commands for operating on the type system.
version -- Show the LLDB debugger version.
watchpoint -- Commands for operating on watchpoints.
Current command abbreviations (type 'help command alias' for more info):
add-dsym -- Add a debug symbol file to one of the target's current modules
by specifying a path to a debug symbols file, or using the
options to specify a module to download symbols for.
attach -- Attach to process by ID or name.
b -- Set a breakpoint using one of several shorthand formats.
bt -- Show the current thread's call stack. Any numeric argument
displays at most that many frames. The argument 'all' displays
all threads.
c -- Continue execution of all threads in the current process.
call -- Evaluate an expression on the current thread. Displays any
returned value with LLDB's default formatting.
continue -- Continue execution of all threads in the current process.
detach -- Detach from the current target process.
di -- Disassemble specified instructions in the current target.
Defaults to the current function for the current thread and
stack frame.
dis -- Disassemble specified instructions in the current target.
Defaults to the current function for the current thread and
stack frame.
display -- Evaluate an expression at every stop (see 'help target
stop-hook'.)
down -- Select a newer stack frame. Defaults to moving one frame, a
numeric argument can specify an arbitrary number.
env -- Shorthand for viewing and setting environment variables.
exit -- Quit the LLDB debugger.
f -- Select the current stack frame by index from within the current
thread (see 'thread backtrace'.)
file -- Create a target using the argument as the main executable.
finish -- Finish executing the current stack frame and stop after
returning. Defaults to current thread unless specified.
image -- Commands for accessing information for one or more target
modules.
j -- Set the program counter to a new address.
jump -- Set the program counter to a new address.
kill -- Terminate the current target process.
l -- List relevant source code using one of several shorthand formats.
list -- List relevant source code using one of several shorthand formats.
n -- Source level single step, stepping over calls. Defaults to
current thread unless specified.
next -- Source level single step, stepping over calls. Defaults to
current thread unless specified.
nexti -- Instruction level single step, stepping over calls. Defaults to
current thread unless specified.
ni -- Instruction level single step, stepping over calls. Defaults to
current thread unless specified.
p -- Evaluate an expression on the current thread. Displays any
returned value with LLDB's default formatting.
parray -- Evaluate an expression on the current thread. Displays any
returned value with LLDB's default formatting.
po -- Evaluate an expression on the current thread. Displays any
returned value with formatting controlled by the type's author.
poarray -- Evaluate an expression on the current thread. Displays any
returned value with LLDB's default formatting.
print -- Evaluate an expression on the current thread. Displays any
returned value with LLDB's default formatting.
q -- Quit the LLDB debugger.
r -- Launch the executable in the debugger.
rbreak -- Sets a breakpoint or set of breakpoints in the executable.
repl -- Evaluate an expression on the current thread. Displays any
returned value with LLDB's default formatting.
run -- Launch the executable in the debugger.
s -- Source level single step, stepping into calls. Defaults to
current thread unless specified.
si -- Instruction level single step, stepping into calls. Defaults to
current thread unless specified.
sif -- Step through the current block, stopping if you step directly
into a function whose name matches the TargetFunctionName.
step -- Source level single step, stepping into calls. Defaults to
current thread unless specified.
stepi -- Instruction level single step, stepping into calls. Defaults to
current thread unless specified.
t -- Change the currently selected thread.
tbreak -- Set a one-shot breakpoint using one of several shorthand
formats.
undisplay -- Stop displaying expression at every stop (specified by stop-hook
index.)
up -- Select an older stack frame. Defaults to moving one frame, a
numeric argument can specify an arbitrary number.
x -- Read from the memory of the current target process.
For more information on any command, type 'help <command-name>'.
命令很多,要查看某一个相关,可以继续 help , 熟悉熟悉,就会发现新大陆。
不常用调试命令:
apropos -- 列出与单词或主题相关的调试器命令
breakpoint -- 在断点上操作的命令 (详情使用'help b'查看)
bugreport -- 用于创建指定域的错误报告
command -- 用于管理自定义LLDB命令的命令
disassemble -- 拆分当前目标中的特定说明。 默认为当前线程和堆栈帧的当前函数
expression -- 求当前线程上的表达式的值。 以LLDB默认格式显示返回的值
frame -- 用于选择和检查当前线程的堆栈帧的命令
gdb-remote -- 通过远程GDB服务器连接到进程。 如果未指定主机,则假定为localhost
gui -- 切换到基于curses的GUI模式
help -- 显示所有调试器命令的列表,或提供指定命令的详细信息
kdp-remote -- 通过远程KDP服务器连接到进程。 如果没有指定UDP端口,则假定端口41139
language -- 指定源语言
log -- 控制LLDB内部日志记录的命令
memory -- 用于在当前目标进程的内存上操作的命令
platform -- 用于管理和创建平台的命令
plugin -- 用于管理LLDB插件的命令
process -- 用于与当前平台上的进程交互的命令
quit -- 退出LLDB调试器
register -- 命令访问当前线程和堆栈帧的寄存器
script -- 使用提供的代码调用脚本解释器并显示任何结果。 如果没有提供代码,启动交互式解释器。
settings -- 用于管理LLDB设置的命令
source -- 检查当前目标进程的调试信息所描述的源代码的命令
target -- 用于在调试器目标上操作的命令
thread -- 用于在当前进程中的一个或多个线程上操作的命令
type -- 在类型系统上操作的命令
version -- 显示LLDB调试器版本
watchpoint -- 在观察点上操作的命令
add-dsym -- ('target symbols add') 通过指定调试符号文件的路径,或使用选项指定下载符号的模块,将调试符号文件添加到目标的当前模块中的一个
attach -- ('_regexp-attach') 通过ID或名称附加到进程
b -- ('_regexp-break') 使用几种简写格式之一设置断点
bt -- ('_regexp-bt') 显示当前线程的调用堆栈。通过数字参数设置最多显示帧数。参数“all”显示所有线程
c -- ('process continue') 继续执行当前进程中的所有线程
call -- ('expression --') 计算当前线程上的表达式,使用LLDB的默认格式显示返回的值
continue -- ('process continue') 继续执行当前进程中的所有线程
detach -- ('process detach') 脱离当前目标进程
di -- ('disassemble') 拆分当前目标中的特定说明。 默认为当前线程和堆栈帧的当前函数
dis -- ('disassemble') 同上
display -- ('_regexp-display') 在每次停止时计算表达式(请参阅'help target stop-hook')
down -- ('_regexp-down') 选择一个新的堆栈帧。默认为移动一个帧,数字参数可以指定值
env -- ('_regexp-env') 查看和设置环境变量的简写
exit -- ('quit') 退出LLDB调试器
f -- ('frame select') 从当前线程中通过索引选择当前堆栈帧(参见'thread backtrace')
file -- ('target create') 使用参数作为主要可执行文件创建目标
finish -- ('thread step-out') 完成当前堆栈帧的执行并返回后停止。 默认为当前线程
image -- ('target modules') 用于访问一个或多个目标模块的信息的命令
j -- ('_regexp-jump') 将程序计数器设置为新地址
jump -- ('_regexp-jump') 同上
kill -- ('process kill') 终止当前目标进程
l -- ('_regexp-list') 使用几种简写格式之一列出相关的源代码
list -- ('_regexp-list') 同上
n -- ('thread step-over') 源级单步执行、步进调用,默认当前线程
next -- ('thread step-over') 同上
nexti -- ('thread step-inst-over') 指令级单步执行、步进调用,默认当前线程
ni -- ('thread step-inst-over') 同上
p -- ('expression --') 计算当前线程上表达式的值,以LLDB默认格式显示返回值
parray -- ('expression -Z %1 --') 同上
po -- 计算当前线程上的表达式。显示由类型作者控制的格式的返回值。
poarray -- ('expression -O -Z %1 --') 计算当前线程上表达式的值,以LLDB默认格式显示返回值
print -- ('expression --') 同上
q -- ('quit') 退出LLDB调试器
r -- ('process launch -X true --') 在调试器中启动可执行文件
rbreak -- ('breakpoint set -r %1') 在可执行文件中设置断点或断点集
repl -- ('expression -r -- ') E计算当前线程上表达式的值,以LLDB默认格式显示返回值
run -- ('process launch -X true --') 在调试器中启动可执行文件
s -- ('thread step-in') 源级单步执行、步进调用,默认当前线程
si -- ('thread step-inst') 指令级单步执行、步进调用,默认当前线程
sif -- 遍历当前块,如果直接步入名称与TargetFunctionName匹配的函数,则停止
step -- ('thread step-in') 源级单步执行、步进调用,默认当前线程
stepi -- ('thread step-inst') 指令级单步执行、步进调用,默认当前线程
t -- ('thread select') 更改当前选择的线程
tbreak -- ('_regexp-tbreak') 使用几种简写格式之一设置单次断点
undisplay -- ('_regexp-undisplay') 每次停止时停止显示表达式(由stop-hook索引指定)
up -- ('_regexp-up') 选择较早的堆栈帧。 默认为移动一个帧,数值参数可以指定任意数字
x -- ('memory read') 从当前目标进程的内存中读取
写这个小结也参考了大牛一些优秀的文章, 非常感谢,共同学习。
这是一位曾经供职于百度的技术大牛伯乐在线的个人主页,文章受益匪浅。