Skip to content

Commit

Permalink
[macOS, UNIX] prefer _SC_PAGESIZE over (partially) deprecated getpage…
Browse files Browse the repository at this point in the history
…size() (#1891)

Add a reusable `psutil_getpagesize()` utility common to all UNIXes.

Related to #1885 (`getpagesize()` is deprecated on recent macOS, POSIX.1-2001 and possibly other UNIXes).

The problem emerged on macOS but `getpagesize()` is also used in FreeBSD, NetBSD, OpenBSD and AIX, so it makes sense to do this in one place only, similarly to Windows which also provide a `psutil_getpagesize()` utility.

Follow cPython's `mmapmodule.c` and `resourcemodule.c` lead and rely on `sysconf(_SC_PAGESIZE)` instead, but leave `getpagesize()` in place as last resort/attempt for systems where it's not deprecated and/or they still legitimately rely on it.

Also provide a python wrapper so we can test the return value of this C function against Python's stdlib modules.

Signed-off-by: Giampaolo Rodola <[email protected]>
  • Loading branch information
giampaolo authored Dec 17, 2020
1 parent c39fef4 commit 6bdde37
Show file tree
Hide file tree
Showing 17 changed files with 91 additions and 49 deletions.
2 changes: 1 addition & 1 deletion psutil/_psaix.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
HAS_NET_IO_COUNTERS = hasattr(cext, "net_io_counters")
HAS_PROC_IO_COUNTERS = hasattr(cext, "proc_io_counters")

PAGE_SIZE = os.sysconf('SC_PAGE_SIZE')
PAGE_SIZE = cext_posix.getpagesize()
AF_LINK = cext_posix.AF_LINK

PROC_STATUSES = {
Expand Down
5 changes: 1 addition & 4 deletions psutil/_psbsd.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,7 @@
cext.PSUTIL_CONN_NONE: _common.CONN_NONE,
}

if NETBSD:
PAGESIZE = os.sysconf("SC_PAGESIZE")
else:
PAGESIZE = os.sysconf("SC_PAGE_SIZE")
PAGESIZE = cext_posix.getpagesize()
AF_LINK = cext_posix.AF_LINK

HAS_PER_CPU_TIMES = hasattr(cext, "per_cpu_times")
Expand Down
2 changes: 1 addition & 1 deletion psutil/_pslinux.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@

# Number of clock ticks per second
CLOCK_TICKS = os.sysconf("SC_CLK_TCK")
PAGESIZE = os.sysconf("SC_PAGE_SIZE")
PAGESIZE = cext_posix.getpagesize()
BOOT_TIME = None # set later
# Used when reading "big" files, namely /proc/{pid}/smaps and /proc/net/*.
# On Python 2, using a buffer with open() for such files may result in a
Expand Down
2 changes: 1 addition & 1 deletion psutil/_psosx.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
# =====================================================================


PAGESIZE = os.sysconf("SC_PAGE_SIZE")
PAGESIZE = cext_posix.getpagesize()
AF_LINK = cext_posix.AF_LINK

TCP_STATUSES = {
Expand Down
2 changes: 1 addition & 1 deletion psutil/_pssunos.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
# =====================================================================


PAGE_SIZE = os.sysconf('SC_PAGE_SIZE')
PAGE_SIZE = cext_posix.getpagesize()
AF_LINK = cext_posix.AF_LINK
IS_64_BIT = sys.maxsize > 2**32

Expand Down
4 changes: 2 additions & 2 deletions psutil/_psutil_aix.c
Original file line number Diff line number Diff line change
Expand Up @@ -876,7 +876,7 @@ psutil_disk_io_counters(PyObject *self, PyObject *args) {
static PyObject *
psutil_virtual_mem(PyObject *self, PyObject *args) {
int rc;
int pagesize = getpagesize();
long pagesize = psutil_getpagesize();
perfstat_memory_total_t memory;

rc = perfstat_memory_total(
Expand All @@ -902,7 +902,7 @@ psutil_virtual_mem(PyObject *self, PyObject *args) {
static PyObject *
psutil_swap_mem(PyObject *self, PyObject *args) {
int rc;
int pagesize = getpagesize();
long pagesize = psutil_getpagesize();
perfstat_memory_total_t memory;

rc = perfstat_memory_total(
Expand Down
2 changes: 1 addition & 1 deletion psutil/_psutil_bsd.c
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) {
long memstack;
int oncpu;
kinfo_proc kp;
long pagesize = sysconf(_SC_PAGESIZE);
long pagesize = psutil_getpagesize();
char str[1000];
PyObject *py_name;
PyObject *py_ppid;
Expand Down
12 changes: 4 additions & 8 deletions psutil/_psutil_osx.c
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ psutil_proc_memory_uss(PyObject *self, PyObject *args) {
mach_vm_size_t size = 0;
mach_msg_type_number_t info_count = VM_REGION_TOP_INFO_COUNT;
kern_return_t kr;
vm_size_t page_size;
long pagesize = psutil_getpagesize();
mach_vm_address_t addr = MACH_VM_MIN_ADDRESS;
mach_port_t task = MACH_PORT_NULL;
vm_region_top_info_data_t info;
Expand Down Expand Up @@ -505,11 +505,7 @@ psutil_proc_memory_uss(PyObject *self, PyObject *args) {
}

mach_port_deallocate(mach_task_self(), task);

if (host_page_size(mach_host_self(), &page_size) != KERN_SUCCESS)
page_size = PAGE_SIZE;

return Py_BuildValue("K", private_pages * page_size);
return Py_BuildValue("K", private_pages * pagesize);
}


Expand All @@ -525,7 +521,7 @@ psutil_virtual_mem(PyObject *self, PyObject *args) {
uint64_t total;
size_t len = sizeof(total);
vm_statistics_data_t vm;
int pagesize = getpagesize();
long pagesize = psutil_getpagesize();
// physical mem
mib[0] = CTL_HW;
mib[1] = HW_MEMSIZE;
Expand Down Expand Up @@ -565,7 +561,7 @@ psutil_swap_mem(PyObject *self, PyObject *args) {
size_t size;
struct xsw_usage totals;
vm_statistics_data_t vmstat;
int pagesize = getpagesize();
long pagesize = psutil_getpagesize();

mib[0] = CTL_VM;
mib[1] = VM_SWAPUSAGE;
Expand Down
47 changes: 47 additions & 0 deletions psutil/_psutil_posix.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <unistd.h>

#ifdef PSUTIL_SUNOS10
#include "arch/solaris/v10/ifaddrs.h"
Expand Down Expand Up @@ -50,6 +51,38 @@

#include "_psutil_common.h"


// ====================================================================
// --- Utils
// ====================================================================


/*
* From "man getpagesize" on Linux, https://linux.die.net/man/2/getpagesize:
*
* > In SUSv2 the getpagesize() call is labeled LEGACY, and in POSIX.1-2001
* > it has been dropped.
* > Portable applications should employ sysconf(_SC_PAGESIZE) instead
* > of getpagesize().
* > Most systems allow the synonym _SC_PAGE_SIZE for _SC_PAGESIZE.
* > Whether getpagesize() is present as a Linux system call depends on the
* > architecture.
*/
long
psutil_getpagesize(void) {
#ifdef _SC_PAGESIZE
// recommended POSIX
return sysconf(_SC_PAGESIZE);
#elif _SC_PAGE_SIZE
// alias
return sysconf(_SC_PAGE_SIZE);
#else
// legacy
return (long) getpagesize();
#endif
}


/*
* Check if PID exists. Return values:
* 1: exists
Expand Down Expand Up @@ -123,6 +156,18 @@ psutil_raise_for_pid(long pid, char *syscall) {
}


// ====================================================================
// --- Python wrappers
// ====================================================================


// Exposed so we can test it against Python's stdlib.
static PyObject *
psutil_getpagesize_pywrapper(PyObject *self, PyObject *args) {
return Py_BuildValue("l", psutil_getpagesize());
}


/*
* Given a PID return process priority as a Python integer.
*/
Expand Down Expand Up @@ -629,6 +674,8 @@ static PyMethodDef mod_methods[] = {
"Retrieve NIC MTU"},
{"net_if_is_running", psutil_net_if_is_running, METH_VARARGS,
"Return True if the NIC is running."},
{"getpagesize", psutil_getpagesize_pywrapper, METH_VARARGS,
"Return memory page size."},
#if defined(PSUTIL_BSD) || defined(PSUTIL_OSX)
{"net_if_duplex_speed", psutil_net_if_duplex_speed, METH_VARARGS,
"Return NIC stats."},
Expand Down
1 change: 1 addition & 0 deletions psutil/_psutil_posix.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
* found in the LICENSE file.
*/

long psutil_getpagesize(void);
int psutil_pid_exists(pid_t pid);
void psutil_raise_for_pid(pid_t pid, char *msg);
10 changes: 2 additions & 8 deletions psutil/arch/freebsd/specific.c
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ psutil_virtual_mem(PyObject *self, PyObject *args) {
size_t size = sizeof(total);
struct vmtotal vm;
int mib[] = {CTL_VM, VM_METER};
long pagesize = getpagesize();
long pagesize = psutil_getpagesize();
#if __FreeBSD_version > 702101
long buffers;
#else
Expand Down Expand Up @@ -465,7 +465,7 @@ psutil_swap_mem(PyObject *self, PyObject *args) {
struct kvm_swap kvmsw[1];
unsigned int swapin, swapout, nodein, nodeout;
size_t size = sizeof(unsigned int);
int pagesize;
long pagesize = psutil_getpagesize();

kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open failed");
if (kd == NULL) {
Expand Down Expand Up @@ -499,12 +499,6 @@ psutil_swap_mem(PyObject *self, PyObject *args) {
"sysctlbyname('vm.stats.vm.v_vnodeout)'");
}

pagesize = getpagesize();
if (pagesize <= 0) {
PyErr_SetString(PyExc_ValueError, "invalid getpagesize()");
return NULL;
}

return Py_BuildValue(
"(KKKII)",
(unsigned long long)kvmsw[0].ksw_total * pagesize, // total
Expand Down
4 changes: 2 additions & 2 deletions psutil/arch/netbsd/specific.c
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ psutil_virtual_mem(PyObject *self, PyObject *args) {
size_t size;
struct uvmexp_sysctl uv;
int mib[] = {CTL_VM, VM_UVMEXP2};
long pagesize = getpagesize();
long pagesize = psutil_getpagesize();

size = sizeof(uv);
if (sysctl(mib, 2, &uv, &size, NULL, 0) < 0) {
Expand All @@ -472,6 +472,7 @@ psutil_swap_mem(PyObject *self, PyObject *args) {
uint64_t swap_total, swap_free;
struct swapent *swdev;
int nswap, i;
long pagesize = psutil_getpagesize();

nswap = swapctl(SWAP_NSWAP, 0, 0);
if (nswap == 0) {
Expand Down Expand Up @@ -505,7 +506,6 @@ psutil_swap_mem(PyObject *self, PyObject *args) {
size_t size = sizeof(total);
struct uvmexp_sysctl uv;
int mib[] = {CTL_VM, VM_UVMEXP2};
long pagesize = getpagesize();
size = sizeof(uv);
if (sysctl(mib, 2, &uv, &size, NULL, 0) < 0) {
PyErr_SetFromErrno(PyExc_OSError);
Expand Down
2 changes: 1 addition & 1 deletion psutil/arch/openbsd/specific.c
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ psutil_virtual_mem(PyObject *self, PyObject *args) {
struct uvmexp uvmexp;
struct bcachestats bcstats;
struct vmtotal vmdata;
long pagesize = getpagesize();
long pagesize = psutil_getpagesize();

size = sizeof(total_physmem);
if (sysctl(physmem_mib, 2, &total_physmem, &size, NULL, 0) < 0) {
Expand Down
11 changes: 6 additions & 5 deletions psutil/tests/test_bsd.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,13 @@


if BSD:
PAGESIZE = os.sysconf("SC_PAGE_SIZE")
if os.getuid() == 0: # muse requires root privileges
MUSE_AVAILABLE = which('muse')
else:
MUSE_AVAILABLE = False
from psutil._psutil_posix import getpagesize

PAGESIZE = getpagesize()
# muse requires root privileges
MUSE_AVAILABLE = True if os.getuid() == 0 and which('muse') else False
else:
PAGESIZE = None
MUSE_AVAILABLE = False


Expand Down
10 changes: 4 additions & 6 deletions psutil/tests/test_osx.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""MACOS specific tests."""
"""macOS specific tests."""

import os
import re
import time

Expand All @@ -23,9 +22,6 @@
from psutil.tests import unittest


PAGESIZE = os.sysconf("SC_PAGE_SIZE") if MACOS else None


def sysctl(cmdline):
"""Expects a sysctl command with an argument and parse the result
returning only the value of interest.
Expand All @@ -40,13 +36,15 @@ def sysctl(cmdline):

def vm_stat(field):
"""Wrapper around 'vm_stat' cmdline utility."""
from psutil._psutil_posix import getpagesize

out = sh('vm_stat')
for line in out.split('\n'):
if field in line:
break
else:
raise ValueError("line not found")
return int(re.search(r'\d+', line).group(0)) * PAGESIZE
return int(re.search(r'\d+', line).group(0)) * getpagesize()


# http://code.activestate.com/recipes/578019/
Expand Down
16 changes: 16 additions & 0 deletions psutil/tests/test_posix.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@
from psutil.tests import unittest
from psutil.tests import which

if POSIX:
import mmap
import resource

from psutil._psutil_posix import getpagesize


def ps(fmt, pid=None):
"""
Expand Down Expand Up @@ -404,6 +410,16 @@ def df(device):
self.assertAlmostEqual(usage.percent, percent, delta=1)


@unittest.skipIf(not POSIX, "POSIX only")
class TestMisc(PsutilTestCase):

def test_getpagesize(self):
pagesize = getpagesize()
self.assertGreater(pagesize, 0)
self.assertEqual(pagesize, resource.getpagesize())
self.assertEqual(pagesize, mmap.PAGESIZE)


if __name__ == '__main__':
from psutil.tests.runner import run_from_name
run_from_name(__file__)
8 changes: 0 additions & 8 deletions psutil/tests/test_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,14 +218,6 @@ def test_users(self):
else:
psutil.Process(user.pid)

@unittest.skipIf(not POSIX, 'POSIX only')
def test_PAGESIZE(self):
# pagesize is used internally to perform different calculations
# and it's determined by using SC_PAGE_SIZE; make sure
# getpagesize() returns the same value.
import resource
self.assertEqual(os.sysconf("SC_PAGE_SIZE"), resource.getpagesize())

def test_test(self):
# test for psutil.test() function
stdout = sys.stdout
Expand Down

0 comments on commit 6bdde37

Please sign in to comment.