简单总结一下Xcode中的BreakPoint断点与LLDB调试功能
BreakPoint
指定位置设置BreakPoint
导航到代码中要暂停执行的行,点击行数的位置即可。
图中功能点:
-
点击后启动、取消所有添加的断点,如果一次性要取消多个端点,可以使用该功能;
-
继续执行程序,如果后续执行代码还有断点还会暂停;
-
单步执行,就相当于一行一行执行代码(下面这俩是干嘛的?):
- Step over instruction (hold Control)
- Step over thread (hold Control-Shift)
-
跳进函数实现,比如图1调用了一个
test
方法,看看内部的一个实现逻辑,点击后就会跳转到test
方法的实现内图2;如果要查看二进制文件里的方法实现,需要有二进制的源码文件,对应的源码文件路径还要与编译时的路径保持一致,详细资料可参考 美团 iOS 工程 zsource 命令;- Step over instruction (hold Control),可以窥探系统内部调用逻辑,需要懂点汇编知识。
- Step over thread (hold Control-Shift),正常情况下感觉和上面的没什么区别。
图1:
图2:
-
跳出函数,图3为先跳进
test
函数后,在点击跳出函数,回到touchesBegan
后会有一条-[KKObject test]
的输出。
图3:
-
可以查看UI界面,查看一些UI层级或者位置偏移的问题,进行UI调试要比以前方便多了;
-
当前内存分析;
-
可以调试一些UI项,比如Dark Mode,文字大小;
-
更改定位信息;
-
查看当前APP内(进程内)的所有线程信息;
-
查看当前线程的调用栈,可以进行切换,查看历史数据;
整个程序的BreakPoint
-
Swift Error BreakPoint:可以捕捉Swift抛出的错误。Swift Error and Exception Breakpoints
-
Exception BreakPoint:可以分别捕捉Objective-C、C++的异常。
- ALL:所有异常。
- Objective-C:仅限于OC的异常。
- C++:仅限于C++的异常。要在特定的 C++ 异常上停止,必须指定异常名称。
-
Symbolic BreakPoint,添加符号断点。比如要看UILabel设置text属性添加断点:
- Symbol:可直接填写
Selector
,例如:setText:
,如果要精确到某个类下面的方法,格式为:[Class Selector]
,例如:[UILabel setText:]
; - Module:所在的库,例如UILabel是在UIKit里,此处可填写
UIKit
,当然也可以省略;
- Symbol:可直接填写
-
OPENGL ES Error BreakPoint:可以捕捉OPENGL ES抛出的错误;
-
Runtime Issue BreakPoint:可以捕捉程序运行时的错误。下面是官方的详细说明:
Xcode具有名为sanitizers的工具来检测几种不同类型的运行时问题:
- 非主线程更新UI;
- 非线程安全的情况下更新变量;
- 访问坏(不安全的)地址(accessing addresses unsafely);
- 执行未定义行为结果的代码(executing code that results in undefined behavior);
-
- 尝试除以零
-
- 尝试从未对齐的指针加载内存
-
- 尝试取消引用 NULL 指针
-
- 导致整数溢出的数学运算
可以在scheme配置中开启sanitizers工具,就可以在静态分析是检测到这些问题。当禁用了sanitizers工具,如果程序遇到以上任意一个问题,会产生崩溃,此时Xcode可能无法清楚地确定问题发生的位置。
下面是一个尝试除以零的案例,如果不同时配置Runtime Issue BreakPoint和scheme中的Undefined Behavior Sanitizer,会直接报错,如果同时配置了回先捕获到异常,执行过后才会报错,这样就可以保留异常现场的数据了。
-
Constraint Error BreakPoint:自动布局约束错误断点,其可以帮你快速定位自动布局错误;
-
Test Failure BreakPoint:单元测试/UI测试失败断点,当测试失败时会直接暂时程序,其可以帮你快速定位测试失败原因
取消删除BreakPoint
在多个源代码文件中设置了多个断点时,单击主窗口导航区中的 Breakpoint Navigator 按钮打开 Breakpoint navigator 以查看和管理所有断点。
单击断点导航器中的断点标签可快速导航到源代码编辑器中的断点。选择断点标签后按 Delete 键将从您的代码中删除该断点。单击断点导航器中的断点图标以启用或禁用它。
配置BreakPoint
- 指定暂停断点的条件。对于在一定迭代次数后出现的bug,或者在需要重复操作的有限条件下,在断点处暂停并重复按Continue按钮直到出现bug是很麻烦的。有两种方法可以在调试器中更有效地处理此类情况。
- 对于迭代一定次数后出现的bug,设置调试器忽略某些迭代的断点。按住 Control 键点按断点,选择“编辑断点”,然后指定在停止前忽略断点的次数。
- 对于在有限条件下发生的错误,将调试器设置为在表达式为真时在断点处暂停。按住 Control 键单击断点,选择“编辑断点”,然后使用局部作用域中可用的变量输入条件。
调试器每次在执行中到达断点时都会对表达式求值,并且仅在表达式为真时才暂停。
2.配置断点的动作。不要编写代码来记录变量值和有关应用程序执行的详细信息,而是使用断点操作来记录消息并执行将变量值打印到控制台的调试器命令。比如有一个循环,需要查看里面的每一个变量,此时就可以在当前变量后添加一个断点,然后编辑断点添加Action,执行po操作。就不用在使用NSLog了。
断点操作还可以在调试器到达断点时播放声音,这对于了解代码何时执行而不暂停很有用。断点操作可以执行 AppleScripts 或 shell 脚本来执行有用的调试任务,例如截取屏幕截图或保存一些应用程序数据以供分析。
要使用断点执行操作,请在源代码编辑器或断点导航器中按住 Control 键单击断点,选择“编辑断点”,单击“添加操作”,选择一个操作并提供任何必要的附加信息。例如,为日志消息操作提供消息,或为调试器命令操作提供命令和参数。
要在断点处执行多个action,请单击现有操作右侧的添加按钮 (+) 以添加另一个操作。要在执行action后继续执行而不暂停,可以勾选“Automatically continue after evaluating actions”选项。
LLDB调试
常用的LLDB命令
这里只列出了日常开发常用的调试命令,如果想学习更多其他命令,可详细阅读The LLDB Debugger、About LLDB and Xcode
完整命令 | 简写命令 | 描述 |
---|---|---|
expr -O — [SomeClass returnAnObject] | po | 打印objct的description,也可用于执行代码 |
expression | e | 可以改更某一个变量的值,也可以也可用于执行代码 |
thread backtrace | bt | 显示当前线程的堆栈回溯跟踪 |
thread backtrace all | bt all | 显示所有线程的堆栈回溯跟踪 |
thread backtrace -c 5 | bt 5 | 显示当前线程前5个的堆栈回溯跟踪 |
run | r | 启动进程,可以替代command + r |
breakpoint set –name “-[NSString stringWithFormat:]” | b -[NSString stringWithFormat:] | 给一个OC中[NSString stringWithFormat:]方法添加断点 |
breakpoint set –selector count | br s -S count | 给OC中所有count方法添加断点,这样也会给C、C++中的 count添加断点 |
breakpoint set –regex regular-expression | br s -r regular-expression | 通过正则表达式给一个方法加断点 |
watchpoint set variable var | wa s v var | 给变量var添加一个观察断点,当值变更时可以触发 |
watch set var global | 给观察断点设置一个条件第一步 | |
watchpoint modify -c ‘(global==5)’ | 给观察断点设置一个条件第二步 | |
frame variable | fr v | 查看当前frame下所有的变量 |
frame variable –no-args | fr v -a | 查看当前frame下局部变量 |
frame select 12 | f 12 | 查看当前线程第12frame |
frame info | – | 查看当前线程选中的frame |
register read | – | 查看当前线程寄存器 |
memory read –size 4 –format x –count 4 0xbffff3c0 | x/4xw 0xbffff3c0 | 从地址 0xbffff3c0 读取内存并显示四个十六进制 uint32_t 值。 |
image list | – | 列出主要的可执行文件和所有相关的共享库 |
image lookup –address 0x1ec4 | im loo -a 0x1ec4 | 在可执行文件或任何共享库中查找原始地址的信息 |
LLDB打印内存的值
lldb中可以使用x
命令来打印内存的值,格式为x/nfu <addr>
(后面学习内存相关内容可以用到,所以在这里总结一下。)
- x:是 examine 的缩写
- n:输出单元的个数。
- f:是输出格式。
x
以16进制形式输出d
以10进制形式输出u
以无符号10进制形式输出o
以8进制形式输出t
以2进制形式输出a
以16进制形式输出i
指令格式地址c
以字符形式输出f
以浮点数格式输出
- u:标明一个单元的长度。
b
是一个byte
,一个字节h
是两个byte
(halfword)w
是四个byte
(word)g
是八个byte
(giant word)
参考链接:
- BreakPoint相关
- LLDB
- 其他