New home page for developer.chrome.com
[chromium-blink-merge.git] / tools / multi_process_rss.py
blob100d0f759b1c4660de6904b16899aeab30bd00fd
1 #!/usr/bin/env python
2 # Copyright 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.
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.
9 # Usage:
10 # ./multi-process-rss.py <pid>|<pid>r [...]
12 # If <pid> has 'r' at the end, all descendants of the process are accounted.
14 # Example:
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.
21 import collections
22 import logging
23 import os
24 import psutil
25 import sys
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
35 class _NullHandler(logging.Handler):
36 def emit(self, record):
37 pass
40 _LOGGER = logging.getLogger('multi-process-rss')
41 _LOGGER.addHandler(_NullHandler())
44 def _recursive_get_children(pid):
45 try:
46 children = psutil.Process(pid).get_children()
47 except psutil.error.NoSuchProcess:
48 return []
49 descendant = []
50 for child in children:
51 descendant.append(child.pid)
52 descendant.extend(_recursive_get_children(child.pid))
53 return descendant
56 def list_pids(argv):
57 pids = []
58 for arg in argv[1:]:
59 try:
60 if arg.endswith('r'):
61 recursive = True
62 pid = int(arg[:-1])
63 else:
64 recursive = False
65 pid = int(arg)
66 except ValueError:
67 raise SyntaxError("%s is not an integer." % arg)
68 else:
69 pids.append(pid)
70 if recursive:
71 children = _recursive_get_children(pid)
72 pids.extend(children)
74 pids = sorted(set(pids), key=pids.index) # uniq: maybe slow, but simple.
76 return pids
79 def count_pageframes(pids):
80 pageframes = collections.defaultdict(int)
81 pagemap_dct = {}
82 for pid in pids:
83 maps = procfs.ProcMaps.load(pid)
84 if not maps:
85 _LOGGER.warning('/proc/%d/maps not found.' % pid)
86 continue
87 pagemap = procfs.ProcPagemap.load(pid, maps)
88 if not pagemap:
89 _LOGGER.warning('/proc/%d/pagemap not found.' % pid)
90 continue
91 pagemap_dct[pid] = pagemap
93 for pid, pagemap in pagemap_dct.iteritems():
94 for vma in pagemap.vma_internals.itervalues():
95 for pageframe, number in vma.pageframes.iteritems():
96 pageframes[pageframe] += number
98 return pageframes
101 def count_statm(pids):
102 resident = 0
103 shared = 0
104 private = 0
106 for pid in pids:
107 statm = procfs.ProcStatm.load(pid)
108 if not statm:
109 _LOGGER.warning('/proc/%d/statm not found.' % pid)
110 continue
111 resident += statm.resident
112 shared += statm.share
113 private += (statm.resident - statm.share)
115 return (resident, shared, private)
118 def main(argv):
119 logging_handler = logging.StreamHandler()
120 logging_handler.setLevel(logging.WARNING)
121 logging_handler.setFormatter(logging.Formatter(
122 '%(asctime)s:%(name)s:%(levelname)s:%(message)s'))
124 _LOGGER.setLevel(logging.WARNING)
125 _LOGGER.addHandler(logging_handler)
127 if sys.platform.startswith('linux'):
128 logging.getLogger('procfs').setLevel(logging.WARNING)
129 logging.getLogger('procfs').addHandler(logging_handler)
130 pids = list_pids(argv)
131 pageframes = count_pageframes(pids)
132 else:
133 _LOGGER.error('%s is not supported.' % sys.platform)
134 return 1
136 # TODO(dmikurube): Classify this total RSS.
137 print len(pageframes) * 4096
139 return 0
142 if __name__ == '__main__':
143 sys.exit(main(sys.argv))