美文网首页
iOS LLDB(Low Lever Debug)

iOS LLDB(Low Lever Debug)

作者: HotPotCat | 来源:发表于2021-05-19 18:30 被阅读0次

    一、概述

    LLDB(Low Lever Debug这里的low指轻量级)默认内置于Xcode中的动态调试工具。标准的 LLDB 提供了一组广泛的命令,旨在与老版本的 GDB 命令兼容。 除了使用标准配置外,还可以很容易地自定义 LLDB 以满足实际需要。

    二、LLDB语法

    <command> [<subcommand> [<subcommand>...]] <action> [-options [option-value]] [argument [argument...]]
    
    • command:命令
    • subcommand:子命令
    • action:执行命令的操作
    • options:命令选项
    • arguement:参数
    • []:表示可选

    例子:

    //command     action    option    arguement
    breakpoint    set       -n        test1
    

    唯一匹配原则:根据n个字母已经能唯一匹配到某个命令,则只写n个字母等效于完整的命令(大小写敏感)。也就是说只要能识别出来命令唯一就可以:

    br s -n test1
    

    help

    直接在LLDB中输入help可以查看所有的LLDB命令。
    查看某一个命令help <command-name>/help <command> <subcommand>:

    help breakpoint
    help breakpoint set
    

    apropos

    apropos可以用来搜索命令相关信息。

    //将所有breakpoint命令搜索出来
    apropos breakpoint
    

    三、lldb常用命令

    3.1 lldb断点设置

    3.1.1 breakpoint

    breakpoint set

    • set 是子命令
    • -n 是选项--name 的缩写。

    根据方法名设置断点
    breakpoint set -n test1,相当于对符号test1下断点,所有的test1都会被断住:

    Breakpoint 4: where = LLDBTest`test1 at ViewController.m:17, address = 0x000000010be80d00
    

    这和在Xcode中设置符号断点是一样的:

    image.png
    区别是前者重新启动后就失效了。

    设置组断点
    breakpoint set -n "[ViewController click1:]" -n "[ViewController click2:]" -n "[ViewController click3:]"相当于下了一组断点:

    (lldb) breakpoint set -n "[ViewController click1:]" -n "[ViewController click2:]" -n "[ViewController click3:]"
    Breakpoint 6: 3 locations.
    (lldb) breakpoint list 6
    6: names = {'[ViewController click1:]', '[ViewController click1:]', '[ViewController click1:]', '[ViewController click2:]', '[ViewController click2:]', '[ViewController click2:]', '[ViewController click3:]', '[ViewController click3:]', '[ViewController click3:]'}, locations = 3, resolved = 3, hit count = 0
      6.1: where = LLDBTest`-[ViewController click1:] at ViewController.m:31, address = 0x000000010be80e00, resolved, hit count = 0 
      6.2: where = LLDBTest`-[ViewController click2:] at ViewController.m:35, address = 0x000000010be80e40, resolved, hit count = 0 
      6.3: where = LLDBTest`-[ViewController click3:] at ViewController.m:40, address = 0x000000010be80e80, resolved, hit count = 0 
    

    可以同时启用和禁用组断点。

    使用-f指定文件

    (lldb) br set -f ViewController.m -n click1:
    Breakpoint 12: where = LLDBTest`-[ViewController click1:] at ViewController.m:31, address = 0x000000010be80e00
    

    使用-l指定某一行设置断点

    (lldb) br set -f ViewController.m -l 40
    Breakpoint 13: where = LLDBTest`-[ViewController click3:] at ViewController.m:40, address = 0x000000010be80e80
    

    使用-c设置条件断点
    只要计算的结果是个bool型或整型数值就可以。test2:方法接收一个布尔值参数,则当传入的值为YES时才断住:

    (lldb) br set -n test2: -c enable==YES
    Breakpoint 1: where = LLDBTest`-[ViewController test2:] at ViewController.m:45, address = 0x0000000100dc1e80
    

    使用-F设置函数全名

    (lldb) br set -F test1
    Breakpoint 1: where = LLDBTest`test1 at ViewController.m:17, address = 0x000000010762ecb0
    

    使用-a设置地址断点

    (lldb)  br set -a 0x000000010762ecb0
    Breakpoint 2: where = LLDBTest`test1 at ViewController.m:17, address = 0x000000010762ecb0
    

    使用--selector设置断点

    (lldb) br set -n touchesBegan:withEvent:
    Breakpoint 2: 97 locations.
    (lldb) br set --selector touchesBegan:withEvent:
    Breakpoint 3: 97 locations.
    

    --selector在这里和-n等价都是全部匹配。不过-n是针对符号,--selector针对OC的方法。

    使用-r模糊匹配

    (lldb) br set -f ViewController.m -r test
    Breakpoint 4: 2 locations.
    (lldb) br list
    Current breakpoints:
    4: regex = 'test', locations = 2, resolved = 2, hit count = 0
      4.1: where = LLDBTest`test1 at ViewController.m:17, address = 0x000000010762ecb0, resolved, hit count = 0 
      4.2: where = LLDBTest`-[ViewController test2:] at ViewController.m:45, address = 0x000000010762ee80, resolved, hit count = 0
    

    使用-i设置忽略次数

    (lldb) br set -f ViewController.m -r test -i 3
    Breakpoint 1: 2 locations.
    

    这里的次数是这组所有断点加起来的次数。

    breakpoint list

    查看断点列表:

    (lldb) br l
    Current breakpoints:
    7: names = {'[ViewController click1:]', '[ViewController click1:]', '[ViewController click1:]', '[ViewController click2:]', '[ViewController click2:]', '[ViewController click2:]', '[ViewController click3:]', '[ViewController click3:]', '[ViewController click3:]'}, locations = 3, resolved = 3, hit count = 0
      7.1: where = LLDBTest`-[ViewController click1:] at ViewController.m:31, address = 0x000000010be80e00, resolved, hit count = 0 
      7.2: where = LLDBTest`-[ViewController click2:] at ViewController.m:35, address = 0x000000010be80e40, resolved, hit count = 0 
      7.3: where = LLDBTest`-[ViewController click3:] at ViewController.m:40, address = 0x000000010be80e80, resolved, hit count = 0 
    

    查看某一个/某一组断点:

    (lldb) br l
    Current breakpoints:
    7: names = {'[ViewController click1:]', '[ViewController click1:]', '[ViewController click1:]', '[ViewController click2:]', '[ViewController click2:]', '[ViewController click2:]', '[ViewController click3:]', '[ViewController click3:]', '[ViewController click3:]'}, locations = 3, resolved = 3, hit count = 0
      7.1: where = LLDBTest`-[ViewController click1:] at ViewController.m:31, address = 0x000000010be80e00, resolved, hit count = 0 
      7.2: where = LLDBTest`-[ViewController click2:] at ViewController.m:35, address = 0x000000010be80e40, resolved, hit count = 0 
      7.3: where = LLDBTest`-[ViewController click3:] at ViewController.m:40, address = 0x000000010be80e80, resolved, hit count = 0 
    
    8: name = 'test1', locations = 1, resolved = 1, hit count = 0
      8.1: where = LLDBTest`test1 at ViewController.m:17, address = 0x000000010be80d00, resolved, hit count = 0 
    
    (lldb) br l 8
    8: name = 'test1', locations = 1, resolved = 1, hit count = 0
      8.1: where = LLDBTest`test1 at ViewController.m:17, address = 0x000000010be80d00, resolved, hit count = 0 
    
    (lldb) br l 7
    7: names = {'[ViewController click1:]', '[ViewController click1:]', '[ViewController click1:]', '[ViewController click2:]', '[ViewController click2:]', '[ViewController click2:]', '[ViewController click3:]', '[ViewController click3:]', '[ViewController click3:]'}, locations = 3, resolved = 3, hit count = 0
      7.1: where = LLDBTest`-[ViewController click1:] at ViewController.m:31, address = 0x000000010be80e00, resolved, hit count = 0 
      7.2: where = LLDBTest`-[ViewController click2:] at ViewController.m:35, address = 0x000000010be80e40, resolved, hit count = 0 
      7.3: where = LLDBTest`-[ViewController click3:] at ViewController.m:40, address = 0x000000010be80e80, resolved, hit count = 0 
    

    breakpoint disable/enable/delete

    breakpoint disable

    (lldb) br dis 7.1
    1 breakpoints disabled.
    
    image.png

    breakpoint enable

    (lldb) br en 7.1
    1 breakpoints enabled.
    
    

    breakpoint delete
    只能删除指定组断点,不能删除组里面的某一个。

    (lldb) br del 7.2
    0 breakpoints deleted; 1 breakpoint locations disabled.
    (lldb) br del 7
    1 breakpoints deleted; 0 breakpoint locations disabled.
    

    breakpoint delete删除所有断点

    (lldb) breakpoint delete
    About to delete all breakpoints, do you want to do that?: [Y/n] y
    All breakpoints removed. (3 breakpoints)
    

    ⚠️breakpoint组在一次运行过程中是一直递增的。多次添加断点只会断住一次。

    3.1.2 watchpoint 内存断点/地址断点

    breakpoint是对方法生效的断点,watchpoint是对地址生效的断点。如果地址里中的数据改变了,就让程序中断。

    watchpoint set

    watchpoint set variable

    (lldb) watchpoint set variable item1->_name
    Watchpoint created: Watchpoint 1: addr = 0x600002ce8868 size = 8 state = enabled type = w
        declare @ '/Users/zaizai/LLDBTest/LLDBTest/ViewController.m:28'
        watchpoint spec = 'item1->_name'
        new value: 0x000000010ad37038
    

    改变name值的时候就会断住(即使值没有变):

    Watchpoint 1 hit:
    old value: 0x0000000109319038
    new value: 0x0000000109319038
    

    watchpoint set variable传入的是变量名,不接受方法。所以不能使用watchpoint set variable item1.name

    watchpoint set expression

    (lldb) p item1->_name
    (__NSCFConstantString *) $0 = 0x000000010a0d0038 @"1"
    (lldb) p &item1->_name
    (NSString **) $1 = 0x00006000033bec48
    (lldb) watchpoint set expression 0x00006000033bec48
    Watchpoint created: Watchpoint 1: addr = 0x6000033bec48 size = 8 state = enabled type = w
        new value: 4463591480
    

    breakpoint类似,watchpoint也有watchpoint listwatchpoint disablewatchpoint enablewatchpoint delete

    3.2 lldb代码执行 expression、p、print、call、po

    expression执行一个表达式,并将表达式返回的结果输出。

    expression <cmd-options> -- <expr>
    
    • cmd-options:命令选项,一般使用默认。
    • --:命令选项结束符。
    • expr:表达式。

    pprintcall 都是expression --的别名:

    • print: 打印某个东西,可以是变量/表达式,pprint的缩写。
    • call: 调用某个方法。

    poexpression -O --的别名。调用的是description或者debugDescription方法。

    进制转换p/x、p/o、p/t
    p除了打印还有常量的进制转换功能。

    //默认10进制打印
    (lldb) p 100
    (int) $0 = 100
    //16进制打印
    (lldb) p/x 100
    (int) $1 = 0x00000064
    //8进制打印
    (lldb) p/o 100
    (int) $2 = 0144
    //2进制打印
    (lldb) p/t 100
    (int) $3 = 0b00000000000000000000000001100100
    //字符串转换为10进制
    (lldb) p/d 'A'
    (char) $4 = 65
    //10进制转换为字符
    (lldb) p/c 65
    (int) $5 = A\0\0\0
    

    浮点数转换

    (lldb) p/x (double) 180.0
    (double) $6 = 0x4066800000000000
    
    (lldb) p/f 0x4066800000000000
    (long) $1 = 180
    
    (lldb) e -f f -- 0x4066800000000000
    (long) $2 = 180
    

    x/nuf<addr>

    (lldb) x self
    0x600002c12180: 29 8a 00 00 01 80 1d 00 00 00 00 00 00 00 00 00  )...............
    0x600002c12190: 0e 00 00 00 00 00 00 00 00 5e 75 01 00 60 00 00  .........^u..`..
    (lldb) x/4gx self
    0x600002c12180: 0x001d800100008a29 0x0000000000000000
    0x600002c12190: 0x000000000000000e 0x0000600001755e00
    (lldb) x/4gw self
    0x600002c12180: 0x00008a29 0x001d8001 0x00000000 0x00000000
    

    x/nuf<addr>
    x就是 memory read 内存读取。

    • n: n表示要打印的内存单元的个数。
    • u: u表示一个地址单元的长度。iOS是小端模式。
      • b表示单字节
      • h表示双字节
      • w表示四字节
      • g表示八字节。
    • ff表示显示方式。
      • x按十六进制格式显示变量
      • d按十进制显示
      • u按十进制显示无符号整形
      • o按八进制显示变量
      • t按二进制显示变量
      • a按十六进制显示变量
      • i指令地址格式
      • c按字符格式显示变量
      • f按浮点数格式显示变量
    • addr:地址/数据。

    3.3查看堆栈信息

    bt(thread backtrace)

     thread #1, queue = 'com.apple.main-thread', stop reason = step over
      * frame #0: 0x000000010e60fa06 LLDBTest`-[ViewController touchesBegan:withEvent:](self=0x00007fce43806030, _cmd="touchesBegan:withEvent:", touches=1 element, event=0x0000600002168540) at ViewController.m:46:23
        frame #1: 0x00007fff246ca70f UIKitCore`forwardTouchMethod + 321
        frame #2: 0x00007fff246ca5bd UIKitCore`-[UIResponder touchesBegan:withEvent:] + 49
        frame #3: 0x00007fff246d95b5 UIKitCore`-[UIWindow _sendTouchesForEvent:] + 622
        frame #4: 0x00007fff246db6c7 UIKitCore`-[UIWindow sendEvent:] + 4774
        frame #5: 0x00007fff246b5466 UIKitCore`-[UIApplication sendEvent:] + 633
        frame #6: 0x00007fff24745f04 UIKitCore`__processEventQueue + 13895
        frame #7: 0x00007fff2473c877 UIKitCore`__eventFetcherSourceCallback + 104
        frame #8: 0x00007fff2039038a CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
        frame #9: 0x00007fff20390282 CoreFoundation`__CFRunLoopDoSource0 + 180
        frame #10: 0x00007fff2038f764 CoreFoundation`__CFRunLoopDoSources0 + 248
        frame #11: 0x00007fff20389f2f CoreFoundation`__CFRunLoopRun + 878
        frame #12: 0x00007fff203896d6 CoreFoundation`CFRunLoopRunSpecific + 567
        frame #13: 0x00007fff2c257db3 GraphicsServices`GSEventRunModal + 139
        frame #14: 0x00007fff24696cf7 UIKitCore`-[UIApplication _run] + 912
        frame #15: 0x00007fff2469bba8 UIKitCore`UIApplicationMain + 101
        frame #16: 0x000000010e60fff2 LLDBTest`main(argc=1, argv=0x00007ffee15efd20) at main.m:17:12
        frame #17: 0x00007fff2025a3e9 libdyld.dylib`start + 1
    

    up、down、frame select

    (lldb) up
    frame #3: 0x00007fff246d95b5 UIKitCore`-[UIWindow _sendTouchesForEvent:] + 622
    UIKitCore`-[UIWindow _sendTouchesForEvent:]:
    ->  0x7fff246d95b5 <+622>: lea    rax, [rip + 0x628a699c]   ; UIApp
        0x7fff246d95bc <+629>: mov    rdi, qword ptr [rax]
        0x7fff246d95bf <+632>: mov    rsi, qword ptr [rbp - 0x170]
        0x7fff246d95c6 <+639>: mov    rdx, r12
        0x7fff246d95c9 <+642>: mov    rcx, rbx
        0x7fff246d95cc <+645>: mov    r8, r14
        0x7fff246d95cf <+648>: call   r13
        0x7fff246d95d2 <+651>: mov    ecx, 0x1
    (lldb) down 
    frame #2: 0x00007fff246ca5bd UIKitCore`-[UIResponder touchesBegan:withEvent:] + 49
    UIKitCore`-[UIResponder touchesBegan:withEvent:]:
    ->  0x7fff246ca5bd <+49>: mov    rdi, rbx
        0x7fff246ca5c0 <+52>: pop    rbx
        0x7fff246ca5c1 <+53>: pop    r12
        0x7fff246ca5c3 <+55>: pop    r14
        0x7fff246ca5c5 <+57>: pop    r15
        0x7fff246ca5c7 <+59>: pop    rbp
        0x7fff246ca5c8 <+60>: jmp    qword ptr [rip + 0x5bf063e2] ; (void *)0x00007fff2018f760: objc_release
    
    UIKitCore`forwardTouchMethod:
        0x7fff246ca5ce <+0>:  push   rbp
    (lldb) frame select 10
    frame #10: 0x00007fff2038f764 CoreFoundation`__CFRunLoopDoSources0 + 248
    CoreFoundation`__CFRunLoopDoSources0:
    ->  0x7fff2038f764 <+248>: mov    r13d, eax
        0x7fff2038f767 <+251>: jmp    0x7fff2038f7dc            ; <+368>
        0x7fff2038f769 <+253>: xor    r13d, r13d
        0x7fff2038f76c <+256>: jmp    0x7fff2038f802            ; <+406>
        0x7fff2038f771 <+261>: mov    rbx, r14
        0x7fff2038f774 <+264>: mov    rdi, qword ptr [rbp - 0x38]
        0x7fff2038f778 <+268>: call   0x7fff20312bcc            ; CFArrayGetCount
        0x7fff2038f77d <+273>: mov    r15, rax
    

    这3个命令只是方便我们查看堆栈信息,寄存器还是在断点处。

    frame variable
    查看当前frame参数

    (lldb) frame variable
    (ViewController *) self = 0x00007f8c59405600
    (SEL) _cmd = "touchesBegan:withEvent:"
    (BOOL) enable = NO
    

    在已经执行过的frame中修改参数不会影响后面的结果。

    thread return

    thread return可以接受一个表达式,调用命令之后直接从当前的frame返回表达式的值。直接返回不执行后面的代码。相当于回滚(相当于直接到bl跳转的下一行汇编代码)。当然修改pc寄存器的值也能达到相同的效果。

    3.4 command指令

    给断点添加命令的命令。
    breakpoint command add

    (lldb) b test2:
    Breakpoint 1: where = LLDBTest`-[ViewController test2:] at ViewController.m:65, address = 0x0000000103e7db40
    (lldb) br command add 1
    Enter your debugger command(s).  Type 'DONE' to end.
    > frame variable
    > DONE
    

    当断点断住的时候执行frame variable指令。
    当然也可以只添加一条指令:

    br command add -o "po self" 1
    

    多次对同一个断点添加命令,后面命令会将前面命令覆盖

    breakpoint command list
    查看某个断点已有的命令(list 后必须有断点编号)

    (lldb) breakpoint command list 1
    Breakpoint 1:
        Breakpoint commands:
          po self
    

    3.5 target stop-Hook指令

    target stop-hook命令可以在每次stop的时候去执行一些命令

    (lldb) target stop-hook add -o "frame variable"
    Stop hook #1 added.
    

    command不同的是它对所有断点生效。相当于对程序下钩子。
    display命令等价:

    (lldb) display frame variable
    Stop hook #2 added.
    

    target stop-hook只对breakpointwatchpointstop生效,直接点击Xcode上的pause或者debug view hierarchy不会生效

    target stop-hook list

    (lldb) target stop-hook list
    Hook: 1
      State: enabled
      Commands: 
        frame variable
    
    Hook: 2
      State: enabled
      Commands: 
        expr -- frame variable
    

    target stop-hook disable / enable
    暂时让某个stop-hook失效/生效,不传id则代表全部。

    target stop-hook delete / undisplay
    删除stop-hook

    (lldb) target stop-hook delete
    Delete all stop hooks?: [Y/n] y
    

    delete可以不传idundisplay必须传id

    3.6 image(target modules)指令

    image lookup --address

    查找某个地址具体对应的文件位置,可以使用image lookup --address(image lookup -a)
    比如有一个crash:

    2021-05-19 18:19:45.833183+0800 LLDBTest[41719:24239029] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndexedSubscript:]: index 5 beyond bounds [0 .. 2]'
    *** First throw call stack:
    (
        0   CoreFoundation                      0x00007fff20421af6 __exceptionPreprocess + 242
        1   libobjc.A.dylib                     0x00007fff20177e78 objc_exception_throw + 48
        2   CoreFoundation                      0x00007fff2049e77f _CFThrowFormattedException + 194
        3   CoreFoundation                      0x00007fff20320825 -[__NSArrayM removeAllObjects] + 0
        4   LLDBTest                            0x0000000107c469f7 -[ViewController touchesBegan:withEvent:] + 151
    

    从上面的堆栈可以看到是-[ViewController touchesBegan:withEvent:]中的调用发生了crash,但是并不知道在ViewController.m的哪一行。使用image lookup -a就可以具体定位到确定的行数:

    (lldb) image lookup -a 0x0000000107c469f7
          Address: LLDBTest[0x00000001000019f7] (LLDBTest.__TEXT.__text + 807)
          Summary: LLDBTest`-[ViewController touchesBegan:withEvent:] + 151 at ViewController.m:48:28
    

    image lookup --name

    查找方法或者符号的信息可以使用image lookup --nameimage lookup -n):

    (lldb) image lookup -n test2:
    1 match found in /Users/zaizai/Library/Developer/Xcode/DerivedData/LLDBTest-enxwhkxlnnynraafdlfrcoxaibzm/Build/Products/Debug-iphonesimulator/LLDBTest.app/LLDBTest:
            Address: LLDBTest[0x0000000100001b30] (LLDBTest.__TEXT.__text + 1120)
            Summary: LLDBTest`-[ViewController test2:] at ViewController.m:65
    

    image lookup --type

    可以使用image lookup --type(image lookup -t)查看类型:

    (lldb) image lookup -t ViewController
    Best match found in /Users/zaizai/Library/Developer/Xcode/DerivedData/LLDBTest-enxwhkxlnnynraafdlfrcoxaibzm/Build/Products/Debug-iphonesimulator/LLDBTest.app/LLDBTest:
    id = {0x100000033}, name = "ViewController", byte-size = 16, decl = ViewController.h:10, compiler_type = "@interface ViewController : UIViewController{
        NSMutableArray * _items;
    }
    @property(nonatomic, readwrite, getter = items, setter = setItems:) NSMutableArray *items;
    @end"
    

    3.7 .lldbinit

    LLDB有了一个启动时加载的文件~/.lldbinit,每次启动都会加载。一些初始化的操作可以添加在.lldbinit中。由于这时候程序还没有真正运行,也有部分操作无法完成,比如设置断点等。
    chisellldb插件就是在.lldbinit初始化的。
    直接在.lldbinit中添加:

    target stop-hook add -o "frame variable"
    

    这样每次运行都会自动添加stop-hook

    Stop hook #1 added.
    (lldb) b -n test2:
    Breakpoint 1: where = LLDBTest`-[ViewController test2:] at ViewController.m:65, address = 0x000000010c519b40
    (ViewController *) self = 0x00007ffd95406160
    (SEL) _cmd = "touchesBegan:withEvent:"
    (BOOL) enable = NO
    

    3.8 其它命令

    • image list: 查看某块列表
    • register read: 读取寄存器
    • register write: 写入寄存器
    • Memory read: 读取内存值

    四、流程控制

    image.png
    • c (continue/thread continue) :继续执行。
    • n(next/thread step-over):单步运行,将子函数当做整体一步执行。
      ni:单步运行汇编级别。
    • sstep/thread step-in):单步运行,遇到子函数会进去。
      si:单步运行可跳转指令内部,汇编级别。
    • finishstep-out):表示直接走完当前方法,返回到上层frame

    LLDB官网API文档

    相关文章

      网友评论

          本文标题:iOS LLDB(Low Lever Debug)

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