首页 > 学院 > 开发设计 > 正文

(转)Xcode中LLDB的基本命令的使用(Swift)

2019-11-06 09:43:48
字体:
来源:转载
供稿:网友

随着Xcode 5的发布,LLDB调试器已经取代了GDB,成为了Xcode工程中默认的调试器。它与LLVM编译器一起,带给我们更丰富的流程控制和数据检测的调试功能。LLDB为Xcode提供了底层调试环境,其中包括内嵌在Xcode IDE中的位于调试区域的控制面板,在这里我们可以直接调用LLDB命令.平时用Xcode运行程序,实际走的都是LLDB。熟练使用LLDB,可以让你debug事半功倍。

我们以一个实际例子进行学习使用,为了方便,我们先写一个Person类:

class Person: NSObject {        var name:String    var age:UInt32    init(name:String,age:UInt32) {        self.name = name        self.age = age    }    //重写description是为了方便调试    override var description: String{     return "name:/(name),age:/(age)"    }}

进入 ViewController.swift添加一个测试函数,添加相应断点,并在viewDidLoad()中调用,执行代码到函数的最后,如下图:

/

 

首先看一下打印命令p/po

 

在控制台使用p和po来替代PRint,对于p和po,第一次看到可能不明白是啥,我们可以输入help,在帮助信息中我们可以找到p和po部分如下:

 

(lldb) help

p -- ('expression --') Evaluate an expression (ObjC++ or Swift)in

the current program context, using user defined variables and

variables currentlyin scope.

po -- ('expression -O -- ') Evaluate an expression (ObjC++ or Swift)

in the current program context, using user defined variables and

variables currentlyin scope.

 

所以,p命令,本质是expression --;po命令,本质是expression -O —,其中-O的表示:-O ( --object-description ) Display using a language-specific description API, if possible.

 

注意打印输出的最底部信息:For more information on any command, type'help '.我们通过输入help和命令名得到更详细的帮助。

 

1:p命令会打印出对象的类型和相关属性.

(lldb) p person

(LLDBDemo.Person) $R0 = 0x00007f93fb4f4ae0 {

ObjectiveC.NSObject = {}

name = "Jack"

age = 24

}

 

2:po命令对于继承自NSObject得对象,指示会打印出description中的内容,类似于print函数.

 

(lldb) po person

name:Jack,age:24

3:在看一个数组的小例子,直接console中输入:

  

(lldb) po ["123","456"]

? 2 elements

- [0] : "123"

- [1] : "456"

 

(lldb) p ["123","456"]

([String]) $R3 = 2 values {

[0] = "123"

[1] = "456"

}

 

 

4:打印视图的层级结构,该功能还是挺有用的

 

(lldb) (lldb) po view.performSelector("recursiveDescription")

? Unmanaged

- _value : >

| >

| <_UILayoutGuide: 0x7f93fb489720; frame = (0 0; 0 0); hidden = YES; layer = >

| <_UILayoutGuide: 0x7f93fb4ef7a0; frame = (0 0; 0 0); hidden = YES; layer = >

 

这里还有一个好玩的东西,那就是每个视图中,都有一个16进制的字符串,代表这个视图的ID,比如这个:UIView:0x7fd71ac66970;这个 ID的作用非常的强大,得到了这个ID,我们就可以通过这个命令来得到这个视图的引用了:

 

(lldb) expression let $view = unsafeBitCast(0x7f93fb4dc930, UIView.self)

 

 

然后就可以对该视图进行操作了,例如执行如下代码:

 

(lldb)expression$view.backgroundColor = UIColor.redColor()

 

 

程序运行完成,整个视图背景颜色变红。ID还是很有用的,当然我们也可以直接操作当前视图,如下:

 

(lldb)expressionself.view.backgroundColor = UIColor.redColor() 

既然可以动态改变视图背景,那么我们也可以动态添加相应的视图,接下来我们添加一个layer到当前视图上:

 

(lldb) (lldb) expression

Enter expressions, then terminate with an empty line to evaluate:

1 let layer = CALayer()

2 layer.backgroundColor = UIColor.yellowColor().CGColor

3 layer.bounds = CGRect(x:0, y:0, width:100, height:100)

4 layer.position = CGPoint(x:250, y:300)

5 layer.cornerRadius = 12.0

6 view.layer.addSublayer(layer)

7

效果如下图:/ 

看起来不错啊,但是如果接下来,我们还需要使用之前创建的layer,那该怎么办?如果我们直接使用肯定不行,(lldb)expression layer,直接报错,所以创建全局常量或者变量就好了,一起来看一下:

 

(lldb) expression let $layer = CALayer()

(lldb) expression

Enter expressions, then terminate with an empty line to evaluate:

1 $layer.backgroundColor = UIColor.yellowColor().CGColor

2 $layer.bounds = CGRect(x:0, y:0, width:100, height:100)

3 $layer.position = CGPoint(x:250, y:300)

4 $layer.cornerRadius = 12.0

5 view.layer.addSublayer($layer)

6

(lldb) po $layer 

[ (kCGColorSpaceDeviceRGB)] ( 1 1 0 1 )>

