Get swap usage per process

A few months ago, I had the need to check what the swap usage was on my Linux system.

(Update 2014-10-26: This can also be checked with the excellent smem tool)

I encountered a little bash script by a guy called Erik Ljungstrom, who had written a small script that iterates through the /proc system, takes the name for the PID and adds up the swap space.

Here’s the original script.

#!/bin/bash
# Get current swap usage for all running processes
# Erik Ljungstrom 27/05/2011
SUM=0
OVERALL=0
for DIR in `find /proc/ -maxdepth 1 -type d | egrep "^/proc/[0-9]"` 
do
  PID=`echo $DIR | cut -d / -f 3`
  PROGNAME=`ps -p $PID -o comm --no-headers`
  for SWAP in `grep Swap $DIR/smaps 2>/dev/null| awk '{ print $2 }'`
  do
    let SUM=$SUM+$SWAP
  done
  echo "PID=$PID - Swap used: $SUM - ($PROGNAME )"
  let OVERALL=$OVERALL+$SUM
  SUM=0
done
echo "Overall swap used: $OVERALL"

It’s not the fasted script, but it does its job, and you can do a

# ./getswap.sh | sort -n -k 5

to have the values sorted in ascending order.

And as it was a free day, I needed to brush up some basic Perl skillz again.

Here’s the result:

#!/usr/bin/perl -w
#
# Get current swap usage for all running processes
#
# Based on a bash script by Erik Ljungstrom 27/05/2011
#
# Author = Christopher Ranschaert - 07/07/2012 
#
# Parses pids in /proc, collects the name from /proc/$pid/comm and 
# calculates the swapsize from /proc/$pid/smaps
# 
# Warning: Needs root permissions to read swap values, will otherwise return 0.
# If you don't trust this, learn some Perl and check the code :-)
# Something I'd suggest you to do anyway!
use strict;
my $overall = 0;
my $dirname = "/proc";
my %names = ();
my %swaps = ();
my $processname = '';
my $pidnumber = '';
my $swapsizenumber = '';
# Open /proc directory and parse for PIDs and store them in hash.
opendir(DIR, $dirname) or die "Couldn't open $dirname: $!";
while ( defined ( my $pid = readdir DIR )){
  next unless $pid =~ /^\d+$/;  # PIDs are digits only, skip the rest
  if (-d "$dirname/$pid") {
    $names{$pid} = &read_processname_from($pid);
    $swaps{$pid} = &get_swapsize_from($pid);
  }
}
closedir(DIR);

format STDOUT =
@<<<<<< | @<<<<< | @<<<<<<<<<<<<<<< |
$pidnumber,$swapsizenumber,$processname
.

print "-------------------------------------\n";
print "Processes and their swapsizes\n";
print "-------------------------------------\n";
print " PID | SWAP | PROCESSNAME |\n";
print "-------------------------------------\n";
my @pids = sort { $swaps{$a} <=> $swaps{$b} } (keys %swaps);
foreach my $pid ( @pids ){
  $pidnumber = $pid;
  $processname = $names{$pid};
  $swapsizenumber = $swaps{$pid};
  $overall += $swapsizenumber;
  write(STDOUT);
}
print "-------------------------------------\n";
print "Overall Swap used: $overall KiB\n";
exit 0;
sub read_processname_from {
  my $pid = shift;
  my $procnamefile = "$dirname/$pid/comm";
  open (FH, $procnamefile) or die "Can't open $procnamefile: $!";
  my $procname = <FH>;
  chomp $procname;
  close(FH);
  return $procname;
}
sub get_swapsize_from {
  my $pid = shift;
  my $sum = 0;
  my $smapsfile = "$dirname/$pid/smaps";
  open (FH, $smapsfile) or die "Can't open $smapsfile: $!";
  while (<FH>){
    next unless /^Swap/;
    $_ =~ m/^Swap:\s+(\d+)\s+kB*$/;
    my $swapsize = $1;
    $sum += $swapsize;
 }
 close (FH);
 return $sum;
}

It already includes sorting so the extra sort command is no longer needed.

Output sample:

# ./getswap.pl
-------------------------------------
Processes and their swapsizes
-------------------------------------
 PID  | SWAP | PROCESSNAME          |
-------------------------------------
27007 | 0    | migration/2          |
...
429   | 832  | udevd                |
3169  | 832  | puppet               |
3327  | 1048 | sensord              |
2382  | 1800 | colord               |
-------------------------------------
Overall Swap used: 24072 KiB

In case you find it interesting, you can also find the script itself at my bitbucket repository:

https://bitbucket.org/haploc/perlscripts/src/ecd4ad1e974a/getswap.pl

Feel free to leave comments on how it sucks, but do inform me of your great improvements!