Skip to content

Commit

Permalink
Merge 5a2551d into c67ab01
Browse files Browse the repository at this point in the history
  • Loading branch information
giampaolo committed Feb 14, 2020
2 parents c67ab01 + 5a2551d commit d95d3e5
Show file tree
Hide file tree
Showing 19 changed files with 338 additions and 11 deletions.
2 changes: 1 addition & 1 deletion Makefile
Expand Up @@ -17,7 +17,7 @@ DEPS = \
pyperf \
requests \
setuptools \
sphinx \
sphinx==2.3.1 \
twine \
unittest2 \
virtualenv \
Expand Down
17 changes: 17 additions & 0 deletions docs/index.rst
Expand Up @@ -493,6 +493,23 @@ Disks
.. versionchanged::
4.0.0 NetBSD no longer has *read_time* and *write_time* fields.

.. function:: disk_swaps()

Enumerate swap partitions and swap files as a list of namedtuples including:

- **path**: the path of the swap partition/file on disk
- **total**: total swap partition/file size
- **used**: used swap partition/file size
- **fstype** (Linux): either "partition" or "swapfile"
- **priority** (Linux): makes sense when multiple swap files are in use.
The lower the priority, the more likely the swap file is o be used.
- **peak** (Windows): the highest peak usage over time.

Availability: Linux, Windows, FreeBSD

.. versionadded:: 5.7.1


Network
-------

Expand Down
20 changes: 20 additions & 0 deletions psutil/__init__.py
Expand Up @@ -220,6 +220,7 @@
"net_io_counters", "net_connections", "net_if_addrs", # network
"net_if_stats",
"disk_io_counters", "disk_partitions", "disk_usage", # disk
# "disk_swaps"
# "sensors_temperatures", "sensors_battery", "sensors_fans" # sensors
"users", "boot_time", # others
]
Expand Down Expand Up @@ -2081,6 +2082,25 @@ def disk_io_counters(perdisk=False, nowrap=True):
disk_io_counters.cache_clear.__doc__ = "Clears nowrap argument cache"


# Linux
if hasattr(_psplatform, "disk_swaps"):

def disk_swaps():
"""Enumerate swap partitions and swap files as a list of namedtuples:
- path: the path of the swap partition/file on disk
- total: total swap partition/file size
- used: used swap partition/file size
- fstype (Linux): either "partition" or "swapfile"
- priority (Linux): makes sense when multiple swap files are in
use. The lower the priority, the more likely the swap file is
to be used.
"""
return _psplatform.disk_swaps()

__all__.append("disk_swaps")


# =====================================================================
# --- network related functions
# =====================================================================
Expand Down
2 changes: 2 additions & 0 deletions psutil/_common.py
Expand Up @@ -176,6 +176,8 @@ class BatteryTime(enum.IntEnum):
'read_time', 'write_time'])
# psutil.disk_partitions()
sdiskpart = namedtuple('sdiskpart', ['device', 'mountpoint', 'fstype', 'opts'])
# psutil.disk_swaps()
sdiskswap = namedtuple('sdiskswap', ['path', 'total', 'used'])
# psutil.net_io_counters()
snetio = namedtuple('snetio', ['bytes_sent', 'bytes_recv',
'packets_sent', 'packets_recv',
Expand Down
4 changes: 4 additions & 0 deletions psutil/_psbsd.py
Expand Up @@ -336,6 +336,10 @@ def disk_partitions(all=False):

disk_usage = _psposix.disk_usage
disk_io_counters = cext.disk_io_counters
if hasattr(cext, "disk_swaps"):
def disk_swaps():
"""Return disk page files information."""
return [_common.sdiskswap(*x) for x in cext.disk_swaps()]


# =====================================================================
Expand Down
34 changes: 34 additions & 0 deletions psutil/_pslinux.py
Expand Up @@ -182,6 +182,9 @@ class IOPriority(enum.IntEnum):
'read_time', 'write_time',
'read_merged_count', 'write_merged_count',
'busy_time'])
# psutil.disk_swaps()
sdiskswap = namedtuple(
'sdiskswap', _common.sdiskswap._fields + ('fstype', 'priority'))
# psutil.Process().open_files()
popenfile = namedtuple(
'popenfile', ['path', 'fd', 'position', 'mode', 'flags'])
Expand Down Expand Up @@ -1182,6 +1185,37 @@ def disk_partitions(all=False):
return retlist


def disk_swaps():
"""Return swap partitions (or swap files)."""
retlist = []
try:
f = open_text("%s/swaps" % get_procfs_path())
except FileNotFoundError:
return retlist
else:
with f:
lines = f.readlines()
lines.pop(0) # header
for line in lines:
# Format is confusing:
# Filename Type\tSize Used Priority
# /dev/nvme0n1p3 partition\t11718652 2724 -2
line = line.strip()
name_and_type, _, other_fields = line.partition('\t')
fstype = name_and_type.split()[-1]
# "/dev/nvme0n1p3 partition" -> "/dev/nvme0n1p3"
path = name_and_type.rstrip(fstype).strip()
# The priority column is useful when multiple swap
# files are in use. The lower the priority, the
# more likely the swap file is to be used.
total, used, priority = map(int, other_fields.split('\t'))
total *= 1024
used *= 1024
nt = sdiskswap(path, total, used, fstype, priority)
retlist.append(nt)
return retlist


# =====================================================================
# --- sensors
# =====================================================================
Expand Down
51 changes: 51 additions & 0 deletions psutil/_psutil_bsd.c
Expand Up @@ -691,6 +691,55 @@ psutil_disk_partitions(PyObject *self, PyObject *args) {
}


