iOS开发之LLDB常用命令

一、前言

1、简介

LLDB是新一代高性能调试器。它构建为一组可重用的组件,可以高度利用较大的LLVM项目中的现有库,例如Clang表达式解析器和LLVM反汇编程序。LLDB是Mac OS X上Xcode的默认调试器,支持在桌面和iOS设备和模拟器上调试C,Objective-C和C ++。LLDB项目中的所有代码都是在标准LLVM许可证下提供的,这是一种开源的“BSD风格”许可证。

对于开发测试同学来说,学习iOS代码调试LLDB命令,能更好地辅助我们通过各种手段如修改变量返回值创造实际难以模拟的环境进行测试,甚至能协助开发同学定位bug。下面小编来分享一些平时常用到的iOS代码调试LLDB命令,希望能对做iOS测试的同学有所帮助。

2、指令格式

LLDB命令的各部分由空格分割,如果参数中包含空格,则需要使用双引号括起参数,如果参数中包含双引号或者反斜杠,则需要使用反斜杠进行转义。

 <command> [<subcommand> [<subcommand>...]] <action> [-options [option-
value]] [argument [argument...]]
  • command:命令
  • subcommand:子命令
  • action:命令操作
  • options:命令选项
  • argument:命令参数
  • 比如给test函数设置断点:breakpoint set -n test
    • breakpoint 是命令
    • set 是命令操作
    • -n 是命令选项
    • test 是命令参数

二、常用指令

1、help(帮助)

LLDB命令有非常多的功能,完全背下来不太容易,也没必要。开发者可以使用help命令查看相关命令的用法,甚至可以查看help命令的用法。

所有命令可以通过help <command> [<subcommand>]查看详细文档。比如help breakpointhelp breakpoint set

(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) 

2、p和po(打印)

p 命令: print 命令的简写,使用p 命令可以查看基本数据类型的值;如果使用p命令查看的是对象,那么只会返回对象的指针地址。 p命令后面除了可以接变量、常量,还可以接表达式。

po 命令:print object的缩写,可以理解为打印对象。功能与p命令类似,也可以打印常量、变量,打印表达式返回的对象等。p 和 po 的区别在于使用 po 只会输出对应的值,而 p 则会返回值的类型以及命令结果的引用名。

图1:p和po的使用.png

在输出结果中有类似于0,1这样的符号,它是指向对象的一个引用,在控制面板中可以直接使用这个符号来操作对应的对象,它们存在于LLDB的全名空间中,目的是为了辅助调试。$后面的数值是递增的,每打印一个与对象相关的命令,这个值都会加1。打印对象除了以上命令外,也可以在控制台左侧区域,点击变量右键点击 Print Deion of “xxx”,或者选中变量后,点击下边栏的i按钮,即可在控制台区看到打印结果。

另外,开发者可以按照print/的语法为print命令指定打印格式:

p    //!< 默认打印十进制
p/x  //!< 以十六进制打印整数 
p/d  //!< 以带符号的十进制打印整数 
p/u  //!< 以无符号的十进制打印整数 
p/o  //!< 以八进制打印整数 
p/t  //!< 以二进制打印整数 
p/a  //!< 以十六进制打印地址 
p/c  //!< 打印字符常量 
p/f  //!< 打印浮点数 
p/s  //!< 打印字符串 
p/r  //!< 格式化打印 

3、expression(执行表达式)

expression命令是调试过程中最有价值有命令了,既可以打印值也可以修改值,简写为expr或者e。它能够在调试时,动态的修改变量的值,同时打印出结果,在调试想要让应用执行异常路径(如执行某else情况)或者修改某些中间变量值如(如修改返回状态码以查看客户端相关响应等)非常有用,可以创造各种实际中难以遇到的测试环境辅助测试。用法举例如下图:

图2:expression的使用.png

expression命令是动态修改变量的值,Xcode还支持动态调用函数。在控制台执行call命令,可以在不修改代码,不重新编译的情况下,在断点调用某个方法,并输出此方法的返回值。

expression、expression --和指令print、p、call的效果一样
expression -O --和指令po的效果一样

4、thread和frame(线程和栈帧)

在进程停止后,LLDB会选择一个当前线程和线程中当前帧(frame)。很多检测状态的命令可以用于这个线程或帧。

1)thread list (列出所有线程)

用于列出所有线程,如下所示,其中星号(*)表示thread#1为当前线程。

