从iOS应用程序获取CPU使用率

49

如何获取应用程序的 CPU 使用情况?这是完全可能的,因为 App Store 中有应用程序(Activity Monitor Touch)可以显示它。


1
你需要找出 task_info() 失败的原因,并解决它。请参考这个问题的答案:https://dev59.com/HFTTa4cB1Zd3GeqPwd5g。我认为这是因为你没有初始化 port。请使用 task_info_self() 代替 port - trojanfoe
1
哇,谢谢您!现在这段代码似乎可以正常工作了。我们唯一需要更正的是将mach_task_self()替换task_info_self()。 - ivanzoid
1
请以答案形式书写,并等待一些时间,如果没有其他好的答案,请接受它。 - Tinyfool
@jww,我已经按照你的方法成功地监控了CPU使用情况;但是你能帮忙回答一下我的相关问题吗?https://stackoverflow.com/questions/47071265/how-to-analyze-stack-info-of-a-thread - Paradise
3个回答

83

更新。这段代码对我有效:

更新2。thread_list有泄漏,因此添加了vm_deallocate。

#import <mach/mach.h>
#import <assert.h>

float cpu_usage()
{
    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 -1;
    }

    task_basic_info_t      basic_info;
    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;
    uint32_t stat_thread = 0; // Mach threads

    basic_info = (task_basic_info_t)tinfo;

    // get threads in the task
    kr = task_threads(mach_task_self(), &thread_list, &thread_count);
    if (kr != KERN_SUCCESS) {
        return -1;
    }
    if (thread_count > 0)
        stat_thread += thread_count;

    long tot_sec = 0;
    long tot_usec = 0;
    float tot_cpu = 0;
    int j;

    for (j = 0; j < (int)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 -1;
        }

        basic_info_th = (thread_basic_info_t)thinfo;

        if (!(basic_info_th->flags & TH_FLAGS_IDLE)) {
            tot_sec = tot_sec + basic_info_th->user_time.seconds + basic_info_th->system_time.seconds;
            tot_usec = tot_usec + basic_info_th->user_time.microseconds + basic_info_th->system_time.microseconds;
            tot_cpu = tot_cpu + basic_info_th->cpu_usage / (float)TH_USAGE_SCALE * 100.0;
        }

    } // for each thread

    kr = vm_deallocate(mach_task_self(), (vm_offset_t)thread_list, thread_count * sizeof(thread_t));
    assert(kr == KERN_SUCCESS);

    return tot_cpu;
}

1
这是给应用程序的 CPU 使用情况还是整体 CPU 使用情况? - Manish Ahuja
1
感谢您的快速回复。有没有办法可以获取总体CPU使用情况? - Manish Ahuja
4
有没有一种方法可以获取进程ID的CPU使用率? - Rukshan
2
在微秒被累加的那一行,系统时间被加了两次。这可能应该改为用户时间+系统时间。 - George
对于线程计算,我会使用+=而不是让计算公式变得更长。在我看来,这样会更清晰。 - Julian
显示剩余12条评论

10

对于 Swift 3:

fileprivate func cpuUsage() -> Double {
    var kr: kern_return_t
    var task_info_count: mach_msg_type_number_t

    task_info_count = mach_msg_type_number_t(TASK_INFO_MAX)
    var tinfo = [integer_t](repeating: 0, count: Int(task_info_count))

    kr = task_info(mach_task_self_, task_flavor_t(TASK_BASIC_INFO), &tinfo, &task_info_count)
    if kr != KERN_SUCCESS {
        return -1
    }

    var thread_list: thread_act_array_t? = UnsafeMutablePointer(mutating: [thread_act_t]())
    var thread_count: mach_msg_type_number_t = 0
    defer {
        if let thread_list = thread_list {
            vm_deallocate(mach_task_self_, vm_address_t(UnsafePointer(thread_list).pointee), vm_size_t(thread_count))
        }
    }

    kr = task_threads(mach_task_self_, &thread_list, &thread_count)

    if kr != KERN_SUCCESS {
        return -1
    }

    var tot_cpu: Double = 0

    if let thread_list = thread_list {

        for j in 0 ..< Int(thread_count) {
            var thread_info_count = mach_msg_type_number_t(THREAD_INFO_MAX)
            var thinfo = [integer_t](repeating: 0, count: Int(thread_info_count))
            kr = thread_info(thread_list[j], thread_flavor_t(THREAD_BASIC_INFO),
                             &thinfo, &thread_info_count)
            if kr != KERN_SUCCESS {
                return -1
            }

            let threadBasicInfo = convertThreadInfoToThreadBasicInfo(thinfo)

            if threadBasicInfo.flags != TH_FLAGS_IDLE {
                tot_cpu += (Double(threadBasicInfo.cpu_usage) / Double(TH_USAGE_SCALE)) * 100.0
            }
        } // for each thread
    }

    return tot_cpu
}

