Jaromir Capik e8bb572
#!/usr/bin/env python
Jaromir Capik e8bb572
Jaromir Capik e8bb572
# Try to determine how much RAM is currently being used per program.
Jaromir Capik e8bb572
# Note per _program_, not per process. So for example this script
Jaromir Capik e8bb572
# will report RAM used by all httpd process together. In detail it reports:
Jaromir Capik e8bb572
# sum(private RAM for program processes) + sum(Shared RAM for program processes)
Jaromir Capik e8bb572
# The shared RAM is problematic to calculate, and this script automatically
Jaromir Capik e8bb572
# selects the most accurate method available for your kernel.
Jaromir Capik e8bb572
Jaromir Capik e8bb572
# Licence: LGPLv2
Jaromir Capik e8bb572
# Author:  P@draigBrady.com
Jaromir Capik e8bb572
# Source:  http://www.pixelbeat.org/scripts/ps_mem.py
Jaromir Capik e8bb572
Jaromir Capik e8bb572
# V1.0      06 Jul 2005     Initial release
Jaromir Capik e8bb572
# V1.1      11 Aug 2006     root permission required for accuracy
Jaromir Capik e8bb572
# V1.2      08 Nov 2006     Add total to output
Jaromir Capik e8bb572
#                           Use KiB,MiB,... for units rather than K,M,...
Jaromir Capik e8bb572
# V1.3      22 Nov 2006     Ignore shared col from /proc/$pid/statm for
Jaromir Capik e8bb572
#                           2.6 kernels up to and including 2.6.9.
Jaromir Capik e8bb572
#                           There it represented the total file backed extent
Jaromir Capik e8bb572
# V1.4      23 Nov 2006     Remove total from output as it's meaningless
Jaromir Capik e8bb572
#                           (the shared values overlap with other programs).
Jaromir Capik e8bb572
#                           Display the shared column. This extra info is
Jaromir Capik e8bb572
#                           useful, especially as it overlaps between programs.
Jaromir Capik e8bb572
# V1.5      26 Mar 2007     Remove redundant recursion from human()
Jaromir Capik e8bb572
# V1.6      05 Jun 2007     Also report number of processes with a given name.
Jaromir Capik e8bb572
#                           Patch from riccardo.murri@gmail.com
Jaromir Capik e8bb572
# V1.7      20 Sep 2007     Use PSS from /proc/$pid/smaps if available, which
Jaromir Capik e8bb572
#                           fixes some over-estimation and allows totalling.
Jaromir Capik e8bb572
#                           Enumerate the PIDs directly rather than using ps,
Jaromir Capik e8bb572
#                           which fixes the possible race between reading
Jaromir Capik e8bb572
#                           RSS with ps, and shared memory with this program.
Jaromir Capik e8bb572
#                           Also we can show non truncated command names.
Jaromir Capik e8bb572
# V1.8      28 Sep 2007     More accurate matching for stats in /proc/$pid/smaps
Jaromir Capik e8bb572
#                           as otherwise could match libraries causing a crash.
Jaromir Capik e8bb572
#                           Patch from patrice.bouchand.fedora@gmail.com
Jaromir Capik e8bb572
# V1.9      20 Feb 2008     Fix invalid values reported when PSS is available.
Jaromir Capik e8bb572
#                           Reported by Andrey Borzenkov <arvidjaar@mail.ru>
481a167
# V3.6      16 Oct 2015
Jaromir Capik e8bb572
#   http://github.com/pixelb/scripts/commits/master/scripts/ps_mem.py
Jaromir Capik e8bb572
Jaromir Capik e8bb572
# Notes:
Jaromir Capik e8bb572
#
Jaromir Capik e8bb572
# All interpreted programs where the interpreter is started
Jaromir Capik e8bb572
# by the shell or with env, will be merged to the interpreter
Jaromir Capik e8bb572
# (as that's what's given to exec). For e.g. all python programs
Jaromir Capik e8bb572
# starting with "#!/usr/bin/env python" will be grouped under python.
Jaromir Capik e8bb572
# You can change this by using the full command line but that will
Jaromir Capik e8bb572
# have the undesirable affect of splitting up programs started with
Jaromir Capik e8bb572
# differing parameters (for e.g. mingetty tty[1-6]).
Jaromir Capik e8bb572
#
Jaromir Capik e8bb572
# For 2.6 kernels up to and including 2.6.13 and later 2.4 redhat kernels
Jaromir Capik e8bb572
# (rmap vm without smaps) it can not be accurately determined how many pages
Jaromir Capik e8bb572
# are shared between processes in general or within a program in our case:
Jaromir Capik e8bb572
# http://lkml.org/lkml/2005/7/6/250
Jaromir Capik e8bb572
# A warning is printed if overestimation is possible.
Jaromir Capik e8bb572
# In addition for 2.6 kernels up to 2.6.9 inclusive, the shared
Jaromir Capik e8bb572
# value in /proc/$pid/statm is the total file-backed extent of a process.
Jaromir Capik e8bb572
# We ignore that, introducing more overestimation, again printing a warning.
Jaromir Capik e8bb572
# Since kernel 2.6.23-rc8-mm1 PSS is available in smaps, which allows
Jaromir Capik e8bb572
# us to calculate a more accurate value for the total RAM used by programs.
Jaromir Capik e8bb572
#
Jaromir Capik e8bb572
# Programs that use CLONE_VM without CLONE_THREAD are discounted by assuming
Jaromir Capik e8bb572
# they're the only programs that have the same /proc/$PID/smaps file for
Jaromir Capik e8bb572
# each instance.  This will fail if there are multiple real instances of a
Jaromir Capik e8bb572
# program that then use CLONE_VM without CLONE_THREAD, or if a clone changes
Jaromir Capik e8bb572
# its memory map while we're checksumming each /proc/$PID/smaps.
Jaromir Capik e8bb572
#
Jaromir Capik e8bb572
# I don't take account of memory allocated for a program
Jaromir Capik e8bb572
# by other programs. For e.g. memory used in the X server for
Jaromir Capik e8bb572
# a program could be determined, but is not.
Jaromir Capik e8bb572
#
Jaromir Capik e8bb572
# FreeBSD is supported if linprocfs is mounted at /compat/linux/proc/
Jaromir Capik e8bb572
# FreeBSD 8.0 supports up to a level of Linux 2.6.16
Jaromir Capik e8bb572
Jaromir Capik e8bb572
import getopt
Jaromir Capik e8bb572
import time
Jaromir Capik e8bb572
import errno
Jaromir Capik e8bb572
import os
Jaromir Capik e8bb572
import sys
Jaromir Capik e8bb572
Jaromir Capik e8bb572
# The following exits cleanly on Ctrl-C or EPIPE
Jaromir Capik e8bb572
# while treating other exceptions as before.
Jaromir Capik e8bb572
def std_exceptions(etype, value, tb):
Jaromir Capik e8bb572
    sys.excepthook = sys.__excepthook__
