1235368Sgnn#!/usr/bin/ksh
2235368Sgnn#
3235368Sgnn# zvmstat - print vmstat style info per Zone.
4235368Sgnn#           This uses DTrace (Solaris 10 3/05).
5235368Sgnn#
6235368Sgnn# This program must be run from the global zone as root.
7235368Sgnn#
8235368Sgnn# $Id: zvmstat 3 2007-08-01 10:50:08Z brendan $
9235368Sgnn#
10235368Sgnn# USAGE: 	zvmstat [-ht] [interval [count]]
11235368Sgnn#
12235368Sgnn#		zvmstat         # default output
13235368Sgnn#			-t      # print times
14235368Sgnn#  eg,
15235368Sgnn#		zvmstat 1       # print every 1 second
16235368Sgnn#		zvmstat 10 5    # print 5 x 10 second samples
17235368Sgnn#		zvmstat -t 5    # print every 5 seconds with time
18235368Sgnn#
19235368Sgnn#
20235368Sgnn# FIELDS:
21235368Sgnn#		re		page reclaims
22235368Sgnn#		mf		minor faults
23235368Sgnn#		fr		pages freed
24235368Sgnn#		sr		scan rate
25235368Sgnn#		epi		executable pages paged in
26235368Sgnn#		epo		executable pages paged out
27235368Sgnn#		epf		executable pages freed
28235368Sgnn#		api		anonymous pages paged in
29235368Sgnn#		apo		anonymous pages paged out
30235368Sgnn#		apf		anonymous pages freed
31235368Sgnn#		fpi		filesystem pages paged in
32235368Sgnn#		fpo		filesystem pages paged out
33235368Sgnn#		fpf		filesystem pages freed
34235368Sgnn#
35235368Sgnn# NOTES: 
36235368Sgnn# - Zone status should really be provided by Kstat, which currently
37235368Sgnn#   provides system wide values, per CPU and per processor set, but not per
38235368Sgnn#   zone. DTrace can fill this role in the meantime until Kstat supports zones.
39235368Sgnn# - First output does not contain summary since boot.
40235368Sgnn#
41235368Sgnn# SEE ALSO: prstat -Z
42235368Sgnn#
43235368Sgnn# COPYRIGHT: Copyright (c) 2005 Brendan Gregg.
44235368Sgnn#
45235368Sgnn# CDDL HEADER START
46235368Sgnn#
47235368Sgnn#  The contents of this file are subject to the terms of the
48235368Sgnn#  Common Development and Distribution License, Version 1.0 only
49235368Sgnn#  (the "License").  You may not use this file except in compliance
50235368Sgnn#  with the License.
51235368Sgnn#
52235368Sgnn#  You can obtain a copy of the license at Docs/cddl1.txt
53235368Sgnn#  or http://www.opensolaris.org/os/licensing.
54235368Sgnn#  See the License for the specific language governing permissions
55235368Sgnn#  and limitations under the License.
56235368Sgnn#
57235368Sgnn# CDDL HEADER END
58235368Sgnn#
59235368Sgnn# BUGS:
60235368Sgnn# - First output may not contain all zones due to how loops are achieved.
61235368Sgnn#   Check for newer versions.
62235368Sgnn#
63235368Sgnn# Author: Brendan Gregg  [Sydney, Australia]
64235368Sgnn#
65235368Sgnn# 11-May-2005   Brendan Gregg   Created this.
66235368Sgnn# 26-Jul-2005	   "      "	Improved code.
67235368Sgnn# 08-Jan-2006	   "      "	Last update.
68235368Sgnn#
69235368Sgnn
70235368Sgnn
71235368Sgnn##############################
72235368Sgnn# --- Process Arguments ---
73235368Sgnn#
74235368Sgnn
75235368Sgnn### default variables
76235368Sgnnopt_time=0; interval=1; counts=1
77235368Sgnn
78235368Sgnn### process options
79235368Sgnnwhile getopts ht name
80235368Sgnndo
81235368Sgnn	case $name in
82235368Sgnn	t)      opt_time=1 ;;
83235368Sgnn	h|?)    cat <<-END >&2
84235368Sgnn		USAGE: zvmstat [-ht] [interval [count]]
85235368Sgnn		       zvmstat         # default output
86235368Sgnn		               -t      # print times
87235368Sgnn		   eg,
88235368Sgnn		       zvmstat 1       # print every 1 second
89235368Sgnn		       zvmstat 10 5    # print 5 x 10 second samples
90235368Sgnn		       zvmstat -t 5    # print every 5 seconds with time
91235368Sgnn		END
92235368Sgnn		exit 1
93235368Sgnn	esac
94235368Sgnndone
95235368Sgnnshift $(( OPTIND - 1 ))
96235368Sgnn
97235368Sgnn### option logic
98235368Sgnnif (( "0$1" > 0 )); then
99235368Sgnn        interval=$1; counts=-1; shift
100235368Sgnnfi
101235368Sgnnif (( "0$1" > 0 )); then
102235368Sgnn        counts=$1; shift
103235368Sgnnfi
104235368Sgnn
105235368Sgnn
106235368Sgnn#################################
107235368Sgnn# --- Main Program, DTrace ---
108235368Sgnn#
109235368Sgnndtrace -n '
110235368Sgnn #pragma D option quiet
111235368Sgnn #pragma D option destructive
112235368Sgnn #pragma D option switchrate=10
113235368Sgnn
114235368Sgnn /*
115235368Sgnn  * Command line arguments
116235368Sgnn  */
117235368Sgnn inline int OPT_time   = '$opt_time';
118235368Sgnn inline int INTERVAL   = '$interval';
119235368Sgnn inline int COUNTER    = '$counts';
120235368Sgnn
121235368Sgnn /* 
122235368Sgnn  * Initialise variables
123235368Sgnn  */
124235368Sgnn dtrace:::BEGIN 
125235368Sgnn {
126235368Sgnn	secs = INTERVAL; 
127235368Sgnn	counts = COUNTER;
128235368Sgnn	zonemax = 0;
129235368Sgnn	listing = 1;
130235368Sgnn	re[""] = 0; pi[""] = 0; po[""] = 0;
131235368Sgnn	mf[""] = 0; sr[""] = 0; fr[""] = 0;
132235368Sgnn	epi[""] = 0; epo[""] = 0; epf[""] = 0;
133235368Sgnn	api[""] = 0; apo[""] = 0; apf[""] = 0;
134235368Sgnn	fpi[""] = 0; fpo[""] = 0; fpf[""] = 0;
135235368Sgnn }
136235368Sgnn
137235368Sgnn /*
138235368Sgnn  * Build zonelist array
139235368Sgnn  *
140235368Sgnn  * Here we want the output of a command to be saved into an array
141235368Sgnn  * inside dtrace. This is done by running the command, sending the
142235368Sgnn  * output to /dev/null, and by probing its write syscalls from dtrace. 
143235368Sgnn  *
144235368Sgnn  * This is an example of a "scraper".
145235368Sgnn  */
146235368Sgnn
147235368Sgnn /*
148235368Sgnn  * List zones
149235368Sgnn  */
150235368Sgnn dtrace:::BEGIN 
151235368Sgnn {
152235368Sgnn	/* run zoneadm */
153235368Sgnn	system("/usr/sbin/zoneadm list > /dev/null; echo END > /dev/null");
154235368Sgnn }
155235368Sgnn
156235368Sgnn /*
157235368Sgnn  * Scrape zone listing
158235368Sgnn  */
159235368Sgnn syscall::write:entry
160235368Sgnn /listing && (execname == "zoneadm") && 
161235368Sgnn (curthread->t_procp->p_parent->p_ppid == $pid)/
162235368Sgnn {
163235368Sgnn	/* read zoneadm output */
164235368Sgnn	zonelist[zonemax] = stringof(copyin(arg1, arg2 - 1));
165235368Sgnn
166235368Sgnn	/* increment max number of zones */
167235368Sgnn	zonemax++;
168235368Sgnn }
169235368Sgnn
170235368Sgnn /*
171235368Sgnn  * Finish scraping zones
172235368Sgnn  */
173235368Sgnn syscall::write:entry
174235368Sgnn /listing && (execname == "sh") && (ppid == $pid)/
175235368Sgnn {
176235368Sgnn	/*
177235368Sgnn	 * this end tag lets us know our zonelist has finished.
178235368Sgnn	 * thanks A. Packer.
179235368Sgnn	 */
180235368Sgnn	listing = stringof(copyin(arg1, arg2 - 1)) == "END" ? 0 : 1;
181235368Sgnn }
182235368Sgnn
183235368Sgnn /*
184235368Sgnn  * Record vminfo counters
185235368Sgnn  */
186235368Sgnn vminfo:::pgrec      { re[zonename] += arg0; }
187235368Sgnn vminfo:::as_fault   { mf[zonename] += arg0; }
188235368Sgnn vminfo:::scan       { sr[zonename] += arg0; }
189235368Sgnn vminfo:::execpgin   { epi[zonename] += arg0; }
190235368Sgnn vminfo:::execpgout  { epo[zonename] += arg0; }
191235368Sgnn vminfo:::execfree   { epf[zonename] += arg0; fr[zonename] += arg0; }
192235368Sgnn vminfo:::anonpgin   { api[zonename] += arg0; }
193235368Sgnn vminfo:::anonpgout  { apo[zonename] += arg0; }
194235368Sgnn vminfo:::anonfree   { apf[zonename] += arg0; fr[zonename] += arg0; }
195235368Sgnn vminfo:::fspgin     { fpi[zonename] += arg0; }
196235368Sgnn vminfo:::fspgout    { fpo[zonename] += arg0; }
197235368Sgnn vminfo:::fsfree     { fpf[zonename] += arg0; fr[zonename] += arg0; }
198235368Sgnn
199235368Sgnn /*
200235368Sgnn  * Timer
201235368Sgnn  */
202235368Sgnn profile:::tick-1sec
203235368Sgnn {
204235368Sgnn	secs--;
205235368Sgnn }
206235368Sgnn
207235368Sgnn /*
208235368Sgnn  * Check for exit
209235368Sgnn  */
210235368Sgnn profile:::tick-1sec
211235368Sgnn /counts == 0/
212235368Sgnn {
213235368Sgnn	exit(0);
214235368Sgnn }
215235368Sgnn
216235368Sgnn /*
217235368Sgnn  * Print header line
218235368Sgnn  */
219235368Sgnn profile:::tick-1sec
220235368Sgnn /secs == 0/
221235368Sgnn {
222235368Sgnn	/* set counters */
223235368Sgnn	secs = INTERVAL;
224235368Sgnn	counts--;
225235368Sgnn	zonei = 0;
226235368Sgnn
227235368Sgnn	/* print time */
228235368Sgnn	OPT_time ? printf("\n%Y,\n",walltimestamp) : 1;
229235368Sgnn
230235368Sgnn	/* print output line */
231235368Sgnn	printf("%10s %4s %5s %4s %5s %4s %4s %4s %4s %4s %4s %4s %4s %4s\n",
232235368Sgnn	    "ZONE", "re", "mf", "fr", "sr", "epi", "epo", "epf", "api", "apo",
233235368Sgnn	    "apf", "fpi", "fpo", "fpf");
234235368Sgnn
235235368Sgnn	/* ensure zone writes are triggered */
236235368Sgnn	printf(" \b");
237235368Sgnn }
238235368Sgnn
239235368Sgnn /*
240235368Sgnn  * Print zone status line
241235368Sgnn  *
242235368Sgnn  * This is a fairly interesting function in that it loops over the keys in 
243235368Sgnn  * an associative array and prints out the values. DTrace cant really do 
244235368Sgnn  * loops, and generally doesnt need to. We "cheat" by generating writes
245235368Sgnn  * in the above probe which in turn trigger the probe below which 
246235368Sgnn  * contains the contents of each loop. Dont do this at home! We are
247235368Sgnn  * supposed to use aggreagations instead, wherever possible.
248235368Sgnn  *
249235368Sgnn  * This is an example of a "feedback loop".
250235368Sgnn  */
251235368Sgnn syscall::write:return
252235368Sgnn /pid == $pid && zonei < zonemax/
253235368Sgnn {
254235368Sgnn	/* fetch zonename */
255235368Sgnn	self->zone = zonelist[zonei];
256235368Sgnn
257235368Sgnn	/* print output */
258235368Sgnn	printf("%10s %4d %5d %4d %5d %4d %4d %4d %4d %4d %4d %4d %4d %4d\n",
259235368Sgnn	    self->zone, re[self->zone], mf[self->zone], fr[self->zone],
260235368Sgnn	    sr[self->zone], epi[self->zone], epo[self->zone],
261235368Sgnn	    epf[self->zone], api[self->zone], apo[self->zone],
262235368Sgnn	    apf[self->zone], fpi[self->zone], fpo[self->zone],
263235368Sgnn	    fpf[self->zone]);
264235368Sgnn	
265235368Sgnn	/* clear values */
266235368Sgnn	re[self->zone] = 0; mf[self->zone] = 0; fr[self->zone] = 0;
267235368Sgnn	sr[self->zone] = 0; epi[self->zone] = 0; epo[self->zone] = 0;
268235368Sgnn	epf[self->zone] = 0; api[self->zone] = 0; apo[self->zone] = 0;
269235368Sgnn	apf[self->zone] = 0; fpi[self->zone] = 0; fpo[self->zone] = 0;
270235368Sgnn	fpf[self->zone] = 0;
271235368Sgnn	self->zone = 0;
272235368Sgnn	
273235368Sgnn	/* go to next zone */
274235368Sgnn	zonei++;
275235368Sgnn }
276235368Sgnn'
277235368Sgnn
278