iOS | LLDB轻量级高性能调试器

LLDB全称Low Level Debugger ,是轻量级的高性能调试器,内置于Xcode,有公司或个人开发了一系列插件来扩展了lldb调试器的功能,使我们在调试代码的时候更方便快捷,常见的插件有ChiselLLDB两个。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/zshzsh为默认Shell,.zprofile为默认配置文件,用下面的命令设置环境变量

echo 'eval "$(/opt/homebrew/bin/brew shellenv)" #brew.idayer.com' >> ~/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"
source ~/.zprofile

如果是/bin/bashbash为默认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

ppo命令修改对象

(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   }

同时配合updown命令追踪函数的调用和被调用关系

(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) 

breakpointcnsfinish命令,分别对应调试台上的五个功能按钮

从左到右一一对应.png

(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命令后,可以继续使用wsadp命令执行移动和打印操作

(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)

定义了PrintCurrentThreadPrintCurrentThreadMain两个类,需要实现namedescriptionrun方法来分别告诉名称、描述、和执行实体。

添加引用

~/.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()文件的源码实现,到这里定制命令就完成了。

最后,实践是检验真理的唯一标准,还是自己动手敲敲代码学起来才更快,希望文章能对你有所帮助。

你可能感兴趣的:(iOS | LLDB轻量级高性能调试器)