iOS Crash 流程化1:一般的 Crash 日志解析方法

  • iOS Crash 流程化:一般的 Crash 日志解析方法
    • TL;DR
    • 一、手动解析 Crash 日志
      • 1、需要的相关文件路径or获取途径
        • Crash Log获取
        • dSYM路径
        • Symbolicatecrash的文件(已提供)路径:
      • 2、UUID校验(确保文件之间彼此正确对应)
        • .crash
        • dSYM
        • .App
      • 3、.Crash文件分析过程
    • 二、通过Xcode查看设备崩溃信息
    • 三、使用 Apple 提供的 Crash 崩溃收集服务
      • 1、上报错误信息
      • 2、查看崩溃次数
      • 3、回到 Xcode 查看
        • 查看具体崩溃信息
    • 四、第三方崩溃分析工具 Bugly
      • 冲突
    • 补充:来自iOS知识小集
    • Ref

默认 Build Settings -> Build Options -> Debug Information Format 中,置为了 DWARF,如果选为DWARF则不会产生dSYM文件,必须选择DWARF with dSYM File才会生成符号表文件。

TL;DR

1、校验Crash Log uuid

grep "ShanLinExample arm" *.crash

2、校验dSYM uuid

dwarfdump --uuid ShanLinExample.App.dSYM/Contents/Resources/DWARF/ShanLinExample

3、校验App uuid

dwarfdump --uuid Payload/ShanLinExample.app/ShanLinExample

4、将 Crash Log、dSYM、App 放在同一个文件夹下,符号化

./symbolicatecrash ShanLinExample-2016-06-14-133332.crash ShanLinExample.App.dSYM > report.log

一、手动解析 Crash 日志

1、需要的相关文件路径or获取途径

Crash Log 获取
  1. 将发生了Crash的手机连接电脑,打开Xcode-菜单栏Window-Devices and Simulators
  2. 打开相应的手机View Device Logs
    image
  3. 建议按时间倒序排列,选中相应App的CrashLog,右键Export Log,以桌面CrashReport文件夹为例拷贝到~/Desktop/CrashReport
    image
dSYM路径

每一个打包版本都有个对应的 Archives 文件,路径为:

~/Library/Developer/Xcode/Archives

  1. Xcode-菜单栏Window-Archives选中相应版本Archive-show in Finder
    image
  2. 对选中.xcarchive右键显示包内容,在dSYMs文件夹下找到#AppName#.app.dSYM,拷贝到桌面CrashReport文件夹中
    image
Symbolicatecrash的文件(已提供)路径:
/Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash

Xcode不同版本间Symbolicatecrash的位置可能会有差异,如DVTFoundation.framework部分文件名不一样。

若找不到可以通过命令来查找:

find /Applications/Xcode.App -name symbolicatecrash -type f

2、UUID校验(确保文件之间彼此正确对应)

Ps. ShanLinExample改为对应的AppName即可。

.crash
grep "ShanLinExample arm" *.crash

输出结果:

0x100054000 - 0x1008e3fff ShanLinExample arm64  <f960888bef2430e9b844732364819642> /var/containers/Bundle/Application/84FA640D-AC63-4848-9989-9C5D8FCA748A/ShanLinExample.app/ShanLinExample
dSYM
dwarfdump --uuid ShanLinExample.App.dSYM/Contents/Resources/DWARF/ShanLinExample

输出结果:

UUID: 271C3816-D14F-3FFE-93FA-D0A8F2912DF0 (armv7) ShanLinExample.App.dSYM/Contents/Resources/DWARF/ShanLinExample
UUID: F960888B-EF24-30E9-B844-732364819642 (arm64) ShanLinExample.App.dSYM/Contents/Resources/DWARF/ShanLinExample
.App

应用Archive生成的.ipa,将后缀改为.zip,解压生成Payload文件夹,里面有个与App同名文件,此时用命令行:

dwarfdump --uuid Payload/ShanLinExample.app/ShanLinExample

输出结果:

UUID: 271C3816-D14F-3FFE-93FA-D0A8F2912DF0 (armv7) Payload/ShanLinExample.app/ShanLinExample
UUID: F960888B-EF24-30E9-B844-732364819642 (arm64) Payload/ShanLinExample.app/ShanLinExample

