ANR监控平台设计

得物之前对于线上的ANR问题都是采用接入的bugly平台进行问题收集和平台展示的,bugly提供的ANR相关信息对于 实际解决ANR问题 信息度不够、并且对于ANR日志的聚合实现效果并不好。因此得物在参考业界各大厂商分享的ANR监控及治理方案后,我们开始着手建设自己内部的ANR监控平台,本文主要介绍 ANR监控 平台部分的实现,Android客户端测相关ANR上下文信息的采集实现不做具体阐述。

ANR 信息的处理

后端在收集到客户端上报的ANR信息后,需要对ANR数据进行数据分析、日志聚合。首先整理下目前端上报的ANR信息内容,主要包括以下几点信息

  • ProcessErrorStateInfo
  • tomstone信息
  • 以采样方式 收集的主线程函数调用信息
  • 主线程 Looper处理的 Message信息,包括 历史调度的Message信息、当前处理的Message、未处理的Message队列
  • APP状态信息
    • 应用前后台状态
    • 应用运行时长
    • 等等

以下针对上述几个信息的处理,阐述需要解析的内容。

ProcessErrorStateInfo解析

ProcessErrorStateInfo 是通过ActivityManager的getProcessesInErrorState()函数获取的,主要包含以下信息

  • processName
  • pid
  • uid
  • tag (如果ANR发生在 Activity、Service等系统组件上,则tag会被设置为对应的组件名)
  • shortMsg (ANR 信息的简短描述)
  • longMsg (ANR 的详细信息)

这里主要关注的时longMsg的信息, 通过正则匹配的方式,从 longMsg中可以解析出以下关键信息

  • ANR 发生的组件
  • ANR 触发的原因
    在这里插入图片描述

tomstone 信息解析

关于 Android tombstone文件包含的信息可以参考 debuggerd中 tombstone的相关代码 ,得物目前在线上采用的是iQiyi开源的xcrash进行ANR tomsbtone信息的采集。 采集的tombstone文件中主要包含以下信息

  • Crash相关元信息
    • Crash类型 (anr、java 、native)
    • 应用启动的时间
    • Crash 的时间
    • 应用版本号
    • 系统版本
    • ABI
    • pid、tid、crash 线程名
  • 虚拟机GC相关信息
  • ANR时各线程的现场信息
    • 线程当前调用栈
    • 线程优先级 prio
    • 线程状态
    • 持有、或者等待的锁信息
    • 线程 nice值、utm、stm等信息
  • logcat信息
  • open files
  • memory info

对xcrash的 tombstone信息的收集,直接采用的是 xcrash中的代码

Looper Message回溯信息的解析

在Looper回溯的数据中包含,在处理状态上包含三类Message,1. 已处理的Message 2.当前正在处理的Message 3.未处理的Message;

对于已处理的Message,采集的Message执行的耗时时间(wallTime),以及cpu执行的时间(cpuTime),根据这两个时间的可以大致判断,主线程cpu的调度情况,如果两者之间的差值较大 (cpuTime 只占 wallTime的30%以下) ,考虑可能存在比较严重的线程调度抢占问题。

堆栈采样信息(method trace)解析

得物会在APP运行时,每隔一定的时间间隔(50ms)采集主线程当前的执行堆栈,总计会保存10S~20S 的线程调用栈,当发生ANR时,采集的堆栈信息会随ANR信息一起上报。
通过在后头对该采集的堆栈进行处理,可以预先分析出较为耗时的函数,这里简单描述下解析的方式

  1. 首先将采集的堆栈遍历解析,并按照函数的调用关系转成NodeTree的形式
  2. 从根节点开始,层级遍历 MethodNodeTree,找出所有耗时大于一定阈值的函数
  3. 过滤掉一些白名单函数,比如 com.android.internal.os.ZygoteInit.main 、android.app.ActivityThread.main

部分代码

public class MethodNode {
	 private List<MethodNode> children = new ArrayList<>();

    //method cost time
    private int cost;
	
    private String fullMethodName;

    private JavaStackFrameSnapshot javaStackFrameSnapshot;
    
}

在这里插入图片描述

在这里插入图片描述

ANR 日志的聚合

对端上上报的 ANR 日志进行聚合可以更好的对ANR问题进行分类,评估不同问题在线上发生ANR的严重程度,集中治理。

聚合策略

