Skip to content

Commit

Permalink
Fix cpu_freq for Apple silicon (#2222)
Browse files Browse the repository at this point in the history
Apple SoC returns empty string after querying the cpu frequency using
sysctl, this information however, can still be extracted from the IOKit
registry. This PR adds a new method that is specific to Apple ARM
architecture.

Signed-off-by: Oliver T <geronimooliver00@gmail.com>
  • Loading branch information
snOm3ad committed Jan 6, 2024
1 parent 89eac06 commit 14a33ff
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 1 deletion.
4 changes: 4 additions & 0 deletions CREDITS
Original file line number Diff line number Diff line change
Expand Up @@ -813,3 +813,7 @@ I: 2156, 2345
N: Lawrence D'Anna
W: https://github.com/smoofra
I: 2010

N: Oliver Tomé
W: https://github.com/snom3ad
I: 2222
2 changes: 2 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
- 2340_, [NetBSD]: if process is terminated, `Process.cwd()`_ will return an
empty string instead of raising `NoSuchProcess`_.
- 2345_, [Linux]: fix compilation on older compiler missing DUPLEX_UNKNOWN
- 2222_, [macOS]: `cpu_freq()` now returns fixed values for `min` and `max`
frequencies in all Apple Silicon chips.

5.9.7
=====
Expand Down
121 changes: 120 additions & 1 deletion psutil/arch/osx/cpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ For reference, here's the git history with original implementations:
#include <sys/sysctl.h>
#include <sys/vmmeter.h>
#include <mach/mach.h>
#if defined(__arm64__) || defined(__aarch64__)
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#endif

#include "../../_psutil_common.h"
#include "../../_psutil_posix.h"
Expand Down Expand Up @@ -109,7 +113,122 @@ psutil_cpu_stats(PyObject *self, PyObject *args) {
);
}

#if defined(__arm64__) || defined(__aarch64__)
PyObject *
psutil_cpu_freq(PyObject *self, PyObject *args) {
uint32_t min;
uint32_t curr;
uint32_t pMin;
uint32_t eMin;
uint32_t max;
kern_return_t status;
CFDictionaryRef matching = NULL;
CFTypeRef pCoreRef = NULL;
CFTypeRef eCoreRef = NULL;
io_iterator_t iter = 0;
io_registry_entry_t entry = 0;
io_name_t name;

matching = IOServiceMatching("AppleARMIODevice");
if (matching == 0) {
return PyErr_Format(
PyExc_RuntimeError,
"IOServiceMatching call failed, 'AppleARMIODevice' not found"
);
}

status = IOServiceGetMatchingServices(kIOMainPortDefault, matching, &iter);
if (status != KERN_SUCCESS) {
PyErr_Format(
PyExc_RuntimeError, "IOServiceGetMatchingServices call failed"
);
goto error;
}

while ((entry = IOIteratorNext(iter)) != 0) {
status = IORegistryEntryGetName(entry, name);
if (status != KERN_SUCCESS) {
IOObjectRelease(entry);
continue;
}
if (strcmp(name, "pmgr") == 0) {
break;
}
IOObjectRelease(entry);
}

if (entry == 0) {
PyErr_Format(
PyExc_RuntimeError,
"'pmgr' entry was not found in AppleARMIODevice service"
);
goto error;
}

pCoreRef = IORegistryEntryCreateCFProperty(
entry, CFSTR("voltage-states5-sram"), kCFAllocatorDefault, 0);
if (pCoreRef == NULL) {
PyErr_Format(
PyExc_RuntimeError, "'voltage-states5-sram' property not found");
goto error;
}

eCoreRef = IORegistryEntryCreateCFProperty(
entry, CFSTR("voltage-states1-sram"), kCFAllocatorDefault, 0);
if (eCoreRef == NULL) {
PyErr_Format(
PyExc_RuntimeError, "'voltage-states1-sram' property not found");
goto error;
}

size_t pCoreLength = CFDataGetLength(pCoreRef);
size_t eCoreLength = CFDataGetLength(eCoreRef);
if (pCoreLength < 8) {
PyErr_Format(
PyExc_RuntimeError,
"expected 'voltage-states5-sram' buffer to have at least size 8"
);
goto error;
}
if (eCoreLength < 4) {
PyErr_Format(
PyExc_RuntimeError,
"expected 'voltage-states1-sram' buffer to have at least size 4"
);
goto error;
}

CFDataGetBytes(pCoreRef, CFRangeMake(0, 4), (UInt8 *) &pMin);
CFDataGetBytes(eCoreRef, CFRangeMake(0, 4), (UInt8 *) &eMin);
CFDataGetBytes(pCoreRef, CFRangeMake(pCoreLength - 8, 4), (UInt8 *) &max);

min = pMin < eMin ? pMin : eMin;
curr = max;

CFRelease(pCoreRef);
CFRelease(eCoreRef);
IOObjectRelease(iter);
IOObjectRelease(entry);

return Py_BuildValue(
"IKK",
curr / 1000 / 1000,
min / 1000 / 1000,
max / 1000 / 1000
);

error:
if (pCoreRef != NULL)
CFRelease(pCoreRef);
if (eCoreRef != NULL)
CFRelease(eCoreRef);
if (iter != 0)
IOObjectRelease(iter);
if (entry != 0)
IOObjectRelease(entry);
return NULL;
}
#else
PyObject *
psutil_cpu_freq(PyObject *self, PyObject *args) {
unsigned int curr;
Expand Down Expand Up @@ -138,7 +257,7 @@ psutil_cpu_freq(PyObject *self, PyObject *args) {
min / 1000 / 1000,
max / 1000 / 1000);
}

#endif

PyObject *
psutil_per_cpu_times(PyObject *self, PyObject *args) {
Expand Down

0 comments on commit 14a33ff

Please sign in to comment.