(lldb) thread list
Process 18671 stopped
* thread #1: tid = 0x5fe79, 0x000000010a4f3e28 LLDBTest`-[ViewController viewDidLoad](self=0x00007fb21c1091f0, _cmd="viewDidLoad") at ViewController.m:21:18, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
  thread #2: tid = 0x5ff6c, 0x00007fff5e73c458 libsystem_pthread.dylib`start_wqthread
  thread #3: tid = 0x5ff6d, 0x00007fff5e70253e libsystem_kernel.dylib`__workq_kernreturn + 10
  thread #4: tid = 0x5ff6e, 0x00007fff5e73c458 libsystem_pthread.dylib`start_wqthread
  thread #5: tid = 0x5ff6f, 0x00007fff5e73c458 libsystem_pthread.dylib`start_wqthread
  thread #6: tid = 0x5ff70, 0x00007fff5e700e7e libsystem_kernel.dylib`mach_msg_trap + 10, name = 'com.apple.uikit.eventfetch-thread'
  thread #7: tid = 0x5ff71, 0x00007fff5e70253e libsystem_kernel.dylib`__workq_kernreturn + 10
  thread #8: tid = 0x5ff72, 0x00007fff5e73c458 libsystem_pthread.dylib`start_wqthread
  thread #9: tid = 0x5ff74, 0x00007fff5e70253e libsystem_kernel.dylib`__workq_kernreturn + 10
(lldb) 
2)thread backtrace(堆栈打印,简写bt)

bt命令可以打印出线程的堆栈信息,bt命令是打印当前线程的堆栈信息,如下图所示。该信息比左侧的Debug Navigator 看到的还要详细一些。如果嫌堆栈打印太长,可以加一个值限制,如bt 10bt all 命令可以打印所有线程的堆栈信息。

图3:bt指令.png
3)thread return(跳出当前方法的执行)

thread return主要用于控制程序流程,如想要直接跳过执行某个方法,或者直接让某方法返回一个想要的值,thread return后有一个可选参数,在执行时它会把可选参数加载进返回寄存器里,然后立刻执行返回命令,跳出当前栈帧(如:numberOfSectionsInTableView:方法直接thread return 10,就可以直接跳过方法执行,返回10)。

4)frame variable(查看帧变量)

frame variable命令显示当前帧的变量,默认输出当前帧所有参数和所有局部变量,如下所示:

(lldb) frame variable
(ViewController *) self = 0x00007fd09f405ed0
(SEL) _cmd = "viewDidLoad"
(__NSArrayI *) temArr = 0x0000600002094ff0 @"3 elements"
(lldb) 
5)frame variable(流程控制)
  • 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:流程控制.png

5、breakpoint(断点)

1)breakpoint set(设置断点,可以简写为br
  • breakpoint set -a 函数地址
  • breakpoint set -n 函数符号
(lldb) breakpoint set -a 0x000000010189bde7
Breakpoint 3: where = LLDBTest`-[ViewController test] + 23 at ViewController.m:26:5, address = 0x000000010189bde7
(lldb) breakpoint set -n "-[ViewController viewDidLoad]"
Breakpoint 4: where = LLDBTest`-[ViewController viewDidLoad] + 30 at ViewController.m:17:5, address = 0x000000010189bcfe
(lldb)  breakpoint set -n touchesBegan:withEvent:
Breakpoint 5: 97 locations.
(lldb) 
2)breakpoint list(列出所有的断点)

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

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

  1.1: where = LLDBTest`-[ViewController viewDidLoad] + 152 at ViewController.m:21:18, address = 0x000000010189bd78, resolved, hit count = 1 

3: address = LLDBTest[0x0000000100001de7], locations = 1, resolved = 1, hit count = 0
  3.1: where = LLDBTest`-[ViewController test] + 23 at ViewController.m:26:5, address = 0x000000010189bde7, resolved, hit count = 0 

4: name = '-[ViewController viewDidLoad]', locations = 1, resolved = 1, hit count = 0
  4.1: where = LLDBTest`-[ViewController viewDidLoad] + 30 at ViewController.m:17:5, address = 0x000000010189bcfe, resolved, hit count = 0 

6: file = '/Users/zhangjh48/Desktop/LLDBTest/LLDBTest/ViewController.m', line = 30, exact_match = 0, locations = 1, resolved = 1, hit count = 1

  6.1: where = LLDBTest`-[ViewController touchesBegan:withEvent:] + 70 at ViewController.m:30:6, address = 0x000000010189be46, resolved, hit count = 1 

(lldb) 
3)设置断点状态
  • breakpoint disable 断点编号: 禁用断点
  • breakpoint enable 断点编号: 启用断点
  • breakpoint delete 断点编号: 删除断点
