目录
一、Breakpoint(断点)调试
1.Edit Breakpoint
(1)Condition
(2)Ignore
(3)action
(4)Options
2.Disable Breakpoint
3.Delete Breakpoint
二、lldb命令
三、lldb命令详细使用
1、Breakpoint
2、process
3、thread
4、frame
5、call
6、image
7、memory查看内存
8、为命令设置别名
四、Swift Error Breakpoint(Swift语言的断点)
五、Exception Breakpoint(全局断点(OC 和 C++语言))
六、OpenGL ES Error Breakpoint
七、Symbolic Breakpoint(符号断点)
1.Symbol
2.Module
六、Constraint Error Breakpoint
八、Test Failure Breakpoint
九、设置NSZombieEnabled、MallocStackLogging、NSAutoreleaseFreedObjectCheckEnabled、NSDebugEnabled
第一种设置方法
第二种设置方法:
苹果官网:传送门
lldb官网:传送门
Xcode断点分以下几种:
点击xcode左下角➕号:
可以看到断点类型:
看这6种断点之前,先来介绍一下,如何编辑断点:
根据需要加断点,鼠标在断点上右键:
可以看到上图四种类型:
点击Edit Breakpoint后(使用比较频繁):
可以看到有4种操作条件:
可以输入一个条件表达式,如果条件满足了,就会触发断点,例如:
执行for循环的时候,只会在value的值为@“b”的时候才会触发断点
使用比较频繁,往往在处理数据时for循环找到指定对象时用到,当然还有其他用法,因场景而异。
设置忽略断点的次数,比如设置2,,在上述例子中,会忽略前两次for循环,而在第三次时触发断点
点击Action
默认是Debugger Command,点击会有
会看到有6中类型:
功能Debugger Command和Log Message比较常用
%B输出断点的名称
%H输出断点执行的次数
@@之间输入表达式,比如(@5*2@ 会输出结果10,也可以输出对象结果)
勾选Log message to console,会在控制台输出结果
勾选Automatically continue after evaluating actions之后程序会在断点产生后继续运行。一般用于断点处修改值,或者输出断点信息时会勾选此选项,运行时,断点生效,但是断点处不停。如图,执行for循环结束,断点处并没有停留。
点击Disable Breakpoint后会使断点失效,但断点依然保留,颜色会边浅色(快速使断点失效方法:鼠标单击断点)。
再次鼠标在断点处右键:
点击Enable Breakpoint断点重新生效(快速使断点生效方法:鼠标单击断点)。
点击Delete Breakpoint删除断点(快速删除断点方法:鼠标左建按住断点拖走松开鼠标,即删除断点)。
点击Reveal in Breakpoint Navigator,会切换到导航断点处
以上断点的操作都可以在导航断点处进行相关操作:
多出来的Share Breakpoint和Move Breakpoint基本用不上,有兴趣的可以试试。
上面介绍了在edit breakpoint中的ation可以输入lldb命令,那么lldb到底有那些命令呢,在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.
statistics -- Print statistics about a debugging session
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.
下面介绍几个命令:
p - 基础数据类型打印
call - 执行一段代码 比如
bt - 显示当前线程的调用堆栈。bt all 显示所有线程堆栈信息
q - 退出断点
e - 设置常量,方便接下来调试用(注意: 声明和使用时对象名称前要加$):
image-由于image跟崩溃调试有关,下面介绍
script - 进入 python 脚本程序
br / breakpoint list - 列出所有断点
thread backtrace - 获取当前线程的调用栈
thread list - 列出所有的线程
target stop-hook :
1.在每次 stop 的时候去执行一些命令;例如:
(lldb) target stop-hook add -o "frame variable”
在每次 stop 时,执行 frame variable 命令
2.target stop-hook list: 列出 hook 的所有断点
3.target stop-hook del 编号: 删除 hook 的断点
break num- 在指定的行上设置断点
clear -删除设置在特定源文件、特定行上的断点。其用法为:clear FILENAME:NUM
continue - 继续执行正在调试的程序。该命令用在程序由于处理信号或断点而导致停止运行时
display EXPR -每次程序停止后显示表达式的值。表达式由程序定义的变量组成。
file FILE -装载指定的可执行文件进行调试。
help NAME -显示指定命令的帮助信息。
info break -显示当前断点清单,包括到达断点处的次数等。
info files -显示被调试文件的详细信息。
info func -显示所有的函数名称。
info local -显示当函数中的局部变量信息。
info prog -显示被调试程序的执行状态。
info var -显示所有的全局和静态变量名称。
kill -终止正被调试的程序。
list -显示源代码段。
make -在不退出 gdb 的情况下运行 make 工具。
next -在不单步执行进入其他函数的情况下,向前执行一行源代码。
print EXPR -显示表达式 EXPR 的值。
print-object -打印一个对象
print (int) name -打印一个类型
print-object [artist description] -调用一个函数
set artist = @"test" -设置变量值
whatis -查看变亮的数据类型
1.给某个文件的某一行下断点。可以使用如下两种方法,比如我想给Foo.m
文件的26行下一个断点。可以使用如下的方法。
(lldb) breakpoint set --file ViewController.m --line 80
出现如下提示则设置成功:
Breakpoint 6: where = TestDemo`-[ViewController click:] + 47 at ViewController.m:80, address = 0x00000001028b899f
可简写为:
(lldb) breakpoint set -f ViewController.m -l 80
2.直接给某个函数下断点
(lldb) breakpoint set --name viewDidLoad
简写:
(lldb) breakpoint set -n viewDidLoad
3.也可以在一次命令中为多个函数下断点:
(lldb) breakpoint set --name viewDidLoad --name viewDidLayoutSubviews
4.给C函数下断点:
(lldb) breakpoint set --method cFoo
简写:(M需要大写)
(lldb) breakpoint set -M cFoo
5.给OC方法下断点:
(lldb) breakpoint set --selector ocFoo
简写:(S需大写)
(lldb) breakpoint set -S ocFoo
6.使用正则,匹配你要打断点的函数(这个不限语言)
(lldb) breakpoint set -r cFoo
(lldb) breakpoint set -r ocFoo
7.指定加载的动态库
(lldb) breakpoint set --shlib foo.dylib --name foo
(lldb) breakpoint set -s foo.dylib -n foo
简写:
(lldb) breakpoint set -n "-[Foo foo]"
(lldb) br s -n "-[Foo foo]"
8.查看所有断点:
(lldb) breakpoint list
结果:
Current breakpoints:
1: name = 'LAYOUT_CONSTRAINTS_NOT_SATISFIABLE', locations = 0 (pending)
2: names = {'objc_exception_throw', '__cxa_throw'}, locations = 2, resolved = 2, hit count = 1
2.1: where = libobjc.A.dylib`objc_exception_throw, address = 0x00000001031df705, resolved, hit count = 1
2.2: where = libc++abi.dylib`__cxa_throw, address = 0x000000010618d089, resolved, hit count = 0
3: file = 'ViewController.m', line = 50, exact_match = 0, locations = 1, resolved = 1, hit count = 0
3.1: where = TestDemo`-[ViewController click:] + 47 at ViewController.m:80, address = 0x00000001028b899f, resolved, hit count = 0
4: file = 'ViewController.m', line = 110, exact_match = 0, locations = 0 (pending)
5: file = 'ViewController.m', line = 50, exact_match = 0, locations = 1, resolved = 1, hit count = 0
5.1: where = TestDemo`-[ViewController click:] + 47 at ViewController.m:80, address = 0x00000001028b899f, resolved, hit count = 0
6: file = 'ViewController.m', line = 80, exact_match = 0, locations = 1, resolved = 1, hit count = 0
6.1: where = TestDemo`-[ViewController click:] + 47 at ViewController.m:80, address = 0x00000001028b899f, resolved, hit count = 0
9.打印追踪轨迹、add、delete等,可以直接使用help命令,不用记。如果需要查看更详细的命令用途,使用help
.比如查看add命令用法:
(lldb) help break command add
Add LLDB commands to a breakpoint, to be executed whenever the breakpoint
is hit. If no breakpoint is specified, adds the commands to the last
created breakpoint.
Syntax: breakpoint command add []
Command Options Usage:
breakpoint command add [-D] [-o ] [-e ] [-s ] []
breakpoint command add [-D] [-e ] [-s ] [-F ] []
-D ( --dummy-breakpoints )
Sets Dummy breakpoints - i.e. breakpoints set before a file is
provided, which prime new targets.
-F ( --python-function )
Give the name of a Python function to run as command for this
breakpoint. Be sure to give a module name if appropriate.
-e ( --stop-on-error )
Specify whether breakpoint command execution should terminate on
error.
-o ( --one-liner )
Specify a one-line breakpoint command inline. Be sure to surround
it with quotes.
-s ( --script-type )
Specify the language for the commands - if none is specified, the
lldb command interpreter will be used.
Values: command | python | default-script
General information about entering breakpoint commands
------------------------------------------------------
This command will prompt for commands to be executed when the specified
breakpoint is hit. Each command is typed on its own line following the '> '
prompt until 'DONE' is entered.
Syntactic errors may not be detected when initially entered, and many malformed
commands can silently fail when executed. If your breakpoint commands do not
appear to be executing, double-check the command syntax.
Note: You may enter any debugger command exactly as you would at the debugger
prompt. There is no limit to the number of commands supplied, but do NOT enter
more than one command per line.
Special information about PYTHON breakpoint commands
----------------------------------------------------
You may enter either one or more lines of Python, including function
definitions or calls to functions that will have been imported by the time the
code executes. Single line breakpoint commands will be interpreted 'as is'
when the breakpoint is hit. Multiple lines of Python will be wrapped in a
generated function, and a call to the function will be attached to the
breakpoint.
This auto-generated function is passed in three arguments:
frame: an lldb.SBFrame object for the frame which hit breakpoint.
bp_loc: an lldb.SBBreakpointLocation object that represents the breakpoint
location that was hit.
dict: the python session dictionary hit.
When specifying a python function with the --python-function option, you need
to supply the function name prepended by the module name:
--python-function myutils.breakpoint_callback
The function itself must have the following prototype:
def breakpoint_callback(frame, bp_loc, dict):
# Your code goes here
The arguments are the same as the arguments passed to generated functions as
described above. Note that the global variable 'lldb.frame' will NOT be
updated when this function is called, so be sure to use the 'frame' argument.
The 'frame' argument can get you to the thread via frame.GetThread(), the
thread can get you to the process via thread.GetProcess(), and the process can
get you back to the target via process.GetTarget().
Important Note: As Python code gets collected into functions, access to global
variables requires explicit scoping using the 'global' keyword. Be sure to use
correct Python syntax, including indentation, when entering Python breakpoint
commands.
Example Python one-line breakpoint command:
(lldb) breakpoint command add -s python 1
Enter your Python command(s). Type 'DONE' to end.
> print "Hit this breakpoint!"
> DONE
As a convenience, this also works for a short Python one-liner:
(lldb) breakpoint command add -s python 1 -o 'import time; print time.asctime()'
(lldb) run
Launching '.../a.out' (x86_64)
(lldb) Fri Sep 10 12:17:45 2010
Process 21778 Stopped
* thread #1: tid = 0x2e03, 0x0000000100000de8 a.out`c + 7 at main.c:39, stop
reason = breakpoint 1.1, queue = com.apple.main-thread
36
37 int c(int val)
38 {
39 -> return val + 3;
40 }
41
42 int main (int argc, char const *argv[])
Example multiple line Python breakpoint command:
(lldb) breakpoint command add -s p 1
Enter your Python command(s). Type 'DONE' to end.
> global bp_count
> bp_count = bp_count + 1
> print "Hit this breakpoint " + repr(bp_count) + " times!"
> DONE
Example multiple line Python breakpoint command, using function definition:
(lldb) breakpoint command add -s python 1
Enter your Python command(s). Type 'DONE' to end.
> def breakpoint_output (bp_no):
> out_string = "Hit breakpoint number " + repr (bp_no)
> print out_string
> return True
> breakpoint_output (1)
> DONE
In this case, since there is a reference to a global variable, 'bp_count', you
will also need to make sure 'bp_count' exists and is initialized:
(lldb) script
>>> bp_count = 0
>>> quit()
Your Python code, however organized, can optionally return a value. If the
returned value is False, that tells LLDB not to stop at the breakpoint to which
the code is associated. Returning anything other than False, or even returning
None, or even omitting a return statement entirely, will cause LLDB to stop.
Final Note: A warning that no breakpoint command was generated when there are
no syntax errors may indicate that a function was declared but never called.
This command takes options and free-form arguments. If your arguments
resemble option specifiers (i.e., they start with a - or --), you must use
' -- ' between the end of the command options and the beginning of the
arguments.
(lldb)
可以看到其实这里面的命令大部分是Python脚本。
10.删除断点
breakpoint delete [-Df] []
具体操作:
(1)先通过breakpoint list
查我的进程:
(lldb) breakpoint list
Current breakpoints:
1: name = 'LAYOUT_CONSTRAINTS_NOT_SATISFIABLE', locations = 0 (pending)
2: names = {'objc_exception_throw', '__cxa_throw'}, locations = 2, resolved = 2, hit count = 1
2.1: where = libobjc.A.dylib`objc_exception_throw, address = 0x00000001031df705, resolved, hit count = 1
2.2: where = libc++abi.dylib`__cxa_throw, address = 0x000000010618d089, resolved, hit count = 0
3: file = 'ViewController.m', line = 50, exact_match = 0, locations = 1, resolved = 1, hit count = 0
3.1: where = TestDemo`-[ViewController click:] + 47 at ViewController.m:80, address = 0x00000001028b899f, resolved, hit count = 0
4: file = 'ViewController.m', line = 110, exact_match = 0, locations = 0 (pending)
5: file = 'ViewController.m', line = 50, exact_match = 0, locations = 1, resolved = 1, hit count = 0
5.1: where = TestDemo`-[ViewController click:] + 47 at ViewController.m:80, address = 0x00000001028b899f, resolved, hit count = 0
6: file = 'ViewController.m', line = 80, exact_match = 0, locations = 1, resolved = 1, hit count = 0
6.1: where = TestDemo`-[ViewController click:] + 47 at ViewController.m:80, address = 0x00000001028b899f, resolved, hit count = 0
(lldb)
若果我要删除5.1断点我就使用breakpoint delete 5.1
,如果我要删除5下面的所有断点,使用breakpoint delete 5
,这样5.1和5.2都会删除:
(lldb) breakpoint delete 5.1
0 breakpoints deleted; 1 breakpoint locations disabled.
(lldb)
删除所有断点:
(lldb) breakpoint delete
About to delete all breakpoints, do you want to do that?: [Y/n]
输入Y,则删除所有断点:
(lldb) breakpoint delete
About to delete all breakpoints, do you want to do that?: [Y/n] y
All breakpoints removed. (6 breakpoints)
(lldb)
11.watchpoint
使用watchpoint来监视任意一段内存的读写.
比如我需要观察某个变量a
的值变化,我可以使用如下命令:
(lldb) watchpoint set variable a
(lldb) watchpoint set variable a
Watchpoint created: Watchpoint 1: addr = 0x7ffeee251830 size = 8 state = enabled type = w
declare @ '/Users/liuzhao/Documents/刘召/demo/TestDemo/TestDemo/ViewController.m:110'
watchpoint spec = 'a'
new value: 10
(lldb)
添加一个更a的for循环:
在log处添加添加观察
然后就可以设置在a的值变化为某个特定值之后触发断点
(lldb) watchpoint modify -c '(a=15)'
继续执行代码,则自动停在for循环时a的值为15处:
这个时候可以看一下具体断点的参数,使用watchpoint list
命令:
(lldb) watchpoint list
Number of supported hardware watchpoints: 4
Current watchpoints:
Watchpoint 1: addr = 0x7ffee27e6830 size = 8 state = enabled type = w
declare @ '/Users/liuzhao/Documents/刘召/demo/TestDemo/TestDemo/ViewController.m:110'
watchpoint spec = 'a'
old value: 10
new value: 15
condition = '(a=15)'
(lldb)
可以看到变量的地址0x7ffee27e6830,声明变量的代码在第110行,已经具体的变量名是a
,当前的值是10,触发条件时的a的值15,触发的条件是'(a=15)'
然后我们c命令,就可以看到断点到a的值变为15的地方
(lldb) c
Process 7179 resuming
Watchpoint 1 hit:
old value: 15
new value: 15
(lldb)
可以看到这个地方a的值已经改变为15。我们可以再使用watchpoint list
命令看看具体值的变化:
(lldb) watchpoint list
Number of supported hardware watchpoints: 4
Current watchpoints:
Watchpoint 1: addr = 0x7ffee27e6830 size = 8 state = enabled type = w
declare @ '/Users/liuzhao/Documents/刘召/demo/TestDemo/TestDemo/ViewController.m:110'
watchpoint spec = 'a'
old value: 15
new value: 15
condition = '(a=15)'
(lldb)
当然我们还可以追踪程序运行的过程,使用bt命令:
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = watchpoint 1
* frame #0: 0x000000010d415cc3 TestDemo`-[ViewController viewDidLoad](self=0x00007fbf27614350, _cmd="viewDidLoad") at ViewController.m:113
frame #1: 0x00000001139a0781 UIKitCore`-[UIViewController loadViewIfRequired] + 1186
frame #2: 0x000000011355c4d0 UIKitCore`-[UINavigationController _updateScrollViewFromViewController:toViewController:] + 68
frame #3: 0x000000011355c7c4 UIKitCore`-[UINavigationController _startTransition:fromViewController:toViewController:] + 147
frame #4: 0x000000011355d857 UIKitCore`-[UINavigationController _startDeferredTransitionIfNeeded:] + 896
frame #5: 0x000000011355ebac UIKitCore`-[UINavigationController __viewWillLayoutSubviews] + 150
frame #6: 0x00000001134ea63f UIKitCore`-[UILayoutContainerView layoutSubviews] + 217
frame #7: 0x000000011398b015 UIKitCore`-[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1441
frame #8: 0x0000000115297d3d QuartzCore`-[CALayer layoutSublayers] + 175
frame #9: 0x000000011529cbf7 QuartzCore`CA::Layer::layout_if_needed(CA::Transaction*) + 395
frame #10: 0x0000000115215aa6 QuartzCore`CA::Context::commit_transaction(CA::Transaction*) + 342
frame #11: 0x000000011524cc2a QuartzCore`CA::Transaction::commit() + 576
frame #12: 0x000000011328cd4c UIKitCore`__34-[UIApplication _firstCommitBlock]_block_invoke_2 + 139
frame #13: 0x000000010ecaca3c CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 12
frame #14: 0x000000010ecac1f0 CoreFoundation`__CFRunLoopDoBlocks + 336
frame #15: 0x000000010eca6a64 CoreFoundation`__CFRunLoopRun + 1284
frame #16: 0x000000010eca6221 CoreFoundation`CFRunLoopRunSpecific + 625
frame #17: 0x00000001173f51dd GraphicsServices`GSEventRunModal + 62
frame #18: 0x0000000113272115 UIKitCore`UIApplicationMain + 140
frame #19: 0x000000010d416490 TestDemo`main(argc=1, argv=0x00007ffee27ea370) at main.m:14
frame #20: 0x0000000110e6a551 libdyld.dylib`start + 1
(lldb)
我们还可以使用frame命令查看变量a的具体值:
(lldb) frame variable a
(NSInteger) a = 15
(lldb)
watchpoint list命令包括了三个可选参数:(使用help命令)
(lldb) help watchpoint list
List all watchpoints at configurable levels of detail.
Syntax: watchpoint list []
Command Options Usage:
watchpoint list [-b] []
watchpoint list [-f] []
watchpoint list [-v] []
-b ( --brief )
Give a brief description of the watchpoint (no location info).
-f ( --full )
Give a full description of the watchpoint and its locations.
-v ( --verbose )
Explain everything we know about the watchpoint (for debugging
debugger bugs).
This command takes options and free-form arguments. If your arguments
resemble option specifiers (i.e., they start with a - or --), you must use
' -- ' between the end of the command options and the beginning of the
arguments.
(lldb)
watchpoint list
,默认的是 watchpoint list -f;
首先我们先用help命令来看看:(help在前在后都行)
(lldb) help process
Commands for interacting with processes on the current platform.
Syntax: process []
The following subcommands are supported:
attach -- Attach to a process.
connect -- Connect to a remote debug service.
continue -- Continue execution of all threads in the current process.
detach -- Detach from the current target process.
handle -- Manage LLDB handling of OS signals for the current target
process. Defaults to showing current policy.
interrupt -- Interrupt the current target process.
kill -- Terminate the current target process.
launch -- Launch the executable in the debugger.
load -- Load a shared library into the current process.
plugin -- Send a custom command to the current target process plug-in.
save-core -- Save the current process as a core file using an appropriate
file type.
signal -- Send a UNIX signal to the current target process.
status -- Show status and stop location for the current target process.
unload -- Unload a shared library from the current process using the
index returned by a previous call to "process load".
For more help on any particular subcommand, type 'help '.
(lldb)
查看更详细的命令使用help
。比如
(lldb) help process attach
结果:
(lldb) help process attach
Attach to a process.
Syntax: process attach
Command Options Usage:
process attach [-c] [-P ] [-p ]
process attach [-ciw] [-P ] [-n ]
-P ( --plugin )
Name of the process plugin you want to use.
-c ( --continue )
Immediately continue the process once attached.
-i ( --include-existing )
Include existing processes when doing attach -w.
-n ( --name )
The name of the process to attach to.
-p ( --pid )
The process ID of an existing process to attach to.
-w ( --waitfor )
Wait for the process with to launch.
(lldb)
process命令在我目前日常开发中其实不怎么使用。
我们可以使用thread命令来做一些断点的操作,使用thread help
来看看有些什么命令:
(lldb) thread help
Commands for operating on one or more threads in the current process.
Syntax: thread []
The following subcommands are supported:
backtrace -- Show thread call stacks. Defaults to the current
thread, thread indexes can be specified as
arguments.
Use the thread-index "all" to see all threads.
Use the thread-index "unique" to see threads grouped by
unique call stacks.
continue -- Continue execution of the current target process. One
or more threads may be specified, by default all
threads continue.
info -- Show an extended summary of one or more threads.
Defaults to the current thread.
jump -- Sets the program counter to a new address.
list -- Show a summary of each thread in the current target
process.
plan -- Commands for managing thread plans that control
execution.
return -- Prematurely return from a stack frame, short-circuiting
execution of newer frames and optionally yielding a
specified value. Defaults to the exiting the current
stack frame. Expects 'raw' input (see 'help
raw-input'.)
select -- Change the currently selected thread.
step-in -- Source level single step, stepping into calls.
Defaults to current thread unless specified.
step-inst -- Instruction level single step, stepping into calls.
Defaults to current thread unless specified.
step-inst-over -- Instruction level single step, stepping over calls.
Defaults to current thread unless specified.
step-out -- Finish executing the current stack frame and stop after
returning. Defaults to current thread unless specified.
step-over -- Source level single step, stepping over calls.
Defaults to current thread unless specified.
step-scripted -- Step as instructed by the script class passed in the -C
option.
until -- Continue until a line number or address is reached by
the current or specified thread. Stops when returning
from the current function as a safety measure. The
target line number(s) are given as arguments, and if
more than one is provided, stepping will stop when the
first one is hit.
For more help on any particular subcommand, type 'help '.
(lldb)
step-开头的这几个命令比较常用:
继续执行:process continue, continue, c
这样就是xcode可视化工具的“暂停”“下一步”“进入”“跳出”“继续”。
1.检查当前进程的状态:
(lldb) thread list
Process 7179 stopped
* thread #1: tid = 0x881108, 0x000000010d415cc3 TestDemo`-[ViewController viewDidLoad](self=0x00007fbf27614350, _cmd="viewDidLoad") at ViewController.m:113, queue = 'com.apple.main-thread', stop reason = watchpoint 1
thread #2: tid = 0x881138, 0x000000011118dbfe libsystem_kernel.dylib`__workq_kernreturn + 10
thread #5: tid = 0x88113b, 0x00000001111e23f0 libsystem_pthread.dylib`start_wqthread
thread #6: tid = 0x881140, 0x000000011118c22a libsystem_kernel.dylib`mach_msg_trap + 10, name = 'com.apple.uikit.eventfetch-thread'
(lldb)
可以看到当前在主线程中(com.apple.main-thread),程序停止原因时因为给a添加了watchpoint,并停在了watchpoint 1,程序停止在113行。在viewDidLoad方法中,当前self的地址:0x00007fbf27614350
注意:*
表明的就是当前的线程。
2.可以使用backtrace命令进行追踪线程:
(lldb) thread backtrace
* thread #1, queue = 'com.apple.main-thread', stop reason = watchpoint 1
* frame #0: 0x000000010d415cc3 TestDemo`-[ViewController viewDidLoad](self=0x00007fbf27614350, _cmd="viewDidLoad") at ViewController.m:113
frame #1: 0x00000001139a0781 UIKitCore`-[UIViewController loadViewIfRequired] + 1186
frame #2: 0x000000011355c4d0 UIKitCore`-[UINavigationController _updateScrollViewFromViewController:toViewController:] + 68
frame #3: 0x000000011355c7c4 UIKitCore`-[UINavigationController _startTransition:fromViewController:toViewController:] + 147
frame #4: 0x000000011355d857 UIKitCore`-[UINavigationController _startDeferredTransitionIfNeeded:] + 896
frame #5: 0x000000011355ebac UIKitCore`-[UINavigationController __viewWillLayoutSubviews] + 150
frame #6: 0x00000001134ea63f UIKitCore`-[UILayoutContainerView layoutSubviews] + 217
frame #7: 0x000000011398b015 UIKitCore`-[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1441
frame #8: 0x0000000115297d3d QuartzCore`-[CALayer layoutSublayers] + 175
frame #9: 0x000000011529cbf7 QuartzCore`CA::Layer::layout_if_needed(CA::Transaction*) + 395
frame #10: 0x0000000115215aa6 QuartzCore`CA::Context::commit_transaction(CA::Transaction*) + 342
frame #11: 0x000000011524cc2a QuartzCore`CA::Transaction::commit() + 576
frame #12: 0x000000011328cd4c UIKitCore`__34-[UIApplication _firstCommitBlock]_block_invoke_2 + 139
frame #13: 0x000000010ecaca3c CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 12
frame #14: 0x000000010ecac1f0 CoreFoundation`__CFRunLoopDoBlocks + 336
frame #15: 0x000000010eca6a64 CoreFoundation`__CFRunLoopRun + 1284
frame #16: 0x000000010eca6221 CoreFoundation`CFRunLoopRunSpecific + 625
frame #17: 0x00000001173f51dd GraphicsServices`GSEventRunModal + 62
frame #18: 0x0000000113272115 UIKitCore`UIApplicationMain + 140
frame #19: 0x000000010d416490 TestDemo`main(argc=1, argv=0x00007ffee27ea370) at main.m:14
frame #20: 0x0000000110e6a551 libdyld.dylib`start + 1
(lldb)
如果想看所有线程的backtrace,可以使用thread backtrace all
命令:
(lldb) thread backtrace all
* thread #1, queue = 'com.apple.main-thread', stop reason = watchpoint 1
* frame #0: 0x000000010d415cc3 TestDemo`-[ViewController viewDidLoad](self=0x00007fbf27614350, _cmd="viewDidLoad") at ViewController.m:113
frame #1: 0x00000001139a0781 UIKitCore`-[UIViewController loadViewIfRequired] + 1186
frame #2: 0x000000011355c4d0 UIKitCore`-[UINavigationController _updateScrollViewFromViewController:toViewController:] + 68
frame #3: 0x000000011355c7c4 UIKitCore`-[UINavigationController _startTransition:fromViewController:toViewController:] + 147
frame #4: 0x000000011355d857 UIKitCore`-[UINavigationController _startDeferredTransitionIfNeeded:] + 896
frame #5: 0x000000011355ebac UIKitCore`-[UINavigationController __viewWillLayoutSubviews] + 150
frame #6: 0x00000001134ea63f UIKitCore`-[UILayoutContainerView layoutSubviews] + 217
frame #7: 0x000000011398b015 UIKitCore`-[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1441
frame #8: 0x0000000115297d3d QuartzCore`-[CALayer layoutSublayers] + 175
frame #9: 0x000000011529cbf7 QuartzCore`CA::Layer::layout_if_needed(CA::Transaction*) + 395
frame #10: 0x0000000115215aa6 QuartzCore`CA::Context::commit_transaction(CA::Transaction*) + 342
frame #11: 0x000000011524cc2a QuartzCore`CA::Transaction::commit() + 576
frame #12: 0x000000011328cd4c UIKitCore`__34-[UIApplication _firstCommitBlock]_block_invoke_2 + 139
frame #13: 0x000000010ecaca3c CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 12
frame #14: 0x000000010ecac1f0 CoreFoundation`__CFRunLoopDoBlocks + 336
frame #15: 0x000000010eca6a64 CoreFoundation`__CFRunLoopRun + 1284
frame #16: 0x000000010eca6221 CoreFoundation`CFRunLoopRunSpecific + 625
frame #17: 0x00000001173f51dd GraphicsServices`GSEventRunModal + 62
frame #18: 0x0000000113272115 UIKitCore`UIApplicationMain + 140
frame #19: 0x000000010d416490 TestDemo`main(argc=1, argv=0x00007ffee27ea370) at main.m:14
frame #20: 0x0000000110e6a551 libdyld.dylib`start + 1
thread #2
frame #0: 0x000000011118dbfe libsystem_kernel.dylib`__workq_kernreturn + 10
frame #1: 0x00000001111e26e6 libsystem_pthread.dylib`_pthread_wqthread + 634
frame #2: 0x00000001111e23fd libsystem_pthread.dylib`start_wqthread + 13
thread #5
frame #0: 0x00000001111e23f0 libsystem_pthread.dylib`start_wqthread
thread #6, name = 'com.apple.uikit.eventfetch-thread'
frame #0: 0x000000011118c22a libsystem_kernel.dylib`mach_msg_trap + 10
frame #1: 0x000000011118c76c libsystem_kernel.dylib`mach_msg + 60
frame #2: 0x000000010ecac5c4 CoreFoundation`__CFRunLoopServiceMachPort + 212
frame #3: 0x000000010eca6bf9 CoreFoundation`__CFRunLoopRun + 1689
frame #4: 0x000000010eca6221 CoreFoundation`CFRunLoopRunSpecific + 625
frame #5: 0x000000010d7c1522 Foundation`-[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 277
frame #6: 0x000000010d7c16f4 Foundation`-[NSRunLoop(NSRunLoop) runUntilDate:] + 79
frame #7: 0x00000001131cc63f UIKitCore`-[UIEventFetcher threadMain] + 118
frame #8: 0x000000010d7d6932 Foundation`__NSThread__start__ + 1221
frame #9: 0x00000001111e32eb libsystem_pthread.dylib`_pthread_body + 126
frame #10: 0x00000001111e6249 libsystem_pthread.dylib`_pthread_start + 66
frame #11: 0x00000001111e240d libsystem_pthread.dylib`thread_start + 13
(lldb)
3.如果我们想单独查看某个线程,可以先使用thread select选择
某个具体的线程,然后再进行其他操作。
比如thread backtrace
操作步骤:
(1)使用thread list查看线程,就能看到线程的编号
(lldb) thread list
Process 7179 stopped
* thread #1: tid = 0x881108, 0x000000010d415cc3 TestDemo`-[ViewController viewDidLoad](self=0x00007fbf27614350, _cmd="viewDidLoad") at ViewController.m:113, queue = 'com.apple.main-thread', stop reason = watchpoint 1
thread #2: tid = 0x881138, 0x000000011118dbfe libsystem_kernel.dylib`__workq_kernreturn + 10
thread #5: tid = 0x88113b, 0x00000001111e23f0 libsystem_pthread.dylib`start_wqthread
thread #6: tid = 0x881140, 0x000000011118c22a libsystem_kernel.dylib`mach_msg_trap + 10, name = 'com.apple.uikit.eventfetch-thread'
(lldb)
可以看到线程编号有1、2、5、6,然后我们选择线程2进行追踪:
可以看到,xcode选中了thread #2这条线程,然后进行追踪,输入命令:thread backtrace 就可以看到thread 2的具体信息了。
整个操作过程如下:
(lldb) thread list
Process 7179 stopped
* thread #1: tid = 0x881108, 0x000000010d415cc3 TestDemo`-[ViewController viewDidLoad](self=0x00007fbf27614350, _cmd="viewDidLoad") at ViewController.m:113, queue = 'com.apple.main-thread', stop reason = watchpoint 1
thread #2: tid = 0x881138, 0x000000011118dbfe libsystem_kernel.dylib`__workq_kernreturn + 10
thread #5: tid = 0x88113b, 0x00000001111e23f0 libsystem_pthread.dylib`start_wqthread
thread #6: tid = 0x881140, 0x000000011118c22a libsystem_kernel.dylib`mach_msg_trap + 10, name = 'com.apple.uikit.eventfetch-thread'
(lldb) thread select 2
(lldb) thread backtrace
* thread #2
* frame #0: 0x000000011118dbfe libsystem_kernel.dylib`__workq_kernreturn + 10
frame #1: 0x00000001111e26e6 libsystem_pthread.dylib`_pthread_wqthread + 634
frame #2: 0x00000001111e23fd libsystem_pthread.dylib`start_wqthread + 13
(lldb)
4.thread return
执行thread return命令可以使得当前函数立即返回,即后续代码都不会执行了。当然执行此命令可能会使得arc的计数追踪出现错乱。
- (void)viewDidLoad {
[super viewDidLoad];
[self test];
}
- (void)test{
NSLog(@"return,该log不会输出");
NSLog(@"该行不会执行");
}
在第一个log处下个断点,运行代码:
执行return命令,则直接返回;
可以看的两个log并没有输出,而test函数在第一个log的地方直接return了;
如果方法有返回值,则thread return命令需要一个参数来指明函数强制返回时的返回值。
- (void)viewDidLoad {
[super viewDidLoad];
NSInteger value = [self test];
NSLog(@"%ld" ,value);
}
- (NSInteger)test{
NSLog(@"return,该log不会输出");
NSLog(@"该行不会执行");
return 10;
}
同样的在第一个log处添加一个断点:
执行return命令:
可以看的log没有输出,直接跳出test方法,我们继续执行代码:
可以看到,打印的value是20,而test方法里的两个log并没有输出;
frame
我们先看看help信息:
(lldb) help frame
Commands for selecting and examing the current thread's stack frames.
Syntax: frame []
The following subcommands are supported:
info -- List information about the current stack frame in the current
thread.
select -- Select the current stack frame by index from within the
current thread (see 'thread backtrace'.)
variable -- Show variables for the current stack frame. Defaults to all
arguments and local variables in scope. Names of argument,
local, file static and file global variables can be
specified. Children of aggregate variables can be specified
such as 'var->child.x'.
For more help on any particular subcommand, type 'help '.
(lldb)
可以看到这个命令比较少,只有三个,info、selelct、variable。
(1)variable
命令观测架构参数和本地变量
(lldb) frame variable
(ViewController *) self = 0x00007fc12fd10300
(SEL) _cmd = "viewDidLoad"
(NSInteger) a = 20
(lldb)
要打印某个变量需要在参数里面指定,比如要查看self:
(lldb) frame variable self
(ViewController *) self = 0x00007fc12fd10300
(lldb)
查看a:
(lldb) frame variable a
(NSInteger) a = 20
(lldb)
查看一些子元素:
(lldb) frame variable self->isa
(Class) self->isa = ViewController
(lldb)
命令虽然不是完整的表达式解释器,但是一些基本的操作 比如 &, *, ->, []还是可以的。比如指针(*):
(lldb) frame variable *self
(ViewController) *self = {
UIViewController = {
UIResponder = {
NSObject = {
isa = ViewController
}
}
_overrideTransitioningDelegate = nil
_view = 0x00007fc12fe100c0
_tabBarItem = nil
_navigationItem = 0x00007fc12fd106a0
_toolbarItems = nil
_title = nil
_nibName = 0x0000600000f22a30 @"BYZ-38-t0r-view-8bC-Xf-vdC"
_nibBundle = 0x00006000022794f0 @"/Users/liuzhao/Library/Developer/CoreSimulator/Devices/56D38A7C-DF1A-4264-8A36-744951DEE407/data/Containers/Bundle/Application/51C70C4B-C619-414A-B43A-195DB1AABD63/TestDemo.app"
_parentViewController = 0x00007fc130801a00
_childModalViewController = nil
_parentModalViewController = nil
_previousRootViewController = nil
_modalTransitionView = nil
_modalPreservedFirstResponder = nil
_dimmingView = nil
_dropShadowView = nil
_currentAction = nil
_storyboard = 0x000060000140d040
_externalObjectsTableForViewLoading = 0x000060000140a000
_topLevelObjectsToKeepAliveFromStoryboard = nil
_savedHeaderSuperview = nil
_savedFooterSuperview = nil
_editButtonItem = nil
_searchDisplayController = nil
_strongSearchDisplayController = nil
_modalTransitionStyle = 0
_modalPresentationStyle = 0
_lastKnownInterfaceOrientation = 0
_popoverController = nil
_containerViewInSheet = nil
_recordedContentScrollView = nil
_afterAppearance = nil
_explicitAppearanceTransitionLevel = 0
_interfaceBuilderKeyCommands = nil
_addedKeyCommands = nil
_overrideTraitCollectionsForChildren = nil
_previewSourceViews = nil
_retainCount = 10
_ignoreAppSupportedOrientations = false
_viewHostsLayoutEngine = false
_storyboardIdentifier = nil
_transitioningDelegate = nil
_frozenTraitCollection = nil
_overrideTraitCollection = nil
_accessibilityHUD = nil
overrideUseCustomPresentation = false
_modalPresentationCapturesStatusBarAppearance = false
_ignoresParentMargins = false
_childViewControllers = nil
_customNavigationInteractiveTransitionDuration = 0
_customNavigationInteractiveTransitionPercentComplete = 0
_customTransitioningView = nil
_lastNotifiedTraitCollection = nil
_presentationController = nil
_navigationControllerContentOffsetAdjustment = 0
_leftContentMargin = 0
_rightContentMargin = 0
_contentMargin = 20
_topLayoutGuide = nil
_bottomLayoutGuide = nil
_topBarInsetGuideConstraint = nil
_bottomBarInsetGuideConstraint = nil
_storyboardSegueTemplates = nil
_segueResponsibleForModalPresentation = nil
_sourceViewControllerIfPresentedViaPopoverSegue = nil
_modalSourceViewController = nil
_expectedWindow = nil
_presentedStatusBarViewController = nil
_presentedUserInterfaceStyleViewController = nil
_edgesForExtendedLayout = 15
__childControllerToIgnoreWhileLookingForTransitionCoordinator = nil
_presentingFocusedItem = nil
_navigationInsetAdjustment = nil
_storyboardPreviewSegueTemplates = nil
_storyboardCommitSegueTemplates = nil
_storyboardPreviewingRegistrants = nil
__embeddedView = nil
__embeddingView = nil
__embeddedDelegate = nil
_originalPresentationController = nil
_temporaryPresentationController = nil
_preferredFocusedItem = nil
}
_delegate = nil
_searchBar = 0x00007fc12fc11b10
_str = nil
}
(lldb)
和之前thread命令类似,我可以使用frame select
去选择另外的一个frame
同样的select后的编号怎么来呢,从help中可以看到,是info命令:
(lldb) frame info
frame #0: 0x0000000109efb07b TestDemo`-[ViewController viewDidLoad](self=0x00007fe149c0b760, _cmd="viewDidLoad") at ViewController.m:155
(lldb)
由于我这代码比较简单,只有一个frame信息,编号为#0,
接下来使用select命令:
(lldb) frame select 0
frame #0: 0x000000010832907b TestDemo`-[ViewController viewDidLoad](self=0x00007fcad3f0cd60, _cmd="viewDidLoad") at ViewController.m:138
135 //错误崩溃代码
136 // NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeNotAnAttribute relatedBy:NSLayoutAttributeTop toItem:self.view attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:100];
137
-> 138 [[4ms[0melf.view addConstraints:@[widthConstraint, heightConstraint, centerXConstraint, centerYConstraint ]];
139
140
141
(lldb)
可以看到frame 0的具体信息:
frame 0地址(0x000000010832907b)、self地址(0x00007fcad3f0cd60)、断点停的代码行数(138),->所指的就是断点所停的具体代码,执行时间4ms
当然我们也可以看到更复杂的数据我们使用expression命令,这个上面有介绍,这里就不赘述了。
一般可以用来调用不需要返回值的调试命令,这个命令很简单,没有其他命令,直接使用就行。
比如更改View的背景颜色:
call [self.view setBackgroundColor:[UIColor greenColor]]
当然po也能达到一样的作用:
po [self.view setBackgroundColor:[UIColor greenColor]]
平时基本都用po,很少用call
这个命令很实用,可用于寻址。比较实用的用法是用于寻找栈地址对应的代码位置。
先来看看help:
(lldb) image help
Commands for accessing information for one or more target modules.
Syntax: target modules ...
The following subcommands are supported:
add -- Add a new module to the current target's modules.
dump -- Commands for dumping information about one or more target
modules.
list -- List current executable and dependent shared library
images.
load -- Set the load addresses for one or more sections in a
target module.
lookup -- Look up information within executable and dependent
shared library images.
search-paths -- Commands for managing module search paths for a target.
show-unwind -- Show synthesized unwind instructions for a function.
For more help on any particular subcommand, type 'help '.
(lldb)
(1)lookup命令
lookup命令查找地址所对应的代码行数。
如下,是一个数组越界崩溃代码:
运行之后的崩溃日志:
2019-11-30 18:32:29.498717+0800 TestDemo[10523:8997027] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndexedSubscript:]: index 3 beyond bounds [0 .. 1]'
*** First throw call stack:
(
0 CoreFoundation 0x0000000101b6129b __exceptionPreprocess + 331
1 libobjc.A.dylib 0x0000000100b54735 objc_exception_throw + 48
2 CoreFoundation 0x0000000101aab8fc _CFThrowFormattedException + 194
3 CoreFoundation 0x0000000101be3b60 +[__NSArrayI allocWithZone:] + 0
4 TestDemo 0x000000010022e230 -[ViewController viewDidLoad] + 176
5 UIKitCore 0x0000000104bed781 -[UIViewController loadViewIfRequired] + 1186
6 UIKitCore 0x00000001047a94d0 -[UINavigationController _updateScrollViewFromViewController:toViewController:] + 68
7 UIKitCore 0x00000001047a97c4 -[UINavigationController _startTransition:fromViewController:toViewController:] + 147
8 UIKitCore 0x00000001047aa857 -[UINavigationController _startDeferredTransitionIfNeeded:] + 896
9 UIKitCore 0x00000001047abbac -[UINavigationController __viewWillLayoutSubviews] + 150
10 UIKitCore 0x000000010473763f -[UILayoutContainerView layoutSubviews] + 217
11 UIKitCore 0x0000000104bd8015 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1441
12 QuartzCore 0x00000001064fcd3d -[CALayer layoutSublayers] + 175
13 QuartzCore 0x0000000106501bf7 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 395
14 QuartzCore 0x000000010647aaa6 _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 342
15 QuartzCore 0x00000001064b1c2a _ZN2CA11Transaction6commitEv + 576
16 UIKitCore 0x00000001044d9d4c __34-[UIApplication _firstCommitBlock]_block_invoke_2 + 139
17 CoreFoundation 0x0000000101ac4a3c __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 12
18 CoreFoundation 0x0000000101ac41f0 __CFRunLoopDoBlocks + 336
19 CoreFoundation 0x0000000101abea64 __CFRunLoopRun + 1284
20 CoreFoundation 0x0000000101abe221 CFRunLoopRunSpecific + 625
21 GraphicsServices 0x000000010a1f11dd GSEventRunModal + 62
22 UIKitCore 0x00000001044bf115 UIApplicationMain + 140
23 TestDemo 0x000000010022e560 main + 112
24 libdyld.dylib 0x00000001035b3551 start + 1
25 ??? 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)
从上面的崩溃日志中可以看到:
4 TestDemo 0x000000010022e230 -[ViewController viewDidLoad] + 176
我们的崩溃的代码在viewDidLoad方法里(因为123都是系统库),再从:
*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndexedSubscript:]: index 3 beyond bounds [0 .. 1]'
可以看出是数组越界,那么怎么定位到崩溃的代码呢,使用lookup命令
(lldb) image lookup --address 0x000000010022e230
Address: TestDemo[0x0000000100002230] (TestDemo.__TEXT.__text + 1664)
Summary: TestDemo`-[ViewController viewDidLoad] + 176 at ViewController.m:117
(lldb)
再看看我们的错误代码,确实是117行:
先来看看help:
(lldb) help memory
Commands for operating on memory in the current target process.
Syntax: memory []
The following subcommands are supported:
find -- Find a value in the memory of the current target process.
history -- Print recorded stack traces for allocation/deallocation events
associated with an address.
read -- Read from the memory of the current target process.
region -- Get information on the memory region containing an address in
the current target process.
write -- Write to the memory of the current target process.
For more help on any particular subcommand, type 'help '.
(lldb)
这里不验证了,一般都用xcode查看内存。
通常,我们使用的p命令,其实是frame variable的别名,当然也可以自定义别名:
command alias bfl breakpoint set -f %1 -l %2
然后就可以使用bfl设置断点了:
(lldb) bfl ViewController.m 116
Breakpoint 1: where = TestDemo`-[ViewController viewDidLoad] + 93 at ViewController.m:116, address = 0x000000010022e1dd
(lldb)
LLDB的具体命令可以看官网,比较多(传送门),有兴趣可以研究。
如果使swift代码,需要使用此断点,比如oc项目中引入了swift的三方库、或swift项目。
使用方法和上面介绍的完全一样,就不赘述了。
分All、OC 和 C++三种,有时候出问题的代码在第三方或混编C代码,这个时候需要选择C++进行调试
异常处理机制四个关键字:@try,@catch,@thorw,@finally。
当程序抛出异常的时候会触发断点。部分错误会断点在发生错误的代码。数组越界等会崩溃到main.m,不能定位到具体的代码,使用Exception Breakpoint就能定位到具体的代码。
可以看到除普通断点的Condition、Ignore、Action、Options四个设置选项外还有两个设置项:
OC,C,C++方法,函数都会触发断点。C,C++函数只需要写函数名。先看下面的例子:
OCFile.h
OCFile.m
CFile.h
CFile.m
C++File.hpp
C++File.cpp
C++代码在ViewController.mm中调用
C函数在SecondViewController中调用
分别给OC、C++和C三个函数和方法添加Symbol断点:
C:只写函数名
OC:
注意:-号是实例方法,+号是类方法,如果写标记的这个类的方法被子类重写了则子类的方法也会触发断点。
运行程序,会分别在该方法或函数处出发Symbol断点,如图:
我们来看一个例子:
NSDictionary *dict = (NSDictionary *)[NSNull null];
NSLog(@"%@" , dict[@"a"]);
从控制台输出的结果可以看出是不能识别的对象:
2019-11-10 21:22:14.394346+0800 TestDemo[5665:117871] -[NSNull objectForKeyedSubscript:]: unrecognized selector sent to instance 0x10abd4ea8
但是具体看不到崩溃到哪一行代码,尤其是复杂的代码,根本不知道哪行代码出了问题,我们添加一个Symbol断点:
-[NSObject doesNotRecognizeSelector:]
运行代码:
就能具体定位到哪行代码出了问题了,还有很多方法,有兴趣的可以研究研究。比如:
objc_exception_throw
还是上面的崩溃,运行之后:
也能找到崩溃的地方。当然Exception Breakpoint也能达到预期效果
填入要设置断点的方法或函数是否在位于dylib中,默认不填。
如果函数是dylib库中,则可以填上具体的库名,可以避免不同库中方法名或者函数名相同。
看一个简单的崩溃代码:
- (void)viewDidLoad {
[super viewDidLoad];
UIView *subView = [[UIView alloc] initWithFrame:CGRectZero];
subView.backgroundColor = [[UIColor cyanColor] colorWithAlphaComponent:.6];
subView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:subView];
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:100.0];
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:200.0];
NSLayoutConstraint *centerXConstraint = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:.0];
NSLayoutConstraint *centerYConstraint = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:.0];
//错误崩溃代码
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeNotAnAttribute relatedBy:NSLayoutAttributeTop toItem:self.view attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:100];
[self.view addConstraints:@[widthConstraint, heightConstraint, centerXConstraint, centerYConstraint ,topConstraint]];
}
很明显,崩溃的原因是没有指定哪个边关联的top属性,看看运行之后结果:
崩溃日志页很详细,但是,问题是我们不知道崩溃在哪部分代码。这个时候我们就要用到【Constraint Error Breakpoint】断点,使其停止到崩溃的具体地方:
运行:
单元测试全局断点。添加后,在单元测试 XCAssert 断言失败时,停留在函数处。此时可以用 lldb 命令 p 强制修改条件满足断言后,继续调试运行。
看一个简单的demo:
#import
NS_ASSUME_NONNULL_BEGIN
@interface TestDemo : NSObject
- (BOOL)getValue;
@end
NS_ASSUME_NONNULL_END
#import "TestDemo.h"
@implementation TestDemo
- (BOOL)getValue{
NSArray *array = @[@"a" ,@"b"];
NSString *value = array[1];
return value ? YES : NO;
}
@end
很明显,该方法会返回YES,再看单元测试文件:
#import
#import "TestDemo.h"
@interface TestDemoTests : XCTestCase
@property (nonatomic,strong) TestDemo *demo;
@end
@implementation TestDemoTests
- (void)setUp {
[super setUp];
self.demo = [[TestDemo alloc] init];
}
- (void)tearDown {
self.demo = nil;//释放
[super tearDown];
}
- (void)testHaha{
BOOL result = [self.demo getValue];
XCTAssertEqual(result, NO,@"测试没通过");
}
执行:点击左侧小棱形,或者command+u
现在设置异常断点:Test Failure Breakpoint
继续执行:
程序会在XCTAssertEqual挺住,然后你就可以使用 lldb 命令 p 强制修改条件满足断言后,继续调试运行,尤其是coomand+u全部运行时,可以帮助快速查看数据并定位问题。
1. Product->Edit Scheme...->Run...->EnvironmentVariables.
2. add NSZombieEnabled,set the value with YES
3. add MallocStackLogging, set the value with YES.
4. add NSAutoreleaseFreedObjectCheckEnabled, set the value with YES.
5. add NSDebugEnabled, set the value with YES.
使用场景:
主要为了解决EXC_BAD_ACCESS问题,MallocStackLogging用来启用malloc记录(使用方式 malloc_history${App_PID}${Object_instance_addr})。
直接通过Editing Scheme窗口中的Run选项下的Diagnostics选项卡来设置。
NSZombieEnabled只能在调试的时候使用,千万不要忘记在产品发布的时候去掉,因为NSZombieEnabled不会真正去释放dealloc对象的内存。