<简书 — 刘小壮> http://www.jianshu.com/p/77660e626874objective-c
在iOS开发调试过程当中以及上线以后,程序常常会出现崩溃的问题。简单的崩溃还好说,复杂的崩溃就须要咱们经过解析Crash文件来分析了,解析Crash文件在iOS开发中是比较常见的。如今网上有不少关于解析崩溃信息的博客,可是大多质量良莠不齐,或者有些细节没有注意到。今天写一篇博客总结一下我对崩溃调试的使用和技巧,若是有哪些错误或遗漏,还请指点,谢谢!😁数组
在iOS
中获取崩溃信息的方式有不少,比较常见的是使用友盟、Bugly等第三方分析工具,或者本身收集崩溃信息并上传公司服务器。下面列举一些咱们经常使用的崩溃分析方式:xcode
Xcode-Devices
中直接查看某个设备的崩溃信息。Crash
崩溃收集服务。苹果给咱们提供了异常处理的类-NSException
。这个类能够建立一个异常对象,也能够经过这个类获取一个异常对象。服务器
这个类中最经常使用的是一个获取崩溃信息的C函数,能够经过这个函数在程序发生异常的时候收集这个异常。微信
// 将系统提供的获取崩溃信息函数写在这个方法中,以保证在程序开始运行就具备获取崩溃信息的功能 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // 将下面C函数的函数地址当作参数 NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler); return YES; } // 设置一个C函数,用来接收崩溃信息 void UncaughtExceptionHandler(NSException *exception){ // 能够经过exception对象获取一些崩溃信息,咱们就是经过这些崩溃信息来进行解析的,例以下面的symbols数组就是咱们的崩溃堆栈。 NSArray *symbols = [exception callStackSymbols]; NSString *reason = [exception reason]; NSString *name = [exception name]; }
咱们也能够经过下面方法获取崩溃统计的函数指针:app
NSUncaughtExceptionHandler *handler = NSGetUncaughtExceptionHandler();
进行崩溃分析,首先要弄懂一个概念,就是符号集。函数
ipa
文件进行打包以后,和.app
文件同级的后缀名为.dSYM
的文件,这个文件必须使用Xcode进行打包才有。dSYM
文件都有一个UUID
,和.app
文件中的UUID
对应,表明着是一个应用。而.dSYM
文件中每一条崩溃信息也有一个单独的UUID
,用来和程序的UUID
进行校对。.dSYM
文件解析出的崩溃信息都不能保证准确。.Crash
文件能够准确知道具体的崩溃信息。咱们每次Archive
一个包以后,都会随之生成一个dSYM
文件。每次发布一个版本,咱们都须要备份这个文件,以方便之后的调试。进行崩溃信息符号化的时候,必须使用当前应用打包的电脑所生成的dSYM
文件,其余电脑生成的文件可能会致使分析不许确的问题。工具
当程序崩溃的时候,能够得到到崩溃的错误堆栈,错误堆栈都是0x
开头的16进制地址,须要使用Xcode自带的symbolicatecrash
工具来将.Crash
和.dSYM
文件进行符号化,就能够获得详细崩溃的信息。优化
在崩溃分析时,dSYM
文件是解析App堆栈的,若是是系统库则须要对应的符号化文件。不少解析不出来系统堆栈的问题,就是由于没有系统的符号化文件。符号化文件就在Xcode的资源库里,能够从下面的目录找到符号化文件。网站
/Users/liuxiaozhuang/Library/Developer/Xcode/iOS DeviceSupport
符号化文件对版本和Architectures
都有要求,例如崩溃的系统是8.4.1系统 arm64的指令集,就须要对应的系统符号化文件8.4.1 (12H321)
。不然仍是不能解析出系统崩溃信息,或者解析出来也是错的。若是在iOS DeviceSupport
文件中没有找到对应的符号化文件,须要找一个对应的才能够解析。
符号化文件的指令集通常都是兼容低版本的,例如8.4.1 (12H321)
的指令集会有arm64
、armv7s
、armv7
三个版本,若是苹果没有明确说明某个iOS
版本不兼容32位处理器,那么指令集都会兼容的。
搜集系统符号化文件很是困难,我在国外也没找到搜集全的网站。可是国内有一个很是敬业的iOS
同行,搜集总结了iOS7
- iOS10
的不少符号化文件,并且做者对文件作了优化,下载下来的文件也不会很大,很是感谢他!
网盘连接: https://pan.baidu.com/s/1nvfi4g5 密码: 79m8
这里的符号化文件也并不全,若是遇到没有找到的符号化文件,到崩溃日志中找到OS Version:iOS 8.4.1 (12H321)
,把后面的系统build
版本放在Google
搜一下试试,若是搜不到就不太好解决了。
经过Mac自带的命令行工具解析Crash
文件须要具有四个文件
symbolicatecrash
,Xcode自带的崩溃分析工具,使用这个工具能够更精确的定位崩溃所在的位置,将0x开头的地址替换为响应的代码和具体行数。dSYM
文件。Crash
文件。iOS
系统和指令集的符号化文件。我在解析崩溃信息的时候,通常会在桌面上单独创建一个Crash
文件夹,而后将.Crash
、.dSYM
、symbolicatecrash
放在这个文件夹中,这样进入这个文件夹下,直接一行命令就解决了。下次再作崩溃分析的时候,换一下对应的文件名就能够解析。
symbolicatecrash
在Xcode8及以后是下面的路径。
/Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash
Xcode8以前symbolicatecrash
在下面的路径中。
/Applications/Xcode.app/Contents/SharedFrameworks/DTDeviceKitBase.framework/Versions/A/Resources/symbolicatecrash
而后Window -> Organizer -> Archives
中,选中archive
的版本右击,选择Show in Finder
就能够获取dSYM
文件了。
将.Crash
、.dSYM
、symbolicatecrash
三个文件都放在咱们在桌面创建的Crash
文件夹中。
开启命令行工具,进入崩溃文件夹中
cd /Users/username/Desktop/崩溃文件夹
使用命令解析Crash文件
./symbolicatecrash ./*.crash ./*.app.dSYM > symbol.crash
若是上面命令不成功,使用命令检查一下环境变量
xcode-select -print-path
返回结果:
/Applications/Xcode.app/Contents/Developer/
若是不是上面的结果,须要使用下面命令设置一下导出的环境变量,而后重复上面解析的操做。
export DEVELOPER_DIR=/Applications/XCode.app/Contents/Developer
解析完成后会生成一个新的.Crash
文件,这个文件中就是崩溃详细信息。图中红色标注的部分就是咱们代码崩溃的部分。
注意,如下状况不会有崩溃信息产生:
bad memory
错误)有时候经过symbolicatecrash
并不能解析出来崩溃信息,或者App自身的堆栈能解析出来,可是系统堆栈解析不出来,这在解析过程当中是常常遇到的。
能够经过atos
命令进行逐行解析,经过这个命令能够解析指定的某一行堆栈。而且这个命令是不须要dSYM
的,能够在没有dSYM
的状况下使用。
在讲atos
以前先对一些基本知识了解一下。
architecture
是指令集类型,例如arm64
、armv7s
之类的。能够在Xcode的Build Settings
中查看,也能够执行下面命令查看二进制文件支持的指令集。
lipo -info 二进制名
会输出下面信息,根据崩溃设备机型选择对应的指令集类型。
Architectures in the fat file: 二进制名 are: armv7 arm64
loadAddress
是Binary Images
中对应的二进制首地址,每一个动态库和二进制都有本身的首地址, 对应崩溃堆栈的首地址。
在每条崩溃堆栈中,都有对应的首地址和地址偏移。例如Zeus这个二进制对应的就是Binary Images
的Zeus arm64
的首地址,根据这个首地址加上后面的地址偏移,就是函数所在的函数地址。
以上面崩溃堆栈为例,atos
命令格式
atos -arch arm64 -o Zeus -l 0x10063000 0x00000001009795d4
经过这个命令能够解析出具体的崩溃信息
-[FocusView updateImageViews] (in Zeus) (FocusView.m:119)
若是是系统堆栈,则须要把二进制名Zeus换成对应的framework
或dylib
名。拿Foundation
举例来讲,为了命令看起来简单点,我先把8.4.1 (12H321)
中的Foundation.framework
拷贝到和Zeus统计目录下,而后执行下面命令便可,dylib
同理。
atos -arch arm64 Foundation.framework/Foundation -l 0x1830bc000 0x0000000183109f94
而后就输出了Foundation
内部的堆栈信息。
-[NSURL(NSURLPathUtilities) URLByAppendingPathComponent:] (in Foundation) + 144
1.须要注意的是,文中提到的dSYM
、符号化文件、App
二进制,都须要和当前崩溃设备的App
版本、iOS
系统版本、Architectures
指令集对应,地址偏移也不要写错,不然会致使解析出来的堆栈不正确。
atos -arch arm64 -o Zeus -l 0x10063000 0x000000010092b52a
例如上面命令解析的堆栈以下
-[DetailHeaderView layoutSubviews] (in Zeus) (DetailHeaderView.m:34)
可是我用其余版本的二进制解析,一样的命令,解析结果可能就不同。
-[ShareManager manager] (in Zeus) (ShareManager.m:194)
因此崩溃信息中最上面的一些参数就很重要,包含了咱们须要的iOS
版本、App build
号、指令集类型等信息。
2.不管是App
的堆栈仍是系统堆栈,若是使用系统自带的symbolicatecrash
和atos
两种方式不能解析,都是由于没有对应的dSYM
或二进制、符号化文件。
若是手里有对应的设备,第一次插入电脑后链接Xcode,会显示Processing symbol files
。这个过程就是拷贝符号化文件到iOS DeviceSupport
目录的过程,拷贝完成后就能够拿到符号化文件了。
除了上面的系统分析工具来进行分析,也能够将发生崩溃的设备链接Xcode,选择window-> devices -> 选择本身的手机 -> view device logs
就能够查看手机上全部的崩溃信息了。
只要手机上的应用是这台电脑安装打包的,这样的崩溃信息系统已经为咱们符号化好了,只须要进去以后等一会就行(不要相信这里面的进度刷新,并不许确,亲测….)。若是仍是没有符号化完毕 ,能够选择文件,而后右击选择Re-Sysbomlicate
就能够。
若是是使用其余电脑进行的打包,能够在这里面将Crash
文件导出,本身经过命令行的方式进行解析。
如今有不少第三方工具均可以进行崩溃统计分析,使用比较多的是友盟崩溃统计,友盟崩溃统计被集成在友盟SDK中,具体用法能够直接看官方文档,这里很少作叙述。
可是我并不推荐友盟,而是推荐一个很是强大的崩溃统计工具—Bugly,我公司项目也在使用Bugly。它最大的优点在于,能够直接将崩溃信息分析出来而且作好分类和汇总,并且一些咱们本身分析不出来或很难分析出来的崩溃,Bugly都能分析出来。固然须要提早上传dSYM
到Bugly后台,不然可能会致使一些崩溃分析不出来。
这是Bugly统计的崩溃分布,能够选择版本、时间之类的。并且Bugly不只限于崩溃统计,还有卡顿分析和错误分析,我感受这两个也比较实用。相对友盟或者其余崩溃分析工具,Bugly是一个轻量级的崩溃统计工具。
出错堆栈Bugly会帮咱们解析好,而且会根据不一样状况给一些解决建议,这个我就不截图了。
Bugly有一个页面追踪功能,这是我认为很是有用的一个功能。这个功能会将用户在不一样页面之间跳转的流程记录下来。这样对于复现bug
是颇有用的,能够根据用户页面跳转推测出用户大概操做流程,根据这个流程复现bug
。
Bugly还有日报功能,能够天天汇总一篇日报,而且发到团队每一个人的邮箱和微信号上。我认为这个功能很是实用,天天大概八九点上班路上,掏出手机看一下微信上Bugly日报,想一下项目哪可能有问题,到公司正好解决bug
。
苹果在Xcode中为咱们集成了崩溃统计功能,在Window -> Organizer -> Crashes
中能够看到。
苹果自带的崩溃统计工具并不推荐用,若是想要使用这个功能,须要用户在iPhone中进行设置设置 -> 隐私 -> 诊断与用量 -> 诊断与用量数据(iOS8如下在通用中设置)
选择自动发送,并与开发者共享。
然而不少人并不想和开发者共享数据,或者不设置这个选项,那这样就收集不到这部分的崩溃。
崩溃收集统计函数应该只进行一次调用,若是用第三方的话也最好只用一个第三方,这样获取崩溃统计信息的途径也是惟一的。第三方统计工具并非用的越多越好,使用多个崩溃收集第三方会致使NSSetUncaughtExceptionHandler()
函数指针的恶意覆盖,致使有些第三方不能收到崩溃信息。
如今不少第三方崩溃收集工具为了确保本身能最大可能的收集到崩溃信息,会对NSSetUncaughtExceptionHandler()
函数指针的恶意覆盖。由于这个函数是将函数地址当作参数传递,因此只要重复调用就会被覆盖,这样就不能保证崩溃收集的稳定性。