1#!/usr/bin/ksh
2#
3# zvmstat - print vmstat style info per Zone.
4#           This uses DTrace (Solaris 10 3/05).
5#
6# This program must be run from the global zone as root.
7#
8# $Id: zvmstat 3 2007-08-01 10:50:08Z brendan $
9#
10# USAGE: 	zvmstat [-ht] [interval [count]]
11#
12#		zvmstat         # default output
13#			-t      # print times
14#  eg,
15#		zvmstat 1       # print every 1 second
16#		zvmstat 10 5    # print 5 x 10 second samples
17#		zvmstat -t 5    # print every 5 seconds with time
18#
19#
20# FIELDS:
21#		re		page reclaims
22#		mf		minor faults
23#		fr		pages freed
24#		sr		scan rate
25#		epi		executable pages paged in
26#		epo		executable pages paged out
27#		epf		executable pages freed
28#		api		anonymous pages paged in
29#		apo		anonymous pages paged out
30#		apf		anonymous pages freed
31#		fpi		filesystem pages paged in
32#		fpo		filesystem pages paged out
33#		fpf		filesystem pages freed
34#
35# NOTES: 
36# - Zone status should really be provided by Kstat, which currently
37#   provides system wide values, per CPU and per processor set, but not per
38#   zone. DTrace can fill this role in the meantime until Kstat supports zones.
39# - First output does not contain summary since boot.
40#
41# SEE ALSO: prstat -Z
42#
43# COPYRIGHT: Copyright (c) 2005 Brendan Gregg.
44#
45# CDDL HEADER START
46#
47#  The contents of this file are subject to the terms of the
48#  Common Development and Distribution License, Version 1.0 only
49#  (the "License").  You may not use this file except in compliance
50#  with the License.
51#
52#  You can obtain a copy of the license at Docs/cddl1.txt
53#  or http://www.opensolaris.org/os/licensing.
54#  See the License for the specific language governing permissions
55#  and limitations under the License.
56#
57# CDDL HEADER END
58#
59# BUGS:
60# - First output may not contain all zones due to how loops are achieved.
61#   Check for newer versions.
62#
63# Author: Brendan Gregg  [Sydney, Australia]
64#
65# 11-May-2005   Brendan Gregg   Created this.
66# 26-Jul-2005	   "      "	Improved code.
67# 08-Jan-2006	   "      "	Last update.
68#
69
70
71##############################
72# --- Process Arguments ---
73#
74
75### default variables
76opt_time=0; interval=1; counts=1
77
78### process options
79while getopts ht name
80do
81	case $name in
82	t)      opt_time=1 ;;
83	h|?)    cat <<-END >&2
84		USAGE: zvmstat [-ht] [interval [count]]
85		       zvmstat         # default output
86		               -t      # print times
87		   eg,
88		       zvmstat 1       # print every 1 second
89		       zvmstat 10 5    # print 5 x 10 second samples
90		       zvmstat -t 5    # print every 5 seconds with time
91		END
92		exit 1
93	esac
94done
95shift $(( OPTIND - 1 ))
96
97### option logic
98if (( "0$1" > 0 )); then
99        interval=$1; counts=-1; shift
100fi
101if (( "0$1" > 0 )); then
102        counts=$1; shift
103fi
104
105
106#################################
107# --- Main Program, DTrace ---
108#
109dtrace -n '
110 #pragma D option quiet
111 #pragma D option destructive
112 #pragma D option switchrate=10
113
114 /*
115  * Command line arguments
116  */
117 inline int OPT_time   = '$opt_time';
118 inline int INTERVAL   = '$interval';
119 inline int COUNTER    = '$counts';
120
121 /* 
122  * Initialise variables
123  */
124 dtrace:::BEGIN 
125 {
126	secs = INTERVAL; 
127	counts = COUNTER;
128	zonemax = 0;
129	listing = 1;
130	re[""] = 0; pi[""] = 0; po[""] = 0;
131	mf[""] = 0; sr[""] = 0; fr[""] = 0;
132	epi[""] = 0; epo[""] = 0; epf[""] = 0;
133	api[""] = 0; apo[""] = 0; apf[""] = 0;
134	fpi[""] = 0; fpo[""] = 0; fpf[""] = 0;
135 }
136
137 /*
138  * Build zonelist array
139  *
140  * Here we want the output of a command to be saved into an array
141  * inside dtrace. This is done by running the command, sending the
142  * output to /dev/null, and by probing its write syscalls from dtrace. 
143  *
144  * This is an example of a "scraper".
145  */
146
147 /*
148  * List zones
149  */
150 dtrace:::BEGIN 
151 {
152	/* run zoneadm */
153	system("/usr/sbin/zoneadm list > /dev/null; echo END > /dev/null");
154 }
155
156 /*
157  * Scrape zone listing
158  */
159 syscall::write:entry
160 /listing && (execname == "zoneadm") && 
161 (curthread->t_procp->p_parent->p_ppid == $pid)/
162 {
163	/* read zoneadm output */
164	zonelist[zonemax] = stringof(copyin(arg1, arg2 - 1));
165
166	/* increment max number of zones */
167	zonemax++;
168 }
169
170 /*
171  * Finish scraping zones
172  */
173 syscall::write:entry
174 /listing && (execname == "sh") && (ppid == $pid)/
175 {
176	/*
177	 * this end tag lets us know our zonelist has finished.
178	 * thanks A. Packer.
179	 */
180	listing = stringof(copyin(arg1, arg2 - 1)) == "END" ? 0 : 1;
181 }
182
183 /*
184  * Record vminfo counters
185  */
186 vminfo:::pgrec      { re[zonename] += arg0; }
187 vminfo:::as_fault   { mf[zonename] += arg0; }
188 vminfo:::scan       { sr[zonename] += arg0; }
189 vminfo:::execpgin   { epi[zonename] += arg0; }
190 vminfo:::execpgout  { epo[zonename] += arg0; }
191 vminfo:::execfree   { epf[zonename] += arg0; fr[zonename] += arg0; }
192 vminfo:::anonpgin   { api[zonename] += arg0; }
193 vminfo:::anonpgout  { apo[zonename] += arg0; }
194 vminfo:::anonfree   { apf[zonename] += arg0; fr[zonename] += arg0; }
195 vminfo:::fspgin     { fpi[zonename] += arg0; }
196 vminfo:::fspgout    { fpo[zonename] += arg0; }
197 vminfo:::fsfree     { fpf[zonename] += arg0; fr[zonename] += arg0; }
198
199 /*
200  * Timer
201  */
202 profile:::tick-1sec
203 {
204	secs--;
205 }
206
207 /*
208  * Check for exit
209  */
210 profile:::tick-1sec
211 /counts == 0/
212 {
213	exit(0);
214 }
215
216 /*
217  * Print header line
218  */
219 profile:::tick-1sec
220 /secs == 0/
221 {
222	/* set counters */
223	secs = INTERVAL;
224	counts--;
225	zonei = 0;
226
227	/* print time */
228	OPT_time ? printf("\n%Y,\n",walltimestamp) : 1;
229
230	/* print output line */
231	printf("%10s %4s %5s %4s %5s %4s %4s %4s %4s %4s %4s %4s %4s %4s\n",
232	    "ZONE", "re", "mf", "fr", "sr", "epi", "epo", "epf", "api", "apo",
233	    "apf", "fpi", "fpo", "fpf");
234
235	/* ensure zone writes are triggered */
236	printf(" \b");
237 }
238
239 /*
240  * Print zone status line
241  *
242  * This is a fairly interesting function in that it loops over the keys in 
243  * an associative array and prints out the values. DTrace cant really do 
244  * loops, and generally doesnt need to. We "cheat" by generating writes
245  * in the above probe which in turn trigger the probe below which 
246  * contains the contents of each loop. Dont do this at home! We are
247  * supposed to use aggreagations instead, wherever possible.
248  *
249  * This is an example of a "feedback loop".
250  */
251 syscall::write:return
252 /pid == $pid && zonei < zonemax/
253 {
254	/* fetch zonename */
255	self->zone = zonelist[zonei];
256
257	/* print output */
258	printf("%10s %4d %5d %4d %5d %4d %4d %4d %4d %4d %4d %4d %4d %4d\n",
259	    self->zone, re[self->zone], mf[self->zone], fr[self->zone],
260	    sr[self->zone], epi[self->zone], epo[self->zone],
261	    epf[self->zone], api[self->zone], apo[self->zone],
262	    apf[self->zone], fpi[self->zone], fpo[self->zone],
263	    fpf[self->zone]);
264	
265	/* clear values */
266	re[self->zone] = 0; mf[self->zone] = 0; fr[self->zone] = 0;
267	sr[self->zone] = 0; epi[self->zone] = 0; epo[self->zone] = 0;
268	epf[self->zone] = 0; api[self->zone] = 0; apo[self->zone] = 0;
269	apf[self->zone] = 0; fpi[self->zone] = 0; fpo[self->zone] = 0;
270	fpf[self->zone] = 0;
271	self->zone = 0;
272	
273	/* go to next zone */
274	zonei++;
275 }
276'
277
278