Compare commits

...

No commits in common. 'c9' and 'c10-beta' have entirely different histories.
c9 ... c10-beta

@ -1,6 +1,6 @@
.\" Simple man page to ps_mem script .\" Simple man page to ps_mem script
.\" Contact fholec@redhat.com .\" Contact fholec@redhat.com
.TH ps_mem 1 "04 October 2014" "" "" .TH ps_mem 1 "25 May 2018" "" ""
.SH NAME .SH NAME
ps_mem \- Memory profiling tool ps_mem \- Memory profiling tool
.SH SYNOPSIS .SH SYNOPSIS
@ -25,9 +25,20 @@ The shared RAM is problematic to calculate, and the tool automatically selects t
\-h \-\-help \-h \-\-help
Show help message Show help message
.TP .TP
\-\-version
Show version info
.TP
\-p PID[,PID2,...PIDN] \-p PID[,PID2,...PIDN]
Show memory consumption for the specified processes Show memory consumption for the specified processes
.TP .TP
\-S \-\-swap
Show swap information of each item
(and Shared swap if the kernel supports SwapPss).
.TP
\-d \-\-discriminate-by-pid
Show and separate memory usage entries by process
rather than by program.
.TP
\-s \-\-split\-args \-s \-\-split\-args
Show and separate memory usage entries by command line arguments Show and separate memory usage entries by command line arguments
and not just the program name. and not just the program name.

