并不是所有的crash都可以捕获的NSException,如果捕获不到,可以使用signal机制来捕获Crash发生时的错误内容。
一、可以捕获NSException,通过注册NSUncaughtExceptionHandler捕获异常信息
//注册异常处理函数
NSSetUncaughtExceptionHandler(&uncaught_exception_handler);
//异常处理函数
static void uncaught_exception_handler (NSException *exception) {
//可以取到 NSException 信息
//...
abort();
}
注:使用OC的异常处理是不能得到signal的
二、无法捕获的NSexception,利用Unix标准的signal机制,注册SIGABRT, SIGBUS, SIGSEGV等信号发生时的处理函数。
/注册处理SIGSEGV信号
signal(SIGSEGV,handleSignal);
// 注册处理其他信号 ....
//信号处理函数
static void handleSignal( int sig ) {
}
三、异常信息解读
1、Exception Type(异常类型)
Exception Type:通常包含Signal信号 和 EXC_BAD_ACCESS,NSRangeException等。
2、Exception Code(异常编码)
Exception Code:以一些文字开头,紧接着是一个或多个十六进制值。这些数值说明了Crash发生的本质。
从Exception Code中,可以区分出Crash是因为程序错误、非法内存访问还是其他原因。
四、Crash日志符号化
1、概述:
线程回溯部分内容如下:
2 AppName 0x0000000100205280 0x0000000100028000 + 1954432
3 AppName 0x00000001002ae59c 0x0000000100028000 + 2647440
这两条记录包括四列:(以第一条记录为例子)
帧编号 2 (数字越小,发生时间越晚,发生顺序往后,越好锁定问题的范围)
二进制库的名称 AppName
调用方法的地址 0x0000000100205280
一个基本地址 和 一个偏移量 0x0000000100028000 + 1954432 第一个数字指向文件,第二个数字指向文件中代码行
说明1: 线程回溯部分并不是我们习惯使用方法名和行数,而是十六进制地址。所以我们在分析Crash前需要将这些十六进制地址转化成方法名称和行数,改过程被称为符号化
说明2: 符号化crash日志需要获取对应的应用二进制文件以及生成二进制文件的.dsym文件(符号表)。必须完全匹配才行。否则,日志将无法被完全符号化。
说明3: Xcode编译项目后,会得到同名的dsym文件(符号表),dsym文件(符号表)是保存16进制函数地址映射信息的中转文件,我们调试的symbols都会包含在这个文件中,并且每次编译项目的时候都会生成一个dsym文件,位于 /Users/<用户名>/Library/Developer/Xcode/Archives 目录下,对于每一个发布版本我们都很有必要保存对应的Archives文件。
说明4:符号化可以使用Xcode的两种命令 symbolicatecrash命令 + atos命令
2、symbolicatecrash命令
1、首先找到symbolicatecrash命令
find /Applications -name symbolicatecrash -type f
我的本机命令的位置:
/Applications/Xcode.app/Contents/SharedFrameworks/CCFoundation.framework/Versions/A/Resources/symbolicatecrash
2、找到线上版本对应的xcarchive文件。从中找到.dsym 和 .app文件
xcarchive所在的路径一般在: /Users/<用户名>/Library/Developer/Xcode/Archives 目录下
3、获取crash日志文件
线上App的crash日志由crash日志收集服务获取。
也可以从真机上获取crash日志文件。点击window->devices,选择你自己的机器,然后点击 View Device Logs,右键可以导出crash文件。
获取的这些日志文件都需要符号化处理。
4、将symbolicatecrash、crash.crash、.dsym、.app拷贝到桌面下同一个文件夹下
5、检查xxx.app 和 xxx.app.dsym文件以及crash文件这三种的UUID是否一致。
查看 xxx.app 文件的 UUID,terminal 中输入命令 :
dwarfdump --uuid xxx.app/xxx (xxx代表你的项目名)
查看 xxx.app.dSYM 文件的 UUID ,在 terminal 中输入命令:
dwarfdump --uuid xxx.app.dSYM
查看crash 日志中的Incident Identifier (crash 文件的 UUID)
6、使用命令,生成“可定位问题的crash文件”
dwarfdump --uuid xxx.app.dSYM
symbolreportXXX.crash就是符号化后的文件
./symbolicatecrash crashXXX.crash appName.app.dSYM > symbolreportXXX.crash
7、根据符号化后的线程回溯信息,可以帮助定位问题的代码行。
说明:如果执行symbolicatecrash命令出现 Error: "DEVELOPER_DIR" is not defined at ./symbolicatecrash...这样的错误,可以在执行命令前,输入export DEVELOPER_DIR="/Applications/XCode.app/Contents/Developer"
3、atos命令
在符号化时候,还可以使用atos命令。发现armv7 处理器上的crash使用 symbolicatecrash 无法符号化。
1)将.dSYM、.app、crash.crash放到同一个文件夹下。
2) 知道crash文件的UUID:执行grep "AppName arm" *crash,得到结果
crash1.crash:0x100040000 - 0x100e23fff +AppName arm64 /var/containers/Bundle/Application/55A4D641-847F-4D24-86E1-129B28461858/AppName.app/AppName
crash2.crash:0x100060000 - 0x100e43fff +AppName arm64 /var/containers/Bundle/Application/3229ED68-8D19-406D-A3F5-EC0310C9DB7C/QAppName.app/AppName
crash3.crash: 0x5000 - 0xce8fff +AppName armv7 <7d62327effef37d384658020625a9944> /var/containers/Bundle/Application/C6BE271D-2EAC-42C0-8E72-4523F88C76B2/AppName.app/AppName
其中0x100040000、0x100060000、0x5000是加载地址(loadingAddress), 而arm64、armv7 是 architecture 的值(architectureValue),这两个值后面都要用。
3)然后执行atos命令,输入成功,进入待输入状态
xcrun atos -o appName.app.dSYM/Contents/Resources/DWARF/appName -l loadingAddress -arch architectureValue
4)此时输入App对应的crash地址,得到发生crash的信息
实例1:
grep "AppName arm" *crash
xcrun atos -o AppName.app.dSYM/Contents/Resources/DWARF/AppName -l 0x100040000 -arch arm64
实例2:
grep "AppName arm" *crash
xcrun atos -o AppName.app.dSYM/Contents/Resources/DWARF/AppName -l 0x5000 -arch armv7
网友评论