iOS卡顿监控方案总结

968 阅读2分钟

思路

  • iOS开发中一切与UI相关的操作都必须放在主线程里执行,系统会以约16.7ms将UI的变化重新绘制。
  • 主线程对于任务的处理是基于Runloop机制, 它对外有如下所示: 截屏2022-12-08 22.45.15.png

如何设计

  • 我们监听RunloopEntryRunloopBeforWaiting(进入休眠前)RunloopAfterWaiting(退出休眠后)RunLoopExit(退出RunLoop)四个时机。
  • RunloopEntryRunloopAfterWaiting两个时机,标记isRunning = YES,表示主线程开始处理耗时操作了。并开始计时startTime = currentTime,表示耗时操作的起始时间
  • RunloopBeforWaitingRunLoopExit两个时机,isRunning = NO则表示主线程处于休眠或退出,未处理耗时操作。 截屏2022-12-08 22.40.33.png
  • 另外开启一个子线程,循环检测isRunning的值。
  • isRunning = YES,并且计算当前时间起始时间的时间差: diff = currentTime - startTime
  • 若时间差>=50ms,则保存一次主线程堆栈,休眠50ms,继续判断isRunning = YES步骤。
  • 当时间差大于阈值,则整合堆栈,进行卡顿上报。

一些细节

  • 这里涉及几个问题:
    1. 为何连续保存主线程堆栈
    2. 如何设置卡顿阈值
    3. 如何整合堆栈
    4. 如何上报卡顿

1. 为何连续保存主线程堆栈

  • 连续保存是为了尽可能的抓取到卡顿堆栈 image.png image.png
  • 在考虑到性能损耗,折中选择当卡顿时间超过50ms时,开始保存主线程堆栈。 image.png

2. 如何设置卡顿阈值

image.png

  • 当卡顿耗时在800ms-1000ms时,被定义为一般卡顿。
  • 其他依次如图。

3. 如何整合堆栈

  • 当达到卡顿阈值时,我们从保存连续卡顿堆栈的数组中,获取距离卡顿时最近的主线程堆栈,比如卡顿耗时900ms,我们就获取800-1000ms时间段内保存的堆栈。(即800ms、850ms、900ms、950ms、1000ms五次堆栈信息) image.png

4. 卡顿如何上报

  • 简单的一套先入库,再读取上报,成功后删除对应数据的机制,有效避免数据丢失。 image.png

疑似卡顿的采集

  • 在微信的卡顿监控方案里,认为单核CPU超过80%认为是疑似卡顿
  • 如果希望采集疑似卡顿,在子线程检测卡顿耗时的同时,也检测CPU的占用率。 截屏2022-12-09 00.21.12.png

符号化处理

  • 上报到服务端的数据,需要通过脚本找到对应安装包dsym文件进行符号化处理。即在打包时,就需要脚本管理每个版本的dsym文件。 截屏2022-12-09 00.38.21.png

火焰图