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日志符号化的主要内容,如果未能解决你的问题,请参考以下文章

当app出现线上奔溃,该如何办?

iOS Crash日志字段解析

linux系统奔溃之vmcore:kdump 的亲密战友 crash

android crash日志怎么分析

iOS-----分析iOS Crash文件:符号化iOS Crash文件的3种方法

iOS-symbolicatecrash符号化Crash文件