Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ can be run directly.
Usage:

```
ps_mem [-h|--help] [-p PID,...] [-s|--split-args] [-t|--total] [-w N]
ps_mem [-h|--help] [-H [user@]host] [-p PID,...] [-s|--split-args] [-t|--total] [-w N]
[-d|--discriminate-by-pid] [-S|--swap]
```

Expand Down Expand Up @@ -48,3 +48,17 @@ for i in $(ps -e -o user= | sort | uniq); do
printf '%-20s%10s\n' $i $(sudo ps_mem --total -p $(pgrep -d, -u $i))
done
```

For embedded systems with no python on board with ssh access you could:

```sh
ps_mem.py -H root@192.168.31.95 -p 15106

Private + Shared = RAM used Program

78.4 MiB + 1.5 MiB = 79.9 MiB cobalt_loader

---------------------------------
79.9 MiB
=================================
```
3 changes: 3 additions & 0 deletions ps_mem.1
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ Show help message
\-\-version
Show version info
.TP
\-H [user@]host
Connect to the remote host via SSH (key-based auth is required)
.TP
\-p PID[,PID2,...PIDN]
Show memory consumption for the specified processes
.TP
Expand Down
71 changes: 62 additions & 9 deletions ps_mem.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,39 @@ def write(self, data):
def close(self):
self.stream.close()

class SSHProc:
def __init__(self, host, proc):
self.host = host
self.proc = proc

def path(self, *args):
return os.path.join(self.proc, *(str(a) for a in args))

def listdir(self, path):
import subprocess as sp

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These imports could be done once in init() I think

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

return sp.Popen(f"ssh {self.host} ls {path}".split(),
stdout=sp.PIPE, stdin=sp.DEVNULL, stderr=sp.DEVNULL,
encoding='utf-8').stdout.read().split()

def exists(self, path):
import subprocess as sp
return sp.Popen(f"ssh {self.host} test -f {path}".split(),
stdout=sp.DEVNULL, stdin=sp.DEVNULL, stderr=sp.DEVNULL,
encoding='utf-8').wait() == 0

def readlink(self, path):
import subprocess as sp
return sp.Popen(f"ssh {self.host} readlink -f {path}".split(),
stdout=sp.PIPE, stdin=sp.DEVNULL, stderr=sp.DEVNULL,
encoding='utf-8').stdout.read()

def open(self, *args):
import subprocess as sp
path = self.path(*args)
return sp.Popen(f"ssh {self.host} cat {path}".split(),
stdout=sp.PIPE, stdin=sp.DEVNULL, stderr=sp.DEVNULL,
encoding='utf-8').stdout

class Proc:
def __init__(self):
uname = os.uname()
Expand All @@ -120,6 +153,15 @@ def __init__(self):
else:
self.proc = '/proc'

def listdir(self, path):
return os.listdir(path)

def exists(self, path):
return os.path.exists(path)

def readlink(self, path):
return os.readlink(path)

def path(self, *args):
return os.path.join(self.proc, *(str(a) for a in args))

Expand All @@ -139,8 +181,6 @@ def open(self, *args):
raise LookupError
raise

proc = Proc()


#
# Functions
Expand All @@ -150,6 +190,12 @@ def parse_options():
help_msg = 'Show program core memory usage.'
parser = argparse.ArgumentParser(prog='ps_mem', description=help_msg)
parser.add_argument('--version', action='version', version='3.14')
parser.add_argument(
'-H', '--host',
dest='host',
action='store',
help='The host to SSH to',
)
parser.add_argument(
'-s', '--split-args',
action='store_true',
Expand Down Expand Up @@ -199,6 +245,7 @@ def parse_options():
parser.error('Seconds must be positive! (%s)' % args.watch)

return (
args.host,
args.split_args,
args.pids_to_show,
args.watch,
Expand Down Expand Up @@ -244,9 +291,9 @@ def getMemStats(pid):

Swap = 0

if os.path.exists(proc.path(pid, 'smaps')): # stat
if proc.exists(proc.path(pid, 'smaps')): # stat
smaps = 'smaps'
if os.path.exists(proc.path(pid, 'smaps_rollup')):
if proc.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
Expand Down Expand Up @@ -308,7 +355,7 @@ def getCmdName(pid, split_args, discriminate_by_pid, exe_only=False):

path = proc.path(pid, 'exe')
try:
path = os.readlink(path)
path = proc.readlink(path)
# Some symlink targets were seen to contain NULs on RHEL 5 at least
# https://github.com/pixelb/scripts/pull/10, so take string up to NUL
path = path.split('\0')[0]
Expand Down Expand Up @@ -408,7 +455,7 @@ def val_accuracy(show_swap):
return 1, swap_accuracy
return 0, swap_accuracy
elif kv[:2] == (2,6):
if os.path.exists(proc.path(pid, 'smaps')):
if proc.exists(proc.path(pid, 'smaps')):
swap_accuracy = 1
if proc.open(pid, 'smaps').read().find("Pss:")!=-1:
return 2, swap_accuracy
Expand All @@ -417,7 +464,7 @@ def val_accuracy(show_swap):
if (2,6,1) <= kv <= (2,6,9):
return -1, swap_accuracy
return 0, swap_accuracy
elif kv[0] > 2 and os.path.exists(proc.path(pid, 'smaps')):
elif kv[0] > 2 and proc.exists(proc.path(pid, 'smaps')):
swap_accuracy = 1
if show_swap and proc.open(pid, 'smaps').read().find("SwapPss:")!=-1:
swap_accuracy = 2
Expand Down Expand Up @@ -482,7 +529,7 @@ def get_memory_usage(pids_to_show, split_args, discriminate_by_pid,
mem_ids = {}
count = {}
swaps = {}
for pid in os.listdir(proc.path('')):
for pid in proc.listdir(proc.path('')):
if not pid.isdigit():
continue
pid = int(pid)
Expand Down Expand Up @@ -613,9 +660,15 @@ def main():
sys.stdout = Unbuffered(sys.stdout)
sys.stderr = Unbuffered(sys.stderr)

split_args, pids_to_show, watch, only_total, discriminate_by_pid, \
host, split_args, pids_to_show, watch, only_total, discriminate_by_pid, \
show_swap = parse_options()

global proc
if host:
proc = SSHProc(host, "/proc")
else:
proc = Proc()

verify_environment(pids_to_show)

if not only_total:
Expand Down