LLDB调试器

#LLDB调试器
###简介

你是否曾经苦恼于理解你的代码,而去尝试打印一个变量的值?

```
NSLog(@"%@", password);
```

或者跳过一个函数调用来简化程序的行为?
实际应该调用这个函数:Foo()
```
NSNumber *n = @7; //complexCalculate() ;
```

或者伪造一个函数实现?

```
int complexCalculate {
return 9;
/*
万行代码.
...
}
```

并且每次必须重新编译,从头开始?

构建软件是复杂的,并且 Bug 总会出现。一个常见的修复周期就是修改代码,编译,重新运行,并且祈祷出现最好的结果。

但是不一定要这么做。你可以使用调试器。而且即使你已经知道如何使用调试器检查变量,它可以做的还有很多。

这篇文章将试图挑战你对调试的认知,并详细地解释一些你可能还不了解的基本原理,然后展示一系列有趣的例子。

###基础

如图所示,当我们在程序的301行打上断点的时候,触发这段代码,程序就会进入lldb的调试模式.运行我们和调试器进行交互.这时候我们可以做些什么呢?

###help

最简单命令是 help,它会列举出所有的命令。如果你忘记了一个命令是做什么的,或者想知道更多的话,你可以通过 help来了解更多细节,例如 help print 或者 help thread。如果你甚至忘记了 help 命令是做什么的,你可以试试 help help。不过你如果知道这么做,那就说明你大概还没有忘光这个命令。??

###po
使用"help po" 我们能简单的看到,官方对这个命令的简单描述.
- 在当前上下文能评估一个表达式(OC对象或者Swift),这个对象可以是用户自己定义的或者是在当前区域内有效的变量.

#####使用场景
1. 基本用法,打印OC对象.

2. po [searchBar recursiveDescription];

利用这个命令,我们可以清楚看到某个控件的内部结构....非常利于我们去修改和调整他们.(比如说tabbar,还有自定义控件,等等);
当然,现在我们也可以通过Xcode的层级化视图来看...这个只能说各有千秋吧.
3. po [UIViewController _printHierarchy]
苹果在IOS8也默默的添加了 [UIViewController _printHierarchy] 这个命令,利用它我们可以对viewController的结构一目了然。这对帮我们了解程序基本结构是非常有帮助的.

4. 如何控制po在打印对象的时候,所打印出来的信息格式呢?
打印对象,会调用对象description方法。是print-object的简写

#####常见小问题
1. 在po一个带宏的值的时候,由于LLDB调试器是不认识定义的宏的,这个时候,我们通常为宏取名的时候,key和value是相同的....给宏加上@""就可以正常使用了.![Alt text](./1447984218129.png)
这是时候,去lldb调试里面去打宏的时候,是还有智能提示的.但是他最终却不认识他...这个时候,我们就需要这样做.![Alt text](./1447984280359.png)

2. po其实是print object的简写...它有点奇怪的就是,拒绝打印结构体的样子..

```
// po对于返回值类型不确定的东西,将无法打印.
(lldb) po cell0.frame
error: property ‘frame‘ not found on object of type ‘IMYAccountCell *‘
error: 1 errors parsing expression
(lldb) po [cell0 frame]
error: Execution was interrupted, reason: EXC_BAD_ACCESS (code=EXC_I386_GPFLT).
The process has been returned to the state before expression evaluation.
(lldb) po (CGRect)[cell0 frame]
(origin = (x = 0, y = 249), size = (width = 375, height = 45))
(origin = (x = 0, y = 249), size = (width = 375, height = 45))
(lldb) po self.navigationController.navigationBar
error: property ‘navigationBar‘ not found on object of type ‘id‘
error: 1 errors parsing expression
// 有时候用点语法无效,尝试用[]来获取
(lldb) po [self.navigationController navigationBar]
<UINavigationBar: 0x7fbe627d9a20; frame = (0 20; 375 44); opaque = NO; autoresize = W; gestureRecognizers = <NSArray: 0x7fbe627d83e0>; layer = <CALayer: 0x7fbe627d7dd0>>
```

