iOS之Xcode斷點調試

 

目錄

一、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斷點分以下幾種:

點擊xcode左下角➕號:

可以看到斷點類型:

  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種斷點之前,先來介紹一下,如何編輯斷點:

根據需要加斷點,鼠標在斷點上右鍵:

可以看到上圖四種類型:

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

1.Edit Breakpoint

點擊Edit Breakpoint後(使用比較頻繁):

可以看到有4種操作條件:

  • Condition(條件)
  • Ignore(忽略)
  • Action(執行語句)
  • Options

(1)Condition

可以輸入一個條件表達式,如果條件滿足了,就會觸發斷點,例如:

執行for循環的時候,只會在value的值爲@“b”的時候纔會觸發斷點

使用比較頻繁,往往在處理數據時for循環找到指定對象時用到,當然還有其他用法,因場景而異。

(2)Ignore

設置忽略斷點的次數,比如設置2,,在上述例子中,會忽略前兩次for循環,而在第三次時觸發斷點

(3)action

點擊Action

默認是Debugger Command,點擊會有

會看到有6中類型:

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

功能Debugger CommandLog Message比較常用

  1. AppleScript
    會在斷點觸發的時候執行Mac OS X內置的一種功能強大的腳本語言。
  2. Gapture GPU Frame
    調試跟GPU相關的問題
  3. Debugger Command  

    可以輸入LLDB命令:

    我們常常使用expr(expression簡寫,兩種寫法lldb都會識別)在斷點處強行修改變量值
  4. Log Message
     

    %B輸出斷點的名稱

    %H輸出斷點執行的次數

    @@之間輸入表達式,比如(@5*2@  會輸出結果10,也可以輸出對象結果)

    勾選Log message to console,會在控制檯輸出結果


    勾選Speak message,不會在控制檯輸出,但是會朗讀處結果(單詞發音還是挺準的,就是有點生硬,程序員估計沒人用的上😄)
  5. Shell Command


    點擊Choose,選擇寫好的腳本文件,觸發斷點時會執行所選擇的腳本
  6. Sound
    會在斷點處觸發聲音,如下圖,有14中聲音。

(4)Options

        勾選Automatically continue after evaluating actions之後程序會在斷點產生後繼續運行。一般用於斷點處修改值,或者輸出斷點信息時會勾選此選項,運行時,斷點生效,但是斷點處不停。如圖,執行for循環結束,斷點處並沒有停留。

 

2.Disable Breakpoint

點擊Disable Breakpoint後會使斷點失效,但斷點依然保留,顏色會邊淺色(快速使斷點失效方法:鼠標單擊斷點)。

再次鼠標在斷點處右鍵:

點擊Enable Breakpoint斷點重新生效(快速使斷點生效方法:鼠標單擊斷點)。

3.Delete Breakpoint

點擊Delete Breakpoint刪除斷點(快速刪除斷點方法:鼠標左建按住斷點拖走鬆開鼠標,即刪除斷點)。

4.Reveal in Breakpoint Navigator

點擊Reveal in Breakpoint Navigator,會切換到導航斷點處

以上斷點的操作都可以在導航斷點處進行相關操作:

多出來的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 - 基礎數據類型打印

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  -查看變亮的數據類型   

三、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 <command> <subcommand>.比如查看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 <cmd-options> [<breakpt-id>]

Command Options Usage:
  breakpoint command add [-D] [-o <one-line-command>] [-e <boolean>] [-s <none>] [<breakpt-id>]
  breakpoint command add [-D] [-e <boolean>] [-s <none>] [-F <python-function>] [<breakpt-id>]

       -D ( --dummy-breakpoints )
            Sets Dummy breakpoints - i.e. breakpoints set before a file is
            provided, which prime new targets.

       -F <python-function> ( --python-function <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 <boolean> ( --stop-on-error <boolean> )
            Specify whether breakpoint command execution should terminate on
            error.

       -o <one-line-command> ( --one-liner <one-line-command> )
            Specify a one-line breakpoint command inline. Be sure to surround
            it with quotes.

       -s <none> ( --script-type <none> )
            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] [<breakpt-id | breakpt-id-list>]

具體操作:

