iOS 闪退收集

本贴最后更新于 2335 天前,其中的信息可能已经天翻地覆

一、dSYM

什么是 dSYM

dSYM 实际上放的是程序在编译过程中收集的符号表的信息,其实质上保存符号表数据的是二进制的 DWARF(DebuggingWith Arbitrary Record Formats)文件。可以用 dwarfdump 命令读取出一些可读的信息。

设置 Xcode 工程生成 dSYM

  • Build Setting 中设置 Debug Information Format 为 DWARF with dSYM File
  • Build Setting 中设置 Generate Debug Symbols 为 Yes

  • 小技巧:当本地调试不需要 dSYM 的时候,可以设置 XCode 不生成 dSYM,这样做的好处是使得 XCode 编译运行的速度更快(少了生成 dSYM 的时间)。

查看 dSYM 的 UUID 信息


这个步骤很关键,这是确定 dSYM 是否可用的唯一标识,用来与崩溃日志进行配对。
注意:

  1. 只要代码是相同的,编译后产生的 dSYM 都有一样的 UUID,也就意味着你的应用出现闪退,而 dSYM 丢失的情况,只要工程还在,代码不变就可以重新编译生成 dSYM,用来解析崩溃日志。
  2. dSYM 的目录可以重命名,但是.dSYM 的后缀不能去掉,不能放在.dSYM 的目录文件夹中。

二、崩溃日志

移动设备产生崩溃日志的情况

  • 应用违反操作系统的规则,主要包括三种情况,watchdog 超时、用户强制退出和低内存终止。
  • 应用中有 bug,这是我们应该关心的情况。

崩溃日志的获取途径(很关键)

  • 手机端:不同的 iOS 版本在不同的目录下,我的 iOS11.2 在 设置-> 隐私-> 分析-> 分析数据,可以看到很多不同的.ips 文件。
  • 电脑端:使用 iTools 的工具,选择工具箱栏目,有一个崩溃日志的选项,文件名的格式是"应用名-闪退时间.ips"。
  • 闪退收集框架:如果在应用中接入了闪退收集的框架,或者自己实现了闪退收集的模块,那就还可以通过这些模块获取闪退的日志信息。

了解崩溃日志的文件结构

* Incident Identifier: 崩溃日志的唯一标识符,对应不同的crash
* CrashReporter Key:   设备的标识key值,不同于设备的UDID,当我们拿到大量的闪退日志而只有几个CrashReport Key时可能意味着闪退值发生在个别机器。    
* Hardware Model:      设备类型 
* Process:             进程名,一般就是我们的AppName,[]中的数字表示的是进程ID
* Path:                可执行程序存储在设备的路径名
* Identifier:          App的Indentifier,来自于你的证书
* Version:             App的版本,info.plist中CFBundleShortVersionString和CFBundleVersion这两个字段
* Code Type:           当前CPU架构
* Role:                异常发生时该进程task_role的值,貌似是描述进程的结构体某个成员变量的枚举值
* Parent Process:      父进程
* Report Version:      崩溃日志的版本,目前就见过104和105
* Exception Type:      异常的类型  
* Thread State:        线程回溯
* Binary Images:       这段内容列出了在线程终止的时候,进程的二进制映像  

符号化解析的过程(可不必过多纠结)

详细过程可以参见/Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash,这实际上就是一份 perl 脚本。

  1. 使用符号化解析工具,第一步是指定脚本中 $DEVELOPER_DIR 的值,使用如下命令
    export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer"
  1. 进行符号化解析
    执行如下命令,实际测试可以发现 symbolicatecrash 工具最少只需要输入闪退日志就可以进行解析,下面这条命令会把 crash.ips 解析结果导出到 out.log,-v 选项可以把解析过程中的一些日志信息打印到标准出错。另外因为 symbolicatecrash 有个 bug,当闪退日志中相邻的重复行太多的时候,命令会卡死,所以在进行解析前先使用 uniq 去重,再通过管道流向 symbolicatecrash 即可。
    uniq crash.ips | symbolicatecrash -v > out.log

其他的一些选项可以查看 symbolicatecrash 的详细帮助信息。

    usage: 
    symbolicatecrash [--help] [--dsym=DSYM] [--output OUTPUT_FILE] <LOGFILE> [SYMBOL_PATH ...]

    &ltLOGFILE&gt                   The crash log to be symbolicated. If "-", then the log will be read from stdin
    &ltSYMBOL_PATH&gt               Additional search paths in which to search for symbol rich binaries
    -o | --output <OUTPUT_FILE> The symbolicated log will be written to OUTPUT_FILE. Defaults to "-" (i.e. stdout) if not specified
    -d | --dsym <DSYM_BUNDLE>   Adds additional dSYM that will be consulted if and when a binary's UUID matches (may be specified more than once)
    -h | --help                 Display this help message
    -v | --verbose              Enables additional output

