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

Get Windows percent swap usage from performance counters #2160

Merged
merged 19 commits into from
Apr 13, 2023
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
2 changes: 1 addition & 1 deletion CREDITS
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,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
=====
Expand Down
1 change: 1 addition & 0 deletions psutil/_psutil_windows.c
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down
16 changes: 11 additions & 5 deletions psutil/_pswindows.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)


Expand Down
51 changes: 51 additions & 0 deletions psutil/arch/windows/mem.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <Python.h>
#include <windows.h>
#include <Psapi.h>
#include <pdh.h>

#include "../../_psutil_common.h"

Expand Down Expand Up @@ -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);
}
1 change: 1 addition & 0 deletions psutil/arch/windows/mem.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
21 changes: 21 additions & 0 deletions psutil/tests/test_windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,27 @@ def test_free_phymem(self):
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)
dbwiddis marked this conversation as resolved.
Show resolved Hide resolved

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)
dbwiddis marked this conversation as resolved.
Show resolved Hide resolved

# @unittest.skipIf(wmi is None, "wmi module is not installed")
# def test__UPTIME(self):
# # _UPTIME constant is not public but it is used internally
Expand Down