Whamcloud - gitweb
- add a helper to call bc with a specified precision
[fs/lustre-release.git] / lustre / scripts / bdev-io-survey.sh
1 #!/bin/bash
2
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
5 #
6 #  todo:
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
13 #
14
15 # a temp dir that is setup and torn down for each script run
16 tmpdir=""
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
22 cur_y="0"
23
24 # defaults for some options:
25 min_threads=1
26 max_threads=4
27 possible_tests="sgp_dd ext2_iozone echo_filter"
28 run_tests="$possible_tests"
29
30 # optional output directory
31 output_dir=""
32  
33 die() {
34         echo $* 1>&2
35         exit 1
36 }
37 rm_or_die() {
38         for path in $*; do
39                 [ -e $path ] || continue;
40                 [ -f $path ] || die "needed to remove non-file $path"
41                 rm -f $path || die "couldn't remove $path"
42         done
43 }
44 save_output() {
45         [ ! -z "$output_dir" ] && mv -f $1 $output_dir/$2
46 }
47 cleanup() {
48         # only cleanup test runs if we have block devices
49         if [ $last_block != -1 ]; then
50                 for pid in ${cleanup_pids[*]}; do
51                         kill $pid
52                 done
53                 cleanup_echo_filter
54                 for a in ${cleanup_mounts[*]}; do
55                         umount -f $a
56                 done
57         fi
58
59         [ ${#tmpdir} == 18 ] && [ -d $tmpdir ] && rm -rf $tmpdir
60 }
61 trap cleanup EXIT
62
63 pid_now_running() {
64         local pid=$1
65         cleanup_pids[$pid]=$pid
66 }
67 pid_has_stopped() {
68         local pid=$1
69         unset cleanup_pids[$pid]
70 }
71                                                                                 
72 commas() {
73         echo $* | sed -e 's/ /,/g'
74 }
75 do_bc_scale() {
76         local scale=$1
77         shift
78         echo "scale=$scale; $*" | bc
79 }
80 do_bc() {
81         do_bc_scale 10 $*
82 }
83 mean_stddev() {
84         local points=$*
85
86         local avg=0
87         local num=0
88         for p in $points; do
89                 avg=`do_bc $avg + $p`
90                 num=`do_bc $num + 1`
91         done
92         case $num in
93                 0) echo '??' ; return ;;
94                 1) echo "$avg:0" ; return ;;
95         esac
96
97         avg=`do_bc $avg / $num`
98         local tmp=0
99         for p in $points; do
100                 local dev=`do_bc \($p - $avg\) \^ 2`
101                 tmp=`do_bc $tmp + $dev`
102         done
103         tmp=`do_bc_scale 1 sqrt \( $tmp / \($num - 1\) \)`
104         avg=`do_bc_scale 1 $avg / 1`
105         echo "$avg:$tmp"
106 }
107
108 usage() {
109         echo $*
110         echo "       -b <block device to profile>"
111         echo "       -d <summary output directory>"
112         echo "       -l <max io len>"
113         echo "       -t <minimum number of threads per device>"
114         echo "       -T <maximum number of threads per device>"
115         echo "       -r <tests to run>"
116         exit;
117 }
118
119 # some cute code for handling tables whose columns fit
120 set_max() {
121         local target=$1
122         local val=$2
123                                                                                 
124         if [ $val -gt ${!target:-0} ]; then
125                 eval $target=$val
126         fi
127 }
128 table_set() {
129         local name="_table_$1"
130         local col=$2
131         local row=$3
132         local val=$4
133         local num
134                                                                                 
135         eval ${name}_${row}_${col}="'$val'"
136                                                                                 
137         set_max ${name}_${col}_longest ${#val}
138         set_max ${name}_num_col $(($col + 1))
139         set_max ${name}_num_row $(($row + 1))
140 }
141                                                                                 
142 table_get() {
143         local name="_table_$1"
144         local col=$2
145         local row=$3
146         tmp="${name}_${row}_${col}"
147         echo ${!tmp}
148 }
149                                                                                 
150 table_dump() {
151         local name="_table_$1"
152         local num_col;
153         local num_row;
154         local fmt="";
155         local tmp
156         local sep
157                                                                                 
158         tmp="${name}_num_col"
159         num_col="${!tmp:-0}"
160         tmp="${name}_num_row"
161         num_row="${!tmp:-0}"
162                                                                                 
163         # iterate through the columns to find the longest
164                                                                                 
165         sep=" "
166         for x in `seq 0 $num_col`; do
167                 tmp="${name}_${x}_longest"
168                 tmp=${!tmp:-0}
169                 [ $tmp -eq 0 ] && continue
170                                                                                 
171                 [ $x -eq $((num_col - 1)) ] && sep='\n'
172                                                                                 
173                 fmt="$fmt%-${tmp}s$sep"
174         done
175                                                                                 
176         # nothing in the table to print
177         [ -z "$fmt" ] && return
178                                                                                 
179         for y in `seq 0 $num_row`; do
180                 local row=""
181                 for x in `seq 0 $num_col`; do
182                                                                                 
183                         # skip this element if the column is empty
184                         tmp="${name}_${x}_longest"
185                         [ ${!tmp:-0} -eq 0 ] && continue
186                                                                                 
187                         # fill this cell with the value or '' for printf
188                         tmp="${name}_${y}_${x}"
189                         row="$row'${!tmp:-""}' "
190                 done
191                 eval printf "'$fmt'" $row
192         done
193 }
194
195 ######################################################################
196 # the sgp_dd tests
197 sgp_dd_banner() {
198         echo sgp_dd using dio=1 and thr=
199 }
200 sgp_dd_config() {
201         # it could be making sure that the block dev
202         # isn't in use by something else
203         local nothing=0
204 }
205 sgp_dd_prepare() {
206         if ! which sgp_dd; then
207                 echo "can't find sgp_dd binary"
208                 return 1
209         fi
210         return 0
211 }
212 sgp_dd_setup() {
213         # it could be making sure that the block dev
214         # isn't in use by something else
215         local nothing=0
216 }
217 sgp_dd_start() {
218         local threads=$1
219         local iosize=$2
220         local wor=$3
221         local i=$4
222         local ifof;
223         local bdev=${blocks[$i]};
224
225         case "$wor" in
226                 w) ifof="if=/dev/zero of=$bdev" ;;
227                 r) ifof="if=$bdev of=/dev/null" ;;
228                 *) die "asked to do io with $wor?"
229         esac
230         echo sgp_dd $ifof bs=$iosize"k" count=$(($io_len / $iosize)) time=1 \
231                         dio=1 thr=$threads
232 }
233 sgp_dd_result() {
234         local output=$1
235
236         awk '($(NF) == "MB/sec") {print $(NF-1)}' < $output
237 }
238 sgp_dd_cleanup() {
239         # got me
240         local nothing=0
241 }
242 sgp_dd_finish() {
243         # got me
244         local nothing=0
245 }
246 sgp_dd_teardown() {
247         # got me
248         local nothing=0
249 }
250
251 ######################################################################
252 # the iozone tests
253 ext2_iozone_banner() {
254         echo "iozone -I on a clean ext2 fs"
255 }
256 ext2_iozone_config() {
257         local nothing=0
258 }
259 ext2_iozone_prepare() {
260         local index=$1
261         local bdev=${blocks[$index]}
262         local mntpnt=$tmpdir/mount_$index
263
264         if ! which iozone; then
265                 echo "iozone binary not found in PATH"
266                 return 1
267         fi
268         if ! which mke2fs; then
269                 echo "mke2fs binary not found in PATH"
270                 return 1
271         fi
272
273         if ! mkdir -p $mntpnt ; then
274                 echo "$mntpnt isn't a directory?"
275         fi
276
277         echo making ext2 filesystem on $bdev
278         if ! mke2fs -b 4096 $bdev; then
279                 echo "mke2fs failed"
280                 return 1;
281         fi
282
283         if ! mount -t ext2 $bdev $mntpnt; then 
284                 echo "couldn't mount $bdev on $mntpnt"
285                 return 1;
286         fi
287
288         cleanup_mounts[$index]="$mntpnt"
289         return 0
290 }
291 ext2_iozone_setup() {
292         local id=$1
293         local wor=$2
294         local f="$tmpdir/mount_$id/iozone"
295
296         case "$wor" in
297                 w) rm -f $f ;;
298                 r) ;;
299                 *) die "asked to do io with $wor?"
300         esac
301 }
302 ext2_iozone_start() {
303         local threads=$1
304         local iosize=$2
305         local wor=$3
306         local id=$4
307         local args;
308         local f="$tmpdir/mount_$id/iozone"
309
310         case "$wor" in
311                 w) args="-i 0 -w" ;;
312                 r) args="-i 1 -w" ;;
313                 *) die "asked to do io with $wor?"
314         esac
315
316         echo iozone "$args -r ${iosize}k -s ${io_len}k -I -f $f"
317 }
318 ext2_iozone_result() {
319         local output=$1
320
321         kps=`awk '($2 == "reclen"){results=NR+1}(results == NR){print $3}' \
322                 < $output`
323         do_bc_scale 0 "$kps / 1024"
324 }
325 ext2_iozone_cleanup() {
326         local id=$1
327         local wor=$2
328         local f="$tmpdir/mount_$id/iozone"
329
330         case "$wor" in
331                 w) ;;
332                 r) rm -f $f ;;
333                 *) die "asked to do io with $wor?"
334         esac
335 }
336 ext2_iozone_finish() {
337         local index=$1
338         local mntpnt=$tmpdir/mount_$index
339
340         umount -f $mntpnt
341         unset cleanup_mounts[$index]
342 }
343 ext2_iozone_teardown() {
344         local nothing=0
345 }
346
347 ######################################################################
348 # the lctl test_brw via the echo_client on top of the filter
349
350 # the echo_client setup is nutty enough to warrant its own clenaup
351 running_config=""
352 running_modules=""
353 declare -a running_names
354 declare -a running_oids
355
356 cleanup_echo_filter() {
357         local i
358
359         for i in `seq 0 $last_block`; do
360                 [ -z "${running_oids[$i]}" ] && continue
361                 lctl --device "\$"echo_$i destroy ${running_oids[$i]} \
362                         $running_threads
363         done
364         unset running_oids
365
366         for n in ${running_names[*]}; do
367 # I can't believe leading whitespace matters here.
368 lctl << EOF
369 cfg_device $n
370 cleanup
371 detach
372 quit
373 EOF
374         done
375         running_names=""
376
377         for m in $running_modules; do
378                 rmmod $m
379         done
380         running_modules=""
381
382         [ ! -z "$running_config" ] && lconf --cleanup $running_config
383         running_config=""
384 }
385
386 echo_filter_banner() {
387         echo "test_brw on the echo_client on the filter" 
388 }
389 echo_filter_config() {
390         local index=$1
391         local bdev=${blocks[$index]}
392         local config="$tmpdir/config.xml"
393
394         if ! which lmc; then
395                 echo "lmc binary not found in PATH"
396                 return 1
397         fi
398         if ! which lconf; then
399                 echo "lconf binary not found in PATH"
400                 return 1
401         fi
402         if ! which lctl; then
403                 echo "lctl binary not found in PATH"
404                 return 1
405         fi
406
407         if [ $index = 0 ]; then
408                 if ! lmc -m $config --add net  \
409                         --node localhost --nid localhost --nettype tcp; then
410                         echo "error adding localhost net node"
411                         return 1
412                 fi
413         fi
414
415         if ! lmc -m $config --add ost --ost ost_$index --node localhost \
416                         --fstype ext3 --dev $bdev --journal_size 400; then
417                 echo "error adding $bdev to config with lmc"
418                 return 1
419         fi
420
421         # it would be nice to be able to ask lmc to setup an echo client
422         # to the filter here.  --add echo_client assumes osc
423 }
424 echo_filter_prepare() {
425         local index=$1
426         local bdev=${blocks[$index]}
427         local config="$tmpdir/config.xml"
428         local name="echo_$index"
429         local uuid="echo_$index_uuid"
430
431         if [ $index = 0 ]; then
432                 if ! lconf --reformat $config; then
433                         echo "error setting up with lconf"
434                         return 1;
435                 fi
436                 running_config="$config"
437                 if ! grep -q '^obdecho\>' /proc/modules; then
438                         if ! modprobe obdecho; then
439                                 echo "error running modprobe obdecho"
440                                 return 1;
441                         fi
442                         running_modules="obdecho"
443                 fi
444         fi
445
446 lctl << EOF
447         newdev
448         attach echo_client $name $uuid
449         setup ost_$index
450         quit
451 EOF
452         if [  $? != 0 ]; then
453                 echo "error setting up echo_client $name against ost_$index"
454                 return 1
455         fi
456         running_names[$index]=$name
457 }
458 echo_filter_setup() {
459         local id=$1
460         local wor=$2
461         local threads=$3
462         local name="echo_$id"
463         local oid
464
465         case "$wor" in
466                 w) ;;
467                 r) return ;;
468                 *) die "asked to do io with $wor?"
469         esac
470
471         running_threads=$threads
472         oid=`lctl --device "\$"$name create $threads | \
473                 awk '/1 is object id/ { print $6 }'`
474         # XXX need to deal with errors
475         running_oids[$id]=$oid
476 }
477 echo_filter_start() {
478         local threads=$1
479         local iosize=$2
480         local wor=$3
481         local id=$4
482         local name="echo_$id"
483         local pages=$(($io_len / 4))
484
485         case "$wor" in
486                 w) args="-i 0 -w" ;;
487                 r) args="-i 1 -w" ;;
488                 *) die "asked to do io with $wor?"
489         esac
490
491         echo lctl --threads $threads v "\$"$name \
492                 test_brw 1 w v $pages ${running_oids[$i]} p$iosize
493 }
494 echo_filter_result() {
495         local output=$1
496         local total=0
497         local mbs
498
499         for mbs in `awk '($8=="MB/s):"){print substr($7,2)}' < $output`; do
500                 total=$(do_bc $total + $mbs)
501         done
502         do_bc_scale $total / 1
503 }
504 echo_filter_cleanup() {
505         local id=$1
506         local wor=$2
507         local threads=$3
508         local name="echo_$id"
509
510         case "$wor" in
511                 w) return ;;
512                 r) ;;
513                 *) die "asked to do io with $wor?"
514         esac
515
516         lctl --device "\$"$name destroy ${running_oids[$i]} $threads
517         unset running_oids[$i]
518 }
519 echo_filter_finish() {
520         local index=$1
521         # leave real work for _teardown
522 }
523 echo_filter_teardown() {
524         cleanup_echo_filter
525 }
526
527 ######################################################################
528 # the iteration that drives the tests
529
530 test_one() {
531         local test=$1
532         local my_x=$2
533         local threads=$3
534         local iosize=$4
535         local wor=$5
536         local vmstat_pid
537         local vmstat_log="$tmpdir/vmstat.log"
538         local opref="$test-$threads-$iosize-$wor"
539         local -a iostat_pids
540         # sigh.  but this makes it easier to dump into the tables
541         local -a read_req_s
542         local -a mb_s
543         local -a write_req_s
544         local -a sects_req
545         local -a queued_reqs
546         local -a service_ms
547
548         for i in `seq 0 $last_block`; do
549                 ${test}_setup $i $wor $threads
550         done
551
552         echo $test with $threads threads
553
554         # start up vmstat and record its pid
555         nice -19 vmstat 1 > $vmstat_log 2>&1 &
556         [ $? = 0 ] || die "vmstat failed"
557         vmstat_pid=$!
558         pid_now_running $vmstat_pid
559
560         # start up each block device's iostat
561         for i in `seq 0 $last_block`; do
562                 nice -19 iostat -x ${blocks[$i]} 1 | awk \
563                         '($1 == "'${blocks[$i]}'"){print $0; fflush()}' \
564                         > $tmpdir/iostat.$i &
565                 local pid=$!
566                 pid_now_running $pid
567                 iostat_pids[$i]=$pid
568         done
569
570         # start all the tests.  each returns a pid to wait on
571         pids=""
572         for i in `seq 0 $last_block`; do
573                 local cmd=`${test}_start $threads $iosize $wor $i`
574                 $cmd > $tmpdir/$i 2>&1 &
575                 local pid=$!
576                 pids="$pids $pid"
577                 pid_now_running $pid
578         done
579
580         echo -n waiting on pids $pids:
581         for p in $pids; do
582                 wait $p
583                 echo -n .
584                 pid_has_stopped $p
585         done
586
587         # stop vmstat and all the iostats
588         kill $vmstat_pid
589         pid_has_stopped $vmstat_pid
590         for i in `seq 0 $last_block`; do
591                 local pid=${iostat_pids[$i]}
592                 [ -z "$pid" ] && continue
593
594                 kill $pid
595                 unset iostat_pids[$i]
596                 pid_has_stopped $pid
597         done
598
599         # collect the results of vmstat and iostat
600         cpu=$(mean_stddev $(awk \
601               '(NR > 3 && NF == 16 && $16 != "id" )     \
602                 {print 100 - $16}' < $vmstat_log) )
603         save_output $vmstat_log $opref.vmstat
604
605         for i in `seq 0 $last_block`; do
606                 read_req_s[$i]=$(mean_stddev $(awk \
607                       '(NR > 1) {print $4}' < $tmpdir/iostat.$i) )
608                 write_req_s[$i]=$(mean_stddev $(awk \
609                       '(NR > 1) {print $5}' < $tmpdir/iostat.$i) )
610                 sects_req[$i]=$(mean_stddev $(awk \
611                       '(NR > 1) {print $10}' < $tmpdir/iostat.$i) )
612                 queued_reqs[$i]=$(mean_stddev $(awk \
613                       '(NR > 1) {print $11}' < $tmpdir/iostat.$i) )
614                 service_ms[$i]=$(mean_stddev $(awk \
615                       '(NR > 1) {print $13}' < $tmpdir/iostat.$i) )
616
617                 save_output $tmpdir/iostat.$i $opref.iostat.$i
618         done
619
620         # record each index's test results and sum them
621         thru=0
622         for i in `seq 0 $last_block`; do
623                 local t=`${test}_result $tmpdir/$i`
624                 save_output $tmpdir/$i $opref.$i
625                 echo test returned "$t"
626                 mb_s[$i]="$t"
627                 # some tests return mean:stddev per device, filter out stddev
628                 thru=$(do_bc $thru + $(echo $t | sed -e 's/:.*$//g'))
629         done
630
631         for i in `seq 0 $last_block`; do
632                 ${test}_cleanup $i $wor $threads
633         done
634
635         # tabulate the results
636         echo $test did $thru mb/s with $cpu
637         table_set $test $my_x $cur_y `do_bc_scale 2 $thru / 1`
638         table_set $test $(($my_x + 1)) $cur_y $cpu
639
640         for i in `seq 0 $last_block`; do
641                 cur_y=$(($cur_y + 1))
642                 table_set $test $(($my_x)) $cur_y ${mb_s[$i]}
643                 table_set $test $(($my_x + 1)) $cur_y ${read_req_s[$i]}
644                 table_set $test $(($my_x + 2)) $cur_y ${write_req_s[$i]}
645                 table_set $test $(($my_x + 3)) $cur_y ${sects_req[$i]}
646                 table_set $test $(($my_x + 4)) $cur_y ${queued_reqs[$i]}
647                 table_set $test $(($my_x + 5)) $cur_y ${service_ms[$i]}
648         done
649
650         cur_y=$(($cur_y + 1))
651 }
652
653 test_iterator() {
654         local test=$1
655         local thr=$min_threads
656         local cleanup=""
657         local rc=0
658         local i
659         
660         for i in `seq 0 $last_block`; do
661                 if ! ${test}_config $i; then
662                         echo "couldn't config $test for bdev ${blocks[$i]}"
663                         echo "skipping $test for all block devices"
664                         cleanup=$(($i - 1))
665                         rc=1;
666                         break
667                 fi
668         done
669
670         for i in `seq 0 $last_block`; do
671                 # don't prepare if _config already failed
672                 [ ! -z "$cleanup" ] && break
673                 if ! ${test}_prepare $i; then
674                         echo "couldn't prepare $test for bdev ${blocks[$i]}"
675                         echo "skipping $test for all block devices"
676                         cleanup=$(($i - 1))
677                         rc=1;
678                         break
679                 fi
680         done
681
682         while [ -z "$cleanup" -a $thr -lt $(($max_threads + 1)) ]; do
683                 for iosize in 64 128; do
684                         table_set $test 0 $cur_y $thr
685                         table_set $test 1 $cur_y $iosize
686
687                         for wor in w r; do
688                                 table_set $test 2 $cur_y $wor
689                                 test_one $test 3 $thr $iosize $wor
690                         done
691                 done
692                 thr=$(($thr + $thr))
693         done
694
695         [ -z "$cleanup" ] && cleanup=$last_block
696
697         if [ "$cleanup" != -1 ]; then
698                 for i in `seq $cleanup 0`; do
699                         ${test}_finish $i
700                 done
701         fi
702
703         ${test}_teardown
704
705         return $rc;
706 }
707
708 while getopts ":d:b:l:t:T:r:" opt; do
709         case $opt in
710                 b) block=$OPTARG                 ;;
711                 d) output_dir=$OPTARG                 ;;
712                 l) io_len=$OPTARG                       ;;
713                 r) run_tests=$OPTARG                    ;;
714                 t) min_threads=$OPTARG                  ;;
715                 T) max_threads=$OPTARG                  ;;
716                 \?) usage
717         esac
718 done
719
720 if [ -z "$io_len" ]; then
721         io_len=`awk '($1 == "MemTotal:"){print $2}' < /proc/meminfo`
722         [ -z "$io_len" ] && die "couldn't determine the amount of memory"
723 fi
724
725 if [ ! -z "$output_dir" ]; then
726         [ ! -e "$output_dir" ] && "output dir $output_dir doesn't exist"
727         [ ! -d "$output_dir" ] && "output dir $output_dir isn't a directory"
728 fi
729
730 block=`echo $block | sed -e 's/,/ /g'`
731 [ -z "$block" ] && usage "need block devices"
732
733 run_tests=`echo $run_tests | sed -e 's/,/ /g'`
734 [ -z "$run_tests" ] && usage "need to specify tests to run with -r"
735 for t in $run_tests; do
736         if ! echo $possible_tests | grep -q $t ; then
737                 die "$t isn't one of the possible tests: $possible_tests"
738         fi
739 done
740
741 [ $min_threads -gt $max_threads ] && \
742         die "min threads $min_threads must be <= min_threads $min_threads"
743
744 last_block=-1
745 for b in $block; do
746         [ ! -e $b ] && die "block device file $b doesn't exist"
747         [ ! -b $b ] && die "$b isn't a block device"
748         dd if=$b of=/dev/null bs=8192 count=1 || \
749                 die "couldn't read 8k from $b, is it alive?"
750         [ ! -b $b ] && die "$b isn't a block device"
751         last_block=$(($last_block + 1))
752         blocks[$last_block]=$b
753 done    
754
755 tmpdir=`mktemp -d /tmp/.surveyXXXXXX` || die "couldn't create tmp dir"
756
757 echo each test will operate on $io_len"k"
758
759 test_results=""
760
761 for t in $run_tests; do
762
763         table_set $t 0 0 "T"
764         table_set $t 1 0 "L"
765         table_set $t 2 0 "m"
766         table_set $t 3 0 "A"
767         table_set $t 4 0 "C"
768         table_set $t 3 1 "MB"
769         table_set $t 4 1 "rR"
770         table_set $t 5 1 "wR"
771         table_set $t 6 1 "SR"
772         table_set $t 7 1 "Q"
773         table_set $t 8 1 "ms"
774         cur_y=2;
775
776         if ! test_iterator $t; then
777                 continue;
778         fi
779         test_results="$test_results $t"
780 done
781
782 [ ! -z "$test_results" ] && (
783         echo
784         echo "T = number of concurrent threads per device"
785         echo "L = base io operation length, in KB"
786         echo "m = IO method: read, write, or over-write"
787         echo "C = percentage CPU used, both user and system"
788         echo "MB/s = per-device throughput"
789         echo "rR = read requests issued to the device per second"
790         echo "wR = write requests issued to the device per second"
791         echo "SR = sectors per request; sectors tend to be 512 bytes"
792         echo "Q = the average number of requests queued on the device"
793         echo "ms = the average ms taken by the device to service a req"
794         echo
795         echo "foo:bar represents a mean of foo with a stddev of bar"
796 )
797
798 for t in $test_results; do
799         ${t}_banner
800         table_dump $t
801 done