LLDB命令速查手册

LLDB是Xcode上默认的调试器,支持C/C++、Objective-C 和 Swift 程序的调试,也是LLVM项目的一个可重用的高性能调试器。在LLVM项目的lldb子目录可以查看LLDB的源码:llvm-project

LLDB命令格式

<command> [<subcommand> [<subcommand>...]] <action> [-options [optionvalue]] [argument [argument...]]
//命令 + 子命令 + 命令选项 + 命令参数

LLDB命令参数、选项和选项值都用空格分隔的,双引号用于保护参数中的空格。如果需要在参数中放置反斜杠或双引号字符,请在使用反斜杠。对于是二进制数据的参数,可以使用' -- '作为参数结束标记。

获取帮助信息

(lldb) help

help命令将列出所有支持的命令列表。如果需要继续查看某一个命令的帮助信息,后面直接跟上命令的名字即可:

(lldb) help memory

LLDB常用命令

expression

执行表达式并将表达式的执行结果输出打印。开发中常用po来打印对象,po实际上就是expression -O的别名。

breakpoint:代码调试断点

指定文件和行号设置断点:

breakpoint set --file easeapi.c --line 12
breakpoint set -f ViewController.m -l 12

指定函数名称设置断点:

//对于C函数,填写完整的函数名称。
breakpoint set --name easeapi_func
breakpoint set -n easeapi_func

//对于Objective-C函数,填写完整的方法签名Selector字符串。
breakpoint set -n viewDidAppear:
breakpoint set -n tableView:cellForRowAtIndexPath:

//对于Swift方法,填写函数名称,参数省略。
breakpoint set -n easeapi_func

指定对象方法设置断点:

breakpoint set -n "-[ViewController viewDidAppear:]"
breakpoint set -n "+[ViewController classFunc]"
//Swift函数
breakpoint set -n "ViewController.easeapi_func"

指定代码地址设置断点:

breakpoint set --address 0x1021744bb
breakpoint set -a 0x1021744bb

查看已经设置的断点:

(lldb) breakpoint list
Current breakpoints:
1: file = '/Users/easeapi/Desktop/Job/ViewController.m', line = 163, exact_match = 0, locations = 1, resolved = 1, hit count = 1

  1.1: where = Project`-[ViewController viewDidLoad] + 3461 at ViewController.m:163:10, address = 0x0000000102174355, resolved, hit count = 1 

2: file = '/Users/easeapi/Desktop/Job/ViewController.m', line = 66, exact_match = 0, locations = 1, resolved = 1, hit count = 1

  2.1: where = Project `-[ViewController viewDidLoad] + 68 at ViewController.m:66:41, address = 0x0000000102173614, resolved, hit count = 1 

3: address = Project[0x00000001000044bb], locations = 1, resolved = 1, hit count = 1
  3.1: where = Project `-[ViewController viewDidLoad] + 3819 at ViewController.m:171:14, address = 0x00000001021744bb, resolved, hit count = 1 

每一次设置的断点都是逻辑断点(对应编号1、2、3等),一个逻辑断点可以解析为多个断点位置(对应编号1.1等)。比如仅对viewDidLoad方法做一个逻辑断点,则每一个加载类的viewDidLoad方法都会被标记为断点位置。

取消断点:

//breakpoint delete [断点编号]
breakpoint delete 1

删除所有断点:

breakpoint delete

watchpoint:内存调试断点

与breakpoint不同,watchpoint允许开发者对内存中的变量或指定内存的数据进行监控。当该地址有数据写入时,会命中断点。

设置观察变量:

watchpoint set variable value
watchpoint set variable self->_dataArray//不能使用点语法

设置观察内存地址:

watchpoint set expression 0x000000016f47fc70

查看所有内存断点:

(lldb) watchpoint list
Number of supported hardware watchpoints: 4
Current watchpoints:
Watchpoint 7: addr = 0x13dd10618 size = 8 state = enabled type = w
    watchpoint spec = 'self->_dataArray'
    new value: 0x000000028279d380
Watchpoint 8: addr = 0x16f47fc70 size = 8 state = enabled type = w
    new value: 1024

删除内存断点:

watchpoint delete index
watchpoint delete

register:寄存器操作

读取所有寄存器信息:

(lldb) register read
General Purpose Registers:
        x0 = 0x0000000100b4a0ec  "/Users/easeapi/Desktop/Job/"
        x1 = 0x000000016f47fc70
        x2 = 0x0000000000000002
        x3 = 0x0000000197b59f30  libsystem_malloc.dylib`nanov2_free_definite_size$VARIANT$mp
        x4 = 0x0000000000000402
        x5 = 0x000000016f47ea88
        x6 = 0x0000000000000001
        x7 = 0x0000000000000002
        x8 = 0x0000000000000000
        x9 = 0x0000000000000000
       x10 = 0x000001a1d17d01c1 (0x00000001d17d01c1) (void *)0xb0000001a1d17d03
       x11 = 0x00000000000007fb
       x12 = 0x00000000000007fd
       x13 = 0x0000000000000000
       x14 = 0x00000000c0c45000
       x15 = 0x000000000000008a
       x16 = 0x0000000197177c90  libobjc.A.dylib`objc_release
       x17 = 0x0000000040c00000
       x18 = 0x0000000000000000
       x19 = 0x00000001d1807364  UIKitCore`_UIApplicationLinkedOnVersion
       x20 = 0x000000013dd102c0
       x21 = 0x0000000000000018
       x22 = 0x00000001c4e93601  "count"
       x23 = 0x0000000000000000
       x24 = 0x0000000000000000
       x25 = 0x000000013e010e00
       x26 = 0x0000000000000400
       x27 = 0x000000028209d4a0
       x28 = 0x0000000000000000
        fp = 0x000000016f47fe20
        lr = 0x0000000100983770  Project`-[ViewController viewDidLoad] + 2608 at ViewController.m:166:9
        sp = 0x000000016f47fa60
        pc = 0x0000000100983790  Project`-[ViewController viewDidLoad] + 2640 at ViewController.m:171:14
      cpsr = 0x20000000

