diff options
author | Giampaolo Rodola <g.rodola@gmail.com> | 2016-02-03 17:13:59 +0100 |
---|---|---|
committer | Giampaolo Rodola <g.rodola@gmail.com> | 2016-02-03 17:13:59 +0100 |
commit | 7f0e0939b56a730ef59c0d743f02019662f55f4d (patch) | |
tree | 677ab3a5f012fbe2a4d5b49de73c4508ee691755 | |
parent | ecf0fdc39382620b42168aff6cc5fcd88988bf5b (diff) | |
download | psutil-7f0e0939b56a730ef59c0d743f02019662f55f4d.tar.gz |
fix #744 aka #753: implement memory USS / PSS on Linux
-rw-r--r-- | HISTORY.rst | 1 | ||||
-rw-r--r-- | README.rst | 2 | ||||
-rw-r--r-- | docs/index.rst | 6 | ||||
-rw-r--r-- | psutil/_pslinux.py | 28 | ||||
-rw-r--r-- | test/_linux.py | 18 |
5 files changed, 48 insertions, 7 deletions
diff --git a/HISTORY.rst b/HISTORY.rst index 27b628b9..96af5586 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -6,6 +6,7 @@ Bug tracker at https://github.com/giampaolo/psutil/issues **Enhancements** - #732: Process.environ(). (patch by Frank Benkstein) +- #753: [Linux] Process memory USS and PSS. **Bug fixes** @@ -239,7 +239,7 @@ Process management >>> p.memory_info() pmem(rss=7471104, vms=68513792) >>> p.memory_info_ex() - extmem(rss=9662464, vms=49192960, shared=3612672, text=2564096, lib=0, data=5754880, dirty=0) + pextmem(rss=10915840, vms=67608576, shared=3313664, text=2310144, lib=0, data=7262208, dirty=0, uss=9830400, pss=1216512) >>> 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), diff --git a/docs/index.rst b/docs/index.rst index 1c564783..aa1bd36b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1006,9 +1006,9 @@ Process class +--------+---------+-------+---------+--------------------+ | dirty | | | | nonpaged_pool | +--------+---------+-------+---------+--------------------+ - | | | | | pagefile | + | uss | | | | pagefile | +--------+---------+-------+---------+--------------------+ - | | | | | peak_pagefile | + | pss | | | | peak_pagefile | +--------+---------+-------+---------+--------------------+ | | | | | private | +--------+---------+-------+---------+--------------------+ @@ -1022,6 +1022,8 @@ Process class >>> p.memory_info_ex() pextmem(rss=15491072, vms=84025344, shared=5206016, text=2555904, lib=0, data=9891840, dirty=0) + .. versionchanged:: 3.5.0 added `uss` and `pss` fields on Linux. + .. method:: memory_percent() Compare physical system memory to process resident memory (RSS) and diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index ee613564..04c95cf3 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -56,6 +56,7 @@ __extra__all__ = [ # --- constants +HAS_SMAPS = os.path.exists('/proc/%s/smaps' % os.getpid()) HAS_PRLIMIT = hasattr(cext, "linux_prlimit") # RLIMIT_* constants, not guaranteed to be present on all kernels @@ -217,7 +218,7 @@ svmem = namedtuple( 'svmem', ['total', 'available', 'percent', 'used', 'free', 'active', 'inactive', 'buffers', 'cached']) -pextmem = namedtuple('pextmem', 'rss vms shared text lib data dirty') +pextmem = namedtuple('pextmem', 'rss vms shared text lib data dirty uss pss') pmmap_grouped = namedtuple( 'pmmap_grouped', ['path', 'rss', 'size', 'pss', 'shared_clean', @@ -972,7 +973,9 @@ class Process(object): int(vms) * PAGESIZE) @wrap_exceptions - def memory_info_ex(self): + def memory_info_ex(self, + _private_re=re.compile(b"Private.*:\s+(\d+)"), + _shared_re=re.compile(b"Shared.*:\s+(\d+)")): # ============================================================ # | FIELD | DESCRIPTION | AKA | TOP | # ============================================================ @@ -983,13 +986,30 @@ class Process(object): # | lib | library (unused in Linux 2.6) | lrs | | # | data | data + stack | drs | DATA | # | dirty | dirty pages (unused in Linux 2.6) | dt | | + # | ----------------------------------------------------------- + # | uss | unique set size ("real memory") | | | + # | pss | proportional set size | | | # ============================================================ with open_binary("%s/%s/statm" % (self._procfs_path, self.pid)) as f: vms, rss, shared, text, lib, data, dirty = \ [int(x) * PAGESIZE for x in f.readline().split()[:7]] - return pextmem(rss, vms, shared, text, lib, data, dirty) + if HAS_SMAPS: + # Note: using two regexes is faster than reading the file + # line by line. + # XXX: on Python 3 the 2 regexes are 30% slower than on + # Python 2 though. Figure out why. + with open_binary("%s/%s/smaps" % (self._procfs_path, self.pid), + buffering=BIGGER_FILE_BUFFERING) as f: + smaps_data = f.read() + uss = sum(map(int, _private_re.findall(smaps_data))) * 1024 + pss = sum(map(int, _shared_re.findall(smaps_data))) * 1024 + else: + # usually means we're on kernel < 2.6.14 or CONFIG_MMU kernel + # configuration option is not enabled. + uss = pss = 0 + return pextmem(rss, vms, shared, text, lib, data, dirty, uss, pss) - if os.path.exists('/proc/%s/smaps' % os.getpid()): + if HAS_SMAPS: @wrap_exceptions def memory_maps(self): diff --git a/test/_linux.py b/test/_linux.py index d36afc53..637ad1f7 100644 --- a/test/_linux.py +++ b/test/_linux.py @@ -599,6 +599,24 @@ class LinuxSpecificTestCase(unittest.TestCase): self.assertEqual(psutil.Process().exe(), "/home/foo") self.assertEqual(psutil.Process().cwd(), "/home/foo") + def test_uss_pss_mem_against_mem_maps(self): + src = textwrap.dedent(""" + import time + with open("%s", "w") as f: + time.sleep(10) + """ % TESTFN) + sproc = pyrun(src) + self.addCleanup(reap_children) + call_until(lambda: os.listdir('.'), "'%s' not in ret" % TESTFN) + p = psutil.Process(sproc.pid) + time.sleep(.1) + memex = p.memory_info_ex() + maps = p.memory_maps(grouped=False) + self.assertEqual( + memex.uss, sum([x.private_dirty + x.private_clean for x in maps])) + self.assertEqual( + memex.pss, sum([x.shared_dirty + x.shared_clean for x in maps])) + def main(): test_suite = unittest.TestSuite() |