iOS -LLDB简单命令

作者: 黄晓坚 | 来源:发表于2017-08-10 23:17 被阅读62次

    LLDB语法

    在使用LLDB之前,我们来先看看LLDB的语法,了解语法可以帮助我们清晰的使用LLDB:

    <command> [<subcommand> [<subcommand>...]] <action> [-options [option-value]] [argument [argument...]]
    

    一眼看上去可能比较迷茫,给大家解释一下:

    <command>(命令)和<subcommand>(子命令):LLDB调试命令的名称。命令和子命令按层级结构来排列:一个命令对象为跟随其的子命令对象创建一个上下文,子命令又为其子命令创建一个上下文,依此类推。
    <action>:执行命令的操作
    <options>:命令选项
    <arguement>:命令的参数
    []:表示命令是可选的,可以有也可以没有
    举个例子,假设我们给main方法设置一个断点,我们使用下面的命令:

    breakpoint set -n main
    这个命令对应到上面的语法就是:

    command:breakpoint表示断点命令
    action: set 表示设置断点
    option: -n 表示根据方法name设置断点
    arguement:mian表示方法名为mian

    原始(raw)命令

    LLDB支持不带命令选项(options)的原始(raw)命令,原始命令会将命令后面的所有东西当做参数(arguement)传递。不过很多原始命令也可以带命令选项,当你使用命令选项的时候,需要在命令选项后面加--区分命令选项和参数。

    常用的expression就是raw命令,一般情况下我们使用expression打印一个东西是这样的:

    (lldb) expression count
    (int) $2 = 4
    

    当我们想打印一个对象的时候。需要使用-O命令选项,我们应该用--将命令选项和参数区分:

    (lldb) expression -O -- self
    <ViewController: 0x7f9000f17660>
    

    唯一匹配原则

    LLDB的命令遵循唯一匹配原则:假如根据前n个字母已经能唯一匹配到某个命令,则只写前n个字母等效于写下完整的命令。
    前面提到我设置断点的命令,我们可以使用唯一匹配原则简写,下面2条命令等效:

    breakpoint set -n main
    br s -n main
    

    ~/.lldbinit

    LLDB有了一个启动时加载的文件~/.lldbinit,每次启动都会加载。所以一些初始化的事儿,我们可以写入~/.lldbinit中,比如给命令定义别名等。但是由于这时候程序还没有真正运行,也有部分操作无法在里面玩,比如设置断点。

    快捷键

    为了方便查询,就直接在文章开头po出常用的快捷键菜单把~

    快捷键功能 命令
    暂停/继续 cmd + ctrl + Y
    控制台显示/隐藏 cmd + Y
    光标切换到控制台 cmd + shift + C
    清空控制台 cmd + K
    step over F6
    step into F7
    step out F8

    LLDB命令

    expression

    expression命令的作用是执行一个表达式,并将表达式返回的结果输出。expression的完整语法是这样的:

    expression <cmd-options> -- <expr>
    <cmd-options>:命令选项,一般情况下使用默认的即可,不需要特别标明。
    --: 命令选项结束符,表示所有的命令选项已经设置完毕,如果没有命令选项,--可以省略
    <expr>: 要执行的表达式
    expression是LLDB里面最重要的命令都不为过。因为他能实现2个功能。

    • 执行某个表达式。
      我们在代码运行过程中,可以通过执行某个表达式来动态改变程序运行的轨迹。
      假如我们在运行过程中,突然想把self.tableView颜色改成红色,看看效果。我们不必写下代码,重新run,只需暂停程序,用expression改变颜色,再刷新一下界面,就能看到效果
      // 改变颜色
      (lldb) expression -- self.tableView.backgroundColor = [UIColor redColor];
      (UICachedDeviceRGBColor *) $0 = 0x0000608000661d00
     // 刷新界面
    (lldb) expression -- (void)[CATransaction flush]
    
    

    将返回值输出。
    也就是说我们可以通过expression来打印东西。
    假如我们想打印self.view:

    (lldb) expression -- self.tableView
    (UITableView *) $1 = 0x00007fefc3053200
    

    p & print & call

    一般情况下,我们直接用expression还是用得比较少的,更多时候我们用的是pprintcall。这三个命令其实都是expression --的别名(--表示不再接受命令选项,详情见前面原始(raw)命令这一节)

    print: 打印某个东西,可以是变量和表达式
    p: 可以看做是print的简写
    call: 调用某个方法。
    表面上看起来他们可能有不一样的地方,实际都是执行某个表达式(变量也当做表达式),将执行的结果输出到控制台上。所以你可以用p调用某个方法,也可以用call打印东西
    下面代码效果相同:

    
    (lldb) expression -- self.tableView
    (UITableView *) $1 = 0x00007fefc3053200
    (lldb) p self.tableView
    (UITableView *) $2 = 0x00007fefc3053200
    (lldb) e self.tableView
    (UITableView *) $3 = 0x00007fefc3053200
    (lldb) call self.tableView
    (UITableView *) $4 = 0x00007fefc3053200
    (lldb) print self.tableView
    (UITableView *) $5 = 0x00007fefc3053200
    
    

    根据唯一匹配原则,如果你没有自己添加特殊的命令别名。e也可以表示expression的意思。原始(raw)命令默认没有命令选项,所以e也能带给你同样的效果

    po

    我们知道,OC里所有的对象都是用指针表示的,所以一般打印的时候,打印出来的是对象的指针,而不是对象本身。如果我们想打印对象。我们需要使用命令选项:-O。为了更方便的使用,LLDB为expression -O --定义了一个别名:po

    (lldb) expression -- self.tableView
    (UITableView *) $6 = 0x00007fefc3053200
    
    (lldb) expression -O -- self.tableView
    <UITableView: 0x7fefc3053200; frame = (0 0; 414 736); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x600000250710>; layer = <CALayer: 0x6000002347a0>; contentOffset: {0, -153}; contentSize: {414, 0}>
    
    (lldb) po self.tableView
    <UITableView: 0x7fefc3053200; frame = (0 0; 414 736); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x600000250710>; layer = <CALayer: 0x6000002347a0>; contentOffset: {0, -153}; contentSize: {414, 0}>
    
    

    还有其他很多命令选项,不过我们一般用得比较少,所以我就不具体的一一介绍了,如果想了解,在LLDB控制台上输入:help expression即可查到expression所有的信息

    (lldb) 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 <source-language>] [-v[<description-verbosity>]] [-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 <source-language>] [-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 <source-language> ( --apply-fixits <source-language> )
                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
    
           -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 top-level definitions rather than code
                to be immediately executed.
    
           -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
    
    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.
    
    

    thread

    thread backtrace & bt

    有时候我们想要了解线程堆栈信息,可以使用thread backtrace
    thread backtrace作用是将线程的堆栈打印出来。我们来看看他的语法

    thread backtrace [-c <count>] [-s <frame-index>] [-e <boolean>]
    

    thread backtrace后面跟的都是命令选项:

    -c:设置打印堆栈的帧数(frame)
    -s:设置从哪个帧(frame)开始打印
    -e:是否显示额外的回溯
    实际上这些命令选项我们一般不需要使用。
    例如当发生crash的时候,我们可以使用thread backtrace查看堆栈调用

    
    (lldb) thread backtrace
    * thread #1: tid = 0xdd42, 0x000000010afb380b libobjc.A.dylib`objc_msgSend + 11, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
        frame #0: 0x000000010afb380b libobjc.A.dylib`objc_msgSend + 11
      * frame #1: 0x000000010aa9f75e TLLDB`-[ViewController viewDidLoad](self=0x00007fa270e1f440, _cmd="viewDidLoad") + 174 at ViewController.m:23
        frame #2: 0x000000010ba67f98 UIKit`-[UIViewController loadViewIfRequired] + 1198
        frame #3: 0x000000010ba682e7 UIKit`-[UIViewController view] + 27
        frame #4: 0x000000010b93eab0 UIKit`-[UIWindow addRootViewControllerViewIfPossible] + 61
        frame #5: 0x000000010b93f199 UIKit`-[UIWindow _setHidden:forced:] + 282
        frame #6: 0x000000010b950c2e UIKit`-[UIWindow makeKeyAndVisible] + 42
    
    

    我们可以看到crash发生在-[ViewController viewDidLoad]中的第23行,只需检查这行代码是不是干了什么非法的事儿就可以了。

    LLDB还为backtrace专门定义了一个别名:bt,他的效果与thread backtrace相同,如果你不想写那么长一串字母,直接写下bt即可:

    (lldb) bt
    

    c & n & s & finish

    一般在调试程序的时候,我们经常用到下面这4个按钮:

    Snip20170628_12.png

    用触摸板的孩子们可能会觉得点击这4个按钮比较费劲。其实LLDB命令也可以完成上面的操作,而且如果不输入命令,直接按Enter键,LLDB会自动执行上次的命令。按一下Enter就能达到我们想要的效果,有木有顿时感觉逼格满满的!!!
    我们来看看对应这4个按钮的LLDB命令:

    c/ continue/ thread continue: 这三个命令效果都等同于上图中第一个按钮的。表示程序继续运行
    n/ next/ thread step-over: 这三个命令效果等同于上图第二个按钮。表示单步运行
    s/ step/ thread step-in: 这三个命令效果等同于上图第三个按钮。表示进入某个方法
    finish/ step-out: 这两个命令效果等同于第四个按钮。表示直接走完当前方法,返回到上层frame

    thread return

    Debug的时候,也许会因为各种原因,我们不想让代码执行某个方法,或者要直接返回一个想要的值。这时候就该thread return上场了。

    thread return [<expr>]
    

    thread return可以接受一个表达式,调用命令之后直接从当前的frame返回表达式的值。

    例如:我们有一个TestMethodLLDB方法,默认情况下是返回YES。我们想要让他返回NO

    Snip20170628_15.png

    我们只需在方法的开始位置加一个断点,当程序中断的时候,输入命令即可:

    (lldb) thread return NO
    

    效果相当于在断点位置直接调用return NO;,不会执行断点后面的代码

    thread其他不常用的命令

    thread相关的还有其他一些不常用的命令,这里就简单介绍一下即可,如果需要了解更多,可以使用命令help thread查阅

    1.thread jump: 直接让程序跳到某一行。由于ARC下编译器实际插入了不少retainrelease命令。跳过一些代码不执行很可能会造成对象内存混乱发生crash
    2.thread list: 列出所有的线程
    3.thread selectt: 选择某个线程
    4.thread until: 传入一个line的参数,让程序执行到这行的时候暂停
    5.thread info: 输出当前线程的信息

    frame

    前面我们提到过很多次frame(帧)。可能有的朋友对frame这个概念还不太了解。随便打个断点

    Snip20170628_16.png

    我们在控制台上输入命令bt,可以打印出来所有的frame。如果仔细观察,这些frame和左边红框里的堆栈是一致的。平时我们看到的左边的堆栈就是frame。

    frame variable

    平时Debug的时候我们经常做的事就是查看变量的值,通过frame variable命令,可以打印出当前frame的所有变量

    (lldb) frame variable
    (ViewController *) self = 0x00007fa158526e60
    (SEL) _cmd = "text:"
    (BOOL) ret = YES
    (int) a = 3
    

    可以看到,他将self,_cmd,ret,a等本地变量都打印了出来

    如果我们要需要打印指定变量,也可以给frame variable传入参数:

    (lldb) frame variable self->_string
    (NSString *) self->_string = nil
    

    不过frame variable只接受变量作为参数,不接受表达式,也就是说我们无法使用frame variable self.string,因为self.string是调用stringgetter方法。所以一般打印指定变量,我更喜欢用p或者po

    其他不常用命令

    一般frame variable打印所有变量用得比较多,frame还有2个不怎么常用的命令:

    frame info: 查看当前frame的信息

    (lldb) frame info
    frame #0: 0x0000000102cb1c8b baisibudejie`__33-[HJTopicController loadViewData]_block_invoke((null)=0x000060800025be40, task=0x00007fab3373fe90, responseObject=2 key/value pairs) + 219 at HJTopicController.m:124
    
    

    frame select: 选择某个frame

    (lldb) frame select 1
    frame #1: 0x0000000102cc1f24 baisibudejie`__116-[AFHTTPSessionManager dataTaskWithHTTPMethod:URLString:parameters:uploadProgress:downloadProgress:success:failure:]_block_invoke.97((null)=<unavailable>, response=0x000060000022f940, responseObject=2 key/value pairs, error=0x0000000000000000) + 228 at AFHTTPSessionManager.m:287
       284              }
       285          } else {
       286              if (success) {
    -> 287                  success(dataTask, responseObject);
       288              }
       289          }
       290      }];
    
    

    当我们选择frame 1的时候,他会把frame1的信息和代码打印出来。不过一般我都是直接在Xcode左边点击某个frame,这样更方便

    相关文章

      网友评论

        本文标题:iOS -LLDB简单命令

        本文链接:https://www.haomeiwen.com/subject/euarcxtx.html