Jaromir Capik e8bb572
    if issubclass(etype, KeyboardInterrupt):
Jaromir Capik e8bb572
        pass
Jaromir Capik e8bb572
    elif issubclass(etype, IOError) and value.errno == errno.EPIPE:
Jaromir Capik e8bb572
        pass
Jaromir Capik e8bb572
    else:
Jaromir Capik e8bb572
        sys.__excepthook__(etype, value, tb)
Jaromir Capik e8bb572
sys.excepthook = std_exceptions
Jaromir Capik e8bb572
Jaromir Capik e8bb572
#
Jaromir Capik e8bb572
#   Define some global variables
Jaromir Capik e8bb572
#
Jaromir Capik e8bb572
Jaromir Capik e8bb572
PAGESIZE = os.sysconf("SC_PAGE_SIZE") / 1024 #KiB
Jaromir Capik e8bb572
our_pid = os.getpid()
Jaromir Capik e8bb572
Jaromir Capik e8bb572
have_pss = 0
Jaromir Capik e8bb572
Jaromir Capik e8bb572
class Proc:
Jaromir Capik e8bb572
    def __init__(self):
Jaromir Capik e8bb572
        uname = os.uname()
Jaromir Capik e8bb572
        if uname[0] == "FreeBSD":
Jaromir Capik e8bb572
            self.proc = '/compat/linux/proc'