@ -9,7 +9,7 @@
# Licence: LGPLv2 # Licence: LGPLv2
# Author: P@draigBrady.com # Author: P@draigBrady.com
# Source: http://www.pixelbeat.org/scripts/ps_mem.py # Source: https://www.pixelbeat.org/scripts/ps_mem.py
# V1.0 06 Jul 2005 Initial release # V1.0 06 Jul 2005 Initial release
# V1.1 11 Aug 2006 root permission required for accuracy # V1.1 11 Aug 2006 root permission required for accuracy
@ -36,8 +36,8 @@
# Patch from patrice.bouchand.fedora@gmail.com # Patch from patrice.bouchand.fedora@gmail.com
# V1.9 20 Feb 2008 Fix invalid values reported when PSS is available. # V1.9 20 Feb 2008 Fix invalid values reported when PSS is available.
# Reported by Andrey Borzenkov <arvidjaar@mail.ru> # Reported by Andrey Borzenkov <arvidjaar@mail.ru>
# V3.6 16 Oct 2015 # V3.14 28 May 2022
# http://github.com/pixelb/scripts/commits/master/scripts/ps_mem.py # https://github.com/pixelb/ps_mem/commits/master/ps_mem.py
# Notes: # Notes:
# #
@ -73,11 +73,11 @@
# FreeBSD is supported if linprocfs is mounted at /compat/linux/proc/ # FreeBSD is supported if linprocfs is mounted at /compat/linux/proc/
# FreeBSD 8.0 supports up to a level of Linux 2.6.16 # FreeBSD 8.0 supports up to a level of Linux 2.6.16
import getopt import argparse
import time
import errno import errno
import os import os
import sys import sys
import time
import io import io
# The following exits cleanly on Ctrl-C or EPIPE # The following exits cleanly on Ctrl-C or EPIPE
@ -100,10 +100,11 @@ PAGESIZE = os.sysconf("SC_PAGE_SIZE") / 1024 #KiB
our_pid = os.getpid() our_pid = os.getpid()
have_pss = 0 have_pss = 0
have_swap_pss = 0
class Unbuffered(io.TextIOBase): class Unbuffered(io.TextIOBase):
def __init__(self, stream): def __init__(self, stream):
super().__init__() super(Unbuffered, self).__init__()
self.stream = stream self.stream = stream
def write(self, data): def write(self, data):
self.stream.write(data) self.stream.write(data)
@ -129,9 +130,12 @@ class Proc:
else: else:
return open(self.path(*args), errors='ignore') return open(self.path(*args), errors='ignore')
except (IOError, OSError): except (IOError, OSError):
if type(args[0]) is not int:
raise
val = sys.exc_info()[1] val = sys.exc_info()[1]
if (val.errno == errno.ENOENT or # kernel thread or process gone if (val.errno == errno.ENOENT or # kernel thread or process gone
val.errno == errno.EPERM): val.errno == errno.EPERM or
val.errno == errno.EACCES):
raise LookupError raise LookupError
raise raise
@ -143,59 +147,68 @@ proc = Proc()
# #
def parse_options(): def parse_options():
try: help_msg = 'Show program core memory usage.'
long_options = ['split-args', 'help', 'total'] parser = argparse.ArgumentParser(prog='ps_mem', description=help_msg)
opts, args = getopt.getopt(sys.argv[1:], "shtp:w:", long_options) parser.add_argument('--version', action='version', version='3.14')
except getopt.GetoptError: parser.add_argument(
sys.stderr.write(help()) '-s', '--split-args',
sys.exit(3) action='store_true',
help='Show and separate by, all command line arguments',
if len(args): )
sys.stderr.write("Extraneous arguments: %s\n" % args) parser.add_argument(
sys.exit(3) '-t', '--total',
dest='only_total',
# ps_mem.py options action='store_true',
split_args = False help='Show only the total value',
pids_to_show = None )
watch = None parser.add_argument(
only_total = False '-d', '--discriminate-by-pid',
action='store_true',
for o, a in opts: help='Show by process rather than by program',
if o in ('-s', '--split-args'): )
split_args = True parser.add_argument(
if o in ('-t', '--total'): '-S', '--swap',
only_total = True dest='show_swap',
if o in ('-h', '--help'): action='store_true',
sys.stdout.write(help()) help='Show swap information',
sys.exit(0) )
if o in ('-p',): parser.add_argument(
try: '-p',
pids_to_show = [int(x) for x in a.split(',')] dest='pids',
except: metavar='<pid>[,pid2,...pidN]',
sys.stderr.write(help()) help='Only show memory usage PIDs in the specified list',
sys.exit(3) )
if o in ('-w',): parser.add_argument(
try: '-w',
watch = int(a) dest='watch',
except: metavar='<N>',
sys.stderr.write(help()) type=int,
sys.exit(3) help='Measure and show process memory every N seconds',
)
return (split_args, pids_to_show, watch, only_total) args = parser.parse_args()
def help(): args.pids_to_show = []
help_msg = 'Usage: ps_mem [OPTION]...\n' \ if args.pids:
'Show program core memory usage\n' \ try:
'\n' \ args.pids_to_show = [int(x) for x in args.pids.split(',')]
' -h, -help Show this help\n' \ except ValueError:
' -p <pid>[,pid2,...pidN] Only show memory usage PIDs in the specified list\n' \ parser.error('Invalid PID(s): %s' % args.pids)
' -s, --split-args Show and separate by, all command line arguments\n' \
' -t, --total Show only the total value\n' \ if args.watch is not None:
' -w <N> Measure and show process memory every N seconds\n' if args.watch <= 0:
parser.error('Seconds must be positive! (%s)' % args.watch)
return help_msg
return (
#(major,minor,release) args.split_args,
args.pids_to_show,
args.watch,
args.only_total,
args.discriminate_by_pid,
args.show_swap,
)
# (major,minor,release)
def kernel_ver(): def kernel_ver():
kv = proc.open('sys/kernel/osrelease').readline().split(".")[:3] kv = proc.open('sys/kernel/osrelease').readline().split(".")[:3]
last = len(kv) last = len(kv)
@ -213,48 +226,82 @@ def kernel_ver():
return (int(kv[0]), int(kv[1]), int(kv[2])) return (int(kv[0]), int(kv[1]), int(kv[2]))
#return Private,Shared #return Private,Shared,Swap(Pss),unique_id
#Note shared is always a subset of rss (trs is not always) #Note shared is always a subset of rss (trs is not always)
def getMemStats(pid): def getMemStats(pid):
global have_pss global have_pss
global have_swap_pss
mem_id = pid #unique mem_id = pid #unique
Private_lines = [] Private_lines = []
Shared_lines = [] Shared_lines = []
Private_huge_lines = []
Shared_huge_lines = []
Pss_lines = [] Pss_lines = []
Rss = (int(proc.open(pid, 'statm').readline().split()[1]) Rss = (int(proc.open(pid, 'statm').readline().split()[1])
* PAGESIZE) * PAGESIZE)
if os.path.exists(proc.path(pid, 'smaps')): #stat Swap_lines = []
lines = proc.open(pid, 'smaps').readlines() #open Swap_pss_lines = []
Swap = 0
if os.path.exists(proc.path(pid, 'smaps')): # stat
smaps = 'smaps'
if os.path.exists(proc.path(pid, 'smaps_rollup')):
smaps = 'smaps_rollup' # faster to process
lines = proc.open(pid, smaps).readlines() # open
# Note we checksum smaps as maps is usually but # Note we checksum smaps as maps is usually but
# not always different for separate processes. # not always different for separate processes.
mem_id = hash(''.join(lines)) mem_id = hash(''.join(lines))
for line in lines: for line in lines:
if line.startswith("Shared"): # {Private,Shared}_Hugetlb is not included in Pss (why?)
# so we need to account for separately.
if line.startswith("Private_Hugetlb:"):
Private_huge_lines.append(line)
elif line.startswith("Shared_Hugetlb:"):
Shared_huge_lines.append(line)
elif line.startswith("Shared"):
Shared_lines.append(line) Shared_lines.append(line)
elif line.startswith("Private"): elif line.startswith("Private"):
Private_lines.append(line) Private_lines.append(line)
elif line.startswith("Pss"): elif line.startswith("Pss:"):
have_pss = 1 have_pss = 1
Pss_lines.append(line) Pss_lines.append(line)
elif line.startswith("Swap:"):
Swap_lines.append(line)
elif line.startswith("SwapPss:"):
have_swap_pss = 1
Swap_pss_lines.append(line)
Shared = sum([int(line.split()[1]) for line in Shared_lines]) Shared = sum([int(line.split()[1]) for line in Shared_lines])
Private = sum([int(line.split()[1]) for line in Private_lines]) Private = sum([int(line.split()[1]) for line in Private_lines])
Shared_huge = sum([int(line.split()[1]) for line in Shared_huge_lines])
Private_huge = sum([int(line.split()[1]) for line in Private_huge_lines])
#Note Shared + Private = Rss above #Note Shared + Private = Rss above
#The Rss in smaps includes video card mem etc. #The Rss in smaps includes video card mem etc.
if have_pss: if have_pss:
pss_adjust = 0.5 # add 0.5KiB as this avg error due to trunctation pss_adjust = 0.5 # add 0.5KiB as this avg error due to truncation
Pss = sum([float(line.split()[1])+pss_adjust for line in Pss_lines]) Pss = sum([float(line.split()[1])+pss_adjust for line in Pss_lines])
Shared = Pss - Private Shared = Pss - Private
Private += Private_huge # Add after as PSS doesn't a/c for huge pages
if have_swap_pss:
# The kernel supports SwapPss, that shows proportional swap share.
# Note that Swap - SwapPss is not Private Swap.
Swap = sum([int(line.split()[1]) for line in Swap_pss_lines])
else:
# Note that Swap = Private swap + Shared swap.
Swap = sum([int(line.split()[1]) for line in Swap_lines])
elif (2,6,1) <= kernel_ver() <= (2,6,9): elif (2,6,1) <= kernel_ver() <= (2,6,9):
Shared = 0 #lots of overestimation, but what can we do? Shared = 0 #lots of overestimation, but what can we do?
Shared_huge = 0
Private = Rss Private = Rss
else: else:
Shared = int(proc.open(pid, 'statm').readline().split()[2]) Shared = int(proc.open(pid, 'statm').readline().split()[2])
Shared *= PAGESIZE Shared *= PAGESIZE
Shared_huge = 0
Private = Rss - Shared Private = Rss - Shared
return (Private, Shared, mem_id) return (Private, Shared, Shared_huge, Swap, mem_id)
def getCmdName(pid, split_args): def getCmdName(pid, split_args, discriminate_by_pid, exe_only=False):
cmdline = proc.open(pid, 'cmdline').read().split("\0") cmdline = proc.open(pid, 'cmdline').read().split("\0")
while cmdline[-1] == '' and len(cmdline) > 1: while cmdline[-1] == '' and len(cmdline) > 1:
cmdline = cmdline[:-1] cmdline = cmdline[:-1]
@ -268,12 +315,13 @@ def getCmdName(pid, split_args):
except OSError: except OSError:
val = sys.exc_info()[1] val = sys.exc_info()[1]
if (val.errno == errno.ENOENT or # either kernel thread or process gone if (val.errno == errno.ENOENT or # either kernel thread or process gone
val.errno == errno.EPERM): val.errno == errno.EPERM or
val.errno == errno.EACCES):
raise LookupError raise LookupError
raise raise
if split_args: if split_args:
return " ".join(cmdline) return ' '.join(cmdline).replace('\n', ' ')
if path.endswith(" (deleted)"): if path.endswith(" (deleted)"):
path = path[:-10] path = path[:-10]
if os.path.exists(path): if os.path.exists(path):
@ -287,17 +335,38 @@ def getCmdName(pid, split_args):
else: else:
path += " [deleted]" path += " [deleted]"
exe = os.path.basename(path) exe = os.path.basename(path)
cmd = proc.open(pid, 'status').readline()[6:-1] if exe_only: return exe
proc_status = proc.open(pid, 'status').readlines()
cmd = proc_status[0][6:-1]
if exe.startswith(cmd): if exe.startswith(cmd):
cmd = exe #show non truncated version cmd = exe #show non truncated version
#Note because we show the non truncated name #Note because we show the non truncated name
#one can have separated programs as follows: #one can have separated programs as follows:
#584.0 KiB + 1.0 MiB = 1.6 MiB mozilla-thunder (exe -> bash) #584.0 KiB + 1.0 MiB = 1.6 MiB mozilla-thunder (exe -> bash)
# 56.0 MiB + 22.2 MiB = 78.2 MiB mozilla-thunderbird-bin # 56.0 MiB + 22.2 MiB = 78.2 MiB mozilla-thunderbird-bin
if sys.version_info < (3,):
return cmd
else: else:
return cmd.encode(errors='replace').decode() #Lookup the parent's exe and use that if matching
#which will merge "Web Content" with "firefox" for example
ppid = 0
for l in range(10):
ps_line = proc_status[l]
if ps_line.startswith('PPid:'):
ppid = int(ps_line[6:-1])
break
if ppid:
try:
p_exe = getCmdName(ppid, False, False, exe_only=True)
except LookupError:
pass
else:
if exe == p_exe:
cmd = exe
if sys.version_info >= (3,):
cmd = cmd.encode(errors='replace').decode()
if discriminate_by_pid:
cmd = '%s [%d]' % (cmd, pid)
return cmd
#The following matches "du -h" output #The following matches "du -h" output
@ -320,62 +389,99 @@ def cmd_with_count(cmd, count):
return cmd return cmd
#Warn of possible inaccuracies #Warn of possible inaccuracies
#RAM:
#2 = accurate & can total #2 = accurate & can total
#1 = accurate only considering each process in isolation #1 = accurate only considering each process in isolation
#0 = some shared mem not reported #0 = some shared mem not reported
#-1= all shared mem not reported #-1= all shared mem not reported
def shared_val_accuracy(): #SWAP:
#2 = accurate & can total
#1 = accurate only considering each process in isolation
#-1= not available
def val_accuracy(show_swap):
"""http://wiki.apache.org/spamassassin/TopSharedMemoryBug""" """http://wiki.apache.org/spamassassin/TopSharedMemoryBug"""
kv = kernel_ver() kv = kernel_ver()
pid = os.getpid() pid = os.getpid()
swap_accuracy = -1
if kv[:2] == (2,4): if kv[:2] == (2,4):
if proc.open('meminfo').read().find("Inact_") == -1: if proc.open('meminfo').read().find("Inact_") == -1:
return 1 return 1, swap_accuracy
return 0 return 0, swap_accuracy
elif kv[:2] == (2,6): elif kv[:2] == (2,6):
if os.path.exists(proc.path(pid, 'smaps')): if os.path.exists(proc.path(pid, 'smaps')):
swap_accuracy = 1
if proc.open(pid, 'smaps').read().find("Pss:")!=-1: if proc.open(pid, 'smaps').read().find("Pss:")!=-1:
return 2 return 2, swap_accuracy
else: else:
return 1 return 1, swap_accuracy
if (2,6,1) <= kv <= (2,6,9): if (2,6,1) <= kv <= (2,6,9):
return -1 return -1, swap_accuracy
return 0 return 0, swap_accuracy
elif kv[0] > 2 and os.path.exists(proc.path(pid, 'smaps')): elif kv[0] > 2 and os.path.exists(proc.path(pid, 'smaps')):
return 2 swap_accuracy = 1
if show_swap and proc.open(pid, 'smaps').read().find("SwapPss:")!=-1:
swap_accuracy = 2
return 2, swap_accuracy
else: else:
return 1 return 1, swap_accuracy
def show_shared_val_accuracy( possible_inacc, only_total=False ): def show_val_accuracy( ram_inacc, swap_inacc, only_total, show_swap ):
level = ("Warning","Error")[only_total] level = ("Warning","Error")[only_total]
if possible_inacc == -1:
# Only show significant warnings
if not show_swap:
swap_inacc = 2
elif only_total:
ram_inacc = 2
if ram_inacc == -1:
sys.stderr.write( sys.stderr.write(
"%s: Shared memory is not reported by this system.\n" % level "%s: Shared memory is not reported by this system.\n" % level
) )
sys.stderr.write( sys.stderr.write(
"Values reported will be too large, and totals are not reported\n" "Values reported will be too large, and totals are not reported\n"
) )
elif possible_inacc == 0: elif ram_inacc == 0:
sys.stderr.write( sys.stderr.write(
"%s: Shared memory is not reported accurately by this system.\n" % level "%s: Shared memory is not reported accurately by this system.\n" % level
) )
sys.stderr.write( sys.stderr.write(
"Values reported could be too large, and totals are not reported\n" "Values reported could be too large, and totals are not reported\n"
) )
elif possible_inacc == 1: elif ram_inacc == 1:
sys.stderr.write( sys.stderr.write(
"%s: Shared memory is slightly over-estimated by this system\n" "%s: Shared memory is slightly over-estimated by this system\n"
"for each program, so totals are not reported.\n" % level "for each program, so totals are not reported.\n" % level
) )
if swap_inacc == -1:
sys.stderr.write(
"%s: Swap is not reported by this system.\n" % level
)
elif swap_inacc == 1:
sys.stderr.write(
"%s: Swap is over-estimated by this system for each program,\n"
"so totals are not reported.\n" % level
)
sys.stderr.close() sys.stderr.close()
if only_total and possible_inacc != 2: if only_total:
sys.exit(1) if show_swap:
accuracy = swap_inacc
else:
accuracy = ram_inacc
if accuracy != 2:
sys.exit(1)
def get_memory_usage( pids_to_show, split_args, include_self=False, only_self=False ): def get_memory_usage(pids_to_show, split_args, discriminate_by_pid,
include_self=False, only_self=False):
cmds = {} cmds = {}
shareds = {} shareds = {}
shared_huges = {}
mem_ids = {} mem_ids = {}
count = {} count = {}
swaps = {}
for pid in os.listdir(proc.path('')): for pid in os.listdir(proc.path('')):
if not pid.isdigit(): if not pid.isdigit():
continue continue
@ -386,11 +492,11 @@ def get_memory_usage( pids_to_show, split_args, include_self=False, only_self=Fa
continue continue
if pid == our_pid and not include_self: if pid == our_pid and not include_self:
continue continue
if pids_to_show is not None and pid not in pids_to_show: if pids_to_show and pid not in pids_to_show:
continue continue
try: try:
cmd = getCmdName(pid, split_args) cmd = getCmdName(pid, split_args, discriminate_by_pid)
except LookupError: except LookupError:
#operation not permitted #operation not permitted
#kernel threads don't have exe links or #kernel threads don't have exe links or
@ -398,7 +504,7 @@ def get_memory_usage( pids_to_show, split_args, include_self=False, only_self=Fa
continue continue
try: try:
private, shared, mem_id = getMemStats(pid) private, shared, shared_huge, swap, mem_id = getMemStats(pid)
except RuntimeError: except RuntimeError:
continue #process gone continue #process gone
if shareds.get(cmd): if shareds.get(cmd):
@ -408,15 +514,27 @@ def get_memory_usage( pids_to_show, split_args, include_self=False, only_self=Fa
shareds[cmd] = shared shareds[cmd] = shared
else: else:
shareds[cmd] = shared shareds[cmd] = shared
if shared_huges.get(cmd):
if shared_huges[cmd] < shared_huge: #just take largest shared_huge
shared_huges[cmd] = shared_huge
else:
shared_huges[cmd] = shared_huge
cmds[cmd] = cmds.setdefault(cmd, 0) + private cmds[cmd] = cmds.setdefault(cmd, 0) + private
if cmd in count: if cmd in count:
count[cmd] += 1 count[cmd] += 1
else: else:
count[cmd] = 1 count[cmd] = 1
mem_ids.setdefault(cmd, {}).update({mem_id:None}) mem_ids.setdefault(cmd, {}).update({mem_id: None})
# Swap (overcounting for now...)
swaps[cmd] = swaps.setdefault(cmd, 0) + swap
# Total swaped mem for each program
total_swap = 0
#Add shared mem for each program # Add shared mem for each program
total = 0 total = 0
for cmd in cmds: for cmd in cmds:
cmd_count = count[cmd] cmd_count = count[cmd]
if len(mem_ids[cmd]) == 1 and cmd_count > 1: if len(mem_ids[cmd]) == 1 and cmd_count > 1:
@ -425,35 +543,61 @@ def get_memory_usage( pids_to_show, split_args, include_self=False, only_self=Fa
cmds[cmd] /= cmd_count cmds[cmd] /= cmd_count
if have_pss: if have_pss:
shareds[cmd] /= cmd_count shareds[cmd] /= cmd_count
# overestimation possible if shared_huges shared across commands
shareds[cmd] += shared_huges[cmd]
cmds[cmd] = cmds[cmd] + shareds[cmd] cmds[cmd] = cmds[cmd] + shareds[cmd]
total += cmds[cmd] #valid if PSS available total += cmds[cmd] # valid if PSS available
total_swap += swaps[cmd]
sorted_cmds = sorted(cmds.items(), key=lambda x:x[1]) sorted_cmds = sorted(cmds.items(), key=lambda x:x[1])
sorted_cmds = [x for x in sorted_cmds if x[1]] sorted_cmds = [x for x in sorted_cmds if x[1]]
return sorted_cmds, shareds, count, total return sorted_cmds, shareds, count, total, swaps, total_swap
def print_header(show_swap, discriminate_by_pid):
output_string = " Private + Shared = RAM used"
if show_swap:
output_string += " Swap used"
output_string += "\tProgram"
if discriminate_by_pid:
output_string += "[pid]"
output_string += "\n\n"
sys.stdout.write(output_string)
def print_header():
sys.stdout.write(" Private + Shared = RAM used\tProgram\n\n")
def print_memory_usage(sorted_cmds, shareds, count, total): def print_memory_usage(sorted_cmds, shareds, count, total, swaps, total_swap,
show_swap):
for cmd in sorted_cmds: for cmd in sorted_cmds:
sys.stdout.write("%9s + %9s = %9s\t%s\n" %
(human(cmd[1]-shareds[cmd[0]]), output_string = "%9s + %9s = %9s"
human(shareds[cmd[0]]), human(cmd[1]), output_data = (human(cmd[1]-shareds[cmd[0]]),
cmd_with_count(cmd[0], count[cmd[0]]))) human(shareds[cmd[0]]), human(cmd[1]))
if have_pss: if show_swap:
output_string += " %9s"
output_data += (human(swaps[cmd[0]]),)
output_string += "\t%s\n"
output_data += (cmd_with_count(cmd[0], count[cmd[0]]),)
sys.stdout.write(output_string % output_data)
# Only show totals if appropriate
if have_swap_pss and show_swap: # kernel will have_pss
sys.stdout.write("%s\n%s%9s%s%9s\n%s\n" %
("-" * 45, " " * 24, human(total), " " * 3,
human(total_swap), "=" * 45))
elif have_pss:
sys.stdout.write("%s\n%s%9s\n%s\n" % sys.stdout.write("%s\n%s%9s\n%s\n" %
("-" * 33, " " * 24, human(total), "=" * 33)) ("-" * 33, " " * 24, human(total), "=" * 33))
def verify_environment():
if os.geteuid() != 0: def verify_environment(pids_to_show):
sys.stderr.write("Sorry, root permission required.\n") if os.geteuid() != 0 and not pids_to_show:
sys.stderr.write("Sorry, root permission required, or specify pids with -p\n")
sys.stderr.close() sys.stderr.close()
sys.exit(1) sys.exit(1)
try: try:
kv = kernel_ver() kernel_ver()
except (IOError, OSError): except (IOError, OSError):
val = sys.exc_info()[1] val = sys.exc_info()[1]
if val.errno == errno.ENOENT: if val.errno == errno.ENOENT:
@ -465,24 +609,34 @@ def verify_environment():
raise raise
def main(): def main():
# Force the stdout and stderr streams to be unbuffered
sys.stdout = Unbuffered(sys.stdout) sys.stdout = Unbuffered(sys.stdout)
sys.stderr = Unbuffered(sys.stderr) sys.stderr = Unbuffered(sys.stderr)
split_args, pids_to_show, watch, only_total = parse_options() split_args, pids_to_show, watch, only_total, discriminate_by_pid, \
verify_environment() show_swap = parse_options()
verify_environment(pids_to_show)
if not only_total: if not only_total:
print_header() print_header(show_swap, discriminate_by_pid)
if watch is not None: if watch is not None:
try: try:
sorted_cmds = True sorted_cmds = True
while sorted_cmds: while sorted_cmds:
sorted_cmds, shareds, count, total = get_memory_usage( pids_to_show, split_args ) sorted_cmds, shareds, count, total, swaps, total_swap = \
if only_total and have_pss: get_memory_usage(pids_to_show, split_args,
discriminate_by_pid)
if only_total and show_swap and have_swap_pss:
sys.stdout.write(human(total_swap, units=1)+'\n')
elif only_total and not show_swap and have_pss:
sys.stdout.write(human(total, units=1)+'\n') sys.stdout.write(human(total, units=1)+'\n')
elif not only_total: elif not only_total:
print_memory_usage(sorted_cmds, shareds, count, total) print_memory_usage(sorted_cmds, shareds, count, total,
swaps, total_swap, show_swap)
sys.stdout.flush()
time.sleep(watch) time.sleep(watch)
else: else:
sys.stdout.write('Process does not exist anymore.\n') sys.stdout.write('Process does not exist anymore.\n')
@ -490,18 +644,23 @@ def main():
pass pass
else: else:
# This is the default behavior # This is the default behavior
sorted_cmds, shareds, count, total = get_memory_usage( pids_to_show, split_args ) sorted_cmds, shareds, count, total, swaps, total_swap = \
if only_total and have_pss: get_memory_usage(pids_to_show, split_args,
discriminate_by_pid)
if only_total and show_swap and have_swap_pss:
sys.stdout.write(human(total_swap, units=1)+'\n')
elif only_total and not show_swap and have_pss:
sys.stdout.write(human(total, units=1)+'\n') sys.stdout.write(human(total, units=1)+'\n')
elif not only_total: elif not only_total:
print_memory_usage(sorted_cmds, shareds, count, total) print_memory_usage(sorted_cmds, shareds, count, total, swaps,
total_swap, show_swap)
# We must close explicitly, so that any EPIPE exception # We must close explicitly, so that any EPIPE exception
# is handled by our excepthook, rather than the default # is handled by our excepthook, rather than the default
# one which is reenabled after this script finishes. # one which is reenabled after this script finishes.
sys.stdout.close() sys.stdout.close()
vm_accuracy = shared_val_accuracy() ram_accuracy, swap_accuracy = val_accuracy( show_swap )
show_shared_val_accuracy( vm_accuracy, only_total ) show_val_accuracy( ram_accuracy, swap_accuracy, only_total, show_swap )
if __name__ == '__main__': main() if __name__ == '__main__': main()

@ -1,9 +1,9 @@
Name: ps_mem Name: ps_mem
Version: 3.6 Version: 3.14
Release: 16%{?dist} Release: 8%{?dist}
Summary: Memory profiling tool Summary: Memory profiling tool
License: LGPLv2 License: LGPL-2.1-only
URL: https://github.com/pixelb/ps_mem URL: https://github.com/pixelb/ps_mem
Source0: https://raw.githubusercontent.com/pixelb/ps_mem/c80287d/ps_mem.py Source0: https://raw.githubusercontent.com/pixelb/ps_mem/c80287d/ps_mem.py
@ -46,18 +46,35 @@ install -Dpm644 %{name}.1 %{buildroot}%{_mandir}/man1/%{name}.1
%changelog %changelog
* Wed Feb 02 2022 Jan Rybar <jrybar@redhat.com> - 3.6-16 * Mon Jun 24 2024 Troy Dawson <tdawson@redhat.com> - 3.14-8
- output is not redirected when ps_mem is killed - Bump release for June 2024 mass rebuild
- cmdline unwanted blank spaces fixed
- Resolves: rhbz#2033997 * Fri Jan 26 2024 Fedora Release Engineering <releng@fedoraproject.org> - 3.14-7
- Resolves: rhbz#2049743 - Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild
* Tue Aug 10 2021 Mohan Boddu <mboddu@redhat.com> - 3.6-15 * Sun Jan 21 2024 Fedora Release Engineering <releng@fedoraproject.org> - 3.14-6
- Rebuilt for IMA sigs, glibc 2.34, aarch64 flags - Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild
Related: rhbz#1991688
* Fri Jul 21 2023 Fedora Release Engineering <releng@fedoraproject.org> - 3.14-5
* Fri Apr 16 2021 Mohan Boddu <mboddu@redhat.com> - 3.6-14 - Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild
- Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937
* Mon Apr 24 2023 Lukáš Zaoral <lzaoral@redhat.com> - 3.14-4
- migrate to SPDX license format
* Fri Jan 20 2023 Fedora Release Engineering <releng@fedoraproject.org> - 3.14-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild
* Fri Jul 22 2022 Fedora Release Engineering <releng@fedoraproject.org> - 3.14-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild
* Mon Jul 11 2022 Pádraig Brady <P@draigBrady.com> - 3.14-1
- Latest upstream
* Fri Jan 21 2022 Fedora Release Engineering <releng@fedoraproject.org> - 3.6-15
- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild
* Fri Jul 23 2021 Fedora Release Engineering <releng@fedoraproject.org> - 3.6-14
- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild
* Wed Jan 27 2021 Fedora Release Engineering <releng@fedoraproject.org> - 3.6-13 * Wed Jan 27 2021 Fedora Release Engineering <releng@fedoraproject.org> - 3.6-13
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild - Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild

Loading…
Cancel
Save