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:
27 possible_tests="sgp_dd ext2_iozone echo_filter"
28 run_tests="$possible_tests"
30 # optional output directory
39 [ -e $path ] || continue;
40 [ -f $path ] || die "needed to remove non-file $path"
41 rm -f $path || die "couldn't remove $path"
45 [ ! -z "$output_dir" ] && mv -f $1 $output_dir/$2
48 # only cleanup test runs if we have block devices
49 if [ $last_block != -1 ]; then
50 for pid in ${cleanup_pids[*]}; do
54 for a in ${cleanup_mounts[*]}; do
59 [ ${#tmpdir} == 18 ] && [ -d $tmpdir ] && rm -rf $tmpdir
65 cleanup_pids[$pid]=$pid
69 unset cleanup_pids[$pid]
73 echo $* | sed -e 's/ /,/g'
76 echo "scale=2; $*" | bc
88 0) echo '??' ; return ;;
89 1) echo "$avg:0" ; return ;;
92 avg=`do_bc $avg / $num`
95 local dev=`do_bc \($p - $avg\) \^ 2`
96 tmp=`do_bc $tmp + $dev`
98 tmp=`do_bc sqrt \( $tmp / \($num - 1\) \)`
104 echo " -b <block device to profile>"
105 echo " -d <summary output directory>"
106 echo " -l <max io len>"
107 echo " -t <minimum number of threads per device>"
108 echo " -T <maximum number of threads per device>"
109 echo " -r <tests to run>"
113 # some cute code for handling tables whose columns fit
118 if [ $val -gt ${!target:-0} ]; then
123 local name="_table_$1"
129 eval ${name}_${row}_${col}="'$val'"
131 set_max ${name}_${col}_longest ${#val}
132 set_max ${name}_num_col $(($col + 1))
133 set_max ${name}_num_row $(($row + 1))
137 local name="_table_$1"
140 tmp="${name}_${row}_${col}"
145 local name="_table_$1"
152 tmp="${name}_num_col"
154 tmp="${name}_num_row"
157 # iterate through the columns to find the longest
160 for x in `seq 0 $num_col`; do
161 tmp="${name}_${x}_longest"
163 [ $tmp -eq 0 ] && continue
165 [ $x -eq $((num_col - 1)) ] && sep='\n'
167 fmt="$fmt%-${tmp}s$sep"
170 # nothing in the table to print
171 [ -z "$fmt" ] && return
173 for y in `seq 0 $num_row`; do
175 for x in `seq 0 $num_col`; do
177 # skip this element if the column is empty
178 tmp="${name}_${x}_longest"
179 [ ${!tmp:-0} -eq 0 ] && continue
181 # fill this cell with the value or '' for printf
182 tmp="${name}_${y}_${x}"
183 row="$row'${!tmp:-""}' "
185 eval printf "'$fmt'" $row
189 ######################################################################
192 echo sgp_dd using dio=1 and thr=
195 # it could be making sure that the block dev
196 # isn't in use by something else
200 if ! which sgp_dd; then
201 echo "can't find sgp_dd binary"
207 # it could be making sure that the block dev
208 # isn't in use by something else
217 local bdev=${blocks[$i]};
220 w) ifof="if=/dev/zero of=$bdev" ;;
221 r) ifof="if=$bdev of=/dev/null" ;;
222 *) die "asked to do io with $wor?"
224 echo sgp_dd $ifof bs=$iosize"k" count=$(($io_len / $iosize)) time=1 \
230 awk '($(NF) == "MB/sec") {print $(NF-1)}' < $output
245 ######################################################################
247 ext2_iozone_banner() {
248 echo "iozone -I on a clean ext2 fs"
250 ext2_iozone_config() {
253 ext2_iozone_prepare() {
255 local bdev=${blocks[$index]}
256 local mntpnt=$tmpdir/mount_$index
258 if ! which iozone; then
259 echo "iozone binary not found in PATH"
262 if ! which mke2fs; then
263 echo "mke2fs binary not found in PATH"
267 if ! mkdir -p $mntpnt ; then
268 echo "$mntpnt isn't a directory?"
271 echo making ext2 filesystem on $bdev
272 if ! mke2fs -b 4096 $bdev; then
277 if ! mount -t ext2 $bdev $mntpnt; then
278 echo "couldn't mount $bdev on $mntpnt"
282 cleanup_mounts[$index]="$mntpnt"
285 ext2_iozone_setup() {
288 local f="$tmpdir/mount_$id/iozone"
293 *) die "asked to do io with $wor?"
296 ext2_iozone_start() {
302 local f="$tmpdir/mount_$id/iozone"
307 *) die "asked to do io with $wor?"
310 echo iozone "$args -r ${iosize}k -s ${io_len}k -I -f $f"
312 ext2_iozone_result() {
315 kps=`awk '($2 == "reclen"){results=NR+1}(results == NR){print $3}' \
319 ext2_iozone_cleanup() {
322 local f="$tmpdir/mount_$id/iozone"
327 *) die "asked to do io with $wor?"
330 ext2_iozone_finish() {
332 local mntpnt=$tmpdir/mount_$index
335 unset cleanup_mounts[$index]
337 ext2_iozone_teardown() {
341 ######################################################################
342 # the lctl test_brw via the echo_client on top of the filter
344 # the echo_client setup is nutty enough to warrant its own clenaup
347 declare -a running_names
348 declare -a running_oids
350 cleanup_echo_filter() {
353 for i in `seq 0 $last_block`; do
354 [ -z "${running_oids[$i]}" ] && continue
355 lctl --device "\$"echo_$i destroy ${running_oids[$i]} \
360 for n in ${running_names[*]}; do
361 # I can't believe leading whitespace matters here.
371 for m in $running_modules; do
376 [ ! -z "$running_config" ] && lconf --cleanup $running_config
380 echo_filter_banner() {
381 echo "test_brw on the echo_client on the filter"
383 echo_filter_config() {
385 local bdev=${blocks[$index]}
386 local config="$tmpdir/config.xml"
389 echo "lmc binary not found in PATH"
392 if ! which lconf; then
393 echo "lconf binary not found in PATH"
396 if ! which lctl; then
397 echo "lctl binary not found in PATH"
401 if [ $index = 0 ]; then
402 if ! lmc -m $config --add net \
403 --node localhost --nid localhost --nettype tcp; then
404 echo "error adding localhost net node"
409 if ! lmc -m $config --add ost --ost ost_$index --node localhost \
410 --fstype ext3 --dev $bdev --journal_size 400; then
411 echo "error adding $bdev to config with lmc"
415 # it would be nice to be able to ask lmc to setup an echo client
416 # to the filter here. --add echo_client assumes osc
418 echo_filter_prepare() {
420 local bdev=${blocks[$index]}
421 local config="$tmpdir/config.xml"
422 local name="echo_$index"
423 local uuid="echo_$index_uuid"
425 if [ $index = 0 ]; then
426 if ! lconf --reformat $config; then
427 echo "error setting up with lconf"
430 running_config="$config"
431 if ! grep -q '^obdecho\>' /proc/modules; then
432 if ! modprobe obdecho; then
433 echo "error running modprobe obdecho"
436 running_modules="obdecho"
442 attach echo_client $name $uuid
447 echo "error setting up echo_client $name against ost_$index"
450 running_names[$index]=$name
452 echo_filter_setup() {
456 local name="echo_$id"
462 *) die "asked to do io with $wor?"
465 running_threads=$threads
466 oid=`lctl --device "\$"$name create $threads | \
467 awk '/1 is object id/ { print $6 }'`
468 # XXX need to deal with errors
469 running_oids[$id]=$oid
471 echo_filter_start() {
476 local name="echo_$id"
477 local pages=$(($io_len / 4))
482 *) die "asked to do io with $wor?"
485 echo lctl --threads $threads v "\$"$name \
486 test_brw 1 w v $pages ${running_oids[$i]} p$iosize
488 echo_filter_result() {
493 for mbs in `awk '($8=="MB/s):"){print substr($7,2)}' < $output`; do
494 total=$(do_bc $total + $mbs)
498 echo_filter_cleanup() {
502 local name="echo_$id"
507 *) die "asked to do io with $wor?"
510 lctl --device "\$"$name destroy ${running_oids[$i]} $threads
511 unset running_oids[$i]
513 echo_filter_finish() {
515 # leave real work for _teardown
517 echo_filter_teardown() {
521 ######################################################################
522 # the iteration that drives the tests
532 local vmstat_log="$tmpdir/vmstat.log"
533 local opref="$test-$threads-$iosize-$wor"
535 for i in `seq 0 $last_block`; do
536 ${test}_setup $i $wor $threads
539 echo $test with $threads threads
541 # start up vmstat and record its pid
543 nice -19 vmstat 1 > $vmstat_log 2>&1 &
544 [ $? = 0 ] || die "vmstat failed"
546 pid_now_running $vmstat_pid
548 # start all the tests. each returns a pid to wait on
550 for i in `seq 0 $last_block`; do
551 cmd=`${test}_start $threads $iosize $wor $i`
552 $cmd > $tmpdir/$i 2>&1 &
558 echo -n waiting on pids $pids:
566 # stop vmstat and get cpu use from it
569 pid_has_stopped $vmstat_pid
570 cpu=$(mean_stddev $(awk \
571 '(NR > 3 && NF == 16 && $16 != "id" ) \
572 {print 100 - $16}' < $vmstat_log) )
573 save_output $vmstat_log $opref.vmstat
575 # record each index's test results and sum them
578 for i in `seq 0 $last_block`; do
579 local t=`${test}_result $tmpdir/$i`
580 save_output $tmpdir/$i $opref.$i
581 echo test returned "$t"
583 # some tests return mean:stddev per thread, filter out stddev
584 thru=$(do_bc $thru + $(echo $t | sed -e 's/:.*$//g'))
586 line="("`commas $line`")"
588 for i in `seq 0 $last_block`; do
589 ${test}_cleanup $i $wor $threads
592 # tabulate the results
593 echo $test did $thru mb/s with $cpu
594 table_set $test $my_x $my_y $thru
595 table_set $test $(($my_x + 1)) $my_y $cpu
596 table_set $test $(($my_x + 2)) $my_y $line
601 local thr=$min_threads
606 for i in `seq 0 $last_block`; do
607 if ! ${test}_config $i; then
608 echo "couldn't config $test for bdev ${blocks[$i]}"
609 echo "skipping $test for all block devices"
616 for i in `seq 0 $last_block`; do
617 # don't prepare if _config already failed
618 [ ! -z "$cleanup" ] && break
619 if ! ${test}_prepare $i; then
620 echo "couldn't prepare $test for bdev ${blocks[$i]}"
621 echo "skipping $test for all block devices"
628 while [ -z "$cleanup" -a $thr -lt $(($max_threads + 1)) ]; do
629 for iosize in 64 128; do
630 table_set $test 0 $cur_y $thr
631 table_set $test 1 $cur_y $iosize
632 table_set $test 2 $cur_y "|"
635 table_set $test 3 $cur_y $wor
636 test_one $test 4 $cur_y $thr $iosize $wor
637 cur_y=$(($cur_y + 1))
643 [ -z "$cleanup" ] && cleanup=$last_block
645 if [ "$cleanup" != -1 ]; then
646 for i in `seq $cleanup 0`; do
656 while getopts ":d:b:l:t:T:r:" opt; do
659 d) output_dir=$OPTARG ;;
661 r) run_tests=$OPTARG ;;
662 t) min_threads=$OPTARG ;;
663 T) max_threads=$OPTARG ;;
668 if [ -z "$io_len" ]; then
669 io_len=`awk '($1 == "MemTotal:"){print $2}' < /proc/meminfo`
670 [ -z "$io_len" ] && die "couldn't determine the amount of memory"
673 if [ ! -z "$output_dir" ]; then
674 [ ! -e "$output_dir" ] && "output dir $output_dir doesn't exist"
675 [ ! -d "$output_dir" ] && "output dir $output_dir isn't a directory"
678 block=`echo $block | sed -e 's/,/ /g'`
679 [ -z "$block" ] && usage "need block devices"
681 run_tests=`echo $run_tests | sed -e 's/,/ /g'`
682 [ -z "$run_tests" ] && usage "need to specify tests to run with -r"
683 for t in $run_tests; do
684 if ! echo $possible_tests | grep -q $t ; then
685 die "$t isn't one of the possible tests: $possible_tests"
689 [ $min_threads -gt $max_threads ] && \
690 die "min threads $min_threads must be <= min_threads $min_threads"
694 [ ! -e $b ] && die "block device file $b doesn't exist"
695 [ ! -b $b ] && die "$b isn't a block device"
696 dd if=$b of=/dev/null bs=8192 count=1 || \
697 die "couldn't read 8k from $b, is it alive?"
698 [ ! -b $b ] && die "$b isn't a block device"
699 last_block=$(($last_block + 1))
700 blocks[$last_block]=$b
703 tmpdir=`mktemp -d /tmp/.surveyXXXXXX` || die "couldn't create tmp dir"
705 echo each test will operate on $io_len"k"
709 for t in $run_tests; do
715 table_set $t 5 0 "C:S"
719 if ! test_iterator $t; then
722 test_results="$test_results $t"
725 [ ! -z "$test_results" ] && (
727 echo "T = number of concurrent threads per device"
728 echo "L = base io operation length, in KB"
729 echo "W/O/R = write/overwrite/read throughput, in MB/s"
730 echo "C = percentage CPU used, both user and system"
731 echo "S = standard deviation in cpu use"
732 echo "B = per-block results: ("`echo ${blocks[*]} | sed -e 's/ /,/g'`")"
736 for t in $test_results; do