From 349ee35b8354ad838532764e7cb0bf182d35680c Mon Sep 17 00:00:00 2001 From: Alex Manuskin Date: Fri, 30 Nov 2018 07:31:19 +0200 Subject: [PATCH 1/3] Add CPU frequency support for FreeBSD --- psutil/_psbsd.py | 22 +++++++++++++++++++++ psutil/_psutil_bsd.c | 2 ++ psutil/arch/freebsd/specific.c | 35 ++++++++++++++++++++++++++++++++++ psutil/arch/freebsd/specific.h | 1 + 4 files changed, 60 insertions(+) diff --git a/psutil/_psbsd.py b/psutil/_psbsd.py index 0727dd2e8..53b6478d4 100644 --- a/psutil/_psbsd.py +++ b/psutil/_psbsd.py @@ -455,6 +455,28 @@ def sensors_temperatures(): return ret + def cpu_freq(): + ret = [] + num_cpus = cpu_count_logical() + for cpu in range(num_cpus): + try: + current, available = cext.cpu_frequency(cpu) + low = None + high = None + if available: + try: + low = available.split(" ")[-1].split("/")[0] + except IndexError: + pass + try: + high = available.split(" ")[0].split("/")[0] + except IndexError: + pass + ret.append(_common.scpufreq(current, low, high)) + except NotImplementedError: + pass + return ret + # ===================================================================== # --- other system functions diff --git a/psutil/_psutil_bsd.c b/psutil/_psutil_bsd.c index 6b366f13b..7805e5a53 100644 --- a/psutil/_psutil_bsd.c +++ b/psutil/_psutil_bsd.c @@ -983,6 +983,8 @@ PsutilMethods[] = { "Return battery information."}, {"sensors_cpu_temperature", psutil_sensors_cpu_temperature, METH_VARARGS, "Return temperature information for a given CPU core number."}, + {"cpu_frequency", psutil_cpu_freq, METH_VARARGS, + "Return cpu 0 frequency"}, #endif // --- others diff --git a/psutil/arch/freebsd/specific.c b/psutil/arch/freebsd/specific.c index 14be26b36..f02926d18 100644 --- a/psutil/arch/freebsd/specific.c +++ b/psutil/arch/freebsd/specific.c @@ -1046,3 +1046,38 @@ psutil_sensors_cpu_temperature(PyObject *self, PyObject *args) { PyErr_SetFromErrno(PyExc_OSError); return NULL; } + + +/* + * Return cpu 0 frequency information + */ +PyObject * +psutil_cpu_freq(PyObject *self, PyObject *args) { + int current; + int max; + int core; + char sensor[26]; + char available[1000]; + char* answer; + size_t size = sizeof(current); + + if (! PyArg_ParseTuple(args, "i", &core)) + return NULL; + sprintf(sensor, "dev.cpu.%d.freq", core); + if (sysctlbyname(sensor, ¤t, &size, NULL, 0)) + goto error; + + size = sizeof(available); + // In case of faliure, an empty string is returned + sprintf(sensor, "dev.cpu.%d.freq_levels", core); + sysctlbyname(sensor, &available, &size, NULL, 0); + + return Py_BuildValue("is", current, available); + +error: + if (errno == ENOENT) + PyErr_SetString(PyExc_NotImplementedError, "Unable to read frequency"); + else + PyErr_SetFromErrno(PyExc_OSError); + return NULL; +} diff --git a/psutil/arch/freebsd/specific.h b/psutil/arch/freebsd/specific.h index cb71ff612..875c81664 100644 --- a/psutil/arch/freebsd/specific.h +++ b/psutil/arch/freebsd/specific.h @@ -30,4 +30,5 @@ PyObject* psutil_cpu_stats(PyObject* self, PyObject* args); #if defined(PSUTIL_FREEBSD) PyObject* psutil_sensors_battery(PyObject* self, PyObject* args); PyObject* psutil_sensors_cpu_temperature(PyObject* self, PyObject* args); +PyObject* psutil_cpu_freq(PyObject* self, PyObject* args); #endif From 59589f8095396c0119c2505166458ba8994dc982 Mon Sep 17 00:00:00 2001 From: Alex Manuskin Date: Fri, 30 Nov 2018 09:52:34 +0200 Subject: [PATCH 2/3] Cast min and max frequency to int --- psutil/_psbsd.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/psutil/_psbsd.py b/psutil/_psbsd.py index 53b6478d4..10500e873 100644 --- a/psutil/_psbsd.py +++ b/psutil/_psbsd.py @@ -465,12 +465,12 @@ def cpu_freq(): high = None if available: try: - low = available.split(" ")[-1].split("/")[0] - except IndexError: + low = int(available.split(" ")[-1].split("/")[0]) + except(IndexError, ValueError): pass try: - high = available.split(" ")[0].split("/")[0] - except IndexError: + high = int(available.split(" ")[0].split("/")[0]) + except(IndexError, ValueError): pass ret.append(_common.scpufreq(current, low, high)) except NotImplementedError: From 86797d525c8ac1f708cfd50e230d5ae8ea50cf9c Mon Sep 17 00:00:00 2001 From: Alex Manuskin Date: Sat, 1 Dec 2018 12:28:20 +0200 Subject: [PATCH 3/3] Add test for FreeBSD cpu_freq() Add test for cpu_freq() and fix review comments --- psutil/_psbsd.py | 32 ++++++++++++++++++-------------- psutil/_psutil_bsd.c | 2 +- psutil/arch/freebsd/specific.c | 14 ++++++-------- psutil/tests/test_bsd.py | 18 ++++++++++++++++++ psutil/tests/test_contracts.py | 2 +- 5 files changed, 44 insertions(+), 24 deletions(-) diff --git a/psutil/_psbsd.py b/psutil/_psbsd.py index 10500e873..d3ce7b5cc 100644 --- a/psutil/_psbsd.py +++ b/psutil/_psbsd.py @@ -456,25 +456,29 @@ def sensors_temperatures(): return ret def cpu_freq(): + """ + Return frequency metrics for CPUs. Currently, only CPU 0 is supported + by FreeBSD, all other cores match the frequency of CPU 0. + """ ret = [] num_cpus = cpu_count_logical() for cpu in range(num_cpus): try: - current, available = cext.cpu_frequency(cpu) - low = None - high = None - if available: - try: - low = int(available.split(" ")[-1].split("/")[0]) - except(IndexError, ValueError): - pass - try: - high = int(available.split(" ")[0].split("/")[0]) - except(IndexError, ValueError): - pass - ret.append(_common.scpufreq(current, low, high)) + current, available_freq = cext.cpu_frequency(cpu) except NotImplementedError: - pass + continue + min_freq = None + max_freq = None + if available_freq: + try: + min_freq = int(available_freq.split(" ")[-1].split("/")[0]) + except(IndexError, ValueError): + pass + try: + max_freq = int(available_freq.split(" ")[0].split("/")[0]) + except(IndexError, ValueError): + pass + ret.append(_common.scpufreq(current, min_freq, max_freq)) return ret diff --git a/psutil/_psutil_bsd.c b/psutil/_psutil_bsd.c index 7805e5a53..dce157f55 100644 --- a/psutil/_psutil_bsd.c +++ b/psutil/_psutil_bsd.c @@ -984,7 +984,7 @@ PsutilMethods[] = { {"sensors_cpu_temperature", psutil_sensors_cpu_temperature, METH_VARARGS, "Return temperature information for a given CPU core number."}, {"cpu_frequency", psutil_cpu_freq, METH_VARARGS, - "Return cpu 0 frequency"}, + "Return frequency of a given CPU"}, #endif // --- others diff --git a/psutil/arch/freebsd/specific.c b/psutil/arch/freebsd/specific.c index f02926d18..70dc7f2ce 100644 --- a/psutil/arch/freebsd/specific.c +++ b/psutil/arch/freebsd/specific.c @@ -1049,16 +1049,14 @@ psutil_sensors_cpu_temperature(PyObject *self, PyObject *args) { /* - * Return cpu 0 frequency information + * Return frequency information of a given CPU */ PyObject * psutil_cpu_freq(PyObject *self, PyObject *args) { int current; - int max; int core; char sensor[26]; - char available[1000]; - char* answer; + char available_freq_levels[1000]; size_t size = sizeof(current); if (! PyArg_ParseTuple(args, "i", &core)) @@ -1067,12 +1065,12 @@ psutil_cpu_freq(PyObject *self, PyObject *args) { if (sysctlbyname(sensor, ¤t, &size, NULL, 0)) goto error; - size = sizeof(available); - // In case of faliure, an empty string is returned + size = sizeof(available_freq_levels); + // In case of failure, an empty string is returned sprintf(sensor, "dev.cpu.%d.freq_levels", core); - sysctlbyname(sensor, &available, &size, NULL, 0); + sysctlbyname(sensor, &available_freq_levels, &size, NULL, 0); - return Py_BuildValue("is", current, available); + return Py_BuildValue("is", current, available_freq_levels); error: if (errno == ENOENT) diff --git a/psutil/tests/test_bsd.py b/psutil/tests/test_bsd.py index df43a023e..87feb1473 100755 --- a/psutil/tests/test_bsd.py +++ b/psutil/tests/test_bsd.py @@ -250,6 +250,24 @@ def test_proc_cpu_times(self): if len(tested) != 2: raise RuntimeError("couldn't find lines match in procstat out") + # --- cpu_freq(); tests against sysctl + def test_cpu_frequency_against_sysctl(self): + # Currently only cpu 0 is frequency is supported in FreeBSD + # All other cores use the same frequency + sensor = "dev.cpu.0.freq" + sysctl_result = int(sysctl(sensor)) + self.assertEqual(psutil.cpu_freq().current, sysctl_result) + + sensor = "dev.cpu.0.freq_levels" + sysctl_result = sysctl(sensor) + # sysctl returns a string of the format: + # / /... + # Ordered highest available to lowest available + max_freq = int(sysctl_result.split()[0].split("/")[0]) + min_freq = int(sysctl_result.split()[-1].split("/")[0]) + self.assertEqual(psutil.cpu_freq().max, max_freq) + self.assertEqual(psutil.cpu_freq().min, min_freq) + # --- virtual_memory(); tests against sysctl @retry_before_failing() diff --git a/psutil/tests/test_contracts.py b/psutil/tests/test_contracts.py index 8ff41e5b3..78e6ba22f 100755 --- a/psutil/tests/test_contracts.py +++ b/psutil/tests/test_contracts.py @@ -115,7 +115,7 @@ def test_cpu_freq(self): (os.path.exists("/sys/devices/system/cpu/cpufreq") or os.path.exists("/sys/devices/system/cpu/cpu0/cpufreq"))) self.assertEqual(hasattr(psutil, "cpu_freq"), - linux or MACOS or WINDOWS) + linux or MACOS or WINDOWS or FREEBSD) def test_sensors_temperatures(self): self.assertEqual(