summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Widdis <widdis@gmail.com>2023-04-13 01:11:27 -0700
committerGitHub <noreply@github.com>2023-04-13 10:11:27 +0200
commit0dde184075f81a9b6e22caa7255396d21ae63769 (patch)
treed234e2a26bc6b77c576a0ddea6de42f61a96c4da
parent67d988aeb5701bca3cc84795ae3f249b5c843451 (diff)
downloadpsutil-0dde184075f81a9b6e22caa7255396d21ae63769.tar.gz
Get Windows percent swap usage from performance counters (#2160)
Signed-off-by: Daniel Widdis <widdis@gmail.com>
-rw-r--r--CREDITS2
-rw-r--r--HISTORY.rst2
-rw-r--r--psutil/_psutil_windows.c1
-rw-r--r--psutil/_pswindows.py16
-rw-r--r--psutil/arch/windows/mem.c51
-rw-r--r--psutil/arch/windows/mem.h1
-rwxr-xr-xpsutil/tests/test_windows.py21
7 files changed, 88 insertions, 6 deletions
diff --git a/CREDITS b/CREDITS
index f4146360..c845a8c6 100644
--- a/CREDITS
+++ b/CREDITS
@@ -804,7 +804,7 @@ I: 2150
N: Daniel Widdis
W: https://github.com/dbwiddis
-I: 2077
+I: 2077, 2160
N: Amir Rossert
W: https://github.com/arossert
diff --git a/HISTORY.rst b/HISTORY.rst
index e82605d8..8d22111a 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -47,6 +47,8 @@
``SPEED_UNKNOWN`` definition. (patch by Amir Rossert)
- 2010_, [macOS]: on MacOS, arm64 ``IFM_1000_TX`` and ``IFM_1000_T`` are the
same value, causing a build failure. (patch by Lawrence D'Anna)
+- 2160_, [Windows]: Get Windows percent swap usage from performance counters.
+ (patch by Daniel Widdis)
5.9.3
=====
diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c
index 2ed937ee..11176de7 100644
--- a/psutil/_psutil_windows.c
+++ b/psutil/_psutil_windows.c
@@ -1560,6 +1560,7 @@ PsutilMethods[] = {
{"disk_usage", psutil_disk_usage, METH_VARARGS},
{"getloadavg", (PyCFunction)psutil_get_loadavg, METH_VARARGS},
{"getpagesize", psutil_getpagesize, METH_VARARGS},
+ {"swap_percent", psutil_swap_percent, METH_VARARGS},
{"init_loadavg_counter", (PyCFunction)psutil_init_loadavg_counter, METH_VARARGS},
{"net_connections", psutil_net_connections, METH_VARARGS},
{"net_if_addrs", psutil_net_if_addrs, METH_VARARGS},
diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py
index 3802f3ed..4081aa17 100644
--- a/psutil/_pswindows.py
+++ b/psutil/_pswindows.py
@@ -244,16 +244,22 @@ def swap_memory():
mem = cext.virtual_mem()
total_phys = mem[0]
- free_phys = mem[1]
total_system = mem[2]
- free_system = mem[3]
# system memory (commit total/limit) is the sum of physical and swap
# thus physical memory values need to be substracted to get swap values
total = total_system - total_phys
- free = min(total, free_system - free_phys)
- used = total - free
- percent = usage_percent(used, total, round_=1)
+ # commit total is incremented immediately (decrementing free_system)
+ # while the corresponding free physical value is not decremented until
+ # pages are accessed, so we can't use free system memory for swap.
+ # instead, we calculate page file usage based on performance counter
+ if (total > 0):
+ percentswap = cext.swap_percent()
+ used = int(0.01 * percentswap * total)
+ else:
+ used = 0
+ free = total - used
+ percent = round(percentswap, 1)
return _common.sswap(total, used, free, percent, 0, 0)
diff --git a/psutil/arch/windows/mem.c b/psutil/arch/windows/mem.c
index 18b535e6..24dc15ad 100644
--- a/psutil/arch/windows/mem.c
+++ b/psutil/arch/windows/mem.c
@@ -7,6 +7,7 @@
#include <Python.h>
#include <windows.h>
#include <Psapi.h>
+#include <pdh.h>
#include "../../_psutil_common.h"
@@ -41,3 +42,53 @@ psutil_virtual_mem(PyObject *self, PyObject *args) {
totalSys,
availSys);
}
+
+
+// Return a float representing the percent usage of all paging files on
+// the system.
+PyObject *
+psutil_swap_percent(PyObject *self, PyObject *args) {
+ WCHAR *szCounterPath = L"\\Paging File(_Total)\\% Usage";
+ PDH_STATUS s;
+ HQUERY hQuery;
+ HCOUNTER hCounter;
+ PDH_FMT_COUNTERVALUE counterValue;
+ double percentUsage;
+
+ if ((PdhOpenQueryW(NULL, 0, &hQuery)) != ERROR_SUCCESS) {
+ PyErr_Format(PyExc_RuntimeError, "PdhOpenQueryW failed");
+ return NULL;
+ }
+
+ s = PdhAddEnglishCounterW(hQuery, szCounterPath, 0, &hCounter);
+ if (s != ERROR_SUCCESS) {
+ PdhCloseQuery(hQuery);
+ PyErr_Format(
+ PyExc_RuntimeError,
+ "PdhAddEnglishCounterW failed. Performance counters may be disabled."
+ );
+ return NULL;
+ }
+
+ s = PdhCollectQueryData(hQuery);
+ if (s != ERROR_SUCCESS) {
+ // If swap disabled this will fail.
+ psutil_debug("PdhCollectQueryData failed; assume swap percent is 0");
+ percentUsage = 0;
+ }
+ else {
+ s = PdhGetFormattedCounterValue(
+ (PDH_HCOUNTER)hCounter, PDH_FMT_DOUBLE, 0, &counterValue);
+ if (s != ERROR_SUCCESS) {
+ PdhCloseQuery(hQuery);
+ PyErr_Format(
+ PyExc_RuntimeError, "PdhGetFormattedCounterValue failed");
+ return NULL;
+ }
+ percentUsage = counterValue.doubleValue;
+ }
+
+ PdhRemoveCounter(hCounter);
+ PdhCloseQuery(hQuery);
+ return Py_BuildValue("d", percentUsage);
+}
diff --git a/psutil/arch/windows/mem.h b/psutil/arch/windows/mem.h
index a10781df..48d3dade 100644
--- a/psutil/arch/windows/mem.h
+++ b/psutil/arch/windows/mem.h
@@ -8,3 +8,4 @@
PyObject *psutil_getpagesize(PyObject *self, PyObject *args);
PyObject *psutil_virtual_mem(PyObject *self, PyObject *args);
+PyObject *psutil_swap_percent(PyObject *self, PyObject *args);
diff --git a/psutil/tests/test_windows.py b/psutil/tests/test_windows.py
index 9b163a18..a9f68933 100755
--- a/psutil/tests/test_windows.py
+++ b/psutil/tests/test_windows.py
@@ -162,6 +162,27 @@ class TestSystemAPIs(WindowsTestCase):
int(w.AvailableBytes), psutil.virtual_memory().free,
delta=TOLERANCE_SYS_MEM)
+ def test_total_swapmem(self):
+ w = wmi.WMI().Win32_PerfRawData_PerfOS_Memory()[0]
+ self.assertEqual(int(w.CommitLimit) - psutil.virtual_memory().total,
+ psutil.swap_memory().total)
+ if (psutil.swap_memory().total == 0):
+ self.assertEqual(0, psutil.swap_memory().free)
+ self.assertEqual(0, psutil.swap_memory().used)
+
+ def test_percent_swapmem(self):
+ if (psutil.swap_memory().total > 0):
+ w = wmi.WMI().Win32_PerfRawData_PerfOS_PagingFile(
+ Name="_Total")[0]
+ # calculate swap usage to percent
+ percentSwap = int(w.PercentUsage) * 100 / int(w.PercentUsage_Base)
+ # exact percent may change but should be reasonable
+ # assert within +/- 5% and between 0 and 100%
+ self.assertGreaterEqual(psutil.swap_memory().percent, 0)
+ self.assertAlmostEqual(psutil.swap_memory().percent, percentSwap,
+ delta=5)
+ self.assertLessEqual(psutil.swap_memory().percent, 100)
+
# @unittest.skipIf(wmi is None, "wmi module is not installed")
# def test__UPTIME(self):
# # _UPTIME constant is not public but it is used internally