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