(lldb) breakpoint disable 1
1 breakpoints disabled.
(lldb) breakpoint enable 1
1 breakpoints enabled.
(lldb) breakpoint delete 3
1 breakpoints deleted; 0 breakpoint locations disabled.
(lldb) 
4)断点命令
  • breakpoint command add 断点编号: 给断点预先设置需要执行的命令,当触发断点时候,就会执行
(lldb) breakpoint set -n "-[ViewController test]"
Breakpoint 3: where = LLDBTest`-[ViewController test] + 23 at ViewController.m:26:5, address = 0x0000000100cf8de7
(lldb) breakpoint command add 3
Enter your debugger command(s).  Type 'DONE' to end.
> print "断点命中了"
> print "断点参数为"
> po self
> DONE
2021-03-31 08:54:15.029736+0800 LLDBTest[2178:59045] (
    "\U5317\U4eac",
    "\U4e0a\U6d77",
    "\U5e7f\U5dde"
)
 print "断点命中了"
(const char [16]) $0 = "断点命中了"

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

 po self
<ViewController: 0x7fe7f3107810>


(lldb) 
  • breakpoint command list 断点编号: 列出断点的绑定的命令
  • breakpoint command delete 断点编号: 删除断点绑定的命令
(lldb) breakpoint command list 3
Breakpoint 3:
    Breakpoint commands:
      print "断点命中了"
      print "断点参数为"
      po self
(lldb) breakpoint command delete 3
(lldb) breakpoint command list 3
Breakpoint 3 does not have an associated command.
(lldb) 

6、watchpoint(内存断点)

相比较于breakpoint是对方法生效的断点,watchpoint则是对地址生效的断点。watchpoint类似于KVO的工作原理,当观察到属性地址里面的东西改变时,就让程序中断。

  • watchpoint set variable 变量
图5:内存断点.png
  • watchpoint set expression 地址
  • watchpoint list
  • watchpoint diable 断点编号
  • watchpoint enable 断点编号
  • watchpoint delete 断点编号
  • watchpoint command add 断点编号
  • watchpoint command list 断点编号
  • watchpoint command delete 断点编号

7、image(镜像寻址)

image命令主要用于寻址,一般是通过各种组合命令实现不同功能。

1)image list(列出所有模块信息)

image list -o -f 打印出模块的偏移地址、全路径。可以使用grep过滤

(lldb) image list
[  0] D87C3A17-73C3-3DFE-8A60-B423A0E92269 0x00000001059bf000 /Users/zhangjh48/Library/Developer/Xcode/DerivedData/LLDBTest-fkdakpzatewwmvdojsurqlcdeomb/Build/Products/Debug-iphonesimulator/LLDBTest.app/LLDBTest 
[  1] DEA51514-B4E8-3368-979B-89D0F8397ABC 0x000000010b7ba000 /usr/lib/dyld 

(lldb) image list -o -f
[  0] 0x00000000059bf000 /Users/zhangjh48/Library/Developer/Xcode/DerivedData/LLDBTest-fkdakpzatewwmvdojsurqlcdeomb/Build/Products/Debug-iphonesimulator/LLDBTest.app/LLDBTest
[  1] 0x000000010b7ba000 /usr/lib/dyld

(lldb) image list -o -f | grep LLDBTest
[  0] 0x00000000059bf000 /Users/zhangjh48/Library/Developer/Xcode/DerivedData/LLDBTest-fkdakpzatewwmvdojsurqlcdeomb/Build/Products/Debug-iphonesimulator/LLDBTest.app/LLDBTest
(lldb) 

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

2)image lookup(查找模块)
  • image lookup -t 类型:查找某个类型的信息
  • image lookup -a 地址:根据内存地址查找在模块中的位置
  • image lookup -n 符号或函数名:查找某个符号或者函数的位置
(lldb) image lookup -t AppDelegate
Best match found in /Users/zhangjh48/Library/Developer/Xcode/DerivedData/LLDBTest-fkdakpzatewwmvdojsurqlcdeomb/Build/Products/Debug-iphonesimulator/LLDBTest.app/LLDBTest:
id = {0x200000033}, name = "AppDelegate", byte-size = 8, decl = AppDelegate.h:10, compiler_type = "@interface AppDelegate : UIResponder
@end"

(lldb) image lookup -n "-[ViewController test]"
1 match found in /Users/zhangjh48/Library/Developer/Xcode/DerivedData/LLDBTest-fkdakpzatewwmvdojsurqlcdeomb/Build/Products/Debug-iphonesimulator/LLDBTest.app/LLDBTest:
        Address: LLDBTest[0x0000000100001df0] (LLDBTest.__TEXT.__text + 144)
        Summary: LLDBTest`-[ViewController test] at ViewController.m:25