Jaromir Capik e8bb572
        else:
Jaromir Capik e8bb572
            self.proc = '/proc'
Jaromir Capik e8bb572
Jaromir Capik e8bb572
    def path(self, *args):
Jaromir Capik e8bb572
        return os.path.join(self.proc, *(str(a) for a in args))
Jaromir Capik e8bb572
Jaromir Capik e8bb572
    def open(self, *args):
Jaromir Capik e8bb572
        try:
5319c0d
            if sys.version_info < (3,):
5319c0d
                return open(self.path(*args))
5319c0d
            else:
5319c0d
                return open(self.path(*args), errors='ignore')
Jaromir Capik e8bb572
        except (IOError, OSError):
Jaromir Capik e8bb572
            val = sys.exc_info()[1]
Jaromir Capik e8bb572
            if (val.errno == errno.ENOENT or # kernel thread or process gone
Jaromir Capik e8bb572
                val.errno == errno.EPERM):
Jaromir Capik e8bb572
                raise LookupError
5319c0d
            raise
Jaromir Capik e8bb572
Jaromir Capik e8bb572
proc = Proc()
Jaromir Capik e8bb572
Jaromir Capik e8bb572
Jaromir Capik e8bb572
#
Jaromir Capik e8bb572
#   Functions
Jaromir Capik e8bb572
#
Jaromir Capik e8bb572
Jaromir Capik e8bb572
def parse_options():
Jaromir Capik e8bb572
    try:
5319c0d
        long_options = ['split-args', 'help', 'total']
5319c0d
        opts, args = getopt.getopt(sys.argv[1:], "shtp:w:", long_options)
Jaromir Capik e8bb572
    except getopt.GetoptError:
Jaromir Capik e8bb572
        sys.stderr.write(help())
Jaromir Capik e8bb572
        sys.exit(3)
Jaromir Capik e8bb572
5319c0d
    if len(args):
5319c0d
        sys.stderr.write("Extraneous arguments: %s\n" % args)
5319c0d
        sys.exit(3)
5319c0d
Jaromir Capik e8bb572
    # ps_mem.py options
Jaromir Capik e8bb572
    split_args = False
Jaromir Capik e8bb572
    pids_to_show = None
Jaromir Capik e8bb572
    watch = None
5319c0d
    only_total = False
Jaromir Capik e8bb572
Jaromir Capik e8bb572
    for o, a in opts:
Jaromir Capik e8bb572
        if o in ('-s', '--split-args'):
Jaromir Capik e8bb572
            split_args = True
5319c0d
        if o in ('-t', '--total'):
5319c0d
            only_total = True
Jaromir Capik e8bb572
        if o in ('-h', '--help'):
Jaromir Capik e8bb572
            sys.stdout.write(help())
Jaromir Capik e8bb572
            sys.exit(0)
Jaromir Capik e8bb572
        if o in ('-p',):
Jaromir Capik e8bb572
            try:
Jaromir Capik e8bb572
                pids_to_show = [int(x) for x in a.split(',')]
Jaromir Capik e8bb572
            except:
Jaromir Capik e8bb572
                sys.stderr.write(help())
Jaromir Capik e8bb572
                sys.exit(3)
Jaromir Capik e8bb572
        if o in ('-w',):
Jaromir Capik e8bb572
            try:
Jaromir Capik e8bb572
                watch = int(a)
Jaromir Capik e8bb572
            except:
Jaromir Capik e8bb572
                sys.stderr.write(help())
Jaromir Capik e8bb572
                sys.exit(3)
Jaromir Capik e8bb572
5319c0d
    return (split_args, pids_to_show, watch, only_total)
Jaromir Capik e8bb572
Jaromir Capik e8bb572
def help():
5319c0d
    help_msg = 'Usage: ps_mem [OPTION]...\n' \
5319c0d
    'Show program core memory usage\n' \
5319c0d
    '\n' \
5319c0d
    '  -h, -help                   Show this help\n' \
