作者 kyson老师 2020.03.28 18:55:00 APM系列(一):基于腾讯 Matrix 实现卡顿监测 评论:0 ### 背景 >APM(Application Performance Management,应用程序性能管理) 是 APP 满意度提升的一个依据,也可以排查 APP 灰度过程中遇到的一些问题。目前笔者公司做的 APM 包括但不限于:APP 实时内存占用,APP 线程数,APP 电量消耗速度等。  如图所示就是 APM 项目上线后的一览图,假设线上版本是 6.6.6,当天要发布的版本是 6.7.4,那么通过上图可以了解到: >随着版本 6.7.4 灰度量越来越大,线程数趋于和线上版本 6.6.6 一致。反之,如果灰度过程中出现线程数和之前出现较大分歧,那么我们有理由相信这次灰度版本可能有 bug,从而尽快安排修复。 ### 开始 以上用一个简单的例子来说明 APM 的作用。接下来,我们开始我们的主题:卡顿监测。 APP 的卡顿监测是在 APM 中难度较高的部分,主要体现在: 1. 如何有效监控主线程卡顿,但又不会对 APP 本身的性能产生影响?(以卡顿上报逻辑为例:如果每次监测到卡顿就上报,那可能会造成频繁上报导致的网络占用问题) 2. 监测到卡顿后能否立刻定位到卡顿页面,甚至导致卡顿的函数? 3. 监测到卡顿后,能否更具体给出卡顿时长? ### 卡顿监测 如果读者对以上问题已经胸有成竹,那么可以直接跳过本文了,否则我们继续分享。 监听主线程卡顿其实方法很多,比较常见的还是通过 Runloop 实现的: 主线程有其对应的 Runloop,Runloop 负责处理各种事件源,包括: 1. Source1 (来自系统内核或者其他进程或线程的事件) 2. Source0(主要是用户事件,也就是我们需要监控的事件) 3. 定时器(定时器事件,我们可以忽略) 当 Runloop 处理相应事件时,会有一些关键时间点,Runloop 的 API 也留出相应回调供应用层调用,我们通过以下方法添加监听并处理回调即可: ``` //创建监听 CFRunLoopObserverCreate(); //添加观察者 CFRunLoopAddObserver(); ``` 通过以上两个函数监听卡顿发生的原理是:当 Runloop 处理事件之前会进入 `kCFRunLoopBeforeSources` 状态,处理完后进入`kCFRunLoopAfterWaiting`,我们获得这两个状态下时间差即可知道卡顿时长。 ### 获取调用堆栈 每次统计到有卡顿后,立刻保存监测线程(一般是主线程)的调用堆栈。 获取任意线程调用栈目前有两种方式。 1. 拿到栈的指针(StackPointer)以及栈帧指针(FramePointer),递归到栈底。 2. Signal 这里我们使用第一种方式。这是 [KSCrash](https://github.com/kstenerud/KSCrash) 的作者想到的方法。 ### 过滤以及优化 如果一个卡顿较频繁,那么频繁上报返回会导致卡顿监测系统对 APP 本身的影响。为了避免这个问题,我们可以对卡顿监测做个优化: (1)卡顿个数满足一定条件即可直接过滤,不需要再次上传 (2)不是每个安装 APP 都需要卡顿监测,做个随机抽检即可。 ### 干货时间 有理论当然也要有代码,笔者已经将 Matrix 中的卡顿部分的代码抠出来,分享给大家,方便大家专注卡顿部分的研究,代码在这里:[https://github.com/kysonzhu/BlockMonotir](https://github.com/kysonzhu/BlockMonotir) ### 总结 本文提及的所有问题以及解决方案都在腾讯的 APM 系统 [Matrix](https://github.com/Tencent/matrix) 中实现,有兴趣的朋友可以阅读一下其源码。 ### 扩展阅读 [RunLoop 监控卡顿为什么要用kCFRunLoopBeforeSources和kCFRunLoopAfterWaiting](https://www.jianshu.com/p/6c10ca55d343) [关于RunLoop你想知道的事](https://zhuanlan.zhihu.com/p/62605958) [iOS 之 Thread调用栈学习](https://elliotsomething.github.io/2017/06/28/thread%E5%AD%A6%E4%B9%A0/) 赏 标签:matrixapm内测监控