Swift进阶-LLDB调试

2020-12-23 09:55:44 浏览数 (1)

工欲善其事,必先利其器。程序猿最好的利器就是开发工具,iOS开发者最基本,最关键的一点就是熟练使用Xcode,而LLDB则是Xcode中至关重要的一环。 作为开发者,我们大部分的工作时间都用于调试,调试协议,调试UI,调试bug,用好LLDB,打遍iOS无敌手。

LLDB拥有大量有用的调试命令:

代码语言:javascript复制
(lldb) help
Debugger commands:
  apropos           -- List debugger commands related to a word or subject.
  breakpoint        -- Commands for operating on breakpoints (see 'help b' for
                       shorthand.)
  bugreport         -- Commands for creating domain-specific bug reports.
  command           -- Commands for managing custom LLDB commands.
  disassemble       -- Disassemble specified instructions in the current
                       target.  Defaults to the current function for the
                       current thread and stack frame.
  expression        -- Evaluate an expression on the current thread.  Displays
                       any returned value with LLDB's default formatting.
  frame             -- Commands for selecting and examing the current thread's
                       stack frames.
  gdb-remote        -- Connect to a process via remote GDB server.  If no host
                       is specifed, localhost is assumed.
  gui               -- Switch into the curses based GUI mode.
  help              -- Show a list of all debugger commands, or give details
                       about a specific command.
  kdp-remote        -- Connect to a process via remote KDP server.  If no UDP
                       port is specified, port 41139 is assumed.
  language          -- Commands specific to a source language.
  log               -- Commands controlling LLDB internal logging.
  memory            -- Commands for operating on memory in the current target
                       process.
  platform          -- Commands to manage and create platforms.
  plugin            -- Commands for managing LLDB plugins.
  process           -- Commands for interacting with processes on the current
                       platform.
  quit              -- Quit the LLDB debugger.
  register          -- Commands to access registers for the current thread and
                       stack frame.
  script            -- Invoke the script interpreter with provided code and
                       display any results.  Start the interactive interpreter
                       if no code is supplied.
  settings          -- Commands for managing LLDB settings.
  source            -- Commands for examining source code described by debug
                       information for the current target process.
  statistics        -- Print statistics about a debugging session
  target            -- Commands for operating on debugger targets.
  thread            -- Commands for operating on one or more threads in the
                       current process.
  type              -- Commands for operating on the type system.
  version           -- Show the LLDB debugger version.
  watchpoint        -- Commands for operating on watchpoints.
