iOS奔溃分析技巧-crash日志符号化
Posted 乌戈勒
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS奔溃分析技巧-crash日志符号化相关的知识,希望对你有一定的参考价值。
前言
ios开发需要不停发版本,开发者要面临线上各种版本的奔溃日志(crash log),解决奔溃问题是移动开发者最日常的工作之一.
在实际的项目开发中,崩溃问题,依赖xcode,依赖于系统记录的崩溃日志或错误堆栈,在本地开发调试阶段,是没有问题的。
如果在发布的线上版本出现崩溃问题,开发者是无法即时准确的取得错误堆栈的,需要获取到crash日志,进行相应处理。
简单的崩溃还好说,复杂的崩溃就需要我们通过解析Crash文件来分析了,解析Crash文件在iOS开发中是比较常见的。对iOS的crash日志进行分析,做符号化,才能准确定位到发生崩溃的原因。
一、crash日志的结构
crash日志主要由6部分组成:
1、进程信息
2、基本信息
3、异常信息
4、线程回溯
5、crash调用堆栈(全是地址信息,需要使用符号表转成可读的)
6、动态库信息(第5部分依赖的库)
iOS的符号化也主要是根据第5和第6部分进行,只有把第5部分后面的二进制的地址信息映射成代码信息,才能定位到发生crash的原因。
对crash日志进行符号化之后的结果如下:
二、crash日志的获取方式
在iOS中获取崩溃信息的方式有很多,比较常见的是使用友盟、百度等第三方分析工具,或者自己收集崩溃信息并上传公司服务器。
下面列举一些我们常用的崩溃分析方式:
1、使用友盟、百度等第三方崩溃统计工具。
2、自己实现应用内崩溃收集,并上传服务器。
3、Xcode-Devices中直接查看某个设备的崩溃信息。
4、使用苹果提供的Crash崩溃收集服务。(少用)
1、自己实现应用内奔溃收集
苹果给我们提供了异常处理的类,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];
//我们也可以通过下面方法获取崩溃统计的函数指针:
NSUncaughtExceptionHandler *handler = NSGetUncaughtExceptionHandler();
2、通过Xcode查看设备crash信息
如果是我们自己直接使用手机连接崩溃或者崩溃之后连接手机,选择window-> devices -> 选择自己的手机 -> view device logs 就可以查看我们的崩溃信息了。
特别说明:
1、如果手机上app是这台电脑安装的,或者打包的,那么app奔溃之后,通过这台电脑xcode导出的crash日志信息,系统已经为我们符号化好了;
如果还是没有符号化完毕,我们选择文件,然后右击选择Re-Sysbomlicate就可以。
2、如果手机上app是其它电脑安装或者打包的,那么app奔溃之后,通过这台电脑xcode导出的crash日志信息,是还没有符号化的,需要我们导出之后,通过symbolicatecrash命令和dSYM符号表进行符号化处理,才能准确定位到crash的位置原因。
因为当前电脑的xcode编译打包的app,会在.app目录下,生成对应的dSYM文件,通过xcode查看的crash log系统已经通过对应的dSYM符号表帮我们符号化了。而在其它电脑,并这个设备crash文件相对应的dSYM符号表,必须要crash文件对应的.app文件的uuid和对应的dSYM文件的uuid一致,才能进行符号化。
(实际上Xcode的Organizer内置了symbolicatecrash工具,所以开发者才可以直接看到符号化的错误日志。)
3、通过bugly上传对应dSYM符号表
可以参考下面这篇文章
Bugly iOS 符号表手动配置详细教程
如果拿到的是未经过符号化的crash文件,就需要用到符号表,那么符号表是什么呢?有什么作用?如何生成?怎么使用符号表?
三、crash日志符号化的过程
1、什么是符号表
符号表就是指在Xcode项目编译后,在编译生成的二进制文件.app的同级目录下生成的同名的.dSYM文件。
.dSYM文件其实是一个目录,在子目录中包含了一个16进制的保存函数地址映射信息的中转文件,所有Debug的symbols都在这个文件中(包括文件名、函数名、行号等),所以也称之为调试符号信息文件。
一般地,Xcode项目每次编译后,都会生成一个新的.dSYM文件。
因此,App的每一个发布版本,都需要备份一个对应的.dSYM文件,以便后续调试定位问题。
项目每一次编译后,.app和.dSYM成对出现,并且二者有相同的UUID值,以标识是同一次编译的结果。
UUID值可以使用dwarfdump —uuid来检查:
// 查看 .dSYM文件的uuid
dwarfdump --uuid **.app.dSYM (**为你app)
//终端输出结果如下:
bogon:symbolicateCrash wugl$ dwarfdump --uuid myApp3.app.dSYM/
UUID: 427C6848-EFF8-3F66-A29E-C0DE376FBDD7 (armv7) myApp3.app.dSYM/Contents/Resources/DWARF/myApp
// 查看 .app文件的uuid
cd **.app(可以看到.app目录中有一个**文件)
dwarfdump --uuid ** (**为你app)
// 终端输出结果如下:
bogon:myApp.app wugl$ dwarfdump --uuid myApp
UUID: 427C6848-EFF8-3F66-A29E-C0DE376FBDD7 (armv7) myApp
2、符号表有什么作用
在Xcode开发调试App时,一旦遇到崩溃问题,开发者可以直接使用Xcode的调试器定位分析。
但如果App发布上线,开发者不可能进行调试,只能通过分析系统记录的崩溃日志来定位问题,在这份崩溃日志文件中,会指出App出错的函数内存地址,而这些函数地址是可以在.dSYM文件中找到具体的文件名、函数名和行号信息的,这正是符号表的重要作用所在,也是为什么要进行符号表进行管理,并纪录这是哪个版本的符号表。
Xcode的Organizer查看崩溃日志时,也自动根据本地存储的.dSYM文件进行了符号化的操作。
并且,崩溃日志也有UUID信息,这个UUID和对应的.dSYM文件是一致的,即只有当三者的UUID一致时,才可以正确的把函数地址符号化。
3、符号表怎么获取
只要获取到.ipa包,就可以获取到对应的.app和.dSYM文件。
对于我们平时开发调试中,想要获取.dSYM文件,可能会遇到下面问题。
xcode release打包版本,Xcode项目默认的配置是会在编译后生成.dSYM,开发者无需额外修改配置。但是有时候,xcode build版本无法生成 dSYM 文件,那么怎么解决呢?
1、打开 xcode-BuildSettings;
2、找到EBUG_INFORMATION_FORMAT这一项;
3、通过查看项目的配置文件,我们可以发现只有 Release 模式配置了 dwarf-with-dsym;
4、我们需要做的是配置 build模式也是dwarf-with-dsym。
通过下图步骤可以获取.app和.dSYM文件
4、符号表怎么使用?
符号表的作用是把崩溃中的函数地址解析为函数名等信息。如果开发者能够获取到崩溃的函数地址信息,就可以利用符号表分析出具体的出错位置。
1、大部分情况下,开发者能获取到的都是错误地址堆栈,需要利用符号表进一步符号化才能分析定位问题。
2、部分情况下,开发者也可以利用backtrace看到符号化堆栈,可以大概定位出错的函数、但却不知道具体的位置。通过利用符号表信息,也是可以进一步得到具体的出错位置的。
那么我们怎么通过符号表来对crash文件进行符号化呢?
四、如何使用symbolicatecrash分析崩溃日志
symbolicatecrash是一个将堆栈地址符号化的脚本。
使用Xcode自带的symbolicatecrash工具来将.Crash和.dSYM文件进行符号化,就可以得到详细崩溃的信息。
通过Mac自带的命令行工具解析Crash文件需要具备三个文件:
1、symbolicatecrash,Xcode自带的崩溃分析工具,使用这个工具可以更精确的定位崩溃所在的位置,将0x开头的地址替换为响应的代码和具体行数。
2、我们打包时产生的dSYM文件。
3、崩溃时产生的Crash文件,例如:*.crash。
解析崩溃信息的时候,首先在桌面上建立一个Crash文件夹,然后将.Crash、.dSYM、symbolicatecrash放在这个文件夹中,这样进入这个文件夹下,直接一行命令就解决了。
./symbolicatecrash XX.crash XX.app.dSYM > xx.sym.crash // 如果输入.dSYM参数,将只解析系统库对应的符号
1、找到symbolicatecrash
find /Applications/Xcode.app -name symbolicatecrash -type f
稍等一会就会有路径输出,这个路径就是symbolicatecrash的路径。用命令将symbolicatecrash拷贝到桌面的crash文件夹里面,与.app和.app.dSYM放一起。
cp /Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash /Users/你的电脑名称/Desktop/crash
2、执行symbolicatecrash
// 打开终端用命令切换到桌面的crash目录下:
cd /Users/你的电脑名称/Desktop/crash
// 执行命令
./symbolicatecrash ./xx.crash ./xx.app.dSYM > xx.dsym.crash
// 这时候终端有可能会出现:
Error: "DEVELOPER_DIR" is not defined at ./symbolicatecrash line 69.
// 输入命令:
export DEVELOPER_DIR="/Applications/XCode.app/Contents/Developer"
// 将终端完成以后,在crash文件夹里面会多出一个文件Control_symbol.crash:这个就是最终的文件,可以查看bug所在的位置。
以上是关于iOS奔溃分析技巧-crash日志符号化的主要内容,如果未能解决你的问题,请参考以下文章
linux系统奔溃之vmcore:kdump 的亲密战友 crash