5319c0d
    '  -p <pid>[,pid2,...pidN]     Only show memory usage PIDs in the specified list\n' \
5319c0d
    '  -s, --split-args            Show and separate by, all command line arguments\n' \
5319c0d
    '  -t, --total                 Show only the total value\n' \
5319c0d
    '  -w <N>                      Measure and show process memory every N seconds\n'
Jaromir Capik e8bb572
Jaromir Capik e8bb572
    return help_msg
Jaromir Capik e8bb572
Jaromir Capik e8bb572
#(major,minor,release)
Jaromir Capik e8bb572
def kernel_ver():
Jaromir Capik e8bb572
    kv = proc.open('sys/kernel/osrelease').readline().split(".")[:3]
Jaromir Capik e8bb572
    last = len(kv)
Jaromir Capik e8bb572
    if last == 2:
Jaromir Capik e8bb572
        kv.append('0')
Jaromir Capik e8bb572
    last -= 1
5319c0d
    while last > 0:
5319c0d
        for char in "-_":
5319c0d
            kv[last] = kv[last].split(char)[0]
5319c0d
        try:
5319c0d
            int(kv[last])
5319c0d
        except:
5319c0d
            kv[last] = 0
5319c0d
        last -= 1
Jaromir Capik e8bb572
    return (int(kv[0]), int(kv[1]), int(kv[2]))
Jaromir Capik e8bb572
Jaromir Capik e8bb572
Jaromir Capik e8bb572
#return Private,Shared
Jaromir Capik e8bb572
#Note shared is always a subset of rss (trs is not always)
Jaromir Capik e8bb572
def getMemStats(pid):
Jaromir Capik e8bb572
    global have_pss
Jaromir Capik e8bb572
    mem_id = pid #unique
Jaromir Capik e8bb572
    Private_lines = []
Jaromir Capik e8bb572
    Shared_lines = []
Jaromir Capik e8bb572
    Pss_lines = []
Jaromir Capik e8bb572
    Rss = (int(proc.open(pid, 'statm').readline().split()[1])
Jaromir Capik e8bb572
           * PAGESIZE)
Jaromir Capik e8bb572
    if os.path.exists(proc.path(pid, 'smaps')): #stat
5319c0d
        lines = proc.open(pid, 'smaps').readlines() #open
5319c0d
        # Note we checksum smaps as maps is usually but
5319c0d
        # not always different for separate processes.
5319c0d
        mem_id = hash(''.join(lines))
5319c0d
        for line in lines:
Jaromir Capik e8bb572
            if line.startswith("Shared"):
Jaromir Capik e8bb572
                Shared_lines.append(line)
Jaromir Capik e8bb572
            elif line.startswith("Private"):
Jaromir Capik e8bb572
                Private_lines.append(line)
Jaromir Capik e8bb572
            elif line.startswith("Pss"):
Jaromir Capik e8bb572
                have_pss = 1
Jaromir Capik e8bb572
                Pss_lines.append(line)
Jaromir Capik e8bb572
        Shared = sum([int(line.split()[1]) for line in Shared_lines])
Jaromir Capik e8bb572
        Private = sum([int(line.split()[1]) for line in Private_lines])
Jaromir Capik e8bb572
        #Note Shared + Private = Rss above
Jaromir Capik e8bb572
        #The Rss in smaps includes video card mem etc.
Jaromir Capik e8bb572
        if have_pss:
Jaromir Capik e8bb572
            pss_adjust = 0.5 # add 0.5KiB as this avg error due to trunctation
Jaromir Capik e8bb572
            Pss = sum([float(line.split()[1])+pss_adjust for line in Pss_lines])
Jaromir Capik e8bb572
            Shared = Pss - Private
Jaromir Capik e8bb572
    elif (2,6,1) <= kernel_ver() <= (2,6,9):
Jaromir Capik e8bb572
        Shared = 0 #lots of overestimation, but what can we do?
Jaromir Capik e8bb572
        Private = Rss
Jaromir Capik e8bb572
    else:
Jaromir Capik e8bb572
        Shared = int(proc.open(pid, 'statm').readline().split()[2])
