iOS 常用的lldb命令

lldb.jpg

一、expression

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

expression <cmd-options> -- <expr>

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

  • 执行某个表达式。
    我们在代码运行过程中,可以通过执行某个表达式来动态改变程序运行的轨迹。
    假如我们在运行过程中,突然想把self.view颜色改成红色,看看效果。我们不必写下代码,重新run,只需暂停程序,用expression改变颜色,再刷新一下界面,就能看到效果
// 改变颜色
  (lldb) expression -- self.view.backgroundColor = [UIColor redColor]
  // 刷新界面
  (lldb) expression -- (void)[CATransaction flush]
  • 将返回值输出。
    也就是说我们可以通过expression来打印东西。
    假如我们想打印self.view:
(lldb) expression -- self.view
    (UIView *) $1 = 0x00007fe322c18a10

二、p & print & call

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

(lldb) expression -- self.view
(UIView *) $5 = 0x00007fb2a40344a0
(lldb) p self.view
(UIView *) $6 = 0x00007fb2a40344a0
(lldb) print self.view
(UIView *) $7 = 0x00007fb2a40344a0
(lldb) call self.view
(UIView *) $8 = 0x00007fb2a40344a0
(lldb) e self.view
(UIView *) $9 = 0x00007fb2a40344a0

三、po

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

(lldb) expression -- self.view
(UIView *) $13 = 0x00007fb2a40344a0
(lldb) expression -O -- self.view
<UIView: 0x7fb2a40344a0; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7fb2a4018c80>>
(lldb) po self.view
<UIView: 0x7fb2a40344a0; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7fb2a4018c80>>

四、thread(thread backtrace & bt)

当发生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

五、thread return

Debug的时候,也许会因为各种原因,我们不想让代码执行某个方法,或者要直接返回一个想要的值。这时候就该thread return上场了。
e.g: 我们有一个someMethod方法,默认情况下是返回YES。我们想要让他返回NO

1122433-cf22e45902233a0c.png.jpeg
(lldb) thread return NO

六、c & n & s & finish

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

1122433-17ba978ac411af3d.png.jpeg

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

七、frame variable

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

1122433-4dce2f2ba17e3857.png.jpeg

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

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

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

八、breakpoint

九、watchpoint

breakpoint有一个孪生兄弟watchpoint。如果说breakpoint是对方法生效的断点,watchpoint就是对地址生效的断点

如果我们想要知道某个属性什么时候被篡改了,我们该怎么办呢?有人可能会说对setter方法打个断点不就行了么?但是如果更改的时候没调用setter方法呢?
这时候最好的办法就是用watchpoint。我们可以用他观察这个属性的地址。如果地址里面的东西改变了,就让程序中断

  • watchpoint set variable
    一般情况下,要观察变量或者属性,使用watchpoint set variable命令即可
    e.g: 观察self->_string
(lldb) watchpoint set variable self->_string
Watchpoint created: Watchpoint 1: addr = 0x7fcf3959c418 size = 8 state = enabled type = w
    watchpoint spec = 'self->_string'
    new value: 0x0000000000000000
  • watchpoint set expression
    如果我们想直接观察某个地址,可以使用watchpoint set expression
    e.g: 我们先拿到_model的地址,然后对地址设置一个watchpoint
(lldb) p &_model
(Modek **) $3 = 0x00007fe0dbf23280
(lldb) watchpoint set expression 0x00007fe0dbf23280
Watchpoint created: Watchpoint 1: addr = 0x7fe0dbf23280 size = 8 state = enabled type = w
    new value: 0

十、target

对于target这个命令,我们用得最多的可能就是target modules lookup。由于LLDB给target modules取了个别名image,所以这个命令我们又可以写成image lookup。

  • image lookup --address
    当我们有一个地址,想查找这个地址具体对应的文件位置,可以使用image lookup --address,简写为image lookup -a
    e.g: 当我们发生一个crash
