1.工程设置
打包的时候生成dSYM(符号表),用来将崩溃的日志符号化
可以将release模式下设置为DWARF with dSYM File
注意:如需要在Xcode中进行错误日志分析,需要在上传到app store时勾选如下选项,上传符号表给Apple
2.dSYM文件收集
步骤如下:
在Xcode中顶部菜单Window -> Organizer -> Archives
中,选中archive
的版本右击,选择Show in Finder
就可以获取dSYM
文件了。
保存好你的app archive,app archive包含调试符号的dSYM。
如果app包含bitcode,需要使用Archives Organizer 中Download Debug Symbols 按钮来下载位码编译生成的dSYM。
3.收集错误日志
收集错误日志有如下几种途径:
a. Xcode,在Xcode中顶部菜单Window -> Organizer -> Crashes
中,选择crash然后Show in Finder
。
b. 第三方,友盟、Bugly、神测等
c. 也可以将发生崩溃的设备连接Xcode,选择window-> devices -> 选择自己的手机 -> view device logs
就可以查看手机上所有的崩溃信息了。
d. 在Mac上使用控制台app查看设备或者模拟器的崩溃日志
e. 在崩溃设备上选择 设备-》隐私 -》 分析与改进 -》分析数据
4. 日志分析
.crash 文件包含:
a.顶部的设备型号、app名称、app版本号、启动时间、崩溃时间、操作系统版本等信息
b.含崩溃原因,这是操作系统发送用来杀死该进程的具体错误或特定信号
崩溃信号说明 https://developer.umeng.com/docs/193624/detail/204694
c.崩溃信息 Application Specific Infomation ,包含控制台信息,在模拟器上部分情况可用,在iOS设备上出于个人隐私原因通常会被隐藏
如果有未处理的异常,则它可能包含如下的异常回溯
d.崩溃线程堆栈,记录崩溃时所有的线程的回溯,其中一条被标记为崩溃的线程
e.崩溃线程的寄存器状态
f.加载到进程中的二进制数据镜像,这是app可执行文件和所有其它的库。Xcode使用它进行符号化,以查找符号文件和行号信息并且显示在堆栈跟踪中
如何查看崩溃日志
- 查看异常类型Exception Type,上面的异常类型为EXC_CRASH (SIGABRT)
异常类型为EXC_BAD_INSTRUCTION (SIGKILL),SIGKILL信号指的是非法指令信号,意味着CPU正在尝试执行由于某种原因不存在或无效的指令,通常被用在当操作系统想要终止你的进程时。SIGKILL信号无法被处理,你的进程也无法捕获它
异常类型为EXC_BAD_ACCESS(SIGSEGV),即段冲突信号,通常由内存错误引起的,要么时写入只读的内存,要么是尝试从内存中读取根本不存在的内容 - 查看异常回溯,查看崩溃时正在执行的代码
常见的内存错误:
- Objective-C 中objc_msgSend 或者retain/release
这通常是由于你拥有某种类型的对象,然后该对象被释放并再次使用导致的。 - Unrecognized selector exception
同一地址分配了一个新的对象取代了以前旧对象所在的位置,当代码尝试使用旧对象调用旧对象的函数,该地址处去有一个不同类型的不同对象,并且它根本无法识别该函数。
反汇编找出具体奔溃地址:
终端找出崩溃地址:
终端符号化崩溃日志
/Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash
拷贝上面文件和崩溃日志文件、dSYM文件到统一目录,终端cd到这个目录执行如下命令:
./symbolicatecrash ./*.crash ./*.app.dSYM > symbol.crash
会生成一个symbol.crash文件,这个文件内容就是解析crash后所需要的内容。
PS: 若报错
Error: "DEVELOPER_DIR" is not defined at ./symbolicatecrash, 只需在终端中运行
export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer"
若上面符号化没有变化,可以使用atos -o 还原某一行,命令如下:
atos -o (上文中.app.dSYM文件右键显示包内容,一直剥离到标有你工程名字的那一层,把那个文件的地址拖进来,这个文件的那一层已经是最后一层,不能再剥离了) -arch arm64 -l (crash日志里你想要解析的那一行从前面数第四列数字,复制过来,例如:0x1000a8000) (crash日志里你想要解析的那一行从前面数第三列数字,复制过来,例如:0x00000001009d80fc )回车就可以看到解析出来的这一行,完整示例
atos -o /Users/Chandler/Downloads/JDBClient.app.dSYM/Contents/Resources/DWARF/JDBClient -arch arm64 -l 0x1000a8000 0x00000001009d80fc
多线程问题导致的错误
多线程错误通常时最难诊断和复现的错误类型之一,它们很难复现,因为只会偶尔导致崩溃,因此你的代码在99%的情况下正常工作,这些错误可能在很长一段时间内被忽略。
使用Thread Sanitizer可以调试处多线程问题导致的错误 ,Xcode -》 edit scheme → run -》 Diagnostics 选项卡勾选Thread Sanitizer (只能用于模拟器 )。
建议,创建队列时,可以在初始化提供自定义队列名称。
日志分析建议:
- 我们大部分时间都在看奔溃的特定代码和崩溃的线程上,重要的是要查看与崩溃相关的其他代码
- 查看崩溃线程以外的堆栈跟踪
- 查看多个崩溃日志,以找出特定的崩溃原因
- 使用Address Sanitizer 和Zombies等工具复现崩溃
5. 崩溃日志分析工具
- Xcode,在Xcode中顶部菜单`Window -> Organizer -> Crashes,如果上传了dSYM给Apple,可以直接在这里查看相关错误信息,但是信息没有第三方的统计的多。
注意:
- 苹果自带的崩溃统计工具并不推荐用,如果想要使用这个功能,需要用户在iPhone中进行设置
设置 -> 隐私 -> 诊断与用量 -> 诊断与用量数据(iOS8以下在通用中设置)
选择自动发送,并与开发者共享。
然而很多人并不想和开发者共享数据,或者不设置这个选项,那这样就收集不到这部分的崩溃。
- 静态库或者动态库Xcode不能查看崩溃的相关堆栈信息
- (推荐)第三方,友盟、Bugly、神测等,需要将上面每次发布版本的dSYM文件上传到第三方平台,第三方平台自动符号化错误日志,第三方平台还有行为日志可以追踪用户的操作的页面。
友盟2020年更新了SDK,将统计和崩溃两个功能分开了,最新的需要集成友盟的APMSDK。
如若需要还可以使用uid等其他信息精准定位错误原因 https://apm.umeng.com/platform/5b73d44bb27b0a02e20001c6/error_analysis/scrutiny,需要接入账号集成
- 使用 symbolicatecrash 工具
- 使用 atos -o
6.崩溃发生原因
代码问题:
- swift中可选值强制解包
- 数组越界
- 溢出,整形变量太大
- 未捕获的异常
- 自己代码写的断言
环境导致的崩溃,操作系统从外部杀死你的进程:
- 你的应用程序执行某件事时间太长
- 设备过热,操作系统将终止使用过多CPU的进程
- 设备内存不足,操作系统将终止正在使用大量内存的进程
- 无效的代码签名,操作系统强制对代码进行签名,如果签名无效或者代码未签名,操作系统将终止该进制并生成特定类型的崩溃日志
由操作系统终止的崩溃可以在设备日志和macOS控制台上找到,它们并不总是会出现在Xcode Organizer中
7. 崩溃防范
-
字典
@{@"part_name":@"学院页",
@"course_id":model.videoID?:@""}
可变字段插入值判断nil
数组
数组访问、修改和替换时需要做数组长度判断
插入数组时需要判断nil字符串访问、修改和替换时需要做字符串长度判断
其他地方的变量都建议加上nil值的判断
接口返回的数据都应是不可信的,应先判断数据类型是否正确,然后再判断是否有此值,再去取值
谨慎使用kvc
注意!!!
打包上传app store时一定要设置生成dSYM,并且保存好dSYM文件
参考资料:
https://segmentfault.com/a/1190000019569184
https://developer.apple.com/videos/play/wwdc2018/414/