Index: tools/multi-process-rss.py |
diff --git a/tools/multi-process-rss.py b/tools/multi-process-rss.py |
new file mode 100755 |
index 0000000000000000000000000000000000000000..c0e861372cf112ae8fe3e117501e39dcec07176a |
--- /dev/null |
+++ b/tools/multi-process-rss.py |
@@ -0,0 +1,116 @@ |
+#!/usr/bin/env python |
+# Copyright (c) 2013 The Chromium Authors. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+ |
+# Counts a resident set size (RSS) of multiple processes without double-counts. |
+# If they share the same page frame, the page frame is counted only once. |
+# |
+# Usage: |
+# ./multi-process-rss.py <pid>|<pid>r [...] |
+# |
+# If <pid> has 'r' at the end, all descendants of the process are accounted. |
+# |
+# Example: |
+# ./multi-process-rss.py 12345 23456r |
+# |
+# The command line above counts the RSS of 1) process 12345, 2) process 23456 |
+# and 3) all descendant processes of process 23456. |
+ |
+ |
+import collections |
+import logging |
+import os |
+import psutil |
+import sys |
+ |
+ |
+if sys.platform.startswith('linux'): |
+ _TOOLS_PATH = os.path.dirname(os.path.abspath(__file__)) |
+ _TOOLS_LINUX_PATH = os.path.join(_TOOLS_PATH, 'linux') |
+ sys.path.append(_TOOLS_LINUX_PATH) |
+ import procfs # pylint: disable=F0401 |
+ |
+ |
+class _NullHandler(logging.Handler): |
+ def emit(self, record): |
+ pass |
+ |
+ |
+_LOGGER = logging.getLogger('multi-process-rss') |
+_LOGGER.addHandler(_NullHandler()) |
+ |
+ |
+def _recursive_get_children(pid): |
+ children = psutil.Process(pid).get_children() |
+ descendant = [] |
+ for child in children: |
+ descendant.append(child.pid) |
+ descendant.extend(_recursive_get_children(child.pid)) |
+ return descendant |
+ |
+ |
+def list_pids(argv): |
+ pids = [] |
+ for arg in argv[1:]: |
+ try: |
+ if arg.endswith('r'): |
+ recursive = True |
+ pid = int(arg[:-1]) |
+ else: |
+ recursive = False |
+ pid = int(arg) |
+ except ValueError: |
+ raise SyntaxError("%s is not an integer." % arg) |
+ else: |
+ pids.append(pid) |
+ if recursive: |
+ children = _recursive_get_children(pid) |
+ pids.extend(children) |
+ |
+ pids = sorted(set(pids), key=pids.index) # uniq: maybe slow, but simple. |
+ |
+ return pids |
+ |
+ |
+def count_pageframes(pids): |
+ pageframes = collections.defaultdict(int) |
+ pagemap_dct = {} |
+ for pid in pids: |
+ maps = procfs.ProcMaps.load(pid) |
+ pagemap_dct[pid] = procfs.ProcPagemap.load(pid, maps) |
+ |
+ for pid, pagemap in pagemap_dct.iteritems(): |
+ for vma in pagemap.vma_internals.itervalues(): |
+ for pageframe, number in vma.pageframes.iteritems(): |
+ pageframes[pageframe] += number |
+ |
+ return pageframes |
+ |
+ |
+def main(argv): |
+ logging_handler = logging.StreamHandler() |
+ logging_handler.setLevel(logging.WARNING) |
+ logging_handler.setFormatter(logging.Formatter( |
+ '%(asctime)s:%(name)s:%(levelname)s:%(message)s')) |
+ |
+ _LOGGER.setLevel(logging.WARNING) |
+ _LOGGER.addHandler(logging_handler) |
+ |
+ if sys.platform.startswith('linux'): |
+ logging.getLogger('procfs').setLevel(logging.WARNING) |
+ logging.getLogger('procfs').addHandler(logging_handler) |
+ pids = list_pids(argv) |
+ pageframes = count_pageframes(pids) |
+ else: |
+ _LOGGER.error('%s is not supported.' % sys.platform) |
+ return 1 |
+ |
+ # TODO(dmikurube): Classify this total RSS. |
+ print len(pageframes) * 4096 |
+ |
+ return 0 |
+ |
+ |
+if __name__ == '__main__': |
+ sys.exit(main(sys.argv)) |