2 # Copyright 2013 The Chromium Authors
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
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.
10 # ./multi-process-rss.py <pid>|<pid>r [...]
12 # If <pid> has 'r' at the end, all descendants of the process are accounted.
15 # ./multi-process-rss.py 12345 23456r
17 # The command line above counts the RSS of 1) process 12345, 2) process 23456
18 # and 3) all descendant processes of process 23456.
20 from __future__ import print_function
29 if sys.platform.startswith('linux'):
30 _TOOLS_PATH = os.path.dirname(os.path.abspath(__file__))
31 _TOOLS_LINUX_PATH = os.path.join(_TOOLS_PATH, 'linux')
32 sys.path.append(_TOOLS_LINUX_PATH)
33 import procfs # pylint: disable=F0401
36 class _NullHandler(logging.Handler):
37 def emit(self, record):
41 _LOGGER = logging.getLogger('multi-process-rss')
42 _LOGGER.addHandler(_NullHandler())
45 def _recursive_get_children(pid):
47 children = psutil.Process(pid).get_children()
48 except psutil.error.NoSuchProcess:
51 for child in children:
52 descendant.append(child.pid)
53 descendant.extend(_recursive_get_children(child.pid))
68 raise SyntaxError("%s is not an integer." % arg)
72 children = _recursive_get_children(pid)
75 pids = sorted(set(pids), key=pids.index) # uniq: maybe slow, but simple.
80 def count_pageframes(pids):
81 pageframes = collections.defaultdict(int)
84 maps = procfs.ProcMaps.load(pid)
86 _LOGGER.warning('/proc/%d/maps not found.' % pid)
88 pagemap = procfs.ProcPagemap.load(pid, maps)
90 _LOGGER.warning('/proc/%d/pagemap not found.' % pid)
92 pagemap_dct[pid] = pagemap
94 for pid, pagemap in pagemap_dct.iteritems():
95 for vma in pagemap.vma_internals.itervalues():
96 for pageframe, number in vma.pageframes.iteritems():
97 pageframes[pageframe] += number
102 def count_statm(pids):
108 statm = procfs.ProcStatm.load(pid)
110 _LOGGER.warning('/proc/%d/statm not found.' % pid)
112 resident += statm.resident
113 shared += statm.share
114 private += (statm.resident - statm.share)
116 return (resident, shared, private)
120 logging_handler = logging.StreamHandler()
121 logging_handler.setLevel(logging.WARNING)
122 logging_handler.setFormatter(logging.Formatter(
123 '%(asctime)s:%(name)s:%(levelname)s:%(message)s'))
125 _LOGGER.setLevel(logging.WARNING)
126 _LOGGER.addHandler(logging_handler)
128 if sys.platform.startswith('linux'):
129 logging.getLogger('procfs').setLevel(logging.WARNING)
130 logging.getLogger('procfs').addHandler(logging_handler)
131 pids = list_pids(argv)
132 pageframes = count_pageframes(pids)
134 _LOGGER.error('%s is not supported.' % sys.platform)
137 # TODO(dmikurube): Classify this total RSS.
138 print(len(pageframes) * 4096)
143 if __name__ == '__main__':
144 sys.exit(main(sys.argv))