 5:打印视图控制器的层级结构 

(lldb) po UIWindow.valueForKeyPath("keyWindow.rootViewController._printHierarchy")

? Optional

- Some : , state: appearing, view: 

| , state: disappeared, view: not in the window

接下来看一下前面使用到的Expression命令

 

Expression命令很灵活,非常多,我们可以输入help expression得到对应的帮助信息:Syntax: expression -- ,expression可以使用e或者expr代替。

 

1:expression命令可以帮助我们执行代码,下面执行改变属性name的值:

 

(lldb) p person

(LLDBDemo.Person) $R1 = 0x00007fd20a706be0 {

ObjectiveC.NSObject = {}

name = "Jack"

age = 24

}

(lldb) e person.name = "hua"

(lldb) p person

(LLDBDemo.Person) $R3 = 0x00007fd20a706be0 {

ObjectiveC.NSObject = {}

name = "hua"

age = 24

}

 

2:看一下格式化打印相关

 

(lldb) e -f bin -- person.age

(UInt32) $R4 = 0b00000000000000000000000000011000

(lldb) e -f oct -- person.age

(UInt32) $R5 = 030

(lldb) e -f hex -- person.age

(UInt32) $R6 = 0x00000018

上面是格式化打印二进制,八进制和十进制,命令的意思为:e是expression的缩写,-f bin是格式化语法为:-f (format): -f ( --format ) Specify a format to be usedfor display.--分隔符,最后person.age是对需要的值进行格式化。

 其实格式化打印可以更简单:

 

(lldb) p/x person.age

(UInt32) $R10 =0x00000018

 

看到这里,可能会想,p/x是怎么回事?怎样查找格式化命令符号所对应的含义?

 

(lldb) type format

The following subcommands are supported:

 

add -- Add a new formatting style for a type.

clear -- Delete all existing format styles.

delete -- Delete an existing formatting style for a type.

info -- This command evaluates the provided expression and shows which

format is applied to the resulting value (if any). This

command takes 'raw' input (no need to quote stuff).

list -- Show a list of current formats.

 

For more help on any particular subcommand, type 'help

