38

I'm a beginner in Objective-C, and I decided to try to write some simple application, so I'm trying to make an app which would measure CPU usage and such. Is there a simple way to get information such as the CPU percent usage using Objective-C in a Cocoa application?

I have found this question Determine Process Info Programmatically in Darwin/OSX which is similar, but not exactly the same. Mainly, I want the CPU percent usage of the whole system, not just my process, and I would actually prefer an Objective-C solution whereas in that question the poster wanted something else.

Cœur
  • 37,241
  • 25
  • 195
  • 267
houbysoft
  • 32,532
  • 24
  • 103
  • 156
  • Not that I am aware of in Objective C, but since Objective C is a superset of C, you could use the [`getloadavg`](http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man3/getloadavg.3.html) function. – highlycaffeinated Jul 22 '11 at 19:30
  • Updated for Swift. Read this link: [iOS - Get CPU usage from application](https://stackoverflow.com/questions/8223348/ios-get-cpu-usage-from-application) – Markus Nov 23 '17 at 09:43
  • There are a Swift version answering this question. I am not clear if it is the calculation of CPU usage from the application or is the calculation of CPU usage from entire device. Below the link with the answer (Swift language): [iOS - Get CPU usage from application](https://stackoverflow.com/questions/8223348/ios-get-cpu-usage-from-application) – Markus Nov 23 '17 at 09:46

2 Answers2

40

Here is the way i did it:

*.h file:

#include <sys/sysctl.h>
#include <sys/types.h>
#include <mach/mach.h>
#include <mach/processor_info.h>
#include <mach/mach_host.h>

ivars:

processor_info_array_t cpuInfo, prevCpuInfo;
mach_msg_type_number_t numCpuInfo, numPrevCpuInfo;
unsigned numCPUs;
NSTimer *updateTimer;
NSLock *CPUUsageLock;

*.m file

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{    
    int mib[2U] = { CTL_HW, HW_NCPU };
    size_t sizeOfNumCPUs = sizeof(numCPUs);
    int status = sysctl(mib, 2U, &numCPUs, &sizeOfNumCPUs, NULL, 0U);
    if(status)
        numCPUs = 1;

    CPUUsageLock = [[NSLock alloc] init];

    updateTimer = [[NSTimer scheduledTimerWithTimeInterval:3
                                                    target:self
                                                  selector:@selector(updateInfo:)
                                                  userInfo:nil
                                                   repeats:YES] retain];    
}

- (void)updateInfo:(NSTimer *)timer
{
    natural_t numCPUsU = 0U;
    kern_return_t err = host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numCPUsU, &cpuInfo, &numCpuInfo);
    if(err == KERN_SUCCESS) {
        [CPUUsageLock lock];

        for(unsigned i = 0U; i < numCPUs; ++i) {
            float inUse, total;
            if(prevCpuInfo) {
                inUse = (
                         (cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_USER]   - prevCpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_USER])
                         + (cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_SYSTEM] - prevCpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_SYSTEM])
                         + (cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_NICE]   - prevCpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_NICE])
                         );
                total = inUse + (cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_IDLE] - prevCpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_IDLE]);
            } else {
                inUse = cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_USER] + cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_SYSTEM] + cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_NICE];
                total = inUse + cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_IDLE];
            }

            NSLog(@"Core: %u Usage: %f",i,inUse / total);
        }
        [CPUUsageLock unlock];

        if(prevCpuInfo) {
            size_t prevCpuInfoSize = sizeof(integer_t) * numPrevCpuInfo;
            vm_deallocate(mach_task_self(), (vm_address_t)prevCpuInfo, prevCpuInfoSize);
        }

        prevCpuInfo = cpuInfo;
        numPrevCpuInfo = numCpuInfo;

        cpuInfo = NULL;
        numCpuInfo = 0U;
    } else {
        NSLog(@"Error!");
        [NSApp terminate:nil];
    }
}
VenoMKO
  • 3,294
  • 32
  • 38
  • btw you get detailed information about cpu usage(used by user, system...) – VenoMKO Jul 22 '11 at 20:19
  • 1
    @VenoMKO Can you explain a little bit about the array you got from `host_processor_info`? As far as I can see, it's an array of size (`core_count` * `CPU_STATE_MAX`) right? Meaning for each core, there are `CPU_STATE_MAX` information about it. Here's my question. A) What does each value look like? B) Why are you subtracting it against previous value? C) What is the relation between `inUse` and `total`? Why does dividing `inUse` by `total` yields us the usage? – Shane Hsu Mar 26 '13 at 16:42
  • @VenoMKO Help will be appreciated! – Shane Hsu Mar 26 '13 at 16:43
  • @ShaneHsu First of all I'd recommend you to read this book - "Mac OS X Internals: A Systems Approach" `cpuInfo` is an array of sub arrays for each logical cpu. The subarrays contain the ticks. – VenoMKO Mar 27 '13 at 13:24
  • 1
    B. To get the ticks of a period between current moment and a previous call. C. `total` - all ticks, `inUse` - amount of ticks used D. It's a simple math. – VenoMKO Mar 27 '13 at 13:32
  • @VenoMKO Thanks! Now I think I understand. But the system has been giving me weird replies, I'll look into it. Thanks! – Shane Hsu Mar 31 '13 at 18:19
  • I used it and it works perfectly for each core , but how can I easily determine overall CPU usage ? I mean in activity monitor there is overall cpu usage , I would get that number could u help me please? – Coldsteel48 May 22 '14 at 22:01
  • @Roma-MT, (core1 + core2 + ... + coreN) / N – VenoMKO Jun 27 '14 at 19:04
  • is there a way to use this code to measure the load of just one process? – Duck Mar 19 '15 at 14:06
  • This doesnt seem to work for iOS 8. `Undefined symbols for architecture armv7: "_numCPUs"`. Any idea how to fix this? – john Mar 26 '15 at 13:18
  • Check this link (Swift version): https://stackoverflow.com/questions/8223348/ios-get-cpu-usage-from-application – Markus Nov 23 '17 at 09:59
  • @VenoMKO Could you please give any hints on how to get detailed information about cpu usage (i.e. used by system, user)? – ixany Jul 29 '22 at 12:54
