iOS之Xcode断点调试

 

iOS之Xcode断点调试_第1张图片

目录

一、Breakpoint(断点)调试

1.Edit Breakpoint

(1)Condition

(2)Ignore

(3)action

(4)Options

2.Disable Breakpoint

3.Delete Breakpoint

4.Reveal in Breakpoint Navigator

二、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断点分以下几种:

iOS之Xcode断点调试_第2张图片

点击xcode左下角➕号:

iOS之Xcode断点调试_第3张图片

可以看到断点类型:

  1. Swift Error Breakpoint
  2. Exception Breakpoint
  3. OpenGL ES Error Breakpoint
  4. Symbolic Breakpoint
  5. Constraint Error Breakpoint
  6. Test Failure Breakpoint

一、Breakpoint(断点)调试

 

看这6种断点之前,先来介绍一下,如何编辑断点:

根据需要加断点,鼠标在断点上右键:

iOS之Xcode断点调试_第4张图片

可以看到上图四种类型:

  • Edit Breakpoint
  • Disable Breakpoint
  • Delete Breakpoint
  • Reveal in Breakpoint Navigator

1.Edit Breakpoint

点击Edit Breakpoint后(使用比较频繁):

iOS之Xcode断点调试_第5张图片

可以看到有4种操作条件:

  • Condition(条件)
  • Ignore(忽略)
  • Action(执行语句)
  • Options

(1)Condition

可以输入一个条件表达式,如果条件满足了,就会触发断点,例如:

iOS之Xcode断点调试_第6张图片

执行for循环的时候,只会在value的值为@“b”的时候才会触发断点

iOS之Xcode断点调试_第7张图片

使用比较频繁,往往在处理数据时for循环找到指定对象时用到,当然还有其他用法,因场景而异。

(2)Ignore

设置忽略断点的次数,比如设置2,,在上述例子中,会忽略前两次for循环,而在第三次时触发断点

iOS之Xcode断点调试_第8张图片

(3)action

点击Action

iOS之Xcode断点调试_第9张图片

默认是Debugger Command,点击会有

iOS之Xcode断点调试_第10张图片

会看到有6中类型:

iOS之Xcode断点调试_第11张图片

  • AppleScript
  • Gapture GPU Frame
  • Debugger Command
  • Log Message
  • Shell Command
  • Sound

功能Debugger CommandLog Message比较常用

  1. AppleScript
    会在断点触发的时候执行Mac OS X内置的一种功能强大的脚本语言。
    iOS之Xcode断点调试_第12张图片
  2. Gapture GPU Frame
    调试跟GPU相关的问题
    iOS之Xcode断点调试_第13张图片
  3. Debugger Command  

    可以输入LLDB命令:
    iOS之Xcode断点调试_第14张图片
    我们常常使用expr(expression简写,两种写法lldb都会识别)在断点处强行修改变量值iOS之Xcode断点调试_第15张图片
  4. Log Message
     

    %B输出断点的名称

    %H输出断点执行的次数

    @@之间输入表达式,比如(@5*2@  会输出结果10,也可以输出对象结果)

    勾选Log message to console,会在控制台输出结果

    iOS之Xcode断点调试_第16张图片
    勾选Speak message,不会在控制台输出,但是会朗读处结果(单词发音还是挺准的,就是有点生硬,程序员估计没人用的上)
  5. Shell Command

    iOS之Xcode断点调试_第17张图片
    点击Choose,选择写好的脚本文件,触发断点时会执行所选择的脚本
  6. Sound
    会在断点处触发声音,如下图,有14中声音。
    iOS之Xcode断点调试_第18张图片

(4)Options

        勾选Automatically continue after evaluating actions之后程序会在断点产生后继续运行。一般用于断点处修改值,或者输出断点信息时会勾选此选项,运行时,断点生效,但是断点处不停。如图,执行for循环结束,断点处并没有停留。iOS之Xcode断点调试_第19张图片

 

2.Disable Breakpoint

点击Disable Breakpoint后会使断点失效,但断点依然保留,颜色会边浅色(快速使断点失效方法:鼠标单击断点)。

iOS之Xcode断点调试_第20张图片

再次鼠标在断点处右键:

iOS之Xcode断点调试_第21张图片

点击Enable Breakpoint断点重新生效(快速使断点生效方法:鼠标单击断点)。

3.Delete Breakpoint

iOS之Xcode断点调试_第22张图片