(lldb) image lookup -a 0x0000000100a68e07
      Address: LLDBTest[0x0000000100001e07] (LLDBTest.__TEXT.__text + 167)
      Summary: LLDBTest`-[ViewController test] + 23 at ViewController.m:26:5
(lldb) 

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

8、register(寄存器)

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

1)register read (读取所有寄存器信息)

注意:真机调试和模拟器调试,打印的值可能不一样,因为他们寄存器不一样

(lldb) register read
General Purpose Registers:
        x0 = 0x0000000105109d20
        x1 = 0x0000000104bbe5f0  "testWithA:b:"
        x2 = 0x0000000000000003
        x3 = 0x0000000000000004
        x4 = 0x000000016b2459c8
        x5 = 0x0000000000000040
        x6 = 0x0000000000000044
        x7 = 0x0000000000000800
        x8 = 0x0000000104bc52b8  "testWithA:b:"
        x9 = 0x0000000000000303
       x10 = 0x000000010681ddb0
       x11 = 0x000000000007b700
       x12 = 0x000000000000003b
       x13 = 0x0000000000000000
       x14 = 0x0000000000000000
       x15 = 0xffffffffffffffff
       x16 = 0x0000000104bc52f2  (void *)0xf3f00000000104bc
       x17 = 0x0000000104bbdd18  LLDBTest`-[ViewController testWithA:b:] at ViewController.m:27
       x18 = 0x0000000000000000
       x19 = 0x0000000105109d20
       x20 = 0x0000000000000000
       x21 = 0x00000001e5f70000  (void *)0x00000001d5623100
       x22 = 0x00000001d1d341fc  
       x23 = 0x0000000000000001
       x24 = 0x0000000000000001
       x25 = 0x00000001dbd44000  UIKitCore`_UIPreviewInteractionController._currentCommitTransition
       x26 = 0x000000028301ff80
       x27 = 0x00000001e1d384a8  CoreFoundation`__NSArray0__struct
       x28 = 0x00000001d24cdffc  
        fp = 0x000000016b245b30
        lr = 0x0000000104bbdcfc  LLDBTest`-[ViewController viewDidLoad] + 192 at ViewController.m:24:5
        sp = 0x000000016b245b00
        pc = 0x0000000104bbdd34  LLDBTest`-[ViewController testWithA:b:] + 28 at ViewController.m:28:43
      cpsr = 0x40000000

(lldb) 
2)register read/格式 寄存器名称
  • po $x0:打印方法调用者
  • x/s $x1:打印方法名
  • po $x2:打印参数(以此类推,x3、x4也可能是参数)
  • 如果是非arm64,寄存器就是r0、r1、r2
图6:打印寄存器的值.png
3)register write 寄存器名称 数值

如下,修改寄存器$x3的值为5

(lldb) register read $x3
      x3 = 0x0000000000000004
(lldb) register write $x3 5
(lldb) register read $x3
      x3 = 0x0000000000000005
(lldb) po $x3
5

(lldb) 

9、缩写

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

更多命令可参考:https://lldb.llvm.org/use/map.html



参考链接:
https://www.dllhook.com/post/51.html#toc_11
https://blog.bombox.org/2019-08-06/ios-lldb/
https://www.sohu.com/a/301595611_216613
https://zhuanlan.51cto.com/art/201904/595593.htm

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容

  • 较常用的命令都会带 ⭐️ ⭐️ ⭐️ 执行命令 断点命令 watchpoint 命令 查看变量 执行表达式 检查线...
    YxxxHao阅读 9,554评论 0 3
  • lldb的Xcode默认的调试器,它与LLVM编译器一起,带给我们更丰富的流程控制和数据检测的调试功能。平时用Xc...
    大王叫我来巡山_Cong阅读 4,323评论 1 3
  • LLDB全称Low Level Debugger,是内置于Xcode中的动态调试工具,是我们开发中必不可少的工具。...
    正_文阅读 980评论 0 2
  • 前言 LLDB是个开源的内置于XCode的具有REPL(read-eval-print-loop)特征的Debug...
    Noskthing阅读 18,456评论 10 89
  • 今天感恩节哎,感谢一直在我身边的亲朋好友。感恩相遇!感恩不离不弃。 中午开了第一次的党会,身份的转变要...
    迷月闪星情阅读 10,559评论 0 11