How We Measure CPU Usage on macOS
Displaying the most relevant information at a glance sometimes deviates from how the operating system reports specific metrics. Case in point: how we measure the CPU usage per process on macOS.
In macOS system utilities such as Activity Monitor or the
top program, the CPU usage per process is reported as a percentage relative to the usage of a single CPU core.
This means that the percentage of a program with a high load spawning over multiple cores can have a CPU usage higher than 100%. For example, a program with four very busy threads would be reported as having a CPU usage of 400%.
We want to present the metrics so our customers can intuitively understand them. The way macOS reports the metric, it is only possible to understand the relative weight of a process within a system by first knowing how many CPU cores the system has. This defeats the purpose of using a percentage.
A better approach is to report the CPU usage relative to the system’s total capacity within a time interval. This way, the metric would make immediate sense to users without needing additional context.
To obtain the CPU usage of a process as a percentage, we first need the CPU usage of the process in a time interval. This is easily obtained using the undocumented
proc_pidinfo() macOS API.
Obtaining the actual CPU usage of all processes in the system within a time interval is not straightforward, as there is no API returning this specific information.
Newer macOS systems based on the M1/M2 chips contain two types of processor cores: efficiency cores and performance cores. They have both different capabilities, but most relevant for usage accounting, they run at different speeds. To make things more complicated, their clock speed is adjusted at runtime by the XNU kernel, depending on the workload.
Luckily, there is an API call that returns the CPU load of all cores in the system:
The real challenge lies in the different APIs reporting CPU usage using different time units.
The portion of the macOS Mach kernel that maintains the counters for resources that tasks use is called Recount. It uses different granularity for counting different kinds of resources.
The XNU source code reveals that the
host_processor_info() function reports the CPU load in ticks, whereas the
proc_pidinfo() reports the CPU usage in Mach time.
Mach time is converted to ticks inside the kernel; however, the conversion routines are inaccessible from user space and are highly hardware-dependent.
How can we obtain the full CPU load for the system without converting between ticks and Mach time?
We can add the CPU usage for each process running on the system, a method used internally by the
We have the total time for all processes in Mach time, the total CPU used time in ticks, and the total idle time in ticks (which the
host_processor_info() function provides). We can use a simple ratio equation to solve for the idle time in Mach time. From there, we can add the idle time (in Mach time) to the total CPU time (in Mach time) for all processes and we get the full CPU time in Mach time without explicitly converting between both time units.
With that achieved, getting the usage per process is trivial.
One of the most challenging aspects of macOS systems programming is the lack of detailed documentation. This makes reading the kernel sources necessary for researching even basic topics.
When deviating from the standard way of reporting metrics by the operating system, it is often necessary to take inspiration from existing implementations (such as
top(1)) that do similar things while at the same time introducing new ways of obtaining information.
In the end, what counts for us is providing reliable metrics that help our customers understand the system’s state as quickly and efficiently as possible.
The uberAgent product family offers innovative digital employee experience monitoring and endpoint security analytics for Windows and macOS.
uberAgent UXM highlights include detailed information about boot and logon duration, application unresponsiveness detection, network reliability drill-downs, process startup duration, application usage metering, browser performance, web app metrics, and Citrix insights. All these varied aspects of system performance and reliability are smartly brought together in the Experience Score dashboard.
uberAgent ESA excels with a sophisticated Threat Detection Engine, endpoint security & compliance rating, the uAQL query language, detection of risky activity, DNS query monitoring, hash calculation, registry monitoring, and Authenticode signature verification. uberAgent ESA comes with Sysmon and Sigma rule converters, a graphical rule editor, and uses a simple yet powerful query language instead of XML.
About vast limits
vast limits GmbH is the company behind uberAgent, the innovative digital employee experience monitoring and endpoint security analytics product. vast limits’ customer list includes organizations from industries like finance, healthcare, professional services, and education, ranging from medium-sized businesses to global enterprises. vast limits’ network of qualified solution partners ensures best-in-class service and support anywhere in the world.