2015-12-17 14:51:06.301 TLLDB[25086:246169] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArray0 objectAtIndex:]: index 1 beyond bounds for empty NSArray'
*** First throw call stack:
(
    0   CoreFoundation                      0x000000010accde65 __exceptionPreprocess + 165
    1   libobjc.A.dylib                     0x000000010a746deb objc_exception_throw + 48
    2   CoreFoundation                      0x000000010ac7c395 -[__NSArray0 objectAtIndex:] + 101
    3   TLLDB                               0x000000010a1c3e36 -[ViewController viewDidLoad] + 86
    4   UIKit                               0x000000010b210f98 -[UIViewController loadViewIfRequired] + 1198
    5   UIKit                               0x000000010b2112e7 -[UIViewController view] + 27

我们可以看到是由于-[__NSArray0 objectAtIndex:]:超出边界而导致的crash,但是objectAtIndex:的代码到底在哪儿呢?

(lldb) image lookup -a 0x000000010a1c3e36
      Address: TLLDB[0x0000000100000e36] (TLLDB.__TEXT.__text + 246)
      Summary: TLLDB`-[ViewController viewDidLoad] + 86 at ViewController.m:32

根据0x000000010a1c3e36 -[ViewController viewDidLoad]里面的地址,使用image lookup --address查找,我们可以看到代码位置在ViewController.m里面的32行

  • image lookup --name
    当我们想查找一个方法或者符号的信息,比如所在文件位置等。我们可以使用image lookup --name,简写为image lookup -n。
(lldb) image lookup -n dictionaryWithXMLString:
2 matches found in /Users/jiangliancheng/Library/Developer/Xcode/DerivedData/VideoIphone-aivsnqmlwjhxapdlvmdmrubbdxpq/Build/Products/Debug-iphoneos/BaiduIphoneVideo.app/BaiduIphoneVideo:
        Address: BaiduIphoneVideo[0x00533a7c] (BaiduIphoneVideo.__TEXT.__text + 5414908)
        Summary: BaiduIphoneVideo`+[NSDictionary(SAPIXmlDictionary) dictionaryWithXMLString:] at XmlDictionary.m
         Module: file = "/Users/jiangliancheng/Library/Developer/Xcode/DerivedData/VideoIphone-aivsnqmlwjhxapdlvmdmrubbdxpq/Build/Products/Debug-iphoneos/BaiduIphoneVideo.app/BaiduIphoneVideo", arch = "armv7"
    CompileUnit: id = {0x00000000}, file = "/Users/jiangliancheng/Development/Work/iOS_ShareLib/SharedLib/Srvcs/BDPassport4iOS/BDPassport4iOS/SAPI/Extensive/ThirdParty/XMLDictionary/XmlDictionary.m", language = "Objective-C"
       Function: id = {0x23500000756}, name = "+[NSDictionary(SAPIXmlDictionary) dictionaryWithXMLString:]", range = [0x005a6a7c-0x005a6b02)
       FuncType: id = {0x23500000756}, decl = XmlDictionary.m:189, clang_type = "NSDictionary *(NSString *)"
         Blocks: id = {0x23500000756}, range = [0x005a6a7c-0x005a6b02)
      LineEntry: [0x005a6a7c-0x005a6a98): /Users/jiangliancheng/Development/Work/iOS_ShareLib/SharedLib/Srvcs/BDPassport4iOS/BDPassport4iOS/SAPI/Extensive/ThirdParty/XMLDictionary/XmlDictionary.m
         Symbol: id = {0x0000f2d5}, range = [0x005a6a7c-0x005a6b04), name="+[NSDictionary(SAPIXmlDictionary) dictionaryWithXMLString:]"
       Variable: id = {0x23500000771}, name = "self", type = "Class", location =  [sp+32], decl = 
       Variable: id = {0x2350000077e}, name = "_cmd", type = "SEL", location =  [sp+28], decl = 
       Variable: id = {0x2350000078b}, name = "string", type = "NSString *", location =  [sp+24], decl = XmlDictionary.m:189
       Variable: id = {0x23500000799}, name = "data", type = "NSData *", location =  [sp+20], decl = XmlDictionary.m:192
        Address: BaiduIphoneVideo[0x012ee160] (BaiduIphoneVideo.__TEXT.__text + 19810016)
        Summary: BaiduIphoneVideo`+[NSDictionary(XMLDictionary) dictionaryWithXMLString:] at XMLDictionary.m
         Module: file = "/Users/jiangliancheng/Library/Developer/Xcode/DerivedData/VideoIphone-aivsnqmlwjhxapdlvmdmrubbdxpq/Build/Products/Debug-iphoneos/BaiduIphoneVideo.app/BaiduIphoneVideo", arch = "armv7"
    CompileUnit: id = {0x00000000}, file = "/Users/wingle/Workspace/qqlive4iphone/iphone_4.0_fabu_20150601/Common_Proj/mobileTAD/VIDEO/Library/Third Party/XMLDictionary/XMLDictionary.m", language = "Objective-C"
       Function: id = {0x79900000b02}, name = "+[NSDictionary(XMLDictionary) dictionaryWithXMLString:]", range = [0x01361160-0x0136119a)
       FuncType: id = {0x79900000b02}, decl = XMLDictionary.m:325, clang_type = "NSDictionary *(NSString *)"
         Blocks: id = {0x79900000b02}, range = [0x01361160-0x0136119a)
      LineEntry: [0x01361160-0x01361164): /Users/wingle/Workspace/qqlive4iphone/iphone_4.0_fabu_20150601/Common_Proj/mobileTAD/VIDEO/Library/Third Party/XMLDictionary/XMLDictionary.m
         Symbol: id = {0x0003a1e9}, range = [0x01361160-0x0136119c), name="+[NSDictionary(XMLDictionary) dictionaryWithXMLString:]"

东西有点多,我们只需关注里面的file这一行:

CompileUnit: id = {0x00000000}, file = "/Users/jiangliancheng/Development/Work/iOS_ShareLib/SharedLib/Srvcs/BDPassport4iOS/BDPassport4iOS/SAPI/Extensive/ThirdParty/XMLDictionary/XmlDictionary.m", language = "Objective-C"
CompileUnit: id = {0x00000000}, file = "/Users/wingle/Workspace/qqlive4iphone/iphone_4.0_fabu_20150601/Common_Proj/mobileTAD/VIDEO/Library/Third Party/XMLDictionary/XMLDictionary.m", language = "Objective-C"

可以清晰的看到,LLDB给我们找出来了这个方法的位置。
当然这个命令也可以找到方法的其他相关信息,比如参数等.

  • image lookup --type
    当我们想查看一个类型的时候,可以使用image lookup --type,简写为image lookup -t:
    e.g: 我们来看看Model的类型:
(lldb) image lookup -t Model
Best match found in /Users/jiangliancheng/Library/Developer/Xcode/DerivedData/TLLDB-beqoowskwzbttrejseahdoaivpgq/Build/Products/Debug-iphonesimulator/TLLDB.app/TLLDB:
id = {0x30000002f}, name = "Model", byte-size = 32, decl = Modek.h:11, clang_type = "@interface Model : NSObject{
    NSString * _bb;
    NSString * _cc;
    NSString * _name;
}
@property ( getter = name,setter = setName:,readwrite,nonatomic ) NSString * name;
@end
"

可以看到,LLDB把Model这个class的所有属性和成员变量都打印了出来,当我们想了解某个类的时候,直接使用image lookup -t即可

十一、Extension

给UIApplicationMain设置一个断点,在断点中添加执行e @import UIKit。

1122433-3f9b172265d0137b.jpg
(lldb) p self.view.frame
error: property 'frame' not found on object of type 'UIView *'
error: 1 errors parsing expression
(lldb) e @import UIKit(不需要写)
(lldb) p self.view.frame
(CGRect) $0 = (origin = (x = 0, y = 0), size = (width = 375, height = 667))

常用的DeBug快捷键

快捷键.png

推荐阅读更多精彩内容

  • LLDB的Xcode默认的调试器,它与LLVM编译器一起,带给我们更丰富的流程控制和数据检测的调试功能。平时用Xc...
    CoderSC阅读 207评论 0 0
  • ps:本文为以前学些是学习所记笔记,原文链接已经丢失,在此发表为以后查阅方便,如有侵权请作者联系我,会马上删除。 ...
    哎中文不艾汉语阅读 780评论 0 1
  • LLDB的Xcode默认的调试器,它与LLVM编译器一起,带给我们更丰富的流程控制和数据检测的调试功能。平时用Xc...
    小笨狼阅读 14,195评论 33 181
  • [转]浅谈LLDB调试器文章来源于:http://www.cocoachina.com/ios/20150126/...
    loveobjc阅读 1,380评论 2 6
  • 宋燕珠阅读 18评论 0 0