genfio: Warn users if read occurs before write
[fio.git] / tools / genfio
1 #!/bin/bash
2 #
3 #  Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
4 #  Author: Erwan Velu  <erwan@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 RUNTIME=300
29 ETA=0
30 MODES="write,randwrite,read,randread"
31 SHORT_HOSTNAME=
32 CACHED_IO="FALSE"
33 PREFIX=""
34 PREFIX_FILENAME=""
35
36 show_help() {
37         PROG=$(basename $0)
38         echo "usage of $PROG:"
39         cat << EOF
40 -h                              : Show this help & exit
41 -c                              : Enable cached-based IOs
42                                         Disabled by default
43 -a                              : Run sequential test then parallel one
44                                         Disabled by default
45 -s                              : Run sequential test (default value)
46                                         one test after another then one disk after another
47                                         Disabled by default
48 -p                              : Run parallel test
49                                         one test after anoter but all disks at the same time
50                                         Enabled by default
51 -d disk1[,disk2,disk3,..]       : Run the tests on the selected disks
52                                         Separated each disk with a comma
53                                         Disk name shall be "sdxx", /dev/ shall NOT be used here
54 -r seconds                      : Time in seconds per benchmark
55                                         0 means till the end of the device
56                                         Default is 300 seconds
57 -b blocksize[,blocksize1, ...]  : The blocksizes to test under fio format (4k, 1m, ...)
58                                         Separated each blocksize with a comma
59                                         Default is 4k
60 -m mode1,[mode2,mode3, ...]     : Define the fio IO profile to use like read, write, randread, randwrite
61                                         Default is "write,randwrite,read,randread"
62 -x prefix                       : Add a prefix to the fio filename
63                                         Useful to let a context associated with the file
64                                         If the prefix features a / (slash), prefix will be considered as a directory
65
66 Example:
67
68 $PROG -d sdb,sdc,sdd,sde -a -b 4k,128k,1m -r 100 -a -x dellr720-day2/
69
70         Will generate an fio file that will run
71                 - a sequential bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 4k with write,randwrite,read,randread tests
72                         ETA ~ 4 tests * 4 disks * 100 seconds
73                 - a sequential bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 128k with write,randwrite,read,randread tests
74                         ETA ~ 4 tests * 4 disks * 100 seconds
75                 - a sequential bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 1m with write,randwrite,read,randread tests
76                         ETA ~ 4 tests * 4 disks * 100 seconds
77                 - a parallel bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 4k with write,randwrite,read,randread tests
78                         ETA ~ 4 tests * 100 seconds
79                 - a parallel bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 128k with write,randwrite,read,randread tests
80                         ETA ~ 4 tests * 100 seconds
81                 - a parallel bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 1m with write,randwrite,read,randread tests
82                         ETA ~ 4 tests * 100 seconds
83
84 Generating dellr720-day2/localhost-4k,128k,1m-all-write,randwrite,read,randread-sdb,sdc,sdd,sde.fio
85 Estimated Time = 6000 seconds : 1 hour 40 minutes
86 EOF
87 }
88
89 gen_template() {
90 cat >$TEMPLATE << EOF
91 [global]
92 ioengine=libaio
93 invalidate=1
94 ramp_time=5
95 EOF
96
97 if [ "$RUNTIME" != "0" ]; then
98 cat >>$TEMPLATE << EOF
99 runtime=$RUNTIME
100 EOF
101 fi
102
103 if [ "$CACHED_IO" = "FALSE" ]; then
104 cat >>$TEMPLATE << EOF
105 direct=1
106 EOF
107 fi
108
109 }
110
111 gen_seq_suite() {
112 TYPE=$1
113 cat >> $OUTFILE << EOF
114 [$TYPE-$disk-$BLK_SIZE-seq]
115 stonewall
116 bs=$BLK_SIZE
117 filename=/dev/$disk
118 rw=$TYPE
119 write_bw_log=${PREFIX_FILENAME}$SHORT_HOSTNAME-$BLK_SIZE-$disk-$TYPE-seq.results
120 write_iops_log=${PREFIX_FILENAME}$SHORT_HOSTNAME-$BLK_SIZE-$disk-$TYPE-seq.results
121 EOF
122 ETA=$(($ETA + $RUNTIME))
123 }
124
125 gen_seq_fio() {
126 for disk in $(echo $DISKS | tr "," " "); do
127         for mode in $(echo $MODES | tr "," " "); do
128                 gen_seq_suite "$mode"
129         done
130 done
131 }
132
133
134 gen_para_suite() {
135 TYPE=$1
136 NEED_WALL=$2
137 D=0
138 for disk in $(echo $DISKS | tr "," " "); do
139     cat >> $OUTFILE << EOF
140 [$TYPE-$disk-$BLK_SIZE-para]
141 bs=$BLK_SIZE
142 EOF
143
144 if [ "$D" = 0 ]; then
145     echo "stonewall" >> $OUTFILE
146     D=1
147 fi
148
149 cat >> $OUTFILE << EOF
150 filename=/dev/$disk
151 rw=$TYPE
152 write_bw_log=${PREFIX_FILENAME}$SHORT_HOSTNAME-$BLK_SIZE-$disk-$TYPE-para.results
153 write_iops_log=${PREFIX_FILENAME}$SHORT_HOSTNAME-$BLK_SIZE-$disk-$TYPE-para.results
154 EOF
155 done
156
157 ETA=$(($ETA + $RUNTIME))
158 echo >> $OUTFILE
159 }
160
161 gen_para_fio() {
162 for mode in $(echo $MODES | tr "," " "); do
163         gen_para_suite "$mode"
164 done
165 }
166
167 gen_fio() {
168 case $SEQ in
169         2)
170                 gen_seq_fio
171                 gen_para_fio
172         ;;
173         1)
174                 gen_seq_fio
175         ;;
176         0)
177                 gen_para_fio
178         ;;
179 esac
180 }
181
182 parse_cmdline() {
183 while getopts "hacpsd:b:r:m:x:" opt; do
184   case $opt in
185     h)
186         show_help
187         exit 0
188         ;;
189     b)
190         BLOCK_SIZE=$OPTARG
191         ;;
192     c)
193         CACHED_IO="TRUE"
194         ;;
195     s)
196         if [ "$SEQ" = "-1" ]; then
197                 SEQ=1
198         fi
199       ;;
200     x)
201         PREFIX=$OPTARG
202         echo "$PREFIX" | grep -q "/"
203         if [ "$?" -eq 0 ]; then
204                 mkdir -p $PREFIX
205                 # No need to keep the prefix for the log files
206                 # we do have a directory for that
207                 PREFIX_FILENAME=""
208         else
209                 # We need to keep the prefix for the log files
210                 PREFIX_FILENAME=$PREFIX
211         fi
212         ;;
213     r)
214         RUNTIME=$OPTARG
215       ;;
216     p)
217         if [ "$SEQ" = "-1" ]; then
218                 SEQ=0
219         fi
220       ;;
221     m)
222         MODES=$OPTARG;
223       ;;
224     d)
225         DISKS=$OPTARG
226       ;;
227     a)
228         SEQ=2
229       ;;
230     \?)
231       echo "Invalid option: -$OPTARG" >&2
232       ;;
233   esac
234 done
235
236 if [ "$SEQ" = "-1" ]; then
237         SEQ=0
238 fi
239
240 SHORT_HOSTNAME=$(hostname -s)
241 case $SEQ in
242         2)
243                 OUTFILE=${PREFIX}$SHORT_HOSTNAME-$BLOCK_SIZE-all-$MODES-$DISKS.fio
244         ;;
245
246         1)
247                 OUTFILE=${PREFIX}$SHORT_HOSTNAME-$BLOCK_SIZE-sequential-$MODES-$DISKS.fio
248         ;;
249         0)
250                 OUTFILE=${PREFIX}$SHORT_HOSTNAME-$BLOCK_SIZE-parallel-$MODES-$DISKS.fio
251         ;;
252 esac
253
254 if [ -z "$DISKS" ]; then
255         echo "Missing DISKS !"
256         echo "Please read the help !"
257         show_help
258         exit 1
259 fi
260
261 }
262
263 check_mode_order() {
264 FOUND_WRITE="NO"
265 CAUSE="You are reading data before writing them          "
266
267 # If no write occurs, let's show a different message
268 echo $MODES | grep -q "write"
269 if [ "$?" -ne 0 ]; then
270         CAUSE="You are reading data while never wrote them before"
271 fi
272
273 for mode in $(echo $MODES | tr "," " "); do
274         echo $mode | grep -q write
275         if [ "$?" -eq 0 ]; then
276                 FOUND_WRITE="YES"
277         fi
278         echo $mode | grep -q "read"
279         if [ "$?" -eq 0 ]; then
280                 if [ "$FOUND_WRITE" = "NO" ]; then
281                         echo "###############################################################"
282                         echo "# Warning : $CAUSE#"
283                         echo "# On some storage devices, this could lead to invalid results #"
284                         echo "#                                                             #"
285                         echo "# Press Ctrl-C to adjust pattern order if you have doubts     #"
286                         echo "# Or Wait 5 seconds before the file will be created           #"
287                         echo "###############################################################"
288                         sleep 5
289                         # No need to try showing the message more than one time
290                         return
291                 fi
292         fi
293 done
294 }
295
296
297 ########## MAIN
298 parse_cmdline $@
299 check_mode_order
300 gen_template
301
302 echo "Generating $OUTFILE"
303 cp -f $TEMPLATE $OUTFILE
304 echo >> $OUTFILE
305
306 for BLK_SIZE in $(echo $BLOCK_SIZE | tr "," " "); do
307         gen_fio
308 done
309 ETA_H=$(($ETA / 3600))
310 ETA_M=$((($ETA - ($ETA_H*3600)) / 60))
311 if [ "$ETA" = "0" ]; then
312         echo "Cannot estimate ETA as RUNTIME=0"
313 else
314         echo "Estimated Time = $ETA seconds : $ETA_H hour $ETA_M minutes"
315 fi