(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 <cmd-options> [<watchpt-id | watchpt-id-list>]

Command Options Usage:
  watchpoint list [-b] [<watchpt-id | watchpt-id-list>]
  watchpoint list [-f] [<watchpt-id | watchpt-id-list>]
  watchpoint list [-v] [<watchpt-id | watchpt-id-list>]

       -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 <subcommand> [<subcommand-options>]

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 <command> <subcommand>'.
(lldb) 

查看更詳細的命令使用help <command> <subcommand>。比如

(lldb) help process attach

結果:

(lldb) help process attach
     Attach to a process.

Syntax: process attach <cmd-options>

Command Options Usage:
  process attach [-c] [-P <plugin>] [-p <pid>]
  process attach [-ciw] [-P <plugin>] [-n <process-name>]

       -P <plugin> ( --plugin <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 <process-name> ( --name <process-name> )
            The name of the process to attach to.

       -p <pid> ( --pid <pid> )
            The process ID of an existing process to attach to.

       -w ( --waitfor )
            Wait for the process with <process-name> 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 <subcommand> [<subcommand-options>]

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 <command> <subcommand>'.
(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進行追蹤:

可以看到,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並沒有輸出;

4、frame

我們先看看help信息:

(lldb) help frame
     Commands for selecting and examing the current thread's stack frames.

Syntax: frame <subcommand> [<subcommand-options>]

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 <command> <subcommand>'.
(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 <sub-command> ...

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 <command> <subcommand>'.
(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行:

7、memory查看內存

先來看看help:

(lldb) help memory
     Commands for operating on memory in the current target process.

Syntax: memory <subcommand> [<subcommand-options>]

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 <command> <subcommand>'.
(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項目。

使用方法和上面介紹的完全一樣,就不贅述了。

五、Exception Breakpoint(全局斷點(OC 和 C++語言))

分All、OC 和 C++三種,有時候出問題的代碼在第三方或混編C代碼,這個時候需要選擇C++進行調試

異常處理機制四個關鍵字:@try,@catch,@thorw,@finally。

  • 當代碼有可能出現異常時,我們把他放到@try語句塊中。
  • @catch()塊包含了處理@try塊裏的拋出的異常的邏輯。
  • 如果直接使用@throw塊來拋出異常,這個異常本質上是一個OC的對象。咱們可以使用NSException對象,但是不侷限於他們。
  • 無論異常是否發生,@finally塊裏面的語句都會執行。

當程序拋出異常的時候會觸發斷點。部分錯誤會斷點在發生錯誤的代碼。數組越界等會崩潰到main.m,不能定位到具體的代碼,使用Exception Breakpoint就能定位到具體的代碼。

六、OpenGL ES Error Breakpoint

 

七、Symbolic Breakpoint(符號斷點)

可以看到除普通斷點的Condition、Ignore、Action、Options四個設置選項外還有兩個設置項:

  • Symbol
  • Module

1.Symbol

    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:只寫函數名

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也能達到預期效果

2.Module

填入要設置斷點的方法或函數是否在位於dylib中,默認不填。

如果函數是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屬性,看看運行之後結果:

崩潰日誌頁很詳細,但是,問題是我們不知道崩潰在哪部分代碼。這個時候我們就要用到【Constraint Error Breakpoint】斷點,使其停止到崩潰的具體地方:

運行:

 

八、Test Failure Breakpoint

單元測試全局斷點。添加後,在單元測試 XCAssert 斷言失敗時,停留在函數處。此時可以用 lldb 命令 p 強制修改條件滿足斷言後,繼續調試運行。

看一個簡單的demo:

#import <Foundation/Foundation.h>

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 <XCTest/XCTest.h>
#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全部運行時,可以幫助快速查看數據並定位問題。

 

九、設置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.

使用場景:

主要爲了解決EXC_BAD_ACCESS問題,MallocStackLogging用來啓用malloc記錄(使用方式 malloc_history${App_PID}${Object_instance_addr})。

第二種設置方法:

直接通過Editing Scheme窗口中的Run選項下的Diagnostics選項卡來設置。

NSZombieEnabled只能在調試的時候使用,千萬不要忘記在產品發佈的時候去掉,因爲NSZombieEnabled不會真正去釋放dealloc對象的內存。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章