Skip to content

Commit

Permalink
fix #1015 / linux: have swap_memory() rely on /proc fs instead of sys…
Browse files Browse the repository at this point in the history
…info() syscall in order to be nice with Linux containers such as Docker and Heroku
  • Loading branch information
giampaolo committed Apr 22, 2017
1 parent a115de4 commit 9d24ec2
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 24 deletions.
2 changes: 1 addition & 1 deletion .ci/travis/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,4 @@ elif [[ $TRAVIS_PYTHON_VERSION == '3.3' ]] || [[ $PYVER == 'py33' ]]; then
pip install -U ipaddress
fi

pip install -U coverage coveralls flake8 pep8 setuptools
pip install --upgrade coverage coveralls flake8 pep8 setuptools
6 changes: 3 additions & 3 deletions .ci/travis/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ else
python psutil/tests/runner.py
fi

if [ "$PYVER" == "2.7" ] || [ "$PYVER" == "3.5" ]; then
# run mem leaks test
# Run memory leak tests and linter only on Linux and latest major Python
# versions.
if [ "$PYVER" == "2.7" ] || [ "$PYVER" == "3.6" ]; then
python psutil/tests/test_memory_leaks.py
# run linter (on Linux only)
if [[ "$(uname -s)" != 'Darwin' ]]; then
python -m flake8
fi
Expand Down
14 changes: 1 addition & 13 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,18 @@ matrix:
- python: 3.5
- python: 3.6
- "pypy"
# XXX - commented because OSX builds are deadly slow
# - language: generic
# os: osx
# env: PYVER=py26
- language: generic
os: osx
env: PYVER=py27
# XXX - commented because OSX builds are deadly slow
# - language: generic
# os: osx
# env: PYVER=py33
- language: generic
os: osx
env: PYVER=py34
# XXX - not supported yet
# - language: generic
# os: osx
# env: PYVER=py35
install:
- ./.ci/travis/install.sh
script:
- ./.ci/travis/run.sh
after_success:
# upload reports to coveralls.io
# upload test reports to coveralls.io
- |
if [ "$(uname -s)" != 'Darwin' ]; then
coveralls
Expand Down
6 changes: 6 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
5.2.3
=====

**Enhancements**

- 1015_: swap_memory() now relies on /proc/meminfo instead of sysinfo() syscall
so that it can be used in conjunction with PROCFS_PATH in order to retrieve
memory info about Linux containers such as Docker and Heroku.

**Bug fixes**

- 1014_: Linux can mask legitimate ENOENT exceptions as NoSuchProcess.
Expand Down
18 changes: 16 additions & 2 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,11 @@ Memory
>>> psutil.swap_memory()
sswap(total=2097147904L, used=886620160L, free=1210527744L, percent=42.3, sin=1050411008, sout=1906720768)

.. versionchanged:: 5.2.3 on Linux this function relies on /proc fs instead
of sysinfo() syscall so that it can be used in conjunction with
:const:`psutil.PROCFS_PATH` in order to retrieve memory info about
Linux containers such as Docker and Heroku.

Disks
-----

Expand Down Expand Up @@ -1979,9 +1984,18 @@ Constants
.. _const-procfs_path:
.. data:: PROCFS_PATH

The path of the /proc filesystem on Linux and Solaris (defaults to "/proc").
The path of the /proc filesystem on Linux and Solaris (defaults to
``"/proc"``).
You may want to re-set this constant right after importing psutil in case
your /proc filesystem is mounted elsewhere.
your /proc filesystem is mounted elsewhere or if you want to retrieve
information about Linux containers such as
`Docker <https://www.docker.io/>`__,
`Heroku <https://www.heroku.com/>`__ or
`LXC <https://linuxcontainers.org/>`__ (see
`here <https://fabiokung.com/2014/03/13/memory-inside-linux-containers/>`__
for more info).
It must be noted that this trick works only for APIs which rely on /proc
filesystem (e.g. `memory`_ APIs and most :class:`Process` class methods).

Availability: Linux, Solaris

Expand Down
20 changes: 17 additions & 3 deletions psutil/_pslinux.py
Original file line number Diff line number Diff line change
Expand Up @@ -489,9 +489,23 @@ def virtual_memory():

def swap_memory():
"""Return swap memory metrics."""
_, _, _, _, total, free, unit_multiplier = cext.linux_sysinfo()
total *= unit_multiplier
free *= unit_multiplier
mems = {}
with open_binary('%s/meminfo' % get_procfs_path()) as f:
for line in f:
fields = line.split()
mems[fields[0]] = int(fields[1]) * 1024
# We prefer /proc/meminfo over sysinfo() syscall so that
# psutil.PROCFS_PATH can be used in order to allow retrieval
# for linux containers, see:
# https://github.com/giampaolo/psutil/issues/1015
try:
total = mems['SwapTotal:']
free = mems['SwapFree:']
except KeyError:
_, _, _, _, total, free, unit_multiplier = cext.linux_sysinfo()
total *= unit_multiplier
free *= unit_multiplier

used = total - free
percent = usage_percent(used, total, _round=1)
# get pgin/pgouts
Expand Down
22 changes: 20 additions & 2 deletions psutil/tests/test_linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -421,8 +421,15 @@ def test_warnings_mocked(self):

def test_no_vmstat_mocked(self):
# see https://github.com/giampaolo/psutil/issues/722
with mock.patch('psutil._pslinux.open', create=True,
side_effect=IOError) as m:
def open_mock(name, *args, **kwargs):
if name == "/proc/vmstat":
raise IOError(errno.ENOENT, 'no such file or directory')
else:
return orig_open(name, *args, **kwargs)

orig_open = open
patch_point = 'builtins.open' if PY3 else '__builtin__.open'
with mock.patch(patch_point, create=True, side_effect=open_mock) as m:
with warnings.catch_warnings(record=True) as ws:
warnings.simplefilter("always")
ret = psutil.swap_memory()
Expand All @@ -437,6 +444,17 @@ def test_no_vmstat_mocked(self):
self.assertEqual(ret.sin, 0)
self.assertEqual(ret.sout, 0)

def test_against_sysinfo(self):
with mock.patch('psutil._pslinux.cext.linux_sysinfo') as m:
swap = psutil.swap_memory()
assert not m.called
import psutil._psutil_linux as cext
_, _, _, _, total, free, unit_multiplier = cext.linux_sysinfo()
total *= unit_multiplier
free *= unit_multiplier
self.assertEqual(swap.total, total)
self.assertEqual(swap.free, free)


# =====================================================================
# --- system CPU
Expand Down

0 comments on commit 9d24ec2

Please sign in to comment.