对比UUID即可。

图例

3、.Crash文件分析过程

将Symbolicatecrash、dSYM文件、.crash文件都放在一个文件夹中,例如~/Desktop/CrashReport

先运行:

export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer

否则会报错:

Error: "DEVELOPER_DIR" is not defined at ./symbolicatecrash line 60.

输入命令:

./symbolicatecrash ShanLinExample-2016-06-14-133332.crash ShanLinExample.App.dSYM > report.log

则最终生成的report.log文件即为崩溃时的具体信息。


二、通过Xcode查看设备崩溃信息

可以将发生崩溃的设备连接Xcode,选择window-> devices -> 选择自己的手机 -> view device logs 就可以查看手机上所有的崩溃信息了。

image

只要手机上的应用是这台电脑安装打包的,这样的崩溃信息系统已经为我们符号化好了,只需要进去之后等一会就行。如果还是没有符号化完毕 ,可以选择文件,然后右击选择Re-Symbolicate就可以。

如果是使用其他电脑进行的打包,可以在这里面将Crash文件导出,自己通过命令行的方式进行解析。


三、使用 Apple 提供的 Crash 崩溃收集服务

1、上报错误信息

App上线以后苹果就会自动捕捉崩溃信息,当 App 出现 Crash 后iOS系统就会记录崩溃日志并上传到Apple的服务器。前提是需要用户同意 “与应用开发者共享”:

设置->隐私->诊断与用量->与应用开发者共享

2、查看崩溃次数

上线运行一段时间后就可以登录iTunes Connect查看。登录后点击 App 分析 --> 选择要查看的App --> 点击右下角的崩溃,就可以根据筛选条件确定Crash的版本和日期。

确定完版本和日期后并不能在iTunes Connect中查看崩溃日志。具体信息还要回到Xcode中完成。

3、回到 Xcode 查看

查看具体崩溃信息

Xcode打开App,选择Window->Organizer,选择右边的Crashes

Organizer
Crashes
崩溃信息

这里选择具体的线上版本,崩溃信息就出现了(此时需要联网)

image

UIKit 开头是应该是无法定位的错误,哪儿红儿哪一行就是错误信息。这里的错误实际是因为使用UIScrollview的Category进行键盘隐藏而产生的Crash。当使用中文手写键盘输入就会出现这种情况。

可定位的错误信息如下,点击箭头选择App打开就会打开Xcode,然后把当时的线程和堆栈呈现出来。


可定位错误
在项目中查看

在项目中查看最好恢复到发布时候的版本。代码保持一致性。有时候项目中查看定位并不准确,但是有这些当时崩溃的线程,还是有助于我们定位错误的。

注意: 官方提供的崩溃信息并不是实时的,只能查看两天之前的崩溃信息。需要实时的可以使用第三方工具。


四、第三方崩溃分析工具 Bugly

有很多第三方工具可以进行崩溃统计分析,个人比较推荐 Bugly(不推荐友盟,网易云捕和国外的 Crashlytics 都没有用过)。它的优势在于,可以直接将崩溃信息分析出来并且做好分类和汇总。当然完整的解析崩溃需要上传dSYMBugly后台,可以通过手动上传的方式,也可以按照 Bugly 的文档配置脚本进行上传。

image

这就是Bugly的页面,出错堆栈Bugly会帮我们解析好,并且会根据不同情况给一些解决建议。

Bugly有一个页面追踪功能,这是我认为非常有用的一个功能。这个功能会将用户在不同页面之间跳转的流程记录下来。这样对于复现bug是很有用的,可以根据用户页面跳转推测出用户大概操作流程,根据这个流程复现bug

image

Bugly还有日报功能,可以每天汇总一篇日报,并且发到团队每个人的邮箱和微信号上。

image

冲突

如果多个 Crash 收集框架存在时,往往会存在冲突。

不管是对于 Signal 捕获还是 NSException 捕获都会存在 handler 覆盖的问题,正确的做法应该是先判断是否有前者已经注册了 handler,如果有则应该把这个 handler 保存下来,在自己处理完自己的 handler 之后,再把这个 handler 抛出去,供前面的注册者处理。这里给出相应的 Demo,Demo 由@zerygao提供。