20

Swift 4 equivalent of VenoMKO answer. Usage: let myUsage = MyCpuUsage() to start the usage observation. Tested with Xcode 10.0.

import Foundation

// CPU usage credit VenoMKO: https://stackoverflow.com/a/6795612/1033581
class MyCpuUsage {
    var cpuInfo: processor_info_array_t!
    var prevCpuInfo: processor_info_array_t?
    var numCpuInfo: mach_msg_type_number_t = 0
    var numPrevCpuInfo: mach_msg_type_number_t = 0
    var numCPUs: uint = 0
    var updateTimer: Timer!
    let CPUUsageLock: NSLock = NSLock()

    init() {
        let mibKeys: [Int32] = [ CTL_HW, HW_NCPU ]
        // sysctl Swift usage credit Matt Gallagher: https://github.com/mattgallagher/CwlUtils/blob/master/Sources/CwlUtils/CwlSysctl.swift
        mibKeys.withUnsafeBufferPointer() { mib in
            var sizeOfNumCPUs: size_t = MemoryLayout<uint>.size
            let status = sysctl(processor_info_array_t(mutating: mib.baseAddress), 2, &numCPUs, &sizeOfNumCPUs, nil, 0)
            if status != 0 {
                numCPUs = 1
            }
            updateTimer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(updateInfo), userInfo: nil, repeats: true)
        }
    }

    @objc func updateInfo(_ timer: Timer) {
        var numCPUsU: natural_t = 0
        let err: kern_return_t = host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numCPUsU, &cpuInfo, &numCpuInfo);
        if err == KERN_SUCCESS {
            CPUUsageLock.lock()

            for i in 0 ..< Int32(numCPUs) {
                var inUse: Int32
                var total: Int32
                if let prevCpuInfo = prevCpuInfo {
                    inUse = cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_USER)]
                        - prevCpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_USER)]
                        + cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_SYSTEM)]
                        - prevCpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_SYSTEM)]
                        + cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_NICE)]
                        - prevCpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_NICE)]
                    total = inUse + (cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_IDLE)]
                        - prevCpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_IDLE)])
                } else {
                    inUse = cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_USER)]
                        + cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_SYSTEM)]
                        + cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_NICE)]
                    total = inUse + cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_IDLE)]
                }

                print(String(format: "Core: %u Usage: %f", i, Float(inUse) / Float(total)))
            }
            CPUUsageLock.unlock()

            if let prevCpuInfo = prevCpuInfo {
                // vm_deallocate Swift usage credit rsfinn: https://stackoverflow.com/a/48630296/1033581
                let prevCpuInfoSize: size_t = MemoryLayout<integer_t>.stride * Int(numPrevCpuInfo)
                vm_deallocate(mach_task_self_, vm_address_t(bitPattern: prevCpuInfo), vm_size_t(prevCpuInfoSize))
            }

            prevCpuInfo = cpuInfo
            numPrevCpuInfo = numCpuInfo

            cpuInfo = nil
            numCpuInfo = 0
        } else {
            print("Error!")
        }
    }
}
Cœur
  • 37,241
  • 25
  • 195
  • 267
  • This does not seem to work for M1 Macs. No compile or runtime errors. No messages, just nothing. How strange. – MrMesees Jul 24 '23 at 13:33
  • 1
    @MrMesees I just tested it on a Playground on MacBook Air M1 on macOS Sonoma, and no issues: it works perfectly. Maybe you forgot to instantiate it? In your Playground, include all the code and the usage example `let myUsage = MyCpuUsage()`, run the Playground and the log messages will show up every 3 seconds. – Cœur Jul 25 '23 at 10:46
  • The two lines missing were `let cpuUsage = MyCpuUsage()` and `RunLoop.main.run()` – MrMesees Jul 27 '23 at 09:51
  • I Have to wrap it in something else anyway in order to have it update a stream-deck. Their `cpu` tool is not updating title so it just has the title CPU; when it should be saying 1%, 2%, etc. – MrMesees Jul 27 '23 at 09:53