  '.

 

 

再输入help format info,将看到所有格式化指令,下面是部分内容截图:

 

/ 

上面划线部分:d对应十进制,h对应十六进制,o对应八进制,再试试其他几个:

 

(lldb) p/o person.age

(UInt32) $R2 = 030

(lldb) p/d person.age

(UInt32) $R3 = 24

 

3:打印Raw value,-R ( --raw-output ) Don't use formatting options.

(lldb) e -R -- person

(LLDBDemo.Person) $R7 = 0x00007fd20a706be0 {

ObjectiveC.NSObject = {}

name = {

_core = {

_baseAddress = {

_rawValue = 0x000000011321e4e0

}

_countAndFlags = {

_value = 3

}

_owner = None {

Some = {

instance_type = 0x0000000000000000

}

}

}

}

age = {

_value = 24

}

}

 

4:显示变量类型, -T ( --show-types ) Show variable types when dumping values.

(lldb) e -T -- person

(LLDBDemo.Person) $R9 = 0x00007fd20a706be0 {

(NSObject) ObjectiveC.NSObject = {}

(String) name = "hua"

(UInt32) age = 24

}

 

5:显示变量位置信息, -L ( --location ) Show variable location information.

(lldb) e -L -- person

scalar(0x00007fd20a706be0): (LLDBDemo.Person) $R10 = 0x00007fd20a706be0 {

scalar(0x00007fd20a706be0): ObjectiveC.NSObject = {}

0x00007fd20a706bf0: name = "hua"

0x00007fd20a706c08: age = 24

}

 

6:当然也可以组合多个部分一起查看,这里就是组合之前的R、T、L

(lldb) e -RTL -- person

scalar(0x00007fd20a706be0): (LLDBDemo.Person) $R11 = 0x00007fd20a706be0 {

scalar(0x00007fd20a706be0): (ObjectiveC.NSObject) ObjectiveC.NSObject = {}

0x00007fd20a706bf0: (Swift.String) name = {

0x00007fd20a706bf0: (Swift._StringCore) _core = {

0x00007fd20a706bf0: (Swift.COpaquePointer) _baseAddress = {

0x00007fd20a706bf0: (Builtin.RawPointer) _rawValue = 0x000000011321e4e0

}

0x00007fd20a706bf8: (Swift.UInt) _countAndFlags = {

0x00007fd20a706bf8: (Builtin.Int64) _value = 3

}

0x00007fd20a706c00: (Swift.Optional) _owner = None {

0x00007fd20a706c00: (AnyObject) Some = {

0x00007fd20a706c00: (Builtin.RawPointer) instance_type = 0x0000000000000000

}

}

}

}

0x00007fd20a706c08: (Swift.UInt32) age = {

0x00007fd20a706c08: (Builtin.Int32) _value = 24

}

}

 

7:多行表达式模式

 

(lldb) e

Enter expressions, then terminate with an empty line to evaluate:

1 struct Compass{var dirction = "N";var angle = 16.5}

2 var c = Compass()

3 print(c)

4

(Compass #1)(dirction: "N", angle: 16.5)

 

(lldb) e

Enter expressions, then terminate with an empty line to evaluate:

1 func add(a:Int,b:Int)->Int {return a+b }

2 let c = add(3,b:4)

3 print(c)

4

7

 

8:导入模块框架

 

如果我们在调试的过程中,需要获取某个框架的类,但是当前文件中并没有导入,我们可以在console中调试过程中直接输入import和框架名称,好比如:我们需要MapKit中的类,直接:

 

(lldb) p import MapKit

 

9:LLDB变量

 

细心的朋友可能会发现输出的信息中带有$1、$2的字样。实际上,我们每次查询的结果会保存在一些持续变量中($[0-9]+),这样你可以在后面的查询中直接使用这些值。其实前面在使用layer的例子中已经提到了,在看个例子:

 

(lldb) e var $a = Person(name:"Jenny",age:24)

(lldb) p $a

(LLDBDemo.Person) $R12 = 0x00007fd20a512ba0 {

ObjectiveC.NSObject = {}

name = "Jenny"

age = 24

}

 

接下来看一下我们经常使用的断点(breakpoint),断点采用这个命令breakpoint,缩写br,这里先列出文档,看一下为我们提供了哪些功能:

 

 

(lldb) help breakpoint

The following subcommands are supported:

 

clear -- Clears a breakpoint or set of breakpoints in the executable.

command -- A set of commands for adding, removing and examining bits of

code to be executed when the breakpoint is hit (breakpoint

'commands').

delete -- Delete the specified breakpoint(s). If no breakpoints are

specified, delete them all.

disable -- Disable the specified breakpoint(s) without removing them. If

none are specified, disable all breakpoints.

enable -- Enable the specified disabled breakpoint(s). If no breakpoints

are specified, enable all of them.

list -- List some or all breakpoints at configurable levels of detail.

modify -- Modify the options on a breakpoint or set of breakpoints in

the executable. If no breakpoint is specified, acts on the

last created breakpoint. With the exception of -e, -d and -i,

passing an empty argument clears the modification.

name -- A set of commands to manage name tags for breakpoints

set -- Sets a breakpoint or set of breakpoints in the executable.

 

For more help on any particular subcommand, type 'help

  '.

 

基本的使用:

 

1:使用list显示所有断点信息

/

 

当前工程只有一个断点,所以只打印了一个断点信息,并且可以知道断点的相关信息,在当前文件的29行,而且可以通过左边断点显示区域来对比结果。

 

2:禁用断点即disable命令

 

根据上面内容,现在我们来玩一玩,现在在原有的代码上再添加2个断点,如下图:

/

 

1)首先使前面两个断点失效

 

(lldb) br disable 1.*

1 breakpoints disabled.

(lldb) br disable 2.*

1 breakpoints disabled.

 

 

2)点击运行程序,会跳到第三个断点,中间断点不在暂停,并且断点信息也会不一样,多了断点失效部分的内容,如下图:

 

