IOS出错调试技术

以下文字源于: http://blog.csdn.net/cococoolwhj/article/details/7459064 ,大家都看看,我是摘选了我觉得有用的部分,后面结合我们的情况写了点。

iPhone应用程序的CrashReporter机能

苹果在固件2.0发布的时候,其中一项特性是向iPhone开发者通过邮件发送错误报告,以便开发人员更好的了解自己的软件运行状况。不过不少开发者报告此服务有时无法获取到~/Library/Logs/CrashReporter/MobileDevice directory的错误信息。

现在苹果提供了一种更简单的方法,使iPhone开发者可以通过iTunes更容易的查看崩溃报告。具体方法使进入iTunesConnect(在进入之前确定你有iPhone开发者帐号),点击管理你应用程序,之后就可以看到用户崩溃日志了。

这里我介绍一下从设备中取出CrashLog,并解析的方法。
CrashLog的位置

程序Crash之后,将设备与PC中的iTunes连接,设备中的CrashLog文件也将一并同步到PC中。其中位置如下;

Mac:
~/Library/Logs/CrashReporter/MobileDevice

Windows Vista/7:
C:\Users\<user_name>\AppData\Roaming\Apple computer\Logs\CrashReporter/MobileDevice

Windows XP:
C:\Documents and Settings\<user_name>\Application Data\Apple computer\Logs\CrashReporter

在这些目录下,会有具体设备的目录,其下就是许多*.crash的文件。

比如程序TestEditor在iPhone1设备上的crashLog如下:

~Library/Logs/CrashReporter/MobileDevice/iPhone1/TestEditor_2010-09-23-454678_iPhone1.crash

Incident Identifier: CAF9ED40-2D59-45EA-96B0-52BDA1115E9F
CrashReporter Key:   30af939d26f6ecc5f0d08653b2aaf47933ad8b8e
Process:         TestEditor [12506]
Path:            /var/mobile/Applications/60ACEDBC-600E-42AF-9252-42E32188A044/TestEditor.app/TestEditor
Identifier:      TestEditor
Version:         ??? (???)
Code Type:       ARM (Native)
Parent Process:  launchd [1]

Date/Time:       2010-09-23 11:25:56.357 +0900
OS Version:      iPhone OS 3.1.3 (7E18)
Report Version:  104

Exception Type:  EXC_BAD_ACCESS (SIGBUS)
Exception Codes: KERN_PROTECTION_FAILURE at 0x00000059
Crashed Thread:  0

Thread 0 Crashed:
0   UIKit                           0x332b98d8 0x331b2000 + 1079512
1   UIKit                           0x3321d1a8 0x331b2000 + 438696
2   UIKit                           0x3321d028 0x331b2000 + 438312
3   UIKit                           0x332b9628 0x331b2000 + 1078824
4   UIKit                           0x33209d70 0x331b2000 + 359792
5   UIKit                           0x33209c08 0x331b2000 + 359432
6   QuartzCore                      0x324cc05c 0x324ae000 + 122972
7   QuartzCore                      0x324cbe64 0x324ae000 + 122468
8   CoreFoundation                  0x3244f4bc 0x323f8000 + 357564
9   CoreFoundation                  0x3244ec18 0x323f8000 + 355352
10  GraphicsServices                0x342e91c0 0x342e5000 + 16832
11  UIKit                           0x331b5c28 0x331b2000 + 15400
12  UIKit                           0x331b4228 0x331b2000 + 8744
13  TestEditor                      0x00002c3a 0x1000 + 7226
14  TestEditor                      0x00002c04 0x1000 + 7172
... (以下略)

虽然我们看到了出为题时的堆栈信息,但是因为没有符号信息,仍然不知道到底哪里出问题了...

.dSYM文件

编译调试相关的符号信息都被包含在编译时的 xxxx.app.dSYM 文件当中,所以我们在发布程序前将它们保存起来,调试Crash问题的时候会很有用。

首先,我们来找到该文件。