###expression
#####使用场景
1. 在开发中,我们经常会遇到这样一种情况:我们设置一个视图的背景颜色,运行后发现颜色不好看。嗯,好吧,在代码里面修改一下,再编译运行一下,嗯,还是不好看,然后再修改吧~~这样无形中浪费了我们大把的时间。在这种情况下,expression命令强大的功能就能体现出来了,它不仅会改变调试器中的值,还改变了程序中的实际值。我们先来看看实际效果,如下所示

但是只有程序继续运行之后才会看到界面的变化。因为改变的内容必须被发送到渲染服务中,然后显示才会被更新。
渲染服务实际上是一个另外的进程 (被称作 backboardd)。这就是说即使我们正在调试的内容所在的进程被打断了,backboardd 也还是继续运行着的。
这意味着你可以运行下面的命令,而不用继续运行程序:
```
(lldb) e (void)[CATransaction flush]
```

2. 这点就比较实用了.也许我们在程序运行时去改变视图的一些信息,用户并不是非常大,可是,如果是运行时去改变程序里面的一些变量值呢?个人觉得还是比较有用的.(脑洞越大,这个命令的功能就越大.)

###断点配合lldb

###流程控制命令
实际上使用xcode自带的可视化工具来控制“继续”“暂停”“下一步”“进入”“跳出”更简单,但这里还是列出其所对应的命令名:
继续:process continue, continue, c
下一步:thread step-over, next, n
进入:thread step-in, step, s
跳出:thread step-out, finish, f

###watchpoint
watchpoint可以在某个变量被写入/读取时暂停程序运行:

###命令补全(Command Completion)

LLDB支持源文件名,符号名,文件名,等等的命令补全(Commmand Completion)。终端窗口中的补全是通过在命令行中输入一个制表符来初始化的。Xcode控制台中的补全与在源码编辑器中的补全方式是一样的:补全会在第三个字符被键入时自动弹出,或者通过Esc键手动弹出。

###其他功能
1. 设置断点
2. 查看内存
3. 查看线程状态
4. 查看调用栈状态
5. 命令别名

###Python脚本

对于高级用户来说,LLDB有一个内置的Python解析器,可以通过脚本命令来访问。调试器中的所有特性在Python解析器中都可以作为类来访问。这样,我们就可以使用LLDB-Python库来写Python函数,并通过脚本将其加载到运行会话中,以执行一些更复杂的调试操作。

#Chisel-LLDB命令插件,让调试更Easy(江大大推荐)

