Chinaunix首页 | 论坛 | 博客
  • 博客访问: 188288
  • 博文数量: 60
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 385
  • 用 户 组: 普通用户
  • 注册时间: 2013-02-19 21:43
个人简介

readonly

文章分类

全部博文(60)

文章存档

2013年(60)

我的朋友

分类: iOS平台

2013-03-21 01:06:29

前言

记得刚学ios那会儿,我还不会用debug工具。编程时,最痛苦的莫过于程序莫名其妙的在main函数crash,其中,SIGABRT、EXC_BAD_ACCESS、Assertion failure等情况居多。虽然也看了一些资料,但是一直也没怎么系统的整理过相关知识,故特此整理一下。

正文

虽然有高手可以纯粹用gdb直接调试,但我等菜鸟还是利用一下Xcode的提供的图形界面,保证一下工作效率:P

1 异常捕捉

想必这也是最最基本的步骤,在Xcode导航栏找到断点导航栏,如图添加Exception breakpoint

alt exception

如果想有针对性的步骤异常,比如Objective-C exception,可以如图添加symbolic breakpoint

alt symbolic

有了上述的操作,大部分的crash都会定位到相应的代码位置。

2 巧用寄存器参数

在汇编语言中,会准备好函数要用到的栈和寄存器,而寄存器中包含有传递给函数的参数信息。我们可以利用这部分信息进一步诊断异常。

到目前为止,由于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

3 系统环境变量使用

1) BSD提供的debug辅助功能

比较常用的是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查看其他环境变量的应用。

2) obj-c Foundation提供的debug辅助功能

比较常用的是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.

3)实践应用

Xcode中,如图1,在xcode菜单栏->product->edit scheme,可以添加相应的环境变量

alt env

或如图2,也可以在Diagnostics中直接勾选相应的选项

alt 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的详细信息。

参考资料

1) iOS Debugging Magic

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) 

阅读(2136) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~