#!/bin/sh # # TITLE: iobal # # PURPOSE: awk to process iostat -x[n] output & aggregate stats by ranges of percent busy # This version figures out by itself whether input is from iostat -x or iostat -xn # # AUTHOR: robert.smith@east.sun.com -- Unsupported tool, use or modify at your own risk # # iostat -x header, awk column position, variable for aggregation: # extended device statistics cpu # device r/s w/s Kr/s Kw/s wait actv svc_t %w %b us sy wt id # $1 $2 $3 $4 $5 $6 $7 $8 $9 $10 - - - - # this_devname rs ws krs kws wait actv svct pctw pctb (the rest are ignored) # iostat -xn on Solaris 2.6 and later # extended device statistics # r/s w/s kr/s kw/s wait actv wsvc_t asvc_t %w %b device # $1 $2 $3 $4 $5 $6 $7 $8 $9 $10 $11 - - - - # rs ws krs kws wait actv wsvct asvct pctw pctb this_devname (any others are ignored) BANNER="Local Physical Disk Load Balance: iobal v2.3 02Aug2002\n" BFLAG=1 # Print Balance report if true TFLAG=0 # Print Throughput report if true IFLAG=0 # Print IOPS report if true SFLAG=0 # Print Service Time report if true NFLAG=1 # Must be followed by number of drives to display in TOP reports TOP=10 # Default number of disks to report top stats USAGE="\nUsage: iobal [-tis] [-n number] iostat-file\n \ Help: iobal -h\n \ Version: iobal -v\n" HELP="\nUsage: iobal [-tis] [-n number] iostat-file\n \ iostat -x[n] interval | iobal [-tis] [-n number] - \n \ -t Report top n drives by Throughput (KBytes/sec)\n \ -i Report top n drives by IOPS (I/O operations per second)\n \ -s Report top n drives by Service Time (avg millseconds response time)\n \ -n number, how many drives to include in top reports,\n \ only with t, s or i options. Defaults to 10 if omitted.\n iobal -h Display this help and exit\n \ iobal -v Display version and exit\n \ \nFile Example: Report top 22 drives by Throughput, IOPS and Service Time\n \ iobal -n 22 -tsi iostat-xn30.out | more\n \ \nInteractive Example: Report top 10 drives by Service Time\n \ iostat -xn 30 | iobal -s - \n" while getopts btishvn: SWCH do case $SWCH in b) BFLAG=1;; t) TFLAG=1;; i) IFLAG=1;; s) SFLAG=1;; h) echo $HELP; exit 0;; v) echo $BANNER; exit 0;; n) TOP=${OPTARG}; NFLAG=1;; \?) echo $USAGE exit 2;; esac done shift `expr $OPTIND - 1` FILE="$*" if [ -z "$FILE" ]; then echo "\niobal: no input file" echo $USAGE exit 3 elif [ "$FILE" = "-" ] ; then echo $BANNER echo "Input file is stdin" else echo $BANNER echo "Input file is $FILE" fi # Must use exec to allow option of reading from pipe or stdin exec awk 'BEGIN { # Initialize all variables and arrays BUSY_RANGE=7 ndevs=0; iter=0; idle_devs=0; active_devs=0 this_devname=" " rs=0; krs=0 # Total per iteration only ws=0; kws=0 # Total per iteration only wait=0; actv=0; asvct=0; wsvct=0; pctw=0; pctb=0 # Total per iteration only for (i=0; i<=BUSY_RANGE; i++) { busy[i]=0 # Disk count in range rs_a[i]=0; krs_a[i]=0; ws_a[i]=0; kws_a[i]=0 # Stats in range wait_a[i]=0; actv_a[i]=0 # Stats in range svct_a[i]=0 # Stats in range wsvct_a[i]=0; asvct_a[i]=0; pctw_a[i]=0; pctb_a[i]=0 # Stats in range contrib_a[i] = 0 # Number of disks contributing non-zero stats avg_read_a[i] = 0 avg_write_a[i] = 0 avg_wait_a[i] = 0 avg_actv_a[i] = 0 avg_svct_a[i] = 0 avg_wsvct_a[i] = 0 avg_asvct_a[i] = 0 avg_pctw_a[i] = 0 avg_pctb_a[i] = 0 } krws=0; kbs=0 for (i=0; i0) { # Compute avg io sizes within busy ranges for (i=0; i<=BUSY_RANGE; i++) { if (rs_a[i]>0) avg_read_a[i]=krs_a[i]/rs_a[i] else avg_read_a[i]=0 if (ws_a[i]>0) avg_write_a[i]=kws_a[i]/ws_a[i] else avg_write_a[i]=0 if (busy[i]>0) { avg_wait_a[i] = wait_a[i]/busy[i] avg_actv_a[i] = actv_a[i]/busy[i] avg_wsvct_a[i] = wsvct_a[i]/busy[i] avg_asvct_a[i] = asvct_a[i]/busy[i] avg_pctw_a[i] = pctw_a[i]/busy[i] avg_pctb_a[i] = pctb_a[i]/busy[i] } else { avg_wait_a[i] = 0 avg_actv_a[i] = 0 avg_wsvct_a[i] = 0 avg_asvct_a[i] = 0 avg_pctw_a[i] = 0 avg_pctb_a[i] = 0 } } # Compute some averages for this whole iteration if (rs>0) avg_read=krs/rs else avg_read=0 if (ws>0) avg_write=kws/ws else avg_write=0 if (kbs>0) for (i=0; i0) for (i=0; i80", "TOTAL") printf ("-------------- ---------- ---------- ---------- ---------- ---------- ---------- ----------\n") printf ("Disks %10d %10d %10d %10d %10d %10d %10d\n", \ busy[0], busy[1], busy[2], busy[3], busy[4], busy[5], ndevs) printf ("Reads/sec %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f\n", \ rs_a[0], rs_a[1], rs_a[2], rs_a[3], rs_a[4], rs_a[5], rs) printf ("KB Read/sec %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f\n", \ krs_a[0], krs_a[1], krs_a[2], krs_a[3], krs_a[4], krs_a[5], krs) printf ("Avg KB/Read %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f\n", \ avg_read_a[0], avg_read_a[1], avg_read_a[2], avg_read_a[3], avg_read_a[4], avg_read_a[5], \ avg_read) printf ("Writes/sec %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f\n", \ ws_a[0], ws_a[1], ws_a[2], ws_a[3], ws_a[4], ws_a[5], ws) printf ("KB Written/sec %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f\n", \ kws_a[0], kws_a[1], kws_a[2], kws_a[3], kws_a[4], kws_a[5], kws) printf ("Avg KB/Write %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f\n", \ avg_write_a[0], avg_write_a[1], avg_write_a[2], avg_write_a[3], avg_write_a[4], avg_write_a[5], \ avg_write) printf ("Avg Wait Cmds %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f\n", \ avg_wait_a[0], avg_wait_a[1], avg_wait_a[2], avg_wait_a[3], avg_wait_a[4], avg_wait_a[5], \ wait/active_devs) printf ("Avg WSvcTime(ms)%10.2f %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f\n", \ avg_wsvct_a[0], avg_wsvct_a[1], avg_wsvct_a[2], avg_wsvct_a[3], avg_wsvct_a[4], avg_wsvct_a[5], \ wsvct/active_devs) printf ("Avg %% Wait %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f\n", \ avg_pctw_a[0], avg_pctw_a[1], avg_pctw_a[2], avg_pctw_a[3], avg_pctw_a[4], avg_pctw_a[5], \ pctw/active_devs) printf ("Avg Active Cmds %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f\n", \ avg_actv_a[0], avg_actv_a[1], avg_actv_a[2], avg_actv_a[3], avg_actv_a[4], avg_actv_a[5], \ actv/active_devs) printf ("Avg ASvcTime(ms)%10.2f %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f\n", \ avg_asvct_a[0], avg_asvct_a[1], avg_asvct_a[2], avg_asvct_a[3], avg_asvct_a[4], avg_asvct_a[5], \ asvct/active_devs) printf ("Avg %% Busy %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f\n", \ avg_pctb_a[0], avg_pctb_a[1], avg_pctb_a[2], avg_pctb_a[3], avg_pctb_a[4], avg_pctb_a[5], \ pctb/active_devs) if (T_FLAG==1) { printf ("\n%d most active drives or LUNs by THROUGHPUT = KB read+written/sec\n", TOP_RANGE) printf ("\n%-15s %-19s %-17s\n", "Disk", " Total KB/sec", "% of Total KB/sec") printf ("%-15s %-19s %-17s\n", "---------------", "-------------------", "-----------------") printf ("%-15s %12.2f KB/sec %16.1f%%\n", "All Disks", kbs, 100) for (i=0; i krws_a[i]) { j = i while (j wasvct_a[i]) { j = i while (j rws_a[i]) { j = i while (j0 && $10<=20) brange=1 else if($10>20 && $10<=40) brange=2 else if($10>40 && $10<=60) brange=3 else if($10>60 && $10<=80) brange=4 else if($10>80) brange=5 else brange=6 # Should always be all zeros busy[brange]++ contrib_a[brange]++ if (input_type=="X") { rs_a[brange]+=$2; krs_a[brange]+=$4 ws_a[brange]+=$3; kws_a[brange]+=$5 wait_a[brange]+=$6; actv_a[brange]+=$7 svct_a[brange]+=$8 wsvct_a[brange]+=$7; asvct_a[brange]+=$8 pctw_a[brange]+=$9; pctb_a[brange]+=$10 } else if (input_type=="N") { rs_a[brange]+=$1; krs_a[brange]+=$3 ws_a[brange]+=$2; kws_a[brange]+=$4 wait_a[brange]+=$5; actv_a[brange]+=$6 wsvct_a[brange]+=$7; asvct_a[brange]+=$8 pctw_a[brange]+=$9; pctb_a[brange]+=$10 } else { printf ("iobal: Fatal: Bad input type: %s\n", input_type) exit 5 } }' TOP_RANGE=$TOP S_FLAG=$SFLAG I_FLAG=$IFLAG T_FLAG=$TFLAG $FILE