#define NSWAP 16

/*
* Enumerate swap locations.
*/
static PyObject *
psutil_disk_swaps(PyObject *self, PyObject *args) {
kvm_t *kd;
int n;
int i;
struct kvm_swap kswap[NSWAP];
int pagesize = getpagesize();
PyObject *py_tuple;
PyObject *py_retlist;

n = kvm_getswapinfo(
kd, kswap, sizeof kswap / sizeof kswap[0], SWIF_DEV_PREFIX);
if (n == -1) {
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}

// For some reason if we don't create the list here kvm_getswapinfo()
// returns 0.
py_retlist = PyList_New(0);
if (! py_retlist)
goto error;

for (i = 0; i < n; ++i) {
py_tuple = Py_BuildValue(
"sII",
kswap[i].ksw_devname,
kswap[i].ksw_total * pagesize,
kswap[i].ksw_used * pagesize
);
if (!py_tuple)
goto error;
if (PyList_Append(py_retlist, py_tuple))
goto error;
Py_CLEAR(py_tuple);
}

return py_retlist;

error:
return NULL;
}


/*
* Return a Python list of named tuples with overall network I/O information
*/
Expand Down Expand Up @@ -976,6 +1025,8 @@ static PyMethodDef mod_methods[] = {
{"disk_partitions", psutil_disk_partitions, METH_VARARGS,
"Return a list of tuples including device, mount point and "
"fs type for all partitions mounted on the system."},
{"disk_swaps", psutil_disk_swaps, METH_VARARGS,
"Enumerate swap partitions/files."},
{"net_io_counters", psutil_net_io_counters, METH_VARARGS,
"Return dict of tuples of networks I/O information."},
{"disk_io_counters", psutil_disk_io_counters, METH_VARARGS,
Expand Down
2 changes: 2 additions & 0 deletions psutil/_psutil_windows.c
Expand Up @@ -1604,6 +1604,8 @@ PsutilMethods[] = {
"Return a list of currently connected users."},
{"disk_partitions", psutil_disk_partitions, METH_VARARGS,
"Return disk partitions."},
{"disk_swaps", psutil_disk_swaps, METH_VARARGS,
"Return information about the disk page files as a list."},
{"net_connections", psutil_net_connections, METH_VARARGS,
"Return system-wide connections"},
{"net_if_addrs", psutil_net_if_addrs, METH_VARARGS,
Expand Down
17 changes: 17 additions & 0 deletions psutil/_pswindows.py
Expand Up @@ -165,6 +165,9 @@ class IOPriority(enum.IntEnum):
['user', 'system', 'idle', 'interrupt', 'dpc'])
# psutil.virtual_memory()
svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free'])
# psutil.disk_swaps()
sdiskswap = namedtuple(
'sdiskswap', _common.sdiskswap._fields + ('peak', ))
# psutil.Process.memory_info()
pmem = namedtuple(
'pmem', ['rss', 'vms',
Expand Down Expand Up @@ -196,6 +199,9 @@ def convert_dos_path(s):
into:
"C:\Windows\systemew\file.txt"
"""
# "\??\" refers to \GLOBAL??\. Just remove it.
if s.startswith("\\??\\"):
return s[4:]
rawdrive = '\\'.join(s.split('\\')[:3])
driveletter = cext.win32_QueryDosDevice(rawdrive)
remainder = s[len(rawdrive):]
Expand Down Expand Up @@ -274,6 +280,17 @@ def disk_partitions(all):
return [_common.sdiskpart(*x) for x in rawlist]


def disk_swaps():
"""Return disk page files information."""
ret = []
rawlist = cext.disk_swaps()
for dospath, total, used, peak in rawlist:
path = convert_dos_path(dospath)
nt = sdiskswap(path, total, used, peak)
ret.append(nt)
return ret


# =====================================================================
# --- CPU
# =====================================================================
Expand Down
83 changes: 83 additions & 0 deletions psutil/arch/windows/disk.c
Expand Up @@ -344,6 +344,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
Expand Up @@ -9,4 +9,5 @@
PyObject *psutil_disk_io_counters(PyObject *self, PyObject *args);
PyObject *psutil_disk_partitions(PyObject *self, PyObject *args);
PyObject *psutil_disk_usage(PyObject *self, PyObject *args);
PyObject *psutil_disk_swaps(PyObject *self, PyObject *args);
PyObject *psutil_win32_QueryDosDevice(PyObject *self, PyObject *args);
10 changes: 10 additions & 0 deletions psutil/arch/windows/ntextapi.h
Expand Up @@ -35,6 +35,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 @@ -370,6 +372,14 @@ typedef struct _SYSTEM_PROCESS_ID_INFORMATION {
UNICODE_STRING ImageName;
} SYSTEM_PROCESS_ID_INFORMATION, *PSYSTEM_PROCESS_ID_INFORMATION;

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
1 change: 1 addition & 0 deletions psutil/tests/__init__.py
Expand Up @@ -171,6 +171,7 @@
HAS_PROC_IO_COUNTERS = hasattr(psutil.Process, "io_counters")
HAS_RLIMIT = hasattr(psutil.Process, "rlimit")
HAS_SENSORS_BATTERY = hasattr(psutil, "sensors_battery")
HAS_DISK_SWAPS = hasattr(psutil, "disk_swaps")
try:
HAS_BATTERY = HAS_SENSORS_BATTERY and bool(psutil.sensors_battery())
except Exception:
Expand Down

0 comments on commit d95d3e5

Please sign in to comment.