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

Windows: get swap usage from the pagefile(s) #2161

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions psutil/_psutil_windows.c
Original file line number Diff line number Diff line change
Expand Up @@ -1596,6 +1596,7 @@ PsutilMethods[] = {
{"cpu_times", psutil_cpu_times, METH_VARARGS},
{"disk_io_counters", psutil_disk_io_counters, METH_VARARGS},
{"disk_partitions", psutil_disk_partitions, METH_VARARGS},
{"disk_swaps", psutil_disk_swaps, METH_VARARGS},
{"disk_usage", psutil_disk_usage, METH_VARARGS},
{"getloadavg", (PyCFunction)psutil_get_loadavg, METH_VARARGS},
{"getpagesize", psutil_getpagesize, METH_VARARGS},
Expand Down
19 changes: 7 additions & 12 deletions psutil/_pswindows.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,18 +241,13 @@ def virtual_memory():

def swap_memory():
"""Swap system memory as a (total, used, free, sin, sout) tuple."""
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
total = used = 0
pagefiles = cext.disk_swaps()
for pf in pagefiles:
path, total_size, total_in_use, peak_usage = pf
total += total_size
used += total_in_use
free = total - used
percent = usage_percent(used, total, round_=1)
return _common.sswap(total, used, free, percent, 0, 0)

Expand Down
83 changes: 83 additions & 0 deletions psutil/arch/windows/disk.c
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,89 @@ psutil_disk_partitions(PyObject *self, PyObject *args) {
}


/*
* Return information about the disk page files as a list.
* A page file is basically the same thing as a swap partition.
*/
PyObject *
psutil_disk_swaps(PyObject *self, PyObject *args) {
NTSTATUS status;
PVOID buffer = NULL;
ULONG bufferSize = 0x200;
PSYSTEM_PAGEFILE_INFORMATION pInfo;
PyObject *py_tuple = NULL;
PyObject *py_path = NULL;
PyObject *py_retlist = PyList_New(0);

if (! py_retlist)
return NULL;

// Enumerate page files.
buffer = MALLOC_ZERO(bufferSize);
while ((status = NtQuerySystemInformation(
SystemPageFileInformation,
buffer,
bufferSize,
NULL)) == STATUS_INFO_LENGTH_MISMATCH)
{
FREE(buffer);
bufferSize *= 2;
buffer = MALLOC_ZERO(bufferSize);
}

if (! NT_SUCCESS(status)) {
psutil_SetFromNTStatusErr(status, "NtQuerySystemInformation");
goto error;
}

// Traverse the resulting struct.
// A TotalSize of 0 is used to indicate that there are no pagefiles.
pInfo = (SYSTEM_PAGEFILE_INFORMATION *)buffer;
if (pInfo->TotalSize != 0) {
while (TRUE) {
// construct python list
py_path = PyUnicode_FromWideChar(
pInfo->PageFileName.Buffer,
wcslen(pInfo->PageFileName.Buffer));
if (! py_path)
goto error;

py_tuple = Py_BuildValue(
"Okkk",
py_path,
pInfo->TotalSize * PSUTIL_SYSTEM_INFO.dwPageSize,
pInfo->TotalInUse * PSUTIL_SYSTEM_INFO.dwPageSize,
pInfo->PeakUsage * PSUTIL_SYSTEM_INFO.dwPageSize
);
if (!py_tuple)
goto error;
if (PyList_Append(py_retlist, py_tuple))
goto error;
Py_CLEAR(py_tuple);
Py_CLEAR(py_path);

// end of list
if (pInfo->NextEntryOffset == 0)
break;
// set pointer to the next pInfo struct
pInfo = (SYSTEM_PAGEFILE_INFORMATION *) \
((BYTE *)pInfo + pInfo->NextEntryOffset);
}
}

FREE(buffer);
return py_retlist;

error:
if (buffer != NULL)
FREE(buffer);
Py_XDECREF(py_tuple);
Py_XDECREF(py_path);
Py_DECREF(py_retlist);
return NULL;
}


/*
Accept a filename's drive in native format like "\Device\HarddiskVolume1\"
and return the corresponding drive letter (e.g. "C:\\").
Expand Down
1 change: 1 addition & 0 deletions psutil/arch/windows/disk.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@

PyObject *psutil_disk_io_counters(PyObject *self, PyObject *args);
PyObject *psutil_disk_partitions(PyObject *self, PyObject *args);
PyObject *psutil_disk_swaps(PyObject *self, PyObject *args);
PyObject *psutil_disk_usage(PyObject *self, PyObject *args);
PyObject *psutil_QueryDosDevice(PyObject *self, PyObject *args);
11 changes: 11 additions & 0 deletions psutil/arch/windows/ntextapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ typedef LONG NTSTATUS;
#define ProcessWow64Information 26
#undef SystemProcessIdInformation
#define SystemProcessIdInformation 88
#undef SystemPageFileInformation
#define SystemPageFileInformation 18

// process suspend() / resume()
typedef enum _KTHREAD_STATE {
Expand Down Expand Up @@ -448,6 +450,15 @@ typedef struct _SYSTEM_PROCESS_ID_INFORMATION {
UNICODE_STRING ImageName;
} SYSTEM_PROCESS_ID_INFORMATION, *PSYSTEM_PROCESS_ID_INFORMATION;

// disk_swaps()
typedef struct _SYSTEM_PAGEFILE_INFORMATION {
ULONG NextEntryOffset;
ULONG TotalSize;
ULONG TotalInUse;
ULONG PeakUsage;
UNICODE_STRING PageFileName;
} SYSTEM_PAGEFILE_INFORMATION, *PSYSTEM_PAGEFILE_INFORMATION;

// ====================================================================
// PEB structs for cmdline(), cwd(), environ()
// ====================================================================
Expand Down
5 changes: 5 additions & 0 deletions psutil/tests/test_windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,11 @@ def test_free_phymem(self):
int(w.AvailableBytes), psutil.virtual_memory().free,
delta=TOLERANCE_SYS_MEM)

def test_percent_swapmem(self):
w = wmi.WMI().Win32_PerfRawData_PerfOS_PagingFile()[0]
percent = int(w.PercentUsage) * 100 / int(w.PercentUsage_Base)
self.assertEqual(int(percent), int(psutil.swap_memory().percent))

# @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