ARM64架构中共有34个寄存器,包括31个通用寄存器、SP、PC、CPSR。

  • 通用寄存器

r0 - r30是31个通用寄存器。每个寄存器可以存取一个64位大小的数。 当使用x0 - x30访问时,它就是一个64位的数。当使用w0 - w30访问时,访问的是这些寄存器的低32位。 r29又称FP寄存器(frame point),主要用来保存栈帧(栈底)指针。 r30又称LR寄存器(link register),主要用来保存函数返回地址。

  • SP:stack pointer,栈顶指针;
  • PC:用来记录当前执行的指令地址;
  • CPSR:状态寄存器。

参考:iOS crash log分析实践

读取指定寄存器的值:

register read x1

写入寄存器:

register write x0 1

memory:内存操作

查看内存数据。命令格式:

memory read/个数+格式+每一个的字节数 内存地址
格式:x-16进制、f-浮点数,d-10进制;
字节数:b-1个字节,h-2个字节,w-4个字节,g-8个字节。
默认为:16xb

示例:

(lldb) memory read/8xg 0x000000016f08fc70
0x16f08fc70: 0x0000000000000400 0x0000000100f5fe28
0x16f08fc80: 0x0000000000000001 0x0000000000000000
0x16f08fc90: 0x0000000100f678b8 0x00000001c4e9ee67
0x16f08fca0: 0x00000001015051e0 0x00000001d17baf18

也可以使用x查看内存,和memory read的效果一样:

x/8xg 0x000000016f08fc70

向指定内存写入数据:

//写入1个字节
memory write 0x000000016f08fc70 9
//以8字节对齐,写入两个数据
memory write 0x000000016f08fc70 -s 8 0x01 0x02

image:image操作

列出当前进程空间加载的image:

image list

查找信息:

//查找类信息
image lookup --type UIViewController
image lookup -t UIViewController

//查找符号
image lookup --name viewDidLoad
image lookup -n viewDidLoad

//查找地址
image lookup --address 0x000000018ff60490
image lookup -a 0x000000018ff60490

导出image信息:

//导出所有sections信息
image dump sections
//导出所有符号信息
image dump symtab

thread:线程

查看线程列表

(lldb) thread list
Process 17177 stopped
  thread #1: tid = 0x285da2e, 0x00007fff603032ba libsystem_kernel.dylib`mach_msg_trap + 10, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
* thread #2: tid = 0x285dc0c, 0x00007fff60305cde libsystem_kernel.dylib`__psynch_cvwait + 10
  thread #3: tid = 0x285dc0d, 0x00007fff60305cde libsystem_kernel.dylib`__psynch_cvwait + 10
  thread #4: tid = 0x285dc0e, 0x00007fff60305cde libsystem_kernel.dylib`__psynch_cvwait + 10
  thread #6: tid = 0x285dc10, 0x00007fff6033f420 libsystem_pthread.dylib`start_wqthread
  thread #8: tid = 0x285dc18, 0x00007fff6033f420 libsystem_pthread.dylib`start_wqthread
  thread #10: tid = 0x285dc1b, 0x00007fff6030495e libsystem_kernel.dylib`__workq_kernreturn + 10
  thread #11: tid = 0x285dc1c, 0x00007fff603032ba libsystem_kernel.dylib`mach_msg_trap + 10, name = 'com.apple.uikit.eventfetch-thread'
  thread #13: tid = 0x285dec2, 0x00007fff6033f420 libsystem_pthread.dylib`start_wqthread
  thread #14: tid = 0x285dec1, 0x00007fff6030495e libsystem_kernel.dylib`__workq_kernreturn + 10

其中,使用*标记当前线程;使用#标记线程编号。

获取线程调用栈:

//获取当前线程调用栈
thread backtrace
bt
//获取所有线程调用栈
thread backtrace all

在Xcode中,有四个控制Debug的按钮:

  • Continue:程序继续运行
thread continue/continue/c
  • Step over:单步指定,遇到子函数时断点不会进入子函数。
thread step-over/next/n
  • Step into:单步运行,遇到子函数时断点会进入子函数。
thread step-into/step/s
  • Step out:执行完当前函数剩余的代码,返回上一层函数。
thread step-out/finish

修改函数返回:

thread return 10

当需要修改一个函数返回值时,可以在函数开始位置加以上断点并修改到需要的返回值。

frame:栈帧信息

查看当前栈帧变量:

frame variable

其它文章

iOS启动优化之二进制重排
iOS crash log分析实践
iOS Crash log符号化
iOS Link Map
iOS Method Swizzling使用陷阱