###1.安装Chisel
源码地址: [Chisel](https://github.com/facebook/chisel)
Chisel 使用 homebrew 来安装,如果你没有安装homebrew, 参考 [homebrew](http://brew.sh/)。

```
brew update
brew install chisel
```
安装完成按照安装日志上的提示,在~/.lldbinit文件中添加一行,没有则新建。 提示类似如下:

```
Add the following line to ~/.lldbinit to load chisel when Xcode launches:
command script import /usr/local/opt/chisel/libexec/fblldb.py
```
做好上面的步骤,然后重启Xcode就可以尝试下了。

###2.内置命令
Chisel 为lldb提供了新增的便捷命令,是非常实用的命令

#####2.1 pviews
这个命令可以递归打印所有的view,并能标示层级,相当于 UIView 的私有辅助方法 [view recursiveDescription] 。 善用使用这个功能会让你在调试定位问题时省去很多麻烦。
使用示例:

```
(lldb) pviews view
<TestView: 0x18df8070; baseClass = UIControl; frame = (144 9; 126 167); layer = <CALayer: 0x18df8150>>
| <UIView: 0x18df81d0; frame = (0 0; 126 126); userInteractionEnabled = NO; layer = <CALayer: 0x18df8240>>
| <UIImageView: 0x18df8330; frame = (0 0; 126 126); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x18df83b0>>
| <UILabel: 0x18df8460; frame = (0 135; 126 14); text = ‘haha‘; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x18df7fb0>>
| | <_UILabelContentLayer: 0x131a3d50> (layer)
| <UILabel: 0x18df8670; frame = (0 155; 126 12); text = ‘hahaha‘; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x18df8730>>
| | <_UILabelContentLayer: 0x131bea10> (layer)
| <UIImageView: 0x18df88d0; frame = (0 9; 28 27); hidden = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x18df8ba0>>
```

#####2.2 pvc
这个命令也是递归打印层级,但是不是view,而是viewController。利用它我们可以对viewController的结构一目了然。 其实苹果在IOS8也默默的添加了 UIViewController 的一个私有辅助方法 [UIViewController _printHierarchy] 同样的效果。
预览效果:

```
<UITabBarController 0x7fa789429fc0>, state: appeared, view: <UILayoutContainerView 0x7fa78961b760>
| <SYYellowViewController 0x7fa78942a610>, state: appeared, view: <UIView 0x7fa789404570>
| <SYRedViewController 0x7fa78942c960>, state: disappeared, view: (view not loaded)
```
是不是方便很多呢,而且还可以看到 viewController 是否已经 viewDidLoad .

#####2.3 visualize
这是个很有意思的功能,它可以让你使用Mac的预览打开一个 UIImage, CGImageRef, UIView, 或 CALayer。 这个功能或许可以帮我们用来截图、用来定位一个view的具体内容。 但是在我试用了一下,发现暂时还是只能在模拟器时使用,真机还不行。
使用简单:

```
(lldb) visualize imageView
```

#####2.4 fv & fvc
fv 和 fvc 这两个命令是用来通过类名搜索当前内存中存在的view和viewController实例的命令,支持正则搜索。

```
(lldb) fv scrollView
0x18d3b8c0 UIScrollView
0x137d0c50 UIScrollView
0x131b1580 UIScrollView
0x131b2070 UIScrollView
(lldb) fvc Home
0x1393fe00 HomeFeedsViewController
0x138a8e00 HomeFeedsViewController
(lldb)
```
#####2.5 show & hide
这两个命令用来显示和隐藏一个指定的 UIView . 你甚至不需要Continue Progress. 就可以看到效果。

#####2.6 caflush
这个命令会重新渲染,即可以重新绘制界面, 相当于执行了 [CATransaction flush] 方法,要注意如果在动画过程中执行这个命令,就直接渲染出动画结束的效果。
当你想在调试界面颜色、坐标之类的时候,可以直接在控制台修改属性,然后caflush就可以看到效果啦,是不是要比改代码,然后重新build省事多了呢。
例, 其中 $122 即是目标UIView:

```
(lldb) p view
(long) $122 = 140718754142192
(lldb) e (void)[$122 setBackgroundColor:[UIColor greenColor]]
(lldb) caflush
```

#一些po不正常的情况(传法推荐)

1. 在使用Xcode6beta时(以下全部简称x6b),发现设置断点,中断后直接跳到汇编视图,这根本不是一般人需要的哦!其实这个是可以设定的哦,在断点断下之后,菜单中依次选择Debug->Debug Workflow后,将总是显示汇编行勾去掉即可:

2.

```
(lldb) po date
error: Couldn‘t materialize: couldn‘t get the value of variable date: no location, value may have been optimized out
Errored out in Execute, couldn‘t PrepareToExecuteJITExpression
```

备注:本例子结合loginBottomButtonAction方法进行讲解.

时间: 2024-10-11 23:12:12

LLDB调试器的相关文章

【转】浅谈LLDB调试器

随着Xcode 5的发布,LLDB调试器已经取代了GDB,成为了Xcode工程中默认的调试器.它与LLVM编译器一起,带给我们更丰富的流程控制和数据检测的调试功能.LLDB为Xcode提供了底层调试环境,其中包括内嵌在Xcode IDE中的位于调试区域的控制面板,在这里我们可以直接调用LLDB命令.如图1所示: 图1:位于Xcode调试区域的控制台 在本文中,我们主要整理一下LLDB调试器提供给我们的调试命令,更详细的内容可以查看The LLDB Debugger. LLDB命令结构 在使用LL

学习笔记之--认识Xcode中的重要成员:lldb调试器

之前对lldb调试器了解比较少,平时主要用来打印日志和暂定时用鼠标查看属性数据以及使用p po一些简单的命令语句. 今天看了一些关于lldb的文章,顿时觉得之前对它了解太少了,原来它还有那么多的功能. 好记性不如烂笔头,我把方便易用的命令记录下来,方便以后查看. 一.ldb的语法结构 lldb的语法结构如下:<command> [<subcommand> [<subcommand>...]] <action> [-options [option-value]

使用Python脚本强化LLDB调试器

LLDB是Xcode自带的调试器,作为一个iOS应用开发程序员,平时我在开发应用时会使用LLDB来调试代码.在逆向应用时,也会用到LLDB来跟踪应用的执行过程. LLDB还内置了一个Python解析器,使用Python脚本可以方便LLDB的调试,比如自动化执行一些命令,或者自动化处理数据之类的,具体说明可以参考官方的文档:LLDB Python Reference. 以下就以一个具体的例子来演示一个Python脚本的编写过程: 一.获取方法的偏移地址 运行系统自带的计算器Calculator.a

为什么Xcode调试模式下, lldb调试器打印任何对象都为空, 鼠标指向对变量时显示为nil?

如果之前有对工程打包发布过,那么一定改过这个位置: 如果是Release状态,那么lldb调试器无法打印变量信息,但NSLog却可以.此时将此选项改为"Debug"即可.

iOS LLDB调试器

随着Xcode 5的发布,LLDB调试器已经取代了GDB,成为了Xcode工程中默认的调试器.它与LLVM编译器一起,带给我们更丰富的流程控制和数据检测的调试功能.LLDB为Xcode提供了底层调试环境,其中包括内嵌在Xcode IDE中的位于调试区域的控制面板,在这里我们可以直接调用LLDB命令.如图1所示: 图1:位于Xcode调试区域的控制台 在本文中,我们主要整理一下LLDB调试器提供给我们的调试命令,更详细的内容可以查看The LLDB Debugger. LLDB命令结构 在使用LL

lldb调试器知多少

lldb调试器简介 lldb 是一个有着 REPL 的特性和 C++ .Python 插件的开源调试器.lldb调试器的由来是伴随着Xcode的版本升级而来. Xcode4.3之前使用的默认调试器是gdb, 到Xcode4.3之后便改成了lldb.gdb是UNIX及UNIX-like下的调试工具,是来自于GNU组织. 后被苹果进行优化,功能添加后,改名为lldb.可以说lldb是gdb的高版本. lldb调试器是一个可执行Mach-O文件,因为通常是和xcode集成在一起,会让人误以为是xcod

转:iOS LLDB调试器和断点调试

本文转自:http://www.cnblogs.com/wfwenchao/p/3991060.html?utm_source=tuicool&utm_medium=referral 技巧一:运行时修改变量的值 你以前怎么验证是不是某个变量的值导致整段程序不能正常工作?修改代码中的变量的值,然后cmd+r重新启动app?现在你不需要这么做了,只需要设置一个断点,当程序在这进入调试模式后,使用expr命令即可在运行时修改变量的值. 假如有一个loginWithUsername:方法,需要两个参数:

iOS LLDB调试器和断点调试

技巧一:运行时修改变量的值 你以前怎么验证是不是某个变量的值导致整段程序不能正常工作?修改代码中的变量的值,然后cmd+r重新启动app?现在你不需要这么做了,只需要设置一个断点,当程序在这进入调试模式后,使用expr命令即可在运行时修改变量的值. 假如有一个loginWithUsername:方法,需要两个参数:username,password. 首先设置好断点,如下图所示: 运行app,进入断点模式后,在(lldb)后输入 ? 1 2 expr username = @"username&

在lldb调试中调用c++函数

在lldb调试时,调用oc对象的方法不足为奇,因为msgSend是有原型导出的,oc对象的方法都运行期绑定的,绑定信息都在objc_class中.只要在调试中[receiver sel]之类,lldb就自动完成的整个由SEL通过msgSend路由到receiver的IMP方法并执行的整个过程.但是要调用c++函数则没有这么方便,虽然c++函数(包括成员函数和非成员函数)的链接符号有着函数原型的详细信息,但却不包括类的定义和名字空间的定义,即使lldb翻译出这样一个符号(symbol)Quartz