Merge branch 'master' of https://github.com/jan--f/fio
[fio.git] / tools / genfio
1 #!/usr/bin/env 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 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 -z filesize                     : Specify the working file size, if you are passing filepaths to -d
58                                         Disabled by default
59 -r seconds                      : Time in seconds per benchmark
60                                         0 means till the end of the device
61                                         Default is $RUNTIME seconds
62 -b blocksize[,blocksize1, ...]  : The blocksizes to test under fio format (4k, 1m, ...)
63                                         Separated each blocksize with a comma
64                                         Default is $BLOCK_SIZE
65 -m mode1,[mode2,mode3, ...]     : Define the fio IO profile to use like read, write, randread, randwrite
66                                         Default is "$MODES"
67 -x prefix                       : Add a prefix to the fio filename
68                                         Useful to let a context associated with the file
69                                         If the prefix features a / (slash), prefix will be considered as a directory
70 -A cmd_to_run                   : System command to run after each job (exec_postrun in fio)
71 -B cmd_to_run                   : System command to run before each job (exec_prerun in fio)
72
73 Example:
74
75 $PROG -d /dev/sdb,/dev/sdc,/dev/sdd,/dev/sde -a -b 4k,128k,1m -r 100 -a -x dellr720-day2/
76
77         Will generate an fio file that will run
78                 - a sequential bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 4k 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 = 128k with write,randwrite,read,randread tests
81                         ETA ~ 4 tests * 4 disks * 100 seconds
82                 - a sequential bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 1m with write,randwrite,read,randread tests
83                         ETA ~ 4 tests * 4 disks * 100 seconds
84                 - a parallel bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 4k 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 = 128k with write,randwrite,read,randread tests
87                         ETA ~ 4 tests * 100 seconds
88                 - a parallel bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 1m with write,randwrite,read,randread tests
89                         ETA ~ 4 tests * 100 seconds
90
91 Generating dellr720-day2/localhost-4k,128k,1m-all-write,randwrite,read,randread-sdb,sdc,sdd,sde.fio
92 Estimated Time = 6000 seconds : 1 hour 40 minutes
93 EOF
94 }
95
96 finish_template() {
97 echo "iodepth=$IODEPTH" >> $TEMPLATE
98
99 if [ "$RUNTIME" != "0" ]; then
100         echo "runtime=$RUNTIME" >> $TEMPLATE
101         echo "time_based" >> $TEMPLATE
102 fi
103
104 if [ "$CACHED_IO" = "FALSE" ]; then
105         echo "direct=1" >> $TEMPLATE
106 fi
107 }
108
109
110 diskname_to_printable() {
111 COUNT=0
112 for disk in $(echo $@ | tr "," " "); do
113         R=$(basename $disk | sed 's|/|_|g')
114         COUNT=$(($COUNT + 1))
115         if [ $COUNT -eq 1 ]; then
116                 P="$R"
117         else
118                 P="$P,$R"
119         fi
120 done
121 echo $P
122 }
123
124 gen_template() {
125 cat >$TEMPLATE << EOF
126 [global]
127 ioengine=libaio
128 invalidate=1
129 ramp_time=5
130 EOF
131 }
132
133 gen_seq_suite() {
134 TYPE=$1
135 disk=$2
136 PRINTABLE_DISK=$(diskname_to_printable $disk)
137 cat >> $OUTFILE << EOF
138 [$TYPE-$PRINTABLE_DISK-$BLK_SIZE-seq]
139 stonewall
140 bs=$BLK_SIZE
141 filename=$disk
142 rw=$TYPE
143 write_bw_log=${PREFIX_FILENAME}$SHORT_HOSTNAME-$BLK_SIZE-$PRINTABLE_DISK-$TYPE-seq.results
144 write_iops_log=${PREFIX_FILENAME}$SHORT_HOSTNAME-$BLK_SIZE-$PRINTABLE_DISK-$TYPE-seq.results
145 EOF
146 ETA=$(($ETA + $RUNTIME))
147 }
148
149 gen_seq_fio() {
150 for disk in $(echo $DISKS | tr "," " "); do
151         for mode in $(echo $MODES | tr "," " "); do
152                 gen_seq_suite "$mode" "$disk"
153         done
154 done
155 }
156
157
158 gen_para_suite() {
159 TYPE=$1
160 NEED_WALL=$2
161 D=0
162 for disk in $(echo $DISKS | tr "," " "); do
163     PRINTABLE_DISK=$(diskname_to_printable $disk)
164     cat >> $OUTFILE << EOF
165 [$TYPE-$PRINTABLE_DISK-$BLK_SIZE-para]
166 bs=$BLK_SIZE
167 EOF
168
169 if [ "$D" = 0 ]; then
170     echo "stonewall" >> $OUTFILE
171     D=1
172 fi
173
174 cat >> $OUTFILE << EOF
175 filename=$disk
176 rw=$TYPE
177 write_bw_log=${PREFIX_FILENAME}$SHORT_HOSTNAME-$BLK_SIZE-$PRINTABLE_DISK-$TYPE-para.results
178 write_iops_log=${PREFIX_FILENAME}$SHORT_HOSTNAME-$BLK_SIZE-$PRINTABLE_DISK-$TYPE-para.results
179 EOF
180 done
181
182 ETA=$(($ETA + $RUNTIME))
183 echo >> $OUTFILE
184 }
185
186 gen_para_fio() {
187 for mode in $(echo $MODES | tr "," " "); do
188         gen_para_suite "$mode"
189 done
190 }
191
192 gen_fio() {
193 case $SEQ in
194         2)
195                 gen_seq_fio
196                 gen_para_fio
197         ;;
198         1)
199                 gen_seq_fio
200         ;;
201         0)
202                 gen_para_fio
203         ;;
204 esac
205 }
206
207 parse_cmdline() {
208 while getopts "hacpsd:b:r:m:x:z:D:A:B:" opt; do
209   case $opt in
210     h)
211         show_help
212         exit 0
213         ;;
214     b)
215         BLOCK_SIZE=$OPTARG
216         ;;
217     c)
218         CACHED_IO="TRUE"
219         ;;
220     s)
221         if [ "$SEQ" = "-1" ]; then
222                 SEQ=1
223         fi
224       ;;
225     x)
226         PREFIX=$OPTARG
227         echo "$PREFIX" | grep -q "/"
228         if [ "$?" -eq 0 ]; then
229                 mkdir -p $PREFIX
230                 # No need to keep the prefix for the log files
231                 # we do have a directory for that
232                 PREFIX_FILENAME=""
233         else
234                 # We need to keep the prefix for the log files
235                 PREFIX_FILENAME=$PREFIX
236         fi
237         ;;
238     r)
239         RUNTIME=$OPTARG
240       ;;
241     p)
242         if [ "$SEQ" = "-1" ]; then
243                 SEQ=0
244         fi
245       ;;
246     m)
247         MODES=$OPTARG;
248       ;;
249     d)
250         DISKS=$OPTARG
251         PRINTABLE_DISKS=$(diskname_to_printable "$DISKS")
252       ;;
253     D)
254         IODEPTH=$OPTARG
255       ;;
256     a)
257         SEQ=2
258       ;;
259     B)
260         echo "exec_prerun=$OPTARG" >> $TEMPLATE
261       ;;
262     A)
263         echo "exec_postrun=$OPTARG" >> $TEMPLATE
264       ;;
265     z)
266         FSIZE=$OPTARG
267         echo "size=$FSIZE" >> $TEMPLATE
268       ;;
269     \?)
270       echo "Invalid option: -$OPTARG" >&2
271       ;;
272   esac
273 done
274
275 if [ "$SEQ" = "-1" ]; then
276         SEQ=0
277 fi
278
279 SHORT_HOSTNAME=$(hostname -s)
280 case $SEQ in
281         2)
282                 OUTFILE=${PREFIX}$SHORT_HOSTNAME-$BLOCK_SIZE-all-$MODES-$PRINTABLE_DISKS.fio
283         ;;
284
285         1)
286                 OUTFILE=${PREFIX}$SHORT_HOSTNAME-$BLOCK_SIZE-sequential-$MODES-$PRINTABLE_DISKS.fio
287         ;;
288         0)
289                 OUTFILE=${PREFIX}$SHORT_HOSTNAME-$BLOCK_SIZE-parallel-$MODES-$PRINTABLE_DISKS.fio
290         ;;
291 esac
292
293 if [ -z "$DISKS" ]; then
294         echo "Missing DISKS !"
295         echo "Please read the help !"
296         show_help
297         exit 1
298 fi
299
300 }
301
302 check_mode_order() {
303 FOUND_WRITE="NO"
304 CAUSE="You are reading data before writing them          "
305
306 # If no write occurs, let's show a different message
307 echo $MODES | grep -q "write"
308 if [ "$?" -ne 0 ]; then
309         CAUSE="You are reading data while never wrote them before"
310 fi
311
312 for mode in $(echo $MODES | tr "," " "); do
313         echo $mode | grep -q write
314         if [ "$?" -eq 0 ]; then
315                 FOUND_WRITE="YES"
316         fi
317         echo $mode | grep -q "read"
318         if [ "$?" -eq 0 ]; then
319                 if [ "$FOUND_WRITE" = "NO" ]; then
320                         echo "###############################################################"
321                         echo "# Warning : $CAUSE#"
322                         echo "# On some storage devices, this could lead to invalid results #"
323                         echo "#                                                             #"
324                         echo "# Press Ctrl-C to adjust pattern order if you have doubts     #"
325                         echo "# Or Wait 5 seconds before the file will be created           #"
326                         echo "###############################################################"
327                         sleep 5
328                         # No need to try showing the message more than one time
329                         return
330                 fi
331         fi
332 done
333 }
334
335
336 ########## MAIN
337 gen_template
338 parse_cmdline "$@"
339 finish_template
340 check_mode_order
341
342 echo "Generating $OUTFILE"
343 cp -f $TEMPLATE $OUTFILE
344 echo >> $OUTFILE
345
346 for BLK_SIZE in $(echo $BLOCK_SIZE | tr "," " "); do
347         gen_fio
348 done
349 ETA_H=$(($ETA / 3600))
350 ETA_M=$((($ETA - ($ETA_H*3600)) / 60))
351 if [ "$ETA" = "0" ]; then
352         echo "Cannot estimate ETA as RUNTIME=0"
353 else
354         echo "Estimated Time = $ETA seconds : $ETA_H hour $ETA_M minutes"
355 fi