用Xcode编译的程序,在其编译目录下都会生成 [程序名].app.dSMY 文件,比如 Xcode 4 的编译目录缺省的是

~Library/Developer/Xcode/DerivedData
  1. 在改目录下搜寻编译后的.dSMY文件
    $ cd ~/Library/Developer/Xcode/DerivedData
    $ find . -name '*.dSYM'
    

另外,我们也可以通过 Xcode的Preferences... -> Locations -> Locations 的Derived Data来确认该目录的位置。

上面例子中的程序,我们就找到了其位置是

~/Library/Developer/Xcode/DerivedData/TestEditor-aahmlrjpobenlsdvhjppcfqhogru/ArchiveIntermediates/TestEditor/BuildProductsPath/Release-iphoneos/TestEditor.app.dSYM

※ 大家每次像App Store发布自己程序的时候都记着保存该文件哦,要不然出现Crash的时候,就无从下手了。

解决符号问题

接下来,我们再来介绍一下使用.dSYM文件来恢复程序符号的方法。

首先,使用一个Xcode提供的叫做 symbolicatecrash 的小工具,它可以实现我们在CrashLog中添加符号信息的机能。该文件位于下面的位置,为方便起见,可以把它拷贝到系统默认路径下。

Xcode4.3.1的位置好像在这 

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKit.framework/Versions/A/Resources/symbolicatecrash

/Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKit.framework/Versions/A/Resources/symbolicatecrash

$ sudo cp /Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKit.framework/Versions/A/Resources/symbolicatecrash /usr/local/bin


使用下面的命令,可以在终端输出有符号信息的CrashLog
$ symbolicatecrash [CrashLog file] [dSYM file]

$ symbolicatecrash TestEditor_2010-09-23-454678_iPhone1.crash TestEditor.app.dSYM
Thread 0 Crashed:
0   UIKit                           0x332b98d8 -[UIWindowController transitionViewDidComplete:fromView:toView:] + 668
1   UIKit                           0x3321d1a8 -[UITransitionView notifyDidCompleteTransition:] + 160
2   UIKit                           0x3321d028 -[UITransitionView _didCompleteTransition:] + 704
3   UIKit                           0x332b9628 -[UITransitionView _transitionDidStop:finished:] + 44
4   UIKit                           0x33209d70 -[UIViewAnimationState sendDelegateAnimationDidStop:finished:] + 284
5   UIKit                           0x33209c08 -[UIViewAnimationState animationDidStop:finished:] + 60
6   QuartzCore                      0x324cc05c _ZL23run_animation_callbacksdPv + 440
7   QuartzCore                      0x324cbe64 _ZN2CAL14timer_callbackEP16__CFRunLoopTimerPv + 156
8   CoreFoundation                  0x3244f4bc CFRunLoopRunSpecific + 2192
9   CoreFoundation                  0x3244ec18 CFRunLoopRunInMode + 44
10  GraphicsServices                0x342e91c0 GSEventRunModal + 188
11  UIKit                           0x331b5c28 -[UIApplication _run] + 552
12  UIKit                           0x331b4228 UIApplicationMain + 960
13  TestEditor                      0x00002c3a main (main.m:14)
14  TestEditor                      0x00002c04 0x1000 + 7172


由此,我们可以具体定位程序中出问题的地方。

用StackTrace取得崩溃时的日志

异常处理机制

对于系统Crash而引起的程序异常退出,可以通过UncaughtExceptionHandler机制捕获;也就是说在程序中catch以外的内容,被系统自带的错误处理而捕获。
这个我们自己可以些程序来获取,但是太麻烦;幸运的是,umeng 可以捕获发布版程序的崩溃日志,我们可以根据umeng的崩溃日志来获取程序到底是在哪里出错了。
举个例子,如下是umeng捕获的出错日志:

                -[__NSCFBoolean isEqualToString:]: unrecognized selector sent to instance 0x3f3f4a18
