LLDB初探

什么是LLDB?

官方说明如下:

LLDB is a next generation, high-performance debugger. It is built as a set of reusable components which highly leverage existing libraries in the larger LLVM Project, such as the Clang expression parser and LLVM disassembler.
LLDB is the default debugger in Xcode on Mac OS X and supports debugging C, Objective-C and C++ on the desktop and iOS devices and simulator.
All of the code in the LLDB project is available under the standard LLVM License, an open source "BSD-style" license.

总结来说:LLDB是一个有着 REPL(交互式解析器) 的特性和 C++ |Python 插件的开源调试器新一代高性能调试器。

随着Xcode5的发布,LLDB调试器成为macOS系统调试的基础部分。作为iOS开发者,我们可以使用Xcode UI提供的控件以及Xcode调试控制台发出的命令来访问其丰富的功能。对于开源和其他非基于GUI的应用程序调试的开发,您可以将终端窗口中的LLDB用作传统的命令行调试器。还可以通过使用Python脚本和Python-LLDB对象库来扩展LLDB。

LLDB基础介绍

1.LLDB命令结构

命令格式大致如下:

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

1.<command> 命令和 <subcommand> 子命令是 LLDB调试器对象的名称。命令和和子命令按照层次结构排列.特定的命令对象为其后面的子命令对象创建上下文,后者再次为下一个子命令提供上下文,依此类推。

2.<action> 是要在组合调试器对象(其前面的命令子命令-实体的字符串)的上下文中执行的操作。

3.-options 动作修饰符,通常带有值。

4.Argument 根据使用命令的上下文,表示各种不同的事物。

LLDB命令行解析在命令执行之前完成。上述 命令,子命令,选项,选项值和参数都是空格分隔的,双引号用于保护选项值和参数中的空格。如果需要在参数中添加反斜杠或双引号字符,则在该字符前面加上参数中的反斜杠。LLDB等效地使用单引号和双引号,允许轻松写入命令行的双引号部分。

(lldb) command [subcommand] -option "some \"quoted\" string"

(lldb) command [subcommand] -option 'some "quoted" string'

上述两个命令是等效的。
此命令解析设计有助于使LLDB命令语法在所有命令中保持规则和统一。

2.LLDB命令选项

LLDB命令选项具有规范形式和缩写形式。
以设置断点设置断点的命令breakpoint set为例为例,列举其部分命令。其中括号中是其规范形式。

breakpoint set

     -M <method> ( --method <method> )
       

    -S <selector> ( --selector <selector> )

    -b <function-name> ( --basename <function-name> )

    -f <filename> ( --file <filename> )

    -l <linenum> ( --line <linenum> )

    -n <function-name> ( --name <function-name> )

可以在命令行之后以任意顺序放置命令选项。如果参数Argument-连字符开头,则需要添加双连字符--选项(终止信号),向LLDB表明已经完成当前命令选项action

例如你想启动一个进程,并给process launch命令添加--stop-at-entry选项,并希望该进程使用参数-program_arg_1 value-program_arg_2 value,
应该这样输入:

(lldb) process launch --stop-at-entry -- -program_arg_1 value -program_arg_2 value
3.原始命令

LLDB支持原始命令(“raw” commands),没有命令选项。当删除命令选项(option),命令字符串的剩余部分未经解析就传递给命令。例如,expression就是原始命令.
可以通过help命令的输出来查看一个命令是否是原始命令.
当然原始命令也允许有选项。如果命令字符串中包含破折号,则通过在在命令名之后命令字符串之前添加--来表明没有选项标记。

4.命令补全

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

LLDB命令示例

1.执行命令(Execution Commands)

启动之前为进程设置环境变量

(lldb) settings set target.env-vars DEBUG=1
(lldb) set  se target.env-vars DEBUG=1

在一个命令中设置进程和启动进程的环境变量。

(lldb) process launch -v DEBUG=1

启动进程

(lldb) process launch
(lldb) run
(lldb) r

使用进程ID为123附加到当前进程

(lldb) process attach --pid 123
(lldb) attach -p 123

使用名为的进程a.out附加到当前进程.
当使用名称来连接一个程序时,LLDB支持--waitfor选项。这个选项告诉LLDB等待下一个名称为指定名称的程序出现,然后连接它.

(lldb) process attach --name a.out 
(lldb) pro at -n a.out
(lldb) process attach --name a.out --waitfor
(lldb) pro at -n a.out -w
2.断点命令(Breakpoint Commands)

给所有main函数设置断点.同样也可以给其他函数名设置断点

(lldb) breakpoint set --name main
(lldb) br s -n main
(lldb) b main

给对应文件指定位置设置断点
下面给类PLShowDynamicVC.m 第63行设置断点

(lldb) breakpoint set --file PLShowDynamicVC.m --line 63

给所有C++以main方法设置断点

(lldb) breakpoint set --method main
(lldb) br s -M main

Objective-C选择器断点,例如
给选择器releaseDynamics设置断点

(lldb) breakpoint set --selector releaseDynamics

还可以用--shlib <path>来将断点限定在一个特定的可执行库中

