作者:姜凡(步定)
本文为《钉钉 ANR 治理最佳实践》系列文章首篇《定位 ANR 不再雾里看花》,主要介绍了钉钉自研的 ANRCanary 通过监控主线程的执行情况,为定位 ANR 问题提供更加丰富的信息。
后续将在第二篇文章中讲述钉钉基于分析算法得出 ANR 归因,上报到 ANR 归因监控平台,帮助研发人员更快更准确的解决 ANR 问题,并总结钉钉 ANR 实战踩坑与经验总结
相信大家对 Android 的 ANR 问题并不陌生。钉钉作为一个用户数超 5 亿,服务着 2100 万家组织的产品,基本上其他 App 遇到的 ANR 问题,我们都会遇到。
和大家一样,我们最初在分析 ANR Trace 日志的时候,都会不禁怀疑上报的堆栈是否真的有问题,总有一种雾里看花的感觉。
本系列文章主要介绍钉钉在 ANR 治理过程中的思考方向,工具建设,典型问题等,希望能够通过本次分享,为有 ANR 治理诉求的团队提供一定的参考。
术语表
系统 ANR 完整流程
系统 ANR 完整流程可以分为如下三个部分:
- 超时检测
- ANR 信息收集
- ANR 信息输出
对于超时检测的逻辑,业界已经有比较详细的阐述,此处不再赘述。重点聊聊检测到超时之后的处理逻辑。详细源码可以参见:ProcessRecord.java,ANR 信息收集和 ANR 信息输出两个流程图如下:
如上图所示,从系统源码中,得到的启示有:
- ANR Trace 的堆栈抓取时机是滞后的,其堆栈不一定是 ANR 根因。
- System Server 会对多个进程发送 SIGQUIT 信号,请求堆栈抓取的操作。
- 收到 SIGQUIT 不代表当前进程发生了 ANR ,可能是手机里有一个其他的 App 发生了 ANR,如果不进行 ANR 的二次确认,就会导致 ANR 误报。
- App 可以通过进程 ANR 错误状态感知发生了前台 ANR 。
刻舟求剑的 ANR Trace
- 以广播发送导致 ANR 的过程为例,当 System Server 进程检测到广播处理超时时,会发送SIGQUIT 信号到 App 进程, App 进程收到信号之后,会将当前所有线程的执行堆栈 Dump 下来为 ANR Trace,并最终输出。
- 然而如图所示,这个 Dump 时机是有一定的滞后性的,真正导致 ANR 的 长耗时消息3 已经执行完了。当前执行消息5 是作为替罪羊被抓到的,甚至 当前执行消息5 到底消耗了多长时间也不确定。因此 Android 系统设计提供的用来分析 ANR 问题的 ANR Trace ,其实只是刻舟求剑, 并不一定能定位到 ANR 的根因。
ANR 误报过滤
鉴于前面提到的收到 SIGQUIT 信号,并不代表当前进程发生了 ANR,需要一个二次确认逻辑,进行误报过滤。
钉钉采用的方案是:
- 在收到 SIGQUIT 信号之后,在 20 秒内轮询进程错误状态的确认是否为前台 ANR。
- 与此同时,因为发生后台 ANR 之后,系统会直接杀进程,而其他进程 ANR 并不会导致进程被杀,因此可以通过持久化的方案来区分。
详细流程图如下:
ANR 监控工具
工欲善其事,必先利其器。钉钉自研的 ANRCanary 监控工具,通过轮询的方式持续记录主线程最新任务的执行耗时,到发生 ANR 时,基于耗时最长的消息定位 ANR