点击Delete Breakpoint删除断点(快速删除断点方法:鼠标左建按住断点拖走松开鼠标,即删除断点)。

4.Reveal in Breakpoint Navigator

点击Reveal in Breakpoint Navigator,会切换到导航断点处

iOS之Xcode断点调试_第23张图片

iOS之Xcode断点调试_第24张图片

以上断点的操作都可以在导航断点处进行相关操作:

iOS之Xcode断点调试_第25张图片

多出来的Share Breakpoint和Move Breakpoint基本用不上,有兴趣的可以试试。

二、lldb命令

上面介绍了在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 - 基础数据类型打印

iOS之Xcode断点调试_第26张图片

call - 执行一段代码 比如

iOS之Xcode断点调试_第27张图片

bt - 显示当前线程的调用堆栈。bt all 显示所有线程堆栈信息

iOS之Xcode断点调试_第28张图片

iOS之Xcode断点调试_第29张图片

q - 退出断点

e - 设置常量,方便接下来调试用(注意: 声明和使用时对象名称前要加$):

iOS之Xcode断点调试_第30张图片

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  -查看变亮的数据类型   

三、lldb命令详细使用

1、Breakpoint

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的值变化,我可以使用如下命令:

iOS之Xcode断点调试_第31张图片

(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循环:

iOS之Xcode断点调试_第32张图片

在log处添加添加观察

iOS之Xcode断点调试_第33张图片

iOS之Xcode断点调试_第34张图片

然后就可以设置在a的值变化为某个特定值之后触发断点

(lldb) watchpoint modify -c '(a=15)'

继续执行代码,则自动停在for循环时a的值为15处:

iOS之Xcode断点调试_第35张图片

这个时候可以看一下具体断点的参数,使用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) 
  • -b是比较简略的信息;
  • -f是比较全面的信息;
  • -v是完整的信息;
  • 如果使用watchpoint list,默认的是 watchpoint list -f;

2、process

首先我们先用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命令在我目前日常开发中其实不怎么使用。

3、thread

我们可以使用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-开头的这几个命令比较常用:

  • 下一步:thread step-over, next, n
  • 进入:thread step-in, step, s
  • 跳出:thread step-out, finish, f

继续执行: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进行追踪:

iOS之Xcode断点调试_第36张图片

可以看到,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处下个断点,运行代码:

iOS之Xcode断点调试_第37张图片

执行return命令,则直接返回;

iOS之Xcode断点调试_第38张图片

可以看的两个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处添加一个断点:

iOS之Xcode断点调试_第39张图片

执行return命令:

iOS之Xcode断点调试_第40张图片

可以看的log没有输出,直接跳出test方法,我们继续执行代码:

iOS之Xcode断点调试_第41张图片

可以看到,打印的value是20,而test方法里的两个log并没有输出;

4、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命令,这个上面有介绍,这里就不赘述了。

5、call

一般可以用来调用不需要返回值的调试命令,这个命令很简单,没有其他命令,直接使用就行。

比如更改View的背景颜色:

call [self.view setBackgroundColor:[UIColor greenColor]]

当然po也能达到一样的作用:

po [self.view setBackgroundColor:[UIColor greenColor]]

平时基本都用po,很少用call

6、image

这个命令很实用,可用于寻址。比较实用的用法是用于寻找栈地址对应的代码位置。

先来看看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行:

iOS之Xcode断点调试_第42张图片

7、memory查看内存

先来看看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查看内存。

8、为命令设置别名

通常,我们使用的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 Error Breakpoint(Swift语言的断点)

如果使swift代码,需要使用此断点,比如oc项目中引入了swift的三方库、或swift项目。

iOS之Xcode断点调试_第43张图片

使用方法和上面介绍的完全一样,就不赘述了。

五、Exception Breakpoint(全局断点(OC 和 C++语言))

分All、OC 和 C++三种,有时候出问题的代码在第三方或混编C代码,这个时候需要选择C++进行调试

iOS之Xcode断点调试_第44张图片

iOS之Xcode断点调试_第45张图片

异常处理机制四个关键字:@try,@catch,@thorw,@finally。

  • 当代码有可能出现异常时,我们把他放到@try语句块中。
  • @catch()块包含了处理@try块里的抛出的异常的逻辑。
  • 如果直接使用@throw块来抛出异常,这个异常本质上是一个OC的对象。咱们可以使用NSException对象,但是不局限于他们。
  • 无论异常是否发生,@finally块里面的语句都会执行。

