diff --git a/psutil/_psbsd.py b/psutil/_psbsd.py index 0727dd2e8..d3ce7b5cc 100644 --- a/psutil/_psbsd.py +++ b/psutil/_psbsd.py @@ -455,6 +455,32 @@ 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_freq = cext.cpu_frequency(cpu) + except NotImplementedError: + 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 + # ===================================================================== # --- other system functions diff --git a/psutil/_psutil_bsd.c b/psutil/_psutil_bsd.c index 6b366f13b..dce157f55 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 frequency of a given CPU"}, #endif // --- others diff --git a/psutil/arch/freebsd/specific.c b/psutil/arch/freebsd/specific.c index 14be26b36..70dc7f2ce 100644 --- a/psutil/arch/freebsd/specific.c +++ b/psutil/arch/freebsd/specific.c @@ -1046,3 +1046,36 @@ psutil_sensors_cpu_temperature(PyObject *self, PyObject *args) { PyErr_SetFromErrno(PyExc_OSError); return NULL; } + + +/* + * Return frequency information of a given CPU + */ +PyObject * +psutil_cpu_freq(PyObject *self, PyObject *args) { + int current; + int core; + char sensor[26]; + char available_freq_levels[1000]; + 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_freq_levels); + // In case of failure, an empty string is returned + sprintf(sensor, "dev.cpu.%d.freq_levels", core); + sysctlbyname(sensor, &available_freq_levels, &size, NULL, 0); + + return Py_BuildValue("is", current, available_freq_levels); + +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 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(