/

 

3:我们也可以使用 enable恢复断点

(lldb) br enable 1.*

1 breakpoints enabled.

(lldb) br enable 2.*

1 breakpoints enabled.

4:删除断点,删除第一个断点

(lldb) br delete 1

1 breakpoints deleted; 0 breakpoint locations disabled.

(lldb) br list

Current breakpoints:

2: file = '/Users/longshihua/Desktop/LLDBDemo/LLDBDemo/ViewController.swift', line = 28, exact_match = 0, locations = 1, resolved = 1, hit count = 1

 

2.1: where = LLDBDemo`LLDBDemo.ViewController.testPerson () -> () + 12 at ViewController.swift:28, address = 0x000000010b4f3d5c, resolved, hit count = 1

 

3: file = '/Users/longshihua/Desktop/LLDBDemo/LLDBDemo/ViewController.swift', line = 30, exact_match = 0, locations = 1, resolved = 1, hit count = 1

 

3.1: where = LLDBDemo`LLDBDemo.ViewController.testPerson () -> () + 204 at ViewController.swift:30, address = 0x000000010b4f3e1c, resolved, hit count = 1

 

5:添加断点

 

(lldb) br set -f ViewController.swift -l26

Breakpoint 4:where = LLDBDemo`LLDBDemo.ViewController.testPerson () -> () +12 at ViewController.swift:28, address =0x000000010b4f3d5c

 

命令中的-f ViewController.swift和-l26

-f ( --file ) 具体的文件

-l ( --line ) 文件的位置,即具体哪一行

 

6:为函数添加断点,这里直接为我们的testPerson函数添加断点,-F为函数名

 

(lldb) br set -F testPerson

Breakpoint 3:2 locations.

 

7:条件断点

 

先添加一个函数,并加一个断点,如下图:当某种条件产生的时候触发的断点,现在这里实现停在1000次循环的第800次

/

 

当i为800时触发断点

(lldb) br modify -c i==800

(lldb) po i

800

 

指令说明:

-c ( --condition )

The breakpoint stops onlyifthis condition expression evaluates to

true.

 

记住一点,多利用help,查找命令和帮助信息,所以这里我们可以通过help br set来查看更多信息。

 

了解一下线程相关内容thread

 

1:threadreturn

 

Debug的时候,也许会因为各种原因,我们不想让代码执行某个方法,或者要直接返回一个想要的值。这时候就该thread return上场了。thread return可以接受一个表达式,调用命令之后直接从当前的堆栈中返回表达式的值。我们只需在方法的开始位置加一个断点,当程序中断的时候,输入命令即可。

 

2:thread step-over单步执行,执行下一行代码

thread step-in 进入函数体,单步执行

thread step-out 退出当前函数体

 

3:thread backtrace显示堆栈信息

(lldb) thread backtrace

* thread #1: tid = 0x35a80, 0x0000000105b62d5c LLDBDemo`ViewController.testPerson(self=0x00007fab09d2bf00) -> () + 12 at ViewController.swift:28, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1

