您的位置:首页 > 移动开发 > IOS开发

进行iOS代码调试之LLDB调试器

2016-04-24 23:45 405 查看


配合LLDB调试器进行iOS代码调试

在一款完整iOS移动应用的开发中,代码的调试和编写占着同等重要的地位。Xcode默认使用LLDB作为代码调试器,LLDB功能丰富且强大,恰当的使用它,可以帮助开发者事半功倍的完成代码调试的工作。


1.expression代码执行指令

关于LLDB调试器,最常用的指令应该是p与po了,开发者常用这两个命令来进行对象的打印操作,p会打印出对象地址和类型,po则会额外打印出对象的值得内容,实际上,这两个命令都是expression相关命令的简写。expression命令也并非简单的打印命令,实际上它是一个执行代码命令,执行后将返回值进行打印,这个命令有一个十分强大的特点,它可以真实改变程序运行中变量的值。例如在如下代码中的int
c = a+b 一行添加一个断点,运行工程。

?

int a = 0;
int b = 1;
int c = a+b;
NSLog(@"%d",c);


如果开发者不进行任何认为操作,此时打印出的值应该是1,为了测试,可以在调试区输入如下命令:

(lldb) expression a=1


此后跳过断点继续运行程序,可以看到打印的结果如下,c变成2。

(lldb) expression a=1
(int) $0 = 1
2016-04-24 11:39:40.213 BreakPointTest[1010:79065] 2


通过上面的演示,我们发现使用LLDB调试代码十分方便的一个特点,当我们知道程序某个地方可能会出现问题,为了找到解决方法,不使用LLDB时我们可能需要在代码中添加大量的打印函数,并且多次尝试修改源代码才能解决问题,如果使用LLDB的expression命令,我们不仅不需要添加额外的打印代码,也不需要直接修改源代码,在调试区进行多次调试,直到找到正确的修改方法后再对源代码修改一次即可。


2.frame代码堆栈块信息相关指令

当Xcode进入断点调试或者遇到异常程序崩溃时,在Xcode左侧的导航区都会将程序运行中的相关堆栈块信息列举出来,例如使用如下测试代码,在text方法中的int c = a+b 一行添加一个断点。

?

#import "ViewController.h"
@interface ViewController ()
{
int ab;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
ab = 1;
[self test];
}
-(void)test{
int a = 0;
int b = 1;
int c = a+b;
NSLog(@"%d",c);
}
@end


当程序运行到断点处断开时,Xcode左侧的堆栈块如下图所示:



从图中可以看出,程序当前处于激活状态的线程有5个,程序目前断在线程1中的test方法堆栈块中,使用frame info指令可以打印当前堆栈块的信息,示例如下:

(lldb) frame info
frame #0: 0x0000000102497905 BreakPointTest`-[ViewController test](self=0x00007fcd5b413320, _cmd="test") + 37 at ViewController.m:39


在打印的信息中,会有所在的文件名称和函数名称及堆栈块标号和内存地址。

在实际代码调试过程中,程序运行的回溯是一个重要的方法,例如上面的代码例子,虽然现在断点断在test方法中,开发者可能需要在viewDidLoad方法中进行相关调试,例如上面viewDidLoad方法中有一个变量ab,如果想查看ab变量的值,我们就需要将当前选中调试的堆栈块选择为viewDidLoad方法所在的堆栈块,从Xcode左侧导航区可以看到,viewDidLoad方法堆栈块的标号为1,执行如下LLDB指令即可切换:

?

(lldb) frame select 1
frame #1: 0x00000001024978cb BreakPointTest`-[ViewController viewDidLoad](self=0x00007fcd5b413320, _cmd="viewDidLoad") + 91 at ViewController.m:31
28          int a = 0;
29          int b = 1;
30          int c = a+b;
-> 31         NSLog(@"%d",c);
32      }
33      @end


从打印信息可以看到,现在选中的调试堆栈块已经切换到viewDidLoad方法,再使用expression指令时就可以操作这个方法中的相关变量了。

在使用LLDB工具前,遇到这样的情况,我往往会采用打多个断点,一步步追溯代码的运行过程并检查过程中变量的值是否正确,调试起来并不十分方便,如果不小心错过了某个断点,又要重新开始,通过选择调试的frame堆栈块可以十分方便的解决这个问题。

与frame相关的还有一个指令十分有用,下面的指令可以打印出当前堆栈块中所有对象的内容:
(lldb) frame variable
(ViewController *) self = 0x00007fcd5b413320
(SEL) _cmd = "test"
(int) a = 0
(int) b = 1
(int) c = 0


variable后面也可以添加参数名来打印特定对象的内容:

