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.
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.
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
):
40 _LOGGER
= logging
.getLogger('multi-process-rss')
41 _LOGGER
.addHandler(_NullHandler())
44 def _recursive_get_children(pid
):
46 children
= psutil
.Process(pid
).get_children()
47 except psutil
.error
.NoSuchProcess
:
50 for child
in children
:
51 descendant
.append(child
.pid
)
52 descendant
.extend(_recursive_get_children(child
.pid
))
67 raise SyntaxError("%s is not an integer." % arg
)
71 children
= _recursive_get_children(pid
)
74 pids
= sorted(set(pids
), key
=pids
.index
) # uniq: maybe slow, but simple.
79 def count_pageframes(pids
):
80 pageframes
= collections
.defaultdict(int)
83 maps
= procfs
.ProcMaps
.load(pid
)
85 _LOGGER
.warning('/proc/%d/maps not found.' % pid
)
87 pagemap
= procfs
.ProcPagemap
.load(pid
, maps
)
89 _LOGGER
.warning('/proc/%d/pagemap not found.' % pid
)
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
101 def count_statm(pids
):
107 statm
= procfs
.ProcStatm
.load(pid
)
109 _LOGGER
.warning('/proc/%d/statm not found.' % pid
)
111 resident
+= statm
.resident
112 shared
+= statm
.share
113 private
+= (statm
.resident
- statm
.share
)
115 return (resident
, shared
, private
)
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
)
133 _LOGGER
.error('%s is not supported.' % sys
.platform
)
136 # TODO(dmikurube): Classify this total RSS.
137 print len(pageframes
) * 4096
142 if __name__
== '__main__':
143 sys
.exit(main(sys
.argv
))