iOS之Xcode断点调试_第46张图片

当程序抛出异常的时候会触发断点。部分错误会断点在发生错误的代码。数组越界等会崩溃到main.m,不能定位到具体的代码,使用Exception Breakpoint就能定位到具体的代码。

六、OpenGL ES Error Breakpoint

 

七、Symbolic Breakpoint(符号断点)

iOS之Xcode断点调试_第47张图片

可以看到除普通断点的Condition、Ignore、Action、Options四个设置选项外还有两个设置项:

  • Symbol
  • Module

1.Symbol

    OC,C,C++方法,函数都会触发断点。C,C++函数只需要写函数名。先看下面的例子:

   iOS之Xcode断点调试_第48张图片

OCFile.h

iOS之Xcode断点调试_第49张图片

OCFile.m

iOS之Xcode断点调试_第50张图片

CFile.h

iOS之Xcode断点调试_第51张图片

CFile.m

iOS之Xcode断点调试_第52张图片

C++File.hpp

iOS之Xcode断点调试_第53张图片

C++File.cpp

iOS之Xcode断点调试_第54张图片

C++代码在ViewController.mm中调用

C函数在SecondViewController中调用

iOS之Xcode断点调试_第55张图片

分别给OC、C++和C三个函数和方法添加Symbol断点:

iOS之Xcode断点调试_第56张图片

C:只写函数名

iOS之Xcode断点调试_第57张图片

C++:只写函数名iOS之Xcode断点调试_第58张图片

OC:

iOS之Xcode断点调试_第59张图片

iOS之Xcode断点调试_第60张图片

注意:-号是实例方法,+号是类方法,如果写标记的这个类的方法被子类重写了则子类的方法也会触发断点。

运行程序,会分别在该方法或函数处出发Symbol断点,如图:

iOS之Xcode断点调试_第61张图片

iOS之Xcode断点调试_第62张图片

iOS之Xcode断点调试_第63张图片

iOS之Xcode断点调试_第64张图片

我们来看一个例子:

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:]

iOS之Xcode断点调试_第65张图片

运行代码:

iOS之Xcode断点调试_第66张图片

就能具体定位到哪行代码出了问题了,还有很多方法,有兴趣的可以研究研究。比如:

objc_exception_throw

iOS之Xcode断点调试_第67张图片

还是上面的崩溃,运行之后:

也能找到崩溃的地方。当然Exception Breakpoint也能达到预期效果

2.Module

填入要设置断点的方法或函数是否在位于dylib中,默认不填。

iOS之Xcode断点调试_第68张图片

如果函数是dylib库中,则可以填上具体的库名,可以避免不同库中方法名或者函数名相同

六、Constraint Error Breakpoint

看一个简单的崩溃代码:

- (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属性,看看运行之后结果:

iOS之Xcode断点调试_第69张图片

崩溃日志页很详细,但是,问题是我们不知道崩溃在哪部分代码。这个时候我们就要用到【Constraint Error Breakpoint】断点,使其停止到崩溃的具体地方:

iOS之Xcode断点调试_第70张图片

iOS之Xcode断点调试_第71张图片

运行:

 

八、Test Failure 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

iOS之Xcode断点调试_第72张图片

现在设置异常断点:Test Failure Breakpoint

iOS之Xcode断点调试_第73张图片

iOS之Xcode断点调试_第74张图片

继续执行:

iOS之Xcode断点调试_第75张图片

程序会在XCTAssertEqual挺住,然后你就可以使用 lldb 命令 p 强制修改条件满足断言后,继续调试运行,尤其是coomand+u全部运行时,可以帮助快速查看数据并定位问题。

 

九、设置NSZombieEnabled、MallocStackLogging、NSAutoreleaseFreedObjectCheckEnabled、NSDebugEnabled

第一种设置方法

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.

iOS之Xcode断点调试_第76张图片

使用场景:

主要为了解决EXC_BAD_ACCESS问题,MallocStackLogging用来启用malloc记录(使用方式 malloc_history${App_PID}${Object_instance_addr})。

第二种设置方法:

直接通过Editing Scheme窗口中的Run选项下的Diagnostics选项卡来设置。

iOS之Xcode断点调试_第77张图片

NSZombieEnabled只能在调试的时候使用,千万不要忘记在产品发布的时候去掉,因为NSZombieEnabled不会真正去释放dealloc对象的内存。

你可能感兴趣的:(ios,xcode)