941 cpu freq #952

Merged
merged 15 commits into from Jan 23, 2017
View
@@ -122,6 +122,9 @@ CPU
>>>
>>> psutil.cpu_stats()
scpustats(ctx_switches=20455687, interrupts=6598984, soft_interrupts=2134212, syscalls=0)
+ >>>
+ >>> psutil.cpu_freq()
+ scpufreq(current=931.42925, min=800.0, max=3500.0)
Memory
======
View
@@ -191,6 +191,33 @@ CPU
.. versionadded:: 4.1.0
+.. function:: cpu_freq(percpu=False)
+
+ Return CPU frequency as a nameduple including *current*, *min* and *max*
+ frequencies expressed in Mhz.
+ If *percpu* is ``True`` and the system supports per-cpu frequency
+ retrieval (Linux only) a list of frequencies is returned for each CPU,
+ if not, a list with a single element is returned.
+ If *min* and *max* cannot be determined they are set to ``0``.
+
+ Example (Linux):
+
+ .. code-block:: python
+
+ >>> import psutil
+ >>> psutil.cpu_freq()
+ scpufreq(current=931.42925, min=800.0, max=3500.0)
+ >>> psutil.cpu_freq(percpu=True)
+ [scpufreq(current=2394.945, min=800.0, max=3500.0),
+ scpufreq(current=2236.812, min=800.0, max=3500.0),
+ scpufreq(current=1703.609, min=800.0, max=3500.0),
+ scpufreq(current=1754.289, min=800.0, max=3500.0)]
+
+ Availability: Linux, OSX, Windows
+
+ .. versionadded:: 5.1.0
+
+
Memory
------
View
@@ -181,7 +181,7 @@
"pid_exists", "pids", "process_iter", "wait_procs", # proc
"virtual_memory", "swap_memory", # memory
"cpu_times", "cpu_percent", "cpu_times_percent", "cpu_count", # cpu
- "cpu_stats",
+ "cpu_stats", # "cpu_freq",
"net_io_counters", "net_connections", "net_if_addrs", # network
"net_if_stats",
"disk_io_counters", "disk_partitions", "disk_usage", # disk
@@ -1852,6 +1852,36 @@ def cpu_stats():
return _psplatform.cpu_stats()
+if hasattr(_psplatform, "cpu_freq"):
+
+ def cpu_freq(percpu=False):
+ """Return CPU frequency as a nameduple including current,
+ min and max frequency expressed in Mhz.
+
+ If percpu is True and the system supports per-cpu frequency
+ retrieval (Linux only) a list of frequencies is returned for
+ each CPU. If not a list with one element is returned.
+ """
+ ret = _psplatform.cpu_freq()
+ if percpu:
+ return ret
+ else:
+ num_cpus = len(ret)
+ if num_cpus == 1:
+ return ret[0]
+ currs, mins, maxs = [], [], []
+ for cpu in ret:
+ currs.append(cpu.current)
+ mins.append(cpu.min)
+ maxs.append(cpu.max)
+ return _common.scpufreq(
+ sum(currs) / num_cpus,
+ sum(mins) / num_cpus,
+ sum(maxs) / num_cpus)
+
+ __all__.append("cpu_freq")
+
+
# =====================================================================
# --- system memory related functions
# =====================================================================
View
@@ -156,6 +156,8 @@ class NicDuplex(enum.IntEnum):
# psutil.cpu_stats()
scpustats = namedtuple(
'scpustats', ['ctx_switches', 'interrupts', 'soft_interrupts', 'syscalls'])
+# psutil.cpu_freq()
+scpufreq = namedtuple('scpufreq', ['current', 'min', 'max'])
# --- for Process methods
View
@@ -9,6 +9,7 @@
import base64
import errno
import functools
+import glob
import os
import re
import socket
@@ -133,6 +134,8 @@ class IOPriority(enum.IntEnum):
"0B": _common.CONN_CLOSING
}
+_DEFAULT = object()
+
# =====================================================================
# -- exceptions
@@ -276,6 +279,18 @@ def set_scputimes_ntuple(procfs_path):
return scputimes
+def cat(fname, fallback=_DEFAULT, binary=True):
+ """Return file content."""
+ try:
+ with open_binary(fname) if binary else open_text(fname) as f:
+ return f.read()
+ except IOError:
+ if fallback != _DEFAULT:
+ return fallback
+ else:
+ raise
+
+
try:
scputimes = set_scputimes_ntuple("/proc")
except Exception:
@@ -607,6 +622,26 @@ def cpu_stats():
ctx_switches, interrupts, soft_interrupts, syscalls)
+if os.path.exists("/sys/devices/system/cpu/cpufreq"):
+
+ def cpu_freq():
+ # scaling_* files seem preferable to cpuinfo_*, see:
+ # http://unix.stackexchange.com/a/87537/168884
+ ret = []
+ ls = glob.glob("/sys/devices/system/cpu/cpufreq/policy*")
+ # Sort the list so that '10' comes after '2'. This should
+ # ensure the CPU order is consistent with other CPU functions
+ # having a 'percpu' argument and returning results for multiple
+ # CPUs (cpu_times(), cpu_percent(), cpu_times_percent()).
+ ls.sort(key=lambda x: int(os.path.basename(x)[6:]))
+ for path in ls:
+ curr = int(cat(os.path.join(path, "scaling_cur_freq"))) / 1000
+ max_ = int(cat(os.path.join(path, "scaling_max_freq"))) / 1000
+ min_ = int(cat(os.path.join(path, "scaling_min_freq"))) / 1000
+ ret.append(_common.scpufreq(curr, min_, max_))
+ return ret
+
+
# =====================================================================
# --- network
# =====================================================================
View
@@ -165,6 +165,16 @@ def cpu_stats():
ctx_switches, interrupts, soft_interrupts, syscalls)
+def cpu_freq():
+ """Return CPU frequency.
+ On OSX per-cpu frequency is not supported.
+ Also, the returned frequency never changes, see:
+ https://arstechnica.com/civis/viewtopic.php?f=19&t=465002
+ """
+ curr, min_, max_ = cext.cpu_freq()
+ return [_common.scpufreq(curr, min_, max_)]
+
+
# =====================================================================
# --- disks
# =====================================================================
View
@@ -808,6 +808,35 @@ psutil_per_cpu_times(PyObject *self, PyObject *args) {
}
+/*
+ * Retrieve CPU frequency.
+ */
+static PyObject *
+psutil_cpu_freq(PyObject *self, PyObject *args) {
+ int64_t curr;
+ int64_t min;
+ int64_t max;
+ size_t size = sizeof(int64_t);
+
+ if (sysctlbyname("hw.cpufrequency", &curr, &size, NULL, 0))
+ goto error;
+ if (sysctlbyname("hw.cpufrequency_min", &min, &size, NULL, 0))
+ goto error;
+ if (sysctlbyname("hw.cpufrequency_max", &max, &size, NULL, 0))
+ goto error;
+
+ return Py_BuildValue(
+ "KKK",
+ curr / 1000 / 1000,
+ min / 1000 / 1000,
+ max / 1000 / 1000);
+
+error:
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+}
+
+
/*
* Return a Python float indicating the system boot time expressed in
* seconds since the epoch.
@@ -1778,6 +1807,8 @@ PsutilMethods[] = {
"Return system cpu times as a tuple (user, system, nice, idle, irc)"},
{"per_cpu_times", psutil_per_cpu_times, METH_VARARGS,
"Return system per-cpu times as a list of tuples"},
+ {"cpu_freq", psutil_cpu_freq, METH_VARARGS,
+ "Return cpu current frequency"},
{"boot_time", psutil_boot_time, METH_VARARGS,
"Return the system boot time expressed in seconds since the epoch."},
{"disk_partitions", psutil_disk_partitions, METH_VARARGS,
View
@@ -24,6 +24,7 @@
#include <iphlpapi.h>
#include <wtsapi32.h>
#include <Winsvc.h>
+#include <PowrProf.h>
// Link with Iphlpapi.lib
#pragma comment(lib, "IPHLPAPI.lib")
@@ -145,6 +146,16 @@ typedef struct _MIB_UDP6TABLE_OWNER_PID {
} MIB_UDP6TABLE_OWNER_PID, *PMIB_UDP6TABLE_OWNER_PID;
#endif
+typedef struct _PROCESSOR_POWER_INFORMATION {
+ ULONG Number;
+ ULONG MaxMhz;
+ ULONG CurrentMhz;
+ ULONG MhzLimit;
+ ULONG MaxIdleState;
+ ULONG CurrentIdleState;
+} PROCESSOR_POWER_INFORMATION, *PPROCESSOR_POWER_INFORMATION;
+
+
PIP_ADAPTER_ADDRESSES
psutil_get_nic_addresses() {
// allocate a 15 KB buffer to start with
@@ -3391,6 +3402,60 @@ psutil_cpu_stats(PyObject *self, PyObject *args) {
}
+/*
+ * Return CPU frequency.
+ */
+static PyObject *
+psutil_cpu_freq(PyObject *self, PyObject *args) {
+ PROCESSOR_POWER_INFORMATION *ppi;
+ NTSTATUS ret;
+ size_t size;
+ LPBYTE pBuffer = NULL;
+ ULONG current;
+ ULONG max;
+ unsigned int num_cpus;
+ SYSTEM_INFO system_info;
+ system_info.dwNumberOfProcessors = 0;
+
+ // Get the number of CPUs.
+ GetSystemInfo(&system_info);
+ if (system_info.dwNumberOfProcessors == 0)
+ num_cpus = 1;
+ else
+ num_cpus = system_info.dwNumberOfProcessors;
+
+ // Allocate size.
+ size = num_cpus * sizeof(PROCESSOR_POWER_INFORMATION);
+ pBuffer = (BYTE*)LocalAlloc(LPTR, size);
+ if (! pBuffer) {
+ PyErr_SetFromWindowsErr(0);
+ return NULL;
+ }
+
+ // Syscall.
+ ret = CallNtPowerInformation(
+ ProcessorInformation, NULL, 0, pBuffer, size);
+ if (ret != 0) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "CallNtPowerInformation syscall failed");
+ goto error;
+ }
+
+ // Results.
+ ppi = (PROCESSOR_POWER_INFORMATION *)pBuffer;
+ max = ppi->MaxMhz;
+ current = ppi->CurrentMhz;
+ LocalFree(pBuffer);
+
+ return Py_BuildValue("kk", current, max);
+
+error:
+ if (pBuffer != NULL)
+ LocalFree(pBuffer);
+ return NULL;
+}
+
+
// ------------------------ Python init ---------------------------
static PyMethodDef
@@ -3495,6 +3560,8 @@ PsutilMethods[] = {
"Return NICs stats."},
{"cpu_stats", psutil_cpu_stats, METH_VARARGS,
"Return NICs stats."},
+ {"cpu_freq", psutil_cpu_freq, METH_VARARGS,
+ "Return CPU frequency."},
// --- windows services
{"winservice_enumerate", psutil_winservice_enumerate, METH_VARARGS,
View
@@ -299,6 +299,15 @@ def cpu_stats():
syscalls)
+def cpu_freq():
+ """Return CPU frequency.
+ On Windows per-cpu frequency is not supported.
+ """
+ curr, max_ = cext.cpu_freq()
+ min_ = 0.0
+ return [_common.scpufreq(float(curr), min_, float(max_))]
+
+
# =====================================================================
# --- network
# =====================================================================
@@ -458,6 +458,11 @@ def test_per_cpu_times(self):
def test_cpu_stats(self):
self.execute(psutil.cpu_stats)
+ @skip_if_linux()
+ @unittest.skipUnless(hasattr(psutil, "cpu_freq"), "platform not supported")
+ def test_cpu_freq(self):
+ self.execute(psutil.cpu_freq)
+
# --- mem
def test_virtual_memory(self):
View
@@ -111,6 +111,8 @@ def test_process_create_time(self):
@unittest.skipUnless(OSX, "OSX only")
class TestSystemAPIs(unittest.TestCase):
+ # --- disk
+
def test_disks(self):
# test psutil.disk_usage() and psutil.disk_partitions()
# against "df -a"
@@ -138,6 +140,8 @@ def df(path):
if abs(usage.used - used) > 10 * 1024 * 1024:
self.fail("psutil=%s, df=%s" % usage.used, used)
+ # --- cpu
+
def test_cpu_count_logical(self):
num = sysctl("sysctl hw.logicalcpu")
self.assertEqual(num, psutil.cpu_count(logical=True))
@@ -146,6 +150,15 @@ def test_cpu_count_physical(self):
num = sysctl("sysctl hw.physicalcpu")
self.assertEqual(num, psutil.cpu_count(logical=False))
+ def test_cpu_freq(self):
+ freq = psutil.cpu_freq()[0]
+ self.assertEqual(
+ freq.current * 1000 * 1000, sysctl("sysctl hw.cpufrequency"))
+ self.assertEqual(
+ freq.min * 1000 * 1000, sysctl("sysctl hw.cpufrequency_min"))
+ self.assertEqual(
+ freq.max * 1000 * 1000, sysctl("sysctl hw.cpufrequency_max"))
+
# --- virtual mem
def test_vmem_total(self):
@@ -206,6 +219,8 @@ def test_swapmem_sout(self):
# self.assertEqual(psutil_smem.used, human2bytes(used))
# self.assertEqual(psutil_smem.free, human2bytes(free))
+ # --- network
+
def test_net_if_stats(self):
for name, stats in psutil.net_if_stats().items():
try:
Oops, something went wrong.