LLDB 学习笔记

Advanced.Apple.Debugging.&.Reverse.Engineering.v2.0 学习笔记

1. 入门

1.1 禁用Rootless 与开启调试功能

为了提高系统的安全性,Mac OS X 10.11开始引入了Rootless,在Rootless开启的情况下,LLDB无法调试到其它进程(模拟器内的进程除外).如果想调试其它进程,需要关闭Rootless,如下:

  1. 重启系统
  2. 开机后按下 Command + R 直到进入恢复模式
  3. 从顶部菜单中找到 Utilities 菜单并选择 Terminal
  4. 输入以下命令禁用Rootless
csrutil disable
reboot

1.2 使用LLDB基本流程-使用LLDB 调试Xcode

实现如下功能

  1. 使用LLDB调试Xcode
  2. 将Xcode的输出重定向到终端

1.2.1 创建重定向终端(tty)

使用tty命令创建重定向终端,此时生成的终端名为ttys002

~ tty
/dev/ttys001

1.2.2 使用LLDB启动Xcode

  1. 使用lldb 命令进入LLDB
  2. 设置可执行目标文件路径(file 命令):
(lldb) file /Applications/Xcode.app/Contents/MacOS/Xcode
Current executable set to '/Applications/Xcode.app/Contents/MacOS/Xcode' (x86_64).

其中/Applications/Xcode.app/Contents/MacOS/Xcode为Xcode的可执行路径,在Xcode打开的情况下可以通过以下命令获取

~ ps -ef `pgrep -x Xcode`
  UID   PID  PPID   C STIME   TTY           TIME CMD
  501 29508     1   0  8:29下午 ??         0:14.03 /Applications/Xcode.app/Contents/MacOS/Xcode
  1. 启动并重定向到终端(之前创建的重定向终端:ttys001)
(lldb) process launch -e /dev/ttys001 --
Process 92898 launched: '/Applications/Xcode.app/Contents/MacOS/Xcode' (x86_64)
  1. Ctrl + C 暂停调试器
  2. 设置断点(b):
(lldb) b -[NSView hitTest:]
Breakpoint 1: where = AppKit`-[NSView hitTest:], address = 0x00007fff31e25f9b
  1. 继续调试(continue)
(lldb) continue
Process 34209 resuming
  1. 触发断点: 点击Xcode任意区域
Process 92898 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x00007fff31e25f9b AppKit`-[NSView hitTest:]
AppKit`-[NSView hitTest:]:
->  0x7fff31e25f9b <+0>: pushq  %rbp
    0x7fff31e25f9c <+1>: movq   %rsp, %rbp
    0x7fff31e25f9f <+4>: pushq  %r15
    0x7fff31e25fa1 <+6>: pushq  %r14
Target 0: (Xcode) stopped.
  1. 查看那个视图被点击(po 命令):
(lldb) po $rdi

(lldb) po [$rdi superclass]
NSTitledFrame
(lldb) po [[$rdi superclass] superclass]
NSFrameView
(lldb) po [[[$rdi superclass] superclass] superclass]
NSView

$rdi 寄存器包含调用hitTest:方法的子类NSView的实例

1.3 常用快捷键

  1. Ctrl + C :暂停调试器
  2. Ctrl + D :结束调试
  3. Command + K: 清空debug区域输出

2. LLDB 帮助与查询

2.1 LLDB命令格式

  [-options [option-value]] [argument [argument...]]
即:<关键字> <动作> [-可选项 [可选项的值]] [参数1 [参数2···]]
  1. 所有命令使用同一的格式
  2. 参数,可选项和选项值都用空格分隔
  3. 使用双引号保护参数中的空格内容
  4. \" 使用\进行转义,如\"表示",\\表示\
  5. 如果参数以-开始,需要在可选项和参数之间使用--加以分隔
  6. 可选项的顺序可以不固定
(lldb) process launch --stop-at-entry -- -program_arg_1 value -program_arg_2 value
关键字:process
动作:launch
可选项:-stop-at-entry
参数:-program_arg_1、-program_arg_2 value
作用:启动程序并传入 `-program_arg value` 作为参数

2.2 help 命令获取帮助

2.2.1 help 命令概览:help

(lldb) help
Debugger commands:
  apropos           -- List debugger commands related to a word or subject.
  breakpoint        -- Commands for operating on breakpoints (see 'help b' for shorthand.)
  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.

2.2.2 help 命令格式:help help

通过help help客户获取help的命令格式

(lldb) help help
     Show a list of all debugger commands, or give details about a specific command.
Syntax: help []
Command Options Usage:
  help [-ahu] [ [ [...]]]
       -a ( --hide-aliases )
            Hide aliases in the command list.
       -h ( --show-hidden-commands )
            Include commands prefixed with an underscore.
       -u ( --hide-user-commands )
            Hide user-defined commands from the list.
     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.

2.2.3查询断点的帮助文档:help breakpoint

(lldb) help breakpoint
     Commands for operating on breakpoints (see 'help b' for shorthand.)
Syntax: breakpoint  []
The following subcommands are supported:
      clear   -- Delete or disable breakpoints matching the specified source file and line.
      command -- Commands for adding, removing and listing LLDB commands executed when a breakpoint is hit.
      delete  -- Delete the specified breakpoint(s).  If no breakpoints are specified, delete them all.
      disable -- Disable the specified breakpoint(s) without deleting them.  If none are specified, disable all breakpoints.
      enable  -- Enable the specified disabled breakpoint(s). If no breakpoints are specified, enable all of them.
      list    -- List some or all breakpoints at configurable levels of detail.
      modify  -- Modify the options on a breakpoint or set of breakpoints in the executable.  If no breakpoint is specified, acts on the last created breakpoint.  With the exception of -e,
                 -d and -i, passing an empty argument clears the modification.
      name    -- Commands to manage name tags for breakpoints
      read    -- Read and set the breakpoints previously saved to a file with "breakpoint write".
      set     -- Sets a breakpoint or set of breakpoints in the executable.
      write   -- Write the breakpoints listed to a file that can be read in with "breakpoint read".  If given no arguments, writes all breakpoints.

For more help on any particular subcommand, type 'help  '.

2.3 查询命令:apropos

在不知道具体命令但知道相应关键字的时候,可以用apropos命令进行查询,语法如下

apropos 

比如查询跟Objective-C相关的命令

(lldb) apropos Objective-C
The following commands may relate to 'Objective-C':
  objc           -- Commands for operating on the Objective-C language runtime.
  class-table    -- Commands for operating on the Objective-C class table.
  dump           -- Dump information on Objective-C classes known to the current process.
  tagged-pointer -- Commands for operating on Objective-C tagged pointers.

查询引用计数相关的命令

(lldb) apropos "reference count"
The following commands may relate to 'reference count':
  refcount -- Inspect the reference count data for a Swift object

3. 使用LLDB附加到进程

3.1 附加到现有进程

  1. 通过进程名,相当于:process attach --name
 ~ lldb -n Xcode
(lldb) process attach --name "Xcode"
  1. 通过进程ID,相当于:process attach --pid
~ pgrep -x Xcode
2253
~ lldb -p 2253
(lldb) process attach --pid 2253

3.2 附加到即将启动的进程

上面的命令只能附加到正在运行的进程。如果Xcode没有运行,或者已经附加到调试器,前面的命令将失败。在不知道PID的情况下,如何捕捉即将启动的进程呢?

3.2.1 通过添加 -w 参数附加到即将启动的进程: lldb -n <进程名> -w

lldb -n <进程名> -w
相当于: (lldb) process attach --name <进程名> --waitfor

比如我们想附加到即将启动的Finder进程,可如下实现

  1. 在终端1 输入如下命令等待附加到即将启动的Finder进程
~ lldb -n Finder -w
(lldb) process attach --name "Finder" --waitfor
  1. 在终端2 输入如下命令,该命令将关闭所有Finder
~ pkill Finder
  1. 系统将自动启动Finder,等系统启动Finder后,终端1 LLDB将附加到Finder进程,如下输出:
Process 4476 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
    frame #0: 0x00007fff6ebabdfa libsystem_kernel.dylib`mach_msg_trap + 10