Jaromir Capik e8bb572
        Shared *= PAGESIZE
Jaromir Capik e8bb572
        Private = Rss - Shared
Jaromir Capik e8bb572
    return (Private, Shared, mem_id)
Jaromir Capik e8bb572
Jaromir Capik e8bb572
Jaromir Capik e8bb572
def getCmdName(pid, split_args):
Jaromir Capik e8bb572
    cmdline = proc.open(pid, 'cmdline').read().split("\0")
Jaromir Capik e8bb572
    if cmdline[-1] == '' and len(cmdline) > 1:
Jaromir Capik e8bb572
        cmdline = cmdline[:-1]
Jaromir Capik e8bb572
Jaromir Capik e8bb572
    path = proc.path(pid, 'exe')
Jaromir Capik e8bb572
    try:
Jaromir Capik e8bb572
        path = os.readlink(path)
Jaromir Capik e8bb572
        # Some symlink targets were seen to contain NULs on RHEL 5 at least
Jaromir Capik e8bb572
        # https://github.com/pixelb/scripts/pull/10, so take string up to NUL
Jaromir Capik e8bb572
        path = path.split('\0')[0]
Jaromir Capik e8bb572
    except OSError:
Jaromir Capik e8bb572
        val = sys.exc_info()[1]
Jaromir Capik e8bb572
        if (val.errno == errno.ENOENT or # either kernel thread or process gone
Jaromir Capik e8bb572
            val.errno == errno.EPERM):
Jaromir Capik e8bb572
            raise LookupError
5319c0d
        raise
Jaromir Capik e8bb572
Jaromir Capik e8bb572
    if split_args:
Jaromir Capik e8bb572
        return " ".join(cmdline)
Jaromir Capik e8bb572
    if path.endswith(" (deleted)"):
Jaromir Capik e8bb572
        path = path[:-10]
Jaromir Capik e8bb572
        if os.path.exists(path):
Jaromir Capik e8bb572
            path += " [updated]"
Jaromir Capik e8bb572
        else:
Jaromir Capik e8bb572
            #The path could be have prelink stuff so try cmdline
Jaromir Capik e8bb572
            #which might have the full path present. This helped for:
Jaromir Capik e8bb572
            #/usr/libexec/notification-area-applet.#prelink#.fX7LCT (deleted)
Jaromir Capik e8bb572
            if os.path.exists(cmdline[0]):
Jaromir Capik e8bb572
                path = cmdline[0] + " [updated]"
Jaromir Capik e8bb572
            else:
Jaromir Capik e8bb572
                path += " [deleted]"
Jaromir Capik e8bb572
    exe = os.path.basename(path)
Jaromir Capik e8bb572
    cmd = proc.open(pid, 'status').readline()[6:-1]
Jaromir Capik e8bb572
    if exe.startswith(cmd):
Jaromir Capik e8bb572
        cmd = exe #show non truncated version
Jaromir Capik e8bb572
        #Note because we show the non truncated name
Jaromir Capik e8bb572
        #one can have separated programs as follows:
Jaromir Capik e8bb572
        #584.0 KiB +   1.0 MiB =   1.6 MiB    mozilla-thunder (exe -> bash)
Jaromir Capik e8bb572
        # 56.0 MiB +  22.2 MiB =  78.2 MiB    mozilla-thunderbird-bin
5319c0d
    if sys.version_info < (3,):
5319c0d
        return cmd
5319c0d
    else:
5319c0d
        return cmd.encode(errors='replace').decode()
Jaromir Capik e8bb572
Jaromir Capik e8bb572
Jaromir Capik e8bb572
#The following matches "du -h" output
Jaromir Capik e8bb572
#see also human.py
5319c0d
def human(num, power="Ki", units=None):
5319c0d
    if units is None:
5319c0d
        powers = ["Ki", "Mi", "Gi", "Ti"]
5319c0d
        while num >= 1000: #4 digits
5319c0d
            num /= 1024.0
5319c0d
            power = powers[powers.index(power)+1]
5319c0d
        return "%.1f %sB" % (num, power)
5319c0d
    else:
5319c0d
        return "%.f" % ((num * 1024) / units)