fileprivate func convertThreadInfoToThreadBasicInfo(_ threadInfo: [integer_t]) -> thread_basic_info {
    var result = thread_basic_info()

    result.user_time = time_value_t(seconds: threadInfo[0], microseconds: threadInfo[1])
    result.system_time = time_value_t(seconds: threadInfo[2], microseconds: threadInfo[3])
    result.cpu_usage = threadInfo[4]
    result.policy = threadInfo[5]
    result.run_state = threadInfo[6]
    result.flags = threadInfo[7]
    result.suspend_count = threadInfo[8]
    result.sleep_time = threadInfo[9]

    return result
}

2
我有一个问题:我的应用程序的CPU使用率是由我的应用程序计算的还是由iOS计算的? - Markus
3
嗨,马库斯,这个函数返回你的应用程序的 CPU 使用率。 - Lionking
2
我认为vm_deallocate是不正确的。在Swift 4中,调用它的正确方式应该是vm_deallocate(mach_task_self_, vm_address_t(bitPattern: thread_list), vm_size_t(Int(thread_count) * MemoryLayout<thread_t>.stride))。访问thread_list.pointee将返回thread_list的第一个值,而不是数组本身的地址。 - kuwerty
谢谢@kuwerty!否则它会不断泄漏。 - RemembranceNN
目前,var thread_list: thread_act_array_t? = UnsafeMutablePointer(mutating: [thread_act_t]()) 这一行会出现关于悬空指针的警告。我认为应该改成这样: return thread_list_array.withUnsafeMutableBufferPointer { pointer in // 其余的代码在这里 }``` @Lionking,请根据这条消息和@kuwerty上面的注释更新答案,以使其与Swift 5.9保持最新。 - undefined

0

试试这个:

- (NSString *)cpuUsage
{
   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 @"NA";
   }

   task_basic_info_t      basic_info;
   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;
   uint32_t stat_thread = 0; // Mach threads

   basic_info = (task_basic_info_t)tinfo;

   // get threads in the task
   kr = task_threads(mach_task_self(), &thread_list, &thread_count);
   if (kr != KERN_SUCCESS)
   {
      return @"NA";
   }
   if (thread_count > 0)
    stat_thread += thread_count;

   long tot_idle = 0;
   long tot_user = 0;
   long tot_kernel = 0;
   int j;

   for (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;

      if (basic_info_th->flags & TH_FLAGS_IDLE)
      {
          //This is idle
          tot_idle = tot_idle + basic_info_th->user_time.microseconds + basic_info_th->system_time.microseconds;
      } else {
          //This is user
          tot_user = tot_user + basic_info_th->user_time.microseconds;

          //This is kernel
          tot_kernel = tot_kernel + basic_info_th->system_time.microseconds;
      }

  } // for each thread

  kr = vm_deallocate(mach_task_self(), (vm_offset_t)thread_list, thread_count * sizeof(thread_t));
  assert(kr == KERN_SUCCESS);

  long tot_cpu = tot_idle + tot_user + tot_kernel

  return [NSString stringWithFormat:@"Idle: %.2f, User: %.2f, Kernel: %.2f", tot_idle/tot_cpu, tot_user/tot_cpu, tot_kernel/tot_cpu];
}

然而,该方法基于每个进程的起始点计算百分比。如果您正在寻找更传统的计算这些数字的方法,请参见Petesh's answer


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接