typedef void (*SignalHandler)(int signo, siginfo_t *info, void *context);

static SignalHandler previousSignalHandler = NULL;

+ (void)installSignalHandler {
    struct sigaction old_action;
    sigaction(SIGABRT, NULL, &old_action);
    if (old_action.sa_flags & SA_SIGINFO) {
        previousSignalHandler = old_action.sa_sigaction;
    }

    LDAPMSignalRegister(SIGABRT);
    // .......

}
static void LDAPMSignalRegister(int signal) {
    struct sigaction action;
    action.sa_sigaction = LDAPMSignalHandler;
    action.sa_flags = SA_NODEFER | SA_SIGINFO;
    sigemptyset(&action.sa_mask);
    sigaction(signal, &action, 0);
}
static void LDAPMSignalHandler(int signal, siginfo_t* info, void* context) {
    //  获取堆栈,收集堆栈
    ........

    LDAPMClearSignalRigister();

    // 处理前者注册的 handler
    if (previousSignalHandler) {
        previousSignalHandler(signal, info, context);
    }
}

上面的是一个处理 Signal handler 冲突的大概代码思路,下面是 NSException handler 的处理思路,两者大同小异。

static NSUncaughtExceptionHandler *previousUncaughtExceptionHandler;

static void LDAPMUncaughtExceptionHandler(NSException *exception) {
    // 获取堆栈,收集堆栈
    // ......
    //  处理前者注册的 handler
    if (previousUncaughtExceptionHandler) {
        previousUncaughtExceptionHandler(exception);
    }
}

+ (void)installExceptionHandler {
    previousUncaughtExceptionHandler = NSGetUncaughtExceptionHandler();
    NSSetUncaughtExceptionHandler(&LDAPMUncaughtExceptionHandler);
}

补充:来自iOS知识小集

1、自己设备上 Xcode 编译的包发生闪退:连上手机打开 Xcode,cmd+shift+2 呼出 Device 的 Window,如图1所示,然后点击 View Device Logs,然后选中对应时间段自己 app 的崩溃日志。如果此时对应的调用栈还没有符号化,可以选中日志后右键如图2所示 Re-Symbolicate Log 即可。

2、如果是打包服务器或者 Appstore 的包发生闪退:拷贝对应的包和 dSYM 到任意文件夹下,注意将 dSYM 解压以及 .ipa 里面的 .app 取出。然后按照情况1的方式处理即可,Xcode 会自动索引二进制及 dSYM。

  • 如果APP是自己电脑编译生成的,Xcode会根据spotlight自动找到对应的符号文件
  • 如果不是自己电脑编译生成的,只需要将.app和dSYM放入同一文件夹,然后手动生成索引,这样Xcode也能找到。在命令行中输入如下命令手动创建索引:
# mdimport ,导入文件到datastore(import file hierarchies into the metadata datastore)。
mdimport pathName

上面两种方式确保了Xcode能依据UUID找到地址对应的符号文件,这样,Xcode就能解析出崩溃日志。

3、如果拿到别的设备导出的未符号化的崩溃日志,可以将日志拖至下图所示的列表中,注意此时上面 tab 记得选 All Logs 而不是 This Device,然后参考情况2,找到崩溃日志对应的二进制包和 dSYM 文件,按照情况2处理即可。可能会遇到系统库的一些方法无法符号化的问题,只需要找到对应的设备连上电脑,让 Xcode 读取一遍该设备(同机型和系统版本的也可以)的符号表,然后再 Re-Symbolicate 一遍就行。


image

4、遇到线上用户崩溃,无法拿到完整崩溃日志,可以让用户到【设置->分析->分析数据】里面找到对应时间点的崩溃日志,然后截图,根据一个开源工具 dSYMTools,把崩溃栈的关键地址输入到文本框中即可解析出崩溃的那个方法,具体使用方法参考 answer-huang/dSYMTools: dSYM analyze

Ref

iOS开发技巧-崩溃分析

iOS查看线上App崩溃信息及第三方Crash监控

iOS Crash 捕获及堆栈符号化思路剖析

推荐阅读更多精彩内容