LLDB全称Low Level Debugger ,是轻量级的高性能调试器,内置于Xcode,有公司或个人开发了一系列插件来扩展了lldb调试器的功能,使我们在调试代码的时候更方便快捷,常见的插件有Chisel
和LLDB
两个。Chisel是Facebook开源的一款lldb调试工具,推荐通过homebrew安装管理,如果电脑没有homebrew需要先安装;LLDB是DerekSelander开源的一款lldb调试工具,能够很好的运用这两款插件会使开发效率事半功倍。
1、安装Homebrew
Homebrew是一款Mac OS平台下的软件包管理工具,拥有安装、卸载、更新、查看、搜索等很多实用的功能。简单的一条指令,就可以实现包管理,而不用你关心各种依赖和文件路径的情况,十分方便快捷。
====== 2023.03.28更新 ======
使用下面的命令安装,兼容了M1、M2系列的macOS,并且会自动配置环境变量。
/bin/bash -c "$(curl -fsSL https://gitee.com/ineo6/homebrew-install/raw/master/install.sh)"
查看brew安装位置
~ % where brew
/opt/homebrew/bin/brew
/usr/local/bin/brew
可以看到在不同位置安装了两个brew,ARM版Homebrew最终被安装在/opt/homebrew
路径下,直接使用brew install ...
命令会报错
~ % brew install chisel
Error: Cannot install under Rosetta 2 in ARM default prefix (/opt/homebrew)!
To rerun under ARM use:
arch -arm64 brew install ...
To install under x86_64, install Homebrew into /usr/local.
根据提示,ARM版要使用arch -arm64 brew install ...
命令来安装
~ % arch -arm64 brew install chisel
==> Fetching chisel
==> Downloading https://mirrors.ustc.edu.cn/homebrew-bottles/bottles/chisel-2.0.
######################################################################## 100.0%
==> Pouring chisel-2.0.1.arm64_monterey.bottle.tar.gz
==> Caveats
Add the following line to ~/.lldbinit to load chisel when Xcode launches:
command script import /opt/homebrew/opt/chisel/libexec/fbchisellldb.py
==> Summary
/opt/homebrew/Cellar/chisel/2.0.1: 33 files, 290.2KB
==> Running `brew cleanup chisel`...
Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP.
Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).
因chisel安装位置的变化,在配置.lldbinit
文件时.py
文件的索引路径会相应变化,注意这点即可
#命令不变,只是索引路径有变化
command script import /opt/homebrew/opt/chisel/libexec/fbchisellldb.py
------ 扩展阅读,仅作为记录 ------
从macOS Catalina(10.15.x) 版开始,Mac使用zsh作为默认Shell,取代了之前的bash,所以配置环境变量需要区分终端类型。
使用echo $SHELL
命令可以打印出当前的终端类型
echo $SHELL
/bin/zsh(或/bin/bash)
如果是/bin/zsh
,zsh
为默认Shell,.zprofile
为默认配置文件,用下面的命令设置环境变量
echo 'eval "$(/opt/homebrew/bin/brew shellenv)" #brew.idayer.com' >> ~/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"
source ~/.zprofile
如果是/bin/bash
,bash
为默认Shell,.bash_profile
为默认配置文件,用下面的命令设置环境变量
echo 'eval "$(/opt/homebrew/bin/brew shellenv)" #brew.idayer.com' >> ~/.bash_profile
eval "$(/opt/homebrew/bin/brew shellenv)"
source ~/.bash_profile
扩展阅读:在 Mac 上将 zsh 用作默认 Shell
====== end ======
1.1、官网安装:
打开终端,输入下面的命令
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
在国内下载速度会非常慢,常用的做法一是换国内镜像安装,二是设置全局代理,推荐使用镜像。
1.2、镜像安装:
/usr/bin/ruby -e "$(curl -fsSL https://cdn.jsdelivr.net/gh/ineo6/homebrew-install/install)"
遇到了下面的提示:
Warning: Ruby版本Homebrew安装脚本已被废弃,新版脚本使用Bash重写。
使用新命令快速安装:
/bin/bash -c "$(curl -fsSL https://cdn.jsdelivr.net/gh/ineo6/homebrew-install/install.sh)"
新命令脚本已经内置 中科大镜像,所以安装会很快,安装成功后可以使用brew help
命令获取使用帮助:
Example usage:
brew search TEXT|/REGEX/
brew info [FORMULA|CASK...]
brew install FORMULA|CASK...
brew update
brew upgrade [FORMULA|CASK...]
brew uninstall FORMULA|CASK...
brew list [FORMULA|CASK...]
Troubleshooting:
brew config
brew doctor
brew install --verbose --debug FORMULA|CASK
Contributing:
brew create URL [--no-fetch]
brew edit [FORMULA|CASK...]
Further help:
brew commands
brew help [COMMAND]
man brew
https://docs.brew.sh
例如可以使用brew config
命令查看安装的homebrew
信息:
HOMEBREW_VERSION: 3.3.9
ORIGIN: https://mirrors.ustc.edu.cn/brew.git
HEAD: 96137bc19e68398ebbb7033379df288cd8b9a3f9
Last commit: 2 weeks ago
Core tap ORIGIN: https://mirrors.ustc.edu.cn/homebrew-core.git
Core tap HEAD: 19cc44afda820c2ed6f0940909fdf18f0465909f
Core tap last commit: 4 hours ago
Core tap branch: master
HOMEBREW_PREFIX: /usr/local
HOMEBREW_CASK_OPTS: []
HOMEBREW_CORE_GIT_REMOTE: https://github.com/Homebrew/homebrew-core
HOMEBREW_MAKE_JOBS: 4
Homebrew Ruby: 2.6.8 => /usr/local/Homebrew/Library/Homebrew/vendor/portable-ruby/2.6.8/bin/ruby
CPU: quad-core 64-bit broadwell
Clang: 13.0.0 build 1300
Git: 2.13.0 => /usr/local/bin/git
Curl: 7.64.1 => /usr/bin/curl
macOS: 11.6-x86_64
CLT: 13.2.0.0.1.1638488800
Xcode: 13.1
2、查看及切换源
安装完后使用brew
命令来安装软件包会很慢,因为Homebrew默认使用官网的源,可以切换到国内的源。Homebrew主要由4部分组成,分别是 brew、homebrew-core、homebrew-bottles和homebrew-cask
,切换源的时候四部分都要换掉。
2.1、查看
查看brew
源:
cd "$(brew --repo)" && git remote -v
回车,结果如下,可以得知当前是中科大的源:
origin https://mirrors.ustc.edu.cn/brew.git (fetch)
origin https://mirrors.ustc.edu.cn/brew.git (push)
查看brew-core
源
cd "$(brew --repo homebrew/core)" && git remote -v
回车,结果如下:
origin https://mirrors.ustc.edu.cn/homebrew-core.git (fetch)
origin https://mirrors.ustc.edu.cn/homebrew-core.git (push)
2.2、切换
国内常用的源有四个
中科大:https://mirrors.ustc.edu.cn/brew.git
清华:https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/brew.git
北京外国语:https://mirrors.bfsu.edu.cn/git/homebrew/brew.git
腾讯:https://mirrors.cloud.tencent.com/homebrew/brew.git
直接上切换脚本,过程一致,url后缀有细微的变化
- 切换中科大:
# 脚本
git -C "$(brew --repo)" remote set-url origin https://mirrors.ustc.edu.cn/brew.git
git -C "$(brew --repo homebrew/core)" remote set-url origin https://mirrors.ustc.edu.cn/homebrew-core.git
git -C "$(brew --repo homebrew/cask)" remote set-url origin https://mirrors.ustc.edu.cn/homebrew-cask.git
brew update
echo 'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.ustc.edu.cn/homebrew-bottles/bottles' >> ~/.bash_profile
source ~/.bash_profile
- 切换清华:
# 脚本
git -C "$(brew --repo)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/brew.git
git -C "$(brew --repo homebrew/core)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-core.git
git -C "$(brew --repo homebrew/cask)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-cask.git
brew update
echo 'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles/bottles' >> ~/.bash_profile
source ~/.bash_profile
- 切换北京外国语:
# 脚本
git -C "$(brew --repo)" remote set-url origin https://mirrors.bfsu.edu.cn/git/homebrew/brew.git
git -C "$(brew --repo homebrew/core)" remote set-url origin https://mirrors.bfsu.edu.cn/git/homebrew/homebrew-core.git
git -C "$(brew --repo homebrew/cask)" remote set-url origin https://mirrors.bfsu.edu.cn/git/homebrew/homebrew-cask.git
brew update
echo 'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.bfsu.edu.cn/homebrew-bottles/bottles' >> ~/.bash_profile
source ~/.bash_profile
- 切换腾讯:
# 脚本
git -C "$(brew --repo)" remote set-url origin https://mirrors.cloud.tencent.com/homebrew/brew.git
git -C "$(brew --repo homebrew/core)" remote set-url origin https://mirrors.cloud.tencent.com/homebrew/homebrew-core.git
git -C "$(brew --repo homebrew/cask)" remote set-url origin https://mirrors.cloud.tencent.com/homebrew/homebrew-cask.git
brew update
echo 'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.cloud.tencent.com/homebrew-bottles/bottles' >> ~/.bash_profile
source ~/.bash_profile
选其中一个来使用就可以了,切换了源后再使用brew
命令安装软件包速度就快了。
参考:镜像快速安转Homebrew教程
3、安装调试器插件
3.1、安装Chisel
Chisel开源地址
按照官方的方法:
brew update
brew install chisel
在brew update
命令时遇到如下错误:
Error:
homebrew-core is a shallow clone.
homebrew-cask is a shallow clone.
To `brew update`, first run:
git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core fetch --unshallow
git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-cask fetch --unshallow
These commands may take a few minutes to run due to the large size of the repositories.
This restriction has been made on GitHub's request because updating shallow
clones is an extremely expensive operation due to the tree layout and traffic of
Homebrew/homebrew-core and Homebrew/homebrew-cask. We don't do this for you
automatically to avoid repeatedly performing an expensive unshallow operation in
CI systems (which should instead be fixed to not use shallow clones). Sorry for
the inconvenience!
通过下面的命令解决了:
cd /usr/local/Homebrew/Library/Taps/homebrew
rm -rf homebrew-core
rm -rf homebrew-cask
//换一个目录后
brew upgrade
安装完后需要配置.lldbinit
文件到根目录下,以便Xcode在启动的时候去加载chisel,根目录下如果没有.lldbinit
文件,就新建一个:
touch ~/.lldbinit
之后需要把fbchisellldb.py
文件的索引路径配置进.lldbinit
文件里,用命令的方式如下
echo "command script import /usr/local/opt/chisel/libexec/fbchisellldb.py" >> ~/.lldbinit
#m1的mac用这个
echo "command script import /opt/homebrew/opt/chisel/libexec/fbchisellldb.py" >> ~/.lldbinit
或者用编辑的方式:
- 使用
open
命令
open ~/.lldbinit
添加如下代码:
command script import /usr/local/opt/chisel/libexec/fbchisellldb.py
#m1的mac用这个
command script import /opt/homebrew/opt/chisel/libexec/fbchisellldb.py
保存并关闭.lldbinit
文件,重启Xcode或者在调试台使用command source ~/.lldbinit
命令使之生效。
- 使用
vim
编辑器
vim ~/.lldbinit
打开文件后,按键盘上的i
键进入可编辑状态,添加代码
# ~/.lldbinit
command script import /usr/local/opt/chisel/libexec/fbchisellldb.py
#m1的mac用这个
command script import /opt/homebrew/opt/chisel/libexec/fbchisellldb.py
~
~
~
-- INSERT --
按键盘上的 esc
键退出可编辑,然后依次输入 :wq
最后回车
# ~/.lldbinit
command script import /usr/local/opt/chisel/libexec/fbchisellldb.py
#m1的mac用这个
command script import /opt/homebrew/opt/chisel/libexec/fbchisellldb.py
~
~
~
:wq
.lldbinit
文件就编辑好了,重启Xcode或者在调试台使用command source ~/.lldbinit
命令使之生效。
3.2、安装LLDB
LLDB开源地址
这个插件是个人开源的,需要单独下载,下载完成后放到某个目录下,找到插件里的dslldb.py
文件,并在 .lldbinit
文件中引入这个文件
command script import /usr/local/opt/chisel/libexec/fbchisellldb.py
command script import /usr/local/opt/LLDB/lldb_commands/dslldb.py
#如果是m1的mac就换成.py文件的对应地址
重启Xcode或者在控制台输入命令command source ~/.lldbinit
使之生效
4、常用Commands
使用Xcode打开一个项目,进入调试模式,在调试台输入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.
reproducer -- Commands for manipulating reproducers. Reproducers make
it possible to capture full debug sessions with all its
dependencies. The resulting reproducer is used to replay
the debug session while debugging the debugger.
Because reproducers need the whole the debug session
from beginning to end, you need to launch the debugger
in capture or replay mode, commonly though the command
line driver.
Reproducers are unrelated record-replay debugging, as
you cannot interact with the debugger during replay.
script -- Invoke the script interpreter with provided code and
display any results. Start the interactive interpreter
if no code is supplied.
session -- Commands controlling LLDB session.
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.
trace -- Commands for loading and using processor trace
information.
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 by using the
options to specify a module.
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. Use 'settings set frame-format' to customize the
printing of individual frames and 'settings set thread-format'
to customize the thread header.
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.
history -- Dump the history of commands in this session.
Commands in the history list can be run again using "!".
"!-" will re-run the command that is commands
from the end of the list (counting the current command).
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 -- parray -- lldb will evaluate EXPRESSION to
get a typed-pointer-to-an-array in memory, and will display
COUNT elements of that type from the array.
po -- Evaluate an expression on the current thread. Displays any
returned value with formatting controlled by the type's author.
poarray -- poarray -- lldb will evaluate EXPRESSION to
get the address of an array of COUNT objects in memory, and will
call po on them.
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.
re -- Commands to access registers for the current thread and stack
frame.
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.
shell -- Run a shell command on the host.
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.
v -- 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.
var -- 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.
vo -- 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.
x -- Read from the memory of the current target process.
Current user-defined commands:
alamborder -- Put a border around views with an ambiguous layout
alamunborder -- Removes the border around views with an ambiguous layout
bdisable -- Disable a set of breakpoints for a regular expression
benable -- Enable a set of breakpoints for a regular expression
binside -- Set a breakpoint for a relative address within the
framework/library that's currently running. This does the
work of finding the offset for the framework/library and
sliding your address accordingly.
bmessage -- Set a breakpoint for a selector on a class, even if the
class itself doesn't override that selector. It walks the
hierarchy until it finds a class that does implement the
selector and sets a conditional breakpoint there.
border -- Draws a border around . Color and width can be
optionally provided. Additionally depth can be provided in
order to recursively border subviews.
caflush -- Force Core Animation to flush. This will 'repaint' the UI
but also may mess with ongoing animations.
copy -- Copy data to your Mac.
dcomponents -- Set debugging options for components.
dismiss -- Dismiss a presented view controller.
fa11y -- Find the views whose accessibility labels match labelRegex
and puts the address of the first result on the clipboard.
findinstances -- Find instances of specified ObjC classes.
flicker -- Quickly show and hide a view to quickly help visualize where
it is.
fv -- Find the views whose class names match classNameRegex and
puts the address of first on the clipboard.
fvc -- Find the view controllers whose class names match
classNameRegex and puts the address of first on the
clipboard.
heapfrom -- Show all nested heap pointers contained within a given
variable.
hide -- Hide a view or layer.
mask -- Add a transparent rectangle to the window to reveal a
possibly obscured or hidden view or layer's bounds
mwarning -- simulate a memory warning
pa11y -- Print accessibility labels of all views in hierarchy of
pa11yi -- Print accessibility identifiers of all views in hierarchy of
pactions -- Print the actions and targets of a control.
paltrace -- Print the Auto Layout trace for the given view. Defaults to
the key window.
panim -- Prints if the code is currently execution with a UIView
animation block.
pbcopy -- Print object and copy output to clipboard
pblock -- Print the block`s implementation address and signature
pbundlepath -- Print application's bundle directory path.
pcells -- Print the visible cells of the highest table view in the
hierarchy.
pclass -- Print the inheritance starting from an instance of any class.
pcomponents -- Print a recursive description of components found starting
from .
pcurl -- Print the NSURLRequest (HTTP) as curl command.
pdata -- Print the contents of NSData object as string.
pdocspath -- Print application's 'Documents' directory path.
pinternals -- Show the internals of an object by dereferencing it as a
pointer.
pinvocation -- Print the stack frame, receiver, and arguments of the
current invocation. It will fail to print all arguments if
any arguments are variadic (varargs).
pivar -- Print the value of an object's named instance variable.
pjson -- Print JSON representation of NSDictionary or NSArray object
pkp -- Print out the value of the key path expression using
-valueForKeyPath:
pmethods -- Print the class and instance methods of a class.
poobjc -- Print the expression result, with the expression run in an
ObjC++ context. (Shortcut for "expression -O -l ObjC++ -- " )
pproperties -- Print the properties of an instance or Class
present -- Present a view controller.
presponder -- Print the responder chain starting from a specific responder.
psjson -- Print JSON representation of Swift Dictionary or Swift Array
object
ptv -- Print the highest table view in the hierarchy.
pvc -- Print the recursion description of .
pviews -- Print the recursion description of .
rcomponents -- Synchronously reflow and update all components.
sequence -- Run commands in sequence, stopping on any error.
setinput -- Input text into text field or text view that is first
responder.
settext -- Set text on text on a view by accessibility id.
show -- Show a view or layer.
slowanim -- Slows down animations. Works on the iOS Simulator and a
device.
taplog -- Log tapped view to the console.
uikit -- Imports the UIKit module to get access to the types while in
lldb.
unborder -- Removes border around .
unmask -- Remove mask from a view or layer
unslowanim -- Turn off slow animations.
visualize -- Open a UIImage, CGImageRef, UIView, CALayer, or
CVPixelBuffer in Preview.app on your Mac.
vs -- Interactively search for a view by walking the hierarchy.
wivar -- Set a watchpoint for an object's instance variable.
xdebug -- Print debug description the XCUIElement in human readable
format.
xnoid -- Print XCUIElement objects with label but without identifier.
xobject -- Print XCUIElement details.
xtree -- Print XCUIElement subtree.
zzz -- Executes specified lldb command after delay.
For more information on any command, type 'help '.
(lldb)
可以看到,经过迭代,chisel的命令库内容已经非常多了,使用help
查看某条命令的详细解释
help
例如想查看border
命令的详细用法,可以使用help border
(lldb) help border
Draws a border around . Color and width can be optionally
provided. Additionally depth can be provided in order to recursively border
subviews. Expects 'raw' input (see 'help raw-input'.)
Syntax: border
Draws a border around . Color and width can be optionally
provided. Additionally depth can be provided in order to recursively border
subviews.
Arguments:
; Type: UIView/NSView/CALayer *; The view/layer to border.
NSViews must be layer-backed.
Options:
--color/-c ; Type: string; A color name such as 'red', 'green',
'magenta', etc.
--width/-w ; Type: CGFloat; Desired width of border.
--depth/-d ; Type: int; Number of levels of subviews to border. Each
level gets a different color beginning with the provided or default color
Syntax: border [--color=color] [--width=width] [--depth=depth]
This command is implemented as FBDrawBorderCommand in
/usr/local/Cellar/chisel/2.0.1/libexec/commands/FBDisplayCommands.py.
或者去查看chisel开源的wiki,里面列出了chisel所有命令的使用方法,LLDB可以参考开源里的例子。
4.1、常用命令介绍
-
pviews
; layer = >
| >
| | >
...............
| | | | | | >
| | | | | | >
打印的东西太多,中间省略了很多内容,想查找关心的内容比较难,使用help pviews
查看更详细的使用方法
(lldb) help pviews
Print the recursion description of . Expects 'raw' input (see 'help
raw-input'.)
Syntax: pviews
Print the recursion description of .
Arguments:
; Type: UIView*/NSView*; The view to print the description of.
Options:
--up/-u ; Print only the hierarchy directly above the view, up to its window.
--depth/-d ; Type: int; Print only to a given depth. 0 indicates
infinite depth.
--window/-w ; Type: int; Specify the window to print a description
of. Check which windows exist with "po (id)[[UIApplication sharedApplication]
windows]".
--short/-s ; Print a short description of the view
--medium/-m ; Print a medium description of the view
Syntax: pviews [--up] [--depth=depth] [--window=window] [--short] [--medium]
This command is implemented as FBPrintViewHierarchyCommand in
/usr/local/Cellar/chisel/2.0.1/libexec/commands/FBPrintCommands.py.
加上
和-m
参数
(lldb) pviews 0x7fd658242d40 -m
|
|
|
| |
|
(lldb)
这样查找结果清晰很多
-
pvc
(lldb) pvc
, state: appeared, view:
| , state: appeared, view:
| | , state: appeared, view:
| | | , state: appeared, view:
| | | , state: disappeared, view: (view not loaded)
| | | , state: disappeared, view: (view not loaded)
| | | , state: disappeared, view: (view not loaded)
| , state: disappeared, view: not in the window
| | , state: disappeared, view: (view not loaded)
| , state: disappeared, view: not in the window
| | , state: disappeared, view: (view not loaded)
| , state: disappeared, view: not in the window
| | , state: disappeared, view: (view not loaded)
-
visualize
(lldb) visualize 0x7fd658242d40
-
fv
(lldb) fv mj
0x7fd658242d40 MJRefreshNormalHeader
(lldb) fv uibutton
0x7fd658124dd0 UIButton
0x7fd658335220 UIButtonLabel
0x7fd658335cd0 UIButton
0x7fd658217a20 UIButtonLabel
0x7fd6582181e0 UIButton
0x7fd658335f90 UIButtonLabel
0x7fd658336280 UIButton
0x7fd658228a30 UIButtonLabel
0x7fd658259cb0 UIButton
0x7fd65775d300 UIButton
0x7fd6577a76d0 UIButton
0x7fd6577a7990 UIButtonLabel
0x7fd65779a320 UIButton
0x7fd65779a5e0 UIButtonLabel
0x7fd65778d3c0 UIButton
0x7fd65778d680 UIButtonLabel
0x7fd65826ded0 UIButton
0x7fd65827bd80 UIButtonLabel
-
fvc
(lldb) fvc first
0x7fd658312950 CustomProject.FirstViewController
0x7fd658850600 CustomProject.FirstChildViewController
0x7fd659051200 CustomProject.FirstChildViewController
0x7fd658833600 CustomProject.FirstChildViewController
0x7fd658847c00 CustomProject.FirstChildViewController
-
hide
(lldb) hide 0x7fd658242d40
-
show
(lldb) show 0x7fd658242d40
-
mask
(lldb) mask 0x7fd65835e370 -c red -a 0.5
-
unmask
(lldb) unmask 0x7fd65835e370
-
border
(lldb) border 0x7fd65835e370 -c blue -w 2
-
unborder
(lldb) unborder 0x7fd65835e370
-
caflush
相当于执行了 [CATransaction flush]
方法
(lldb) e (void)[0x7fd65835e370 setBackgroundColor: [UIColor redColor]]
(lldb) caflush
-
bmessage
(lldb) bmessage "-[0x7fd658225820 viewWillAppear:]"
Setting a breakpoint at -[CustomProject.FourthViewController viewWillAppear:] with condition (void*)(id)$rdi == 0x00007fd658225820
(lldb) bmessage "-[0x7fd65835e370 setFrame:]"
Setting a breakpoint at -[UILabel setFrame:] with condition (void*)(id)$rdi == 0x00007fd65835e370
Breakpoint 3: where = UIKitCore`-[UILabel setFrame:], address = 0x00007fff254eae37
-
presponder
打印对象的响应链
(lldb) presponder 0x7fd65835e370
>
| >
| | ; layer = >
| | | >
| | | | ; layer = ; contentOffset: {0, 0}; contentSize: {414, 1651.9565217391305}; adjustedContentInset: {0, 0, 0, 0}; dataSource: >
| | | | | >
| | | | | |
| | | | | | | ; layer = >
| | | | | | | | >
| | | | | | | | | ; layer = ; contentOffset: {0, 0}; contentSize: {1656, 725}; adjustedContentInset: {0, 0, 0, 0}; layout: ; dataSource: >>
| | | | | | | | | | >
| | | | | | | | | | | >
| | | | | | | | | | | |
| | | | | | | | | | | | | >
| | | | | | | | | | | | | | >
| | | | | | | | | | | | | | | ; layer = >
| | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | >
| | | | | | | | | | | | | | | | | | >
| | | | | | | | | | | | | | | | | | | >
| | | | | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | | | >
| | | | | | | | | | | | | | | | | | | | | | >
| | | | | | | | | | | | | | | | | | | | | | | ; layer = >
| | | | | | | | | | | | | | | | | | | | | | | | ; persistentIdentifier = AF981626-839B-49EC-8EDB-AE33A31AEDFE; activationState = UISceneActivationStateForegroundActive; settingsScene = ; windows = (
; layer = >,
)>
| | | | | | | | | | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | | | | | | | |
(lldb)
-
p、po及image命令
p
是打印对象,po
是打印对象的description
(lldb) p key
(Int) $R0 = 111
(lldb) po key
111
p
或po
命令修改对象
(lldb) p key = 222
() $R2 = {}
(lldb) p key
(Int) $R3 = 222
(lldb) po key
222
(lldb) po key = 333
0 elements
(lldb) p key
(Int) $R6 = 333
(lldb) po key
333
程序崩溃时如果不能定位到崩溃的代码位置,可以根据抛出的出错堆栈信息,找到与项目名称一致的对应地址,使用image
寻址命令可以定位到具体的崩溃位置在第几行。
(lldb) image lookup -a 0x000000010077a681
Address: TestCustomProject[0x0000000100004681] (TestCustomProject.__TEXT.__text + 2769)
Summary: TestCustomProject`TestCustomProject.ViewController.loadTest(__C.UIButton) -> () + 161 at ViewController.swift:35:19
(lldb)
这里的image
不是图片的意思,程序编译后生成mach-o
的可执行文件,也可以理解为镜像文件,image
就代表镜像,在项目对应的镜像文件里寻址,结果才是所需要的。
-
bt及frame 打印函数调用顺序
在项目中某个方法处打断点,程序运行到断点时可以使用bt
命令查看函数的调用顺序,这个顺序是倒序排列
的
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* frame #0: 0x00000001020c083b TestCustomProject`ViewController.loadTest3(key3="222222", self=0x00007fd32120df50) at ViewController.swift:44:15
frame #1: 0x00000001020c080a TestCustomProject`ViewController.loadTest2(key2="222222", self=0x00007fd32120df50) at ViewController.swift:40:9
frame #2: 0x00000001020c07bb TestCustomProject`ViewController.loadTest1(self=0x00007fd32120df50) at ViewController.swift:36:9
frame #3: 0x00000001020c0707 TestCustomProject`ViewController.loadTest(sender=0x00007fd320708f70, self=0x00007fd32120df50) at ViewController.swift:32:9
frame #4: 0x00000001020c0745 TestCustomProject`@objc ViewController.loadTest(_:) at :0
frame #5: 0x00007fff25002189 UIKitCore`-[UIApplication sendAction:to:from:forEvent:] + 83
frame #6: 0x00007fff2489b573 UIKitCore`-[UIControl sendAction:to:forEvent:] + 110
frame #7: 0x00007fff2489b955 UIKitCore`-[UIControl _sendActionsForEvents:withEvent:] + 332
frame #8: 0x00007fff24897e8c UIKitCore`-[UIButton _sendActionsForEvents:withEvent:] + 148
frame #9: 0x00007fff2489a206 UIKitCore`-[UIControl touchesEnded:withEvent:] + 488
frame #10: 0x00007fff2504295d UIKitCore`-[UIWindow _sendTouchesForEvent:] + 1287
frame #11: 0x00007fff250449df UIKitCore`-[UIWindow sendEvent:] + 5295
frame #12: 0x00007fff2501b4e8 UIKitCore`-[UIApplication sendEvent:] + 825
frame #13: 0x00007fff250b128a UIKitCore`__dispatchPreprocessedEventFromEventQueue + 8695
frame #14: 0x00007fff250b3a10 UIKitCore`__processEventQueue + 8579
frame #15: 0x00007fff250aa1b6 UIKitCore`__eventFetcherSourceCallback + 240
frame #16: 0x00007fff20369e25 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
frame #17: 0x00007fff20369d1d CoreFoundation`__CFRunLoopDoSource0 + 180
frame #18: 0x00007fff203691f2 CoreFoundation`__CFRunLoopDoSources0 + 242
frame #19: 0x00007fff20363951 CoreFoundation`__CFRunLoopRun + 875
frame #20: 0x00007fff20363103 CoreFoundation`CFRunLoopRunSpecific + 567
frame #21: 0x00007fff2c851cd3 GraphicsServices`GSEventRunModal + 139
frame #22: 0x00007fff24ffbe63 UIKitCore`-[UIApplication _run] + 928
frame #23: 0x00007fff25000a53 UIKitCore`UIApplicationMain + 101
frame #24: 0x00007fff5933d052 libswiftUIKit.dylib`UIKit.UIApplicationMain(Swift.Int32, Swift.Optional>>, Swift.Optional, Swift.Optional) -> Swift.Int32 + 98
frame #25: 0x00000001020c17a8 TestCustomProject`static UIApplicationDelegate.main() at :0
frame #26: 0x00000001020c1737 TestCustomProject`static AppDelegate.$main(self=TestCustomProject.AppDelegate) at AppDelegate.swift:10:1
frame #27: 0x00000001020c1868 TestCustomProject`main at :0
frame #28: 0x00000001020dde1e dyld_sim`start_sim + 10
可以使用frame select <序号>
命令查看某个函数的详细信息
(lldb) frame select 0
frame #0: 0x00000001020c083b TestCustomProject`ViewController.loadTest3(key3="222222", self=0x00007fd32120df50) at ViewController.swift:44:15
41 }
42
43 func loadTest3(key3:String) {
-> 44 print(key3)
^
45 }
46
47 }
同时配合up
和down
命令追踪函数的调用和被调用关系
(lldb) up
frame #1: 0x00000001020c080a TestCustomProject`ViewController.loadTest2(key2="222222", self=0x00007fd32120df50) at ViewController.swift:40:9
37 }
38
39 func loadTest2(key2:String) {
-> 40 loadTest3(key3: key2)
^
41 }
42
43 func loadTest3(key3:String) {
(lldb) down
frame #0: 0x00000001020c083b TestCustomProject`ViewController.loadTest3(key3="222222", self=0x00007fd32120df50) at ViewController.swift:44:15
41 }
42
43 func loadTest3(key3:String) {
-> 44 print(key3)
^
45 }
46
47 }
同时配合frame info
命令查看当前追踪函数的详细信息
(lldb) frame info
frame #0: 0x00000001020c083b TestCustomProject`ViewController.loadTest3(key3="222222", self=0x00007fd32120df50) at ViewController.swift:44:15
(lldb) up
frame #1: 0x00000001020c080a TestCustomProject`ViewController.loadTest2(key2="222222", self=0x00007fd32120df50) at ViewController.swift:40:9
37 }
38
39 func loadTest2(key2:String) {
-> 40 loadTest3(key3: key2)
^
41 }
42
43 func loadTest3(key3:String) {
(lldb) frame info
frame #1: 0x00000001020c080a TestCustomProject`ViewController.loadTest2(key2="222222", self=0x00007fd32120df50) at ViewController.swift:40:9
-
breakpoint
使用b
命令给方法添加断点
(lldb) b loadTest3
Breakpoint 2: where = TestCustomProject`TestCustomProject.ViewController.loadTest3() -> () at ViewController.swift:45, address = 0x0000000104a656a0
可以看到所加断点的详细信息,Breakpoint 2
这里的2
代表的是编号为2
的组断点,一个组断点下可能包含多个子断点。
使用breakpoint list
命令查看所有断点
(lldb) breakpoint list
Current breakpoints:
2: name = 'loadTest3', locations = 1, resolved = 1, hit count = 0
2.1: where = TestCustomProject`TestCustomProject.ViewController.loadTest3() -> () at ViewController.swift:45, address = 0x0000000104a656a0, resolved, hit count = 0
使用breakpoint delete <组号>
命令删除所有断点
(lldb) breakpoint delete 2
1 breakpoints deleted; 0 breakpoint locations disabled.
如果一组里有多个子断点,要删除某一个, 使用breakpoint delete <断点序号>
命令会把这个断点标识为disabled
状态,在使用breakpoint delete <组号>
时才会把断点彻底删除
// loadTest是一个button的点击事件,添加断点时加了两个,一个加在了声明,一个加在了实现
(lldb) b loadTest
Breakpoint 3: 2 locations.
//查看所有断点
(lldb) breakpoint list
Current breakpoints:
3: name = 'loadTest', locations = 2, resolved = 2, hit count = 0
//加在了声明
3.1: where = TestCustomProject`TestCustomProject.ViewController.loadTest(__C.UIButton) -> () at ViewController.swift:33, address = 0x0000000104a65580, resolved, hit count = 0
//加在了实现
3.2: where = TestCustomProject`@objc TestCustomProject.ViewController.loadTest(__C.UIButton) -> () at , address = 0x0000000104a655c0, resolved, hit count = 0
//删除了声明位置的断点
(lldb) breakpoint delete 3.1
0 breakpoints deleted; 1 breakpoint locations disabled.
//查看所有断点,声明处的断点仍在,只是被置为了disabled
(lldb) breakpoint list
Current breakpoints:
3: name = 'loadTest', locations = 2, resolved = 1, hit count = 0
3.1: where = TestCustomProject`TestCustomProject.ViewController.loadTest(__C.UIButton) -> () at ViewController.swift:33, address = 0x0000000104a65580, unresolved, hit count = 0 Options: disabled
3.2: where = TestCustomProject`@objc TestCustomProject.ViewController.loadTest(__C.UIButton) -> () at , address = 0x0000000104a655c0, resolved, hit count = 0
//删除组编号是3的断点
(lldb) breakpoint delete 3
1 breakpoints deleted; 0 breakpoint locations disabled.
//断点彻底删除了
(lldb) breakpoint list
No breakpoints currently set.
(lldb)
breakpoint
、c
、n
、s
及finish
命令,分别对应调试台上的五个功能按钮
(lldb) breakpoint list
No breakpoints currently set.
(lldb) b loadTest2
Breakpoint 1: where = TestCustomProject`TestCustomProject.ViewController.loadTest2() -> () at ViewController.swift:41, address = 0x00000001022cc660
(lldb) b loadTest3
Breakpoint 2: where = TestCustomProject`TestCustomProject.ViewController.loadTest3() -> () at ViewController.swift:45, address = 0x00000001022cc6a0
(lldb) c
Process 29563 resuming
(lldb) n
(lldb) s
(lldb) finish
方法 --loadTest3-- 执行完毕
(lldb)
打完断点后使用target stop-hook add -o "frame variable"
命令,重新运行,每次进入断点都会自动打印详细的参数信息
(lldb) b loadTest2
Breakpoint 1: where = TestCustomProject`TestCustomProject.ViewController.loadTest2(key2: Swift.String) -> () at ViewController.swift:41, address = 0x000000010ad89630
(lldb) target stop-hook add -o "frame variable"
Stop hook #1 added.
(String) key2 = {
_guts = {
_object = (_countAndFlagsBits = 16573246628723425280, _object = 0x00007ff2137066b0)
}
}
(TestCustomProject.ViewController) self = 0x000000010ad8961e {
UIKit.UIViewController = {
UIKit.UIResponder = {
ObjectiveC.NSObject = {}
}
}
}
222222//参数的值
-
pactions
打印对象调用者及调用方法
//先拿到UIButton对象
(lldb) pviews
; layer = >
| >
| | >
| | | >
| | | | >
| | | | | >
| | | | | | >
//打印对象调用者、调用方法名
(lldb) pactions 0x7f99d670ab10
: loadTest:
(lldb)
-
vs
动态查看控件的层级关系,使用了vs
命令后,可以继续使用w
、s
、a
、d
、p
命令执行移动和打印操作
(lldb) vs 0x7fb023709b90
Use the following and (q) to quit.
(w) move to superview //移动到父视图
(s) move to first subview //移动到第一个子视图
(a) move to previous sibling //移动到上一个兄弟视图
(d) move to next sibling //移动到下一个兄弟视图
(p) print the hierarchy //打印层级结构
>
p //打印层级结构
>
| >
| | >
| | | >
w //移动到父视图
>
s //移动到第一个子视图
>
a //这里没有上一个兄弟视图,给出了提示
There are no sibling views to this view.
>
d //这里没有下一个兄弟视图,给出了提示
There are no sibling views to this view.
>
-
methods
打印对象的属性和方法
(lldb) pvc
, state: appeared, view:
(lldb) methods 0x7f923220a390
:
in TestCustomProject.ViewController:
Instance Methods:
- (void) loadTest:(id)arg1; (0x1043595c0)
- (id) initWithCoder:(id)arg1; (0x104359c80)
- (void) .cxx_destruct; (0x104359cf0)
- (id) initWithNibName:(id)arg1 bundle:(id)arg2; (0x104359a70)
- (void) viewDidLoad; (0x104359550)
(UIViewController ...)
-
pclass
打印对象的继承关系
(lldb) pclass 0x7fdc11a97800
UIButton
| UIControl
| | UIView
| | | UIResponder
| | | | NSObject
(lldb)
-
search
搜索已经存在于栈中的控件及其子控件
(lldb) search UIButton
>
>
(lldb)
-
lookup
搜索,可执行正则表达式,会搜索所用镜像模块,重点查看与工程名字相同的模块,即可查看哪些地方调用了这些方法。
(lldb) lookup loadTest
****************************************************
4 hits in: TestCustomProject
****************************************************
TestCustomProject.ViewController.loadTest() -> ()
TestCustomProject.ViewController.loadTest1() -> ()
TestCustomProject.ViewController.loadTest3() -> ()
TestCustomProject.ViewController.loadTest2() -> ()
(lldb)
-
pbundlepath
打印二进制文件路径
(lldb) pbundlepath
/Users/user/Library/Developer/CoreSimulator/Devices/CE00694F-E26B-499A-890A-AB60982200F0/data/Containers/Bundle/Application/22424862-765E-4E43-B123-A4A20DB4611E/TestCustomProject.app
(lldb)
-
pdocspath
打印文档路径
(lldb) pdocspath
/Users/user/Library/Developer/CoreSimulator/Devices/CE00694F-E26B-499A-890A-AB60982200F0/data/Containers/Data/Application/60181B03-4BCE-407E-BD5E-230362B03F7E/Documents
(lldb)
到这里为止,介绍了一些常用命令和高级命令的用法,熟练掌握这些命令可以大幅度提高调试能力和开发效率。如果没有想要的命令或者有更好的想法,还可以利用python自己定制命令。
4.2、自定义命令
-
新建
Python
文件
例如: 新建一个python文件用来打印当前线程,路径为:/Users/user/Desktop/Commands/test.py
,
定义两个命令:
pct
,打印当前线程;
pctm
,打印当前线程是否是主线程;
import os
import lldb
import fbchisellldbbase as fb
def lldbcommands():
return [
PrintCurrentThread(),
PrintCurrentThreadMain()
]
class PrintCurrentThread(fb.FBCommand):
def name(self):
return 'pct'
def description(self):
return 'print current thread'
def run(self, arguments, options):
#如果是swift就用'po Thread.current'
command = 'po [NSThread currentThread]'
lldb.debugger.HandleCommand(command)
class PrintCurrentThreadMain(fb.FBCommand):
def name(self):
return 'pctm'
def description(self):
return 'check current thread is or is not equal to main thread'
def run(self, arguments, options):
#如果是swift就用'po Thread.current.isMainThread'
command = 'po [NSThread currentThread].isMainThread'
lldb.debugger.HandleCommand(command)
定义了PrintCurrentThread
、PrintCurrentThreadMain
两个类,需要实现name
、description
、run
方法来分别告诉名称、描述、和执行实体。
添加引用
在 ~/.lldbinit
文件中添加script fbchisellldb.loadCommandsInDirectory('python文件的绝对路径')
,必须使用绝对路径,否则会报错。
command script import /usr/local/opt/chisel/libexec/fbchisellldb.py
command script import /usr/local/opt/LLDB/lldb_commands/dslldb.py
script fbchisellldb.loadCommandsInDirectory('/Users/user/Desktop/Commands/')
重启Xcode或者在控制台输入命令command source ~/.lldbinit
使之生效。
(lldb) command source ~/.lldbinit
Executing commands in '/Users/user/.lldbinit'.
command script import /usr/local/opt/chisel/libexec/fbchisellldb.py
command script import /usr/local/opt/LLDB/lldb_commands/dslldb.py
script fbchisellldb.loadCommandsInDirectory('/Users/user/Desktop/Commands/')
使用自定义命令
(lldb) pct
<_NSMainThread: 0x60000227c000>{number = 1, name = main}
(lldb) pctm
YES
加载自定义command 的过程,参考fbchisellldb.py: loadCommandsInDirectory()
文件的源码实现,到这里定制命令就完成了。
最后,实践是检验真理的唯一标准,还是自己动手敲敲代码学起来才更快,希望文章能对你有所帮助。