0%

lldb常用命令

lldb是llvm的一个子项目,用于调试llvm编译的程序,做iOS调试时,和lldb打交道应该是最多的,本文对lldb常用的一些命令做一些整理,以便后续查阅

lldb常用操作

lldb的指令格式

1
<command> [<subcommand> [<subcommand>...]] <action> [-options [optionvalue]] [argument [argument...]]
  • 命令
  • 子命令
  • 命令操作
  • 命令选项
  • 命令参数

例如给函数test设置短点

1
(lldb) breakpoint set -n test

我们常用的命令有

  • expression:表达式
  • thread:线程
  • frame:
  • breakpoint:符号断点
  • watchpoint:内存断点
  • image:镜像
  • register:寄存器
  • memory:内存
  • help:查看命令帮助

所有命令可以通过help <command> [<subcommand>]查看详细文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
(lldb) help breakpoint
Commands for operating on breakpoints (see 'help b' for shorthand.)

Syntax: breakpoint <subcommand> [<command-options>]

The following subcommands are supported:

clear -- Delete or disable breakpoints matching the specified source file and line.
command -- Commands for adding, removing and listing LLDB commands executed when a breakpoint is hit.
delete -- Delete the specified breakpoint(s). If no breakpoints are specified, delete them all.
disable -- Disable the specified breakpoint(s) without deleting them. If none are specified, disable all
breakpoints.
enable -- Enable the specified disabled breakpoint(s). If no breakpoints are specified, enable all of them.
list -- List some or all breakpoints at configurable levels of detail.
modify -- Modify the options on a breakpoint or set of breakpoints in the executable. If no breakpoint is
specified, acts on the last created breakpoint. With the exception of -e, -d and -i, passing an
empty argument clears the modification.
name -- Commands to manage name tags for breakpoints
read -- Read and set the breakpoints previously saved to a file with "breakpoint write".
set -- Sets a breakpoint or set of breakpoints in the executable.
write -- Write the breakpoints listed to a file that can be read in with "breakpoint read". If given no
arguments, writes all breakpoints.

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


(lldb) help breakpoint set
Sets a breakpoint or set of breakpoints in the executable.

Syntax: breakpoint set <cmd-options>
...

