#!/usr/bin/ksh
#
# zvmstat - print vmstat style info per Zone.
# This uses DTrace (Solaris 10 3/05).
#
# This program must be run from the global zone as root.
#
# $Id: zvmstat 3 2007-08-01 10:50:08Z brendan $
#
# USAGE: zvmstat [-ht] [interval [count]]
#
# zvmstat # default output
# -t # print times
# eg,
# zvmstat 1 # print every 1 second
# zvmstat 10 5 # print 5 x 10 second samples
# zvmstat -t 5 # print every 5 seconds with time
#
#
# FIELDS:
# re page reclaims
# mf minor faults
# fr pages freed
# sr scan rate
# epi executable pages paged in
# epo executable pages paged out
# epf executable pages freed
# api anonymous pages paged in
# apo anonymous pages paged out
# apf anonymous pages freed
# fpi filesystem pages paged in
# fpo filesystem pages paged out
# fpf filesystem pages freed
#
# NOTES:
# - Zone status should really be provided by Kstat, which currently
# provides system wide values, per CPU and per processor set, but not per
# zone. DTrace can fill this role in the meantime until Kstat supports zones.
# - First output does not contain summary since boot.
#
# SEE ALSO: prstat -Z
#
# COPYRIGHT: Copyright (c) 2005 Brendan Gregg.
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License, Version 1.0 only
# (the "License"). You may not use this file except in compliance
# with the License.
#
# You can obtain a copy of the license at Docs/cddl1.txt
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# CDDL HEADER END
#
# BUGS:
# - First output may not contain all zones due to how loops are achieved.
# Check for newer versions.
#
# Author: Brendan Gregg [Sydney, Australia]
#
# 11-May-2005 Brendan Gregg Created this.
# 26-Jul-2005 " " Improved code.
# 08-Jan-2006 " " Last update.
#
##############################
# --- Process Arguments ---
#
### default variables
opt_time=0; interval=1; counts=1
### process options
while getopts ht name
do
case $name in
t) opt_time=1 ;;
h|?) cat <<-END >&2
USAGE: zvmstat [-ht] [interval [count]]
zvmstat # default output
-t # print times
eg,
zvmstat 1 # print every 1 second
zvmstat 10 5 # print 5 x 10 second samples
zvmstat -t 5 # print every 5 seconds with time
END
exit 1
esac
done
shift $(( OPTIND - 1 ))
### option logic
if (( "0$1" > 0 )); then
interval=$1; counts=-1; shift
fi
if (( "0$1" > 0 )); then
counts=$1; shift
fi
#################################
# --- Main Program, DTrace ---
#
dtrace -n '
#pragma D option quiet
#pragma D option destructive
#pragma D option switchrate=10
/*
* Command line arguments
*/
inline int OPT_time = '$opt_time';
inline int INTERVAL = '$interval';
inline int COUNTER = '$counts';
/*
* Initialise variables
*/
dtrace:::BEGIN
{
secs = INTERVAL;
counts = COUNTER;
zonemax = 0;
listing = 1;
re[""] = 0; pi[""] = 0; po[""] = 0;
mf[""] = 0; sr[""] = 0; fr[""] = 0;
epi[""] = 0; epo[""] = 0; epf[""] = 0;
api[""] = 0; apo[""] = 0; apf[""] = 0;
fpi[""] = 0; fpo[""] = 0; fpf[""] = 0;
}
/*
* Build zonelist array
*
* Here we want the output of a command to be saved into an array
* inside dtrace. This is done by running the command, sending the
* output to /dev/null, and by probing its write syscalls from dtrace.
*
* This is an example of a "scraper".
*/
/*
* List zones
*/
dtrace:::BEGIN
{
/* run zoneadm */
system("/usr/sbin/zoneadm list > /dev/null; echo END > /dev/null");
}
/*
* Scrape zone listing
*/
syscall::write:entry
/listing && (execname == "zoneadm") &&
(curthread->t_procp->p_parent->p_ppid == $pid)/
{
/* read zoneadm output */
zonelist[zonemax] = stringof(copyin(arg1, arg2 - 1));
/* increment max number of zones */
zonemax++;
}
/*
* Finish scraping zones
*/
syscall::write:entry
/listing && (execname == "sh") && (ppid == $pid)/
{
/*
* this end tag lets us know our zonelist has finished.
* thanks A. Packer.
*/
listing = stringof(copyin(arg1, arg2 - 1)) == "END" ? 0 : 1;
}
/*
* Record vminfo counters
*/
vminfo:::pgrec { re[zonename] += arg0; }
vminfo:::as_fault { mf[zonename] += arg0; }
vminfo:::scan { sr[zonename] += arg0; }
vminfo:::execpgin { epi[zonename] += arg0; }
vminfo:::execpgout { epo[zonename] += arg0; }
vminfo:::execfree { epf[zonename] += arg0; fr[zonename] += arg0; }
vminfo:::anonpgin { api[zonename] += arg0; }
vminfo:::anonpgout { apo[zonename] += arg0; }
vminfo:::anonfree { apf[zonename] += arg0; fr[zonename] += arg0; }
vminfo:::fspgin { fpi[zonename] += arg0; }
vminfo:::fspgout { fpo[zonename] += arg0; }
vminfo:::fsfree { fpf[zonename] += arg0; fr[zonename] += arg0; }
/*
* Timer
*/
profile:::tick-1sec
{
secs--;
}
/*
* Check for exit
*/
profile:::tick-1sec
/counts == 0/
{
exit(0);
}
/*
* Print header line
*/
profile:::tick-1sec
/secs == 0/
{
/* set counters */
secs = INTERVAL;
counts--;
zonei = 0;
/* print time */
OPT_time ? printf("\n%Y,\n",walltimestamp) : 1;
/* print output line */
printf("%10s %4s %5s %4s %5s %4s %4s %4s %4s %4s %4s %4s %4s %4s\n",
"ZONE", "re", "mf", "fr", "sr", "epi", "epo", "epf", "api", "apo",
"apf", "fpi", "fpo", "fpf");
/* ensure zone writes are triggered */
printf(" \b");
}
/*
* Print zone status line
*
* This is a fairly interesting function in that it loops over the keys in
* an associative array and prints out the values. DTrace cant really do
* loops, and generally doesnt need to. We "cheat" by generating writes
* in the above probe which in turn trigger the probe below which
* contains the contents of each loop. Dont do this at home! We are
* supposed to use aggreagations instead, wherever possible.
*
* This is an example of a "feedback loop".
*/
syscall::write:return
/pid == $pid && zonei < zonemax/
{
/* fetch zonename */
self->zone = zonelist[zonei];
/* print output */
printf("%10s %4d %5d %4d %5d %4d %4d %4d %4d %4d %4d %4d %4d %4d\n",
self->zone, re[self->zone], mf[self->zone], fr[self->zone],
sr[self->zone], epi[self->zone], epo[self->zone],
epf[self->zone], api[self->zone], apo[self->zone],
apf[self->zone], fpi[self->zone], fpo[self->zone],
fpf[self->zone]);
/* clear values */
re[self->zone] = 0; mf[self->zone] = 0; fr[self->zone] = 0;
sr[self->zone] = 0; epi[self->zone] = 0; epo[self->zone] = 0;
epf[self->zone] = 0; api[self->zone] = 0; apo[self->zone] = 0;
apf[self->zone] = 0; fpi[self->zone] = 0; fpo[self->zone] = 0;
fpf[self->zone] = 0;
self->zone = 0;
/* go to next zone */
zonei++;
}
'