Advanced.Apple.Debugging.&.Reverse.Engineering.v2.0 学习笔记
1. 入门
1.1 禁用Rootless 与开启调试功能
为了提高系统的安全性,Mac OS X 10.11开始引入了Rootless,在Rootless开启的情况下,LLDB无法调试到其它进程(模拟器内的进程除外).如果想调试其它进程,需要关闭Rootless,如下:
- 重启系统
- 开机后按下
Command + R
直到进入恢复模式 - 从顶部菜单中找到
Utilities
菜单并选择Terminal
- 输入以下命令禁用Rootless
csrutil disable
reboot
1.2 使用LLDB基本流程-使用LLDB 调试Xcode
实现如下功能
- 使用LLDB调试Xcode
- 将Xcode的输出重定向到终端
1.2.1 创建重定向终端(tty
)
使用tty
命令创建重定向终端,此时生成的终端名为ttys002
~ tty
/dev/ttys001
1.2.2 使用LLDB启动Xcode
- 使用
lldb
命令进入LLDB - 设置可执行目标文件路径(
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
- 启动并重定向到终端(之前创建的重定向终端:ttys001)
(lldb) process launch -e /dev/ttys001 --
Process 92898 launched: '/Applications/Xcode.app/Contents/MacOS/Xcode' (x86_64)
-
Ctrl + C
暂停调试器 - 设置断点(
b
):
(lldb) b -[NSView hitTest:]
Breakpoint 1: where = AppKit`-[NSView hitTest:], address = 0x00007fff31e25f9b
- 继续调试(
continue
)
(lldb) continue
Process 34209 resuming
- 触发断点: 点击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.
- 查看那个视图被点击(
po
命令):
(lldb) po $rdi
<NSThemeFrame: 0x1651174c0>
(lldb) po [$rdi superclass]
NSTitledFrame
(lldb) po [[$rdi superclass] superclass]
NSFrameView
(lldb) po [[[$rdi superclass] superclass] superclass]
NSView
$rdi 寄存器包含调用hitTest:方法的子类NSView的实例
1.3 常用快捷键
-
Ctrl + C
:暂停调试器 -
Ctrl + D
:结束调试 -
Command + K
: 清空debug区域输出
2. LLDB 帮助与查询
2.1 LLDB命令格式
<noun> <verb> [-options [option-value]] [argument [argument...]]
即:<关键字> <动作> [-可选项 [可选项的值]] [参数1 [参数2···]]
- 所有命令使用同一的格式
- 参数,可选项和选项值都用空格分隔
- 使用双引号保护参数中的空格内容
-
\
和"
使用\
进行转义,如\"
表示"
,\\
表示\
- 如果参数以
-
开始,需要在可选项和参数之间使用--
加以分隔 - 可选项的顺序可以不固定
(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 [<cmd-name>]
Command Options Usage:
help [-ahu] [<cmd-name> [<cmd-name> [...]]]
-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 <subcommand> [<command-options>]
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 <command> <subcommand>'.
2.3 查询命令:apropos
在不知道具体命令但知道相应关键字的时候,可以用apropos
命令进行查询,语法如下
apropos <search-word>
比如查询跟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 附加到现有进程
- 通过进程名,相当于:
process attach --name
~ lldb -n Xcode
(lldb) process attach --name "Xcode"
- 通过进程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 输入如下命令等待附加到即将启动的
Finder
进程
~ lldb -n Finder -w
(lldb) process attach --name "Finder" --waitfor
- 在终端2 输入如下命令,该命令将关闭所有
Finder
~ pkill Finder
- 系统将自动启动
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 <进程路径>
- 通过
lldb -f <进程路径>
命令指定可执行文件
~ lldb -f /System/Library/CoreServices/Finder.app/Contents/MacOS/Finder
- 运行
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 [<run-args>]
Command Options Usage:
run [<run-args>]
'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
- 将文件内容输出到文件
~ 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: 查找符号
-
image lookup -a
: 根据地址查找符号信息 -
image lookup -n
: 根据符号名字查找符号信息 -
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 <cmd-options> [<filename> [<filename> [...]]]
Command Options Usage:
target modules lookup [-Av] -a <address-expression> [-o <offset>] [<filename> [<filename> [...]]]
target modules lookup [-Arv] -s <symbol> [<filename> [<filename> [...]]]
target modules lookup [-Aiv] -f <filename> [-l <linenum>] [<filename> [<filename> [...]]]
target modules lookup [-Airv] -F <function-name> [<filename> [<filename> [...]]]
target modules lookup [-Airv] -n <function-or-symbol> [<filename> [<filename> [...]]]
target modules lookup [-Av] -t <name> [<filename> [<filename> [...]]]
-A ( --all )
Print all matches, not just the best match, if a best match is
available.
-F <function-name> ( --function <function-name> )
Lookup a function by name in the debug symbols in one or more
target modules.
-a <address-expression> ( --address <address-expression> )
Lookup an address in one or more target modules.
-f <filename> ( --file <filename> )
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 <linenum> ( --line <linenum> )
Lookup a line number in a file (must be used in conjunction with
--file).
-n <function-or-symbol> ( --name <function-or-symbol> )
Lookup a function or symbol by name in one or more target modules.
-o <offset> ( --offset <offset> )
When looking up an address subtract <offset> from any addresses
before doing the lookup.
-r ( --regex )
The <name> argument for name lookups are regular expressions.
-s <symbol> ( --symbol <symbol> )
Lookup a symbol by name in the symbol tables in one or more target
modules.
-t <name> ( --type <name> )
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
会生成如下两个setter
和getter
方法:
-[TestClass name];
-[TestClass setName:];
4.2.2 Swift 属性名字重整
在Swift中如下定义一个属性: name
class SwiftTestClass: NSObject {
var name: String!
}
会生成如下两个setter
和getter
方法,其中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<Swift.String> at <compiler-generated> Address: Signals[0x000000010000e510] (Signals.__TEXT.__text + 44624)
Summary: Signals`key path getter for Signals.SwiftTestClass.name : Swift.Optional<Swift.String> : Signals.SwiftTestClass at <compiler-generated> Address: Signals[0x000000010000e580] (Signals.__TEXT.__text + 44736)
Summary: Signals`key path setter for Signals.SwiftTestClass.name : Swift.Optional<Swift.String> : Signals.SwiftTestClass at <compiler-generated> Address: Signals[0x000000010000e620] (Signals.__TEXT.__text + 44896)
Summary: Signals`Signals.SwiftTestClass.name.getter : Swift.Optional<Swift.String> at SwiftTestClass.swift:28 Address: Signals[0x000000010000e6c0] (Signals.__TEXT.__text + 45056)
Summary: Signals`Signals.SwiftTestClass.name.setter : Swift.Optional<Swift.String> at SwiftTestClass.swift:28 Address: Signals[0x000000010000e780] (Signals.__TEXT.__text + 45248)
Summary: Signals`Signals.SwiftTestClass.name.modify : Swift.Optional<Swift.String> at SwiftTestClass.swift:28 Address: Signals[0x000000010000e7d0] (Signals.__TEXT.__text + 45328)
Summary: Signals`Signals.SwiftTestClass.name.modify : Swift.Optional<Swift.String> 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 <filename>:<linenum>
main.c:12 // Break at line 12 of main.c
_regexp-break <linenum>
12 // Break at line 12 of current file
_regexp-break 0x<address>
0x1234000 // Break at address 0x1234000
_regexp-break <name>
main // Break in 'main' after the prologue
_regexp-break &<name>
&main // Break at first instruction in 'main'
_regexp-break <module>`<name>
libc.so`malloc // Break in 'malloc' from 'libc.so'
_regexp-break /<source-regex>/
/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<Swift.String>
Breakpoint 15: where = Signals`Signals.SwiftTestClass.name.setter : Swift.Optional<Swift.String> + 174 at SwiftTestClass.swift:28:7, address = 0x000000010da5376e
(lldb) b SwiftTestClass.name.setter : Swift.Optional<Swift.String>
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<Swift.String> + 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
选项:源码正则选项,在符合特定源码规则的地方停止
-
breakpoint set -A -p "if let"
: 在包含if-let的每个源代码位置上创建一个断点 -
breakpoint set -p "if let" -s Signals -A
: 在Signals里包含if-let的每个源代码位置上创建一个断点
-
-c
选项:条件断点:
-
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 <subcommand> [<command-options>]
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 <command> <subcommand>'.
5. 表达式
5.1 po
格式化输出与修改值
-
po
输出的是debugDescription
的内容 -
debugDescription
默认调用description
- 可以通过
(lldb) image lookup -rn '\ debugDescription\]'
命令查找实现了debugDescription
的类
(lldb) po self
debugDescription of <ViewController: 0x7f801bc05770>
(lldb) po [self description]
description of ViewController
(lldb) po [self debugDescription]
debugDescription of <ViewController: 0x7f801bc05770>
(lldb) po self.view.layer
<CALayer:0x600001c3c760; name = "VC:ViewController"; position = CGPoint (207 368); bounds = CGRect (0 0; 414 736); delegate = <UIView: 0x7fe813c09da0; frame = (0 0; 414 736); autoresize = W+H; layer = <CALayer: 0x600001c3c760>>; allowsGroupOpacity = YES; name = VC:ViewController; backgroundColor = <CGColor 0x600003f232f0> [<CGColorSpace 0x60000382c840> (kCGColorSpaceICCBased; kCGColorSpaceModelMonochrome; Generic Gray Gamma 2.2 Profile; extended range)] ( 1 1 )>
(lldb) po self.view.layer.description
<CALayer: 0x600001c3c760>
(lldb) po self.view.layer.debugDescription
<CALayer:0x600001c3c760; name = "VC:ViewController"; position = CGPoint (207 368); bounds = CGRect (0 0; 414 736); delegate = <UIView: 0x7fe813c09da0; frame = (0 0; 414 736); autoresize = W+H; layer = <CALayer: 0x600001c3c760>>; allowsGroupOpacity = YES; name = VC:ViewController; backgroundColor = <CGColor 0x600003f232f0> [<CGColorSpace 0x60000382c840> (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调试上下文
:
- 当你在
Objective-C代码
中停止时,LLDB将使用非Swift(Objective-C)上下文
- 当你在
Swift代码
中停止,LLDB将使用Swift调试上下文
。 - 如果您
突然停止调试器
,默认情况下LLDB将选择Objective-C调试上下文
。
如在swift代码中设置断点并停下,执行以下命令将出错
(lldb) po [UIApplication sharedApplication]
error: <EXPR>:3:16: error: expected ',' separator
[UIApplication sharedApplication]
^
,
5.3.2 指定调试上下文: expression -l
在swift调试上下文下,可以通过-l
命令指定上下文,如下:
(lldb) expression -l objc -O -- [UIApplication sharedApplication]
<UIApplication: 0x7fe7d2c015d0>
注意:po
被映射为expression-O-
,无法使用po
显式指定上下文.
方然也可以使用swift语法打印相应值:
(lldb) po UIApplication.shared
<UIApplication: 0x7fe7d2c015d0>
突然停止调试器,会默认进入非swift调试上下文,此时使用swift语法打印相应值也会出错,使用objective-c语法则会成功,如下:
(lldb) po UIApplication.shared
error: property 'shared' not found on object of type 'UIApplication'
(lldb) po [UIApplication sharedApplication]
<UIApplication: 0x7fe7d2c015d0>
(lldb) expression -l swift -O -- UIApplication.shared
<UIApplication: 0x7fe7d2c015d0>
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
<NSObject: 0x60400000e7f0>
注意:在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<String>
- 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<String>
- some : "7bK-jq-Zjz-view-r7i-6Z-zg0"
6. 线程与堆栈
6.1 xcode 单步调试基本命令
- 如上图,从左到右依次为:
-
continue / pause
: 继续运行(或者遇到下一个断点停止)/ 暂停运行 -
step over
: 单步执行,在函数内遇到子函数时不会进入子函数. -
step into
: 单步执行,遇到子函数就进入并且继续单步执行. -
step out
: 在单步执行到子函数内时,按step out
就可以执行完子函数余下部分并返回上一层函数.
- 指令级单步调试:
Ctrl +
按住Ctrl
执行step over
或step into
功能,将执行汇编指令级别的单步调试,按住Ctrl
+step into
可进入无源码函数进行单步调试 - 多线程单步调试:
Ctrl + Shift
Ctrl + Shift
执行step over
或step 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 <compiler-generated>: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 <compiler-generated>:0
(lldb) frame info
frame #1: 0x000000010556e03b Signals`@objc MasterViewController.viewDidLoad() at <compiler-generated>: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 <subcommand> [<subcommand-options>]
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 <command> <subcommand>'.
6.1 LLDB中的 单步调试基本命令
Xcode | LLDB | 描述 |
---|---|---|
continue | continue | 继续运行(或者遇到下一个断点停止) |
step over | next | 单步执行,在函数内遇到子函数时不会进入子函数. |
step into | step | 单步执行,遇到子函数就进入(有源码才可以)并且继续单步执行. |
Ctrl + step into | step -a0 | 单步执行,遇到子函数就进入(无源码也可以)并且继续单步执行. |
step out | finish | 执行完当前栈帧并返回上一层调用 |
-
continue / pause
: 继续运行(或者遇到下一个断点停止)/ 暂停运行 -
step over
: 单步执行,在函数内遇到子函数时不会进入子函数. -
step into
: 单步执行,遇到子函数就进入并且继续单步执行. -
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
其中:
- 模块的UUID: UUID对于寻找符号信息和唯一标识基础模块,30153EA5-45E2-334A-99DF-6E79D88AB4D0
- 加载地址: 0x000000010303d000
- 模块路径: /Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 12.4.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/Foundation.framework/Foundation
7.2 image lookup: 查找符号
-
image lookup -a
: 根据地址查找符号信息 -
image lookup -n
: 根据符号名字查找符号信息 -
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下断点
- 在一个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
- 查找特定模块的所有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
- 给特定模块的所有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 <cmd-options> -- <alias-name> <cmd-name> [<options-for-aliased-command>]
Command Options Usage:
command alias [-h <help-text>] [-H <help-text>] -- <alias-name> <cmd-name> [<options-for-aliased-command>]
command alias <alias-name> <cmd-name> [<options-for-aliased-command>]
-H <help-text> ( --long-help <help-text> )
Long help text for this command
-h <help-text> ( --help <help-text> )
如
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 alias
给image 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 <cmd-name> [s/<regex>/<subst>/ ...]
该命令定义<regex>
部分的内容由
(lldb) help command regex
Define a custom command in terms of existing commands by matching regular expressions.
Syntax: command regex <cmd-name> [s/<regex>/<subst>/ ...]
Command Options Usage:
command regex [-h <none>] [-s <none>]
-h <none> ( --help <none> )
The help text to display for this command.
-s <none> ( --syntax <none> )
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 <compiler-generated>
网友评论