summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiampaolo Rodola <g.rodola@gmail.com>2016-09-18 19:23:26 +0200
committerGitHub <noreply@github.com>2016-09-18 19:23:26 +0200
commit1e707020f116627d5e3d060164c32b56257a1938 (patch)
tree4a20be0e8050525eb308fc4068578c9084afd0c8
parent8fabdb6cbe70fb1d2d2ada8e72c2a2a3cce9aaff (diff)
parentddb4c76ec5fc02079cd6a9290c60d7b82a7ed0ed (diff)
downloadpsutil-887-linux-free-mem-standardization-2.tar.gz
Merge pull request #889 from giampaolo/revert-888-887-linux-free-mem-standardization887-linux-free-mem-standardization-2
Revert "887 linux free mem standardization"
-rw-r--r--HISTORY.rst5
-rw-r--r--docs/index.rst7
-rw-r--r--psutil/__init__.py2
-rw-r--r--psutil/_pslinux.py146
-rw-r--r--psutil/tests/test_linux.py60
5 files changed, 76 insertions, 144 deletions
diff --git a/HISTORY.rst b/HISTORY.rst
index 34a34165..b5775bfd 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -1,7 +1,7 @@
Bug tracker at https://github.com/giampaolo/psutil/issues
-4.4.0 - XXXX-XX-XX
+4.3.2 - XXXX-XX-XX
==================
**Bug fixes**
@@ -10,9 +10,6 @@ Bug tracker at https://github.com/giampaolo/psutil/issues
- #880: [Windows] Handle race condition inside psutil_net_connections.
- #885: ValueError is raised if a negative integer is passed to cpu_percent()
functions.
-- #887: [Linux] free, available and used fields are more precise and match
- "free" cmdline utility. It also takes into account LCX containers preventing
- "avail" to overflow "total".
4.3.1 - 2016-09-01
diff --git a/docs/index.rst b/docs/index.rst
index d64ab43a..fa83aefa 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -178,8 +178,8 @@ Memory
- **available**: the actual amount of available memory that can be given
instantly to processes that request more memory in bytes; this is
calculated by summing different memory values depending on the platform
- and it is supposed to be used to monitor actual memory usage in a cross
- platform fashion.
+ (e.g. ``(free + buffers + cached)`` on Linux) and it is supposed to be used
+ to monitor actual memory usage in a cross platform fashion.
- **percent**: the percentage usage calculated as
``(total - available) / total * 100``.
- **used**: memory used, calculated differently depending on the platform and
@@ -221,9 +221,6 @@ Memory
.. versionchanged:: 4.2.0 added *shared* metrics on Linux.
- .. versionchanged:: 4.4.0 on Linux, *free*, *available* and *used* fields
- are more precise and match "free" cmdline utility.
-
.. function:: swap_memory()
Return system swap memory statistics as a namedtuple including the following
diff --git a/psutil/__init__.py b/psutil/__init__.py
index 020a0bdd..0a6f3ec6 100644
--- a/psutil/__init__.py
+++ b/psutil/__init__.py
@@ -187,7 +187,7 @@ __all__ = [
]
__all__.extend(_psplatform.__extra__all__)
__author__ = "Giampaolo Rodola'"
-__version__ = "4.4.0"
+__version__ = "4.3.2"
version_info = tuple([int(num) for num in __version__.split('.')])
AF_LINK = _psplatform.AF_LINK
_TOTAL_PHYMEM = None
diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py
index 1a0b57bc..7f6e0405 100644
--- a/psutil/_pslinux.py
+++ b/psutil/_pslinux.py
@@ -289,109 +289,61 @@ except Exception:
def virtual_memory():
- """Report memory stats trying to match "free" and "vmstat -s" cmdline
- utility values as much as possible.
-
- This implementation uses procps-ng-3.3.12 as a reference (2016-09-18):
- https://gitlab.com/procps-ng/procps/blob/
- 24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c
-
- For reference, procps-ng-3.3.10 is the version available on Ubuntu
- 16.04.
- """
- missing_fields = []
- mems = {}
+ total, free, buffers, shared, _, _, unit_multiplier = cext.linux_sysinfo()
+ total *= unit_multiplier
+ free *= unit_multiplier
+ buffers *= unit_multiplier
+ # Note: this (on my Ubuntu 14.04, kernel 3.13 at least) may be 0.
+ # If so, it will be determined from /proc/meminfo.
+ shared *= unit_multiplier or None
+ if shared == 0:
+ shared = None
+
+ cached = active = inactive = None
with open_binary('%s/meminfo' % get_procfs_path()) as f:
for line in f:
- fields = line.split()
- mems[fields[0]] = int(fields[1]) * 1024
-
- # Note: these info are available also as cext.linux_sysinfo().
- total = mems[b'MemTotal:']
- free = mems[b'MemFree:']
- buffers = mems[b'Buffers:']
- cached = mems[b"Cached:"]
- # "free" cmdline utility sums cached + reclamaible (available
- # since kernel 2.6.19):
- # https://gitlab.com/procps-ng/procps/
- # blob/195565746136d09333ded280cf3ba93853e855b8/proc/sysinfo.c#L761
- # Older versions of procps added slab memory instead.
- # This got changed in:
- # https://gitlab.com/procps-ng/procps/commit/
- # 05d751c4f076a2f0118b914c5e51cfbb4762ad8e
- cached += mems.get(b"SReclaimable:", 0)
-
- try:
- shared = mems[b'Shmem:'] # since kernel 2.6.32
- except KeyError:
- try:
- shared = mems[b'MemShared:'] # kernels 2.4
- except KeyError:
- shared = 0
- missing_fields.append('shared')
-
- try:
- active = mems[b"Active:"]
- except KeyError:
+ if cached is None and line.startswith(b"Cached:"):
+ cached = int(line.split()[1]) * 1024
+ elif active is None and line.startswith(b"Active:"):
+ active = int(line.split()[1]) * 1024
+ elif inactive is None and line.startswith(b"Inactive:"):
+ inactive = int(line.split()[1]) * 1024
+ # From "man free":
+ # The shared memory column represents either the MemShared
+ # value (2.4 kernels) or the Shmem value (2.6+ kernels) taken
+ # from the /proc/meminfo file. The value is zero if none of
+ # the entries is exported by the kernel.
+ elif shared is None and \
+ line.startswith(b"MemShared:") or \
+ line.startswith(b"Shmem:"):
+ shared = int(line.split()[1]) * 1024
+
+ missing = []
+ if cached is None:
+ missing.append('cached')
+ cached = 0
+ if active is None:
+ missing.append('active')
active = 0
- missing_fields.append('active')
-
- try:
- inactive = mems[b"Inactive:"]
- except KeyError:
- # https://gitlab.com/procps-ng/procps/blob/
- # 195565746136d09333ded280cf3ba93853e855b8/proc/sysinfo.c#L758
- try:
- inactive = \
- mems[b"Inact_dirty:"] + \
- mems[b"Inact_clean:"] + \
- mems[b"Inact_laundry:"]
- except KeyError:
- inactive = 0
- missing_fields.append('inactive')
-
- # https://gitlab.com/procps-ng/procps/blob/
- # 24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c#L769
- used = total - free - cached - buffers
- if used < 0:
- # May be symptomatic of running within a LCX container where such
- # values will be dramatically distorted over those of the host.
- used = total - free
-
- # Note: starting from 4.4.0 we match "free" "available" column.
- # Before 4.4.0 we calculated it as:
- # >>> avail = free + buffers + cached
- # ...which matched htop.
- # free and htop available memory differs as per:
- # http://askubuntu.com/a/369589
- # http://unix.stackexchange.com/a/65852/168884
- try:
- avail = mems[b'MemAvailable:']
- except KeyError:
- # Column is not there; it's likely this is an older kernel.
- # In this case "free" won't show an "available" column.
- # Also, procps does some hacky things:
- # https://gitlab.com/procps-ng/procps/blob/
- # /24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c#L774
- # We won't. Like this we'll match "htop".
- avail = free + buffers + cached
- # If avail is greater than total or our calculation overflows,
- # that's symptomatic of running within a LCX container where such
- # values will be dramatically distorted over those of the host.
- # https://gitlab.com/procps-ng/procps/blob/
- # 24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c#L764
- if avail > total:
- avail = free
-
- percent = usage_percent((total - avail), total, _round=1)
-
- # Warn about missing metrics which are set to 0.
- if missing_fields:
+ if inactive is None:
+ missing.append('inactive')
+ inactive = 0
+ if shared is None:
+ missing.append('shared')
+ shared = 0
+ if missing:
msg = "%s memory stats couldn't be determined and %s set to 0" % (
- ", ".join(missing_fields),
- "was" if len(missing_fields) == 1 else "were")
+ ", ".join(missing),
+ "was" if len(missing) == 1 else "were")
warnings.warn(msg, RuntimeWarning)
+ # Note: this value matches "htop" perfectly.
+ avail = free + buffers + cached
+ # Note: this value matches "free", but not all the time, see:
+ # https://github.com/giampaolo/psutil/issues/685#issuecomment-202914057
+ used = total - free
+ # Note: this value matches "htop" perfectly.
+ percent = usage_percent((total - avail), total, _round=1)
return svmem(total, avail, percent, used, free,
active, inactive, buffers, cached, shared)
diff --git a/psutil/tests/test_linux.py b/psutil/tests/test_linux.py
index a6b5d3f3..9f7c25fb 100644
--- a/psutil/tests/test_linux.py
+++ b/psutil/tests/test_linux.py
@@ -117,9 +117,8 @@ def free_physmem():
if line.startswith('Mem'):
total, used, free, shared = \
[int(x) for x in line.split()[1:5]]
- nt = collections.namedtuple(
- 'free', 'total used free shared output')
- return nt(total, used, free, shared, out)
+ nt = collections.namedtuple('free', 'total used free shared')
+ return nt(total, used, free, shared)
raise ValueError(
"can't find 'Mem' in 'free' output:\n%s" % '\n'.join(lines))
@@ -133,11 +132,6 @@ def vmstat(stat):
raise ValueError("can't find %r in 'vmstat' output" % stat)
-def get_free_version_info():
- out = sh("free -V").strip()
- return tuple(map(int, out.split()[-1].split('.')))
-
-
# =====================================================================
# system virtual memory
# =====================================================================
@@ -154,20 +148,12 @@ class TestSystemVirtualMemory(unittest.TestCase):
psutil_value = psutil.virtual_memory().total
self.assertAlmostEqual(vmstat_value, psutil_value)
- # Older versions of procps used slab memory to calculate used memory.
- # This got changed in:
- # https://gitlab.com/procps-ng/procps/commit/
- # 05d751c4f076a2f0118b914c5e51cfbb4762ad8e
- @unittest.skipUnless(
- LINUX and get_free_version_info() >= (3, 3, 12), "old free version")
@retry_before_failing()
def test_used(self):
- free = free_physmem()
- free_value = free.used
+ free_value = free_physmem().used
psutil_value = psutil.virtual_memory().used
self.assertAlmostEqual(
- free_value, psutil_value, delta=MEMORY_TOLERANCE,
- msg='%s %s \n%s' % (free_value, psutil_value, free.output))
+ free_value, psutil_value, delta=MEMORY_TOLERANCE)
@retry_before_failing()
def test_free(self):
@@ -202,30 +188,31 @@ class TestSystemVirtualMemory(unittest.TestCase):
vmstat_value, psutil_value, delta=MEMORY_TOLERANCE)
@retry_before_failing()
+ @unittest.skipIf(TRAVIS, "fails on travis")
def test_shared(self):
- free = free_physmem()
- free_value = free.shared
+ free_value = free_physmem().shared
if free_value == 0:
raise unittest.SkipTest("free does not support 'shared' column")
psutil_value = psutil.virtual_memory().shared
self.assertAlmostEqual(
- free_value, psutil_value, delta=MEMORY_TOLERANCE,
- msg='%s %s \n%s' % (free_value, psutil_value, free.output))
+ free_value, psutil_value, delta=MEMORY_TOLERANCE)
- @retry_before_failing()
- def test_available(self):
- # "free" output format has changed at some point:
- # https://github.com/giampaolo/psutil/issues/538#issuecomment-147192098
- out = sh("free -b")
- lines = out.split('\n')
- if 'available' not in lines[0]:
- raise unittest.SkipTest("free does not support 'available' column")
- else:
- free_value = int(lines[1].split()[-1])
- psutil_value = psutil.virtual_memory().available
- self.assertAlmostEqual(
- free_value, psutil_value, delta=MEMORY_TOLERANCE,
- msg='%s %s \n%s' % (free_value, psutil_value, out))
+ # --- mocked tests
+
+ def test_warnings_mocked(self):
+ with mock.patch('psutil._pslinux.open', create=True) as m:
+ with warnings.catch_warnings(record=True) as ws:
+ warnings.simplefilter("always")
+ ret = psutil._pslinux.virtual_memory()
+ assert m.called
+ self.assertEqual(len(ws), 1)
+ w = ws[0]
+ self.assertTrue(w.filename.endswith('psutil/_pslinux.py'))
+ self.assertIn(
+ "memory stats couldn't be determined", str(w.message))
+ self.assertEqual(ret.cached, 0)
+ self.assertEqual(ret.active, 0)
+ self.assertEqual(ret.inactive, 0)
# =====================================================================
@@ -383,7 +370,6 @@ class TestSystemCPU(unittest.TestCase):
@unittest.skipUnless(LINUX, "not a Linux system")
class TestSystemCPUStats(unittest.TestCase):
- @unittest.skipIf(TRAVIS, "fails on Travis")
def test_ctx_switches(self):
vmstat_value = vmstat("context switches")
psutil_value = psutil.cpu_stats().ctx_switches