背景
- CPU作为性能监控的核心数据,目前APP监控主要有两个侧重方向
- 侧重监控CPU
瞬时
使用率,是一种偏向卡顿,流畅度
的监控 - 侧重监控CPU
平均
使用率,是一种偏向耗能,发热
的监控
- 侧重监控CPU
方案一:瞬时CPU使用率检测方案
- 轮询检测所有线程CPU使用总和,若CPU使用总和大于阈值,记录所有线程堆栈。
- 此方案更适合结合runloop监控进行卡顿检测。
苹果能耗监控:Energy Logs
- 监控CPU平均使用率的方案,是参考Energy Logs方案。
Energy Logs监控原理
- 系统定期获取当前应用的线程堆栈。
- 当App给CPU带来很大的负载,那么就会根据搜集的线程堆栈生成总调用树。
- 触发生成总调用树的事件有两个:
前台3分钟平均超过80%
的CPU使用。后台1分钟平均超过80%
的CPU使用。
Energy Logs总调用树
总调用树是将所有CPU异常线程的堆栈拍平,并聚合成一棵树。
- 假设代码中有一个main函数和许多函数构成(method1、method2、method3、method4)
- 当程序运行到检测到高CPU能量时间时,会以每秒1次的周期进行采样:
- 每个采样数据中都是当前时间点的活跃函数
- 将获得到的采样数据进行组合,就能得到一副加权调用图
数字
代表堆栈函数被收集到的次数
,缩进
关系代表函数之间的调用关系
。可以认为在CPU占用堆栈中,函数对应的数字越大,这个函数就占用了更多的CPU。
方案二:平均CPU使用率检测方案
- 根据Energy Logs的设计思路,我们设计出一套CPU监控方案:
- 应用启动后开启一个检测子线程,每间隔
5S
计算应用CPU占用。 - 如果应用CPU总占用超过
80%
,识别出当前应用 CPU 占用超过5%
的线程,将这些线程的堆栈存储起来。 - 当检测达到
阈值
(平均一分钟超过80% CPU占用)时,上报存储的堆栈。 - 平台将上报数据进行(异常)聚合,并组合成
线程调用树、总调用树
。
几个需要注意的地方
- 应用启动后开启一个检测子线程,每间隔
5S
计算应用CPU占用。 - 如果应用CPU总占用超过
80%
,识别出当前应用 CPU 占用超过5%
的线程,将这些线程的堆栈存储起来。 - 当检测达到
阈值
(平均一分钟超过80% CPU占用)时,上报存储的堆栈。 - 平台将上报数据进行(异常)聚合,并组合成
线程调用树、总调用树
。
几个疑问
- 问题一:如何获取并检测CPU占用
- 问题二:数据上报格式
- 问题三:如何生成CPU总调用树
- 问题四:如何生成线程调用树
问题一:如何获取并检测CPU占用
- iOS/macOS 的
Mach
内核提供了获取所有线程和获取线程信息的方法。 - 线程信息存放在
thread_basic_info
结构体中,从thread_basic_info
中可以获得线程的CPU
占用。 - 当前应用的总 CPU 占用即为每个线程 CPU 占用的累加。
问题二:数据上报格式
- 当一分钟内应用
平均CPU占用超过80%
阈值,会通过日志库进行一次上报。 - 堆栈信息
以5S/次检测为一组
,每组包含此次检测所有CPU占用超过5%线程堆栈信息
。 thread_back_trace
字段存储线程函数调用栈,以/d进行分割。- iOS上报
thread_back_trace
字段是16进制地址,需通过DSYM
转成函数堆栈。 - 数据在平台最终以
总调用树
和线程调用树
两种形式呈现。
问题三:如何生成CPU总调用树
- 总调用树最终呈现效果
问题四:如何生成线程调用树
- 线程调用树和总调用树的区别是以线程维度将信息和堆栈分开。
参考
- iOS 性能监控(1)——CPU、Memory、FPS | 楚权的世界
- iOS 性能监控(一)—— CPU功耗监控 - 掘金
- aozhimin/iOS-Monitor-Platform: iOS 性能监控 SDK —— Wedjat(华狄特)开发过程的调研和整理
- Matrix 监控方案解析 - 詹乃龙的博客 | RandomJ Blog
- Matrix-iOS 耗电监控 - 云+社区 - 腾讯云
- (23条消息) 《iOS APP 性能检测》_腾讯Bugly的博客-CSDN博客_ios性能测试
- 产品简介 应用性能监控全链路版-火山引擎
- 获取iOS线程调用栈 - 掘金
- 一文读懂iOS线程调用栈原理 - 掘金
- iOS-底层原理 24:内存五大区 - 掘金
- 谈谈iOS堆栈那些事 | Joey's House
- 通过mach thread捕获任意线程调用栈信息-Swift - 掘金
- 栈和帧指针使用方法 - 知乎