Reporting of cache/swap usage per process - decreasing sort

If you want a report of cache/swap usage on a per process basis, this script will give it to you.

The script implemented an approach which was an attempt to get longer/fuller strings for commands, but it appears that the kernel is cutting those off (arbitrarily ?) at 15 characters. :frowning:

----- edit -----
I wanted to give credit to Eugene (@ugnvs) for being the one who planted the seed by offering this reference. :slight_smile:
----- end of edit -----

----- edit 2 -----
I've posted another response (#4 in chain) with an improved reporting overcoming the above-mentionned limitation on process description string. :slight_smile:
----- end of edit -----

OS_Admin__IdentifyProcessUsingCache.sh

#!/bin/bash

###
###	Script to report cache per process in decreasing usage
###

# Concept REF:	https://www.cyberciti.biz/faq/linux-which-process-is-using-swap/

harvestDetails()
{
	awk 'BEGIN{
		swap="" ;
	}{
		if( $1 ~ /^Command:/ ){
			nam=$2 ;
		} ;	
		if( $1 ~ /^VmSwap:/ ){
			swap=$2 ;
			scal=$3 ;
		} ;	
	}END{
		if( swap != "" ){
			printf("%s|%s|%s\n", nam, swap, scal ) ;
		} ;
	}'
}



reportDetails()
{
	awk -v dbg=${debug} 'BEGIN{
		split("", procdat) ;
		indx=0
		longest=0 ;
	}{
		n=split( $0, inp, "|" ) ;

		if( n  > 0 ){
			indx++ ;
			procdat[indx,1]=inp[1] ;
			procdat[indx,2]=inp[2] ;
			procdat[indx,3]=inp[3] ;
			len=length(inp[1]) ;
			if( len > longest ){
				longest=len ;
				if( dbg == 1 ){ printf("%3d  %s   BUMP\n", len, inp[1] ) | "cat 1>&2" ; } ;
			}else{
				if( dbg == 1 ){ printf("%3d  %s\n", len, inp[1] ) | "cat 1>&2" ; } ;
			} ;
		} ;
	}END{
	if( dbg == 1 ){ printf("\n\t longest = %d\n", longest ) ; } ;

		for( i=1 ; i <=indx-1 ; i++ ){
			for( j=i+1 ; j <=indx ; j++ ){
				if( procdat[j,2] > procdat[i,2] ){
					if( dbg == 1 ){ printf("\t switch:   %d -> %d\n", j, i ) | "cat 1>&2" ; } ;

					t1=procdat[i,1] ;
					t2=procdat[i,2] ;
					t3=procdat[i,3] ;

					procdat[i,1]=procdat[j,1] ;
					procdat[i,2]=procdat[j,2] ;
					procdat[i,3]=procdat[j,3] ;

					procdat[j,1]=t1;
					procdat[j,2]=t2;
					procdat[j,3]=t3;
				} ;
			} ;
		} ;

		fmt=sprintf("%s%s%s", "%-", longest, "s %s %s\n" ) ;
		if( dbg == 1 ){ printf("\t fmt = %s\n", fmt ) | "cat 1>&2" ; } ;
		for( i=1 ; i <=indx ; i++ ){
			printf(fmt, procdat[i,1], procdat[i,2], procdat[i,3] ) ;
		} ;
	}'
}


ls /proc/*/status |
while read file
do
	{
	cat $( dirname ${file} )/comm 2>>/dev/null | awk '{ if( $0 != "" ){ printf("Command: %s\n", $0 ) ; } ; }'
	cat ${file} 2>>/dev/null
	} | harvestDetails
done | reportDetails

exit
5 Likes

Have I ever mentioned that I prefer Python scripting? Just for our amusement here is nearly same script in Python 3.10:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Get swap usage (sorted)
# by ugnvs 4 Ubuntu Mate forum 

import subprocess

res = subprocess.run(['ps', '-eo', 'comm,pid'], capture_output=True)
src = res.stdout.decode('utf-8').splitlines()
print(src[0], 'SWAP (kB)') # title
del(src[0])
tgt = []
for line in src:
    res = subprocess.run(['grep', 'VmSwap', f'/proc/{line.split()[1]}/status'],capture_output=True)
    if res.stdout:
        tgt.append((line, res.stdout.decode('utf-8').split()[1]))

tgt.sort(key = lambda item: int(item[1])) 

for line in tgt:
    print(line[0], line[1])

# EOF
5 Likes

I think I'll join the good party here :slight_smile:

#!/bin/bash

grep VmSwap /proc/*/status |
while IFS=':' read pid _ vmswap
do
	echo "$(< "${pid/status/comm}" ) $vmswap"
