| #!/usr/bin/env python |
| # Copyright 2013 The Chromium Authors |
| # 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. |
| |
| from __future__import print_function |
| |
| 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): |
| try: |
| children= psutil.Process(pid).get_children() |
| except psutil.error.NoSuchProcess: |
| return[] |
| descendant=[] |
| for childin children: |
| descendant.append(child.pid) |
| descendant.extend(_recursive_get_children(child.pid)) |
| return descendant |
| |
| |
| def list_pids(argv): |
| pids=[] |
| for argin argv[1:]: |
| try: |
| if arg.endswith('r'): |
| recursive=True |
| pid= int(arg[:-1]) |
| else: |
| recursive=False |
| pid= int(arg) |
| exceptValueError: |
| raiseSyntaxError("%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 pidin pids: |
| maps= procfs.ProcMaps.load(pid) |
| ifnot maps: |
| _LOGGER.warning('/proc/%d/maps not found.'% pid) |
| continue |
| pagemap= procfs.ProcPagemap.load(pid, maps) |
| ifnot pagemap: |
| _LOGGER.warning('/proc/%d/pagemap not found.'% pid) |
| continue |
| pagemap_dct[pid]= pagemap |
| |
| for pid, pagemapin pagemap_dct.iteritems(): |
| for vmain pagemap.vma_internals.itervalues(): |
| for pageframe, numberin vma.pageframes.iteritems(): |
| pageframes[pageframe]+= number |
| |
| return pageframes |
| |
| |
| def count_statm(pids): |
| resident=0 |
| shared=0 |
| private=0 |
| |
| for pidin pids: |
| statm= procfs.ProcStatm.load(pid) |
| ifnot statm: |
| _LOGGER.warning('/proc/%d/statm not found.'% pid) |
| continue |
| resident+= statm.resident |
| shared+= statm.share |
| private+=(statm.resident- statm.share) |
| |
| return(resident, shared, private) |
| |
| |
| 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) |
| return1 |
| |
| # TODO(dmikurube): Classify this total RSS. |
| print(len(pageframes)*4096) |
| |
| return0 |
| |
| |
| if __name__=='__main__': |
| sys.exit(main(sys.argv)) |