libsystem_kernel.dylib`mach_msg_trap:
->  0x7fff6ebabdfa <+10>: retq
    0x7fff6ebabdfb <+11>: nop

libsystem_kernel.dylib`mach_msg_overwrite_trap:
    0x7fff6ebabdfc <+0>:  movq   %rcx, %r10
    0x7fff6ebabdff <+3>:  movl   $0x1000020, %eax          ; imm = 0x1000020
Target 0: (Finder) stopped.

Executable module set to "/System/Library/CoreServices/Finder.app/Contents/MacOS/Finder".
Architecture set to: x86_64h-apple-macosx-.

3.2.2 通过指定可执行文件路径附加到即将启动的进程: lldb -f <进程路径>

  1. 通过lldb -f <进程路径>命令指定可执行文件
~ lldb -f /System/Library/CoreServices/Finder.app/Contents/MacOS/Finder
  1. 运行 process launch命令启动进程

注意:直接使用process launch 命令启动进程,被调试进程的stderr输出会自动发送到当前终端窗口.我们可以通过 -e 参数将输出重定向到其它终端

process launch -e /dev/ttys001 --

3.3 process launch 详解

以调试 ls命令为例子

3.3.1 修改被调试进程工作路径: process launch -w

默认情况下,被调试进程会工作在当前目录,在当前目录执行ls,输出的是当前目录下的文件列表.

~ lldb -f /bin/ls
(lldb) target create "/bin/ls"
Current executable set to '/bin/ls' (x86_64).
(lldb) process launch
Process 6375 launched: '/bin/ls' (x86_64)
Applications            Music
Applications (Parallels)    Parallels
Desktop             Pictures
Documents           Public
Downloads           markdown.md
Library             sslkeylog.log
Movies
Process 6375 exited with status = 0 (0x00000000)

如果想改变被调试进程的工作目录路径,我们可以通过添加-w参数实现,如:

~ lldb -f /bin/ls
(lldb) target create "/bin/ls"
Current executable set to '/bin/ls' (x86_64).
(lldb) process launch -w /Users/Shared
Process 7130 launched: '/bin/ls' (x86_64)
Parallels           SC Info
Previously Relocated Items  adi
Process 7130 exited with status = 0 (0x00000000)

输出/Users/Shared,跟上面列出的文件列表一样

➜  Shared pwd
/Users/Shared
➜  Shared ls
Parallels                  Previously Relocated Items SC Info                    adi

3.3.2 传递启动参数给目标进程

我们也可以将参数传递给目标进程,注意中间的--,用以区分选项和参数

➜  ~ pwd
/Users/sammylan
➜  ~ ls /Users/Shared
Parallels                  Previously Relocated Items SC Info                    adi
➜  ~ lldb -f /bin/ls
(lldb) target create "/bin/ls"
Current executable set to '/bin/ls' (x86_64).
(lldb) process launch -- /Users/Shared
Process 8074 launched: '/bin/ls' (x86_64)
Parallels           SC Info
Previously Relocated Items  adi
Process 8074 exited with status = 0 (0x00000000)

3.3.3 启动参数环境变量展开: process launch -X true

如果传递的参数里面有环境变量,如~,则无法将参数正确传递过去,如下,ls无法展开~/Public