Current command abbreviations (type 'help command alias' for more info):
  add-dsym  -- Add a debug symbol file to one of the target's current modules
               by specifying a path to a debug symbols file, or using the
               options to specify a module to download symbols for.
  attach    -- Attach to process by ID or name.
  b         -- Set a breakpoint using one of several shorthand formats.
  bt        -- Show the current thread's call stack.  Any numeric argument
               displays at most that many frames.  The argument 'all' displays
               all threads.
  c         -- Continue execution of all threads in the current process.
  call      -- Evaluate an expression on the current thread.  Displays any
               returned value with LLDB's default formatting.
  continue  -- Continue execution of all threads in the current process.
  detach    -- Detach from the current target process.
  di        -- Disassemble specified instructions in the current target. 
               Defaults to the current function for the current thread and
               stack frame.
  dis       -- Disassemble specified instructions in the current target. 
               Defaults to the current function for the current thread and
               stack frame.
  display   -- Evaluate an expression at every stop (see 'help target
               stop-hook'.)
  down      -- Select a newer stack frame.  Defaults to moving one frame, a
               numeric argument can specify an arbitrary number.
  env       -- Shorthand for viewing and setting environment variables.
  exit      -- Quit the LLDB debugger.
  f         -- Select the current stack frame by index from within the current
               thread (see 'thread backtrace'.)
  file      -- Create a target using the argument as the main executable.
  finish    -- Finish executing the current stack frame and stop after
               returning.  Defaults to current thread unless specified.
  image     -- Commands for accessing information for one or more target
               modules.
  j         -- Set the program counter to a new address.
  jump      -- Set the program counter to a new address.
  kill      -- Terminate the current target process.
  l         -- List relevant source code using one of several shorthand formats.
  list      -- List relevant source code using one of several shorthand formats.
  n         -- Source level single step, stepping over calls.  Defaults to
               current thread unless specified.
  next      -- Source level single step, stepping over calls.  Defaults to
               current thread unless specified.
  nexti     -- Instruction level single step, stepping over calls.  Defaults to
               current thread unless specified.
  ni        -- Instruction level single step, stepping over calls.  Defaults to
               current thread unless specified.
  p         -- Evaluate an expression on the current thread.  Displays any
               returned value with LLDB's default formatting.
  parray    -- Evaluate an expression on the current thread.  Displays any
               returned value with LLDB's default formatting.
  po        -- Evaluate an expression on the current thread.  Displays any
               returned value with formatting controlled by the type's author.
  poarray   -- Evaluate an expression on the current thread.  Displays any
               returned value with LLDB's default formatting.
  print     -- Evaluate an expression on the current thread.  Displays any
               returned value with LLDB's default formatting.
  q         -- Quit the LLDB debugger.
  r         -- Launch the executable in the debugger.
  rbreak    -- Sets a breakpoint or set of breakpoints in the executable.
  repl      -- Evaluate an expression on the current thread.  Displays any
               returned value with LLDB's default formatting.
  run       -- Launch the executable in the debugger.
  s         -- Source level single step, stepping into calls.  Defaults to
               current thread unless specified.
  si        -- Instruction level single step, stepping into calls.  Defaults to
               current thread unless specified.
  sif       -- Step through the current block, stopping if you step directly
               into a function whose name matches the TargetFunctionName.
  step      -- Source level single step, stepping into calls.  Defaults to
               current thread unless specified.
  stepi     -- Instruction level single step, stepping into calls.  Defaults to
               current thread unless specified.
  t         -- Change the currently selected thread.
  tbreak    -- Set a one-shot breakpoint using one of several shorthand
               formats.
  undisplay -- Stop displaying expression at every stop (specified by stop-hook
               index.)
  up        -- Select an older stack frame.  Defaults to moving one frame, a
               numeric argument can specify an arbitrary number.
  x         -- Read from the memory of the current target process.

先简单翻一下,做个大概了解

代码语言:javascript复制
Debugger commands:
apropos  列出与Word或主题相关的调试器命令
breakpoint  操作断点的命令
bugreport 创建特定领域错误报告的命令
command  管理自定义LLDB命令的命令
disassemble 拆卸当前指定的指令,默认为当前函数为当前线程和堆栈帧
expression 评估当前线程上的表达式。任何返回值与LLDB的默认格式。
frame  命令选择和检查当前线程的堆栈帧,GDB远程通过远程GDB服务器连接到进程,如果没有主机指定localhost假设
gui 切换到基于诅咒的GUI模式。
kdp-remote 通过远程KDP服务器连接进程,如果没有UDP端口被指定,假定端口41139。
language 特定于源语言的命令log 控制LLDB内部记录命令。
memory 在当前目标上操作内存的命令过程
platform  管理和创建平台的命令
plugin  管理LLDB插件
process  与当前进程交互的命令平台
quit  退出LLDB调试器。
register  为当前线程访问寄存器的命令栈帧
script  使用提供的代码调用脚本解释器,显示任何结果。启动交互式解释器,如果没有提供代码。

settings  管理LLDB设置命令
source  检查由调试描述的源代码的命令,当前目标过程的信息。
target  在调试器对象上操作的命令
thread  中的一个或多个线程操作的命令目前的过程
type  在类型系统上操作的命令
version  显示LLDB调试器版本
watchpoint  操作上观察点的命令.当前命令缩写(类型“帮助命令别名”以获取更多信息)
add-dsym 将调试符号文件添加到目标当前模块中的一个通过指定调试符号文件的路径,或使用选项来指定下载符号的模块
attach  通过ID或名称附加到进程
b 使用几个速记格式中的一个设置断点
bt 显示当前线程的调用堆栈。任何数字参数最多显示许多帧。参数“所有”显示所有的线程
c  继续执行当前进程中的所有线程
call  评估当前线程上的表达式。显示任何返回值与LLDB的默认格式
continue  继续执行当前进程中的所有线程
detach 从当前目标进程分离
di  拆卸当前目标中指定的指令,默认为当前线程的当前功能和栈帧
dis  拆卸当前目标中指定的指令,默认为当前线程的当前功能和栈帧
display  在每一站评估表达式(参见“帮助目标”停止钩子)
down 选择一个较新的堆栈帧。默认为移动一帧数字参数可以指定任意数量
env 查看和设置环境变量的简写
exit  退出LLDB调试器
f 从当前范围内选择索引的当前堆栈帧线程(见螺纹回溯”。)
file  使用参数作为主要可执行文件创建目标
finish 完成执行当前堆栈帧后停止返回,默认为当前线程,除非指定
image  为一个或多个目标访问信息的命令模块
j  将程序计数器设置为新地址
jump  将程序计数器设置为新地址
kill  终止当前目标进程
l  使用几个速记中的一个列出相关的源代码格式
list 使用几个速记中的一个列出相关的源代码格式
n  源级单步,单步调用。默认为当前线程,除非指定
next 源级单步,单步调用。默认为当前线程,除非指定
nexti  单步单步执行指令,默认为当前线程,除非指定
ni  单步单步执行指令,默认为当前线程,除非指定
p  评估当前线程上的表达式。显示任何返回值与LLDB的默认格式
parray 评估当前线程上的表达式。显示任何返回值与LLDB的默认格式
po  评估当前线程上的表达式。显示任何由类型作者控制的格式返回值
poarray 评估当前线程上的表达式。显示任何返回值与LLDB的默认格式
print  评估当前线程上的表达式。显示任何返回值与LLDB的默认格式
q  退出LLDB调试器
r  在调试器中启动可执行文件
rbreak  在可执行文件中设置断点或断点集。
repl  评估当前线程上的表达式。显示任何返回值与LLDB的默认格式
run  在调试器中启动可执行文件
s  源级单步,单步调用。默认为当前线程,除非指定
si 单步单步执行指令,默认为当前线程,除非指定
sif  通过当前块,如果直接单步执行停止为一个函数的名称相匹配的targetfunctionname
step  源级单步,单步调用。默认为当前线程,除非指定
stepi  单步单步执行指令,默认为当前线程,除非指定
t  更改当前选定的线程
tbreak  使用一一个快捷键中的一个设置断点格式
undisplay  停止在每一站显示表达式(由停止钩子指定)指标。)
up 选择一个旧的堆栈帧。默认为移动一帧,一个数字参数可以指定任意数量
x  从当前目标进程的内存中读取

