分类: iOS平台
2013-03-21 01:06:29
记得刚学ios那会儿,我还不会用debug工具。编程时,最痛苦的莫过于程序莫名其妙的在main函数crash,其中,SIGABRT、EXC_BAD_ACCESS、Assertion failure等情况居多。虽然也看了一些资料,但是一直也没怎么系统的整理过相关知识,故特此整理一下。
虽然有高手可以纯粹用gdb直接调试,但我等菜鸟还是利用一下Xcode的提供的图形界面,保证一下工作效率:P
想必这也是最最基本的步骤,在Xcode导航栏找到断点导航栏,如图添加Exception breakpoint
如果想有针对性的步骤异常,比如Objective-C exception,可以如图添加symbolic breakpoint
有了上述的操作,大部分的crash都会定位到相应的代码位置。
在汇编语言中,会准备好函数要用到的栈和寄存器,而寄存器中包含有传递给函数的参数信息。我们可以利用这部分信息进一步诊断异常。
到目前为止,由于iOS 模拟器是以i386(32位)模式运行在os x系统上,而iOS设备是基于arm结构的处理器, 参考官方文档和这篇文章,我们可以在gdb或lldb中print一些信息
arm | i386 (before prolog) | i386 (after prolog) | ||
---|---|---|---|---|
previous frame | po $lr | - | po *(id*)($ebp) | |
return address / receiver | po $r0 | po *(id*)($esp) | po *(id*)($ebp + 4) | |
1st parameter / SEL | p (SEL)$r1 | p *(SEL*)($esp + 4) | p *(SEL*)($ebp + 8) | |
2nd parameter | po $r2 | po *(id*)($esp + 8) | po *(id*)($ebp + 12) | |
3rd parameter | po $r3 | po *(id*)($esp + 12) | po *(id*)($ebp + 16) | |
... and so on | po *(id*)($sp + 4n) n>=0 | po *(id*)($esp + 4n) n>=4 | po *(id*)($ebp + 4n) n>=5 |
另外,函数返回的结果也会保留在寄存器中,在arm和i386中分别对应($r0)和($eax)。
上述的寄存器($r0或$eax)中,可能的情况(仅涉及我遇到过的情况)是
一个NSException
在gdb或lldb中,尝试一下po [$reg] 或访问相关属性 po [$reg class] 、po [$reg name]、po [$reg reason]
一些状况的描述
以我在给项目添加GHUnit做单元测试的过程中遇到的问题为例,当我自以为已经成功引入GHUnit,尝试运行相应的Target时,Xcode直接在main.m中报错
Assertion failure in int UIApplicationMain(int, char **, NSString *, NSString *)(), /SourceCache/UIKit_Sim/UIKit-1912.3/UIApplication.m:1601
咋看之下,确实没啥头绪,只知道这里有问题,当我在终端输入 po $eax后,便显示出详细的原因:
(unsigned int) $2 = 109471968 Unable to instantiate the UIApplication delegate instance. No class named GHUnitIOSAppDelegate is loaded.
通过这个提示,我才想起来忘记添加编译链接需要的-ObjC -all_load
比较常用的是MallocStackLogging
Record backtraces for each memory block to assist memory debugging tools; if the block is allocated and then immediately freed, both entries are removed from the log, which helps reduce the size of the log
它主要提供的是memory allocator的相关信息,你也可以man malloc查看其他环境变量的应用。
比较常用的是NSZombieEnabled,设为YES,则可以很容易debug给一个已经释放的对象发送消息有关的问题。 原理嘛,就是让deallocated的对象并没有真正的被deallocated,从而让系统能够检测到zombie对象,而执行断点指令。
适用条件:
NSZombieEnabled also affects Core Foundation objects as well as Objective-C objects. However, you'll only be notified when you access a zombie Core Foundation object from Objective-C, not when you access it via a CF API.
Xcode中,如图1,在xcode菜单栏->product->edit scheme,可以添加相应的环境变量
或如图2,也可以在Diagnostics中直接勾选相应的选项
实际开发过程中,EXC_BAD_ACCESS,objc_msgSend相关的crash往往和retain、release不当有关,可以利用上述的两个环境变量检测问题。例如下述代码
NSObject *o = [NSObject new]; NSMutableArray *test = [[NSMutableArray alloc] initWithCapacity:1]; [test release]; [test addObject:o]; [o release];
开启NSZombieEnabled,可以获得信息:
2012-09-04 17:21:55.925 test[1705:11303] *** -[__NSArrayM addObject:]: message sent to deallocated instance 0x741fc40
开启MallocStackLogging,在gdb(不是lldb)终端输入
shell malloc_history 1786 0x741fc40 --1786 "app_pid"; 0x741fc40 "deallocated instance"
就可以获得一份有关malloc的详细信息。
2) http://cocoadev.com/wiki/NSZombie
3) objc_explain_So_you_crashed_in_objc_msgSend
4) http://www.clarkcox.com/blog/2009/02/04/inspecting-obj-c-parameters-in-gdb/
5)