一种检测和测试容器高可用线程卡顿的技术(Android)
原创
巧善
蚂蚁质量AnTest
蚂蚁质量AnTest
微信号
gh_ac5eb4e2c97a
功能介绍
蚂蚁质量技术团队的技术分享以及产品介绍。
线程卡顿的问题已经成为很多app产品的重大关注对象,市场上也出现了很多相关的技术探索,如Raster、Sliver、BlockCanary、Matrix、Dokit、ANR-WatchDog、Profilo、Nanoscope...但都较为局限或不适用我们实际的场景。
每个业务都有自己的特性,市场上大多数的技术都是一种很通用的技术形式,解决是common的问题,但是如果我们想突破瓶颈,就需要结合业务场景结合已有技术继续深挖研究出新的技术模型。
本文章主要介绍了我们如何站在巨人的肩膀上去更准确的检测到卡顿的根因慢函数;又如何把自动化测试紧密的结合起来形成一套完整的线程卡顿的卡口体系。
一. 线程卡顿的根因分析
1.1 首先明确卡顿和ANR的区别
卡顿
:是指程序运行慢导致当前使用不流畅
ANR
:在没有特定的自定义技术实现的情况下一般是指因主线程耗时超过了系统设定的阀值而抛出的系统anr导致当前应用不可用的现状
两者联系
:卡顿是发生ANR的前兆,卡顿造成的卡死必然引起ANR,两者表现形式背后的技术原理都是一样的。
1.2 ANR的技术根因
应用程序的主线程响应超时会导致ANR,由AMS进行通知且发生在四大组件:
Service、Activity、ContentProviver,BroadcastReceiver
下面以Service为例,去了解ANR的前世今生,不难猜测任何机制的触发一定要经过:
机制预埋、机制触发、结果响应
。
1.2.1 机制如何预埋?
说明
:
1.关键点一,timeoutneed默认是true代表是需要在此监测的,但是只有在三方服务还未启动时不需要检测,三方服务一般是系统服务,会先于用户服务启动。
2.关键点二就是真正的anr监测超时的设置了。
说明
:
1.其实很简单,就是进入一个消息队列发送一个延时的消息。
2.两个时间点在代码里可以看到如果service在前台是20s,如果处于后台是100s。也就是说两种场景下在有限的时间内没执行完就会发生ANR。
1.2.2 机制如何触发?
说明
:
1.以上标红的地方是service的ANR预埋的时间到了之后message触发的地方。
2.这个时候还不知道是否发生了ANR,只是说时间到了去检测一下ANR是否发生。
1.2.3 结果如何响应?
说明
:
1.关键1很明显是代表有没有ANR的标识。
2.关键2就是核心,给ANR的标识赋值。
3.所有正在执行的service都会进入AMS的 mLruProcesses这个队列里,同时每个service的基本信息会在ServiceRecord里记录,executingStart记录的就是service真正启动时的时间可以看
ActiveServices
的
realStartServiceLocked
的方法内的这一句。
4.用当前的时间减掉我们ANR的预埋时间,代表的就是service最早的启动时间,如果比这个还早那就代表ANR了,所以有了
这个判断。
5.最后根据timeout这个标识和正在运行的process里是否包含此service来证明确实发生了ANR。
6.在关键2会看到如果发生ANR后了,会把堆栈信息收集手释放掉堆栈信息的一些操作也是为了性能考虑代表此次ANR监听采集完毕。
7.最后根据anrMessage这个错误进行系统通知,接下来就是会看到系统弹出的无响应界面。
1.2.4 总结ANR根因流程
说明
:
1.以Service为例就是在Service真正启动的时候记录启动时间,同时通过一个Handler发送一个满足ANR延时的消息,在预期的延时信息处理时,去check正在运行的service的开始执行时间是否遭早于ANR发生的最早时间,如果比ANR预期的时间还早说明执行超过了预期时间,会直接上报系统产生ANR,锁住进程。
2.另外,service的执行也是队列的形式,一个执行完会把执行的时间设置成预期的下一个执行时间,根据exectuingStart和nextTime进行执行。
主因:
主线程阻塞、挂起、死循环、执行了比较耗时的操作等;
他因
:其他进程对CPU的使用占用时间过高导致应用进程抢不到CPU的时间片。
三.我们的痛点
3.1 现状
根据以上的实际案例我们也发现,虽然我们版本发布前进行了严格的测试包括格鲁特稳定性测试,以及也分析了微镜5s和10sANR的原理但是仍旧拦不住我们的卡死的问题。
通过我们的实际的案例分析以及我们对目前卡顿测试手段的分析,我们得出以下原因和痛点
:
3.2 痛点明细
目前已有的线程卡顿检测技术只能检测到当前Message耗时但存在目标堆栈偏移问题更不能回放卡顿前函数状态,比如ANR-WatchDog和blockCanary。
微镜的5s和10s的ANR拦截就是Watch-Dog的原理,但是缺点一:不能线下测试;缺点二:堆栈偏移且不够准确导致分析问题困难,解决成本太高。
ANR问题的修复验证困难且验证周期长,目前完全依赖线上问题是否暴露来说明是否修复。
没有针对性的测试case和线下自动化卡口测试,使其问题流到线上造成风险治理偏右。
没有基线,单纯的看和上个版本的ANR率不能细分版本的劣化的根因,解决问题处在拆东墙补西墙的漩涡。
没有一套完整完整的防劣化系统,导致我们无法很好的拦截新生问题使其anr以及卡顿的问题反复纠缠我们。
四.技术实现说明
4.1 技术方向和特性
业界所有的稳定性技术无非分两派:一是技术识别方向;二是测试场景覆盖方向;
识别分析方向一般是假设场景发生和存在的情况下,有足够的能力去搜集和分析发生的问题。
识别分析方向的能力一般会聚焦:如何能做到颗粒度更细的内存分配,堆栈的精准获取,关键节点的耗时精准捕捉等技术专项,它的基石是技术创新。
测试自动化方向则是识别分析方向的能力落地,测试自动化方向更专注的是:测试提效能力,全面发现问题的能力,它的基石是技术测试用例的设计和高阶白盒的技术创新。
两个方向都是业界比较关注的方向,但是稳定性的治理单凭一个方向是不够的,可以说两者缺一不可,因为再牛的技术需要落地场景,因此我们针对线程的卡口做了一整套;
因此线程卡顿的卡口的特性围绕四个能力如展开技术识别能力、技术验证能力(自动化测试能力)、技术分析定位能力以及实验室能力。
4.2 技术架构
主要包含几个子系统:
函数级别的识别系统,核心技术模块插桩系统,
基线Case执行系统,卡口解析和识别系统,卡口自动化适配和分析系统,卡口基线数据生产系统,卡口数据劣化分析系统。
4.3 技术识别能力
技术方案
:方案是通过监控单Message、多Message、单函数、容器对外扩展实现的方式对运行期的代码进行全面监控。
4.3.1 多Message耗时监控原理
我们都知道android的所有执行都是handler和looper的那套机制,最终都是通过message一个个去执行,如果当前message被阻塞就必然会引起卡顿(如果实在主线程),多Message的耗时原理可以以目前支付宝使用的这套类似ANR-WatchDog来分析,核心代码:
核心原理如下
:
安排一个可运行对象尽快在 UI 线程上运行。
等待 5 秒钟。(5 秒是默认值,但可以配置)。
查看runnable是否已经运行。如果有,请返回 1。
如果 runnable 尚未运行,这意味着 UI 线程已被阻塞至少 5 秒,则会引发所有正在运行的线程堆栈跟踪的错误
通过以上看我们会发现这种机制存在2个问题
:
问题1:ANR监控过程中可能会包含多条Message,因为设置的阈值是5s。像这类其中一个Message耗时高达4.5s的慢消息,因为机制原因不会被检测出来。
问题2:ANR监控周期内涉及3个Message,Message耗时高达4.9s,Message3耗时只有150ms,按照ANR-WatchDog堆栈采集机制,会定位到Message3上,单Message3并不是真正造成此次ANR的原因。
4.3.2 单Message耗时监控原理
目前我们通过在Looper处理Message前后做卡顿时间判断
核心技术原理
:
但是该方案同样存在一下问题
:
问题1:ams关键消息(ams会做anr监控)前面有11个消息,每个消息耗时30ms不是很长,但是合在一起就是330ms,超过了AMS Input ANR 5s监控了。所以这类问题如果你不知道Message之前发生了什么,基本很难定位问题。
问题2:最后堆栈采集也可能会存在轻微堆栈偏移问题,尽管一般都是从卡顿阈值80%时开始采集堆栈,最后做个TopOne筛选再记录。
问题3:单Message卡顿监控过程中会包含多个方法调用,因为设置的阈值是200ms。像这类其中一个方法耗时高达195ms的慢方法,因为机制原因不会被检测出来。
问题4:因为该耗时监听原理,基本都是代理现有print,然后自定义自己逻辑后再设置给Looper。因为自定义逻辑通常都是根据字符串来判断一个Message执行开始和结束,所以存在字符串拼接和销毁问题。
4.3.3 我们的技术解决方案
我们分析了市场上的技术方案存在的不完整性也是导致我们上面痛点讲到的问题一旦发生解决成本高且不好定位的一个根本的技术原因。
了解到以上各个方案的技术原理后,解决这种技术风险带来的痛点是我们必须要做的,因此我们的技术方案是结合我们业务特性柔和了以上技术,
通过堆栈追溯、单函数插桩、关卡设定
来解决以上技术风险从而
解决技术风险带来的堆栈不准确分析问题的难的痛点。
通过以上方法我们的检测结果如:
4.4 技术验证能力
目前本地检测能力有整体
卡顿检测、慢函数检测、扩展检测、IPC检测等
卡顿
核心是挖掘Handler特性,通过对单/多Message及相关属性的多维度分析,推断出卡顿风险点。
检测能力理论上大于端上检测能力,主要原因是线下使用,无需过多担心兼容性问题。
慢函数
当前对慢函数检测并非是对线上所有函数都拦截计时,这样的成本太大。
一是统计成本太大(侵入代码,计算耗时),二是聚合成本太大(调用链串联与过滤)。
当前采用的是堆栈定时采集再聚合堆栈的方式,聚合完堆栈再单独计算各函数耗时。
扩展
线上:当发生ANR时,没有一条消息是无辜的。各种的扩展实现也是同样如此,分析一类ANR问题时,我们通常需要具体化这类ANR所涉及的各扩展的详细耗时情况,才能给出优化策略。所以线上积攒着很多无解的ANR,这些ANR如果不采取特殊手段,但从现有信息确实无解。
线下:线下我们有自动化卡口能力,其中包含各扩展的检测。但毕竟自动化测试的场景和机型有限,无法和线上真实大数据相比,所以同样我们着手建立起了线上扩展监控能力。
IPC
目前是对耗时与传输大小进行统计进行实现。
当前还未投入到实际卡口项中,卡口能力需逐步有序推进,不做大杂烩。
通过ARTHook技术,对系统/业务代码进行Hook,实现IPC信息采集与尽可能的无侵入集成。
4.4.1 验证什么?
这里主要是验证其核心能力,更多的边缘能力不在此文章中做详细阐述;需要验证的核心能力主要有以下几个:
多message的情况下是否能检测到慢函数且堆栈信息正确
单message的情况下是否能检测到慢函数且堆栈信息正确
正常ANR的情况下是否能检测到且堆栈信息正确
真实的线上版本是否能拦截到卡口规则的问题
4.4.2 如何验证?
4.5 技术分析定位能力
4.5.1 卡口规则如何设计
设置三层关卡,层层深入,每一层都定义出自己的能力,如下:
4.5.2 卡口技术的实现
1、 KEY值:每个卡顿点KEY值定义规则。
首先我们需要给每个卡顿问题提炼出一个KEY值作为该问题的ID值;
既利用这个KEY值来聚合该卡顿问题出现了多少次,平均耗时是多少等数据;
卡顿问题的KEY值规则为:大于根节点1/2耗时的最后一个节点链路为此卡顿的KEY;
如下图的卡顿KEY值为:A->B->E。
2、慢函数类型:
卡顿标准:单方法耗时超出阈值且下方无子堆栈。
自动化分析会把此次卡顿中典型慢函数单独分析出来,如下方的CRVUtils.loadSo问题。
此类问题一般是锁等待耗时、native函
数调用耗时等,既如果多次超出阈值,一定是非正常耗时。
4.5.3 用例如何设计
用例设计考虑的因素
:业务理解、技术原理、实际问题分析、用例的分类设计、设计的方法、用例的可执行性和有效性。
实际举例
:
目标
:假设想挖掘ANR的潜在风险
业务对象
:容器
测试环境
:机型是Redmi Note4x-3G内存 && 独立实验室
详细设计
:
STEP1: 首先来理解业务,针对容器的业务特性直接操纵的宏观场景是小程序运行或者是h5的运行。
STEP2:理解ANR的技术原理,主因是主线程阻塞、挂起、死循环、执行了比较耗时的操作等;他因是其他进程对CPU的使用占用时间过高导致应用进程抢不到CPU的时间片。
STEP3:找个线上的例子实际分析,比如蚂蚁森林卡顿是因为线程阻塞导致(
整体分析下来业务存在分技术风险点:1.线程池管理不统一,存在多个线程池管理 2.单线程操作耗时
)。
STEP4:这种用例若设计该如何分类?我暂时定为典型性业务的技术类case。
STEP5:进行用例设计的方法选择,根据以上分析业务特性很明显适合黑盒的压测或者是特定的业务路径设计;而技术风险case适合白盒的case进行技术边界的压测。
STEP6:综合以上用例基本可以定型了,可执行和有效性我定义为-用最少的case覆盖尽可能最多的路径,否则就会造成冗余case只有数量没有质量。
用例明细:
用例特点
:可以吸收多种场景的黑盒case
用例目标
:
1. 验证进程之间是否有影响
2. 验证交叉case对进程和线程调度的影响,去尽量命中技术边界,挖掘技术风险类问题
3.验证压测的热启动是否存在内存泄漏
4.快速点击事件去交叉验证线程的响应和应用的响应速度,去尽量命中边界卡顿的场景
用例描述
:压测20000067预热小程序10次,每一次启动后静置10s后依次启动top10的业务小程序执行monkey,其中前五次是支付宝冷启动且执行次数为偶数的时候monkey模拟正常点击且执行10min,后五次是支付宝热启动且执行次数为偶数的时候模拟用户快速点击且执行30min;
用例执行代码
:(可一次执行n次,n个预热小程序以及n个top小程序,n:用户自定义)
Demo用例执行效果:
4.5.4 如何实现自动化测试
整体实现是通过java工程,实现
基线Case执行系统,卡口解析和识别系统,卡口自动化适配和分析系统,卡口基线数据生产系统,卡口数据劣化分析系统
五个子系统,具体代码实现就不粘贴了;
核心技术链路
:
Shell脚本:
负责用例的执行
Java:
负责整个数据的流通和分析
动态Html:
负责数据的展示
通信实现:
Java调用Shell,脚本执行完成通知Java;Java数据处理完成后通知Html;Html若需要发生数据行为需通知Java;各负其责互相解决耦合;
技术架构原型
:MVP
4.5.5 如何自动化分析问题
4.5.5.1 通用blockfile的卡口聚合
4.5.5.2 其他技术模块的卡口实现
原理流程图
:
4.6 实验室能力
实验室亮点
【
可测】
独立实验室,支持24小时无间隔测试,任意压测;
【
可扩展】
支持case的交叉执行,部分case可定制化路径;
【
可拦截
】灵活的规则应用和扩展,能使一些稳定性问题可测可拦截;存量问题也能线下历史复现;
【
可提效
】支持一键全自动化,
极大了提高了测试效率,支持实验室灵活部署;
测试流程体系
从测试-- >发现问题-->分析问题-->问题跟进解决-->问题修复验证的一条龙测试
目前已经上线的测试环境是低端机+2h的测试
五.展望
最终的目标是做成可伸缩性架构
问:什么是可伸缩架构?
答
:根据应用环境以及容器状态(比如webview 执行的效率,V8执行的效率等等这些特质化的判断来得出我们的运行环境),根据这些综合属性,我们动态的给当前容器运行环境定级。根据级别来动态的对容器现有能力做调整
问:可伸缩架构将来会有什么能力?
答
:比如低端机上关闭转场动画、关闭部分非必要预起操作、关闭部分非必要JSAPI调用、根据当前cpu或者内存情况动态调整容器内部对这两处高消费的实现
特别说明
:
我们的卡口技术体系可以完全吸收可伸缩架构将来的扩展,我们做架构设计的时候也是保留了这份扩展能力
如对本文有任何建议或问题,请关注我们的微信公众号,我们将私信回复 ❤️
预览时标签不可点
微信扫一扫
关注该公众号
继续滑动看下一个
轻触阅读原文
蚂蚁质量AnTest
向上滑动看下一个
知道了
微信扫一扫
使用小程序
取消
允许
取消
允许
:
,
。
视频
小程序
赞
,轻点两下取消赞
在看
,轻点两下取消在看
分享
留言