其中一些常见的重要命令我会提炼出来跟大家一起探讨:

  • 获取变量值: expression , e , print , po , p
  • 获取执行环境 特定语言命令: bugreport , frame , language
  • 执行流程控制: process , breakpoint , thread , watchpoint
  • 单步调试:n,s,finish,c
  • 其他: command , platform , gui,image

基本功能:获取变量值和状态

调试最基本的功能是打印和修改变量的值,单步调试,确定是不是按照设定的方式运行,便于快速定位。 命令: expression , e , print , po , p

  • expression、e指令: 既可以打印也可以赋值(后面有例子详解)
  • print指令: 除了 print 命令没有可用选项无需传递参数外, print 和 expression -- 几乎一样。
  • p指令: 可打印其对象类型、内存地址以及该对象的值等具体信息,print的简写
  • po指令: 是打印其调用description方法得到的值。实际上,po指令就是 e -O -- 的别名
  • expr指令: expression的简写
  • call指令: 调用方法的意思

例如:

代码语言:javascript复制
func sum(_ a: Int, _ b: Int) -> Int {
        return a   b
    }

sum(5, 4)

p指令结果:

代码语言:javascript复制
(lldb) p a
(Int) $R0 = 5

po指令结果:

代码语言:javascript复制
(lldb) po a
5

e指令结果:

代码语言:javascript复制
(lldb) e a
(Int) $R6 = 5

e赋值结果:

代码语言:javascript复制
(lldb) e b = 10
(lldb) po a b
15

