18

iOS 性能监控(一)—— CPU功耗监控

 4 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzUxMTcwOTM4Mg%3D%3D&%3Bmid=2247486303&%3Bidx=1&%3Bsn=0bc6db09f860638e776ef88e5464a040
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Z7NBFrq.jpg!web

前言

最近,在看戴铭老师关于 “性能监控” 相关的技术分享,感觉收获很多。基于最近的学习,总结了一些性能监控相关的实践,并计划落地一系列 “性能监控” 相关的文章。

目录如下:

  1. iOS 性能监控(一)—— CPU功耗监控

  2. iOS 性能监控(二)—— 主线程卡顿监控

  3. iOS 性能监控(三)—— 方法耗时监控

本篇将介绍iOS性能监控工具(QiLagMonitor)中与 “CPU功耗监控” 相关的功能模块。

1

了解CPU架构

CPU(Central Processing Unit):中央处理器。

主要由 “ 运算器” 、 “控制器” 、 “寄存器 ” 三部分组成。

运算器:负责一些运算操作。(运算)

控制器:负责发出CPU每条指令所需的信息。(发指令)

寄存器:负责存储运算过程或者指令操作的一些临时文件。(存数据)

CPU有“处理指令”、“执行操作”、“控制时间”、“处理数据”四大作用。与我们人体的大脑类似,帮助我们完成各种各样的生理活动。

市场上,我们比较熟悉的CPU架构有ARM(arm64)和Intel(x86)等等。

问:那么对于我们iPhone而言,有哪些CPU架构呢?

目前,市场上大部分的iPhone都是基于 arm64 架构。(iPhone 5s之后)

因为 arm 架构有着功耗低的特点,因此广泛应用在移动设备领域。(intel虽然性能好,但功耗高。因此失去了移动端领域的市场份额。)

vANjmeu.png!web

PS:CPU与GPU比较?

GPU是图像处理器。在大部分计算机中,GPU仅仅会用来绘制图像。它会迅速算出当前屏幕的所有像素,并在显示器上绘制出来。

2

iOS如何监控CPU功耗?

说一下 QiCPUMonitor 的大致实现思路。

  • 首先,获取当前的任务task。从任务task中获得当前所有存活的线程信息。这时,我们就拿到了当前任务所有存活的 “ 线程信息”(threads) 和 “ 存活的线程个数 ”(threadCount) 。

  • 然后,设置一个预定的CPU使用阈值。遍历所有线程的信息,查看是否有线程的CPU使用率cpu_usage “ 超过 ” 预定的阈值(例如CPU使用率超过80%)。

  • 如果有线程的CPU使用率cpu_usage超过预定阈值,就 “存储” 当前线程的调用的堆栈信息。

3

QiCPUMonitor的具体实现

首先,介绍一下存储单个线程信息的结构体 thread_basic_info。

struct thread_basic_info {

time_value_t user_time; // 用户运行时长

time_value_t system_time; // 系统运行时长

integer_t cpu_usage; // CPU使用率(理论上限为1000)

policy_t policy; // 调度策略

integer_t run_state; // 运行状态

integer_t flags; // 各种标记

integer_t suspend_count; // 暂停线程的计数

integer_t sleep_time; // 休眠时间

};

名称 介绍 user_time 用户运行时间(精确到微妙)。 system_time 系统运行时(精确到微妙)。 cpu_usage cpu使用率(理论上限1000)。 policy 调度策略。 run_state 五种 “运行状态”:
1> running 运行中
2> stopped 已停止
3> waiting 等待中
4> uninterruptible 不可中断
5> halted 被阻塞 flags 三种 “线程标志”:
1> swapped 换出
2> idle 空闲
3> global forced idle 全局强制空闲。 suspend_count 线程已经被挂起的计数。 sleep_time 线程已经挂起的时间(精确到秒)。

其次,声明三个变量: threads、threadCount、thisTask

分别表示:

参数名 参数含义 threads 用来存储当前任务task下的所有线程信息。 threadCount 用来存储有几条线程。 thisTask 用来存储当前任务task。

thread_act_array_t threads; //! 一个数组,用来记录当前任务下的所有线程。

mach_msg_type_number_t threadCount = 0; //! 一个数,该参数用来记录线程的个数。

const task_t thisTask = mach_task_self(); //! 获取当前任务的task

然后,通过 thisTask, 获取对应的 threads 信息以及 threadCount

 kern_return_t kr = task_threads(thisTask, &threads, &threadCount); //! 通过thisTask,获取threads以及threadCount。

同时,检查是否获取成功, KERN_SUCCESS = 0 代表成功,其他有对应的错误码有 52 种。

if (kr != KERN_SUCCESS) { //! 检查是否成功,KERN_SUCCESS = 0 代表成功,其他有对应的错误码有52种。

return;

}

最后,遍历当前任务内所有存活的线程,查看每条线程的信息。每当有线程的CPU使用率( cpu_usage )超过指定阈值,就将当前线程的调用堆栈存入数据库。

//! 遍历当前任务内存活的所有线程

for (int i = 0; i < threadCount; i++) {


thread_info_data_t threadInfo; // 32位data

thread_basic_info_t threadBaseInfo;


mach_msg_type_number_t threadInfoCount = THREAD_INFO_MAX;


if (thread_info((thread_act_t)threads[i], THREAD_BASIC_INFO, (thread_info_t)threadInfo, &threadInfoCount) == KERN_SUCCESS) {

threadBaseInfo = (thread_basic_info_t)threadInfo; // 获取线程的信息

if (!(threadBaseInfo->flags & TH_FLAGS_IDLE)) {

integer_t cpuUsage = threadBaseInfo->cpu_usage / 10; // CPU最大usage为1000,因此除10即可获得CPU当前的利用率。

if (cpuUsage > CPUMONITORRATE) { // 超过设定的阈值时,记录堆栈

//cup 消耗大于设置值时打印和记录堆栈

NSString *reStr = qiStackOfThread(threads[i]);

QiCallStackModel *model = [[QiCallStackModel alloc] init];

model.stackStr = reStr;

//记录数据库中

[[[QiLagDB shareInstance] increaseWithStackModel:model] subscribeNext:^(id x) {}];

NSLog(@"CPU useage overload thread stack:\n%@",reStr);

}

}

}

}

为了监控的同时,又不影响App性能,故这个判断用一个定时器,每3秒刷新一次即可。

//! 监测 CPU 消耗

self.cpuMonitorTimer = [NSTimer scheduledTimerWithTimeInterval:3

target:self

selector:@selector(updateCPUInfo)

userInfo:nil

repeats:YES];

源码

QiLagMonitor

U3mAzma.jpg!web

360技术公众号

技术干货|一手资讯|精彩活动


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK