From d13cb186974aeb0d61291318d28523ecf8059b60 Mon Sep 17 00:00:00 2001 From: Oliver T Date: Sat, 8 Apr 2023 12:18:01 -0400 Subject: [PATCH] Fix cpu_freq for Apple silicon 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. Fixes #1892 --- psutil/_psosx.py | 6 +++++- psutil/_psutil_osx.c | 1 + psutil/arch/osx/cpu.c | 45 +++++++++++++++++++++++++++++++++++++++++++ psutil/arch/osx/cpu.h | 1 + 4 files changed, 52 insertions(+), 1 deletion(-) diff --git a/psutil/_psosx.py b/psutil/_psosx.py index 58359bc90c..7c31aeb01b 100644 --- a/psutil/_psosx.py +++ b/psutil/_psosx.py @@ -176,7 +176,11 @@ def cpu_freq(): Also, the returned frequency never changes, see: https://arstechnica.com/civis/viewtopic.php?f=19&t=465002 """ - curr, min_, max_ = cext.cpu_freq() + try: + curr, min_, max_ = cext.cpu_freq() + except FileNotFoundError: + # apple silicon requires specialized call. + curr, min_, max_ = cext.arm_cpu_freq() return [_common.scpufreq(curr, min_, max_)] diff --git a/psutil/_psutil_osx.c b/psutil/_psutil_osx.c index ab43871f8f..2598a00485 100644 --- a/psutil/_psutil_osx.c +++ b/psutil/_psutil_osx.c @@ -1723,6 +1723,7 @@ static PyMethodDef mod_methods[] = { {"cpu_count_cores", psutil_cpu_count_cores, METH_VARARGS}, {"cpu_count_logical", psutil_cpu_count_logical, METH_VARARGS}, {"cpu_freq", psutil_cpu_freq, METH_VARARGS}, + {"arm_cpu_freq", psutil_arm_cpu_freq, METH_VARARGS}, {"cpu_stats", psutil_cpu_stats, METH_VARARGS}, {"cpu_times", psutil_cpu_times, METH_VARARGS}, {"disk_io_counters", psutil_disk_io_counters, METH_VARARGS}, diff --git a/psutil/arch/osx/cpu.c b/psutil/arch/osx/cpu.c index 6e56471812..2e408eca1f 100644 --- a/psutil/arch/osx/cpu.c +++ b/psutil/arch/osx/cpu.c @@ -29,6 +29,9 @@ For reference, here's the git history with original implementations: #include "../../_psutil_common.h" #include "../../_psutil_posix.h" +#include +#include + PyObject * @@ -109,6 +112,48 @@ psutil_cpu_stats(PyObject *self, PyObject *args) { ); } +PyObject * +psutil_arm_cpu_freq(PyObject *self, PyObject *args) { + unsigned int curr; + uint32_t min = UINT32_MAX; + uint32_t max = 0; + + CFDictionaryRef matching = IOServiceMatching("AppleARMIODevice"); + io_iterator_t iter; + + IOServiceGetMatchingServices(kIOMainPortDefault, matching, &iter); + io_registry_entry_t entry; + while ((entry = IOIteratorNext(iter))) { + io_name_t name; + IORegistryEntryGetName(entry, name); + + if (strncmp(name, "pmgr", 4) == 0) { + break; + } + } + IOObjectRelease(iter); + CFRelease(matching); + CFTypeRef pCoreRef = IORegistryEntryCreateCFProperty(entry, CFSTR("voltage-states5-sram"), kCFAllocatorDefault, 0); + + size_t length = CFDataGetLength(pCoreRef); + for (size_t i = 0; i < length - 3; i += 4) { + uint32_t curr_freq = 0; + CFDataGetBytes(pCoreRef, CFRangeMake(i, sizeof(uint32_t)), (UInt8 *) &curr_freq); + if (curr_freq > 1e6 && curr_freq < min) { + min = curr_freq; + } + if (curr_freq > max) { + max = curr_freq; + } + } + curr = max; + + return Py_BuildValue( + "IKK", + curr / 1000 / 1000, + min / 1000 / 1000, + max / 1000 / 1000); +} PyObject * psutil_cpu_freq(PyObject *self, PyObject *args) { diff --git a/psutil/arch/osx/cpu.h b/psutil/arch/osx/cpu.h index aac0f809b1..070f042279 100644 --- a/psutil/arch/osx/cpu.h +++ b/psutil/arch/osx/cpu.h @@ -10,4 +10,5 @@ PyObject *psutil_cpu_count_logical(PyObject *self, PyObject *args); PyObject *psutil_cpu_count_cores(PyObject *self, PyObject *args); PyObject *psutil_cpu_times(PyObject *self, PyObject *args); PyObject *psutil_cpu_freq(PyObject *self, PyObject *args); +PyObject *psutil_arm_cpu_freq(PyObject *self, PyObject *args); PyObject *psutil_cpu_stats(PyObject *self, PyObject *args);