summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--psutil/__init__.py30
-rw-r--r--psutil/_psbsd.py7
-rw-r--r--psutil/_pslinux.py24
-rw-r--r--psutil/_psosx.py12
-rw-r--r--psutil/_psutil_bsd.c8
-rw-r--r--psutil/_psutil_common.c17
-rw-r--r--psutil/_psutil_common.h1
-rw-r--r--psutil/_psutil_linux.c29
-rw-r--r--psutil/_psutil_osx.c8
-rw-r--r--psutil/_psutil_windows.c3
-rw-r--r--psutil/_pswindows.py17
-rw-r--r--psutil/arch/freebsd/cpu.c29
-rw-r--r--psutil/arch/freebsd/cpu.h1
-rw-r--r--psutil/arch/netbsd/specific.c29
-rw-r--r--psutil/arch/netbsd/specific.h20
-rw-r--r--psutil/arch/openbsd/cpu.c28
-rw-r--r--psutil/arch/openbsd/cpu.h6
-rw-r--r--psutil/arch/osx/cpu.c229
-rw-r--r--psutil/arch/osx/cpu.h12
-rw-r--r--psutil/arch/windows/cpu.c557
-rw-r--r--psutil/arch/windows/cpu.h4
-rw-r--r--psutil/tests/__init__.py3
-rwxr-xr-xpsutil/tests/test_memleaks.py5
-rwxr-xr-xpsutil/tests/test_osx.py55
-rwxr-xr-xpsutil/tests/test_system.py9
-rwxr-xr-xpsutil/tests/test_windows.py10
-rwxr-xr-xsetup.py2
27 files changed, 1106 insertions, 49 deletions
diff --git a/psutil/__init__.py b/psutil/__init__.py
index 6deebb26..9b781d2b 100644
--- a/psutil/__init__.py
+++ b/psutil/__init__.py
@@ -184,7 +184,7 @@ __all__ = [
"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_freq", "getloadavg"
+ "cpu_stats", # "cpu_freq", "getloadavg", "cpu_info",
"net_io_counters", "net_connections", "net_if_addrs", # network
"net_if_stats",
"disk_io_counters", "disk_partitions", "disk_usage", # disk
@@ -1899,6 +1899,34 @@ if hasattr(_psplatform, "cpu_freq"):
__all__.append("cpu_freq")
+if hasattr(_psplatform, "cpu_info"):
+
+ def cpu_info():
+ """Return CPU varius types of information about the CPU as a dict.
+ Usually used in conjunction with cpu_count().
+ Dictionary keys availability:
+
+ * "model": Linux, macOS, Windows, FreeBSD, NetBSD, OpenBSD
+ * "vendor": Linux, macOS, Windows, OpenBSD
+ * "arch": Linux, macOS, Windows
+ * "byteorder": all
+ * "l1d_cache": Linux, macOS
+ * "l1i_cache": Linux, macOS
+ * "l1_cache": Windows
+ * "l2_cache": Linux, macOS, Windows
+ * "l3_cache": Linux, macOS, Windows
+ * "flags": Linux, macOS, Windows
+ """
+ ret = _psplatform.cpu_info()
+ if 'arch' not in ret and POSIX:
+ ret['arch'] = os.uname()[4]
+ if 'byteorder' not in ret:
+ ret['byteorder'] = sys.byteorder
+ return ret
+
+ __all__.append("cpu_info")
+
+
if hasattr(os, "getloadavg") or hasattr(_psplatform, "getloadavg"):
# Perform this hasattr check once on import time to either use the
# platform based code or proxy straight from the os module.
diff --git a/psutil/_psbsd.py b/psutil/_psbsd.py
index c4200cce..ead05381 100644
--- a/psutil/_psbsd.py
+++ b/psutil/_psbsd.py
@@ -280,6 +280,13 @@ else:
return ret
+def cpu_info():
+ d = dict(model=cext.cpu_model())
+ if OPENBSD:
+ d["vendor"] = cext.cpu_vendor()
+ return d
+
+
def cpu_stats():
"""Return various CPU stats as a named tuple."""
if FREEBSD:
diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py
index 5f149a03..c4ec0c94 100644
--- a/psutil/_pslinux.py
+++ b/psutil/_pslinux.py
@@ -680,6 +680,29 @@ def cpu_count_cores():
return result or None # mimic os.cpu_count()
+def cpu_info():
+ def lookup_in_lines(lines, text):
+ for line in lines:
+ if line.startswith(text):
+ return line.split('\t:', 1)[1].strip()
+ return None
+
+ with open_text('%s/cpuinfo' % get_procfs_path()) as f:
+ lines = f.readlines()
+
+ caches = cext.cpu_caches()
+ return dict(
+ # strings
+ model=lookup_in_lines(lines, "model name"),
+ vendor=lookup_in_lines(lines, 'vendor_id'),
+ flags=lookup_in_lines(lines, 'flags'),
+ l1i_cache=caches[0] if caches[0] != -1 else None,
+ l1d_cache=caches[1] if caches[1] != -1 else None,
+ l2_cache=caches[2] if caches[2] != -1 else None,
+ l3_cache=caches[3] if caches[3] != -1 else None,
+ )
+
+
def cpu_stats():
"""Return various CPU stats as a named tuple."""
with open_binary('%s/stat' % get_procfs_path()) as f:
@@ -714,6 +737,7 @@ def _cpu_get_cpuinfo_freq():
if os.path.exists("/sys/devices/system/cpu/cpufreq/policy0") or \
os.path.exists("/sys/devices/system/cpu/cpu0/cpufreq"):
+
def cpu_freq():
"""Return frequency metrics for all CPUs.
Contrarily to other OSes, Linux updates these values in
diff --git a/psutil/_psosx.py b/psutil/_psosx.py
index ac8ecc53..b54c2101 100644
--- a/psutil/_psosx.py
+++ b/psutil/_psosx.py
@@ -170,6 +170,18 @@ def cpu_stats():
ctx_switches, interrupts, soft_interrupts, syscalls)
+def cpu_info():
+ return dict(
+ model=cext.cpu_model(),
+ vendor=cext.cpu_vendor(),
+ l1i_cache=cext.cpu_l1i_cache(),
+ l1d_cache=cext.cpu_l1d_cache(),
+ l2_cache=cext.cpu_l2_cache(),
+ l3_cache=cext.cpu_l3_cache(),
+ flags=cext.cpu_flags(),
+ )
+
+
def cpu_freq():
"""Return CPU frequency.
On macOS per-cpu frequency is not supported.
diff --git a/psutil/_psutil_bsd.c b/psutil/_psutil_bsd.c
index 13170838..a1aec439 100644
--- a/psutil/_psutil_bsd.c
+++ b/psutil/_psutil_bsd.c
@@ -1112,12 +1112,20 @@ static PyMethodDef mod_methods[] = {
{"sensors_battery", psutil_sensors_battery, METH_VARARGS},
{"sensors_cpu_temperature", psutil_sensors_cpu_temperature, METH_VARARGS},
#endif
+#if defined(PSUTIL_NETBSD)
+ {"cpu_model", psutil_cpu_model, METH_VARARGS, ""},
+#endif
+#if defined(PSUTIL_OPENBSD)
+ {"cpu_vendor", psutil_cpu_vendor, METH_VARARGS, ""},
+ {"cpu_model", psutil_cpu_model, METH_VARARGS, ""},
+#endif
// --- others
{"set_debug", psutil_set_debug, METH_VARARGS},
{NULL, NULL, 0, NULL}
};
+
#if PY_MAJOR_VERSION >= 3
#define INITERR return NULL
diff --git a/psutil/_psutil_common.c b/psutil/_psutil_common.c
index 9679da67..31a62008 100644
--- a/psutil/_psutil_common.c
+++ b/psutil/_psutil_common.c
@@ -151,6 +151,23 @@ psutil_set_debug(PyObject *self, PyObject *args) {
}
+/*
+ * Add a new python object to an existing dict, DECREFing that object and
+ * setting it to NULL both in case of success or failure.
+ */
+int
+psutil_add_to_dict(PyObject *py_dict, char *keyname, PyObject *py_obj) {
+ if (!py_obj)
+ return 1;
+ if (PyDict_SetItemString(py_dict, keyname, py_obj)) {
+ Py_CLEAR(py_obj);
+ return 1;
+ }
+ Py_CLEAR(py_obj);
+ return 0;
+}
+
+
// ============================================================================
// Utility functions (BSD)
// ============================================================================
diff --git a/psutil/_psutil_common.h b/psutil/_psutil_common.h
index 591f5521..6c3d97e4 100644
--- a/psutil/_psutil_common.h
+++ b/psutil/_psutil_common.h
@@ -99,6 +99,7 @@ PyObject* PyErr_SetFromOSErrnoWithSyscall(const char *syscall);
// --- Global utils
// ====================================================================
+int psutil_add_to_dict(PyObject *py_dict, char *keyname, PyObject *py_obj);
PyObject* psutil_set_debug(PyObject *self, PyObject *args);
int psutil_setup(void);
diff --git a/psutil/_psutil_linux.c b/psutil/_psutil_linux.c
index 70cf5d1b..743fc786 100644
--- a/psutil/_psutil_linux.c
+++ b/psutil/_psutil_linux.c
@@ -469,6 +469,32 @@ error:
/*
+ * Return L1/2/3 CPU cache sizes.
+ */
+static PyObject*
+psutil_cpu_caches(PyObject* self, PyObject* args) {
+ long l1i = -1;
+ long l1d = -1;
+ long l2 = -1;
+ long l3 = -1;
+
+#ifdef _SC_LEVEL1_ICACHE_SIZE
+ l1i = sysconf(_SC_LEVEL1_ICACHE_SIZE);
+#endif
+#ifdef _SC_LEVEL1_DCACHE_SIZE
+ l1d = sysconf(_SC_LEVEL1_DCACHE_SIZE);
+#endif
+#ifdef _SC_LEVEL2_CACHE_SIZE
+ l2 = sysconf(_SC_LEVEL2_CACHE_SIZE);
+#endif
+#ifdef _SC_LEVEL3_CACHE_SIZE
+ l3 = sysconf(_SC_LEVEL3_CACHE_SIZE);
+#endif
+ return Py_BuildValue("llll", l1i, l1d, l2, l3);
+}
+
+
+/*
* Module init.
*/
@@ -484,9 +510,10 @@ static PyMethodDef mod_methods[] = {
{"proc_cpu_affinity_set", psutil_proc_cpu_affinity_set, METH_VARARGS},
#endif
// --- system related functions
+ {"cpu_caches", psutil_cpu_caches, METH_VARARGS},
{"disk_partitions", psutil_disk_partitions, METH_VARARGS},
- {"users", psutil_users, METH_VARARGS},
{"net_if_duplex_speed", psutil_net_if_duplex_speed, METH_VARARGS},
+ {"users", psutil_users, METH_VARARGS},
// --- linux specific
{"linux_sysinfo", psutil_linux_sysinfo, METH_VARARGS},
diff --git a/psutil/_psutil_osx.c b/psutil/_psutil_osx.c
index 2470c3eb..5e74cadd 100644
--- a/psutil/_psutil_osx.c
+++ b/psutil/_psutil_osx.c
@@ -24,7 +24,6 @@
#include <mach/mach.h>
#include <mach/mach_vm.h>
#include <mach/shared_region.h>
-
#include <mach-o/loader.h>
#include <CoreFoundation/CoreFoundation.h>
@@ -1662,9 +1661,16 @@ static PyMethodDef mod_methods[] = {
{"boot_time", psutil_boot_time, METH_VARARGS},
{"cpu_count_cores", psutil_cpu_count_cores, METH_VARARGS},
{"cpu_count_logical", psutil_cpu_count_logical, METH_VARARGS},
+ {"cpu_flags", psutil_cpu_flags, METH_VARARGS},
{"cpu_freq", psutil_cpu_freq, METH_VARARGS},
+ {"cpu_l1d_cache", psutil_cpu_l1d_cache, METH_VARARGS},
+ {"cpu_l1i_cache", psutil_cpu_l1i_cache, METH_VARARGS},
+ {"cpu_l2_cache", psutil_cpu_l2_cache, METH_VARARGS},
+ {"cpu_l3_cache", psutil_cpu_l3_cache, METH_VARARGS},
+ {"cpu_model", psutil_cpu_model, METH_VARARGS},
{"cpu_stats", psutil_cpu_stats, METH_VARARGS},
{"cpu_times", psutil_cpu_times, METH_VARARGS},
+ {"cpu_vendor", psutil_cpu_vendor, METH_VARARGS},
{"disk_io_counters", psutil_disk_io_counters, METH_VARARGS},
{"disk_partitions", psutil_disk_partitions, METH_VARARGS},
{"net_io_counters", psutil_net_io_counters, METH_VARARGS},
diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c
index 83da3a26..5ce2ee02 100644
--- a/psutil/_psutil_windows.c
+++ b/psutil/_psutil_windows.c
@@ -1587,6 +1587,7 @@ PsutilMethods[] = {
{"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},
+ {"cpu_info", psutil_cpu_info, 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},
@@ -1616,6 +1617,7 @@ PsutilMethods[] = {
{"winservice_stop", psutil_winservice_stop, METH_VARARGS},
// --- windows API bindings
+ {"GetLogicalProcessorInformationEx", psutil_GetLogicalProcessorInformationEx, METH_VARARGS},
{"QueryDosDevice", psutil_QueryDosDevice, METH_VARARGS},
// --- others
@@ -1625,6 +1627,7 @@ PsutilMethods[] = {
};
+
struct module_state {
PyObject *error;
};
diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py
index 9966b1b4..55dc7173 100644
--- a/psutil/_pswindows.py
+++ b/psutil/_pswindows.py
@@ -315,7 +315,7 @@ def cpu_count_logical():
def cpu_count_cores():
"""Return the number of CPU cores in the system."""
- return cext.cpu_count_cores()
+ return cext.GetLogicalProcessorInformationEx()['cores'] or None
def cpu_stats():
@@ -335,6 +335,21 @@ def cpu_freq():
return [_common.scpufreq(float(curr), min_, float(max_))]
+def cpu_info():
+ """Return CPU hardware-related information."""
+ ret = cext.cpu_info()
+ infoex = cext.GetLogicalProcessorInformationEx()
+ ret['l1_cache'] = infoex['l1_cache']
+ ret['l2_cache'] = infoex['l2_cache']
+ ret['l3_cache'] = infoex['l3_cache']
+ # https://superuser.com/a/1441469
+ # "AMD64", "X86", "IA64", "ARM64", "EM64T"
+ ret['arch'] = \
+ os.environ.get('PROCESSOR_ARCHITEW6432', '') or \
+ os.environ.get('PROCESSOR_ARCHITECTURE', '') or None
+ return ret
+
+
_loadavg_inititialized = False
diff --git a/psutil/arch/freebsd/cpu.c b/psutil/arch/freebsd/cpu.c
index f31e9bb0..8ab6beac 100644
--- a/psutil/arch/freebsd/cpu.c
+++ b/psutil/arch/freebsd/cpu.c
@@ -128,3 +128,32 @@ error:
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
+
+
+PyObject *
+psutil_cpu_model(PyObject *self, PyObject *args) {
+ void *buf = NULL;
+ size_t size = 0;
+ PyObject *py_str;
+
+ if (sysctlbyname("hw.model", NULL, &size, NULL, 0))
+ goto error;
+
+ buf = malloc(size);
+ if (!buf) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ if (sysctlbyname("hw.model", buf, &size, NULL, 0))
+ goto error;
+
+ py_str = Py_BuildValue("s", buf);
+ free(buf);
+ return py_str;
+
+error:
+ if (buf != NULL)
+ free(buf);
+ Py_RETURN_NONE;
+}
diff --git a/psutil/arch/freebsd/cpu.h b/psutil/arch/freebsd/cpu.h
index 8decd773..9676c523 100644
--- a/psutil/arch/freebsd/cpu.h
+++ b/psutil/arch/freebsd/cpu.h
@@ -9,3 +9,4 @@
PyObject* psutil_cpu_freq(PyObject* self, PyObject* args);
PyObject* psutil_cpu_stats(PyObject* self, PyObject* args);
PyObject* psutil_cpu_topology(PyObject* self, PyObject* args);
+PyObject* psutil_cpu_model(PyObject* self, PyObject* args);
diff --git a/psutil/arch/netbsd/specific.c b/psutil/arch/netbsd/specific.c
index 4e286e5e..e9f8a915 100644
--- a/psutil/arch/netbsd/specific.c
+++ b/psutil/arch/netbsd/specific.c
@@ -686,3 +686,32 @@ psutil_cpu_stats(PyObject *self, PyObject *args) {
uv.forks // forks
);
}
+
+
+PyObject *
+psutil_cpu_model(PyObject *self, PyObject *args) {
+ void *buf = NULL;
+ size_t size = 0;
+ PyObject *py_str;
+
+ if (sysctlbyname("machdep.cpu_brand", NULL, &size, NULL, 0))
+ goto error;
+
+ buf = malloc(size);
+ if (!buf) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ if (sysctlbyname("machdep.cpu_brand", buf, &size, NULL, 0))
+ goto error;
+
+ py_str = Py_BuildValue("s", buf);
+ free(buf);
+ return py_str;
+
+error:
+ if (buf != NULL)
+ free(buf);
+ Py_RETURN_NONE;
+}
diff --git a/psutil/arch/netbsd/specific.h b/psutil/arch/netbsd/specific.h
index 391ed164..1892756b 100644
--- a/psutil/arch/netbsd/specific.h
+++ b/psutil/arch/netbsd/specific.h
@@ -14,16 +14,16 @@ struct kinfo_file * kinfo_getfile(pid_t pid, int* cnt);
int psutil_get_proc_list(kinfo_proc **procList, size_t *procCount);
char *psutil_get_cmd_args(pid_t pid, size_t *argsize);
-//
+PyObject *psutil_cpu_model(PyObject *self, PyObject *args);
+PyObject *psutil_cpu_stats(PyObject* self, PyObject* args);
+PyObject *psutil_disk_io_counters(PyObject* self, PyObject* args);
PyObject *psutil_get_cmdline(pid_t pid);
-PyObject *psutil_proc_threads(PyObject *self, PyObject *args);
-PyObject *psutil_virtual_mem(PyObject *self, PyObject *args);
-PyObject *psutil_swap_mem(PyObject *self, PyObject *args);
-PyObject *psutil_proc_num_fds(PyObject *self, PyObject *args);
-PyObject *psutil_proc_connections(PyObject *self, PyObject *args);
PyObject *psutil_per_cpu_times(PyObject *self, PyObject *args);
-PyObject* psutil_disk_io_counters(PyObject* self, PyObject* args);
-PyObject* psutil_proc_exe(PyObject* self, PyObject* args);
-PyObject* psutil_proc_num_threads(PyObject* self, PyObject* args);
-PyObject* psutil_cpu_stats(PyObject* self, PyObject* args);
+PyObject *psutil_proc_connections(PyObject *self, PyObject *args);
PyObject *psutil_proc_cwd(PyObject *self, PyObject *args);
+PyObject *psutil_proc_exe(PyObject* self, PyObject* args);
+PyObject *psutil_proc_num_fds(PyObject *self, PyObject *args);
+PyObject *psutil_proc_num_threads(PyObject* self, PyObject* args);
+PyObject *psutil_proc_threads(PyObject *self, PyObject *args);
+PyObject *psutil_swap_mem(PyObject *self, PyObject *args);
+PyObject *psutil_virtual_mem(PyObject *self, PyObject *args);
diff --git a/psutil/arch/openbsd/cpu.c b/psutil/arch/openbsd/cpu.c
index 0691fd1f..3724843e 100644
--- a/psutil/arch/openbsd/cpu.c
+++ b/psutil/arch/openbsd/cpu.c
@@ -107,3 +107,31 @@ psutil_cpu_freq(PyObject *self, PyObject *args) {
return Py_BuildValue("i", freq);
}
+
+
+PyObject *
+psutil_cpu_vendor(PyObject *self, PyObject *args) {
+ char vendor[128];
+ size_t size = sizeof(vendor);
+ int mib[2] = {CTL_HW, HW_VENDOR};
+
+ if (sysctl(mib, 2, vendor, &size, NULL, 0) < 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+ return Py_BuildValue("s", vendor);
+}
+
+
+PyObject *
+psutil_cpu_model(PyObject *self, PyObject *args) {
+ char product[128];
+ size_t size = sizeof(product);
+ int mib[2] = {CTL_HW, HW_PRODUCT};
+
+ if (sysctl(mib, 2, product, &size, NULL, 0) < 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+ return Py_BuildValue("s", product);
+}
diff --git a/psutil/arch/openbsd/cpu.h b/psutil/arch/openbsd/cpu.h
index 07bf95fd..5a6d3a65 100644
--- a/psutil/arch/openbsd/cpu.h
+++ b/psutil/arch/openbsd/cpu.h
@@ -7,6 +7,8 @@
#include <Python.h>
-PyObject *psutil_cpu_freq(PyObject* self, PyObject* args);
-PyObject *psutil_cpu_stats(PyObject* self, PyObject* args);
+PyObject *psutil_cpu_freq(PyObject *self, PyObject* args);
+PyObject *psutil_cpu_model(PyObject *self, PyObject* args);
+PyObject *psutil_cpu_stats(PyObject *self, PyObject* args);
+PyObject *psutil_cpu_vendor(PyObject *self, PyObject* args);
PyObject *psutil_per_cpu_times(PyObject *self, PyObject *args);
diff --git a/psutil/arch/osx/cpu.c b/psutil/arch/osx/cpu.c
index 37141a2d..cdd943e1 100644
--- a/psutil/arch/osx/cpu.c
+++ b/psutil/arch/osx/cpu.c
@@ -7,6 +7,15 @@
/*
System-wide CPU related functions.
+References:
+- https://opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/sys/sysctl.h.auto.html
+- sysctl C types: https://ss64.com/osx/sysctl.html
+- https://apple.stackexchange.com/questions/238777
+- it looks like CPU "sockets" on macOS are called "packages"
+- it looks like macOS does not support NUMA nodes:
+ https://apple.stackexchange.com/questions/36465/do-mac-pros-use-numa
+- $ sysctl -a | grep machdep.cpu
+
Original code was refactored and moved from psutil/_psutil_osx.c in 2020
right before a4c0a0eb0d2a872ab7a45e47fcf37ef1fde5b012.
For reference, here's the git history with original implementations:
@@ -18,8 +27,11 @@ For reference, here's the git history with original implementations:
- CPU frequency: 6ba1ac4ebfcd8c95fca324b15606ab0ec1412d39
*/
+
#include <Python.h>
#include <sys/sysctl.h>
+#include <ctype.h>
+#include <sys/sysctl.h>
#include <sys/vmmeter.h>
#include <mach/mach_error.h>
@@ -30,7 +42,6 @@ For reference, here's the git history with original implementations:
#include "../../_psutil_posix.h"
-
PyObject *
psutil_cpu_count_logical(PyObject *self, PyObject *args) {
int num;
@@ -56,6 +67,20 @@ psutil_cpu_count_cores(PyObject *self, PyObject *args) {
PyObject *
+psutil_cpu_sockets() {
+ // It looks like on macOS "sockets" are called "packages".
+ // Hopefully it's the same thing.
+ int value;
+ size_t size = sizeof(value);
+
+ if (sysctlbyname("hw.packages", &value, &size, NULL, 2))
+ Py_RETURN_NONE;
+ else
+ return Py_BuildValue("i", value);
+}
+
+
+PyObject *
psutil_cpu_times(PyObject *self, PyObject *args) {
mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
kern_return_t error;
@@ -138,3 +163,205 @@ psutil_cpu_freq(PyObject *self, PyObject *args) {
min / 1000 / 1000,
max / 1000 / 1000);
}
+
+
+PyObject *
+psutil_cpu_model() {
+ size_t len;
+ char *buffer;
+ PyObject *py_str = NULL;
+
+ if (sysctlbyname("machdep.cpu.brand_string", NULL, &len, NULL, 0) != 0)
+ Py_RETURN_NONE;
+
+ buffer = malloc(len);
+ if (sysctlbyname("machdep.cpu.brand_string", buffer, &len, NULL, 0) != 0) {
+ free(buffer);
+ Py_RETURN_NONE;
+ }
+
+ py_str = Py_BuildValue("s", buffer);
+ free(buffer);
+ return py_str;
+}
+
+
+PyObject *
+psutil_cpu_vendor() {
+ size_t len;
+ char *buffer;
+ PyObject *py_str = NULL;
+
+ if (sysctlbyname("machdep.cpu.vendor", NULL, &len, NULL, 0) != 0)
+ Py_RETURN_NONE;
+
+ buffer = malloc(len);
+ if (sysctlbyname("machdep.cpu.vendor", buffer, &len, NULL, 0) != 0) {
+ free(buffer);
+ Py_RETURN_NONE;
+ }
+
+ py_str = Py_BuildValue("s", buffer);
+ free(buffer);
+ return py_str;
+}
+
+
+PyObject *
+psutil_cpu_flags() {
+ size_t len1;
+ size_t len2;
+ char *buf1 = NULL;
+ char *buf2 = NULL;
+ char *buf3 = NULL;
+ int i;
+ PyObject *py_str = NULL;
+
+ // There are standard and extended features (both strings). First,
+ // get the size for both.
+ if (sysctlbyname("machdep.cpu.features", NULL, &len1, NULL, 0) != 0)
+ Py_RETURN_NONE;
+ if (sysctlbyname("machdep.cpu.extfeatures", NULL, &len2, NULL, 0) != 0)
+ Py_RETURN_NONE;
+
+ // Now we get the real values; we need 2 mallocs.
+
+ // ...standard
+ buf1 = malloc(len1);
+ if (buf1 == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+ if (sysctlbyname("machdep.cpu.features", buf1, &len1, NULL, 0) != 0)
+ goto error;
+
+ // ...extended
+ buf2 = malloc(len2);
+ if (buf2 == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+ if (sysctlbyname("machdep.cpu.extfeatures", buf2, &len2, NULL, 0) != 0)
+ goto error;
+
+ // Lower case both strings (mimic Linux lscpu).
+ for (i = 0; buf1[i]; i++)
+ buf1[i] = tolower(buf1[i]);
+ for (i = 0; buf2[i]; i++)
+ buf2[i] = tolower(buf2[i]);
+
+ // Make space for both in a new buffer and join them (+1 is for the
+ // null terminator).
+ buf3 = malloc(len1 + len2 + 1);
+ if (buf3 == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+ sprintf(buf3, "%s %s", buf1, buf2);
+
+ // Return.
+ py_str = Py_BuildValue("s", buf3);
+ free(buf1);
+ free(buf2);
+ free(buf3);
+ return py_str;
+
+error:
+ if (buf1 != NULL)
+ free(buf1);
+ if (buf2 != NULL)
+ free(buf2);
+ if (buf3 != NULL)
+ free(buf3);
+ if (PyErr_Occurred()) // malloc failed
+ return NULL;
+ Py_RETURN_NONE;
+}
+
+
+// also available as sysctlbyname("hw.l1icachesize") but it returns 1
+PyObject *
+psutil_cpu_l1i_cache() {
+ int value;
+ size_t len = sizeof(value);
+ int mib[2] = { CTL_HW, HW_L1ICACHESIZE };
+
+ if (sysctl(mib, 2, &value, &len, NULL, 0) < 0)
+ Py_RETURN_NONE;
+ else
+ return Py_BuildValue("i", value);
+}
+
+
+// also available as sysctlbyname("hw.l1dcachesize") but it returns 1
+PyObject *
+psutil_cpu_l1d_cache() {
+ int value;
+ size_t len = sizeof(value);
+ int mib[2] = { CTL_HW, HW_L1DCACHESIZE };
+
+ if (sysctl(mib, 2, &value, &len, NULL, 0) < 0)
+ Py_RETURN_NONE;
+ else
+ return Py_BuildValue("i", value);
+}
+
+
+// also available as sysctlbyname("hw.l2cachesize") but it returns 1
+PyObject *
+psutil_cpu_l2_cache() {
+ int value;
+ size_t len = sizeof(value);
+ int mib[2] = { CTL_HW, HW_L2CACHESIZE };
+
+ if (sysctl(mib, 2, &value, &len, NULL, 0) < 0)
+ Py_RETURN_NONE;
+ else
+ return Py_BuildValue("i", value);
+}
+
+
+// also available as sysctlbyname("hw.l3cachesize") but it returns 1
+PyObject *
+psutil_cpu_l3_cache() {
+ int value;
+ size_t len = sizeof(value);
+ int mib[2] = { CTL_HW, HW_L3CACHESIZE };
+
+ if (sysctl(mib, 2, &value, &len, NULL, 0) < 0)
+ Py_RETURN_NONE;
+ else
+ return Py_BuildValue("i", value);
+}
+
+
+/*
+PyObject *
+psutil_cpu_num_cores_per_socket() {
+ int value;
+ size_t size = sizeof(value);
+
+ if (sysctlbyname("machdep.cpu.cores_per_package",
+ &value, &size, NULL, 0) != 0) {
+ Py_RETURN_NONE;
+ }
+ else
+ return Py_BuildValue("i", value);
+}
+
+
+// "threads_per_core" is how it's being called by lscpu on Linux.
+// Here it's "thread_count". Hopefully it's the same thing.
+PyObject *
+psutil_cpu_threads_per_core() {
+ int value;
+ size_t size = sizeof(value);
+
+ if (sysctlbyname("machdep.cpu.thread_count",
+ &value, &size, NULL, 0) != 0) {
+ Py_RETURN_NONE;
+ }
+ else
+ return Py_BuildValue("i", value);
+}
+*/
diff --git a/psutil/arch/osx/cpu.h b/psutil/arch/osx/cpu.h
index aac0f809..d6c91557 100644
--- a/psutil/arch/osx/cpu.h
+++ b/psutil/arch/osx/cpu.h
@@ -6,8 +6,16 @@
#include <Python.h>
-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_count_logical(PyObject *self, PyObject *args);
+PyObject *psutil_cpu_count_sockets(PyObject *self, PyObject *args);
+PyObject *psutil_cpu_flags(PyObject *self, PyObject *args);
PyObject *psutil_cpu_freq(PyObject *self, PyObject *args);
+PyObject *psutil_cpu_l1d_cache(PyObject *self, PyObject *args);
+PyObject *psutil_cpu_l1i_cache(PyObject *self, PyObject *args);
+PyObject *psutil_cpu_l2_cache(PyObject *self, PyObject *args);
+PyObject *psutil_cpu_l3_cache(PyObject *self, PyObject *args);
+PyObject *psutil_cpu_model(PyObject *self, PyObject *args);
PyObject *psutil_cpu_stats(PyObject *self, PyObject *args);
+PyObject *psutil_cpu_times(PyObject *self, PyObject *args);
+PyObject *psutil_cpu_vendor(PyObject *self, PyObject *args);
diff --git a/psutil/arch/windows/cpu.c b/psutil/arch/windows/cpu.c
index 20c01a0d..a6ab68d2 100644
--- a/psutil/arch/windows/cpu.c
+++ b/psutil/arch/windows/cpu.c
@@ -7,6 +7,9 @@
#include <Python.h>
#include <windows.h>
#include <PowrProf.h>
+#include <stdio.h>
+#include <string.h>
+#include <intrin.h>
#include "../../_psutil_common.h"
@@ -177,17 +180,29 @@ psutil_cpu_count_logical(PyObject *self, PyObject *args) {
/*
- * Return the number of CPU cores (non hyper-threading).
+ * Re-adapted from:
+ * https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/
+ * nf-sysinfoapi-getlogicalprocessorinformation?redirectedfrom=MSDN
*/
PyObject *
-psutil_cpu_count_cores(PyObject *self, PyObject *args) {
+psutil_GetLogicalProcessorInformationEx(PyObject *self, PyObject *args) {
DWORD rc;
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX buffer = NULL;
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX ptr = NULL;
DWORD length = 0;
DWORD offset = 0;
- DWORD ncpus = 0;
- DWORD prev_processor_info_size = 0;
+ DWORD coresCount = 0;
+ DWORD socketsCount = 0;
+ DWORD numaNodesCount = 0;
+ DWORD L1CacheCount = 0;
+ DWORD L2CacheCount = 0;
+ DWORD L3CacheCount = 0;
+ DWORD prevProcInfoSize = 0;
+ PCACHE_RELATIONSHIP Cache;
+ PyObject *py_retdict = PyDict_New();
+
+ if (py_retdict == NULL)
+ return NULL;
// GetLogicalProcessorInformationEx() is available from Windows 7
// onward. Differently from GetLogicalProcessorInformation()
@@ -196,12 +211,11 @@ psutil_cpu_count_cores(PyObject *self, PyObject *args) {
// https://bugs.python.org/issue33166
if (GetLogicalProcessorInformationEx == NULL) {
psutil_debug("Win < 7; cpu_count_cores() forced to None");
- Py_RETURN_NONE;
+ return py_retdict;
}
while (1) {
- rc = GetLogicalProcessorInformationEx(
- RelationAll, buffer, &length);
+ rc = GetLogicalProcessorInformationEx(RelationAll, buffer, &length);
if (rc == FALSE) {
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
if (buffer) {
@@ -215,9 +229,9 @@ psutil_cpu_count_cores(PyObject *self, PyObject *args) {
}
}
else {
- psutil_debug("GetLogicalProcessorInformationEx() returned %u",
- GetLastError());
- goto return_none;
+ PyErr_SetFromOSErrnoWithSyscall(
+ "GetLogicalProcessorInformationEx");
+ return NULL;
}
}
else {
@@ -230,31 +244,61 @@ psutil_cpu_count_cores(PyObject *self, PyObject *args) {
// Advance ptr by the size of the previous
// SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX struct.
ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*) \
- (((char*)ptr) + prev_processor_info_size);
+ (((char*)ptr) + prevProcInfoSize);
if (ptr->Relationship == RelationProcessorCore) {
- ncpus += 1;
+ coresCount += 1;
+ }
+ else if (ptr->Relationship == RelationProcessorCore) {
+ numaNodesCount += 1;
+ }
+ else if (ptr->Relationship == RelationProcessorPackage) {
+ socketsCount += 1;
+ }
+ else if (ptr->Relationship == RelationCache) {
+ // Cache data is in ptr->Cache, one CACHE_DESCRIPTOR structure for each cache.
+ Cache = &ptr->Cache;
+ if (Cache->Level == 1)
+ L1CacheCount = Cache->CacheSize;
+ else if (Cache->Level == 2)
+ L2CacheCount = Cache->CacheSize;
+ else if (Cache->Level == 3)
+ L3CacheCount = Cache->CacheSize;
}
// When offset == length, we've reached the last processor
// info struct in the buffer.
offset += ptr->Size;
- prev_processor_info_size = ptr->Size;
+ prevProcInfoSize = ptr->Size;
}
free(buffer);
- if (ncpus != 0) {
- return Py_BuildValue("I", ncpus);
+
+ if (psutil_add_to_dict(py_retdict, "cores",
+ Py_BuildValue("I", coresCount)) == 1) {
+ return NULL;
}
- else {
- psutil_debug("GetLogicalProcessorInformationEx() count was 0");
- Py_RETURN_NONE; // mimick os.cpu_count()
+ if (psutil_add_to_dict(py_retdict, "sockets",
+ Py_BuildValue("I", socketsCount)) == 1) {
+ return NULL;
}
-
-return_none:
- if (buffer != NULL)
- free(buffer);
- Py_RETURN_NONE;
+ if (psutil_add_to_dict(py_retdict, "numa",
+ Py_BuildValue("I", numaNodesCount)) == 1) {
+ return NULL;
+ }
+ if (psutil_add_to_dict(py_retdict, "l1_cache",
+ Py_BuildValue("I", L1CacheCount)) == 1) {
+ return NULL;
+ }
+ if (psutil_add_to_dict(py_retdict, "l2_cache",
+ Py_BuildValue("I", L2CacheCount)) == 1) {
+ return NULL;
+ }
+ if (psutil_add_to_dict(py_retdict, "l3_cache",
+ Py_BuildValue("I", L3CacheCount)) == 1) {
+ return NULL;
+ }
+ return py_retdict;
}
@@ -412,3 +456,470 @@ error:
LocalFree(pBuffer);
return NULL;
}
+
+
+// CPU info for x86, x64 processors.
+// Re-adapted from: https://docs.microsoft.com/en-us/previous-versions/
+// visualstudio/visual-studio-2008/hskdteyh(v=vs.90)?redirectedfrom=MSDN
+// List of CPU flags:
+// https://project.altservice.com/documents/14
+
+typedef enum {
+ false = 0,
+ true = 1,
+} bool;
+
+static char* szFeatures[] = {
+ "fpu", // "x87 FPU On Chip",
+ "vme", // "Virtual-8086 Mode Enhancement",
+ "de", // "Debugging Extensions",
+ "psa", // "Page Size Extensions",
+ "tsc", // "Time Stamp Counter",
+ "msr", // "RDMSR and WRMSR Support",
+ "pae", // "Physical Address Extensions",
+ "mce", // "Machine Check Exception",
+ "cx8", // "CMPXCHG8B Instruction",
+ "apic", // "APIC On Chip",
+ "unknown1", // "Unknown1",
+ "sep", // "SYSENTER and SYSEXIT",
+ "mtrr", // "Memory Type Range Registers",
+ "pge", // "PTE Global Bit",
+ "mca", // "Machine Check Architecture",
+ "cmov", // "Conditional Move/Compare Instruction",
+ "pat", // "Page Attribute Table",
+ "pse36", // "36-bit Page Size Extension",
+ "pn", // "Processor Serial Number",
+ "clflush", // "CFLUSH Extension",
+ "unknown2", // "Unknown2",
+ "dts", // "Debug Store",
+ "tmclockctrl", // "Thermal Monitor and Clock Ctrl", ???
+ "mmx", // "MMX Technology",
+ "fxsr", // "FXSAVE/FXRSTOR",
+ "sse", // "SSE Extensions",
+ "sse2", // "SSE2 Extensions",
+ "ss", // "Self Snoop",
+ "mthread", // "Multithreading Technology",
+ "tm", // "Thermal Monitor",
+ "unknown4", // "Unknown4",
+ "pbe", // "Pending Break Enable"
+};
+
+
+static void
+stradd(char *base, int len, char *tail) {
+ if (strlen(base) != 0)
+ strcat_s(base, len, " ");
+ strcat_s(base, len, tail);
+}
+
+
+PyObject *
+psutil_cpu_info(PyObject *self, PyObject *args) {
+ char CPUString[0x20];
+ char CPUBrandString[0x40];
+ int CPUInfo[4] = {-1};
+ int nSteppingID = 0;
+ int nModel = 0;
+ int nFamily = 0;
+ int nProcessorType = 0;
+ int nExtendedmodel = 0;
+ int nExtendedfamily = 0;
+ int nBrandIndex = 0;
+ int nCLFLUSHcachelinesize = 0;
+ int nLogicalProcessors = 0;
+ int nAPICPhysicalID = 0;
+ int nFeatureInfo = 0;
+ int nCacheLineSize = 0;
+ int nL2Associativity = 0;
+ int nCacheSizeK = 0;
+ int nPhysicalAddress = 0;
+ int nVirtualAddress = 0;
+ int nRet = 0;
+
+ int nCores = 0;
+ int nCacheType = 0;
+ int nCacheLevel = 0;
+ int nMaxThread = 0;
+ int nSysLineSize = 0;
+ int nPhysicalLinePartitions = 0;
+ int nWaysAssociativity = 0;
+ int nNumberSets = 0;
+
+ unsigned nIds, nExIds, i;
+
+ bool bSSE3Instructions = false;
+ bool bMONITOR_MWAIT = false;
+ bool bCPLQualifiedDebugStore = false;
+ bool bVirtualMachineExtensions = false;
+ bool bEnhancedIntelSpeedStepTechnology = false;
+ bool bThermalMonitor2 = false;
+ bool bSupplementalSSE3 = false;
+ bool bL1ContextID = false;
+ bool bCMPXCHG16B = false;
+ bool bxTPRUpdateControl = false;
+ bool bPerfDebugCapabilityMSR = false;
+ bool bSSE41Extensions = false;
+ bool bSSE42Extensions = false;
+ bool bPOPCNT = false;
+
+ bool bMultithreading = false;
+
+ bool bLAHF_SAHFAvailable = false;
+ bool bCmpLegacy = false;
+ bool bSVM = false;
+ bool bExtApicSpace = false;
+ bool bAltMovCr8 = false;
+ bool bLZCNT = false;
+ bool bSSE4A = false;
+ bool bMisalignedSSE = false;
+ bool bPREFETCH = false;
+ bool bSKINITandDEV = false;
+ bool bSYSCALL_SYSRETAvailable = false;
+ bool bExecuteDisableBitAvailable = false;
+ bool bMMXExtensions = false;
+ bool bFFXSR = false;
+ bool b1GBSupport = false;
+ bool bRDTSCP = false;
+ bool b64Available = false;
+ bool b3DNowExt = false;
+ bool b3DNow = false;
+ bool bNestedPaging = false;
+ bool bLBRVisualization = false;
+ bool bFP128 = false;
+ bool bMOVOptimization = false;
+
+ bool bSelfInit = false;
+ bool bFullyAssociative = false;
+ char flags[900] = "";
+
+ PyObject *py_retdict = PyDict_New();
+
+ if (py_retdict == NULL)
+ return NULL;
+
+ // __cpuid with an InfoType argument of 0 returns the number of
+ // valid Ids in CPUInfo[0] and the CPU identification string in
+ // the other three array elements. The CPU identification string is
+ // not in linear order. The code below arranges the information
+ // in a human readable form.
+ __cpuid(CPUInfo, 0);
+ nIds = CPUInfo[0];
+ memset(CPUString, 0, sizeof(CPUString));
+ *((int*)CPUString) = CPUInfo[1];
+ *((int*)(CPUString+4)) = CPUInfo[3];
+ *((int*)(CPUString+8)) = CPUInfo[2];
+
+ // Get the information associated with each valid Id
+ for (i=0; i <= nIds; ++i) {
+ __cpuid(CPUInfo, i);
+
+ // Interpret CPU feature information.
+ if (i == 1) {
+ nSteppingID = CPUInfo[0] & 0xf;
+ nModel = (CPUInfo[0] >> 4) & 0xf;
+ nFamily = (CPUInfo[0] >> 8) & 0xf;
+ nProcessorType = (CPUInfo[0] >> 12) & 0x3;
+ nExtendedmodel = (CPUInfo[0] >> 16) & 0xf;
+ nExtendedfamily = (CPUInfo[0] >> 20) & 0xff;
+ nBrandIndex = CPUInfo[1] & 0xff;
+ nCLFLUSHcachelinesize = ((CPUInfo[1] >> 8) & 0xff) * 8;
+ nLogicalProcessors = ((CPUInfo[1] >> 16) & 0xff);
+ nAPICPhysicalID = (CPUInfo[1] >> 24) & 0xff;
+ bSSE3Instructions = (CPUInfo[2] & 0x1) || false;
+ bMONITOR_MWAIT = (CPUInfo[2] & 0x8) || false;
+ bCPLQualifiedDebugStore = (CPUInfo[2] & 0x10) || false;
+ bVirtualMachineExtensions = (CPUInfo[2] & 0x20) || false;
+ bEnhancedIntelSpeedStepTechnology = (CPUInfo[2] & 0x80) || false;
+ bThermalMonitor2 = (CPUInfo[2] & 0x100) || false;
+ bSupplementalSSE3 = (CPUInfo[2] & 0x200) || false;
+ bL1ContextID = (CPUInfo[2] & 0x300) || false;
+ bCMPXCHG16B= (CPUInfo[2] & 0x2000) || false;
+ bxTPRUpdateControl = (CPUInfo[2] & 0x4000) || false;
+ bPerfDebugCapabilityMSR = (CPUInfo[2] & 0x8000) || false;
+ bSSE41Extensions = (CPUInfo[2] & 0x80000) || false;
+ bSSE42Extensions = (CPUInfo[2] & 0x100000) || false;
+ bPOPCNT= (CPUInfo[2] & 0x800000) || false;
+ nFeatureInfo = CPUInfo[3];
+ bMultithreading = (nFeatureInfo & (1 << 28)) || false;
+ }
+ }
+
+ // Calling __cpuid with 0x80000000 as the InfoType argument
+ // gets the number of valid extended IDs.
+ __cpuid(CPUInfo, 0x80000000);
+ nExIds = CPUInfo[0];
+ memset(CPUBrandString, 0, sizeof(CPUBrandString));
+
+ // Get the information associated with each extended ID.
+ for (i=0x80000000; i<=nExIds; ++i) {
+ __cpuid(CPUInfo, i);
+ if (i == 0x80000001) {
+ bLAHF_SAHFAvailable = (CPUInfo[2] & 0x1) || false;
+ bCmpLegacy = (CPUInfo[2] & 0x2) || false;
+ bSVM = (CPUInfo[2] & 0x4) || false;
+ bExtApicSpace = (CPUInfo[2] & 0x8) || false;
+ bAltMovCr8 = (CPUInfo[2] & 0x10) || false;
+ bLZCNT = (CPUInfo[2] & 0x20) || false;
+ bSSE4A = (CPUInfo[2] & 0x40) || false;
+ bMisalignedSSE = (CPUInfo[2] & 0x80) || false;
+ bPREFETCH = (CPUInfo[2] & 0x100) || false;
+ bSKINITandDEV = (CPUInfo[2] & 0x1000) || false;
+ bSYSCALL_SYSRETAvailable = (CPUInfo[3] & 0x800) || false;
+ bExecuteDisableBitAvailable = (CPUInfo[3] & 0x10000) || false;
+ bMMXExtensions = (CPUInfo[3] & 0x40000) || false;
+ bFFXSR = (CPUInfo[3] & 0x200000) || false;
+ b1GBSupport = (CPUInfo[3] & 0x400000) || false;
+ bRDTSCP = (CPUInfo[3] & 0x8000000) || false;
+ b64Available = (CPUInfo[3] & 0x20000000) || false;
+ b3DNowExt = (CPUInfo[3] & 0x40000000) || false;
+ b3DNow = (CPUInfo[3] & 0x80000000) || false;
+ }
+
+ // Interpret CPU brand string and cache information.
+ if (i == 0x80000002)
+ memcpy(CPUBrandString, CPUInfo, sizeof(CPUInfo));
+ else if (i == 0x80000003)
+ memcpy(CPUBrandString + 16, CPUInfo, sizeof(CPUInfo));
+ else if (i == 0x80000004)
+ memcpy(CPUBrandString + 32, CPUInfo, sizeof(CPUInfo));
+ else if (i == 0x80000006) {
+ nCacheLineSize = CPUInfo[2] & 0xff;
+ nL2Associativity = (CPUInfo[2] >> 12) & 0xf;
+ nCacheSizeK = (CPUInfo[2] >> 16) & 0xffff;
+ }
+ else if (i == 0x80000008) {
+ nPhysicalAddress = CPUInfo[0] & 0xff;
+ nVirtualAddress = (CPUInfo[0] >> 8) & 0xff;
+ }
+ else if (i == 0x8000000A) {
+ bNestedPaging = (CPUInfo[3] & 0x1) || false;
+ bLBRVisualization = (CPUInfo[3] & 0x2) || false;
+ }
+ else if (i == 0x8000001A) {
+ bFP128 = (CPUInfo[0] & 0x1) || false;
+ bMOVOptimization = (CPUInfo[0] & 0x2) || false;
+ }
+ }
+
+ if (psutil_add_to_dict(py_retdict, "vendor",
+ Py_BuildValue("s", CPUString)) == 1)
+ goto error;
+
+ if (nIds >= 1) {
+ /*
+ if (nSteppingID)
+ printf_s("Stepping ID = %d\n", nSteppingID);
+ if (nModel)
+ printf_s("Model = %d\n", nModel);
+ if (nFamily)
+ printf_s("Family = %d\n", nFamily);
+ if (nProcessorType)
+ printf_s("Processor Type = %d\n", nProcessorType);
+ if (nExtendedmodel)
+ printf_s("Extended model = %d\n", nExtendedmodel);
+ if (nExtendedfamily)
+ printf_s("Extended family = %d\n", nExtendedfamily);
+ if (nBrandIndex)
+ printf_s("Brand Index = %d\n", nBrandIndex);
+ if (nCLFLUSHcachelinesize)
+ printf_s("CLFLUSH cache line size = %d\n",
+ nCLFLUSHcachelinesize);
+ if (bMultithreading && (nLogicalProcessors > 0))
+ printf_s("Logical Processor Count = %d\n", nLogicalProcessors);
+ if (nAPICPhysicalID)
+ printf_s("APIC Physical ID = %d\n", nAPICPhysicalID);
+ */
+
+ if (nFeatureInfo || bSSE3Instructions ||
+ bMONITOR_MWAIT || bCPLQualifiedDebugStore ||
+ bVirtualMachineExtensions || bEnhancedIntelSpeedStepTechnology ||
+ bThermalMonitor2 || bSupplementalSSE3 || bL1ContextID ||
+ bCMPXCHG16B || bxTPRUpdateControl || bPerfDebugCapabilityMSR ||
+ bSSE41Extensions || bSSE42Extensions || bPOPCNT ||
+ bLAHF_SAHFAvailable || bCmpLegacy || bSVM ||
+ bExtApicSpace || bAltMovCr8 ||
+ bLZCNT || bSSE4A || bMisalignedSSE ||
+ bPREFETCH || bSKINITandDEV || bSYSCALL_SYSRETAvailable ||
+ bExecuteDisableBitAvailable || bMMXExtensions || bFFXSR ||
+ b1GBSupport || bRDTSCP || b64Available || b3DNowExt || b3DNow ||
+ bNestedPaging || bLBRVisualization || bFP128 || bMOVOptimization)
+ {
+
+ if (bSSE3Instructions) // SSE3
+ stradd(flags, _countof(flags), "sse3");
+ if (bMONITOR_MWAIT) // MONITOR/MWAIT
+ stradd(flags, _countof(flags), "monitor");
+ if (bCPLQualifiedDebugStore) // CPL Qualified Debug Store
+ stradd(flags, _countof(flags), "ds_cpl");
+ if (bVirtualMachineExtensions) // Virtual Machine Extensions???
+ stradd(flags, _countof(flags), "vmext");
+ if (bEnhancedIntelSpeedStepTechnology) // Enhanced Intel SpeedStep Technology
+ stradd(flags, _countof(flags), "est");
+ if (bThermalMonitor2) // Thermal Monitor 2
+ stradd(flags, _countof(flags), "tm2");
+ if (bSupplementalSSE3) // Supplemental Streaming SIMD Extensions 3?
+ stradd(flags, _countof(flags), "supplsse3");
+ if (bL1ContextID) // L1 Context ID ???
+ stradd(flags, _countof(flags), "l1ctxid");
+ if (bCMPXCHG16B) // CMPXCHG16B Instruction
+ stradd(flags, _countof(flags), "cx16");
+ if (bxTPRUpdateControl) // xTPR Update Control
+ stradd(flags, _countof(flags), "xtpr");
+ if (bPerfDebugCapabilityMSR) // Perf\\Debug Capability MSR
+ stradd(flags, _countof(flags), "perfdebugmsr");
+ if (bSSE41Extensions) // SSE4.1 Extensions
+ stradd(flags, _countof(flags), "sse4_1");
+ if (bSSE42Extensions) // SSE4.2 Extensions
+ stradd(flags, _countof(flags), "sse4_2");
+ if (bPOPCNT) // PPOPCNT Instruction
+ stradd(flags, _countof(flags), "popcnt");
+
+ i = 0;
+ nIds = 1;
+ while (i < (sizeof(szFeatures) / sizeof(const char*))) {
+ if (nFeatureInfo & nIds) {
+ stradd(flags, _countof(flags), szFeatures[i]);
+ }
+ nIds <<= 1;
+ ++i;
+ }
+
+ if (bLAHF_SAHFAvailable) // LAHF/SAHF in 64-bit mode
+ stradd(flags, _countof(flags), "lhaf_lm");
+ if (bCmpLegacy) // Core multi-processing legacy mode
+ stradd(flags, _countof(flags), "cmplegacy");
+ if (bSVM) // Secure Virtual Machine
+ stradd(flags, _countof(flags), "svm");
+ if (bExtApicSpace) // Extended APIC Register Space
+ stradd(flags, _countof(flags), "x2apic");
+ if (bAltMovCr8) // AltMovCr8 ???
+ stradd(flags, _countof(flags), "altmovcr8");
+ if (bLZCNT) // LZCNT instruction
+ stradd(flags, _countof(flags), "lzcnt");
+ if (bSSE4A) // SSE4A
+ stradd(flags, _countof(flags), "sse4a)");
+ if (bMisalignedSSE) // Misaligned SSE mode
+ stradd(flags, _countof(flags), "misalignsse");
+ if (bPREFETCH) // PREFETCH and PREFETCHW Instructions
+ stradd(flags, _countof(flags), "3dnowprefetch");
+ if (bSKINITandDEV) // SKINIT and DEV support
+ stradd(flags, _countof(flags), "skinit");
+ if (bSYSCALL_SYSRETAvailable) // SYSCALL/SYSRET in 64-bit mode
+ stradd(flags, _countof(flags), "syscall");
+ if (bExecuteDisableBitAvailable) // Execute Disable Bit
+ stradd(flags, _countof(flags), "nx");
+ if (bMMXExtensions) // Extensions to MMX Instructions
+ stradd(flags, _countof(flags), "mmxext");
+ if (bFFXSR) // FFXSR
+ stradd(flags, _countof(flags), "ffxsr");
+ if (b1GBSupport) // 1GB page support
+ stradd(flags, _countof(flags), "pdpe1gb");
+ if (bRDTSCP) // RDTSCP instruction
+ stradd(flags, _countof(flags), "rdtscp");
+ if (b64Available) // 64 bit Technology
+ stradd(flags, _countof(flags), "lm");
+ if (b3DNowExt) // 3Dnow Ext
+ stradd(flags, _countof(flags), "3dnowext");
+ if (b3DNow) // 3Dnow! instructions
+ stradd(flags, _countof(flags), "3dnow");
+ if (bNestedPaging) // Nested Paging
+ stradd(flags, _countof(flags), "npt");
+ if (bLBRVisualization) // LBR Visualization
+ stradd(flags, _countof(flags), "lbrv");
+ if (bFP128) // FP128 optimization???
+ stradd(flags, _countof(flags), "fp128");
+ if (bMOVOptimization) // MOVU Optimization???
+ stradd(flags, _countof(flags), "movu");
+ }
+ }
+
+ if (nExIds >= 0x80000004) {
+ if (psutil_add_to_dict(py_retdict, "model",
+ Py_BuildValue("s", CPUBrandString)) == 1)
+ goto error;
+
+ }
+ /*
+ if (nExIds >= 0x80000006) {
+ printf_s("Cache Line Size = %d\n", nCacheLineSize);
+ printf_s("L2 Associativity = %d\n", nL2Associativity);
+ printf_s("Cache Size = %dK\n", nCacheSizeK);
+ }
+
+ while (1) {
+ __cpuidex(CPUInfo, 0x4, i);
+ if (!(CPUInfo[0] & 0xf0))
+ break;
+
+ if (i == 0) {
+ nCores = CPUInfo[0] >> 26;
+ // printf_s("\n\nNumber of Cores = %d\n", nCores + 1);
+ }
+
+ nCacheType = (CPUInfo[0] & 0x1f);
+ nCacheLevel = (CPUInfo[0] & 0xe0) >> 5;
+ bSelfInit = (CPUInfo[0] & 0x100) >> 8;
+ bFullyAssociative = (CPUInfo[0] & 0x200) >> 9;
+ nMaxThread = (CPUInfo[0] & 0x03ffc000) >> 14;
+ nSysLineSize = (CPUInfo[1] & 0x0fff);
+ nPhysicalLinePartitions = (CPUInfo[1] & 0x03ff000) >> 12;
+ nWaysAssociativity = (CPUInfo[1]) >> 22;
+ nNumberSets = CPUInfo[2];
+
+ printf_s("\n");
+
+ printf_s("ECX Index %d\n", i);
+ switch (nCacheType) {
+ case 0:
+ printf_s(" Type: Null\n");
+ break;
+ case 1:
+ printf_s(" Type: Data Cache\n");
+ break;
+ case 2:
+ printf_s(" Type: Instruction Cache\n");
+ break;
+ case 3:
+ printf_s(" Type: Unified Cache\n");
+ break;
+ default:
+ printf_s(" Type: Unknown\n");
+ }
+
+ printf_s(" Level = %d\n", nCacheLevel + 1);
+ if (bSelfInit) {
+ printf_s(" Self Initializing\n");
+ }
+ else {
+ printf_s(" Not Self Initializing\n");
+ }
+ if (bFullyAssociative) {
+ printf_s(" Is Fully Associatve\n");
+ }
+ else {
+ printf_s(" Is Not Fully Associatve\n");
+ }
+ printf_s(" Max Threads = %d\n",
+ nMaxThread+1);
+ printf_s(" System Line Size = %d\n",
+ nSysLineSize+1);
+ printf_s(" Physical Line Partions = %d\n",
+ nPhysicalLinePartitions+1);
+ printf_s(" Ways of Associativity = %d\n",
+ nWaysAssociativity+1);
+ printf_s(" Number of Sets = %d\n",
+ nNumberSets+1);
+ i = i + 1;
+ }
+ */
+
+ if (psutil_add_to_dict(py_retdict, "flags",
+ Py_BuildValue("s", flags)) == 1)
+ goto error;
+
+ return py_retdict;
+
+error:
+ Py_DECREF(py_retdict);
+ return NULL;
+}
diff --git a/psutil/arch/windows/cpu.h b/psutil/arch/windows/cpu.h
index 1ef3ff1f..029f057b 100644
--- a/psutil/arch/windows/cpu.h
+++ b/psutil/arch/windows/cpu.h
@@ -7,8 +7,10 @@
#include <Python.h>
PyObject *psutil_cpu_count_logical(PyObject *self, PyObject *args);
-PyObject *psutil_cpu_count_cores(PyObject *self, PyObject *args);
+PyObject *psutil_GetLogicalProcessorInformationEx(
+ PyObject *self, PyObject *args);
PyObject *psutil_cpu_freq(PyObject *self, PyObject *args);
PyObject *psutil_cpu_stats(PyObject *self, PyObject *args);
PyObject *psutil_cpu_times(PyObject *self, PyObject *args);
PyObject *psutil_per_cpu_times(PyObject *self, PyObject *args);
+PyObject *psutil_cpu_info(PyObject *self, PyObject *args);
diff --git a/psutil/tests/__init__.py b/psutil/tests/__init__.py
index 21bb3e61..5d23bc74 100644
--- a/psutil/tests/__init__.py
+++ b/psutil/tests/__init__.py
@@ -86,7 +86,7 @@ __all__ = [
'CI_TESTING', 'VALID_PROC_STATUSES', 'TOLERANCE_DISK_USAGE', 'IS_64BIT',
"HAS_CPU_AFFINITY", "HAS_CPU_FREQ", "HAS_ENVIRON", "HAS_PROC_IO_COUNTERS",
"HAS_IONICE", "HAS_MEMORY_MAPS", "HAS_PROC_CPU_NUM", "HAS_RLIMIT",
- "HAS_SENSORS_BATTERY", "HAS_BATTERY", "HAS_SENSORS_FANS",
+ "HAS_SENSORS_BATTERY", "HAS_BATTERY", "HAS_SENSORS_FANS", "HAS_CPU_FREQ",
"HAS_SENSORS_TEMPERATURES", "HAS_MEMORY_FULL_INFO",
# subprocesses
'pyrun', 'terminate', 'reap_children', 'spawn_testproc', 'spawn_zombie',
@@ -176,6 +176,7 @@ HERE = os.path.realpath(os.path.dirname(__file__))
HAS_CONNECTIONS_UNIX = POSIX and not SUNOS
HAS_CPU_AFFINITY = hasattr(psutil.Process, "cpu_affinity")
HAS_CPU_FREQ = hasattr(psutil, "cpu_freq")
+HAS_CPU_INFO = hasattr(psutil, "cpu_info")
HAS_GETLOADAVG = hasattr(psutil, "getloadavg")
HAS_ENVIRON = hasattr(psutil.Process, "environ")
HAS_IONICE = hasattr(psutil.Process, "ionice")
diff --git a/psutil/tests/test_memleaks.py b/psutil/tests/test_memleaks.py
index d5baffa5..40f3d7ef 100755
--- a/psutil/tests/test_memleaks.py
+++ b/psutil/tests/test_memleaks.py
@@ -32,6 +32,7 @@ from psutil._compat import ProcessLookupError
from psutil._compat import super
from psutil.tests import HAS_CPU_AFFINITY
from psutil.tests import HAS_CPU_FREQ
+from psutil.tests import HAS_CPU_INFO
from psutil.tests import HAS_ENVIRON
from psutil.tests import HAS_IONICE
from psutil.tests import HAS_MEMORY_MAPS
@@ -368,6 +369,10 @@ class TestModuleFunctionsLeaks(TestMemoryLeak):
def test_cpu_freq(self):
self.execute(psutil.cpu_freq)
+ @unittest.skipIf(not HAS_CPU_INFO, "not supported")
+ def test_cpu_info(self):
+ self.execute(psutil.cpu_info)
+
@unittest.skipIf(not WINDOWS, "WINDOWS only")
def test_getloadavg(self):
psutil.getloadavg()
diff --git a/psutil/tests/test_osx.py b/psutil/tests/test_osx.py
index 4f4b1c29..b5e9300c 100755
--- a/psutil/tests/test_osx.py
+++ b/psutil/tests/test_osx.py
@@ -137,7 +137,7 @@ class TestSystemAPIs(PsutilTestCase):
# --- cpu
def test_cpu_count_logical(self):
- num = sysctl("sysctl hw.logicalcpu")
+ num = sysctl("hw.logicalcpu")
self.assertEqual(num, psutil.cpu_count(logical=True))
def test_cpu_count_cores(self):
@@ -147,11 +147,11 @@ class TestSystemAPIs(PsutilTestCase):
def test_cpu_freq(self):
freq = psutil.cpu_freq()
self.assertEqual(
- freq.current * 1000 * 1000, sysctl("sysctl hw.cpufrequency"))
+ freq.current * 1000 * 1000, sysctl("hw.cpufrequency"))
self.assertEqual(
- freq.min * 1000 * 1000, sysctl("sysctl hw.cpufrequency_min"))
+ freq.min * 1000 * 1000, sysctl("hw.cpufrequency_min"))
self.assertEqual(
- freq.max * 1000 * 1000, sysctl("sysctl hw.cpufrequency_max"))
+ freq.max * 1000 * 1000, sysctl("hw.cpufrequency_max"))
# --- virtual mem
@@ -233,6 +233,53 @@ class TestSystemAPIs(PsutilTestCase):
self.assertEqual(psutil_result.percent, int(percent))
+@unittest.skipIf(not MACOS, "MACOS only")
+class TestCpuInfo(PsutilTestCase):
+
+ def test_model(self):
+ value = psutil.cpu_info()['model']
+ self.assertEqual(value, sysctl("machdep.cpu.brand_string"))
+
+ def test_vendor(self):
+ value = psutil.cpu_info()['vendor']
+ self.assertEqual(value, sysctl("machdep.cpu.vendor"))
+
+ def test_features(self):
+ value = psutil.cpu_info()['features']
+ sctl = "%s %s" % (
+ sysctl("machdep.cpu.features").lower(),
+ sysctl("machdep.cpu.extfeatures").lower())
+ self.assertEqual(value, sctl)
+
+ def test_cores_per_socket(self):
+ value = psutil.cpu_info()['cores_per_socket']
+ self.assertEqual(value, sysctl("machdep.cpu.cores_per_package"))
+
+ def test_threads_per_core(self):
+ value = psutil.cpu_info()['threads_per_core']
+ self.assertEqual(value, sysctl("machdep.cpu.thread_count"))
+
+ def test_sockets(self):
+ value = psutil.cpu_info()['sockets']
+ self.assertEqual(value, sysctl("hw.packages"))
+
+ def test_l1i_cache(self):
+ value = psutil.cpu_info()['l1i_cache']
+ self.assertEqual(value, sysctl("hw.l1icachesize"))
+
+ def test_l1d_cache(self):
+ value = psutil.cpu_info()['l1d_cache']
+ self.assertEqual(value, sysctl("hw.l1dcachesize"))
+
+ def test_l2_cache(self):
+ value = psutil.cpu_info()['l2_cache']
+ self.assertEqual(value, sysctl("hw.l2cachesize"))
+
+ def test_l3_cache(self):
+ value = psutil.cpu_info()['l3_cache']
+ self.assertEqual(value, sysctl("hw.l3cachesize"))
+
+
if __name__ == '__main__':
from psutil.tests.runner import run_from_name
run_from_name(__file__)
diff --git a/psutil/tests/test_system.py b/psutil/tests/test_system.py
index ed328d01..4478fe75 100755
--- a/psutil/tests/test_system.py
+++ b/psutil/tests/test_system.py
@@ -10,6 +10,7 @@ import contextlib
import datetime
import errno
import os
+import platform
import pprint
import shutil
import signal
@@ -37,6 +38,7 @@ from psutil.tests import GITHUB_ACTIONS
from psutil.tests import GLOBAL_TIMEOUT
from psutil.tests import HAS_BATTERY
from psutil.tests import HAS_CPU_FREQ
+from psutil.tests import HAS_CPU_INFO
from psutil.tests import HAS_GETLOADAVG
from psutil.tests import HAS_NET_IO_COUNTERS
from psutil.tests import HAS_SENSORS_BATTERY
@@ -532,6 +534,13 @@ class TestCpuAPIs(PsutilTestCase):
if LINUX:
self.assertEqual(len(ls), psutil.cpu_count())
+ @unittest.skipIf(not HAS_CPU_INFO, "not suported")
+ def test_cpu_info(self):
+ info = psutil.cpu_info()
+ with self.subTest(info):
+ self.assertEqual(info['arch'], platform.machine())
+ self.assertEqual(info['byteorder'], sys.byteorder)
+
@unittest.skipIf(not HAS_GETLOADAVG, "not supported")
def test_getloadavg(self):
loadavg = psutil.getloadavg()
diff --git a/psutil/tests/test_windows.py b/psutil/tests/test_windows.py
index 55b6bc7b..b39f5d31 100755
--- a/psutil/tests/test_windows.py
+++ b/psutil/tests/test_windows.py
@@ -104,6 +104,16 @@ class TestCpuAPIs(WindowsTestCase):
self.assertEqual(proc.CurrentClockSpeed, psutil.cpu_freq().current)
self.assertEqual(proc.MaxClockSpeed, psutil.cpu_freq().max)
+ def test_cpu_info_model(self):
+ w = wmi.WMI()
+ proc = w.Win32_Processor()[0]
+ self.assertEqual(psutil.cpu_info()['model'], proc.Name)
+
+ def test_cpu_info_vendor(self):
+ w = wmi.WMI()
+ proc = w.Win32_Processor()[0]
+ self.assertEqual(psutil.cpu_info()['vendor'], proc.Manufacturer)
+
class TestSystemAPIs(WindowsTestCase):
diff --git a/setup.py b/setup.py
index e0bb5d09..151ea147 100755
--- a/setup.py
+++ b/setup.py
@@ -192,8 +192,8 @@ elif MACOS:
'psutil._psutil_osx',
sources=sources + [
'psutil/_psutil_osx.c',
- 'psutil/arch/osx/process_info.c',
'psutil/arch/osx/cpu.c',
+ 'psutil/arch/osx/process_info.c',
],
define_macros=macros,
extra_link_args=[