* frame #0: 0x0000000105b62d5c LLDBDemo`ViewController.testPerson(self=0x00007fab09d2bf00) -> () + 12 at ViewController.swift:28

............

............

Type类型查找

 

(lldb) help type

The following subcommands are supported:

 

category -- A set of commands for Operating on categories

filter -- A set of commands for operating on type filters

format -- A set of commands for editing variable value display options

lookup -- Lookup a type by name in the select target. This command

takes 'raw' input (no need to quote stuff).

summary -- A set of commands for editing variable summary display

options

synthetic -- A set of commands for operating on synthetic type

representations

 

For more help on any particular subcommand, type 'help

  '.

 

 

抛出一个异常,不知道是啥的时候怎么办?我们可以使用lookup命令,查找我们的类型信息:

(lldb) type lookup Person

@objc class Person : ObjectiveC.NSObject {

@objc var name: Swift.String

@objc var age: Swift.UInt32

@objc init(name: Swift.String, age: Swift.UInt32)

@objc override var description: Swift.String {

@objc override get {}

}

@objc func personException() throws

@objc deinit

@objc @objc init()

}

 

frame(帧)首先看一下与frame(帧)相关解释:

作用:frame -- A set of commandsfor operating on the current thread's frames.

 

(lldb) help frame

The following subcommands are supported:

 

info -- List information about the currently selected frame in the

current thread.

select -- Select a frame by index from within the current thread and

make it the current frame.

variable -- Show frame variables. All argument and local variables that

are in scope will be shown when no arguments are given. If

any arguments are specified, they can be names of argument,

local, file static and file global variables. Children of

aggregate variables can be specified such as 'var->child.x'.

 

For more help on any particular subcommand, type 'help

  '.

 

(lldb) frame variable person

(LLDBDemo.Person) person = 0x00007fab09e13790 {

ObjectiveC.NSObject = {}

name = "Jack"

age = 24

}

 

 

我们可以使用frame info查看当前frame的信息:

(lldb) frame info

frame #0:0x0000000105b62e1c LLDBDemo`ViewController.testPerson(self=0x00007fab09d2bf00) -> () +204 at ViewController.swift:30

 

target命令

对于target这个命令,我们用得最多的可能就是target modules lookup。由于LLDB给 target modules取了个别名image,所以这个命令我们又可以写成image lookup.

1:当我们想查看一个类型的时候,可以使用image lookup --type,简写为image lookup -t,比如我们可以看看自己的Person类型信息:

 

(lldb) image lookup -t Person

2:当我们想查找一个方法或者符号的信息,比如所在文件位置等。我们可以使用image lookup --name,简写为 image lookup -n,这里我们就直接查找我们的testPerson函数,我们可以看到如下信息:

(lldb) image lookup -n testPerson

2 matches found in /Users/longshihua/Library/Developer/Xcode/DerivedData/LLDBDemo-fxvhyqfwhdszpwdegllmmmsjcohz/Build/Products/Debug-iphonesimulator/LLDBDemo.app/LLDBDemo:

Address: LLDBDemo[0x0000000100003d50] (LLDBDemo.__TEXT.__text + 9872)

Summary: LLDBDemo`LLDBDemo.ViewController.testPerson () -> () at ViewController.swift:26 Address: LLDBDemo[0x0000000100003e30] (LLDBDemo.__TEXT.__text + 10096)

Summary: LLDBDemo`@objc LLDBDemo.ViewController.testPerson () -> () at ViewController.swift

 

3:当我们有一个地址,想查找这个地址具体对应的文件位置,可以使用 image lookup --address,简写为image lookup -a.

 
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表