done | column -t
5 Likes

Devised an approach to incorporate "proper" fuller reporting of the process identification string, by combining results of the "ps -columns 256 -axo pid,args" command into the previous version of my script.

Sample of output:

   PID    Swap     Process
  6558   10624 kB  /usr/lib/x86_64-linux-gnu/brisk-menu/brisk-menu
  5642    9728 kB  /usr/bin/python3 /usr/bin/glances
  6556    7680 kB  /usr/lib/mate-applets/trashapplet
  6554    5248 kB  /usr/lib/mate-panel/wnck-applet
  4665    4864 kB  /usr/bin/python3 /usr/bin/networkd-dispatcher
  6629    4480 kB  /usr/lib/mate-applets/mate-cpufreq-applet
  6638    3968 kB  /usr/lib/mate-applets/mate-multiload-applet
  5662    3840 kB  /usr/bin/python3 /usr/share/unattended-upgrades/unattended-upgrade-shutdown
  6161    3456 kB  /usr/bin/pulseaudio
  6828    3456 kB  /usr/libexec/xdg-desktop-portal-gtk
  6925    3200 kB  /usr/libexec/gvfsd-http
  6632    2688 kB  /usr/lib/mate-sensors-applet/mate-sensors-applet
  5639    2304 kB  /usr/sbin/cupsd
  6151    1920 kB  (sd-pam)
  3238    1920 kB  /lib/systemd/systemd-resolved
  6678    1792 kB  /usr/lib/mate-panel/notification-area-applet
  6851    1536 kB  /usr/libexec/evolution-calendar-factory
  6803    1408 kB  /usr/libexec/evolution-source-registry
  6875    1280 kB  /usr/libexec/evolution-addressbook-factory
  4668    1152 kB  /usr/sbin/rsyslogd
  5842    1152 kB  /usr/sbin/cups-browsed
  6674    1152 kB  /usr/lib/mate-panel/clock-applet
  4687     896 kB  /usr/sbin/smartd
  6791     896 kB  /usr/libexec/xdg-desktop-portal
  6159     896 kB  /usr/bin/pipewire
  6160     896 kB  /usr/bin/pipewire-media-session
  5847     896 kB  /usr/sbin/apache2
  6421     896 kB  /usr/libexec/at-spi-bus-launcher
  5852     896 kB  /usr/sbin/apache2
  6676     768 kB  /usr/lib/mate-indicator-applet/mate-indicator-applet-complete
  6516     768 kB  /usr/libexec/gvfs-udisks2-volume-monitor
  6521     768 kB  /usr/libexec/gvfs-afc-volume-monitor
  5971     768 kB  /usr/libexec/upowerd
  6530     640 kB  /usr/libexec/gvfs-gphoto2-volume-monitor
  6412     640 kB  /usr/libexec/gvfsd-fuse /run/user/1000/gvfs
  6658     640 kB  /usr/libexec/gvfsd-trash
  4756     640 kB  /usr/libexec/udisks2/udisksd
  3247     640 kB  /lib/systemd/systemd-timesyncd
  4753     640 kB  /lib/systemd/systemd-logind
  6933     640 kB  /usr/lib/x86_64-linux-gnu/libproxy/0.4.17/pxgsettings org.gnome.system.proxy org.gnome.system.proxy.http org.gnome.system.proxy.https org.gnome.system.proxy.ftp org.gnome.system.proxy.socks
  6436     512 kB  /usr/libexec/dconf-service
  6845     512 kB  /usr/lib/x86_64-linux-gnu/libproxy/0.4.17/pxgsettings org.gnome.system.proxy org.gnome.system.proxy.http org.gnome.system.proxy.https org.gnome.system.proxy.ftp org.gnome.system.proxy.socks
  5841     512 kB  /usr/lib/cups/notifier/dbus dbus://
  6817     384 kB  /usr/libexec/xdg-permission-store
  6149     384 kB  /lib/systemd/systemd
  6526     384 kB  /usr/libexec/gvfs-mtp-volume-monitor
     1     384 kB  /sbin/init verbose splash
  6448     384 kB  /usr/libexec/at-spi2-registryd
  4667     384 kB  /usr/libexec/polkitd
  6805     384 kB  /usr/libexec/gvfsd-metadata
  5698     256 kB  /usr/sbin/kerneloops
  5700     256 kB  /usr/sbin/kerneloops
  6427     256 kB  /usr/bin/dbus-daemon
  6168     256 kB  /usr/bin/dbus-daemon
  6534     256 kB  /usr/libexec/gvfs-goa-volume-monitor
  6407     256 kB  /usr/libexec/gvfsd
  6811     256 kB  /usr/libexec/xdg-document-portal
  4773     128 kB  avahi-daemon: chroot helper
  4608     128 kB  @dbus-daemon
  4658     128 kB  /usr/sbin/irqbalance
  4697     128 kB  /usr/bin/spacenavd
  5918     128 kB  /usr/libexec/rtkit-daemon
  5673     128 kB  /usr/sbin/lightdm
  4601     128 kB  /usr/sbin/acpid
  4606     128 kB  avahi-daemon: running [OasisMega1.local]
  5702     128 kB  /usr/bin/netserver
  5776     128 kB  /sbin/agetty
  5840     128 kB  /usr/lib/cups/notifier/dbus dbus://
 63693       0 kB  /usr/lib/firefox/firefox-bin
  5659       0 kB  /bin/sh /snap/cups/1058/scripts/run-cups-browsed
  5660       0 kB  /bin/sh /snap/cups/1058/scripts/run-cupsd
 10644       0 kB  gvim OS_Admin__IdentifyProcessUsingCache.sh
   357       0 kB  /lib/systemd/systemd-journald
 37908       0 kB  /usr/libexec/fwupd/fwupd
  6442       0 kB  /usr/bin/mate-settings-daemon
   421       0 kB  /lib/systemd/systemd-udevd
  6486       0 kB  marco
  6494       0 kB  mate-panel
  6515       0 kB  /usr/bin/caja
  4696       0 kB  /usr/lib/snapd/snapd
  5774       0 kB  /usr/lib/xorg/Xorg
 11398       0 kB  bash
  5805       0 kB  cupsd
  5806       0 kB  cups-proxyd /var/snap/cups/common/run/cups.sock /run/cups/cups.sock
  6543       0 kB  /usr/bin/python3 /usr/bin/smart-notifier
  6550       0 kB  mate-maximus
  5813       0 kB  /bin/sh /snap/cups/1058/scripts/run-cups-browsed
  5820       0 kB  /usr/libexec/colord
  4750       0 kB  /usr/libexec/accounts-daemon
  6573       0 kB  /usr/libexec/ayatana-indicator-printers/ayatana-indicator-printers-service
  6588       0 kB  /usr/libexec/ayatana-indicator-sound/ayatana-indicator-sound-service
  6601       0 kB  /usr/libexec/ayatana-indicator-session/ayatana-indicator-session-service
  6602       0 kB  nm-applet
  6624       0 kB  /usr/lib/x86_64-linux-gnu/polkit-mate/polkit-mate-authentication-agent-1
  6626       0 kB  mate-power-manager
  4751       0 kB  /usr/sbin/cron
 11691       0 kB  gvim OS_Admin__ShowInstalledDistroVersion.sh
 10212       0 kB  /usr/libexec/gvfsd-network
  6649       0 kB  /usr/libexec/ayatana-indicator-messages/ayatana-indicator-messages-service
  4757       0 kB  /sbin/wpa_supplicant
  4609       0 kB  /usr/sbin/NetworkManager
   487       0 kB  /lib/systemd/systemd-networkd
  6101       0 kB  lightdm
  6696       0 kB  /usr/libexec/ayatana-indicator-application/ayatana-indicator-application-service
  6697       0 kB  /usr/libexec/geoclue-2.0/demos/agent
  6719       0 kB  /usr/libexec/ayatana-indicator-datetime/ayatana-indicator-datetime-service
  6728       0 kB  /usr/libexec/ayatana-indicator-notifications/ayatana-indicator-notifications-service
  6751       0 kB  /usr/bin/mate-screensaver
  5434       0 kB  /usr/sbin/ModemManager
  5495       0 kB  /usr/bin/atop
  5638       0 kB  /usr/bin/freshclam
  4633       0 kB  /usr/sbin/atopacctd
  5640       0 kB  /usr/sbin/dnscrypt-proxy
  6824       0 kB  fusermount3
  6164       0 kB  /usr/bin/gnome-keyring-daemon
  6844       0 kB  sh
 10230       0 kB  /usr/libexec/gvfsd-dnssd
  6170       0 kB  mate-session
 63647       0 kB  /usr/lib/firefox/firefox-bin
 63681       0 kB  /usr/lib/firefox/firefox-bin
  6932       0 kB  sh
 63687       0 kB  /usr/lib/firefox/firefox-bin
  6945       0 kB  /usr/bin/mate-terminal
  6981       0 kB  dbus-launch
  6982       0 kB  /usr/bin/dbus-daemon
  7010       0 kB  /usr/libexec/dconf-service
  7013       0 kB  bash
  7306       0 kB  /usr/lib/thunderbird/thunderbird
  7430       0 kB  /usr/lib/thunderbird/thunderbird
  7492       0 kB  firefox
  7637       0 kB  /usr/lib/firefox/firefox-bin
  7639       0 kB  /usr/lib/firefox/firefox-bin
  7652       0 kB  /usr/lib/firefox/firefox-bin
 76716       0 kB  sleep 3600
  7731       0 kB  /usr/lib/firefox/firefox-bin
  7790       0 kB  /usr/lib/firefox/firefox-bin
  7802       0 kB  /usr/lib/firefox/firefox-bin
 80492       0 kB  /bin/bash ./OS_Admin__IdentifyProcessUsingCache.sh
 80499       0 kB  OS_Admin__Ident
 80500       0 kB  OS_Admin__Ident
  9074       0 kB  /usr/lib/firefox/firefox-bin
   907       0 kB  /sbin/dhclient
  9547       0 kB  /usr/lib/firefox/firefox-bin
 82307       0 kB  cat
 82314       0 kB  cat

