今天有用户反馈,在mac微信不退出的情况下,盖上电脑,发热会比较厉害,然后打开电脑,用活动监视器看到mac微信的能耗和CPU占用率都比较高。很自然的想法就是,盖上电脑期间,mac微信究竟干了什么事情,这些事情当中,又有哪些比较耗CPU?
如果是开着电脑,并且可以重现,就可以用instrument来定位耗CPU的业务代码。然而,盖上电脑没法用instrument,所以只能靠日志了。那么,日志要怎么加,才能让CPU占用率跟特定的业务代码关联起来呢?
mach内核中有数据结构记录了每个线程的CPU占用率,而堆栈调用分析框架(这里选用BSBacktraceLogger)可以打出每个线程执行的现场日志,两者结合,就是我们想要的。下面直接上代码:
kern_return_t kr;
task_info_data_t tinfo;
mach_msg_type_number_t task_info_count;
task_info_count = TASK_INFO_MAX;
kr = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)tinfo, &task_info_count);
if (kr != KERN_SUCCESS) {
return nil;
}
thread_array_t thread_list; // 线程数组
mach_msg_type_number_t thread_count; // 线程数
thread_info_data_t thinfo;
mach_msg_type_number_t thread_info_count;
thread_basic_info_t basic_info_th;
kr = task_threads(mach_task_self(), &thread_list, &thread_count); // 获取线程列表和线程数
if (kr != KERN_SUCCESS) {
return nil;
}
NSString* backstraces = nil;// 遍历每个线程
for (int j = 0; j < thread_count; j++)
{
thread_info_count = THREAD_INFO_MAX;
kr = thread_info(thread_list[j], THREAD_BASIC_INFO, (thread_info_t)thinfo, &thread_info_count); //获取线程信息
if (kr != KERN_SUCCESS) {
return nil;
}
basic_info_th = (thread_basic_info_t)thinfo; // 线程基本信息,包含CPU占用率、CPU占用时间等等
if (!(basic_info_th->flags & TH_FLAGS_IDLE)) {
float current_thread_cpu = basic_info_th->cpu_usage / (float)TH_USAGE_SCALE * 100.0; // 当前线程的CPU占用率
if(current_thread_cpu >= 自定义单核CPU占用率阈值){
// 记录CPU占用率超过阈值的线程的执行堆栈
NSString* backstrace = [BSBacktraceLogger bs_backtraceOfMachThread:thread_list[j]];
backstraces = [NSString stringWithFormat:@"%@\n%@", backstraces, backstrace];
}
}
}
kr = vm_deallocate(mach_task_self(), (vm_offset_t)thread_list, thread_count * sizeof(thread_t));
return backstraces;
这里只是抛砖引玉,有待后续深入研究。