在线上ANR发生的原因多种多样,简单归类的话可以分为以下几种情况

  • 主线程慢函数执行导致
    • 比如主线程执行繁重的代码,比如对大数据量的排序
    • 主线程上执行 文件IO操作
  • 锁等待、死锁
  • 系统负载较高、当前进程的CPU调度受到影响
  • …其他异常情况

根据以上的分析,针对可能因为慢函数导致的ANR问题,通过上节的 “堆栈信息解析”环节已经解析出所有耗时大于一定阈值的应用内函数,在符合一定条件的情况下(主线程cpu调度正常) 可以按照 耗时函数进行聚合

对于不存在明显耗时的函数,可以先简单的按照 发生ANR的组件+ANR触发的原因 两个关键信息进行聚合,先按照这种比较粗的粒度进行聚合,再进行分析,如果发现一定的规律后,可以在平台支持一些 特定规则自定义聚合能力进行数据聚合。

平台体验

这个小节讲的是,在平台展示上,如何展现现有的这些数据、信息,更好的发现定位问题。对于每一个ANR日志来说,除了一些通用的埋点信息外(如userId、sessionId、deviceOs等),更重要的是对ANR相关上下文信息的展示。

问题列表

ANR日志经过聚合后,分成一个个ANR ISSUE,每个ISSUE都有单独的处理状态及趋势 ,以便进行问题治理及问题趋势的跟进

在这里插入图片描述

在应用整体上,也有相应维度的趋势图,可以观测目前线上ANR的情况。

在这里插入图片描述

问题详情

从问题列表中,点击每个问题时,开始进入问题分析处理的步骤,在聚合算法实现较为完备的情况下(在实际场景中,以ANR目前原因的多样性,较难达到),每个问题详情都是一个特定原因导致的ANR、或者是发生在某一个特定的应用组件上的ANR。
在问题详情页,同样可以展示该问题的发生趋势,以及问题处理状态等,但是更为重要的是 平台提供哪些关键元素信息来帮助开发者定位ANR问题。

在这里插入图片描述

日志详情

每个ANR ISSUE 都是很多 ANR Log的集合,即ISSUE 和 Log,是一个一对多的关系,在问题详情页,可以查看该问题下面上报的所有异常case(即日志),点击每个异常case时,可以查看该日志的详细信息。在日志详情部分,ANR平台将上个小节 解析的所以信息,包括主线程 Message回溯信息、method trace信息,各进程CPU使用率等,以更友好的可视化方式进行展示。
在这里插入图片描述

ANR 基础信息的展示
  • ANR 触发的组件
  • ANR触发的原因
  • ANR 发生时的应用的前后台状态
  • 应用运行时长

  • 在这里插入图片描述
ANR后一段时间各进程CPU的使用率信息

在这里插入图片描述

ANR 时各线程现场信息展示

在这里插入图片描述

Looper消息回溯信息的展示

Looper消息的回溯是可以从主线程处理Message的视角看消息的分发、处理情况。ANR发生的原因除了耗时函数外,也可能是由于主线程CPU的调度受到了瓶颈、或者是向Looper发送了过多消息导致等诸多原因。
在这里插入图片描述

logcat 信息

从logcat信息中,可以辅助验证、获取ANR发生前后的一些信息。
在这里插入图片描述

函数执行火焰图(Flame Chart)的展示

火焰图可以以全局的视角来看应用函数执行的情况,更直观的分析出可能导致性能瓶颈的调用栈。
在这里插入图片描述

使用ANR监控平台排查问题的思路

简单来说,通过现有的平台,可以从以下几个方面来分析ANR问题

  1. 从ANR的前后台状态、应用运行时长、进程cpu使用率、Message Trace 中Message 的cpuTime、wallTime的差值 大概对当前应用的cpu调度能力有一个评估。
  2. 从火焰图中 分析是否有明显耗时的业务代码
  3. 从 MessageTrace中看是否有明显异常的消息处理(单个消息处理过长、Message数过多 等情况)
  4. 从CPU使用率上,看 user和kernel的使用占比有无异常,如果kernel使用较高,则分析是否发生了大量的系统调用,如果user层的使用率较高,则说明在业务侧有较多繁重的任务执行

部分问题治理分享

本节是分析在ANR平台上线后,利用ANR平台定位到的一些ANR问题。

调用Kotlin Function 对象的 toString 函数调用导致ANR

在排查ANR日志时,发现有多个ANR 日志中,Kotlin 的KfunctionImpl.toString都比较耗时,火焰图case如下
在这里插入图片描述

于是在线下对Kotlin的该函数进行了测试,在demo中参照火焰图中的业务代码进行了场景还原
在这里插入图片描述

结果发现,一个简单的toString调用,在demo中耗时达到了 118ms。经过一系列的变量测试发现,在引入kotlin后,并且 引入了 kotlin-reflection库的情况下, 调用 Kotlin Function对象的toString函数时 耗时非常严重。因此在业务代码中要注意,不要对kotlin的 函数对象 直接或者间接的触发toString操作。

主线程执行View.getDrawingCache进行bitmap转换

在这里插入图片描述

从队列中,发现大量的消息已被阻塞,并且在前序的Mesage处理记录中,发现有耗时900ms的消息处理。
转到对应的 method trace中发现,卡顿主要是由于在主线程对Webview获取drawingCache导致的。
在这里插入图片描述

在线下进行测试时,可能在正常情况下该函数的耗时并没有这么耗时,因此暂不能确定该问题是如何引起的,但是从代码实现的角度,也不应该在主线程执行类似的耗时操作,因此可以将该操作移入异步线程执行。

SP 等待任务造成的 ANR 问题

在ANR平台上线后,发现目前收集的用例中 有大量数据是由于 系 统组件在 onPause、onStop等相关流程需要等待所有异步的 SP写入操作完成造成的
在这里插入图片描述
在这里插入图片描述

得物目前的业务都已使用 MMKV 替换SP,主要使用SP的场景都是接入的一些三方SDK,而这些sdk一般不需要对这部分数据在组件跳转间 对“异步任务数据写入完成”的强保证,因此得物对这部分系统的机制进行了HOOK,在这部分HOOK功能上线后,ANR率有了明显下降。

后续优化迭代方向

从目前ANR平台上线后 问题的治理情况上来看,大部分容易解决的问题都是从 火焰图上分析出明显耗时的业务代码从而解析的,在实际的线上场景中,目前还是发现有一些特定的case以现有上报的日志信息无法明确给出发生ANR的根本原因。后面会根据每种case,加强其他方面的监控能力,来协助定位问题。

端侧

其他上下文信息收集的增强

比如在线上发现,某些ANR问题 耗时函数卡在一个简单的 File.exist函数调用上,针对此类问题,后续可能需要结合native层 对file io操作的一些监控、hook来协助定位问题。
在这里插入图片描述

端上 Method Trace 性能优化

目前得物APP在端上进行Method Trace的方案是 在子线程定时通过主线程的Thread对象 调用getStackTrace进行采集,虽然该函数的执行是在异步线程,但是在虚拟机底层在非当前线程获取某个线程的堆栈时,会先suspend对应的线程,获取完成后再resume。 因此如果对主线程抓起堆栈的频率较为频繁的话,势必会影响主线程正常函数的执行。
在这里插入图片描述

我们的MethodTrace的要求是应该尽量保证其只产生极小的运行时开销,避免为了监控反而影响应用性能,虽然目前的这套采样方案,控制的最小采样间隔为50s,对性能影响不大。在facebook 的profilo开源库中有一套 异步非阻塞获取线程堆栈的方案,不过其目前只开源了适配了 Android 9及以下的版本。字节跳动的西瓜视频 分享了另外一种 Java Method Trace ,不过该方案也是需要suspend thread, 也是一个可以参考的方向。

平台测

MessageTrace 体验优化

目前Looper Message Trace 功能和 Method Trace(函数执行火焰图)功能是两个比较独立的功能,当我们在Message Trace 功能 中发现某个消息比较耗时,正常情况下是希望能够直接看到该消息具体做了什么操作,因此在平台测可以做的一个优化是, 当点击Message时,根据这个Message的处理的开始结束时间,自动截取对应时间的函数堆栈片段,进行信息展示。

参考资料

  1. https://www.ssw.uni-linz.ac.at/Teaching/PhDTheses/Hofer/PhD.pdf
  2. https://mp.weixin.qq.com/s?__biz=MzI1MzYzMjE0MQ==&mid=2247489902&idx=1&sn=bfdf9f48dc6dc973722b5dcab9cd5882&chksm=e9d0d28cdea75b9ad255eb5de227240d2e6f0e9d66e562d3f49cf69f8ed4127c9954ef21bb6d&scene=178&cur_album_id=1780091311874686979#rd
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卓修武

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值