Jaromir Capik e8bb572
Jaromir Capik e8bb572
Jaromir Capik e8bb572
def cmd_with_count(cmd, count):
Jaromir Capik e8bb572
    if count > 1:
Jaromir Capik e8bb572
        return "%s (%u)" % (cmd, count)
Jaromir Capik e8bb572
    else:
Jaromir Capik e8bb572
        return cmd
Jaromir Capik e8bb572
Jaromir Capik e8bb572
#Warn of possible inaccuracies
Jaromir Capik e8bb572
#2 = accurate & can total
Jaromir Capik e8bb572
#1 = accurate only considering each process in isolation
Jaromir Capik e8bb572
#0 = some shared mem not reported
Jaromir Capik e8bb572
#-1= all shared mem not reported
Jaromir Capik e8bb572
def shared_val_accuracy():
Jaromir Capik e8bb572
    """http://wiki.apache.org/spamassassin/TopSharedMemoryBug"""
Jaromir Capik e8bb572
    kv = kernel_ver()
5319c0d
    pid = os.getpid()
Jaromir Capik e8bb572
    if kv[:2] == (2,4):
Jaromir Capik e8bb572
        if proc.open('meminfo').read().find("Inact_") == -1:
Jaromir Capik e8bb572
            return 1
Jaromir Capik e8bb572
        return 0
Jaromir Capik e8bb572
    elif kv[:2] == (2,6):
Jaromir Capik e8bb572
        if os.path.exists(proc.path(pid, 'smaps')):
Jaromir Capik e8bb572
            if proc.open(pid, 'smaps').read().find("Pss:")!=-1:
Jaromir Capik e8bb572
                return 2
Jaromir Capik e8bb572
            else:
Jaromir Capik e8bb572
                return 1
Jaromir Capik e8bb572
        if (2,6,1) <= kv <= (2,6,9):
Jaromir Capik e8bb572
            return -1
Jaromir Capik e8bb572
        return 0
5319c0d
    elif kv[0] > 2 and os.path.exists(proc.path(pid, 'smaps')):
Jaromir Capik e8bb572
        return 2
Jaromir Capik e8bb572
    else:
Jaromir Capik e8bb572
        return 1
Jaromir Capik e8bb572
5319c0d
def show_shared_val_accuracy( possible_inacc, only_total=False ):
5319c0d
    level = ("Warning","Error")[only_total]
Jaromir Capik e8bb572
    if possible_inacc == -1:
Jaromir Capik e8bb572
        sys.stderr.write(
5319c0d
         "%s: Shared memory is not reported by this system.\n" % level
Jaromir Capik e8bb572
        )
Jaromir Capik e8bb572
        sys.stderr.write(
Jaromir Capik e8bb572
         "Values reported will be too large, and totals are not reported\n"
Jaromir Capik e8bb572
        )
Jaromir Capik e8bb572
    elif possible_inacc == 0:
Jaromir Capik e8bb572
        sys.stderr.write(
5319c0d
         "%s: Shared memory is not reported accurately by this system.\n" % level
Jaromir Capik e8bb572
        )
Jaromir Capik e8bb572
        sys.stderr.write(
Jaromir Capik e8bb572
         "Values reported could be too large, and totals are not reported\n"
Jaromir Capik e8bb572
        )
Jaromir Capik e8bb572
    elif possible_inacc == 1:
Jaromir Capik e8bb572
        sys.stderr.write(
5319c0d
         "%s: Shared memory is slightly over-estimated by this system\n"
5319c0d
         "for each program, so totals are not reported.\n" % level
Jaromir Capik e8bb572
        )
Jaromir Capik e8bb572
    sys.stderr.close()
5319c0d
    if only_total and possible_inacc != 2:
5319c0d
        sys.exit(1)
Jaromir Capik e8bb572
Jaromir Capik e8bb572
def get_memory_usage( pids_to_show, split_args, include_self=False, only_self=False ):
Jaromir Capik e8bb572
    cmds = {}
Jaromir Capik e8bb572
    shareds = {}
Jaromir Capik e8bb572
    mem_ids = {}