~ lldb -f /bin/ls
(lldb) target create "/bin/ls"
Current executable set to '/bin/ls' (x86_64).
(lldb) process launch -- ~/Public
Process 8464 launched: '/bin/ls' (x86_64)
ls: ~/Public: No such file or directory
Process 8464 exited with status = 1 (0x00000001)

这时候需要传递-X true参数给process launch

(lldb) process launch -Xtrue -- ~/Public
Process 8654 launched: '/bin/ls' (x86_64)
Drop Box
Process 8654 exited with status = 0 (0x00000000)

X选项扩展您提供的任何shell参数,例如波浪号。在LLDB中有一个快捷命令run可实现类似功能,如

(lldb) run ~/Public
Process 8792 launched: '/bin/ls' (x86_64)
Drop Box
Process 8792 exited with status = 0 (0x00000000)

查看其帮助文档可发现,它等价于process launch -X true --命令

(lldb) help run
     Launch the executable in the debugger.
Syntax: run []
Command Options Usage:
  run []
'run' is an abbreviation for 'process launch -X true --'

3.3.4 输出重定向:process launch -o

  • 重定向到文件:
(lldb)  process launch -o /tmp/ls_output.txt -- /Applications
Process 9397 launched: '/bin/ls' (x86_64)
Process 9397 exited with status = 0 (0x00000000)
  • 重定向到终端
(lldb) process launch -o /dev/ttys001 --
Process 9647 launched: '/bin/ls' (x86_64)
Process 9647 exited with status = 0 (0x00000000)

执行上述命令后,将会输出到终端ttys001,如下


终端 ttys001

3.3.5 输入重定向:process launch -i

  1. 将文件内容输出到文件
~ echo "hello world" > /tmp/wc_input.txt

通过wc统计

target create /usr/bin/wc
Current executable set to '/usr/bin/wc' (x86_64).
(lldb) process launch -i /tmp/wc_input.txt
Process 10159 launched: '/usr/bin/wc' (x86_64)
       1       2      12

对于需要输入的程序,如wc,如果没有设置输入重定向,程序启动后会挂在那里等待输入,如下启动,当执行run后,会一直停在那里等待输入.输入"helloworld"并按回车,然后按Ctrl + D,结束输入,之后wc将结束输入并退出,如下

(lldb) target create /usr/bin/wc
Current executable set to '/usr/bin/wc' (x86_64).
(lldb) run
Process 12287 launched: '/usr/bin/wc' (x86_64)
hello world
       1       2      12
Process 12287 exited with status = 0 (0x00000000)

3.3.7 不创建标准输入:process launch -n

n 选项告诉LLDB不要创建stdin;因此wc没有要处理的数据,并立即退出。

(lldb) target create /usr/bin/wc
Current executable set to '/usr/bin/wc' (x86_64).
(lldb)  process launch -n
Process 12623 launched: '/usr/bin/wc' (x86_64)
Process 12623 exited with status = 0 (0x00000000)

3.3.8 错误重定向:process launch -e

将错误重定向到其它终端

process launch -e /dev/ttys001 --

4. 断点

4.1 image lookup: 查找符号

  1. image lookup -a: 根据地址查找符号信息
  2. image lookup -n: 根据符号名字查找符号信息
  3. image lookup -r: 启用正则表达式查找符号
image lookup -rn "UIViewController"  ;查找UIViewController相关的全部符号
image lookup -rn "-\[UIViewController" ;查找UIViewController的实例方法
image lookup -rn "\+\[UIViewController"; 查找UIViewController的类方法
image lookup -rn "-\[UIViewController\sset" ; 查找UIViewController的所有set方法
image lookup -rn "-\[UIViewController\(\w+\)" 查找UIViewController的所有分类的方法

注意,以下字符需要转义:
[,],(,),(空格,也可以用\s代替),+,-

image lookup 的详细命令参数如下:

(lldb) help image lookup
     Look up information within executable and dependent shared library images.
     Syntax: target modules lookup  [ [ [...]]]

Command Options Usage:
  target modules lookup [-Av] -a  [-o ] [ [ [...]]]
  target modules lookup [-Arv] -s  [ [ [...]]]
  target modules lookup [-Aiv] -f  [-l ] [ [ [...]]]
  target modules lookup [-Airv] -F  [ [ [...]]]
  target modules lookup [-Airv] -n  [ [ [...]]]
  target modules lookup [-Av] -t  [ [ [...]]]

       -A ( --all )
            Print all matches, not just the best match, if a best match is
            available.

       -F  ( --function  )
            Lookup a function by name in the debug symbols in one or more
            target modules.

       -a  ( --address  )
            Lookup an address in one or more target modules.

       -f  ( --file  )
            Lookup a file by fullpath or basename in one or more target
            modules.

       -i ( --no-inlines )
            Ignore inline entries (must be used in conjunction with --file or
            --function).

       -l  ( --line  )
            Lookup a line number in a file (must be used in conjunction with
            --file).

       -n  ( --name  )
            Lookup a function or symbol by name in one or more target modules.

       -o  ( --offset  )
            When looking up an address subtract  from any addresses
            before doing the lookup.

       -r ( --regex )
            The  argument for name lookups are regular expressions.

       -s  ( --symbol  )
            Lookup a symbol by name in the symbol tables in one or more target
            modules.

       -t  ( --type  )
            Lookup a type by name in the debug symbols in one or more target
            modules.

       -v ( --verbose )
            Enable verbose lookup information.

4.2 属性名字重整

4.2.1 Objective-C 属性名字重整

在Objective-C中如下定义一个属性: name

@interface TestClass : NSObject
@property (nonatomic, strong) NSString *name;
@end

会生成如下两个settergetter方法:

-[TestClass name];
-[TestClass setName:];