(lldb) frame variable a
(int) a = 0



3.thread线程操作相关指令

上面提到过,程序运行中会有多个激活的线程,每个线程中又有许多堆栈块,frame相关指令用于综合调试各个堆栈块,thread指令则是用于综合调试各个线程。首先Xcode左侧导航区为我们列出的线程堆栈块并不是当前线程中的所有堆栈块,使用如下命令可以打印出当前线程的所有堆栈块:
(lldb) thread backtrace
* thread #1: tid = 0x152f8, 0x0000000102497905 BreakPointTest`-[ViewController test](self=0x00007fcd5b413320, _cmd="test") + 37 at ViewController.m:39, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
* frame #0: 0x0000000102497905 BreakPointTest`-[ViewController test](self=0x00007fcd5b413320, _cmd="test") + 37 at ViewController.m:39
frame #1: 0x00000001024978cb BreakPointTest`-[ViewController viewDidLoad](self=0x00007fcd5b413320, _cmd="viewDidLoad") + 91 at ViewController.m:31
frame #2: 0x0000000103475984 UIKit`-[UIViewController loadViewIfRequired] + 1198
frame #3: 0x0000000103475cd3 UIKit`-[UIViewController view] + 27
frame #4: 0x000000010334bfb4 UIKit`-[UIWindow addRootViewControllerViewIfPossible] + 61
frame #5: 0x000000010334c69d UIKit`-[UIWindow _setHidden:forced:] + 282
frame #6: 0x000000010335e180 UIKit`-[UIWindow makeKeyAndVisible] + 42
frame #7: 0x00000001032d2ed9 UIKit`-[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 4131
frame #8: 0x00000001032d9568 UIKit`-[UIApplication _runWithMainScene:transitionContext:completion:] + 1769
frame #9: 0x00000001032d6714 UIKit`-[UIApplication workspaceDidEndTransaction:] + 188
frame #10: 0x0000000105d438c8 FrontBoardServices`__FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 24
frame #11: 0x0000000105d43741 FrontBoardServices`-[FBSSerialQueue _performNext] + 178
frame #12: 0x0000000105d43aca FrontBoardServices`-[FBSSerialQueue _performNextFromRunLoopSource] + 45
frame #13: 0x0000000102e4a301 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
frame #14: 0x0000000102e4022c CoreFoundation`__CFRunLoopDoSources0 + 556
frame #15: 0x0000000102e3f6e3 CoreFoundation`__CFRunLoopRun + 867
frame #16: 0x0000000102e3f0f8 CoreFoundation`CFRunLoopRunSpecific + 488
frame #17: 0x00000001032d5f21 UIKit`-[UIApplication _run] + 402
frame #18: 0x00000001032daf09 UIKit`UIApplicationMain + 171
frame #19: 0x0000000102497c3f BreakPointTest`main(argc=1, argv=0x00007fff5d768668) + 111 at main.m:14
frame #20: 0x00000001056fe92d libdyld.dylib`start + 1
frame #21: 0x00000001056fe92d libdyld.dylib`start + 1


thread list指令则可以打印出当前所有激活的线程,如下:

(lldb) thread list
Process 1049 stopped
* thread #1: tid = 0x152f8, 0x0000000102497905 BreakPointTest`-[ViewController test](self=0x00007fcd5b413320, _cmd="test") + 37 at ViewController.m:39, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
thread #2: tid = 0x1531b, 0x0000000105a43ee2 libsystem_kernel.dylib`kevent64 + 10, queue = 'com.apple.libdispatch-manager'
thread #3: tid = 0x1531c, 0x0000000105a435e2 libsystem_kernel.dylib`__workq_kernreturn + 10
thread #4: tid = 0x15324, 0x0000000105a435e2 libsystem_kernel.dylib`__workq_kernreturn + 10
thread #5: tid = 0x15328, 0x0000000105a435e2 libsystem_kernel.dylib`__workq_kernreturn + 10


thread info可以打印出当前选中调试的线程的信息:

(lldb) thread info
thread #1: tid = 0x152f8, 0x0000000102497905 BreakPointTest`-[ViewController test](self=0x00007fcd5b413320, _cmd="test") + 37 at ViewController.m:39, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1


同样也可以使用thread select指令来切换选中调试的线程:

(lldb) thread select 2


thread continue指令用于继续执行当前的线程:

(lldb) thread continue
2016-04-24 12:29:54.562 BreakPointTest[1049:86776] 1
Resuming thread 0x152f8 in process 1049
Process 1049 resuming

http://my.oschina.net/u/2340880/blog/665265?fromerr=gv8QhJw7
浅谈LLDB调试器

/article/1225651.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: