问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

iOS应用ANR(无响应)问题详解

创作时间:
作者:
@小白创作中心

iOS应用ANR(无响应)问题详解

引用
腾讯
1.
https://bugly.tds.qq.com/docs/tutorial/iOS/anr

ANR (Deadlock)

ANR(Application Not Responding)是Android系统中的一个术语,用于描述应用程序在一段时间内未能响应用户输入的情况。在iOS系统中,ANR表现为因为错误码为0x8badf00d的SIGKILL信号。

在Bugly平台中,由于其本质为同一种异常表现,因此这里统称为ANR,方便平台和开发者统一概念。

ANR 指标

ANR问题的本质是主线程卡顿,导致App响应用户输入超时,触发系统WatchDog机制,从而杀死进程。Bugly通过检查主线程Runloop执行周期,判定主线程是否发生了卡顿,对于超过阈值(默认5s)的卡顿,则标记为可能发生ANR事件。结合App进程退出判定,若进程最后退出状态时,依然为可能发生ANR事件的状态,则判定上一次进程退出为ANR。

综上,Bugly将App前台运行期间进程被SIGKILL时,处于长卡顿状态的退出场景,判定归类为ANR异常。

注意:由于SIGKILL信号是直接杀死进程,不会有任何信号或通知到进程内部,因此Bugly将已知的退出(例如用户手动杀死进程、其他Crash等退出)排除后的退出当作SIGKILL处理,这一定的处理上,与FOOM判定方式类似。

为了方便衡量ANR事件的严重程度,Bugly提供了ANR指标——ANR率——ANR退出次数与进程启动次数的比值。同时加入了从设备角度和用户角度衡量的指标,即设备ANR率和用户ANR率,二者均是有设备ID和用户ID去重后计算所得。

  • ANR率= 退出次数 / 启动次数
  • 设备ANR率= 设备ID去重后的退出次数 / 设备ID去重后的启动次数
  • 用户ANR率= 用户ID去重后的退出次数 / 用户ID去重后的启动次数

与其他指标分析类似,支持按照不同的条件进行筛选分析不同条件下的指标变化和对比。

ANR 诊断信息

如ANR指标所述,Bugly根据主线程Runloop执行情况来判定是否发生了ANR。在监控主线程执行时间的同时,Bugly会对主线程的执行调用栈进行周期性采样,如此以来在发生卡顿或ANR的时候,便可以收集到对应期间的具体执行逻辑。

Bugly默认会以60次/秒的频率对主线程的执行栈进行采样,在发生卡顿或者ANR时,将之前采集到对应时间的调用栈作为对应的诊断数据一并上报到后台进行调用栈还原和分析。因此Bugly可以提供卡顿的具体耗时方法和执行情况。

限于采样频率的缘故,Bugly计算出来的方法执行耗时一定是采样间隔的整数倍,存在一定的误差。对于默认60Hz的采样频率,其误差应为±16ms。

ANR 问题列表

Bugly将上报的ANR个例按造成其异常的调用栈的特征进行聚类,将同类问题作为一个issue进行统计,方便业务跟进和定位问题。对于每一个issue,问题列表中会统计其影响设备数、最近上报时间等信息,与其他问题列表的表现类似。

调用栈特征提取

如前所述,Bugly会将造成ANR的调用栈上报到后台。为了有效聚类个例问题,Bugly通过提取关键耗时方法,使用关键耗时方法中的前三层函数作为其特征。

由于调用栈采集的为连续时间内的执行情况,因此按照栈底往上,将相邻相同的帧进行合并,便可以将调用栈作为调用树,其中每个节点表示对应方法的执行耗时。

在堆栈树中,找到耗时较大的一条路径,作为关键耗时方法,之后提取这条路径中的前三层作为其特征进行聚类。

问题列表 -- 无堆栈问题

在问题列表中,会存在一个特殊的issue类,其没有关键耗时特征栈——“无堆栈问题”。无堆栈问题的出现是由于Bugly采样机制导致的。

对于没有开启堆栈采样的个例,由于其无法提供对应的诊断堆栈信息,但在某些情况下,需要查看对应的用户或设备的ANR发生情况,故此问题列表中会展示这部分发生ANR的个例,并将其都归类在“无堆栈问题”中。

注意:在堆栈采样率开启到100%时,依然会有无堆栈问题上报,属于Bugly已知问题,在早期版本中表现更加明显。其是因为SDK在堆栈持久化时有一定延迟,导致堆栈还未持久化,进程就已被杀导致,Bugly目前这在逐步优化以减少此类情况的出现。

ANR 个例分析

个例分析是解决ANR问题的重要依据,其提供了造成ANR的详细原因,提供有效的诊断数据定位ANR问题。通过问题列表中的issue进入到其对应的个例列表中,与其他问题列表及个例分析类似,同样提供基本的个例信息,例如发生时间、上报时间、用户/设备ID等,同时也提供对应的筛选条件。下钻分析也是类似,提供了该issue下的个例上报趋势、版本分布等等信息,此处不再赘述。

个例问题的消息详情中,提供了出错堆栈、全线程堆栈、操作日志、符号表、现场数据等信息,其中操作日志、符号表、现场数据等都与其他问题的消息详情一致,此处不做多余赘述,重点解释出错堆栈和全线程堆栈。

1. 出错堆栈

出错堆栈即为前述中提到的周期性采集的主线程调用栈,提供了三种展示方式:时间片、堆栈树、火焰图。这三种方式都是用同一份数据生成的,只是以不同的方式呈现,方便查看。

  • 时间片:采集的原始数据,以采集的时间区间的顺序依次展示,其中
    TimeSlice: xxxx
    标识对应的采集区间内的调用栈详情,点击查看即可。在采集和上报时,会将相同时间片的相同调用栈进行合并,展示时亦是如此。例如TimeSlice: 10
    20表示采集区间为10~20时刻都是一样的调用栈,即这里一直在执行同样的逻辑;
    这里的时间区间时相对值,表示最后采集时间范围内的第几次采集,因上报堆栈均为判定疑似ANR发生的时刻最近5s内采集,序号有远及近的表示对应的采集周期,例如1表示ANR判定发生那一刻时保留的最早的一次采集数据,以此类推。

  • 堆栈树:堆栈树为更为有效的一种表现方式,其通过对时间片数据从栈底依次往上聚合,生成的调用树,其每一个节点表示对应方法的执行时间,可以更加直观的发现卡顿方法及其时间;

  • 火焰图:堆栈树的图形化表示方式;

出错堆栈直接展示了卡顿的时间和对应的方法,对于一般问题而言,基本可以定位到导致异常的方法。但出错堆栈只包含主线程的状态,但有些ANR问题可能是由于主线程等待锁导致的,此时需要知道锁被何处持有,才能更好的定位具体原因。此时就需要用到全线程堆栈了。

2. 全线程堆栈

全线程堆栈是在判定疑似ANR时捕获的所有线程的调用栈,包括主线程自己,其目的是方便业务查看锁竞争的问题。

一般而言,若主线程因为锁等待造成卡顿,按照调用栈找相同的锁使用逻辑,可以查看其他线程是否有持有对应锁或等待相同的锁,便可以知道是否发生了死锁或长时间的锁等待等问题。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号