Skip to content

Commit

Permalink
#1291 / OSX: mark memory_maps() as deprecated and make it alwats rais…
Browse files Browse the repository at this point in the history
…e AccessDenied
  • Loading branch information
giampaolo committed Feb 27, 2019
1 parent e827bf4 commit b31f3bf
Show file tree
Hide file tree
Showing 11 changed files with 56 additions and 216 deletions.
5 changes: 5 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ XXXX-XX-XX
- 1439_: [NetBSD] Process.connections() may return incomplete results if using
oneshot()

**API changes**

- 1291_: [OSX] Process.memory_maps() is deprecated and will always raise
AccessDenied. It will be removed in psutil 6.0.0.

5.5.1
=====

Expand Down
67 changes: 33 additions & 34 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1675,48 +1675,47 @@ Process class
See `pmap.py <https://github.com/giampaolo/psutil/blob/master/scripts/pmap.py>`__
for an example application.

+---------------+--------------+---------+-----------+--------------+
| Linux | macOS | Windows | Solaris | FreeBSD |
+===============+==============+=========+===========+==============+
| rss | rss | rss | rss | rss |
+---------------+--------------+---------+-----------+--------------+
| size | private | | anonymous | private |
+---------------+--------------+---------+-----------+--------------+
| pss | swapped | | locked | ref_count |
+---------------+--------------+---------+-----------+--------------+
| shared_clean | dirtied | | | shadow_count |
+---------------+--------------+---------+-----------+--------------+
| shared_dirty | ref_count | | | |
+---------------+--------------+---------+-----------+--------------+
| private_clean | shadow_depth | | | |
+---------------+--------------+---------+-----------+--------------+
| private_dirty | | | | |
+---------------+--------------+---------+-----------+--------------+
| referenced | | | | |
+---------------+--------------+---------+-----------+--------------+
| anonymous | | | | |
+---------------+--------------+---------+-----------+--------------+
| swap | | | | |
+---------------+--------------+---------+-----------+--------------+
+---------------+---------+--------------+-----------+
| Linux | Windows | FreeBSD | Solaris |
+===============+=========+==============+===========+
| rss | rss | rss | rss |
+---------------+---------+--------------+-----------+
| size | | private | anonymous |
+---------------+---------+--------------+-----------+
| pss | | ref_count | locked |
+---------------+---------+--------------+-----------+
| shared_clean | | shadow_count | |
+---------------+---------+--------------+-----------+
| shared_dirty | | | |
+---------------+---------+--------------+-----------+
| private_clean | | | |
+---------------+---------+--------------+-----------+
| private_dirty | | | |
+---------------+---------+--------------+-----------+
| referenced | | | |
+---------------+---------+--------------+-----------+
| anonymous | | | |
+---------------+---------+--------------+-----------+
| swap | | | |
+---------------+---------+--------------+-----------+

>>> import psutil
>>> p = psutil.Process()
>>> p.memory_maps()
[pmmap_grouped(path='/lib/x8664-linux-gnu/libutil-2.15.so', rss=32768, size=2125824, pss=32768, shared_clean=0, shared_dirty=0, private_clean=20480, private_dirty=12288, referenced=32768, anonymous=12288, swap=0),
pmmap_grouped(path='/lib/x8664-linux-gnu/libc-2.15.so', rss=3821568, size=3842048, pss=3821568, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=3821568, referenced=3575808, anonymous=3821568, swap=0),
pmmap_grouped(path='/lib/x8664-linux-gnu/libcrypto.so.0.1', rss=34124, rss=32768, size=2134016, pss=15360, shared_clean=24576, shared_dirty=0, private_clean=0, private_dirty=8192, referenced=24576, anonymous=8192, swap=0),
pmmap_grouped(path='[heap]', rss=32768, size=139264, pss=32768, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=32768, referenced=32768, anonymous=32768, swap=0),
pmmap_grouped(path='[stack]', rss=2465792, size=2494464, pss=2465792, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=2465792, referenced=2277376, anonymous=2465792, swap=0),
...]
>>> p.memory_maps(grouped=False)
[pmmap_ext(addr='00400000-006ea000', perms='r-xp', path='/usr/bin/python2.7', rss=2293760, size=3055616, pss=1157120, shared_clean=2273280, shared_dirty=0, private_clean=20480, private_dirty=0, referenced=2293760, anonymous=0, swap=0),
pmmap_ext(addr='008e9000-008eb000', perms='r--p', path='/usr/bin/python2.7', rss=8192, size=8192, pss=6144, shared_clean=4096, shared_dirty=0, private_clean=0, private_dirty=4096, referenced=8192, anonymous=4096, swap=0),
pmmap_ext(addr='008eb000-00962000', perms='rw-p', path='/usr/bin/python2.7', rss=417792, size=487424, pss=317440, shared_clean=200704, shared_dirty=0, private_clean=16384, private_dirty=200704, referenced=417792, anonymous=200704, swap=0),
pmmap_ext(addr='00962000-00985000', perms='rw-p', path='[anon]', rss=139264, size=143360, pss=139264, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=139264, referenced=139264, anonymous=139264, swap=0),
pmmap_ext(addr='02829000-02ccf000', perms='rw-p', path='[heap]', rss=4743168, size=4874240, pss=4743168, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=4743168, referenced=4718592, anonymous=4743168, swap=0),
...]