expression

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

  1. 调用方法expression

    1
    2
    3
    (lldb) expression self.view.hidden = 1

    (lldb) expression [self.view layoutIfNeeded]
  2. 添加一个本地变量expression

    1
    2
    3
    4
    # 命名以$开头
    (lldb) expression int $a = 10
    (lldb) p $a
    (int) $a = 1

    我们可以通过添加本地变量实现页面跳转

    1
    2
    3
    4
    5
    6
    7
    8
    # 定义变量并赋值
    (lldb) expression id $navVC = self.navigationController;
    # 定义变量
    (lldb) expression id $vc
    # 创建vc
    (lldb) expression $vc = [[ViewController alloc] init];
    # push跳转
    (lldb) expression [$navVC pushViewController:$vc animated:YES]
  3. 打印变量地址expression --, p

    1
    2
    3
    4
    5
    6
    7
    8
    9
    (lldb) expression button
    (UIButton *) $4 = 0x00007f9d1ea05b50

    # `--`表示选项结束
    (lldb) expression -- button
    (UIButton *) $5 = 0x00007f9d1ea05b50

    (lldb) p button
    (UIButton *) $6 = 0x00007f9d1ea05b50

    expression --可以简写成expressionprintp

  4. 打印变量值expression -o --,po

    1
    2
    3
    4
    5
    6
    # `--` 表示选项结束,`-o`表示按对象输出
    (lldb) expression -o -- button
    <UIButton: 0x7f9d1ea05b50; frame = (184 433; 46 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x600003a54ba0>>

    (lldb) po button
    <UIButton: 0x7f9d1ea05b50; frame = (184 433; 46 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x600003a54ba0>>

    expression -o --可以简写成po,实际是调用对象的description方法,输出返回值

thread

  1. 打印调用堆栈信息thread backtrace, bt

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    (lldb) bt
    * thread #1, queue = 'com.apple.main-thread', stop reason = step over
    * frame #0: 0x00000001087d1bbf Test`-[ViewController viewDidLoad](self=0x00007fba7cc0ce10, _cmd="viewDidLoad") at ViewController.m:101:1
    frame #1: 0x00007fff4856c066 UIKitCore`-[UIViewController _sendViewDidLoadWithAppearanceProxyObjectTaggingEnabled] + 83
    frame #2: 0x00007fff48570f84 UIKitCore`-[UIViewController loadViewIfRequired] + 1084
    frame #3: 0x00007fff485713a1 UIKitCore`-[UIViewController view] + 27
    frame #4: 0x00007fff48c51ae1 UIKitCore`-[UIWindow addRootViewControllerViewIfPossible] + 326
    frame #5: 0x00007fff48c5110a UIKitCore`-[UIWindow _updateLayerOrderingAndSetLayerHidden:actionBlock:] + 219
    frame #6: 0x00007fff48c52195 UIKitCore`-[UIWindow _setHidden:forced:] + 362
    frame #7: 0x00007fff48c655bc UIKitCore`-[UIWindow _mainQueue_makeKeyAndVisible] + 42
    frame #8: 0x00007fff48e84d2c UIKitCore`-[UIWindowScene _makeKeyAndVisibleIfNeeded] + 202
    frame #9: 0x00007fff48177720 UIKitCore`+[UIScene _sceneForFBSScene:create:withSession:connectionOptions:] + 1405
    frame #10: 0x00007fff48c15561 UIKitCore`-[UIApplication _connectUISceneFromFBSScene:transitionContext:] + 1019
    frame #11: 0x00007fff48c15898 UIKitCore`-[UIApplication workspace:didCreateScene:withTransitionContext:completion:] + 291
    frame #12: 0x00007fff4876a160 UIKitCore`-[UIApplicationSceneClientAgent scene:didInitializeWithEvent:completion:] + 361
    frame #13: 0x00007fff36c6091e FrontBoardServices`-[FBSSceneImpl _callOutQueue_agent_didCreateWithTransitionContext:completion:] + 419
    frame #14: 0x00007fff36c869b1 FrontBoardServices`__86-[FBSWorkspaceScenesClient sceneID:createWithParameters:transitionContext:completion:]_block_invoke.154 + 102
    frame #15: 0x00007fff36c6b347 FrontBoardServices`-[FBSWorkspace _calloutQueue_executeCalloutFromSource:withBlock:] + 220
    frame #16: 0x00007fff36c86642 FrontBoardServices`__86-[FBSWorkspaceScenesClient sceneID:createWithParameters:transitionContext:completion:]_block_invoke + 355
    frame #17: 0x0000000108a81e8e libdispatch.dylib`_dispatch_client_callout + 8
    frame #18: 0x0000000108a84da2 libdispatch.dylib`_dispatch_block_invoke_direct + 300
    frame #19: 0x00007fff36cac2d9 FrontBoardServices`__FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 30
    frame #20: 0x00007fff36cabfc7 FrontBoardServices`-[FBSSerialQueue _queue_performNextIfPossible] + 441
    frame #21: 0x00007fff36cac4d6 FrontBoardServices`-[FBSSerialQueue _performNextFromRunLoopSource] + 22
    frame #22: 0x00007fff23da1c71 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    frame #23: 0x00007fff23da1b9c CoreFoundation`__CFRunLoopDoSource0 + 76
    frame #24: 0x00007fff23da13cc CoreFoundation`__CFRunLoopDoSources0 + 268
    frame #25: 0x00007fff23d9bf6e CoreFoundation`__CFRunLoopRun + 974
    frame #26: 0x00007fff23d9b884 CoreFoundation`CFRunLoopRunSpecific + 404
    frame #27: 0x00007fff38b5ac1a GraphicsServices`GSEventRunModal + 139
    frame #28: 0x00007fff48c19220 UIKitCore`UIApplicationMain + 1605
    frame #29: 0x00000001087d2072 Test`main(argc=1, argv=0x00007ffee742dd98) at main.m:18:12
    frame #30: 0x00007fff519b910d libdyld.dylib`start + 1
    frame #31: 0x00007fff519b910d libdyld.dylib`start + 1
  2. frame variable [value]
    打印当前调用栈的变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    - (int)a:(NSSet *)a b:(NSString *)b c:(long)c d:(int)d e:(int)e {
    NSString *f = @"v";
    NSUInteger g = f.length;
    g += 1;
    return 10;
    }

    // 调用
    [self a:nil b:@"fdd" c:1231 d:2123 e:23];

    在函数里面查看

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    (lldb) frame variable
    (ViewController *) self = 0x00007ffe4f4090b0
    (SEL) _cmd = "a:b:c:d:e:"
    (NSSet *) a = nil
    (__NSCFConstantString *) b = 0x0000000101b41088 @"fdd"
    (long) c = 1231
    (int) d = 2123
    (int) e = 23
    (__NSCFConstantString *) f = 0x0000000101b410a8 @"v"
    (NSUInteger) g = 2
  3. 流程控制

    • thread continue̵/continue̵/c: 程序继续运行
    • thread step-over/next/n:单步运行(源码),不会进入子函数
    • thread step-in/step/s: 单步运行(源码),会进入子函数
    • thread step-out/finish: 直接执行完当前函数的所有代码,返回到调用的地方
    • thread return [value]/thread r:让当前函数直接返回,不执行当前断点后面代码
    • thread step-inst-over/nexti/ni:单步运行(汇编),不会进入子函数
    • thread step-inst/stepi/si:单步运行(汇编),会进入子函数

    前面四个对应Xcode工具栏的前四个
    -

  4. 调用栈回退和前进

    • up/down

breakpoint

  1. 设置断点breakpoint set

    • breakpoint set -a 函数地址
    • breakpoint set -n 函数符号
    1
    2
    3
    4
    5
    6
    (lldb) breakpoint set -a 0x0000000101b3eab3
    Breakpoint 6: where = Test`-[ViewController viewDidLoad] + 147 at ViewController.m:101:5, address = 0x0000000101b3eab3

    (lldb) breakpoint set -n "-[ViewController viewDidLoad]"

    (lldb) breakpoint set -n touchesBegan:withEvent:
  2. 列出所有的断点:breakpoint list

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    (lldb) breakpoint list
    Current breakpoints:
    1: file = '/Users/bomo/Desktop/Test/Test/ViewController.m', line = 116, exact_match = 0, locations = 1, resolved = 1, hit count = 0
    1.1: where = Test`-[ViewController test:] + 60 at ViewController.m:116:21, address = 0x0000000101b3ebec, resolved, hit count = 0

    2: file = '/Users/bomo/Desktop/Test/Test/ViewController.m', line = 114, exact_match = 0, locations = 1, resolved = 1, hit count = 0
    2.1: where = Test`-[ViewController test:] + 36 at ViewController.m:114:21, address = 0x0000000101b3ebd4, resolved, hit count = 0

    3: file = '/Users/bomo/Desktop/Test/Test/ViewController.m', line = 109, exact_match = 0, locations = 1, resolved = 1, hit count = 1
    3.1: where = Test`-[ViewController a:b:c:d:e:] + 162 at ViewController.m:110:1, address = 0x0000000101b3eb72, resolved, hit count = 1

    4: address = 0x00000001007f1cac, locations = 1
    4.1: address = 0x00000001007f1cac, unresolved, hit count = 0

    上面有4个断点,每个断点都有一个整数编号

  3. 设置断点状态

    • breakpoint disable 断点编号: 禁用断点
    • breakpoint enable 断点编号: 启用断点
    • breakpoint delete 断点编号: 删除断点
  4. 断点命令:

    • breakpoint command add 断点编号: 给断点预先设置需要执行的命令,当触发断点时候,就会执行

    • breakpoint command list 断点编号: 列出断点的绑定的命令

    • breakpoint command delete 断点编号: 删除断点绑定的命令

      1
      2
      3
      4
      5
      6
      (lldb) breakpoint command add 1
      Enter your debugger command(s). Type 'DONE' to end.
      > print "断点命中了"
      > print "断点参数为"
      > po sender
      > DONE

      当断点命中的时候时候

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      print "断点命中了"
      (const char [16]) $0 = "断点命中了"

      print "断点参数为"
      (const char [16]) $1 = "断点参数为"

      po sender
      <UIButton: 0x7fa86f80bf10; frame = (184 433; 46 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x600001826580>>

      # 查看断点的命令
      (lldb) breakpoint command list 1
      1: file = '/Users/bomo/Desktop/Test/Test/ViewController.m', line = 105, exact_match = 0, locations = 1, resolved = 1, hit count = 4
      Breakpoint commands:
      print "断点命中了"
      print "断点参数为"
      po sender

      1.1: where = Test`-[ViewController test:] + 36 at ViewController.m:105:20, address = 0x000000010375ed24, resolved, hit count = 4

      # 删除断点的命令
      (lldb) breakpoint command delete 1

内存断点

内存断点与符号断点用法类似

  • watchpoint set variable 变量

    1
    (lldb) watchpoint set variable self->age
  • watchpoint set expression 地址

    1
    (lldb) watchpoint set expression 0x0000000101b3eab3
  • watchpoint list

  • watchpoint diable 断点编号

  • watchpoint enable 断点编号

  • watchpoint delete 断点编号

  • watchpoint command add 断点编号

  • watchpoint command list 断点编号

  • watchpoint command delete 断点编号

image

  1. 列出所加载的模块信息image list -o -f

    1
    2
    3
    4
    5
    6
    7
    (lldb) image list -o -f
    [ 0] 0x000000000375d000 /Users/bomo/Library/Developer/Xcode/DerivedData/Test-frsrzegtqyfuthfhcjefdlbaqmgi/Build/Products/Debug-iphonesimulator/Test.app/Test
    [ 1] 0x0000000108ca5000 /usr/lib/dyld
    [ 2] 0x000000010376c000 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/dyld_sim
    [ 3] 0x00000001039c7000 /Users/bomo/Library/Developer/Xcode/DerivedData/Test-frsrzegtqyfuthfhcjefdlbaqmgi/Build/Products/Debug-iphonesimulator/Test.app/Frameworks/AppOrderFiles.framework/AppOrderFiles
    [ 4] 0x00007fff2587b000 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/Foundation.framework/Foundation
    ...

    可以使用grep过滤

    1
    2
    3
    4
    5
    (lldb) image list -o -f Test
    [ 0] /Users/bomo/Library/Developer/Xcode/DerivedData/Test-frsrzegtqyfuthfhcjefdlbaqmgi/Build/Products/Debug-iphoneos/Test.app/Test 0x0000000004a2c000

    (lldb) image list -o -f | grep Test
    [ 0] /Users/bomo/Library/Developer/Xcode/DerivedData/Test-frsrzegtqyfuthfhcjefdlbaqmgi/Build/Products/Debug-iphoneos/Test.app/Test 0x0000000004a2c000

    可以使用image list命令查看ASLR偏移地址,上面表示Test文件的偏移地址为0x0000000004a2c000,MachO的TEXT段的偏移地址为0x0000000104a2c000(ASLR+PAGEZERO)

  2. 查找模块image lookup

    • image lookup -t 类型
    • image lookup -a 地址
    • image lookup -n 符号
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    (lldb) image lookup -t AppDelegate
    Best match found in /Users/bomo/Library/Developer/Xcode/DerivedData/Test-frsrzegtqyfuthfhcjefdlbaqmgi/Build/Products/Debug-iphonesimulator/Test.app/Test:
    id = {0x20000002b}, name = "AppDelegate", byte-size = 8, decl = AppDelegate.h:11, compiler_type = "@interface AppDelegate : UIResponder
    @end"

    (lldb) image lookup -n test:
    1 match found in /Users/bomo/Library/Developer/Xcode/DerivedData/Test-frsrzegtqyfuthfhcjefdlbaqmgi/Build/Products/Debug-iphonesimulator/Test.app/Test:
    Address: Test[0x0000000100001d00] (Test.__TEXT.__text + 688)
    Summary: Test`-[ViewController test:] at ViewController.m:103

    (lldb) image lookup -a 0x000000010375ed24
    Address: Test[0x0000000100001d24] (Test.__TEXT.__text + 724)
    Summary: Test`-[ViewController test:] + 36 at ViewController.m:105:20

    其中image lookup -a 地址经常用于查询崩溃位置,通过地址查询崩溃的地方位于哪个模块哪个文件哪个位置

register

register指令能够获取和修改各个寄存器的信息

  1. 读取所有寄存器信息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    (lldb) register read
    General Purpose Registers:
    x0 = 0x0000000000000000
    x1 = 0x00000001bcf641e4
    x2 = 0x000000010536d050
    x3 = 0x000000028283b840
    x4 = 0x000000028283b840
    x5 = 0x000000028283b840
    x6 = 0x0000000000000000
    x7 = 0x0000000000000403
    x8 = 0x0000000104dd1568 (void *)0x0000000104dd15b8: ABC
    x9 = 0x0000000000000006
    x10 = 0x0000000000000008
    x11 = 0x0000000000000000
    x12 = 0x000000010a811840
    x13 = 0x000005a1ce8843c7 (0x00000001ce8843c7) (void *)0x000001ce88448800
    x14 = 0x00000001bd054dde
    x15 = 0x0000000104dd0220 (void *)0x00000001bd054dde
    x16 = 0x00000001818daa90 libobjc.A.dylib`objc_release
    x17 = 0x0000000186100418 UIKitCore`-[UIView(UIKitManual) retain]
    x18 = 0x0000000000000000
    x19 = 0x000000028283b840
    x20 = 0x000000010536d050
    x21 = 0x00000001be56b4fe
    x22 = 0x0000000106c04bd0
    x23 = 0x0000000000000001
    x24 = 0x00000001ce893fe8 UIKitCore`UIApp
    x25 = 0x0000000000000000
    x26 = 0x00000001bcf656b4
    x27 = 0x0000000106c04bd0
    x28 = 0x00000002813adfe0
    fp = 0x000000016b039390
    lr = 0x0000000104dc9d1c Test`-[ViewController test:] + 40 at ViewController.m:104
    sp = 0x000000016b039340
    pc = 0x0000000104dc9d24 Test`-[ViewController test:] + 48 at ViewController.m:106:20
    cpsr = 0x00000000
  • register read/格式 寄存器名称

    1
    2
    # 读取寄存器x0的值
    registe read $x0
  • register write 寄存器名称 数值

    1
    registe write $x1 10

查看内存

memory read/数量+格式+字节数 内存地址

格式:x为16进制,f位浮点数,d为10进制
字节数:b为1个字节,h为2个字节,w为4个字节,g为8个字节

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
(lldb) memory read 0x00006000001600b0
0x6000001600b0: 00 ed c1 89 ff 7f 00 00 00 00 00 00 00 00 00 00 ................
0x6000001600c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

(lldb) memory read/3xg 0x00006000001600b0
0x6000001600b0: 0x00007fff89c1ed00 0x0000000000000000
0x6000001600c0: 0x0000000000000000

(lldb) memory read/8xg 0x00006000001600b0
0x6000001600b0: 0x00007fff89c1ed00 0x0000000000000000
0x6000001600c0: 0x0000000000000000 0x0000000000000000
0x6000001600d0: 0x0000000000000000 0x0000000000000000
0x6000001600e0: 0x0000000000000000 0x0000000000000000

(lldb) memory read/8xw 0x00006000001600b0
0x6000001600b0: 0x89c1ed00 0x00007fff 0x00000000 0x00000000
0x6000001600c0: 0x00000000 0x00000000 0x00000000 0x00000000

(lldb) memory read/3dw 0x00006000001600b0
0x6000001600b0: -1983779584
0x6000001600b4: 32767
0x6000001600b8: 0

(lldb) memory read/3fw 0x00006000001600b0
0x6000001600b0: -4.66859336E-33
0x6000001600b4: 4.59163468E-41
0x6000001600bc: 0

也可以直接使用x查看内存,用法和memory read一样

1
2
3
(lldb) x/4xg 0x00006000001600b0
0x6000001600b0: 0x00007fff89c1ed00 0x0000000000000000
0x6000001600c0: 0x0000000000000000 0x0000000000000000

写内存

1
memory write 内存地址 述职

缩写

lldb大部分命令都支持缩写

命令 缩写
expression exp
thread backtrace bt
breakpoint list br l
process continue continue, c
thread step-over next, n
thread step-in step, s
thread step-out finish, f
thread step-inst-over nexti, ni
thread step-inst stepi, si