4.2.2 Swift 属性名字重整

在Swift中如下定义一个属性: name

class SwiftTestClass: NSObject { 
  var name: String!
}

会生成如下两个settergetter方法,其中Signals是模块名

Signals.SwiftTestClass.name.getter;
Signals.SwiftTestClass.name.setter;

通过如下命令查询会发现还有其它一些不常用的方法,这里不做详细讨论:

image lookup -rn Signals.SwiftTestClass.name
7 matches found in /Users/sammylan/Library/Developer/Xcode/DerivedData/Signals-efbmlrmvuqjbiyfptsisstosyqdx/Build/Products/Debug-iphonesimulator/Signals.app/Signals:
        Address: Signals[0x000000010000e500] (Signals.__TEXT.__text + 44608)
        Summary: Signals`variable initialization expression of Signals.SwiftTestClass.name : Swift.Optional at         Address: Signals[0x000000010000e510] (Signals.__TEXT.__text + 44624)
        Summary: Signals`key path getter for Signals.SwiftTestClass.name : Swift.Optional : Signals.SwiftTestClass at         Address: Signals[0x000000010000e580] (Signals.__TEXT.__text + 44736)
        Summary: Signals`key path setter for Signals.SwiftTestClass.name : Swift.Optional : Signals.SwiftTestClass at         Address: Signals[0x000000010000e620] (Signals.__TEXT.__text + 44896)
        Summary: Signals`Signals.SwiftTestClass.name.getter : Swift.Optional at SwiftTestClass.swift:28        Address: Signals[0x000000010000e6c0] (Signals.__TEXT.__text + 45056)
        Summary: Signals`Signals.SwiftTestClass.name.setter : Swift.Optional at SwiftTestClass.swift:28        Address: Signals[0x000000010000e780] (Signals.__TEXT.__text + 45248)
        Summary: Signals`Signals.SwiftTestClass.name.modify : Swift.Optional at SwiftTestClass.swift:28        Address: Signals[0x000000010000e7d0] (Signals.__TEXT.__text + 45328)
        Summary: Signals`Signals.SwiftTestClass.name.modify : Swift.Optional at SwiftTestClass.swift:28

4.3 创建断点

4.3.1 创建断点:b(_regexp-break)

(lldb) help b
     Set a breakpoint using one of several shorthand formats.  Expects 'raw'
     input (see 'help raw-input'.)

Syntax: 
_regexp-break :
              main.c:12             // Break at line 12 of main.c

_regexp-break 
              12                    // Break at line 12 of current file

_regexp-break 0x
0x1234000 // Break at address 0x1234000 _regexp-break main // Break in 'main' after the prologue _regexp-break & &main // Break at first instruction in 'main' _regexp-break ` libc.so`malloc // Break in 'malloc' from 'libc.so' _regexp-break // /break here/ // Break on source lines in current file // containing text 'break here'. 'b' is an abbreviation for '_regexp-break'

4.3.2创建断点:rb(breakpoint set -r %1)

使用正则表达式方式创建断点,如过使用b创建前面的断点,需要如下方式创建

(lldb) b  Signals.SwiftTestClass.name.setter : Swift.Optional
Breakpoint 15: where = Signals`Signals.SwiftTestClass.name.setter : Swift.Optional + 174 at SwiftTestClass.swift:28:7, address = 0x000000010da5376e

(lldb) b SwiftTestClass.name.setter : Swift.Optional
Breakpoint 16: no locations (pending).
WARNING:  Unable to resolve breakpoint to any actual locations.

如果使用rb命令,可以简化成如下命令

