Home | History | Annotate | Download | only in tools
      1 #!/usr/bin/env bash
      2 #
      3 #  Copyright (C) 2013 eNovance SAS <licensing (at] enovance.com>
      4 #  Author: Erwan Velu  <erwan (at] enovance.com>
      5 #
      6 #  The license below covers all files distributed with fio unless otherwise
      7 #  noted in the file itself.
      8 #
      9 #  This program is free software; you can redistribute it and/or modify
     10 #  it under the terms of the GNU General Public License version 2 as
     11 #  published by the Free Software Foundation.
     12 #
     13 #  This program is distributed in the hope that it will be useful,
     14 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
     15 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16 #  GNU General Public License for more details.
     17 #
     18 #  You should have received a copy of the GNU General Public License
     19 #  along with this program; if not, write to the Free Software
     20 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     21 
     22 BLK_SIZE=
     23 BLOCK_SIZE=4k
     24 SEQ=-1
     25 TEMPLATE=/tmp/template.fio
     26 OUTFILE=
     27 DISKS=
     28 PRINTABLE_DISKS=
     29 RUNTIME=300
     30 ETA=0
     31 MODES="write,randwrite,read,randread"
     32 SHORT_HOSTNAME=
     33 CACHED_IO="FALSE"
     34 PREFIX=""
     35 PREFIX_FILENAME=""
     36 IODEPTH=1
     37 
     38 show_help() {
     39 	PROG=$(basename $0)
     40 	echo "usage of $PROG:"
     41 	cat << EOF
     42 -h				: Show this help & exit
     43 -c				: Enable cached-based IOs
     44 					Disabled by default
     45 -a				: Run sequential test then parallel one
     46 					Disabled by default
     47 -s				: Run sequential test (default value)
     48 					one test after another then one disk after another
     49 					Disabled by default
     50 -p				: Run parallel test
     51 					one test after anoter but all disks at the same time
     52 					Enabled by default
     53 -D iodepth			: Run with the specified iodepth
     54 					Default is $IODEPTH
     55 -d disk1[,disk2,disk3,..]	: Run the tests on the selected disks
     56 					Separated each disk with a comma
     57 -r seconds			: Time in seconds per benchmark
     58 					0 means till the end of the device
     59 					Default is $RUNTIME seconds
     60 -b blocksize[,blocksize1, ...]  : The blocksizes to test under fio format (4k, 1m, ...)
     61 					Separated each blocksize with a comma
     62 					Default is $BLOCK_SIZE
     63 -m mode1,[mode2,mode3, ...]     : Define the fio IO profile to use like read, write, randread, randwrite
     64 					Default is "$MODES"
     65 -x prefix			: Add a prefix to the fio filename
     66 					Useful to let a context associated with the file
     67 					If the prefix features a / (slash), prefix will be considered as a directory
     68 -A cmd_to_run			: System command to run after each job (exec_postrun in fio)
     69 -B cmd_to_run			: System command to run before each job (exec_prerun in fio)
     70 
     71 Example:
     72 
     73 $PROG -d /dev/sdb,/dev/sdc,/dev/sdd,/dev/sde -a -b 4k,128k,1m -r 100 -a -x dellr720-day2/
     74 
     75 	Will generate an fio file that will run
     76 		- a sequential bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 4k with write,randwrite,read,randread tests
     77 			ETA ~ 4 tests * 4 disks * 100 seconds
     78 		- a sequential bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 128k with write,randwrite,read,randread tests
     79 			ETA ~ 4 tests * 4 disks * 100 seconds
     80 		- a sequential bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 1m with write,randwrite,read,randread tests
     81 			ETA ~ 4 tests * 4 disks * 100 seconds
     82 		- a parallel bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 4k with write,randwrite,read,randread tests
     83 			ETA ~ 4 tests * 100 seconds
     84 		- a parallel bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 128k with write,randwrite,read,randread tests
     85 			ETA ~ 4 tests * 100 seconds
     86 		- a parallel bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 1m with write,randwrite,read,randread tests
     87 			ETA ~ 4 tests * 100 seconds
     88 
     89 Generating dellr720-day2/localhost-4k,128k,1m-all-write,randwrite,read,randread-sdb,sdc,sdd,sde.fio
     90 Estimated Time = 6000 seconds : 1 hour 40 minutes
     91 EOF
     92 }
     93 
     94 finish_template() {
     95 echo "iodepth=$IODEPTH" >> $TEMPLATE
     96 
     97 if [ "$RUNTIME" != "0" ]; then
     98 	echo "runtime=$RUNTIME" >> $TEMPLATE
     99 	echo "time_based" >> $TEMPLATE
    100 fi
    101 
    102 if [ "$CACHED_IO" = "FALSE" ]; then
    103 	echo "direct=1" >> $TEMPLATE
    104 fi
    105 }
    106 
    107 
    108 diskname_to_printable() {
    109 COUNT=0
    110 for disk in $(echo $@ | tr "," " "); do
    111 	R=$(basename $disk | sed 's|/|_|g')
    112 	COUNT=$(($COUNT + 1))
    113 	if [ $COUNT -eq 1 ]; then
    114 		P="$R"
    115 	else
    116 		P="$P,$R"
    117 	fi
    118 done
    119 echo $P
    120 }
    121 
    122 gen_template() {
    123 cat >$TEMPLATE << EOF
    124 [global]
    125 ioengine=libaio
    126 invalidate=1
    127 ramp_time=5
    128 EOF
    129 }
    130 
    131 gen_seq_suite() {
    132 TYPE=$1
    133 disk=$2
    134 PRINTABLE_DISK=$(diskname_to_printable $disk)
    135 cat >> $OUTFILE << EOF
    136 [$TYPE-$PRINTABLE_DISK-$BLK_SIZE-seq]
    137 stonewall
    138 bs=$BLK_SIZE
    139 filename=$disk
    140 rw=$TYPE
    141 write_bw_log=${PREFIX_FILENAME}$SHORT_HOSTNAME-$BLK_SIZE-$PRINTABLE_DISK-$TYPE-seq.results
    142 write_iops_log=${PREFIX_FILENAME}$SHORT_HOSTNAME-$BLK_SIZE-$PRINTABLE_DISK-$TYPE-seq.results
    143 EOF
    144 ETA=$(($ETA + $RUNTIME))
    145 }
    146 
    147 gen_seq_fio() {
    148 for disk in $(echo $DISKS | tr "," " "); do
    149 	for mode in $(echo $MODES | tr "," " "); do
    150 		gen_seq_suite "$mode" "$disk"
    151 	done
    152 done
    153 }
    154 
    155 
    156 gen_para_suite() {
    157 TYPE=$1
    158 NEED_WALL=$2
    159 D=0
    160 for disk in $(echo $DISKS | tr "," " "); do
    161     PRINTABLE_DISK=$(diskname_to_printable $disk)
    162     cat >> $OUTFILE << EOF
    163 [$TYPE-$PRINTABLE_DISK-$BLK_SIZE-para]
    164 bs=$BLK_SIZE
    165 EOF
    166 
    167 if [ "$D" = 0 ]; then
    168     echo "stonewall" >> $OUTFILE
    169     D=1
    170 fi
    171 
    172 cat >> $OUTFILE << EOF
    173 filename=$disk
    174 rw=$TYPE
    175 write_bw_log=${PREFIX_FILENAME}$SHORT_HOSTNAME-$BLK_SIZE-$PRINTABLE_DISK-$TYPE-para.results
    176 write_iops_log=${PREFIX_FILENAME}$SHORT_HOSTNAME-$BLK_SIZE-$PRINTABLE_DISK-$TYPE-para.results
    177 EOF
    178 done
    179 
    180 ETA=$(($ETA + $RUNTIME))
    181 echo >> $OUTFILE
    182 }
    183 
    184 gen_para_fio() {
    185 for mode in $(echo $MODES | tr "," " "); do
    186 	gen_para_suite "$mode"
    187 done
    188 }
    189 
    190 gen_fio() {
    191 case $SEQ in
    192 	2)
    193 		gen_seq_fio
    194 		gen_para_fio
    195 	;;
    196 	1)
    197 		gen_seq_fio
    198 	;;
    199 	0)
    200 		gen_para_fio
    201 	;;
    202 esac
    203 }
    204 
    205 parse_cmdline() {
    206 while getopts "hacpsd:b:r:m:x:D:A:B:" opt; do
    207   case $opt in
    208     h)
    209 	show_help
    210 	exit 0
    211 	;;
    212     b)
    213 	BLOCK_SIZE=$OPTARG
    214 	;;
    215     c)
    216 	CACHED_IO="TRUE"
    217 	;;
    218     s)
    219 	if [ "$SEQ" = "-1" ]; then
    220 		SEQ=1
    221 	fi
    222       ;;
    223     x)
    224 	PREFIX=$OPTARG
    225 	echo "$PREFIX" | grep -q "/"
    226 	if [ "$?" -eq 0 ]; then
    227 		mkdir -p $PREFIX
    228 		# No need to keep the prefix for the log files
    229 		# we do have a directory for that
    230 		PREFIX_FILENAME=""
    231 	else
    232 		# We need to keep the prefix for the log files
    233 		PREFIX_FILENAME=$PREFIX
    234 	fi
    235 	;;
    236     r)
    237 	RUNTIME=$OPTARG
    238       ;;
    239     p)
    240 	if [ "$SEQ" = "-1" ]; then
    241 		SEQ=0
    242 	fi
    243       ;;
    244     m)
    245         MODES=$OPTARG;
    246       ;;
    247     d)
    248  	DISKS=$OPTARG
    249 	PRINTABLE_DISKS=$(diskname_to_printable "$DISKS")
    250       ;;
    251     D)
    252 	IODEPTH=$OPTARG
    253       ;;
    254     a)
    255 	SEQ=2
    256       ;;
    257     B)
    258 	echo "exec_prerun=$OPTARG" >> $TEMPLATE
    259       ;;
    260     A)
    261 	echo "exec_postrun=$OPTARG" >> $TEMPLATE
    262       ;;
    263     \?)
    264       echo "Invalid option: -$OPTARG" >&2
    265       ;;
    266   esac
    267 done
    268 
    269 if [ "$SEQ" = "-1" ]; then
    270 	SEQ=0
    271 fi
    272 
    273 SHORT_HOSTNAME=$(hostname -s)
    274 case $SEQ in
    275 	2)
    276 		OUTFILE=${PREFIX}$SHORT_HOSTNAME-$BLOCK_SIZE-all-$MODES-$PRINTABLE_DISKS.fio
    277 	;;
    278 
    279 	1)
    280 		OUTFILE=${PREFIX}$SHORT_HOSTNAME-$BLOCK_SIZE-sequential-$MODES-$PRINTABLE_DISKS.fio
    281 	;;
    282 	0)
    283 		OUTFILE=${PREFIX}$SHORT_HOSTNAME-$BLOCK_SIZE-parallel-$MODES-$PRINTABLE_DISKS.fio
    284 	;;
    285 esac
    286 
    287 if [ -z "$DISKS" ]; then
    288 	echo "Missing DISKS !"
    289 	echo "Please read the help !"
    290 	show_help
    291 	exit 1
    292 fi
    293 
    294 }
    295 
    296 check_mode_order() {
    297 FOUND_WRITE="NO"
    298 CAUSE="You are reading data before writing them          "
    299 
    300 # If no write occurs, let's show a different message
    301 echo $MODES | grep -q "write"
    302 if [ "$?" -ne 0 ]; then
    303 	CAUSE="You are reading data while never wrote them before"
    304 fi
    305 
    306 for mode in $(echo $MODES | tr "," " "); do
    307 	echo $mode | grep -q write
    308 	if [ "$?" -eq 0 ]; then
    309 		FOUND_WRITE="YES"
    310 	fi
    311 	echo $mode | grep -q "read"
    312 	if [ "$?" -eq 0 ]; then
    313 		if [ "$FOUND_WRITE" = "NO" ]; then
    314 			echo "###############################################################"
    315 			echo "# Warning : $CAUSE#"
    316 			echo "# On some storage devices, this could lead to invalid results #"
    317 			echo "#                                                             #"
    318 			echo "# Press Ctrl-C to adjust pattern order if you have doubts     #"
    319 			echo "# Or Wait 5 seconds before the file will be created           #"
    320 			echo "###############################################################"
    321 			sleep 5
    322 			# No need to try showing the message more than one time
    323 			return
    324 		fi
    325 	fi
    326 done
    327 }
    328 
    329 
    330 ########## MAIN
    331 gen_template
    332 parse_cmdline "$@"
    333 finish_template
    334 check_mode_order
    335 
    336 echo "Generating $OUTFILE"
    337 cp -f $TEMPLATE $OUTFILE
    338 echo >> $OUTFILE
    339 
    340 for BLK_SIZE in $(echo $BLOCK_SIZE | tr "," " "); do
    341 	gen_fio
    342 done
    343 ETA_H=$(($ETA / 3600))
    344 ETA_M=$((($ETA - ($ETA_H*3600)) / 60))
    345 if [ "$ETA" = "0" ]; then
    346 	echo "Cannot estimate ETA as RUNTIME=0"
    347 else
    348 	echo "Estimated Time = $ETA seconds : $ETA_H hour $ETA_M minutes"
    349 fi
    350