Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

941 cpu freq #952

Merged
merged 15 commits into from
Jan 23, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
======
Expand Down
27 changes: 27 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
------

Expand Down
32 changes: 31 additions & 1 deletion psutil/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
# =====================================================================
Expand Down
2 changes: 2 additions & 0 deletions psutil/_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
35 changes: 35 additions & 0 deletions psutil/_pslinux.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import base64
import errno
import functools
import glob
import os
import re
import socket
Expand Down Expand Up @@ -133,6 +134,8 @@ class IOPriority(enum.IntEnum):
"0B": _common.CONN_CLOSING
}

_DEFAULT = object()


# =====================================================================
# -- exceptions
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
# =====================================================================
Expand Down
10 changes: 10 additions & 0 deletions psutil/_psosx.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
# =====================================================================
Expand Down
31 changes: 31 additions & 0 deletions psutil/_psutil_osx.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down
67 changes: 67 additions & 0 deletions psutil/_psutil_windows.c
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
9 changes: 9 additions & 0 deletions psutil/_pswindows.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
# =====================================================================
Expand Down
5 changes: 5 additions & 0 deletions psutil/tests/test_memory_leaks.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
15 changes: 15 additions & 0 deletions psutil/tests/test_osx.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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))
Expand All @@ -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):
Expand Down Expand Up @@ -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:
Expand Down
Loading