iOS CPU监控方案总结

2,561 阅读4分钟

背景

  • CPU作为性能监控的核心数据,目前APP监控主要有两个侧重方向
    1. 侧重监控CPU瞬时使用率,是一种偏向卡顿,流畅度的监控
    2. 侧重监控CPU平均使用率,是一种偏向耗能,发热的监控

方案一:瞬时CPU使用率检测方案

  • 轮询检测所有线程CPU使用总和,若CPU使用总和大于阈值,记录所有线程堆栈。
  • 此方案更适合结合runloop监控进行卡顿检测。 image.png

苹果能耗监控:Energy Logs

  • 监控CPU平均使用率的方案,是参考Energy Logs方案。

Energy Logs监控原理

image.png

  • 系统定期获取当前应用的线程堆栈。
  • 当App给CPU带来很大的负载,那么就会根据搜集的线程堆栈生成总调用树。
  • 触发生成总调用树的事件有两个:
    1. 前台3分钟平均超过80%的CPU使用。
    2. 后台1分钟平均超过80%的CPU使用。

Energy Logs总调用树

总调用树是将所有CPU异常线程的堆栈拍平,并聚合成一棵树。
  • 假设代码中有一个main函数和许多函数构成(method1、method2、method3、method4) image.png
  • 当程序运行到检测到高CPU能量时间时,会以每秒1次的周期进行采样: image.png
  • 每个采样数据中都是当前时间点的活跃函数 image.png
  • 将获得到的采样数据进行组合,就能得到一副加权调用图 image.png
  • 数字代表堆栈函数被收集到的次数缩进关系代表函数之间的调用关系。可以认为在CPU占用堆栈中,函数对应的数字越大,这个函数就占用了更多的CPU。

方案二:平均CPU使用率检测方案

  • 根据Energy Logs的设计思路,我们设计出一套CPU监控方案: image.png
  1. 应用启动后开启一个检测子线程,每间隔5S计算应用CPU占用。
  2. 如果应用CPU总占用超过80%,识别出当前应用 CPU 占用超过5%的线程,将这些线程的堆栈存储起来。
  3. 当检测达到阈值(平均一分钟超过80% CPU占用)时,上报存储的堆栈。
  4. 平台将上报数据进行(异常)聚合,并组合成线程调用树、总调用树

几个需要注意的地方

  1. 应用启动后开启一个检测子线程,每间隔5S计算应用CPU占用。
  2. 如果应用CPU总占用超过80%,识别出当前应用 CPU 占用超过5%的线程,将这些线程的堆栈存储起来。
  3. 当检测达到阈值(平均一分钟超过80% CPU占用)时,上报存储的堆栈。
  4. 平台将上报数据进行(异常)聚合,并组合成线程调用树、总调用树

几个疑问

  • 问题一:如何获取并检测CPU占用
  • 问题二:数据上报格式
  • 问题三:如何生成CPU总调用树
  • 问题四:如何生成线程调用树

问题一:如何获取并检测CPU占用

  • iOS/macOS 的 Mach 内核提供了获取所有线程和获取线程信息的方法。 image.png
  • 线程信息存放在 thread_basic_info 结构体中,从 thread_basic_info 中可以获得线程的 CPU 占用。 image.png
  • 当前应用的总 CPU 占用即为每个线程 CPU 占用的累加。

问题二:数据上报格式

image.png

  • 当一分钟内应用平均CPU占用超过80%阈值,会通过日志库进行一次上报。
  • 堆栈信息以5S/次检测为一组,每组包含此次检测所有CPU占用超过5%线程堆栈信息
  • thread_back_trace字段存储线程函数调用栈,以/d进行分割。
  • iOS上报thread_back_trace字段是16进制地址,需通过DSYM转成函数堆栈。
  • 数据在平台最终以总调用树线程调用树两种形式呈现。

问题三:如何生成CPU总调用树

image.png

  • 总调用树最终呈现效果 截屏2022-11-14 20.52.50.png

问题四:如何生成线程调用树

  • 线程调用树和总调用树的区别是以线程维度将信息和堆栈分开。 截屏2022-11-14 20.53.55.png

参考