【iOS】LLDB调试技巧

LLDB调试器很早就取代GDB,成为Xcode工程中默认的调试器。

http://lldb.llvm.org/tutorial.html

命令运行方法: 必须先打断点—>然后才能在控制台中输入调试语句. 或者运行后点击暂停.

命令补全: 补全会在第三个字符被键入时自动弹出,或者通过Esc键手动弹出。

关于~/.lldbinit文件: LLDB在启动时会读取~/.lldbinit文件. 这个文件里存放LLDB启动时执行的脚本/alias等.


调试命令

$0 $1 之类的是当前运行的调试记录, 可直接使用值. (任何以美元符开头的东西都是存在于 LLDB 的命名空间的)

LLDB 命令实际上会作前缀匹配. 所以如print命令, 你也可以使用 prinpri,或者 p。但你不能使用 pr,因为 LLDB 不能消除和 process 的歧义. (很多命令的是简写或者别名)

help 查看命令帮助. (不传参数时显示所有命令, 可以具体查看莫伊参数的使用)

1
2
help po
help break command add 具体到查看add参数的帮助

p(print) 功能是输出原生类型(boolean、integer、float、etc)的信息。格式清单

1
2
3
4
5
p (int)[[[self view] subviews] count]
p/x 16 print/<fmt>格式 这里表示将变量或值以16进制输出
p/t 16 二进制输出(t表示two)
p/c 16 打印字符串
x/4c $str x命名为查看内存. 这里查看4个字节

po(print object)功能是输出objective-c中对象(objects)的信息. (为e -o —的别名)

1
2
po [self view] 输出当前view信息
po [[[UIApplication sharedApplication] keyWindow] recursiveDescription] 打印当前的视图层级

call 同po或p一样, 也是调用. 当不需要显示输出时使用.

1
call [self.view setBackgroundColor:[UIColor redColor]]

expr(全称expression, 也简写为e) 运行时修改变量值, 指针等.

1
2
3
4
e NSString *$str = @"blog.xigulu.com" 定义一个变量str
e @import UIKit
e cellItem.layer.borderWidth = 1
e (void)[CATransaction flush] 刷新界面(在刷新界面时调用, 因为程序已经暂停)
1
2
3
e id $view = (id) 0x7fbd71432590
e (void) [$view setBackgroundColor:[UIColor redColor]] 改变view的背景颜色
e (void)[CATransaction flush]
1
2
3
4
5
6
e id $nvc = [[[UIApplication sharedApplication] keyWindow] rootViewController] //拿到根NavigationController
e id $vc = [UIViewController new] //生成一个vc
e (void)[[$vc view] setBackgroundColor:[UIColor yellowColor]]
e (void)[$vc setTitle:@"Yay!"]
e (void)[$nvc pushViewContoller:$vc animated:YES] //push
caflush // e (void)[CATransaction flush] //显示

bt (全称thread backtrace) 打印调用堆栈,加all可打印所有thread的堆栈。程序出错时可使用这个命令.

可以把断点放在函数的开头,然后用 thread return 命令重写函数的行为,然后继续。

1
2
3
thread backtrace all 查看所有线程调用栈
thread list 列出所有线程
thread return <exp> 可用来控制程序流程, 伪造返回值

frame variable 获取全部变量值。

1
2
frame v self->testVar 也可以获取单个变量值
frame info 当前的行数和源码文件,以及其他一些信息

image 可用于寻址,有多个组合命令。常用于寻找栈地址对应的代码位置, 用于查错(能定位出错误代码行数)。

1
image lookup --address 0x0000000100004af8 最后为栈地址

breakpoint(或简写为b) 设置断点. (可在运行过程中添加)

1
2
3
4
5
6
7
8
breakpoint set -f XXX.m -l 28 第28行设置断点
b ViewController.swift:28 第28行设置断点(xxx.m也一样)
b 28 功能同上
b myfunc 直接指定函数(符号断点:Xcode GUI中只要方法执行就触发断点, 这里只针对本类)
b -[NSArray objectAtIndex:]
breakpoint list // 列出所有断点. 简写br li
br dis 1 禁用某个断点
breakpoint delete 3 // 删除3号断点

断点调试命令: c 即process continue,n 即step over下一步, s即step in, thread step-out即step out. (n,s比较常用)

1
2
n 下一步
s 进入

watchpoint: 监听某个实例的变化. (等同于在Xcode调试变量窗口—>右键某个变量—>Watch xx)

注意: watchpoint是分类型的,包括read,write或者read_write类型. 通过Xcode右键添加的只能是write类型.

1
2
3
4
5
6
watchpoint set self->testVar //为该变量地址设置watchpoint
watchpoint set v -w read_write _mybtn //为_mybtn变量设置read_write类型的监听
watchpoint set expression 0x00007fb27b4969e0 //为该内存地址设置watchpoint,内存地址可从前文提及的`p`命令获取
watchpoint command add -o 'frame info' 1 //为watchpoint 1号加上子命令 `frame info`
watchpoint list //列出所有watchpoint
watchpoint delete // 删除所有watchpoint

定义别名alias: ( 我们可以自由地创建LLDB命令的别名集合。LLDB在启动时会读取~/.lldbinit文件。这个文件中存储了command alias命令创建的别名。)

1
command alias mycommand image lookup --address %1 定义mycommand别名

上面的就可以简化为mycommand 0x0000000100004af8进行调用

1
2
3
4
breakpoint set --file foo.c --line 12
command alias bfl breakpoint set -f %1 -l %2 用别名
bfl foo.c 12 简化后的调用方式
command unalias bfl 取消别名

lldb中声明变量: 和PHP中变量一样

1
2
3
4
e int $a = 2 //普通类型
p $a * 19
e NSArray *$array = @[ @"Saturday", @"Sunday", @"Monday" ] //对象类型
p [$array count]

在终端中调试程序

使用LLDB调试程序

一般是在Xcode中进行调试, 也可以在命令行中调试. 步骤如下: 参考 (这个就类似于Windows中的debug程序)

1.加载程序以备调试

2.将一个运行的程序绑定到LLDB

3.设置断点和观察点

4.控制程序的执行

5.在调试的程序中导航

6.检查状态和值的变量

7.执行替代代码

1
2
3
4
$ lldb /Users/xuneng/Desktop/MacApp/MacDown.app 启动调试. (或者直接`lldb`进入REPL交互界面)
设置断点.... (这个只有app程序不好设置)
$ run 启动程序
$ q 退出lldb

常见问题

问题1: has unknown return type (不明类型或者类型不匹配)

1
2
p NSLog(@"%@",[self.view viewWithTag:1001]) (最新版本的这个已经修复了)
p (void)NSLog(@"%@",[self.view viewWithTag:1001]) 多见于id类型, 必须显式声明类型。

问题2: no known method ‘-characterAtIndex:’; cast the message send to the method’s return type

1
2
p [[$array objectAtIndex:$a] characterAtIndex:0] 也是返回值的问题
p (char)[[$array objectAtIndex:$a] characterAtIndex:0] 加上(char)转换即可

参考

与调试器共舞 - LLDB 的华尔兹

Apple LLDB Quick Start Guide

转载请注明出处,有疑问欢迎留言!