(null)
(
    0   CoreFoundation                      0x353bf8a7 __exceptionPreprocess + 186
    1   libobjc.A.dylib                     0x37766259 objc_exception_throw + 32
    2   CoreFoundation                      0x353c2a9b -[NSObject doesNotRecognizeSelector:] + 174
    3   CoreFoundation                      0x353c1915 ___forwarding___ + 300
    4   CoreFoundation                      0x3531c650 _CF_forwarding_prep_0 + 48
    5   Drug                                0x000110f3 Drug + 65779
    6   Drug                                0x0000f43f Drug + 58431
    7   Drug                                0x000459c3 Drug + 281027
    8   Drug                                0x00045827 Drug + 280615
    9   Drug                                0x0000f0eb Drug + 57579
    10  UIKit                               0x32e35b95 -[UIViewController _setViewAppearState:isAnimating:] + 144
    11  UIKit                               0x32e8f8af -[UIViewController beginAppearanceTransition:animated:] + 190
    12  UIKit                               0x32e38913 -[UINavigationController _startTransition:fromViewController:toViewController:] + 838
    13  UIKit                               0x32e38503 -[UINavigationController _startDeferredTransitionIfNeeded] + 250
    14  UIKit                               0x32e2caff -[UINavigationController pushViewController:transition:forceImmediate:] + 806
    15  UIKit                               0x32e2c7d5 -[UINavigationController pushViewController:animated:] + 36
    16  Drug                                0x0000ddcb Drug + 52683
    17  UIKit                               0x32ea593d -[UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:] + 944
    18  UIKit                               0x32f1f627 -[UITableView _userSelectRowAtPendingSelectionIndexPath:] + 158
    19  Foundation                          0x34ed9933 __NSFireDelayedPerform + 414
    20  CoreFoundation                      0x35393a33 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 14
    21  CoreFoundation                      0x35393699 __CFRunLoopDoTimer + 364
    22  CoreFoundation                      0x3539226f __CFRunLoopRun + 1206
    23  CoreFoundation                      0x353154a5 CFRunLoopRunSpecific + 300
    24  CoreFoundation                      0x3531536d CFRunLoopRunInMode + 104
    25  GraphicsServices                    0x36fb1439 GSEventRunModal + 136
    26  UIKit                               0x32e21cd5 UIApplicationMain + 1080
    27  Drug                                0x000035d7 Drug + 9687
    28  Drug                                0x0000353c Drug + 9532
)


其中第五行就是在我们的程序中最后出错的地方,我们在这里没法看出来到底那行出错了,注意观察 “5 Drug 0x000110f3 Drug + 65779”
其中Drug指的是模块,就是我们的药品指南的程序中出错的,其中的0x000110f3 指的是出错代码在我们程序的0x000110f3 地址处,我们所需要做的就是查出来0x000110f3到底是那个文件的那一行。
umeng也告诉我了,这个出错是发布版 8 出的错误,我把发布版 8 的dSym文件放在我本机的 /Users/user/Library/Developer/Xcode/Archives/2012-08-24/Drug 12-8-24 下午2.55.xcarchive/dSYMs/Drug.app.dSYM/Contents/Resources/DWARF 目录下,那我就需要用命令行进入这个目录,然后需要用gdb来加载这个dSym文件,然后查找0x000110f3到底属于源代码的那个文件以及行号。
插曲:gdb是什么,自己去学习下,如果你直接使用gdb,肯定不行,因为我们的代码是在iphone上运行的,pc机是x86架构,iphone/android手机都是arm架构,x86的gdb没法用,但是我们想想xcode自己能调试iphone的arm程序,它肯定有这种在x86上运行但是可以调试arm的gdb,实际上我是在我本机的 /Applications/Xcode.app 目录 通过执行 find . -name "gdb" 来找到好多gdb,然后最后确定了一个我们可以用的gdb;看看我的查找过程:
$ find . -name "gdb" 
./Contents/Developer/Documentation/DocSets/com.apple.ADC_Reference_Library.DeveloperTools.docset/Contents/Resources/Documents/documentation/DeveloperTools/gdb
./Contents/Developer/Documentation/DocSets/com.apple.ADC_Reference_Library.DeveloperTools.docset/Contents/Resources/Documents/documentation/DeveloperTools/gdb/gdb
./Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/gdb
./Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/libexec/gdb
./Contents/Developer/usr/bin/gdb
./Contents/Developer/usr/libexec/gdb
./Contents/Developer/usr/share/gdb