(lldb) breakpoint set --shlib foo.dylib --name foo

无论是在逻辑断点生成的所有位置上,还是在逻辑断点解析到的任何一个特定位置上,都可以使用断点触发的命令删除,禁用,设置条件和忽略计数。例如,如果要在LLDB命中编号的断点时添加命令以打印回溯1.1,可输入以下命令:

(lldb) breakpoint command add 1.1

Enter your debugger command(s). Type 'DONE' to end.
> bt

> DONE

列出所有断点

(lldb) breakpoint list
(lldb) br l

删除断点,删除指定断点就跟断点位置,删除所有断点 直接 breakpoint delete

(lldb) breakpoint delete 1

(lldb) br del 1
3.观察点(Watchpoint Commands)

在写入变量时在变量上设置观察点,
比如我要观察当前类PLReleaseDynamicsViewController中一个字典属性imageDict

注意:watchpoint set variable传入的是变量名。需要注意的是,这里不接受方法,所以不能使用watchpoint set variable self.imageDict,因为self.imageDict调用的imageDict的getter方法,所以观察的是self->_imageDict

如下:

(lldb) watchpoint set variable self->_imageDict
(lldb) wa s v self->_imageDict

写入时在内存位置设置观察点。
watchpoint set expression.
还以imageDict属性为例。

(lldb) watchpoint set expression --  &_imageDict
(lldb) wa s e --  &_imageDict

还可以使用以下命令来监测名为global的变量的写操作,并在global==5为真时停止监测.

(lldb) watch set var global
(lldb) watchpoint modify -c '(global==5)'

列出所有观察点

(lldb) watchpoint list
(lldb) watch l

删除观察点.同样删除所有观察点就不需要跟观察点的ID

(lldb) watchpoint delete 1
(lldb) watch del 1
4.检查变量

显示当前帧的参数和局部变量

(lldb) frame variable
(lldb) fr v

显示当前帧的局部变量

(lldb) frame variable --no-args
(lldb) fr v -a

显示局部变量的内容bar

(lldb) frame variable bar
(lldb) fr v bar
(lldb) p bar

显示当前源文件中定义的全局/静态变量

(lldb) target variable
(lldb) ta v
5.检查线程状态

当前线程的堆栈回溯

(lldb) thread backtrace
(lldb) bt

所有线程的堆栈回溯

(lldb) thread backtrace all
(lldb) bt all

当前线程中当前所选帧的信息

(lldb) frame info

回溯当前线程的前五帧

(lldb) thread backtrace -c 5
(lldb) bt 5 (lldb-169 and later)
(lldb) bt -c 5 (lldb-168 and earlier)

按索引为当前线程选择不同的堆栈帧

(lldb) frame select 12
(lldb) fr s 12
(lldb) f 12
6.可执行和共享库查询命令

列出可执行文件和所有相关的共享库

(lldb) image list

在可执行文件或任何共享库中查找原始地址的信息.
当程序崩溃的时候,可以使用这条命令来查找崩溃所在的具体位置

(lldb) image lookup --address 0x1ec4
(lldb) im loo -a 0x1ec4

7.其他一些命令

命令别名

可以使用LLDB的别名机制来为常用的命令创建一个别名。
例如我们要经常用下以下命令:

(lldb) breakpoint set --file PLShowDynamicVC.m --line 63

我们可以通过下面命令创建别名

(lldb) command alias bfl breakpoint set -f %1 -l %2

这样我们可以这样调用:

(lldb) bfl PLShowDynamicVC.m 63

调试过程,如果我们需要频繁修改值或者背景色来看实时效果.可以使用expression,它不仅会改变调试器中的值,还改变了程序中的实际值

(lldb)  exp openid = @"10"
(NSTaggedPointerString *) $8 = 0xd08c4ed8c801cb09 @"10"

打印Objective-C对象

(lldb) expr -O -- [SomeClass returnAnObject]
或使用po别名:
(lldb) po [SomeClass returnAnObject]

更多可参考GDB and LLDB Command Examples

LLDB作为独立调试器

大多数情况向,我们通过xcode调试功能间接使用LLDB调试器,并使用Xcode控制台窗格发出LLDB命令.
我们需要在终端启动LLDBB并使用命令行指定要调试的文件。
命令如下:

$ lldb /Users/fitfun/Library/Developer/Xcode/DerivedData/FitfunAssistant-fylwbhdtgpfqvdetubgpsutdfczv/Build/Products/Debug-iphonesimulator/FitfunAssistant.app

或者

$ lldb
(lldb)file /Users/fitfun/Library/Developer/Xcode/DerivedData/FitfunAssistant-fylwbhdtgpfqvdetubgpsutdfczv/Build/Products/Debug-iphonesimulator/FitfunAssistant.app

接着我们就可以使用上面讲述LLDB相关命令在终端操作了。

好了。LLDB初步探路就先这样了。不足和错误之处欢迎指正。

参考:

LLDB官方文档

LLDB调试器使用简介

WWDC 2018:效率提升爆表的 Xcode 和 LLDB 调试技巧

LLDB Quick Start Guide