iOS Abort 问题系统性解决方案
一、背景
崩溃(Crash),即闪退,多指移动设备(如 iOS、Android 设备)在打开/使用应用程序的过程中,突然出现意外退出/中断的情况。如果 App 线上版本频繁发生崩溃,会极大地影响用户体验,甚至导致用户流失,以及收益减少。因此,崩溃问题是客户端稳定性团队需要重点解决的问题。
然而,对于所有崩溃场景,仅 25%的崩溃可通过信号量捕获,实施相应改进;另有 75%的崩溃则难以识别,从而对 App 的用户体验,造成了巨大的潜在影响。
Facebook 的工程师将 App 退出分为以下 6 个类别:
1.App 内部主动调用 exit()或 abort()退出;
2.App 升级过程中,用户进程被杀死;
3.系统升级过程中,用户进程被杀死;
4.App 在后台被杀死;
5.App 在前台被杀死,且可获取堆栈;
6.App 在前台被杀死,且无法获取堆栈。
对于第 1~4 类退出,属于 App 的正常退出,对用户体验没有太大影响,无需进行相应处理;对于第 5 类退出,可通过堆栈代码级定位崩溃原因,对此业界已形成比较成熟的解决方案,推荐免费试用阿里云的崩溃分析服务,即可快速定位、解决此类崩溃问题;对于第 6 类退出,可能的原因很多,包括但不限于:系统内存不足时继续申请内存、主线程卡死 20s 以上、CPU 使用率过高 Stack Overflow 等,在此我们统一称之为 iOS 客户端的“Abort 问题”。
Abort 问题无法被堆栈捕获,且发生频次远高于可被捕获的崩溃(下称“堆栈崩溃”)。从历史数据来看,手淘(电商类超级 App 代表)的 Abort 问题数量一般是堆栈崩溃数量的 3 倍左右;优酷 Pad(视频类超级 App 代表)的 Abort 问题数量一般是堆栈崩溃数量的 5 倍左右。可见,Abort 问题对用户的使用体验造成巨大影响。
本文将针对 iOS 客户端的 Abort 问题,进行根因定位分析,并提出系统性解决方案。
二、Abort 问题的原因分类
形成 Abort 问题的原因主要包括以下 4 个。
2.1 内存 Jetsam
移动端设备的物理内存资源紧张,但 App 仍不断申请内存。因此系统 signal 9 杀死进程,造成异常退出。
2.2 主线程死锁
A/B 两个线程同时等待对方完成某些操作,因而无法继续执行,形成死锁,造成异常退出。
2.3 启动/重启超时
App 由于启动/重启的时间超过系统允许的时间限制,造成异常退出。
2.4 CPU 打爆
主线程死锁、启动/重启超时,都可能间接导致 CPU 打爆,造成异常退出。
三、Abort 问题的根因定位
Abort 问题常常没有明显线索进行问题定位,因此,解决难度比较大。手淘曾经历过很多次 Abort 问题数量飙升,但无从下手的事故,甚至还有一两次发生在双 11 前不久,但往往以“一群人苦逼的众测复现、复现之后也无法确定是否真的复现”收场。
因此,我们迫切需要基于已有经验,形成一套完整的解决方案,快速、准确地定位/解决问题。这就需要我们从以下几个方面着手进行考虑:
1.Abort 问题发生的场景:例如,哪个页面、什么操作。
2.Abort 问题发生的原因:例如,内存 Jetsam、主线程死锁、启动/重启超时、CPU 打爆。
3.对于内存 Jetsam,需进一步定位到是否发生了内存泄露以及泄露的循环引用(Retain Cycle)。
4.对于主线程死锁,需进一步定位到卡死的堆栈。
5.对于启动/重启超时,以及 CPU 打爆,需进一步定位到堆栈。
接下来,我们以手淘的主线程死锁问题为例,进行根因分析。首先,来看一下某版本手淘 Abort 问题数据的总体视图:
由于 Abort 问题出现之前,内存、CPU 使用量正常,因此初步判断造成异常退出的原因为主线程死锁。
查看相关日志文件,验证时间、线索吻合,因此可最终确定造成异常退出的原因为主线程死锁。
四、Abort 问题的系统性解决方案
4.1 Abort 系统性解决方案难点:现场捕获
为实现 Abort 问题的系统性解决方案,需充分考虑以下问题:
1.通过 signal 9 杀死进程造成的 Abort 问题,往往难以通过信号量捕获至堆栈。在这种情况下,应如何尽可能完整地捕获崩溃现场的关键信息?具体包含哪些信息?
2.App 崩溃时系统处于极不稳定的状态,应如何保证崩溃现数据稳定落盘?
3.在信息采集、数据捕获的过程中,需对大量数据进行写入操作,应如何保证日志高性能写入?
4.在数据量较大的情况下,数据的存储、上传可能对系统造成较大压力,应如何保证数据的高压缩率?
基于以上考虑,我们提出并设计了一套基于 mmap 的高性能、高压缩率、高一致性、可自解释的 trace 文件协议,作为 iOS 端高可用体系的数据载体。
4.1.1 mmap 数据存储层保证数据写入的高性能和高一致性
1.通过 mmap 将一个文件或者其它对象映射到进程的地址空间,对内存的操作会由内核将数据写到对应的磁盘文件上;数据写入的性能与内存操作相当(略比内存操作高)
2.用户进程崩溃之后,这块映射区仍由内核管理,可以保证数据的一致性
4.1.2 二进制编码协议保证数据压缩率最高
1.具体编码协议
2.实测编码在压缩率能达到 80%以上,或者直观一点说,使用 50k 的内存可以记录下用户二十分钟内详细的使用记录,包括页面访问记录、系统事件、秒级别的内存、CPU 数据。
4.1.3 尽可能多的记录系统多维度指标及异常事件
包括:
1.性能数据,包括 CPU、内存数据,用于判断应用当前是不是处理 overload 状态
2.大内存申请
3.Retain Cycle,用于定位 Jetsam Event
4.卡顿,用于定位 watch dog kill
5.当前存活 VC 实例数量
五、总结
在 App 的世界里,功能层面的差异已经越来越难以体现。在这种情况下,良好的用户体验,往往是 App 致胜的关键。而 Abort 问题对于每一个 App 而言,都是对用户体验的最大挑战,需要 App 开发者给予足够的重视。
为了更好地发现解决崩溃问题,构建异常“感知-定位-恢复”的运维能力闭环,提升 App 使用体验,建议接入阿里云崩溃分析,支持各类异常事件采集,支持现场回溯分析,帮助您更好的提高 iOS App 稳定性。
钉钉搜索 35248489,加入阿里云云原生应用研发平台 EMAS 技术交流群,探讨最新最热门的应用研发技术和实践。
作者:淘宝庐轩
版权声明: 本文为 InfoQ 作者【应用研发平台EMAS】的原创文章。
原文链接:【http://xie.infoq.cn/article/119486935a1ff34950b456f7e】。文章转载请联系作者。
评论