我们的应用通常充斥着崩溃,为了方便寻找导致崩溃的原因,苹果为我们提供了崩溃报告,每个安装了应用的手机,在崩溃时都会产生一个报告,我们可以利用合格报告推断出导致崩溃的原因。
首先,崩溃日志显示的通常是每个执行线程的完整回溯,而这些回溯类似于下面这样,
Last Exception Backtrace:
(0x1a759e164 0x1a72b2c1c 0x1a748dbb8 0x100ed83c0 0x1a72a1404 0x1a72a4930 0x1a72328b4 0x1a7357f40 0x1a73573ec 0x1a735a154 0x1a72b2de8 0x1a75136d0 0x1b1924604 0x1ab6e815c 0x100641624 0x1a738f1ec)
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libsystem_kernel.dylib 0x00000001a7384df0 0x1a735e000 + 159216
1 libsystem_pthread.dylib 0x00000001a72a4930 0x1a72a2000 + 10544
我只是截取了一部分,这里显示的都是些堆栈信息,包括线程名、内存地址等,显然这些内存地址等信息我们读不懂,所以我们需要将其符号化成可读性高的方法名及行数,那么问题来了?我们如何收集这些线上的崩溃?
获取崩溃报告
我们可以用Xcode通过以下方式获取崩溃报告,前提是线上的包是用这个Xcode归档(archived)的,而且要保存这个归档。
- 1、Xcode->Window->Devices and Simulators->View Device Logs (或直接shift+command+2,再点击View Device Logs)
就可以获取到手机的所有崩溃日志了,我们到处日志并随便命名,我就一直叫xx.crash
- 2、Xcode->Window->Organizer->Crash,针对发布App Store的应用,需要用户同意发送错误报告给苹果开发者才可以看到这些Crash报告,而对于TestFlight则默认支持这个。
- 3、App Store Connect 拒绝时提供的崩溃信息
有.dSYM 文件时的分析步骤
获取调试符号文件.dSYM
我们获取到的崩溃报告,需要用对应的.dsym符号文件解析成我们看的懂的报告。
我们打了release包后,会生成一个二进制文件,调试符号会存储在dsym文件中,我们要做的就是将dsym文件下载下来。有以下几个方式获取dsym文件。
- 1、从Xcode的归档文件中获取:Xcode->Window->Organizer->Archives,选择对应归档文件,Show in Finder->右键->显示包内容,就看到dsym文件了。
- 2、从App Store Connect中获取,我目前没找到从哪下载
解析Crash文件
有三种方法可解析Crash文件内容
1、Xcode直接解析
要使用Xcode符号化 crash log,需要下面所列的3个文件:
1.crash报告(.crash文件)
2.符号文件 (.dSYM文件)
3.应用程序文件(appName.app文件,把IPA文件后缀改为zip,然后解压,Payload目录下的appName.app文件), 这里的appName是应用程序的名称。
把这3个文件放到同一个目录下,打开Xcode的Window菜单下的organizer,然后点击Devices tab,然后选中左边的Device Logs。
然后把.crash文件拖到Device Logs或者选择下面的import导入.crash文件。
这样就可以看到crash的详细log了。
2、symbolicatecrash
首先,要判断.dSYM文件、.crash文件、.app文件中的uuid是否一致,一致才表示他们是一起打包生成出来的,才能解析成功。
获取 .dYSM 文件 uuid 的方法,输入以下命令
dwarfdump --uuid .dSYM文件名
得到以下结果
UUID: 02000565-D5A9-3627-81E5-659B2AC06791 (arm64) 02000565-D5A9-3627-81E5-659B2AC06791.dSYM/...
crash文件中的uuid获取方法,找到Binary Images:这一行就看到uuid了。比对相同后,下面开始解析
找到symbolicatecrash
find /Applications/Xcode.app -name symbolicatecrash -type f
一会儿会输出symbolicatecrash的路径
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/PrivateFrameworks/DVTFoundation.framework/symbolicatecrash
然后把symbolicatecrash
复制出来,将“.app“, “.dSYM”,“symbolicatecrash”和 ".crash"文件放到同一个目录下,分别执行两个命令
export DEVELOPER_DIR=/Applications/XCode.app/Contents/Developer
./symbolicatecrash ./xxxx.crash ./xxxx.app.dSYM > symbol.crash
symbol.crash就是解析成功的crash
3、命令行工具atos解析
使用atos工具解析就不需要区分uuid是否一致了,因为这是按照崩溃提供的地址信息逐个解析的。
命令形式如下
xcrun atos -o appName.app.dSYM/Contents/Resources/DWARF/appName -l 模块加载地址 报错地址
会得到类似下面这种结果
-[UMNetworkWork postDataWithFile:] (in ) + 668
模块地址和报错地址对应崩溃报告文件中的位置如下
无.dSYM时,解析崩溃信息
.dSYM
有找不到的情况,可能是不小心删除了等等原因,反正就是找不到了,这时就需要另一个办法了。
在没有.dSYM
文件时,我们无法按上述方式解析,为什么呢?因为在 App 中供启动用的可执行文件
会配备一个符号表,符号表包含了系统动态库的链接符号及我们自己的源文件的符号,都需要通过符号表进行解析。
而 Xcode 在打包成 ipa 时,会剔除非系统动态库的符号表,防止被逆向,因此崩溃日志无法解析,都是因为这个符号表被剔除了,我们要做的就是恢复符号表。
恢复符号表
1、下载恢复符号表的工具并编译
首先来到桌面
cd Desktop
clone 在 git 上的工具库
git clone --recursive https://github.com/tobefuturer/restore-symbol.git
下载完成后,进入文件夹内并编译
cd restore-symbol && make ./restore-symbol
如图所示,表示编译成功
然后执行cd ..
回到桌面,接下来我们要恢复符号表
2、恢复符号表
我们首先来看崩溃日志的Code Type
这个位置
我们知道了是 ARM-64 架构的 CPU 架构类型,于是我们下一步要做的是分离可执行文件的 ARM-64 架构。
首先我们要找到可执行文件
然后把下面这个黑色的可执行文件复制到桌面
打开权限
chmod a+x restore-symbol
将可执行文件瘦身
lipo -thin arm64 XXX -output XXX-arm64
执行完上面的命令后,会发现桌面上多了一个XXX-arm64
文件,然后我们用工具恢复符号表
./restore-symbol -o CrashTest-symbol XXX-arm64
上面命令的解释,-o
是输出,CrashTest-symbol
是输出符号表的名字,XXX-arm64
是我们在上一步分离出来的 Arm64 架构。
3、用苹果自带命令行工具atos,将崩溃地址解析成具体函数
atos -arch arm64 -o CrashTest-symbol -l 0x100c20000 0x0000000100c26f34
上面命令的-l
后面分别是模块加载地址
和报错地址
,
xxxxapp 0x0000000100c26f34(报错地址) 0x100c20000(模块地址) + 1349156
上面的命令执行完,我们就会得到具体的函数名和行数了,如下所示
-[MEAdBaseManager rewardVideoFailedOpertion:] (in CrashTest-symbol) + 184