上述解析命令能执行成功的前提条件是解析命令的本机是有对应的 dSYM 存在的。
此外还有一个实际应用中的问题需要补充,因为我们的解析有可能是后台的一个服务,所以解析的过程可能在一个脚本中执行,而脚本解析闪退日志的第一步是下载服务器上的 dSYM,一般会是一个压缩包,然后进行解压和符号化解析,这时候解析的过程很可能会失败。
猜测原因是 symbolicatecrash 工具使用 mdfind 工具在 Mac 上进行全局搜索,而由于 Mac 的缓存机制,刚解压的 dSYM,可能还不被认为是 dSYM(也就是说缓存机制在一定时间之后会把这个解压出来的 dSYM 加入到 mdfind 的搜索范围中,在这之前 mdfind 找不到),所以会导致闪退日志找不到解析不出来。解决办法是使用 mdimport 强制刷新刚解压的 dSYM 的目录,再进行符号化解析就没问题了。

三、如何解析闪退日志,看懂闪退日志(敲黑板划重点)

分析闪退日志

  • 首先找到闪退发生的闪退类型,如"EXC_BAD_ACCESS",拿着这个闪退类型去到 Apple 的官网,可以确定一个大致的方向。
  • 如果是直接从手机拿的系统的闪退日志,异常子类型的字段(Exception Codes)可能会有一串特殊含义的编码(英文或数字都有可能),可以去 Apple 官网或者百度搜索其具体含义,以获得进一步的异常信息。
  • 在闪退日志中可能会有这样的字段"Application Specific Information:",这是闪退日志中最有用的一段信息,这段信息意味着某个 NSException 导致程序 Crash,且这段信息包含了 NSException 的 reason 和 name,同时还会有"Last Exception Backtrace:"的一段信息,可以精准的定位到程序出错的位置。
  • 找到闪退对应的线程,大部分情况是线程 0 闪退,分析闪退线程对应的堆栈,找到自己应用的应用名对应的行,一般会解析出函数名、行号和文件名,这里大概就是问题的所在。

闪退日志解析不完整的原因

有以下两种情况(可能还有未知的原因)
  • 系统的库解不出来, 是因为~/Library/Developer/Xcode/iOS\ DeviceSupport 目录下缺失对应的版本系统库的 dSYM,可以把对应的设备插在 Mac 机上,可以自动生成这些文件。
  • 用户库解析不出来, 这是因为用户库的 dSYM 不完整,可能是如下的情况,游戏或者 SDK 有引用到动态库,而动态库的 dSYM 是需要额外提供的,所以会解析不出来。

调试的过程中获取闪退日志

在本地打包调试的时候,也可以直接使用 Xcode 查看闪退日志,这种方式更加的方便,可以看到 xcode 自动会把闪退日志进行解析,不过前提是 XCode 已经设置生成 dSYM 了。

四、简单说下闪退解析服务的完整解决方案

app 层面:

  1. 接入第三方的闪退收集框架(可以自己实现,原理不难),开源的如:PLCrashReport
  2. 每次进入 app 之前检查上一次打开应用是不是出现了闪退,如果是则把上一次出现的闪退日志进行上报,上报的时候可以把用户信息,设备信息,或者玩家等级等信息一并上传,方便进行统计分析。

后台服务:

  1. 接收 app 层的闪退上报。
  2. ios 打包的时候应该把 ipa 跟 dSYM 对应的管理起来(亲测在 product name 设置标记是可行的),解析之前去服务器上下载闪退日志对应的 dSYM。
  3. 按照上述的方式进行闪退日志的解析,另外如果有需求的话,可以对上报的内容进行分类统计(前面有说到,app 层面可以把用户信息上传)。
  4. 闪退日志上传之后最好按照闪退的类型进行分类,分类的方法(这里我是参考的腾讯 Bugly):首先按包分类,其次如果闪退日志中捕获到异常,那么按照异常类型分类,最后按照关键堆栈里面的第一个非系统方法名进行分类。

参考文档

特别推荐--关于闪退收集框架的原理
https://www.cnblogs.com/smileEvday/p/Crash1.html
http://blog.csdn.net/a253143598/article/details/22289839
https://developer.apple.com/library/content/technotes/tn2151/_index.html

  • B3log

    B3log 是一个开源组织,名字来源于“Bulletin Board Blog”缩写,目标是将独立博客与论坛结合,形成一种新的网络社区体验,详细请看 B3log 构思。目前 B3log 已经开源了多款产品:SymSoloVditor思源笔记

    1063 引用 • 3454 回帖 • 189 关注
  • iOS

    iOS 是由苹果公司开发的移动操作系统,最早于 2007 年 1 月 9 日的 Macworld 大会上公布这个系统,最初是设计给 iPhone 使用的,后来陆续套用到 iPod touch、iPad 以及 Apple TV 等产品上。iOS 与苹果的 Mac OS X 操作系统一样,属于类 Unix 的商业操作系统。

    85 引用 • 139 回帖
  • Crash
    2 引用

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...