(lldb) rb SwiftTestClass.name.setter
Breakpoint 14: where = Signals`Signals.SwiftTestClass.name.setter : Swift.Optional + 174 at SwiftTestClass.swift:28:7, address = 0x000000010da5376e

常用选项:(详细命令请参考:help rb)

  • -s:限定某个库的符号,如:rb . -s UIKit UIKit的所有符号设置一个断点
  • -o: 断点只执行一次,执行后就删除
  • -L: 按照语言来筛选,如: rb . -L swift -s Commons,这将在Commons模块中的每个Swift方法上设置一个断点

4.3.3 创建断点:breakpoint set

  • -p 选项:源码正则选项,在符合特定源码规则的地方停止
  1. breakpoint set -A -p "if let": 在包含if-let的每个源代码位置上创建一个断点
  2. breakpoint set -p "if let" -s Signals -A: 在Signals里包含if-let的每个源代码位置上创建一个断点
  • -c 选项:条件断点:
  1. breakpoint set -n "-[UIViewController viewDidLoad]" -c "*(uintptr_t*)$rsp >= 0x0000000102573000 && *(uintptr_t*)$rsp <= 0x0000000102587000" 只有从Signals模块调用-[UIViewController viewDidLoad]
  • 获取模块地址:
(lldb) image dump sections Signals
image.png

4.4 管理断点:

  • breakpoint list: 列出断点
  • breakpoint delete: 删除断点
  • breakpoint set: 设置断点
(lldb) help breakpoint
     Commands for operating on breakpoints (see 'help b' for shorthand.)

Syntax: breakpoint  []

The following subcommands are supported:

      clear   -- Delete or disable breakpoints matching the specified source file and line.
      command -- Commands for adding, removing and listing LLDB commands executed when a breakpoint is hit.
      delete  -- Delete the specified breakpoint(s).  If no breakpoints are specified, delete them all.
      disable -- Disable the specified breakpoint(s) without deleting them.  If none are specified, disable all breakpoints.
      enable  -- Enable the specified disabled breakpoint(s). If no breakpoints are specified, enable all of them.
      list    -- List some or all breakpoints at configurable levels of detail.
      modify  -- Modify the options on a breakpoint or set of breakpoints in the executable.  If no breakpoint is specified, acts on the last created
                 breakpoint.  With the exception of -e, -d and -i, passing an empty argument clears the modification.
      name    -- Commands to manage name tags for breakpoints
      read    -- Read and set the breakpoints previously saved to a file with "breakpoint write".
      set     -- Sets a breakpoint or set of breakpoints in the executable.
      write   -- Write the breakpoints listed to a file that can be read in with "breakpoint read".  If given no arguments, writes all breakpoints.

For more help on any particular subcommand, type 'help  '.

5. 表达式

5.1 po 格式化输出与修改值

  • po 输出的是debugDescription的内容
  • debugDescription 默认调用 description
  • 可以通过(lldb) image lookup -rn '\ debugDescription\]'命令查找实现了debugDescription 的类
(lldb) po self
debugDescription of 

(lldb) po [self description]
description of ViewController

(lldb) po [self debugDescription]
debugDescription of 

(lldb) po self.view.layer
>; allowsGroupOpacity = YES; name = VC:ViewController; backgroundColor =  [ (kCGColorSpaceICCBased; kCGColorSpaceModelMonochrome; Generic Gray Gamma 2.2 Profile; extended range)] ( 1 1 )>

(lldb) po self.view.layer.description


(lldb) po self.view.layer.debugDescription
>; allowsGroupOpacity = YES; name = VC:ViewController; backgroundColor =  [ (kCGColorSpaceICCBased; kCGColorSpaceModelMonochrome; Generic Gray Gamma 2.2 Profile; extended range)] ( 1 1 )>
  • 使用po命令修改值
(lldb) po self.titleLabel.text
快来占领榜单第一名吧
(lldb) po self.titleLabel.text = @"Test"
Test
(lldb) po self.titleLabel.text
Test

5.2 p 命令:

5.2.1 使用p命令输出所有成员信息

(lldb) p self
(Signals.MasterViewController) $R0 = 0x00007f9e03f05060 {
  UIKit.UITableViewController = {
    baseUIViewController@0 = {
      baseUIResponder@0 = {
        NSObject = {
          isa = Signals.MasterViewController
        }
      }
      _overrideTransitioningDelegate = nil
      _view = 0x00007f9e04033400
      _tabBarItem = nil
      _navigationItem = 0x00007f9e03f03de0
      _toolbarItems = nil
      _title = nil
      _nibName = 0x000060000044def0 "7bK-jq-Zjz-view-r7i-6Z-zg0"
      _nibBundle = 0x000060000009d240 "/Users/sammylan/Library/Developer/CoreSimulator/Devices/B9ABA4CA-3F98-4607-898B-254D9351490C/data/Containers/Bundle/Application/D1011E4B-2BDF-4820-AC4B-E9DBE7EFC1CF/Signals.app"
      _parentViewController = nil
      _childModalViewController = nil
      _parentModalViewController = nil
      _previousRootViewController = nil
      _modalTransitionView = nil
      _modalPreservedFirstResponder = nil
      _dimmingView = nil
      _dropShadowView = nil
      _currentAction = nil
      _storyboard = 0x0000604000268740
      _externalObjectsTableForViewLoading = 0x0000600000464940 2 key/value pairs
      _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 = 18
      _ignoreAppSupportedOrientations = false
      _viewHostsLayoutEngine = false
      _storyboardIdentifier = 0x0000604000269580 "UITableViewController-7bK-jq-Zjz"
      _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 = 0
      _topLayoutGuide = nil
      _bottomLayoutGuide = nil
      _topBarInsetGuideConstraint = nil
      _bottomBarInsetGuideConstraint = nil
      _storyboardSegueTemplates = 0x000060000000d070 1 element
      _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
    }
    _tableViewStyle = 0
    _keyboardSupport = nil
    _staticDataSource = nil
    _filteredDataSource = 0x000060000044d9e0
    _filteredDataType = 0
  }
  detailViewController = nil
}

5.2.2 使用p命令格式化输出

兼容C的格式化输出: https://sourceware.org/gdb/onlinedocs/gdb/输出-格式.html

  • x:十六进制,如 p/x 10
  • d:十进制,如 p/x 10
  • u:无符号十进制数
  • o:八进制
  • t:二进制, 如p/t 10
  • a:地址,
  • c:字符常数
  • f:浮动
  • s:字符串

LLDB 提供的格式化输出(取自):http://lldb.llvm.org/varformats.html

  • B:布尔值
  • b:二进制
  • y:字节
  • Y:带ASCII的字节
  • c:字符
  • C:可打印字符
  • F:复杂浮动
  • s:c型管柱
  • i:十进制
  • E:枚举
  • x:十六进制
  • f:浮动
  • o:八进制
  • O: 十进制
  • U:unicode16
  • u:无符号十进制数
  • p:指针

5.3 Swift与Objective-C调试上下文

5.3.1 默认调试上下文

在调试程序时有两个调试上下文:非Swift调试上下文Swift调试上下文:

  1. 当你在Objective-C代码中停止时,LLDB将使用非Swift(Objective-C)上下文
  2. 当你在Swift代码中停止,LLDB将使用Swift调试上下文
  3. 如果您突然停止调试器,默认情况下LLDB将选择Objective-C调试上下文
    如在swift代码中设置断点并停下,执行以下命令将出错
(lldb) po [UIApplication sharedApplication]
error: :3:16: error: expected ',' separator
[UIApplication sharedApplication]
               ^
              ,

5.3.2 指定调试上下文: expression -l

在swift调试上下文下,可以通过-l命令指定上下文,如下:

(lldb) expression -l objc -O -- [UIApplication sharedApplication]

注意:po 被映射为expression-O-,无法使用po 显式指定上下文.
方然也可以使用swift语法打印相应值:

(lldb) po UIApplication.shared
 

突然停止调试器,会默认进入非swift调试上下文,此时使用swift语法打印相应值也会出错,使用objective-c语法则会成功,如下:

(lldb) po UIApplication.shared
error: property 'shared' not found on object of type 'UIApplication'

(lldb) po [UIApplication sharedApplication]


(lldb) expression -l swift -O -- UIApplication.shared

5.4 用户自定义变量

在调试的时候,可以创建一些变量供后面使用,注意:变量名必需是$开头的,如下,直接创建变量(test)无法给后面使用,需要$开头的变量($test)才能在后面继续使用.

(lldb) po id test = [NSObject new]
(lldb) po test
error: use of undeclared identifier 'test'
(lldb) po id $test = [NSObject new]
(lldb) po $test

注意:在Objective-C上下文中创建的LLDB用户自定义变量,转到Swift上下文后,不一定能正常工作(尚未实现的功能)
在swift调试上下文下,执行p obj后,会自动生成一个简短的LLDB用户自定义变量,可以在后续使用,如下,生成了个简短的$R0变量

(lldb) p self
(Signals.MasterViewController) $R0 = 0x00007f9e03f05060 {
...
      _nibName = 0x000060000044def0 "7bK-jq-Zjz-view-r7i-6Z-zg0"
...
(lldb) po $R0.nibName
▿ Optional
  - some : "7bK-jq-Zjz-view-r7i-6Z-zg0"

(lldb) p $R0.nibName
(String?) $R10 = "7bK-jq-Zjz-view-r7i-6Z-zg0"

(lldb) po  $R10
▿ Optional
  - some : "7bK-jq-Zjz-view-r7i-6Z-zg0"

6. 线程与堆栈

6.1 xcode 单步调试基本命令

  • 如上图,从左到右依次为:
  1. continue / pause : 继续运行(或者遇到下一个断点停止)/ 暂停运行
  2. step over: 单步执行,在函数内遇到子函数时不会进入子函数.
  3. step into: 单步执行,遇到子函数就进入并且继续单步执行.
  4. step out: 在单步执行到子函数内时,按 step out 就可以执行完子函数余下部分并返回上一层函数.
  • 指令级单步调试:Ctrl +
    按住 Ctrl 执行step overstep into功能,将执行汇编指令级别的单步调试,按住Ctrl + step into 可进入无源码函数进行单步调试
  • 多线程单步调试:Ctrl + Shift
    Ctrl + Shift 执行step overstep into功能时将只单步调试当前线程(其它线程进入暂停状态)

6.2 查看堆栈信息

6.2.1 thread backtrace: 查看堆栈信息

(lldb) thread backtrace
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
  * frame #0: 0x000000010556d7c2 Signals`MasterViewController.viewDidLoad(self=0x00007fd887c0a9a0) at MasterViewController.swift:38:5
    frame #1: 0x000000010556e03b Signals`@objc MasterViewController.viewDidLoad() at :0
    frame #2: 0x000000010fe400f7 UIKitCore`-[UIViewController loadViewIfRequired] + 1183
    frame #3: 0x000000010fe40524 UIKitCore`-[UIViewController view] + 27
    frame #4: 0x00000001105dd4b5 UIKitCore`__67-[UIStoryboardEmbedSegueTemplate newDefaultPerformHandlerForSegue:]_block_invoke + 180
    frame #5: 0x00000001105df4d3 UIKitCore`-[UIStoryboardSegueTemplate _performWithDestinationViewController:sender:] + 276
    frame #6: 0x00000001105df391 UIKitCore`-[UIStoryboardSegueTemplate _perform:] + 82
    frame #7: 0x00000001105df653 UIKitCore`-[UIStoryboardSegueTemplate perform:] + 157
    frame #8: 0x000000010fe3fee3 UIKitCore`-[UIViewController loadViewIfRequired] + 651
    frame #9: 0x000000010fe40524 UIKitCore`-[UIViewController view] + 27
    frame #10: 0x000000010fdb1690 UIKitCore`-[UINavigationController preferredContentSize] + 197
    frame #11: 0x000000010fdd745b UIKitCore`-[UIViewController(UIPopoverController_Internal) _resolvedPreferredContentSize] + 62
    frame #12: 0x000000010fdde97f UIKitCore`-[UIPopoverController _transitionFromViewController:toViewController:animated:] + 141
    frame #13: 0x000000010fdd7c0b UIKitCore`-[UIPopoverController _initWithContentViewController:popoverControllerStyle:] + 699
    frame #14: 0x000000010fe1463b UIKitCore`-[UISplitViewControllerClassicImpl _setupHiddenPopoverControllerWithViewController:] + 188
    frame #15: 0x000000010fe1323e UIKitCore`-[UISplitViewControllerClassicImpl _addOrRemovePopoverPresentationGestureRecognizer] + 121
    frame #16: 0x000000010fe128db UIKitCore`-[UISplitViewControllerClassicImpl loadView] + 339
    frame #17: 0x000000010fe3fd04 UIKitCore`-[UIViewController loadViewIfRequired] + 172
    frame #18: 0x000000010fe40524 UIKitCore`-[UIViewController view] + 27