结果有好几条,其实第三条就是我们要找的,绝对路径就是:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/gdb

找到后我们来看看怎么用,回到刚才的思路,在dSym目录下执行如下gdb命令,哪个目录,就是:

 /Users/user/Library/Developer/Xcode/Archives/2012-08-24/Drug 12-8-24 下午2.55.xcarchive/dSYMs/Drug.app.dSYM/Contents/Resources/DWARF

gdb命令如下执行:

$ /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/gdb Drug

GNU gdb 6.3.50-20050815 (Apple version gdb-1708) (Mon Oct 17 16:52:01 UTC 2011)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "--host=i386-apple-darwin --target=arm-apple-darwin"...
(gdb) 

如果输出如上,我们就成功了一半,然后执行:

(gdb) info line *0x000110f3
Line 562 of "/Volumes/Work/source/xywy/trunks/drug/ios/Drug/Drug/DrugSummaryInfoViewController.m" 
   starts at address 0x110ce <-[DrugSummaryInfoViewController tranformDic:]+1558> and ends at 0x110f8 <-[DrugSummaryInfoViewController tranformDic:]+1600>.

查询结果显示0x000110f3 就是DrugSummaryInfoViewController.m文件中的第562行,打开文件看看源代码

 562:  if ([data.drugElements isEqualToString:@""]) {
 563:      [_theArray removeObject:@"成分"];

再结合umeng给的错误信息:[__NSCFBoolean isEqualToString:]: unrecognized selector sent to instance 0x3f3f4a18
很明显,这里的data.drugElements 根本不是一个字符串,原来它是一个NSCFBoolean对象,什么原因先不说,至少我们知道哪里出错了。
继续分析为啥出错,另外确保我们定位是对的,怎么分析,就是把崩溃日志的第6 7 8 9行都定位出来看看:
(gdb) info line *0x0000f43f     
Line 207 of "/Volumes/Work/source/xywy/trunks/drug/ios/Drug/Drug/DrugSummaryInfoViewController.m" 
   starts at address 0xf430 <-[DrugSummaryInfoViewController prepareJsonFileOk:]+380>
   and ends at 0xf44a <-[DrugSummaryInfoViewController prepareJsonFileOk:]+406>.
(gdb) info line *0x000459c3
Line 124 of "/Volumes/Work/source/xywy/trunks/drug/ios/Drug/Drug/model/DrugIntroductionCache.m" 
   starts at address 0x459c2 <-[DrugIntroductionCache sendNotice]+98> and ends at 0x459d6 <-[DrugIntroductionCache sendNotice]+118>.
(gdb) info line *0x00045827
Line 80 of "/Volumes/Work/source/xywy/trunks/drug/ios/Drug/Drug/model/DrugIntroductionCache.m" 
   starts at address 0x45814 <-[DrugIntroductionCache prepareDataById:]+104> and ends at 0x45828 <-[DrugIntroductionCache prepareDataById:]+124>.
(gdb) info line *0x0000f0eb
Line 153 of "/Volumes/Work/source/xywy/trunks/drug/ios/Drug/Drug/DrugSummaryInfoViewController.m" 
   starts at address 0xf0c8 <-[DrugSummaryInfoViewController viewWillAppear:]+140> and ends at 0xf0f0 <-[DrugSummaryInfoViewController viewWillAppear:]+180>.
(gdb)

说明这个错误是从DrugIntroductionCache里调用prepareJsonFileOk,然后在tranformDic时候出错的,出错的原因估计是json格式不对,然后SBJson解析的时候把elements元素解析成boolean变量,而我们获取elements的时候没有进行类型判断,导致后来做比较(isEqualToString)的时候出错了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值