Modified script version of the script follows ...

"OS_Admin__IdentifyProcessUsingCache.sh :

#!/bin/bash

###
###	Script to report cache per process in decreasing usage
###

# Concept REF:	https://www.cyberciti.biz/faq/linux-which-process-is-using-swap/

###	Version 2 - Extra logic for fuller process identification string

harvestDetails()
{
	awk -v pFile="${base}.procs" 'BEGIN{
		swap="" ;
	}{
		if( $1 ~ /^Command:/ ){
			nam=$2 ;
		} ;	
		if( $1 ~ /^Pid:/ ){
			pid=$2 ;
		} ;	
		if( $1 ~ /^VmSwap:/ ){
			swap=$2 ;
			scal=$3 ;
		} ;	
	}END{
		if( swap != "" ){
			printf("%s|%s|%s|%d\n", nam, swap, scal, pid ) ;
		} ;
	}'
}



reportDetails()
{
	awk -v dbg=${debug} -v pFile="${base}.procs" 'BEGIN{
		split("", procdat) ;
		indx=0
		lgstC=0 ;

		split("", pdat) ;
		pdex=0 ;
		abandon=0 ;
		do {
			if( ( getline aLine <pFile ) > 0 ){
				if( dbg == 1 ){ printf("aLine = |%s|\n", aLine ) | "cat 1>&2" ; } ;
				if( aLine != "" ){
				pdex++ ;

				posC=index( aLine, " -" ) ;
				if( posC > 0 ){
					beg=substr( aLine, 1, posC-1 ) ;
				}else{
					beg=aLine ;
				} ;

				posP=index( beg, " " ) ;
				pdat[pdex,1]=substr( beg, 1, posP-1 ) ;
				pdat[pdex,2]=substr( beg, posP+1 ) ;

				if( dbg == 1 ){ printf("proc = %d   args = |%s|\n", pdat[pdex,1], pdat[pdex,2] ) | "cat 1>&2" ; } ;
				abandon=0 ;
				} ;
			}else{
				abandon=1 ;
			} ;
		} while( abandon == 0 ) ;

		if( dbg == 1 ){
			print "" ;
			for( i=1 ; i <= pdex ; i++ ){
				printf("%d  %s\n", pdat[i,1], pdat[i,2] ) | "cat 1>&2" ;
			} ;
		} ;
	}{
		n=split( $0, inp, "|" ) ;

		if( n  > 0 ){
			indx++ ;
			procdat[indx,1]=inp[1] ;
			procdat[indx,2]=inp[2] ;
			procdat[indx,3]=inp[3] ;
			procdat[indx,4]=inp[4] ;

			### substitute short comm with long comm
			for( j=1 ; j <= pdex ; j++ ){
				if( pdat[j,1] == procdat[indx,4] ){
					procdat[indx,1]=pdat[j,2] ;
					break
				} ;
			} ;

			len=length(procdat[indx,1]) ;
			if( len > lgstC ){
				lgstC=len ;
				if( dbg == 1 ){ printf("%3d  %s   BUMP\n", len, inp[1] ) | "cat 1>&2" ; } ;
			}else{
				if( dbg == 1 ){ printf("%3d  %s\n", len, inp[1] ) | "cat 1>&2" ; } ;
			} ;
		} ;
	}END{
	if( dbg == 1 ){ printf("\n\t lgstC = %d\n", lgstC ) ; } ;

		for( i=1 ; i <=indx-1 ; i++ ){
			for( j=i+1 ; j <=indx ; j++ ){
				if( procdat[j,2] > procdat[i,2] ){
					if( dbg == 1 ){ printf("\t switch:   %d -> %d\n", j, i ) | "cat 1>&2" ; } ;

					t1=procdat[i,1] ;
					t2=procdat[i,2] ;
					t3=procdat[i,3] ;
					t4=procdat[i,4] ;

					procdat[i,1]=procdat[j,1] ;
					procdat[i,2]=procdat[j,2] ;
					procdat[i,3]=procdat[j,3] ;
					procdat[i,4]=procdat[j,4] ;

					procdat[j,1]=t1;
					procdat[j,2]=t2;
					procdat[j,3]=t3;
					procdat[j,4]=t4;
				} ;
			} ;
		} ;

		lgstP=length( sprintf("%s\n", procdat[1,2]) ) ;
		if( dbg == 1 ){ printf("lgstP = %d\n", lgstP ) | "cat 1>&2" ; } ;

		fmt=sprintf("%s%s%s", "%6d  %", lgstP, "d %s  %s\n" ) ;
		if( dbg == 1 ){ printf("\t fmt = %s\n", fmt ) | "cat 1>&2" ; } ;

		fmt2=sprintf("%s%s%s", "%6s  %", lgstP, "s %2s  %s\n" ) ;
		printf(fmt2, "PID", "Swap", "", "Process" ) ;

		for( i=1 ; i <=indx ; i++ ){
			printf(fmt, procdat[i,4], procdat[i,2], procdat[i,3], procdat[i,1] ) ;
		} ;
	}'
}

base=$(basename "$0" ".sh" )

ps --columns 256 -eo pid,args | tail -n +2 | sed 's+^[ ]*++' | sed 's+[ ]*++' >"${base}.procs"

ls /proc/*/status |
while read file
do
	{
	cat $( dirname ${file} )/comm 2>>/dev/null | awk '{ if( $0 != "" ){ printf("Command: %s\n", $0 ) ; } ; }'
	cat ${file} 2>>/dev/null
	} | harvestDetails
done | reportDetails

exit

Enjoy! :slight_smile:

2 Likes