6.2.2 frame 命令: 栈帧操作

frame 常用命令

  • frame info : 列出当前栈帧信息
  • frame select: 选择某一层栈帧作为当前栈帧
  • frame variable: 查看当前栈帧变量,默认为展开方式输出, -F 选项用平铺方式输出
(lldb) frame info
frame #0: 0x000000010556d7c2 Signals`MasterViewController.viewDidLoad(self=0x00007fd887c0a9a0) at MasterViewController.swift:38:5
(lldb) frame select 1
frame #1: 0x000000010556e03b Signals`@objc MasterViewController.viewDidLoad() at :0
(lldb) frame info
frame #1: 0x000000010556e03b Signals`@objc MasterViewController.viewDidLoad() at :0
(lldb) frame variable     #默认展开方式输出
(Bool) animated = false
(Signals.MasterViewController) self = 0x00007f9d4fd076b0 {
  UIKit.UITableViewController = {
    baseUIViewController@0 = {
      baseUIResponder@0 = {
        baseNSObject@0 = {
          isa = Signals.MasterViewController
        }
      }
# 其它未列出
(lldb) frame variable -F self      # -F参数,平铺方式输出
self = 0x00007f9d4fd076b0
self =
self.isa = Signals.MasterViewController
self._overrideTransitioningDelegate = nil
self._view = some
self._view.some = 0x00007f9d5a034000
self._view.some.isa = UITableView
# 其它未列出
  • 详细帮助信息如下:
(lldb) help fram
     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.
      recognizer -- Commands for editing and viewing frame recognizers.
      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'.  The -> and [] operators in 'frame
                    variable' do not invoke operator overloads if they exist,
                    but directly access the specified element.  If you want to
                    trigger operator overloads use the expression command to
                    print the variable instead.
                    It is worth noting that except for overloaded operators,
                    when printing local variables 'expr local_var' and 'frame
                    var local_var' produce the same results.  However, 'frame
                    variable' is more efficient, since it uses debug
                    information and memory reads directly, rather than parsing
                    and evaluating an expression, which may even involve JITing
                    and running code in the target program.

For more help on any particular subcommand, type 'help  '.

6.1 LLDB中的 单步调试基本命令

Xcode LLDB 描述
continue continue 继续运行(或者遇到下一个断点停止)
step over next 单步执行,在函数内遇到子函数时不会进入子函数.
step into step 单步执行,遇到子函数就进入(有源码才可以)并且继续单步执行.
Ctrl + step into step -a0 单步执行,遇到子函数就进入(无源码也可以)并且继续单步执行.
step out finish 执行完当前栈帧并返回上一层调用
  1. continue / pause : 继续运行(或者遇到下一个断点停止)/ 暂停运行
  2. step over: 单步执行,在函数内遇到子函数时不会进入子函数.
  3. step into: 单步执行,遇到子函数就进入并且继续单步执行.
  4. step out: 在单步执行到子函数内时,按 step out 就可以执行完子函数余下部分并返回上

7. image: 进程模块信息

7.1 image 常用命令

  • image list: 列出所有模块信息,
  • image list 模块名: 列出指定模块信息
  • image dump symtab : 输出符号信息
  • image dump symtab 模块名 :输出特定模块的符号信息
  • image lookup :查找符号信息
(lldb) image list Foundation
[  0] 30153EA5-45E2-334A-99DF-6E79D88AB4D0 0x000000010303d000 /Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 12.4.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/Foundation.framework/Foundation 

(lldb) image lookup -n "-[UIViewController viewDidLoad]"
1 match found in /Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 12.4.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore:
        Address: UIKitCore[0x000000000034cab5] (UIKitCore.__TEXT.__text + 3451269)
        Summary: UIKitCore`-[UIViewController viewDidLoad]

(lldb) image dump symtab UIKit -s address
Symtab, file = /Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 12.4.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/UIKit.framework/UIKit, num_symbols = 3 (sorted by address):
               Debug symbol
               |Synthetic symbol
               ||Externally Visible
               |||
Index   UserID DSX Type            File Address/Value Load Address       Size               Flags      Name
------- ------ --- --------------- ------------------ ------------------ ------------------ ---------- ----------------------------------
[    0]      0     Data            0x0000000000000fd0 0x0000000104715fd0 0x0000000000000028 0x001e0000 UIKitVersionString
[    1]      1     Data            0x0000000000000ff8 0x0000000104715ff8 0x0000000000000008 0x001e0000 UIKitVersionNumber

其中:

  1. 模块的UUID: UUID对于寻找符号信息和唯一标识基础模块,30153EA5-45E2-334A-99DF-6E79D88AB4D0
  2. 加载地址: 0x000000010303d000
  3. 模块路径: /Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 12.4.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/Foundation.framework/Foundation

7.2 image lookup: 查找符号

  1. image lookup -a: 根据地址查找符号信息
  2. image lookup -n: 根据符号名字查找符号信息
  3. image lookup -r: 启用正则表达式查找符号
image lookup -rn "UIViewController"  ;查找UIViewController相关的全部符号
image lookup -rn "-\[UIViewController" ;查找UIViewController的实例方法
image lookup -rn "\+\[UIViewController"; 查找UIViewController的类方法
image lookup -rn "-\[UIViewController\sset" ; 查找UIViewController的所有set方法
image lookup -rn "-\[UIViewController\(\w+\)" 查找UIViewController的所有分类的方法

注意,以下字符需要转义:
[,],(,),(空格,也可以用\s代替),+,-

7.3 给所有Block下断点

  1. 在一个Block中打断点,执行到Block的时候,输入以下命令查看Block的命名规范.block 都是包含 _block_invoke_ 的函数调用
(lldb) frame info
frame #0: 0x000000010b9f1440 Commons`__34+[UnixSignalHandler sharedHandler]_block_invoke(.block_descriptor=0x000000010b9f9348) at UnixSignalHandler.m:68:28
  1. 查找特定模块的所有Block: image lookup -rn _block_invoke <模块名>
(lldb) image lookup -rn _block_invoke Commons
6 matches found in /Users/sammylan/Library/Developer/Xcode/DerivedData/Signals-efbmlrmvuqjbiyfptsisstosyqdx/Build/Products/Debug-iphonesimulator/Signals.app/Frameworks/Commons.framework/Commons:
        Address: Commons[0x0000000000004830] (Commons.__TEXT.__text + 1152)
        Summary: Commons`__32-[UnixSignalHandler initPrivate]_block_invoke at UnixSignalHandler.m:78        Address: Commons[0x0000000000004430] (Commons.__TEXT.__text + 128)
        Summary: Commons`__34+[UnixSignalHandler sharedHandler]_block_invoke at UnixSignalHandler.m:67        Address: Commons[0x0000000000004bc0] (Commons.__TEXT.__text + 2064)
        Summary: Commons`__38-[UnixSignalHandler appendSignal:sig:]_block_invoke at UnixSignalHandler.m:119        Address: Commons[0x0000000000004c00] (Commons.__TEXT.__text + 2128)
        Summary: Commons`__38-[UnixSignalHandler appendSignal:sig:]_block_invoke_2 at UnixSignalHandler.m:123        Address: Commons[0x0000000000004f70] (Commons.__TEXT.__text + 3008)
        Summary: Commons`__38-[UnixSignalHandler appendSignal:sig:]_block_invoke_3 at UnixSignalHandler.m:135        Address: Commons[0x0000000000004a70] (Commons.__TEXT.__text + 1728)
        Summary: Commons`__32-[UnixSignalHandler initPrivate]_block_invoke.24 at UnixSignalHandler.m:105
  1. 给特定模块的所有Block下断点:rb appendSignal.*_block_invoke -s Commons
    该断点是在Commons里的appendSignal函数下的所有Block下断点
(lldb)  rb appendSignal.*_block_invoke -s Commons
Breakpoint 6: 3 locations.

8 自定义命令及持久化"command alias

lldb 可以使用command alias自定义命令

(lldb) help command alias
     Define a custom command in terms of an existing command.  Expects 'raw' input (see 'help raw-input'.)

Syntax: command alias  --   []

Command Options Usage:
  command alias [-h ] [-H ] --   []
  command alias   []

       -H  ( --long-help  )
            Long help text for this command

       -h  ( --help  )

command alias -- Yay_Autolayout expression -l objc -O -- [[[[[UIApplication sharedApplication] keyWindow] rootViewController] view] recursiveDescription]
command alias cpo expression -l objc -O --
command alias sc script
command alias bp breakpoint
command alias bpl breakpoint list
command alias bcppfl breakpoint set -f %1.cpp -l %2
command alias bfl breakpoint set -f %1 -l %2

需要持久化的自定义命令需要保存到 ~/.lldbinit.
~/.lldbinit中新增的命令在LLDB中无法立刻使用,需要重启LLDB或者执行以下命令加载新增命令

(lldb) command source ~/.lldbinit
Executing commands in '/Users/sammylan/.lldbinit'.
(lldb)  command alias -- Yay_Autolayout expression -l objc -O -- [[[[[UIApplication sharedApplication] keyWindow] rootViewController] view] recursiveDescription]
warning: Overwriting existing definition for 'Yay_Autolayout'.
command alias cpo expression -l objc -O --
command alias sc script
command alias bp breakpoint
command alias bpl breakpoint list
command alias bcppfl breakpoint set -f %1.cpp -l %2
command alias bfl breakpoint set -f %1 -l %2

输入参数可以用%1,%2,...定义,可以直接在LLDB中使用自定义命令,如下:

(lldb) bfl KSDataNodeManager.m 30
Breakpoint 15: where = QQKSong`-[KSDataNodeManager init] + 36 at KSDataNodeManager.m:30:12, address = 0x0000000102fa0e8c
(lldb) 

命令别名: command regex

我们发现,无法使用command aliasimage lookup提供别名,如:

(lldb) command alias rnlook image lookup -rn
(lldb) rnlook viewWillAppear
warning: Unable to find an image that matches 'lookup'.

这时候就需要用到 command regex来定义别名,command regex的语法如下:
regex [s/// ...]
该命令定义部分的内容由

(lldb) help command regex
     Define a custom command in terms of existing commands by matching regular expressions.

Syntax: command regex  [s/// ...]

Command Options Usage:
  command regex [-h ] [-s ]

       -h  ( --help  )
            The help text to display for this command.

       -s  ( --syntax  )
            A syntax string showing the typical usage syntax.

image lookup -rn定义别名,如下

(lldb) command regex rlook 's/(.+)/image lookup -rn %1/'
(lldb) rlook viewWillAppear
2 matches found in /Users/sammylan/Library/Developer/Xcode/DerivedData/Signals-efbmlrmvuqjbiyfptsisstosyqdx/Build/Products/Debug-iphonesimulator/Signals.app/Signals:
        Address: Signals[0x0000000100004050] (Signals.__TEXT.__text + 3216)
        Summary: Signals`Signals.MasterViewController.viewWillAppear(Swift.Bool) -> () at MasterViewController.swift:51        Address: Signals[0x0000000100004780] (Signals.__TEXT.__text + 5056)
        Summary: Signals`@objc Signals.MasterViewController.viewWillAppear(Swift.Bool) -> () at 

命令速查

查看内存 x

x/nuf 

n 表示要显示的内存单元的个数
------------------------
u 表示一个地址单元的长度
b 表示单字节
h 表示双字节
w 表示4字节
g 表示8字节
------------------------
f 表示显示方式,可取以下值:
x 按十六进制格式显示变量
d 按十进制格式显示变量
u 按十进制格式显示无符号整型
o 按八进制格式显示变量
t 按二进制格式显示变量
a 按十六进制格式显示变量
i 按指令地址格式显示变量
c 按字符格式显示变量
f 按浮点数格式显示变量

你可能感兴趣的:(LLDB 学习笔记)