latest upstream (3.14)

This commit is contained in:
Pádraig Brady 2022-07-11 23:28:06 +01:00
parent 4191c54c0f
commit 0b05c4858f
3 changed files with 306 additions and 119 deletions

View File

@ -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.

405
ps_mem.py
View File

@ -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,12 @@
# 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
# The following exits cleanly on Ctrl-C or EPIPE # The following exits cleanly on Ctrl-C or EPIPE
# while treating other exceptions as before. # while treating other exceptions as before.
@ -99,6 +100,17 @@ 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):
def __init__(self, stream):
super(Unbuffered, self).__init__()
self.stream = stream
def write(self, data):
self.stream.write(data)
self.stream.flush()
def close(self):
self.stream.close()
class Proc: class Proc:
def __init__(self): def __init__(self):
@ -118,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
@ -132,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',
)
parser.add_argument(
'-t', '--total',
dest='only_total',
action='store_true',
help='Show only the total value',
)
parser.add_argument(
'-d', '--discriminate-by-pid',
action='store_true',
help='Show by process rather than by program',
)
parser.add_argument(
'-S', '--swap',
dest='show_swap',
action='store_true',
help='Show swap information',
)
parser.add_argument(
'-p',
dest='pids',
metavar='<pid>[,pid2,...pidN]',
help='Only show memory usage PIDs in the specified list',
)
parser.add_argument(
'-w',
dest='watch',
metavar='<N>',
type=int,
help='Measure and show process memory every N seconds',
)
args = parser.parse_args()
if len(args): args.pids_to_show = []
sys.stderr.write("Extraneous arguments: %s\n" % args) if args.pids:
sys.exit(3) try:
args.pids_to_show = [int(x) for x in args.pids.split(',')]
except ValueError:
parser.error('Invalid PID(s): %s' % args.pids)
# ps_mem.py options if args.watch is not None:
split_args = False if args.watch <= 0:
pids_to_show = None parser.error('Seconds must be positive! (%s)' % args.watch)
watch = None
only_total = False
for o, a in opts: return (
if o in ('-s', '--split-args'): args.split_args,
split_args = True args.pids_to_show,
if o in ('-t', '--total'): args.watch,
only_total = True args.only_total,
if o in ('-h', '--help'): args.discriminate_by_pid,
sys.stdout.write(help()) args.show_swap,
sys.exit(0) )
if o in ('-p',):
try:
pids_to_show = [int(x) for x in a.split(',')]
except:
sys.stderr.write(help())
sys.exit(3)
if o in ('-w',):
try:
watch = int(a)
except:
sys.stderr.write(help())
sys.exit(3)
return (split_args, pids_to_show, watch, only_total)
def help(): # (major,minor,release)
help_msg = 'Usage: ps_mem [OPTION]...\n' \
'Show program core memory usage\n' \
'\n' \
' -h, -help Show this help\n' \
' -p <pid>[,pid2,...pidN] Only show memory usage PIDs in the specified list\n' \
' -s, --split-args Show and separate by, all command line arguments\n' \
' -t, --total Show only the total value\n' \
' -w <N> Measure and show process memory every N seconds\n'
return help_msg
#(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)
@ -202,50 +226,84 @@ 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")
if cmdline[-1] == '' and len(cmdline) > 1: while cmdline[-1] == '' and len(cmdline) > 1:
cmdline = cmdline[:-1] cmdline = cmdline[:-1]
path = proc.path(pid, 'exe') path = proc.path(pid, 'exe')
@ -257,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):
@ -276,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
@ -309,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
) )
sys.stderr.close()
if only_total and possible_inacc != 2:
sys.exit(1)
def get_memory_usage( pids_to_show, split_args, include_self=False, only_self=False ): 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()
if only_total:
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, 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
@ -375,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
@ -387,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):
@ -397,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})
#Add shared mem for each program # 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
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:
@ -414,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(): def print_header(show_swap, discriminate_by_pid):
sys.stdout.write(" Private + Shared = RAM used\tProgram\n\n") 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_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:
@ -454,21 +609,34 @@ def verify_environment():
raise raise
def main(): def main():
split_args, pids_to_show, watch, only_total = parse_options() # Force the stdout and stderr streams to be unbuffered
verify_environment() sys.stdout = Unbuffered(sys.stdout)
sys.stderr = Unbuffered(sys.stderr)
split_args, pids_to_show, watch, only_total, discriminate_by_pid, \
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')
@ -476,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()

View File

@ -1,7 +1,7 @@
Name: ps_mem Name: ps_mem
Version: 3.6 Version: 3.14
Release: 15%{?dist} Release: 1%{?dist}
Summary: Memory profiling tool Summary: Memory profiling tool
License: LGPLv2 License: LGPLv2
URL: https://github.com/pixelb/ps_mem URL: https://github.com/pixelb/ps_mem
@ -46,6 +46,9 @@ install -Dpm644 %{name}.1 %{buildroot}%{_mandir}/man1/%{name}.1
%changelog %changelog
* 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 * Fri Jan 21 2022 Fedora Release Engineering <releng@fedoraproject.org> - 3.6-15
- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild - Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild