LLDB基础知识

来源:互联网 发布:sqlserver 截断字符串 编辑:程序博客网 时间:2024/05/28 05:16

LLDB是Xcode默认的调试器,它与LLVM编译器一起,带给我们更丰富的流程控制和数据检测的调试功能。Xcode运行程序,实际走的都是LLDB。


LLDB控制台


Xcode中内嵌了LLDB控制台,在Xcode中代码的下方,我们可以看到LLDB控制台。



LLDB控制台平时会输出一些log信息。如果我们想输入命令调试,必须让程序进入暂停状态。


让程序进入暂停状态的方式主要有2种:


1>断点或者watchpoint: 在代码中设置一个断点(watchpoint),当程序运行到断点位置的时候,会进入stop状态

2>直接暂停,控制台上方有一个暂停按钮,上图红框已标出,点击即可暂停程序


LLDB语法


在使用LLDB之前,我们来先看看LLDB的语法,了解语法可以帮助我们清晰的使用LLDB:


<command> [<subcommand> [<subcommand>...]] <action> [-options [option-value]] [argument [argument...]]


一眼看上去可能比较迷茫,给大家解释一下:


<command>(命令)和<subcommand>(子命令):LLDB调试命令的名称。命令和子命令按层级结构来排列:一个命令对象为跟随其的子命令对象创建一个上下文,子命令又为其子命令创建一个上下文,依此类推。


<action>:执行命令的操作

<options>:命令选项

<arguement>:命令的参数

[]:表示命令是可选的,可以有也可以没有


举个例子,假设我们给main方法设置一个断点,我们使用下面的命令:


breakpoint set -n main

这个命令对应到上面的语法就是:


command: breakpoint 表示断点命令

action: set 表示设置断点

option: -n 表示根据方法name设置断点

arguement: mian 表示方法名为mian


原始(raw)命令


LLDB支持不带命令选项(options)的原始(raw)命令,原始命令会将命令后面的所有东西当做参数(arguement)传递。不过很多原始命令也可以带命令选项,当你使用命令选项的时候,需要在命令选项后面加--区分命令选项和参数。


e.g: 常用的expression就是raw命令,一般情况下我们使用expression打印一个东西是这样的:

(lldb) expression count

(int) $2 = 4


当我们想打印一个对象的时候。需要使用-O命令选项,我们应该用--将命令选项和参数区分:


(lldb) expression -O -- self

<ViewController0x7f9000f17660>


唯一匹配原则


LLDB的命令遵循唯一匹配原则:假如根据前n个字母已经能唯一匹配到某个命令,则只写前n个字母等效于写下完整的命令。


e.g: 前面提到我设置断点的命令,我们可以使用唯一匹配原则简写,下面2条命令等效:


breakpoint set -n main

br s -n main



~/.lldbinit


LLDB有了一个启动时加载的文件~/.lldbinit,每次启动都会加载。所以一些初始化的事儿,我们可以写入~/.lldbinit中,比如给命令定义别名等。但是由于这时候程序还没有真正运行,也有部分操作无法在里面玩,比如设置断点。


LLDB命令


expression


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


expression <cmd-options> -- <expr>


  • <cmd-options>:命令选项,一般情况下使用默认的即可,不需要特别标明。

  • --: 命令选项结束符,表示所有的命令选项已经设置完毕,如果没有命令选项,--可以省略

  • <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


一般情况下,我们直接用expression还是用得比较少的,更多时候我们用的是p、print、call。这三个命令其实都是expression --的别名(--表示不再接受命令选项,详情见前面原始(raw)命令这一节)


  • print: 打印某个东西,可以是变量和表达式

  • p: 可以看做是print的简写

  • 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


根据唯一匹配原则,如果你没有自己添加特殊的命令别名。e也可以表示expression的意思。原始命令默认没有命令选项,所以e也能带给你同样的效果


po


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


(lldb) expression -- self.view

(UIView *) $13 = 0x00007fb2a40344a0

(lldb) expression -O -- self.view

<UIView0x7fb2a40344a0; frame = (0 0; 375 667); autoresize = W+H


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。


e.g: 刚刚遇到的真问题,某个第三方SDK用了一个我们项目里原有的第三方库,库里面对NSDictionary添加了category。也就是有2个class对NSDictionary添加了名字相同的category,项目中调用自己的category的地方实际走到了第三方SDK里面去了。最大的问题是,这2个同名category方法行为并不一致,导致出现bug


现在问题来了,怎么寻找到底是哪个第三方SDK?方法完全包在.a里面。


其实只需使用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:] atXmlDictionary.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


0 0
原创粉丝点击