Jaromir Capik e8bb572
    count = {}
Jaromir Capik e8bb572
    for pid in os.listdir(proc.path('')):
Jaromir Capik e8bb572
        if not pid.isdigit():
Jaromir Capik e8bb572
            continue
Jaromir Capik e8bb572
        pid = int(pid)
Jaromir Capik e8bb572
Jaromir Capik e8bb572
        # Some filters
Jaromir Capik e8bb572
        if only_self and pid != our_pid:
Jaromir Capik e8bb572
            continue
Jaromir Capik e8bb572
        if pid == our_pid and not include_self:
Jaromir Capik e8bb572
            continue
Jaromir Capik e8bb572
        if pids_to_show is not None and pid not in pids_to_show:
Jaromir Capik e8bb572
            continue
Jaromir Capik e8bb572
Jaromir Capik e8bb572
        try:
Jaromir Capik e8bb572
            cmd = getCmdName(pid, split_args)
Jaromir Capik e8bb572
        except LookupError:
5319c0d
            #operation not permitted
Jaromir Capik e8bb572
            #kernel threads don't have exe links or
Jaromir Capik e8bb572
            #process gone
Jaromir Capik e8bb572
            continue
Jaromir Capik e8bb572
Jaromir Capik e8bb572
        try:
Jaromir Capik e8bb572
            private, shared, mem_id = getMemStats(pid)
Jaromir Capik e8bb572
        except RuntimeError:
Jaromir Capik e8bb572
            continue #process gone
Jaromir Capik e8bb572
        if shareds.get(cmd):
Jaromir Capik e8bb572
            if have_pss: #add shared portion of PSS together
Jaromir Capik e8bb572
                shareds[cmd] += shared
Jaromir Capik e8bb572
            elif shareds[cmd] < shared: #just take largest shared val
Jaromir Capik e8bb572
                shareds[cmd] = shared
Jaromir Capik e8bb572
        else:
Jaromir Capik e8bb572
            shareds[cmd] = shared
Jaromir Capik e8bb572
        cmds[cmd] = cmds.setdefault(cmd, 0) + private
Jaromir Capik e8bb572
        if cmd in count:
Jaromir Capik e8bb572
            count[cmd] += 1
Jaromir Capik e8bb572
        else:
Jaromir Capik e8bb572
            count[cmd] = 1
Jaromir Capik e8bb572
        mem_ids.setdefault(cmd, {}).update({mem_id:None})
Jaromir Capik e8bb572
Jaromir Capik e8bb572
    #Add shared mem for each program
Jaromir Capik e8bb572
    total = 0
Jaromir Capik e8bb572
    for cmd in cmds:
Jaromir Capik e8bb572
        cmd_count = count[cmd]
Jaromir Capik e8bb572
        if len(mem_ids[cmd]) == 1 and cmd_count > 1:
Jaromir Capik e8bb572
            # Assume this program is using CLONE_VM without CLONE_THREAD
Jaromir Capik e8bb572
            # so only account for one of the processes
Jaromir Capik e8bb572
            cmds[cmd] /= cmd_count
Jaromir Capik e8bb572
            if have_pss:
Jaromir Capik e8bb572
                shareds[cmd] /= cmd_count
Jaromir Capik e8bb572
        cmds[cmd] = cmds[cmd] + shareds[cmd]
Jaromir Capik e8bb572
        total += cmds[cmd] #valid if PSS available
Jaromir Capik e8bb572
Jaromir Capik e8bb572
    sorted_cmds = sorted(cmds.items(), key=lambda x:x[1])
Jaromir Capik e8bb572
    sorted_cmds = [x for x in sorted_cmds if x[1]]
Jaromir Capik e8bb572
Jaromir Capik e8bb572
    return sorted_cmds, shareds, count, total
Jaromir Capik e8bb572
Jaromir Capik e8bb572
def print_header():
5319c0d
    sys.stdout.write(" Private  +   Shared  =  RAM used\tProgram\n\n")
Jaromir Capik e8bb572
Jaromir Capik e8bb572
def print_memory_usage(sorted_cmds, shareds, count, total):
Jaromir Capik e8bb572
    for cmd in sorted_cmds:
5319c0d
        sys.stdout.write("%9s + %9s = %9s\t%s\n" %
Jaromir Capik e8bb572
                         (human(cmd[1]-shareds[cmd[0]]),
Jaromir Capik e8bb572
                          human(shareds[cmd[0]]), human(cmd[1]),
Jaromir Capik e8bb572
                          cmd_with_count(cmd[0], count[cmd[0]])))
Jaromir Capik e8bb572
    if have_pss:
5319c0d
        sys.stdout.write("%s\n%s%9s\n%s\n" %
Jaromir Capik e8bb572
                         ("-" * 33, " " * 24, human(total), "=" * 33))
Jaromir Capik e8bb572
Jaromir Capik e8bb572
def verify_environment():
Jaromir Capik e8bb572
    if os.geteuid() != 0:
Jaromir Capik e8bb572
        sys.stderr.write("Sorry, root permission required.\n")
481a167
        sys.stderr.close()
481a167
        sys.exit(1)
Jaromir Capik e8bb572
Jaromir Capik e8bb572
    try:
Jaromir Capik e8bb572
        kv = kernel_ver()
Jaromir Capik e8bb572
    except (IOError, OSError):
Jaromir Capik e8bb572
        val = sys.exc_info()[1]
Jaromir Capik e8bb572
        if val.errno == errno.ENOENT:
Jaromir Capik e8bb572
            sys.stderr.write(
Jaromir Capik e8bb572
              "Couldn't access " + proc.path('') + "\n"
Jaromir Capik e8bb572
              "Only GNU/Linux and FreeBSD (with linprocfs) are supported\n")
Jaromir Capik e8bb572
            sys.exit(2)
Jaromir Capik e8bb572
        else:
Jaromir Capik e8bb572
            raise
Jaromir Capik e8bb572
481a167
def main():
5319c0d
    split_args, pids_to_show, watch, only_total = parse_options()
Jaromir Capik e8bb572
    verify_environment()
Jaromir Capik e8bb572
5319c0d
    if not only_total:
5319c0d
        print_header()
Jaromir Capik e8bb572
Jaromir Capik e8bb572
    if watch is not None:
Jaromir Capik e8bb572
        try:
Jaromir Capik e8bb572
            sorted_cmds = True
Jaromir Capik e8bb572
            while sorted_cmds:
Jaromir Capik e8bb572
                sorted_cmds, shareds, count, total = get_memory_usage( pids_to_show, split_args )
5319c0d
                if only_total and have_pss:
5319c0d
                    sys.stdout.write(human(total, units=1)+'\n')
5319c0d
                elif not only_total:
5319c0d
                    print_memory_usage(sorted_cmds, shareds, count, total)
Jaromir Capik e8bb572
                time.sleep(watch)
Jaromir Capik e8bb572
            else:
Jaromir Capik e8bb572
                sys.stdout.write('Process does not exist anymore.\n')
Jaromir Capik e8bb572
        except KeyboardInterrupt:
Jaromir Capik e8bb572
            pass
Jaromir Capik e8bb572
    else:
Jaromir Capik e8bb572
        # This is the default behavior
Jaromir Capik e8bb572
        sorted_cmds, shareds, count, total = get_memory_usage( pids_to_show, split_args )
5319c0d
        if only_total and have_pss:
5319c0d
            sys.stdout.write(human(total, units=1)+'\n')
5319c0d
        elif not only_total:
5319c0d
            print_memory_usage(sorted_cmds, shareds, count, total)
Jaromir Capik e8bb572
Jaromir Capik e8bb572
    # We must close explicitly, so that any EPIPE exception
Jaromir Capik e8bb572
    # is handled by our excepthook, rather than the default
Jaromir Capik e8bb572
    # one which is reenabled after this script finishes.
Jaromir Capik e8bb572
    sys.stdout.close()
Jaromir Capik e8bb572
Jaromir Capik e8bb572
    vm_accuracy = shared_val_accuracy()
5319c0d
    show_shared_val_accuracy( vm_accuracy, only_total )
481a167
481a167
if __name__ == '__main__': main()