------分割线------ 看完基本指令,再来深入了解下expression指令。 expression命令拥有大约30个选项,可以调用help命令先大概了解下:

代码语言:javascript复制
help expression
     Evaluate an expression on the current thread.  Displays any returned value
     with LLDB's default formatting.  Expects 'raw' input (see 'help
     raw-input'.)

Syntax: expression <cmd-options> -- <expr>

Command Options Usage:
  expression [-AFLORTgp] [-f <format>] [-G <gdb-format>] [-a <boolean>] [-i <boolean>] [-t <unsigned-integer>] [-u <boolean>] [-l <source-language>] [-X <boolean>] [-v[<description-verbosity>]] [-j <boolean>] [-d <none>] [-S <boolean>] [-D <count>] [-P <count>] [-Y[<count>]] [-V <boolean>] [-Z <count>] -- <expr>
  expression [-AFLORTgp] [-a <boolean>] [-i <boolean>] [-t <unsigned-integer>] [-u <boolean>] [-l <source-language>] [-X <boolean>] [-j <boolean>] [-d <none>] [-S <boolean>] [-D <count>] [-P <count>] [-Y[<count>]] [-V <boolean>] [-Z <count>] -- <expr>
  expression [-r] -- <expr>
  expression <expr>

       -A ( --show-all-children )
            Ignore the upper bound on the number of children to show.

       -D <count> ( --depth <count> )
            Set the max recurse depth when dumping aggregate types (default is
            infinity).

       -F ( --flat )
            Display results in a flat format that uses expression paths for
            each variable or member.

       -G <gdb-format> ( --gdb-format <gdb-format> )
            Specify a format using a GDB format specifier string.

       -L ( --location )
            Show variable location information.

       -O ( --object-description )
            Display using a language-specific description API, if possible.

       -P <count> ( --ptr-depth <count> )
            The number of pointers to be traversed when dumping values (default
            is zero).

       -R ( --raw-output )
            Don't use formatting options.

       -S <boolean> ( --synthetic-type <boolean> )
            Show the object obeying its synthetic provider, if available.

       -T ( --show-types )
            Show variable types when dumping values.

       -V <boolean> ( --validate <boolean> )
            Show results of type validators.

       -X <boolean> ( --apply-fixits <boolean> )
            If true, simple fix-it hints will be automatically applied to the
            expression.

       -Y[<count>] ( --no-summary-depth=[<count>] )
            Set the depth at which omitting summary information stops (default
            is 1).

       -Z <count> ( --element-count <count> )
            Treat the result of the expression as if its type is an array of
            this many values.

       -a <boolean> ( --all-threads <boolean> )
            Should we run all threads if the execution doesn't complete on one
            thread.

       -d <none> ( --dynamic-type <none> )
            Show the object as its full dynamic type, not its static type, if
            available.
            Values: no-dynamic-values | run-target | no-run-target

       -f <format> ( --format <format> )
            Specify a format to be used for display.

       -g ( --debug )
            When specified, debug the JIT code by setting a breakpoint on the
            first instruction and forcing breakpoints to not be ignored (-i0)
            and no unwinding to happen on error (-u0).

       -i <boolean> ( --ignore-breakpoints <boolean> )
            Ignore breakpoint hits while running expressions

       -j <boolean> ( --allow-jit <boolean> )
            Controls whether the expression can fall back to being JITted if
            it's not supported by the interpreter (defaults to true).

       -l <source-language> ( --language <source-language> )
            Specifies the Language to use when parsing the expression.  If not
            set the target.language setting is used.

       -p ( --top-level )
            Interpret the expression as a complete translation unit, without
            injecting it into the local context.  Allows declaration of
            persistent, top-level entities without a $ prefix.

       -r ( --repl )
            Drop into Swift REPL

       -t <unsigned-integer> ( --timeout <unsigned-integer> )
            Timeout value (in microseconds) for running the expression.

       -u <boolean> ( --unwind-on-error <boolean> )
            Clean up program state if the expression causes a crash, or raises
            a signal.  Note, unlike gdb hitting a breakpoint is controlled by
            another option (-i).

       -v[<description-verbosity>] ( --description-verbosity=[<description-verbosity>] )
            How verbose should the output of this expression be, if the object
            description is asked for.
            Values: compact | full

