【iOS】LLDB调试技巧

4,451
原文链接: blog.xigulu.com

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

lldb.llvm.org/tutorial.ht…

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

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

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


调试命令

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

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

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

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

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

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 —的别名)

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

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

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

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

e NSString *$str = @"blog.xigulu.com"           定义一个变量str
e @import UIKit
e cellItem.layer.borderWidth = 1
e (void)[CATransaction flush]                   刷新界面(在刷新界面时调用, 因为程序已经暂停)
e id $view = (id) 0x7fbd71432590
e (void) [$view setBackgroundColor:[UIColor redColor]]         改变view的背景颜色
e (void)[CATransaction flush]
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 命令重写函数的行为,然后继续。

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

frame variable 获取全部变量值。

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

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

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

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

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比较常用)

n            下一步
s            进入

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

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

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命令创建的别名。)

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

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

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中变量一样

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.执行替代代码

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

常见问题

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

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

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

参考

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

Apple LLDB Quick Start Guide