Availability: All platforms except OpenBSD, NetBSD and AIX.
Availability: Linux, Windows, FreeBSD, SunOS

.. warning::
on macOS, starting from version 5.6.0, this function is deprecated and
will always raise :class:`psutil.AccessDenied`. It is scheduled for
removal in 6.0.0
(see `issue 1020 <https://github.com/giampaolo/psutil/issues/1291#issuecomment-467828376>`_).

.. versionchanged::
5.6.0 deprecated on macOS, always raise AccessDenied

.. method:: children(recursive=False)

Expand Down
1 change: 0 additions & 1 deletion psutil/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1218,7 +1218,6 @@ def memory_percent(self, memtype="rss"):
return (value / float(total_phymem)) * 100

if hasattr(_psplatform.Process, "memory_maps"):
# Available everywhere except OpenBSD and NetBSD.
def memory_maps(self, grouped=True):
"""Return process' mapped memory regions as a list of namedtuples
whose fields are variable depending on the platform.
Expand Down
2 changes: 1 addition & 1 deletion psutil/_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ def outer(fun):

@functools.wraps(fun)
def inner(self, *args, **kwargs):
warnings.warn(msg, category=FutureWarning, stacklevel=2)
warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
return getattr(self, replacement)(*args, **kwargs)
return inner
return outer
Expand Down
5 changes: 0 additions & 5 deletions psutil/_psaix.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,6 @@
scputimes = namedtuple('scputimes', ['user', 'system', 'idle', 'iowait'])
# psutil.virtual_memory()
svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free'])
# psutil.Process.memory_maps(grouped=True)
pmmap_grouped = namedtuple('pmmap_grouped', ['path', 'rss', 'anon', 'locked'])
# psutil.Process.memory_maps(grouped=False)
pmmap_ext = namedtuple(
'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields))


# =====================================================================
Expand Down
13 changes: 4 additions & 9 deletions psutil/_psosx.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import errno
import functools
import os
import warnings
from socket import AF_INET
from collections import namedtuple

Expand Down Expand Up @@ -107,13 +108,6 @@
pmem = namedtuple('pmem', ['rss', 'vms', 'pfaults', 'pageins'])
# psutil.Process.memory_full_info()
pfullmem = namedtuple('pfullmem', pmem._fields + ('uss', ))
# psutil.Process.memory_maps(grouped=True)
pmmap_grouped = namedtuple(
'pmmap_grouped',
'path rss private swapped dirtied ref_count shadow_depth')
# psutil.Process.memory_maps(grouped=False)
pmmap_ext = namedtuple(
'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields))


# =====================================================================
Expand Down Expand Up @@ -582,6 +576,7 @@ def threads(self):
retlist.append(ntuple)
return retlist

@wrap_exceptions
def memory_maps(self):
return cext.proc_memory_maps(self.pid)
msg = "memory_maps() on OSX is deprecated and will be removed in 6.0.0"
warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
raise AccessDenied(self.pid, msg=msg)
160 changes: 0 additions & 160 deletions psutil/_psutil_osx.c
Original file line number Diff line number Diff line change
Expand Up @@ -341,164 +341,6 @@ psutil_proc_environ(PyObject *self, PyObject *args) {
}


/*
* Return a list of tuples for every process memory maps.
* 'procstat' cmdline utility has been used as an example.
* This will fail for all processes except os.getpid(),
* even as root.
*/
static PyObject *
psutil_proc_memory_maps(PyObject *self, PyObject *args) {
char buf[PATH_MAX];
char addr_str[34];
char perms[8];
int pagesize = getpagesize();
int iteration = 0;
long pid;
kern_return_t kr;
mach_port_t task = MACH_PORT_NULL;
uint32_t depth = 1;
vm_address_t address = 0;
vm_size_t size = 0;
struct vm_region_submap_info_64 info;
mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64;
PyObject *py_tuple = NULL;
PyObject *py_path = NULL;
PyObject *py_list = PyList_New(0);

if (py_list == NULL)
return NULL;
if (! PyArg_ParseTuple(args, "l", &pid))
goto error;
if (psutil_task_for_pid(pid, &task) != 0)
goto error;

while (1) {
iteration += 1;
py_tuple = NULL;
kr = vm_region_recurse_64(task, &address, &size, &depth,
(vm_region_info_64_t)&info, &count);
if (kr != KERN_SUCCESS) {
if ((kr == KERN_INVALID_ADDRESS) && (iteration > 1)) {
break; // no more regions to read
}
PyErr_Format(
PyExc_RuntimeError,
"vm_region_recurse_64() failed: %s",
mach_error_string(kr));
goto error;
}

if (info.is_submap) {
depth++;
}
else {
// Free/Reset the char[]s to avoid weird paths
memset(buf, 0, sizeof(buf));
memset(addr_str, 0, sizeof(addr_str));
memset(perms, 0, sizeof(perms));

sprintf(addr_str,
"%016lx-%016lx",
(long unsigned int)address,
(long unsigned int)address + size);
sprintf(perms, "%c%c%c/%c%c%c",
(info.protection & VM_PROT_READ) ? 'r' : '-',
(info.protection & VM_PROT_WRITE) ? 'w' : '-',
(info.protection & VM_PROT_EXECUTE) ? 'x' : '-',
(info.max_protection & VM_PROT_READ) ? 'r' : '-',
(info.max_protection & VM_PROT_WRITE) ? 'w' : '-',
(info.max_protection & VM_PROT_EXECUTE) ? 'x' : '-');

// proc_regionfilename() is undocumented but from its source
// code we can determine that it sets errno on error and
// return length of path.
errno = 0;
proc_regionfilename((pid_t)pid, address, buf, sizeof(buf));
if (errno != 0) {
psutil_debug("proc_regionfilename() failed errno=%i", errno);
psutil_raise_for_pid(pid, "proc_regionfilename()");
goto error;
}

if (info.share_mode == SM_COW && info.ref_count == 1) {
// Treat single reference SM_COW as SM_PRIVATE
info.share_mode = SM_PRIVATE;
}

if (strlen(buf) == 0) {
switch (info.share_mode) {
// #ifdef SM_LARGE_PAGE
// case SM_LARGE_PAGE:
// Treat SM_LARGE_PAGE the same as SM_PRIVATE
// since they are not shareable and are wired.
// #endif
case SM_COW:
strcpy(buf, "[cow]");
break;
case SM_PRIVATE:
strcpy(buf, "[prv]");
break;
case SM_EMPTY:
strcpy(buf, "[nul]");
break;
case SM_SHARED:
case SM_TRUESHARED:
strcpy(buf, "[shm]");
break;
case SM_PRIVATE_ALIASED:
strcpy(buf, "[ali]");
break;
case SM_SHARED_ALIASED:
strcpy(buf, "[s/a]");
break;
default:
strcpy(buf, "[???]");
}
}

py_path = PyUnicode_DecodeFSDefault(buf);
if (! py_path)
goto error;
py_tuple = Py_BuildValue(
"ssOIIIIIH",
addr_str, // "start-end"address
perms, // "rwx" permissions
py_path, // path
info.pages_resident * pagesize, // rss
info.pages_shared_now_private * pagesize, // private
info.pages_swapped_out * pagesize, // swapped
info.pages_dirtied * pagesize, // dirtied
info.ref_count, // ref count
info.shadow_depth // shadow depth
);
if (!py_tuple)
goto error;
if (PyList_Append(py_list, py_tuple))
goto error;
Py_DECREF(py_tuple);
Py_DECREF(py_path);

// increment address for the next map/file
address += size;
}
}

if (task != MACH_PORT_NULL)
mach_port_deallocate(mach_task_self(), task);

return py_list;

error:
if (task != MACH_PORT_NULL)
mach_port_deallocate(mach_task_self(), task);
Py_XDECREF(py_tuple);
Py_XDECREF(py_path);
Py_DECREF(py_list);
return NULL;
}


/*
* Return the number of logical CPUs in the system.
* XXX this could be shared with BSD.
Expand Down Expand Up @@ -1931,8 +1773,6 @@ PsutilMethods[] = {
"Return the number of fds opened by process."},
{"proc_connections", psutil_proc_connections, METH_VARARGS,
"Get process TCP and UDP connections as a list of tuples"},
{"proc_memory_maps", psutil_proc_memory_maps, METH_VARARGS,
"Return a list of tuples for every process's memory map"},

// --- system-related functions

Expand Down
2 changes: 1 addition & 1 deletion psutil/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@
HAS_PROC_IO_COUNTERS = hasattr(psutil.Process, "io_counters")
HAS_IONICE = hasattr(psutil.Process, "ionice")
HAS_MEMORY_FULL_INFO = 'uss' in psutil.Process().memory_full_info()._fields
HAS_MEMORY_MAPS = hasattr(psutil.Process, "memory_maps")
HAS_MEMORY_MAPS = not MACOS and hasattr(psutil.Process, "memory_maps")
HAS_PROC_CPU_NUM = hasattr(psutil.Process, "cpu_num")
HAS_RLIMIT = hasattr(psutil.Process, "rlimit")
HAS_THREADS = hasattr(psutil.Process, "threads")
Expand Down
10 changes: 9 additions & 1 deletion psutil/tests/test_contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,18 @@ def test_memory_info_ex(self):
with warnings.catch_warnings(record=True) as ws:
psutil.Process().memory_info_ex()
w = ws[0]
self.assertIsInstance(w.category(), FutureWarning)
self.assertIsInstance(w.category(), DeprecationWarning)
self.assertIn("memory_info_ex() is deprecated", str(w.message))
self.assertIn("use memory_info() instead", str(w.message))

@unittest.skipIf(not MACOS, "deprecated on macOS")
def test_memory_maps_osx(self):
with warnings.catch_warnings(record=True) as ws:
with self.assertRaises(psutil.AccessDenied):
psutil.Process().memory_maps()
w = ws[0]
self.assertIsInstance(w.category(), DeprecationWarning)


# ===================================================================
# --- System API types
Expand Down
3 changes: 0 additions & 3 deletions psutil/tests/test_osx.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,6 @@ def test_threads(self):
self.assertRaises((psutil.ZombieProcess, psutil.AccessDenied),
self.p.threads)

def test_memory_maps(self):
self.assertRaises(psutil.ZombieProcess, self.p.memory_maps)


@unittest.skipIf(not MACOS, "MACOS only")
class TestSystemAPIs(unittest.TestCase):
Expand Down
4 changes: 3 additions & 1 deletion psutil/tests/test_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -1265,7 +1265,7 @@ def test_halfway_terminated_process(self):
p.send_signal(signal.CTRL_BREAK_EVENT)

excluded_names = ['pid', 'is_running', 'wait', 'create_time',
'oneshot']
'oneshot', 'memory_info_ex']
if LINUX and not HAS_RLIMIT:
excluded_names.append('rlimit')
for name in dir(p):
Expand All @@ -1291,6 +1291,8 @@ def test_halfway_terminated_process(self):
ret = meth([0])
elif name == 'send_signal':
ret = meth(signal.SIGTERM)
elif MACOS and name == 'memory_maps':
continue # XXX
else:
ret = meth()
except psutil.ZombieProcess:
Expand Down

0 comments on commit b31f3bf

Please sign in to comment.