#! /usr/bin/perl -w
# sysalive.pl 2005.03.12
# Randy Dunlap <rddunlap@osdl.org>
#
# Changelog:
# Add page_io/second, swap_io/second, ints/second,
# and context_switches/second;
# Also make $interval and $loopcount be int(value);
# Omit page and swap data in 2.6.x;
# Add some /proc/vmstat pieces;
#
# Prints some selected system stats for a quick indication of
#   system liveliness;
# Be small and take less CPU time (unlike top or vmstat);
# Hang around so that another fork() is not required;
#
# possibilities:
# - cmdline + version (1 time only) //
# - time;			//
# - cpuinfo summary:		TBD
# - interrupts summary:		TBD
# - loadavg pieces:		//
# 	load[last 1 min], load[last 5 min], load[last 15 min],
# 	nr_procs_in_readyq, nr_procs_total, last_pid_used
# - meminfo summary:		TBD
# - stat pieces:		//
# 	page: in, out | swap: in, out | intr: total_ints (ignore rest)
# 	ctxt: cntxt_sw | processes: total_forks
# - vmstat pieces:		TBD

my $prevpageio = 0;
my $prevswapio = 0;
my $prevints = 0;
my $prevctxsw = 0;

sub usage
{
	print "usage:  sysalive.pl [delay_in_seconds [loop_count]]\n";
	print "  defaults: delay = 1 and count = forever\n";
} # end usage

sub do_loadavg
{
	seek (LOADAVG, 0, 0) or die $!;
	my $loadavg;
	read (LOADAVG, $loadavg, 255);
	print "load[1, 5, 15], rdyq/ttl_procs, last_pid:  $loadavg";	# $loadavg w/ \n
} # end do_loadavg

sub do_stat
{
	my $skip_page_swap = 0;

	seek (STAT, 0, 0);	# to BOF
	while ($stat = <STAT>)		# STAT has multiple lines
	{
		chomp $stat;
		###print "raw stat: [$stat]\n";
		# split into words
		@words = split / +/, $stat;
		###print "raw words: [@words]\n";
		$tag = $words[0];
		if ($tag eq "intr")
		{
			$data = $words[1];	# use only first "intr" value
		}
		else
		{
			$data = join ($", @words[1..$#words]);
		}
		###print "raw tag [$tag], data [$data]\n";
		$stats{$tag} = $data;
		if ($tag eq "page")
		{
			$thispageio = $words[1] + $words[2];
		}
		if ($tag eq "swap")
		{
			$thisswapio = $words[1] + $words[2];
		}
	}
	if (defined($stats{'page'}))
	{
		print "page I/O: $stats{'page'} | swap I/O: $stats{'swap'} | intr_sum: $stats{'intr'}\n";
		print "ctxt_sw: $stats{'ctxt'} | forks: $stats{'processes'}\n";
	}
	else {
		print "intr_sum: $stats{'intr'} | ctxt_sw: $stats{'ctxt'} | forks: $stats{'processes'}\n";
		$skip_page_swap = 1;
	}

	if (defined($stats{'bounce_io'}))
	{
		if (defined($stats{'bounce_rw'}))
		{
			print "bounce I/O: $stats{'bounce_io'} | bounce R/W: $stats{'bounce_rw'} | bounce-swap I/O: $stats{'bounce_swap_io'}\n";
		}
		else
		{
			print "bounce I/O: $stats{'bounce_io'} | bounce-swap I/O: $stats{'bounce_swap_io'}\n";
		}
	}

	if (! $skip_page_swap)
	{
		$prtpgio = ($thispageio - $prevpageio) / $interval;
		$prtswio = ($thisswapio - $prevswapio) / $interval;
	}
	$prtints = ($stats{'intr'} - $prevints) / $interval;
	$prtctxt = ($stats{'ctxt'} - $prevctxsw) / $interval;
	if (! $skip_page_swap)
	{
		print "pgio/sec: $prtpgio | swio/sec: $prtswio | ints/sec: $prtints | ctsw/sec: $prtctxt\n";
	}
	else {
		print "ints/sec: $prtints | ctsw/sec: $prtctxt\n";
	}
	$prevpageio = $thispageio;
	$prevswapio = $thisswapio;
	$prevints = $stats{'intr'};
	$prevctxsw = $stats{'ctxt'};
} # end do_stat

sub do_meminfo
{
	seek (MEMINFO, 0, 0);	# to BOF
	###$header = <MEMINFO>;		# first line is header:
	###chomp $header;

	while ($meminfo = <MEMINFO>)	# MEMINFO has multiple lines
	{
		chomp $meminfo;
		###print "raw meminfo [$meminfo]\n";
		# split into words
		@words = split / +/, $meminfo;
		###print "raw words: [@words]\n";
		$tag = $words[0];
		if ($tag eq "")
		{
			shift @words;
			$tag = $words[0];
		}
		$data = join ($", @words[1..$#words]);
		###print "raw tag [$tag], data [$data]\n";
		$stats{$tag} = $data;
	}
	###print "$header\n";
	###print "Mem: $stats{'Mem:'}\n";
	###print "Swap: $stats{'Swap:'}  |  MemFree: $stats{'MemFree:'}\n";
	print "MemTotal/Free: $stats{'MemTotal:'}/$stats{'MemFree:'} | ";
	print "SwapTotal/Free: $stats{'SwapTotal:'}/$stats{'SwapFree:'}\n";
} # end do_meminfo

sub do_vmstat
{
} # end do_vmstat

###################### main ######################

$interval = 1;			# seconds to sleep between updates
if (scalar (@ARGV) > 0)
{
	$interval = int (shift (@ARGV));
	print "update interval changed from 1 second to $interval\n";
	if ($interval <= 0)
	{
		print "invalid interval: $interval\n";
		usage();
		exit 2;
	}
}

$loopcount = 0;			# number of loops to stay alive
if (scalar (@ARGV) > 0)
{
	$loopcount = int (shift (@ARGV));
	print "update loopcount changed from 0 to $loopcount\n";
	if ($loopcount <= 0)
	{
		print "invalid loopcount: $loopcount\n";
		usage();
		exit 2;
	}
}

# get /proc/cmdline and /proc/version one time only
open (PROCFILE, "</proc/cmdline") or die "cannot open /proc/cmdline\n";
$cmdline = <PROCFILE>;
chomp $cmdline;
close PROCFILE;

open (PROCFILE, "</proc/version") or die "cannot open /proc/version\n";
$version = <PROCFILE>;
chomp $version;
close PROCFILE;
 
open (LOADAVG, "</proc/loadavg") or die "cannot open /proc/loadavg\n";
open (STAT, "</proc/stat")       or die "cannot open /proc/stat\n";
open (MEMINFO, "</proc/meminfo") or die "cannot open /proc/meminfo\n";
 
$tm = localtime;
print "time: $tm\n";
print "cmdline: $cmdline\n";
print "version: $version\n";
print "==================================================\n";

# do until killed or loopcount times:
LOOPER:
while (1)
{
	$tm = localtime;
	print "time:  $tm\n";

	do_loadavg();			# /proc/loadavg file
	do_stat();			# /proc/stat file
	do_vmstat();			# /proc/vmstat file
	do_meminfo();			# /proc/meminfo file

	print "==================================================\n";
	sleep ($interval);

	if ($loopcount)			# is being used
	{
		$loopcount--;
		last LOOPER if $loopcount == 0;
	}
} # end forever/LOOPER


close (LOADAVG);
close (STAT);
close (MEMINFO);

exit 0;
# end sysalive.pl
