#!/usr/bin/perl
#
# fcachestat7 - filesystem cache statistics. Solaris 7.
#
# 21-Aug-2004	ver 0.95
#
# This program is to support older Solaris, before the perl Kstat library.
# See the Solaris 8 version of this program. "netstat -k" is used here.
#
# USAGE: fcachestat7 [-h] | [interval [count]]
#        fcachestat7		# print historic line only
#        fcachestat7 -h		# print help
#        fcachestat7 1 5	# print 5 x 1 second samples
#
# NOTE: The segvn field is a crude approximation. check for updates.
#
# SEE ALSO: dnlcstat, inodestat, ufsbufstat, segmapstat
#
# COPYRIGHT: Copyright (c) 2003, 2004 Brendan Gregg.
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software Foundation,
#  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
#  (http://www.gnu.org/copyleft/gpl.html)
#
# Author: Brendan Gregg  [Sydney, Australia]
#
# 11-Mar-2004	Brendan Gregg	Created this.


#
#  Process command line args
#
if ($ARGV[0] eq "-h" || $ARGV[0] eq "--help" || $ARGV[0] eq "0") { &usage(); }

$sleep = $ARGV[0];
$loop = $ARGV[1];
if ($sleep eq "") {
	$sleep = 0; $loop = 1; 
} elsif ($loop eq "") {
	$loop = 2**32;
}
$PAGESIZE = 22;				# max lines per header
$lines = $PAGESIZE;			# counter for lines printed


#
#  Main
#

while (1) {
	if ($lines++ >= $PAGESIZE) {
		$lines = 0;
		print "   --- dnlc ---      --- inode ---   " . 
		 "  -- ufs buf --     -- segmap --\n";
		print "   %hit    total  " x 4, "\n";
	}

	foreach $j (0..3) {
		($oldhit[$j],$oldmiss[$j]) = ($hit[$j],$miss[$j]);
		if ($j == 0) { ($hit[0],$miss[0]) = &fetch_dnlc(); }
		if ($j == 1) { ($hit[1],$miss[1]) = &fetch_inode(); }
		if ($j == 2) { ($hit[2],$miss[2]) = &fetch_ufsbuf(); }
		if ($j == 3) { ($hit[3],$miss[3]) = &fetch_segmap(); }
		$hits[$j] = $hit[$j] - $oldhit[$j];
		$misses[$j] = $miss[$j] - $oldmiss[$j];
	
		if ($hits[$j] > 0) { 
			$ratio = $hits[$j] / ($misses[$j] + $hits[$j]);
		} else {
			$ratio = 0;
		}
		$total = $hits[$j] + $misses[$j];
		if ($i == 0 && $total > 1_000_000) {
			# print in Millions for the summary since boot line,
			printf "%7.2f %7.1fM  ",$ratio * 100,$total / 1000000;
		} else {
			printf "%7.2f %8s  ",$ratio * 100,$total;
		}
	}

	print "\n";
	last if ++$i == $loop;
	sleep ($sleep);
}


#
#  Subroutines
#

# fetch - fetch current values for hits and misses.
#
sub fetch_dnlc {
	# old Solaris method for reading Kstat
	my $data = `netstat -k dnlcstats`;
	my ($hits) = $data =~ /\shits\s*(\S*)/;
	my ($misses) = $data =~ /\smisses\s*(\S*)/;
	return ($hits,$misses);
}
sub fetch_inode {
	# old Solaris method for reading Kstat
	my $data = `netstat -k inode_cache`;
	my ($hits) = $data =~ /\shits\s*(\S*)/;
	my ($misses) = $data =~ /\smisses\s*(\S*)/;
	return ($hits,$misses);
}
sub fetch_ufsbuf {
	# old Solaris method for reading Kstat
	my $data = `netstat -k biostats`;
	my ($lookups) = $data =~ /\sbuffer_cache_lookups\s*(\S*)/;
	my ($hits) = $data =~ /\sbuffer_cache_hits\s*(\S*)/;
	my $misses = $lookups - $hits;
	return ($hits,$misses);
}
sub fetch_segmap {
	my ($hits,$misses,$reclaim,$use,$map,$data);
	# old Solaris method for reading Kstat
	$data = `netstat -k segmap`;
	($map) = $data =~ /\sgetmap\s*(\S*)/;
	($use) = $data =~ /\sget_use\s*(\S*)/;
	($reclaim) = $data =~ /\sget_reclaim\s*(\S*)/;
	$hits = $use + $reclaim;
	$misses = $map - $hits;
	return ($hits,$misses);
}

# usage - print usage and exit.
#
sub usage {
        print STDERR <<END;
USAGE: $0 [-h] | [interval [count]]
   eg, $0		# print historic line only
       $0 1 5		# print 5 x 1 second samples
END
        exit 1;
}

