浅入浅出LLDB(1)

这周开始好好钻研一下LLDB相关的知识,这是一系列的文章。有些初级知识可能大家都有所涉猎,嘿嘿,懂得自然懂,看我的博客,什么时候会收获小。

基础语法

1.help 有啥不会,就直接输入help,你会得到如下的一系列信息,

command           -- A set of commands for managing or customizing the
                   debugger commands.
disassemble       -- Disassemble bytes in the current function, or elsewhere
                   in the executable program as specified by the user.

2.print 输出变量值
比如,对于如下的程序语句 let haha = 5; 你只要在LLDB里面输入 print haha 就可以得到如下输出结果。

(lldb) p haha
(Int) $R0 = 5

请注意,在与调试器共舞- LLDB 的华尔兹一文中曾经提出会输出类似于 $0 = 5,并指出$0是当前的输出值。这个说话其实是不严谨的,正确的说法是,应该是当前的haha的值5存在了R0寄存器里。

比如,当我们构建如下的程序语句时,

let haha = 5;
let object = "jkjksdjf";

我们在分别print haha以及 print object就会分别得到(Int) $R0 = 5(String) $R1 = "jkjksdjf"

这说明haha的值和object的值分别以int型和string型存在了R0和R1寄存器之中。

当然,图快速的话,可以像我上面一样将print简写成p。

3.po
输出变量值

哎,有人奇怪了,po也是输出变量值,那和p有啥区别啊?本质上没啥区别,如果真要说,就是po = e -O –,具体我们后续再说啦。

4.breakpoubt l 输出所有的断点,可以得到如下的结果

Current breakpoints:
1: file = 'xxx/ViewController.swift', line = 19, locations = 1, resolved = 1, hit count = 1

1.1: where = xxx.ViewController.viewDidLoad (xxx.ViewController)() -> () + 131 at ViewController.swift:19, address = 0x0000000108d0a443, resolved, hit count = 1 

其中 1: file的这个1就是ID号。
那这里的1.1是什么鬼?嘿嘿,当里使用Symbolic Breakpoint的时候,你一个断点很有可能截获了多个地方,比如AViewController和BViewController的viewDidLoad都被加上了断点,这个时候就需要靠诸如1.1和1.2之类的细分ID来进行区别了。

当然,有人会问,输出这个断点有什么用啊。嘿嘿,当你使用Xcode Symbolic Breakpoint的时候,你就会发现究竟在多少个地方下了断点了。

同样的,你可以将breakpoint简写成br。

5.br delete ID 这里的ID就是之前的断点的ID号
通过这个命令,可以删除ID对应的断点

6.br e ID 启用一个ID号对应的断点

7.br di ID 禁用一个ID号对应的断点

8.b xxx.swift:lineY 在xxx文件的第lineY行设置一个断点
b ViewController.swift:10 就是在ViewController的第10号下了一个断点。

需要注意的是,通过b命令设置的断点,无法直观的在Xcode界面上显示出来,而br delete删除一个断点可以直接在Xcode上看出效果。

9.br set -n functionName 对functionName设置Symbolic Breakpoint
br set -n viewDidLoad 就是对所有的viewDidLoad设置了Symboloc Breakpoint

10.br mod -C "Condition" ID 对ID号对应的breakpoint添加条件触发
假设我们有下面这样的一段代码

1. //ViewController.swift
2. for var value in money {
3.     totalValue += value
4. }

我们首先先使用b ViewController.swift:3设置一个断点,然后使用br l查询到对应的ID为3。
然后我们使用br mod -C "totalValue > 50" 3对这个断点设置条件触发,条件为当totalValue 大于50时候才触发

当然,可能有些人会问,如果我不想删除断点,只是想移除条件触发怎么办?很简单,只要输入br mod -C "" ID,将其中的Condition部分设置为空即可。

11.continue 继续运行程序
12.n step over单步调试
13.s step in进行函数
14.finish step out退出函数

大杀器

上面的命令是不是很多,一个个敲实在是太麻烦,那如果我想对一个断点执行多条语句怎么办?
嘿嘿,大杀器来了。

br com add ID 对ID对应的断点进入交互式指定。如:

br com add 2
> bt
> continue
> DONE

上面的语句指的是,对2号断点进行交互式指定,当这个断点触发的时候,首先执行bt(具体bt命令的意思我们后续再说,粗略理解就是backtrace输出调用栈,可以简单看下面的例子),然后执行continue,最后通过关键字Done退出指定,这里的Done类似于shell里面的exit。

frame #0: xxx`xxx.ViewController.viewDidLoad (self=0x00007faf52439c20)() -> () + 470 at ViewController.swift:27
frame #1: xxx`@objc xxx.ViewController.viewDidLoad (xxx.ViewController)() -> () + 34 at ViewController.swift:0
frame #2: UIKit`-[UIViewController loadViewIfRequired] + 1198
frame #3: UIKit`-[UIViewController view] + 27
frame #4: UIKit`-[UIWindow addRootViewControllerViewIfPossible] + 61
frame #5: UIKit`-[UIWindow _setHidden:forced:] + 282
frame #6: UIKit`-[UIWindow makeKeyAndVisible] + 42
frame #7: UIKit`-[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 4131
frame #8: UIKit`-[UIApplication _runWithMainScene:transitionContext:completion:] + 1760
frame #9: UIKit`-[UIApplication workspaceDidEndTransaction:] + 188
frame #10: FrontBoardServices`-[FBSSerialQueue _performNext] + 192
frame #11: FrontBoardServices`-[FBSSerialQueue _performNextFromRunLoopSource] + 45
frame #12: CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
frame #13: CoreFoundation`__CFRunLoopDoSources0 + 556
frame #14: CoreFoundation`__CFRunLoopRun + 867
frame #15: CoreFoundation`CFRunLoopRunSpecific + 488
frame #16: UIKit`-[UIApplication _run] + 402
frame #17: UIKit`UIApplicationMain + 171
frame #18: xxx`main + 109 at AppDelegate.swift:12
frame #19: libdyld.dylib`start + 1
frame #20: libdyld.dylib`start + 1