OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. |
| 5 |
| 6 # Counts a resident set size (RSS) of multiple processes without double-counts. |
| 7 # If they share the same page frame, the page frame is counted only once. |
| 8 # |
| 9 # Usage: |
| 10 # ./multi-process-rss.py <pid>|<pid>r [...] |
| 11 # |
| 12 # If <pid> has 'r' at the end, all descendants of the process are accounted. |
| 13 # |
| 14 # Example: |
| 15 # ./multi-process-rss.py 12345 23456r |
| 16 # |
| 17 # The command line above counts the RSS of 1) process 12345, 2) process 23456 |
| 18 # and 3) all descendant processes of process 23456. |
| 19 |
| 20 |
| 21 import collections |
| 22 import logging |
| 23 import os |
| 24 import psutil |
| 25 import sys |
| 26 |
| 27 |
| 28 if sys.platform.startswith('linux'): |
| 29 _TOOLS_PATH = os.path.dirname(os.path.abspath(__file__)) |
| 30 _TOOLS_LINUX_PATH = os.path.join(_TOOLS_PATH, 'linux') |
| 31 sys.path.append(_TOOLS_LINUX_PATH) |
| 32 import procfs # pylint: disable=F0401 |
| 33 |
| 34 |
| 35 class _NullHandler(logging.Handler): |
| 36 def emit(self, record): |
| 37 pass |
| 38 |
| 39 |
| 40 _LOGGER = logging.getLogger('multi-process-rss') |
| 41 _LOGGER.addHandler(_NullHandler()) |
| 42 |
| 43 |
| 44 def _recursive_get_children(pid): |
| 45 children = psutil.Process(pid).get_children() |
| 46 descendant = [] |
| 47 for child in children: |
| 48 descendant.append(child.pid) |
| 49 descendant.extend(_recursive_get_children(child.pid)) |
| 50 return descendant |
| 51 |
| 52 |
| 53 def list_pids(argv): |
| 54 pids = [] |
| 55 for arg in argv[1:]: |
| 56 try: |
| 57 if arg.endswith('r'): |
| 58 recursive = True |
| 59 pid = int(arg[:-1]) |
| 60 else: |
| 61 recursive = False |
| 62 pid = int(arg) |
| 63 except ValueError: |
| 64 raise SyntaxError("%s is not an integer." % arg) |
| 65 else: |
| 66 pids.append(pid) |
| 67 if recursive: |
| 68 children = _recursive_get_children(pid) |
| 69 pids.extend(children) |
| 70 |
| 71 pids = sorted(set(pids), key=pids.index) # uniq: maybe slow, but simple. |
| 72 |
| 73 return pids |
| 74 |
| 75 |
| 76 def count_pageframes(pids): |
| 77 pageframes = collections.defaultdict(int) |
| 78 pagemap_dct = {} |
| 79 for pid in pids: |
| 80 maps = procfs.ProcMaps.load(pid) |
| 81 pagemap_dct[pid] = procfs.ProcPagemap.load(pid, maps) |
| 82 |
| 83 for pid, pagemap in pagemap_dct.iteritems(): |
| 84 for vma in pagemap.vma_internals.itervalues(): |
| 85 for pageframe, number in vma.pageframes.iteritems(): |
| 86 pageframes[pageframe] += number |
| 87 |
| 88 return pageframes |
| 89 |
| 90 |
| 91 def main(argv): |
| 92 logging_handler = logging.StreamHandler() |
| 93 logging_handler.setLevel(logging.WARNING) |
| 94 logging_handler.setFormatter(logging.Formatter( |
| 95 '%(asctime)s:%(name)s:%(levelname)s:%(message)s')) |
| 96 |
| 97 _LOGGER.setLevel(logging.WARNING) |
| 98 _LOGGER.addHandler(logging_handler) |
| 99 |
| 100 if sys.platform.startswith('linux'): |
| 101 logging.getLogger('procfs').setLevel(logging.WARNING) |
| 102 logging.getLogger('procfs').addHandler(logging_handler) |
| 103 pids = list_pids(argv) |
| 104 pageframes = count_pageframes(pids) |
| 105 else: |
| 106 _LOGGER.error('%s is not supported.' % sys.platform) |
| 107 return 1 |
| 108 |
| 109 # TODO(dmikurube): Classify this total RSS. |
| 110 print len(pageframes) * 4096 |
| 111 |
| 112 return 0 |
| 113 |
| 114 |
| 115 if __name__ == '__main__': |
| 116 sys.exit(main(sys.argv)) |
OLD | NEW |