Single and multi-line expressions:

    The expression provided on the command line must be a complete expression
    with no newlines.  To evaluate a multi-line expression, hit a return after
    an empty expression, and lldb will enter the multi-line expression editor.
    Hit return on an empty line to end the multi-line expression.

Timeouts:

    If the expression can be evaluated statically (without running code) then
    it will be.  Otherwise, by default the expression will run on the current
    thread with a short timeout: currently .25 seconds.  If it doesn't return
    in that time, the evaluation will be interrupted and resumed with all
    threads running.  You can use the -a option to disable retrying on all
    threads.  You can use the -t option to set a shorter timeout.

User defined variables:

    You can define your own variables for convenience or to be used in
    subsequent expressions.  You define them the same way you would define
    variables in C.  If the first character of your user defined variable is a
    $, then the variable's value will be available in future expressions,
    otherwise it will just be available in the current expression.

Continuing evaluation after a breakpoint:

    If the "-i false" option is used, and execution is interrupted by a
    breakpoint hit, once you are done with your investigation, you can either
    remove the expression execution frames from the stack with "thread return
    -x" or if you are still interested in the expression result you can issue
    the "continue" command and the expression evaluation will complete and the
    expression result will be available using the "thread.completed-expression"
    key in the thread format.

Examples:

    expr my_struct->a = my_array[3]
    expr -f bin -- (index * 8)   5
    expr unsigned int $foo = 5
    expr char c[] = "foo"; c[0]
     
     Important Note: Because this command takes 'raw' input, if you use any
     command options you must use ' -- ' between the end of the command options
     and the beginning of the raw input.

LLDB使用双破折号--分隔选项和表达式:

代码语言:javascript复制
(lldb) expression <some flags> -- <variable></variable></some flags>

下面列出了几个比较常用的选项:

  • D ( --depth ) - 设置打印聚合类型的递归深度(默认无限递归)。
  • O ( --object-desctiption ) - 打印description方法。
  • T ( --show-types ) - 显示每个变量的类型。
  • f ( --format ) - 设置输出格式。
  • i ( --ignore-breakpoints ) - 运行表达式时忽略表达式内的断点。

获取执行环境 特定语言命令

bugreport , frame , language

  • bugreport LLDB的 bugreport 命令可以生成一份详细的app当前状态的报告。该命令对于想要延迟追踪定位问题非常有用。为了保存app的状态,你可以使用 bugreport 来生成报告。
代码语言:javascript复制
help bugreport
     Commands for creating domain-specific bug reports.

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

The following subcommands are supported:

      unwind -- Create a bugreport for a bug in the stack unwinding code.

For more help on any particular subcommand, type 'help <command> <subcommand>'.

可以看到,bugreport的参数只有unwind,即:

代码语言:javascript复制
(lldb) bugreport unwind --outfile <path to output file></path to output file>
  • frame
  • language

单步调试

单步调试,修改指针变量的值,观测程序不同变化 Xcode调试面板上实际已经有了按钮去实现,再这里简单介绍下: n 命令,代表 Step Over 操作。 s 命令,代表 Step Into 操作。 finish 命令,代表 Step Out 操作。 c 命令,代表恢复程序执行操作。

其他:

  • command
  • platform
  • gui
  • image 1、image lookup --address寻址,定位异常代码位置 举例说明,下面这段代码,执行的时候必然会出现异常,因为越界了:
代码语言:javascript复制
NSString *str = @"245656";
[str substringFromIndex:10];

2、image lookup --type查看类型

------------------------------分割线------------------------------ ------------------------------分割线------------------------------ ------------------------------分割线------------------------------ --------------------------重要事情说三遍-------------------------

编辑断点

介绍完命令后,来个五星推荐必备技能:编辑断点!!编辑断点!!编辑断点!! 断点调试是一把刀,帮助我们披荆斩棘,而编辑断点则是磨刀石,能够让我们的刀更锋利,所向披靡。

0 人点赞