3 # for now all the units are in 'k', but we could introduce some helpers
4 # would be nice to run tests in the background and trap signals and kill
7 # make sure devices aren't in use before going to town
8 # really use threads with iozone
9 # look into what sgp_dd is really doing, update arguments
10 # rename config/prepare/setup/cleanup/finish/teardown
11 # do something with sf and fpp iterating
12 # discard first vmstat line
15 # a temp dir that is setup and torn down for each script run
17 # so we can kill background processes as the test cleans up
18 declare -a cleanup_pids
19 # to unmount mounts in our tmpdir before removing it
20 declare -a cleanup_mounts
21 # global for completing the table. XXX this is a wart that could go
24 # defaults for some options:
28 # optional output directory
37 [ -e $path ] || continue;
38 [ -f $path ] || die "needed to remove non-file $path"
39 rm -f $path || die "couldn't remove $path"
43 [ ! -z "$output_dir" ] && mv -f $1 $output_dir/$2
46 for pid in ${cleanup_pids[*]}; do
50 for a in ${cleanup_mounts[*]}; do
53 [ ${#tmpdir} == 18 ] && [ -d $tmpdir ] && rm -rf $tmpdir
59 cleanup_pids[$pid]=$pid
63 unset cleanup_pids[$pid]
67 echo $* | sed -e 's/ /,/g'
70 echo "scale=2; $*" | bc
82 0) echo '??' ; return ;;
83 1) echo "$avg:0" ; return ;;
86 avg=`do_bc $avg / $num`
89 local dev=`do_bc \($p - $avg\) \^ 2`
90 tmp=`do_bc $tmp + $dev`
92 tmp=`do_bc sqrt \( $tmp / \($num - 1\) \)`
98 echo " -b <block device to profile>"
99 echo " -d <summary output directory>"
100 echo " -l <max io len>"
101 echo " -t <minimum number of threads per device>"
102 echo " -T <maximum number of threads per device>"
106 # some cute code for handling tables whose columns fit
111 if [ $val -gt ${!target:-0} ]; then
116 local name="_table_$1"
122 eval ${name}_${row}_${col}="'$val'"
124 set_max ${name}_${col}_longest ${#val}
125 set_max ${name}_num_col $(($col + 1))
126 set_max ${name}_num_row $(($row + 1))
130 local name="_table_$1"
133 tmp="${name}_${row}_${col}"
138 local name="_table_$1"
145 tmp="${name}_num_col"
147 tmp="${name}_num_row"
150 # iterate through the columns to find the longest
153 for x in `seq 0 $num_col`; do
154 tmp="${name}_${x}_longest"
156 [ $tmp -eq 0 ] && continue
158 [ $x -eq $((num_col - 1)) ] && sep='\n'
160 fmt="$fmt%-${tmp}s$sep"
163 # nothing in the table to print
164 [ -z "$fmt" ] && return
166 for y in `seq 0 $num_row`; do
168 for x in `seq 0 $num_col`; do
170 # skip this element if the column is empty
171 tmp="${name}_${x}_longest"
172 [ ${!tmp:-0} -eq 0 ] && continue
174 # fill this cell with the value or '' for printf
175 tmp="${name}_${y}_${x}"
176 row="$row'${!tmp:-""}' "
178 eval printf "'$fmt'" $row
182 ######################################################################
185 echo sgp_dd using dio=1 and thr=
188 # it could be making sure that the block dev
189 # isn't in use by something else
193 if ! which sgp_dd; then
194 echo "can't find sgp_dd binary"
200 # it could be making sure that the block dev
201 # isn't in use by something else
210 local bdev=${blocks[$i]};
213 w) ifof="if=/dev/zero of=$bdev" ;;
214 r) ifof="if=$bdev of=/dev/null" ;;
215 *) die "asked to do io with $wor?"
217 echo sgp_dd $ifof bs=$iosize"k" count=$(($io_len / $iosize)) time=1 \
223 awk '($(NF) == "MB/sec") {print $(NF-1)}' < $output
238 ######################################################################
240 ext2_iozone_banner() {
241 echo "iozone -I on a clean ext2 fs"
243 ext2_iozone_config() {
246 ext2_iozone_prepare() {
248 local bdev=${blocks[$index]}
249 local mntpnt=$tmpdir/mount_$index
251 if ! which iozone; then
252 echo "iozone binary not found in PATH"
255 if ! which mke2fs; then
256 echo "mke2fs binary not found in PATH"
260 if ! mkdir -p $mntpnt ; then
261 echo "$mntpnt isn't a directory?"
264 echo making ext2 filesystem on $bdev
265 if ! mke2fs -b 4096 $bdev; then
270 if ! mount -t ext2 $bdev $mntpnt; then
271 echo "couldn't mount $bdev on $mntpnt"
275 cleanup_mounts[$index]="$mntpnt"
278 ext2_iozone_setup() {
281 local f="$tmpdir/mount_$id/iozone"
286 *) die "asked to do io with $wor?"
289 ext2_iozone_start() {
295 local f="$tmpdir/mount_$id/iozone"
300 *) die "asked to do io with $wor?"
303 echo iozone "$args -r ${iosize}k -s ${io_len}k -I -f $f"
305 ext2_iozone_result() {
308 kps=`awk '($2 == "reclen"){results=NR+1}(results == NR){print $3}' \
312 ext2_iozone_cleanup() {
315 local f="$tmpdir/mount_$id/iozone"
320 *) die "asked to do io with $wor?"
323 ext2_iozone_finish() {
325 local mntpnt=$tmpdir/mount_$index
328 unset cleanup_mounts[$index]
330 ext2_iozone_teardown() {
334 ######################################################################
335 # the lctl test_brw via the echo_client on top of the filter
337 # the echo_client setup is nutty enough to warrant its own clenaup
340 declare -a running_names
342 cleanup_echo_filter() {
345 for i in `seq 0 $last_block`; do
346 [ -z "${running_oids[$i]}" ] && continue
347 lctl --device "\$"echo_$i destroy ${running_oids[$i]} \
352 for n in ${running_names[*]}; do
353 # I can't believe leading whitespace matters here.
363 for m in $running_modules; do
368 [ ! -z "$running_config" ] && lconf --cleanup $running_config
372 echo_filter_banner() {
373 echo "test_brw on the echo_client on the filter"
375 echo_filter_config() {
377 local bdev=${blocks[$index]}
378 local config="$tmpdir/config.xml"
381 echo "lmc binary not found in PATH"
384 if ! which lconf; then
385 echo "lconf binary not found in PATH"
388 if ! which lctl; then
389 echo "lctl binary not found in PATH"
393 if [ $index = 0 ]; then
394 if ! lmc -m $config --add net \
395 --node localhost --nid localhost --nettype tcp; then
396 echo "error adding localhost net node"
401 if ! lmc -m $config --add ost --ost ost_$index --node localhost \
402 --fstype ext3 --dev $bdev --journal_size 400; then
403 echo "error adding $bdev to config with lmc"
407 # it would be nice to be able to ask lmc to setup an echo client
408 # to the filter here. --add echo_client assumes osc
410 echo_filter_prepare() {
412 local bdev=${blocks[$index]}
413 local config="$tmpdir/config.xml"
414 local name="echo_$index"
415 local uuid="echo_$index_uuid"
417 if [ $index = 0 ]; then
418 if ! lconf --reformat $config; then
419 echo "error setting up with lconf"
422 running_config="$config"
423 if ! grep -q '^obdecho\>' /proc/modules; then
424 if ! modprobe obdecho; then
425 echo "error running modprobe obdecho"
428 running_modules="obdecho"
434 attach echo_client $name $uuid
439 echo "error setting up echo_client $name against ost_$index"
442 running_names[$index]=$name
444 echo_filter_setup() {
448 local name="echo_$id"
454 *) die "asked to do io with $wor?"
457 running_threads=$threads
458 oid=`lctl --device "\$"$name create $threads | \
459 awk '/1 is object id/ { print $6 }'`
460 # XXX need to deal with errors
461 running_oids[$id]=$oid
463 echo_filter_start() {
468 local name="echo_$id"
469 local pages=$(($io_len / 4))
474 *) die "asked to do io with $wor?"
477 echo lctl --threads $threads v "\$"$name \
478 test_brw 1 w v $pages ${running_oids[$i]} p$iosize
480 echo_filter_result() {
485 for mbs in `awk '($8=="MB/s):"){print substr($7,2)}' < $output`; do
486 total=$(do_bc $total + $mbs)
490 echo_filter_cleanup() {
494 local name="echo_$id"
499 *) die "asked to do io with $wor?"
502 lctl --device "\$"$name destroy ${running_oids[$i]} $threads
503 unset running_oids[$i]
505 echo_filter_finish() {
507 # leave real work for _teardown
509 echo_filter_teardown() {
513 ######################################################################
514 # the iteration that drives the tests
524 local vmstat_log="$tmpdir/vmstat.log"
525 local opref="$test-$threads-$iosize-$wor"
527 for i in `seq 0 $last_block`; do
528 ${test}_setup $i $wor $threads
531 echo $test with $threads threads
533 # start up vmstat and record its pid
535 nice -19 vmstat 1 > $vmstat_log 2>&1 &
536 [ $? = 0 ] || die "vmstat failed"
538 pid_now_running $vmstat_pid
540 # start all the tests. each returns a pid to wait on
542 for i in `seq 0 $last_block`; do
543 cmd=`${test}_start $threads $iosize $wor $i`
544 $cmd > $tmpdir/$i 2>&1 &
550 echo -n waiting on pids $pids:
558 # stop vmstat and get cpu use from it
561 pid_has_stopped $vmstat_pid
562 cpu=$(mean_stddev $(awk \
563 '(NR > 3 && NF == 16 && $16 != "id" ) \
564 {print 100 - $16}' < $vmstat_log) )
565 save_output $vmstat_log $opref.vmstat
567 # record each index's test results and sum them
570 for i in `seq 0 $last_block`; do
571 local t=`${test}_result $tmpdir/$i`
572 save_output $tmpdir/$i $opref.$i
573 echo test returned "$t"
575 # some tests return mean:stddev per thread, filter out stddev
576 thru=$(do_bc $thru + $(echo $t | sed -e 's/:.*$//g'))
578 line="("`commas $line`")"
580 for i in `seq 0 $last_block`; do
581 ${test}_cleanup $i $wor $threads
584 # tabulate the results
585 echo $test did $thru mb/s with $cpu
586 table_set $test $my_x $my_y $thru
587 table_set $test $(($my_x + 1)) $my_y $cpu
588 table_set $test $(($my_x + 2)) $my_y $line
593 local thr=$min_threads
598 for i in `seq 0 $last_block`; do
599 if ! ${test}_config $i; then
600 echo "couldn't config $test for bdev ${blocks[$i]}"
601 echo "skipping $test for all block devices"
608 for i in `seq 0 $last_block`; do
609 # don't prepare if _config already failed
610 [ ! -z "$cleanup" ] && break
611 if ! ${test}_prepare $i; then
612 echo "couldn't prepare $test for bdev ${blocks[$i]}"
613 echo "skipping $test for all block devices"
620 while [ -z "$cleanup" -a $thr -lt $(($max_threads + 1)) ]; do
621 for iosize in 64 128; do
622 table_set $test 0 $cur_y $thr
623 table_set $test 1 $cur_y $iosize
624 table_set $test 2 $cur_y "|"
627 table_set $test 3 $cur_y $wor
628 test_one $test 4 $cur_y $thr $iosize $wor
629 cur_y=$(($cur_y + 1))
635 [ -z "$cleanup" ] && cleanup=$last_block
637 if [ "$cleanup" != -1 ]; then
638 for i in `seq $cleanup 0`; do
648 while getopts ":d:b:l:t:T:" opt; do
651 d) output_dir=$OPTARG ;;
653 t) min_threads=$OPTARG ;;
654 T) max_threads=$OPTARG ;;
659 if [ -z "$io_len" ]; then
660 io_len=`awk '($1 == "MemTotal:"){print $2}' < /proc/meminfo`
661 [ -z "$io_len" ] && die "couldn't determine the amount of memory"
664 if [ ! -z "$output_dir" ]; then
665 [ ! -e "$output_dir" ] && "output dir $output_dir doesn't exist"
666 [ ! -d "$output_dir" ] && "output dir $output_dir isn't a directory"
669 block=`echo $block | sed -e 's/,/ /g'`
670 [ -z "$block" ] && usage "need block devices"
672 [ $min_threads -gt $max_threads ] && \
673 die "min threads $min_threads must be <= min_threads $min_threads"
677 [ ! -e $b ] && die "block device file $b doesn't exist"
678 [ ! -b $b ] && die "$b isn't a block device"
679 last_block=$(($last_block + 1))
680 blocks[$last_block]=$b
683 tmpdir=`mktemp -d /tmp/.surveyXXXXXX` || die "couldn't create tmp dir"
685 echo each test will operate on $io_len"k"
687 tests="sgp_dd ext2_iozone echo_filter"
696 table_set $t 5 0 "C:S"
700 if ! test_iterator $t; then
703 test_results="$test_results $t"
706 [ ! -z "$test_results" ] && (
708 echo "T = number of concurrent threads per device"
709 echo "L = base io operation length, in KB"
710 echo "W/O/R = write/overwrite/read throughput, in MB/s"
711 echo "C = percentage CPU used, both user and system"
712 echo "S = standard deviation in cpu use"
713 echo "B = per-block results: ("`echo ${blocks[*]} | sed -e 's/ /,/g'`")"
717 for t in $test_results; do