xref: /onnv-gate/usr/src/cmd/filebench/fbscript/filebench.pl (revision 9356:2ff1c33b24c1)
1#!/usr/bin/perl
2#
3# CDDL HEADER START
4#
5# The contents of this file are subject to the terms of the
6# Common Development and Distribution License (the "License").
7# You may not use this file except in compliance with the License.
8#
9# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10# or http://www.opensolaris.org/os/licensing.
11# See the License for the specific language governing permissions
12# and limitations under the License.
13#
14# When distributing Covered Code, include this CDDL HEADER in each
15# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16# If applicable, add the following below this CDDL HEADER, with the
17# fields enclosed by brackets "[]" replaced with your own identifying
18# information: Portions Copyright [yyyy] [name of copyright owner]
19#
20# CDDL HEADER END
21#
22#
23# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24# Use is subject to license terms.
25#
26
27use POSIX;
28use Socket;
29
30my $MULTI_CLIENT = 0;
31my $USE_XANADU = 0;
32my $TIMEOUT = 60;
33my $EOL = "\n";
34my $FILEBENCH = "/usr/benchmarks/filebench";
35my $PROG = "/usr/benchmarks/filebench/bin/go_filebench";
36my $SHAREDFILEALLOCATOR;
37my $TARGETPATH;
38my $TARGETDIR;
39my $FB_MASTERPATH;
40my $STATSBASE;
41my $CONFNAME;
42my $FSCRIPT;
43my $SCRIPT_NO;
44my @CLIENTLIST = ();
45my %CLIENTHASH = ();
46my @CONFLIST;
47my %MULTIDATA = ();
48my %DEFDATA = ();
49my %CONFDATA = ();
50my %STATSHASH = ();
51my $OPTIONFLAGS = "cleanupstorage";
52@ext_stats=();
53@file_stats=();
54@arg_stats=();
55@pid_arr=();
56
57# The following if test caters for running benchpoint from an alternative path
58#if (-r $ENV{"FILEBENCH") {
59#	$FILEBENCH = $ENV{"FILEBENCH"};
60#}
61
62##############################################################################
63## Configuration hash data operations
64##############################################################################
65
66# This sub allows a function program to extract the base directory for filebench
67sub get_FILEBENCH {
68    return ($FILEBENCH);
69}
70
71sub get_STATSBASE {
72    return ($STATSBASE);
73}
74
75sub get_CONFNAME {
76    return ($CONFNAME);
77}
78
79sub multi_putval {
80    my ($key) = shift;
81    my ($val) = shift;
82    @{MULTIDATA{$key}} = ();
83    push(@{ $MULTIDATA{$key} }, $val);
84}
85
86sub multi_getval {
87    my ($key) = shift;
88    return ("@{$MULTIDATA{$key}}");
89}
90
91sub multi_exists {
92    my ($key) = shift;
93    if (exists($MULTIDATA{$key})) {
94	return (1);
95    }
96    return (0);
97}
98
99sub conf_getval {
100    my ($key) = shift;
101    return ("@{$CONFDATA{$key}}");
102}
103
104sub conf_reqval {
105    my ($key) = shift;
106
107    if (exists($CONFDATA{$key})) {
108	return ("@{$CONFDATA{$key}}");
109    }
110    print "ERROR: required key \"$key\" missing from configuration\n";
111    exit(1);
112}
113
114sub conf_exists {
115    my ($key) = shift;
116    if (exists($CONFDATA{$key})) {
117	return (1);
118    }
119    return (0);
120}
121
122sub conf_hash {
123    return(%CONFDATA);
124}
125
126##############################################################################
127## Filebench Operations
128##############################################################################
129
130sub op_init {
131}
132
133sub op_load {
134    my ($workload) = shift;
135    $scriptname = conf_reqval("statsdir") . "/thisrun.f";
136
137    if($workload ne '') {
138	print ("Creating Client Script " . $scriptname . "\n");
139	open (FSCRIPT, ">$scriptname");
140	chmod (0755, $scriptname);
141	print FSCRIPT "#!$PROG -f\n\n";
142	# Load the df
143	print FSCRIPT "load $workload\n";
144	# Load the user defined defaults
145	op_load_defaults();
146
147	# enable multiclient, if needed
148	if ($MULTI_CLIENT == 1) {
149	    print FSCRIPT "enable multi master=".multi_getval("masterhost").", client=".conf_getval("myname")."\n";
150	}
151	# Create the associated files and filesets
152	print FSCRIPT "create filesets\n";
153
154    }
155    $SCRIPT_NO = 1;
156    return(0);
157}
158
159sub op_set {
160    my ($var, $val) = @_;
161    if($var eq 'debug') {
162	    print FSCRIPT "debug $val\n";
163    } elsif($var ne '') {
164	    print FSCRIPT "set \$$var=$val\n";
165    }
166    return(0);
167}
168
169sub op_eventrate {
170    my ($eventrate) = shift;
171	if ($eventrate ne '') {
172		print FSCRIPT "eventgen rate=$eventrate\n";
173		return(0);
174	}
175}
176
177sub op_run {
178    my ($time) = shift;
179    print FSCRIPT "run $time\n";
180    return(0);
181}
182
183sub op_sleep {
184    my ($time) = shift;
185    print FSCRIPT "sleep $time\n";
186    return(0);
187}
188
189sub op_msg {
190    my ($msg) = shift;
191    print FSCRIPT "echo \"$msg\"\n";
192    return(0);
193}
194
195sub op_quit {
196    # Shutdown the appropriate processes
197    print FSCRIPT "shutdown processes\n";
198
199    # remove filesets, if requested
200    if (conf_exists("cleanupstorage") == 1) {
201	printf FSCRIPT "shutdown filesets\n";
202    }
203
204    # Quit filebench
205    print FSCRIPT "quit\n";
206    close(FSCRIPT);
207}
208
209sub op_statsdir {
210    print FSCRIPT "stats directory ".conf_reqval("statsdir")."\n";
211    return(0);
212}
213
214sub op_indiv_vars {
215    my ($ivar) = shift;
216    print FSCRIPT "echo \"\$$ivar\"\n";
217    my ($imatch, $ierr, $ibefore, $iafter) = &expect(FSCRIPT,
218						 $TIMEOUT, "filebench>");
219
220    $ibefore =~ /(.*): (.*): (-*\d+)/;
221    $imatch = $3;
222    $imatch =~ s/^\s+//;
223    chomp($imatch);
224    return($imatch);
225}
226
227sub op_indiv_stats {
228    my ($var) = shift;
229    print FSCRIPT "echo \"\${stats.$var}\"\n";
230    my ($match, $err, $before, $after) = &expect(FSCRIPT,
231						 $TIMEOUT, "filebench>");
232
233    $before =~ /(.*): (.*): (-*\d+)/;
234    $match = $3;
235    $match =~ s/^\s+//;
236    chomp($match);
237    return($match);
238}
239
240sub op_stats {
241    my ($time) = shift;
242    my ($warmup) = shift;
243    my ($statsfile) = shift;
244    my $mstrstatsdir = $STATSBASE."/".$CONFNAME;
245
246    if ($MULTI_CLIENT == 1) {
247	print FSCRIPT "domultisync value=1\n";
248    }
249
250    # Create the associated processes and start them running
251    print FSCRIPT "create processes\n";
252
253    if ($warmup ne '') {
254	print FSCRIPT "warmup $warmup\n";
255    }
256
257    if (($time ne '') && ($statsfile ne '')) {
258	# Clear the current statistics buffers
259	print FSCRIPT "stats clear\n";
260
261	# Start external statistics collection (if any)
262	# Note all statistics arrays MUST be the same length !
263	if (@ext_stats != ()) {
264	    if (($#ext_stats == $#file_stats) && ($#ext_stats == $#arg_stats)) {
265		$script = $mstrstatsdir . "/stats$SCRIPT_NO.sh";
266		open (RUNSCRIPT, ">$script");
267		chmod (0755, $script);
268		print FSCRIPT "system \"$script\"\n";
269		$SCRIPT_NO++;
270		$index=0;
271		foreach my $ext (@ext_stats) {
272		    print RUNSCRIPT "$FILEBENCH/scripts/collect_$ext $ext $file_stats[$index] ";
273		    print RUNSCRIPT  $mstrstatsdir;
274		    print RUNSCRIPT " $time $FILEBENCH $arg_stats[$index] &\n";
275		    $index++;
276		}
277	    }
278	}
279	close(RUNSCRIPT);
280
281	# Sleep for the run time
282	print FSCRIPT "sleep $time\n";
283
284	# Snap the statistics
285	print FSCRIPT "stats snap\n";
286
287	# Dump the statistics to a raw file - out required due to filename constraint
288	if ($MULTI_CLIENT == 1) {
289	    print FSCRIPT "domultisync value=2\n";
290	    print FSCRIPT "stats multidump \"$statsfile.out\"\n";
291	} else {
292	    print FSCRIPT "stats dump \"$statsfile.out\"\n";
293	}
294
295	# Statistics reaping occurs here
296	if (@ext_stats != ()) {
297	    if (($#ext_stats == $#file_stats) && ($#ext_stats == $#arg_stats)) {
298		$script = $mstrstatsdir . "/stats$SCRIPT_NO.sh";
299		open (RUNSCRIPT, ">$script");
300		chmod (0755, $script);
301		print FSCRIPT "system \"$script\"\n";
302		$SCRIPT_NO++;
303		foreach my $ext (@ext_stats) {
304		    print RUNSCRIPT "$FILEBENCH/scripts/kill_stats $ext &\n";
305		}
306		close(RUNSCRIPT);
307	    }
308	}
309
310	# Dump the statistics to a Xanadu compatible XML file
311	if ($USE_XANADU) {
312	    op_xmlstats($statsfile);
313
314	    $script = $mstrstatsdir . "/stats$SCRIPT_NO.pl";
315	    open (RUNSCRIPT, ">$script");
316	    chmod (0755, $script);
317	    print FSCRIPT "system \"$script\"\n";
318	    $SCRIPT_NO++;
319
320	    # The following loop adds the benchpoint run parameters and statistics into the filebench XML file
321	    # We capture the meta data from the start of the filebench xml file
322	    print RUNSCRIPT "#!/usr/bin/perl\n";
323	    print RUNSCRIPT "\$phase=1;\n";
324	    print RUNSCRIPT "open(STATSFILE,\"<".$mstrstatsdir."/$statsfile.xml\");\n";
325	    print RUNSCRIPT "open(OSTATSFILE,\">".$mstrstatsdir."/$statsfile.new.xml\");\n";
326	    print RUNSCRIPT "while (<STATSFILE>) {\n";
327	    print RUNSCRIPT "\t\$temp=\$_;\n";
328	    print RUNSCRIPT "\tif ((!((/.*meta.*/) || (/.*stat_doc.*/))) && (\$phase == 1)) {\n";
329	    print RUNSCRIPT "\t\topen(XMLFILE,\"<".$mstrstatsdir."/$statsfile.config.xml\");\n";
330	    print RUNSCRIPT "\t\twhile (<XMLFILE>) {\n";
331	    print RUNSCRIPT "\t\t\tprint OSTATSFILE \$_;\n";
332	    print RUNSCRIPT "\t\t}\n";
333	    print RUNSCRIPT "\t\tclose(XMLFILE);\n";
334	    print RUNSCRIPT "\t\t\$phase++;\n";
335	    print RUNSCRIPT "\t}\n";
336	    print RUNSCRIPT "\tprint OSTATSFILE \$temp;\n";
337	    print RUNSCRIPT "}\n";
338	    print RUNSCRIPT "close(STATSFILE);\n";
339	    print RUNSCRIPT "close(OSTATSFILE);\n";
340	    print RUNSCRIPT "unlink(\"".$mstrstatsdir."/$statsfile.xml\");\n";
341	    print RUNSCRIPT "unlink(\"".$mstrstatsdir."/$statsfile.config.xml\");\n";
342	    print RUNSCRIPT "system(\"mv ".$mstrstatsdir."/$statsfile.new.xml ".$mstrstatsdir."/$statsfile.xml\");\n";
343
344	    $script = $mstrstatsdir . "/stats$SCRIPT_NO.sh";
345	    open (RUNSCRIPT, ">$script");
346	    chmod (0755, $script);
347	    print FSCRIPT "system \"$script\"\n";
348	    $SCRIPT_NO++;
349
350	    print RUNSCRIPT "mkdir ".$mstrstatsdir."/xml\n";
351	    print RUNSCRIPT "mkdir ".$mstrstatsdir."/html\n";
352
353	    print RUNSCRIPT "mv ".$mstrstatsdir."/$statsfile.xml ".$mstrstatsdir."/xml/$statsfile.xml\n";
354
355	    # Process XML file using Xanadu 2
356	    print RUNSCRIPT "$FILEBENCH/xanadu/scripts/xanadu import ".$mstrstatsdir." ".$mstrstatsdir."/xml ".conf_reqval("function")."-".$mstrstatsdir."\n";
357	    print RUNSCRIPT "$FILEBENCH/xanadu/scripts/xanadu export ".$mstrstatsdir."/xml ".$mstrstatsdir."/html\n";
358	    close(RUNSCRIPT);
359	}
360    }
361    return(0);
362}
363
364sub op_xmlstats {
365    my ($statsfile) = shift;
366    my $mstrstatsdir = $STATSBASE."/".$CONFNAME;
367    if($statsfile ne '') {
368	print FSCRIPT "stats xmldump \"$statsfile.xml\"\n";
369
370	# The following loop adds the benchpoint run parameters and statistics into a temporary XML file
371	open(OSTATSFILE,">".$mstrstatsdir."/$statsfile.config.xml");
372	%CONFHASH = conf_hash();
373	# There is no test for whether CONFHASH contains no keys
374	# The following two lines is to obtain the stats run directory name for xanadu meta data
375	print OSTATSFILE "<meta name=\"RunId\" value=\"".conf_reqval("function")."-".$mstrstatsdir."\"/>\n";
376	print OSTATSFILE "<stat_group name=\"Benchpoint Configuration\">\n";
377	print OSTATSFILE "<cell_list>\n";
378	foreach $k (keys(%CONFHASH)) {
379	    print OSTATSFILE "<cell>@{ $CONFHASH{$k} }</cell>\n";
380	}
381	print OSTATSFILE "</cell_list>\n";
382	print OSTATSFILE "<dim_list>\n";
383	print OSTATSFILE "<dim>\n";
384	print OSTATSFILE "<dimval>Value</dimval>\n";
385	print OSTATSFILE "</dim>\n";
386	print OSTATSFILE "<dim>\n";
387	foreach $k (keys(%CONFHASH)) {
388	    print OSTATSFILE "<dimval>$k</dimval>\n";
389	}
390	print OSTATSFILE "</dim>\n";
391	print OSTATSFILE "</dim_list>\n";
392	print OSTATSFILE "</stat_group>\n";
393	close(OSTATSFILE);
394
395	return(0);
396    }
397    return(1);
398}
399
400sub op_command {
401    my ($command) = shift;
402	if($command ne '') {
403	    print FSCRIPT "$command\n";
404	}
405	return(0);
406}
407
408sub op_statshash {
409    op_indiv_stats("iocount");
410    $STATSHASH{"iocount"} = op_indiv_stats("iocount");
411    $STATSHASH{"iorate"} = op_indiv_stats("iorate");
412    $STATSHASH{"ioreadrate"} = op_indiv_stats("ioreadrate");
413    $STATSHASH{"iowriterate"} = op_indiv_stats("iowriterate");
414    $STATSHASH{"iobandwidth"} = op_indiv_stats("iobandwidth");
415    $STATSHASH{"iolatency"} = op_indiv_stats("iolatency");
416    $STATSHASH{"iocpu"} = op_indiv_stats("iocpu");
417    $STATSHASH{"oheadcpu"} = op_indiv_stats("oheadcpu");
418    $STATSHASH{"iowait"} = op_indiv_stats("iowait");
419    $STATSHASH{"syscpu"} = op_indiv_stats("syscpu");
420    $STATSHASH{"iocpusys"} = op_indiv_stats("iocpusys");
421    return(%STATSHASH);
422}
423
424sub op_load_defaults {
425# The following code causes an intermittent bug - may be fixed at a later date
426# Prevents the capture of filebench default parameters
427#    print FSCRIPT "vars\n";
428#    my ($match, $err, $before, $after) = &expect(FSCRIPT,
429#						 $TIMEOUT, "filebench>");
430#    chomp($before);
431#    $before =~ /(.*): (.*): (.*)/;
432#    $match = $3;
433#    my @vars = split(/ /, $match);
434#    my $value = "";
435#    # Cater for the default filebench commands
436#    foreach my $var (@vars) {
437#        if (!conf_exists($var)) {
438#            $var =~ s/ //g;
439#	    if ($var ne '') {
440#		$value = op_indiv_vars($var);
441#       	        push(@{ $CONFDATA{$var} }, $value);
442#	    }
443#	}
444#    }
445
446    # Cater for the user defined defaults
447    foreach $var (keys(%CONFDATA)) {
448        if (conf_exists($var)) {
449            $var =~ s/ //g;
450            my $val = conf_getval($var);
451
452	    if (($SHAREDFILEALLOCATOR) and ($var eq "sharedprealloc")) {
453		if (conf_reqval("myname") ne $SHAREDFILEALLOCATOR) {
454		    $val = "0";
455		}
456	    }
457
458	    if ($val ne "") {
459		op_set($var, $val);
460	    }
461	}
462    }
463}
464
465##############################################################################
466## Local functions
467##############################################################################
468
469sub parse_profile {
470    my ($profile) = shift;
471    my ($config_section, $default_section, $multi_section);
472
473    open(CFILE, "$profile") or
474	die "ERROR: couldn't open profile";
475
476    while(<CFILE>) {
477	my ($line) = $_;
478	chomp($line);
479	$line =~ s/^\s+//; # Get rid of spaces
480
481	if($line =~ /^#/ or $line eq "") {
482	} else {
483	    if($line =~ /}/) {
484		if($multi_section == 1) {
485		    $multi_section = 0;
486		}
487		if($default_section == 1) {
488		    $default_section = 0;
489		}
490		if($config_section == 1) {
491		    $config_section = 0;
492		}
493	    } elsif($multi_section) {
494		$line =~ /(.+) = (.+);/;
495		my $opt = $1;
496		my $val = $2;
497		chomp($opt);
498		chomp($val);
499		my @vals = ();
500		# Check to see if this needs to be a list
501		if($val =~ /,/) {
502		    push(@vals, $+) while $val =~
503			m{"([^\"\\]*(?:\\.[^\"\\]*)*)",? | ([^,]+),? | , }gx;
504		    push(@vals, undef) if substr($val, -1,1) eq ',';
505		    @{ $MULTIDATA{$opt} } = @vals;
506		} else {
507		    @{MULTIDATA{$opt}} = ();
508		    push(@{ $MULTIDATA{$opt} }, $val);
509		}
510	    } elsif($default_section) {
511		if ($line =~ /(.+) = (.+);/) {
512		    my $opt = $1;
513		    my $val = $2;
514		    chomp($opt);
515		    chomp($val);
516		    my @vals = ();
517		    # Check to see if this needs to be a list
518		    if(($val =~ /,/) && ($val !~ /"/)) {
519			push(@vals, $+) while $val =~
520			    m{"([^\"\\]*(?:\\.[^\"\\]*)*)",? | ([^,]+),? | , }gx;
521			push(@vals, undef) if substr($val, -1,1) eq ',';
522			@{ $DEFDATA{$opt} } = @vals;
523		    } else {
524			@{DEFDATA{$opt}} = ();
525			push(@{ $DEFDATA{$opt} }, $val);
526		    }
527		} else {
528		    $line =~ /($OPTIONFLAGS);/;
529		    my $opt = $1;
530		    chomp($opt);
531		    @{DEFDATA{$opt}} = ();
532		    push(@{ $DEFDATA{$opt} }, "");
533		}
534	    } else {
535		if($line =~ /^CONFIG /) {
536                    my $config = $line;
537	 	    $config =~ s/CONFIG[ 	]+(.+) {/$1/;
538		    push(@CONFLIST, $config);
539		    $config_section = 1;
540		} elsif($line =~ /MULTICLIENT {/) {
541		    $multi_section = 1;
542		    $MULTI_CLIENT = 1;
543		} elsif($line =~ /DEFAULTS {/) {
544		    $default_section = 1;
545		}
546	    }
547	}
548    }
549}
550
551
552#
553# Parse the configuration file
554#
555sub parse_config {
556    my ($config) = shift;
557
558    my $config_section = 0;
559
560    print "parsing profile for config: $config\n";
561
562    # Howdy
563    seek(CFILE, 0, 0);
564
565    while(<CFILE>) {
566	# Read in the line and chomp...munch...chomp
567	my ($line) = $_;
568	chomp($line);
569	$line =~ s/^\s+//; # Get rid of spaces
570
571	# look for our stuff
572	if ($line =~ /CONFIG $config /) {
573	    $config_section = 1;
574        }
575
576        if($line =~ /}/) {
577	    $config_section = 0;
578        }
579
580	# Skip until our config is found
581	next if (!$config_section);
582
583	next if ($line =~ /^#/ or $line eq "");
584
585	if ($line =~ /(.+) = (.+);/) {
586	    my $opt = $1;
587	    my $val = $2;
588	    chomp($opt);
589	    chomp($val);
590	    my @vals = ();
591	    # Check to see if this needs to be a list
592	    if(($val =~ /,/) && ($val !~ /"/)) {
593		push(@vals, $+) while $val =~
594	            m{"([^\"\\]*(?:\\.[^\"\\]*)*)",? | ([^,]+),? | , }gx;
595		push(@vals, undef) if substr($val, -1,1) eq ',';
596		@{ $CONFDATA{$opt} }  = @vals;
597	    } else {
598		@{CONFDATA{$opt}} = ();
599		push(@{ $CONFDATA{$opt} }, $val);
600	    }
601	} else {
602	    $line =~ /($OPTIONFLAGS);/;
603	    my $opt = $1;
604	    chomp($opt);
605	    @{CONFDATA{$opt}} = ();
606	    push(@{ $CONFDATA{$opt} }, "");
607	}
608    }
609
610    # Bye, bye
611    #close(CFILE) or die "ERROR: config file closing difficulties";
612    return \%confdata;
613}
614
615sub build_run
616{
617    # The following function is taken from the user's function file
618    pre_run();
619
620    # Set the global statistics directory for this run
621    op_statsdir();
622
623    # The following function is taken from the user's function file
624    bm_run();
625
626    # Finish and close the .f script
627    op_quit();
628}
629
630# statistics aggregation section
631my %FLOWOPVALS;
632my @SUMMARYVALS;
633
634sub init_combined_stats
635{
636    %FLOWOPVALS = ();
637    @SUMMARYVALS = (0,0,0,0,0,0);
638}
639
640sub add_2combstats
641{
642    my ($confname) = shift;
643    my ($thisclient) = shift;
644    my $clstatdir;
645    my $flowopmode = 0;
646    my $summarymode = 0;
647
648    print "adding in stats for client: $thisclient, configuration: $confname\n";
649
650    $clstatdir = multi_getval("masterpath")."/".$thisclient;
651
652    print "from: ".$clstatdir."/stats.".$confname.".out\n";
653    open (CLSTATS, $clstatdir."/stats.".$confname.".out");
654    while(<CLSTATS>) {
655	my ($line) = $_;
656	chomp($line);
657	if (($flowopmode == 0) and ($summarymode == 0)) {
658	    if ($line =~ /^Flowop totals:/) {
659		$flowopmode = 1;
660		next;
661	    }
662	    if ($line =~ /^IO Summary:/) {
663		$summarymode = 1;
664		next;
665	    }
666	}
667	if ($line eq "") {
668	    $flowopmode = 0;
669	    $summarymode = 0;
670	    next;
671	}
672
673	# get the good stuff
674	if ($flowopmode == 1) {
675	    my @elementlist;
676	    my @valuelist;
677	    my $flkey;
678	    my $vallistref = [];
679
680	    @elementlist = split('	', $line);
681	    $flkey = $elementlist[0];
682	    @valuelist = @elementlist[1..$#elementlist];
683
684	    if (exists($FLOWOPVALS{$flkey})) {
685		my $numvals;
686
687		$vallistref = $FLOWOPVALS{$flkey};
688		$numvals = @{$vallistref};
689		for (my $idx = 0; $idx < $numvals; $idx++) {
690		    $vallistref->[$idx] += $valuelist[$idx];
691		}
692	    } else {
693		# newly found flowop name
694		$vallistref = [@valuelist];
695		$FLOWOPVALS{$flkey} = $vallistref;
696	    }
697	    next;
698	}
699
700	# get final totals
701	if ($summarymode == 1) {
702	    my @valuelist;
703
704	    @valuelist = split('	', $line);
705
706	    for (my $idx = 0; $idx <= $#valuelist; $idx++) {
707		$SUMMARYVALS[$idx] += $valuelist[$idx];
708	    }
709	    next;
710	}
711    }
712    close (CLSTATS);
713}
714
715sub print_usage
716{
717    print "Usage:\n\tfilebench <profile name>\n\tfilebench -c <stat_dir> ...\n";
718}
719
720sub dump_combined_stats
721{
722    my ($confname) = shift;
723    my $totvalsref = [];
724    my $flkey;
725    use FileHandle;
726
727## set up output formating info
728format flowoplinefrm =
729@<<<<<<<<<<<<<<<<<<< @#######ops/s @###.#mb/s @#####.#ms/op @#######us/op-cpu
730$flkey, $totvalsref->[0], $totvalsref->[1], $totvalsref->[2]/$#CLIENTLIST, $totvalsref->[3]/$#CLIENTLIST
731.
732
733format summarylinefrm =
734
735IO Summary: @#######ops, @#####.#ops/s, (@####/@#### r/w) @#####.#mb/s, @######us cpu/op, @####.#ms latency
736$SUMMARYVALS[0], $SUMMARYVALS[1], $SUMMARYVALS[2], $SUMMARYVALS[3], $SUMMARYVALS[4], $SUMMARYVALS[5], $SUMMARYVALS[6]
737.
738
739    open (SUMSTATS, ">$STATSBASE/$confname/stats.$confname.out");
740    print "Per-Operation Breakdown:\n";
741    print SUMSTATS "Per-Operation Breakdown:\n";
742
743    format_name  STDOUT "flowoplinefrm";
744    format_name  SUMSTATS "flowoplinefrm";
745
746    foreach $flkey (keys %FLOWOPVALS) {
747
748	$totvalsref = $FLOWOPVALS{$flkey};
749
750	write STDOUT;
751	write SUMSTATS;
752    }
753
754    format_name  STDOUT "summarylinefrm";
755    format_name  SUMSTATS "summarylinefrm";
756
757    write STDOUT;
758    write SUMSTATS;
759    close (SUMSTATS);
760}
761
762#
763# polls the synchronization socket for each client in turn every 5 seconds,
764# then sends synch responses once all clients have "checked in". The
765# sample number in the received sync requests must match the sequence
766# number supplied with the call.
767#
768sub sync_receive
769{
770    my $seqnum = shift;
771#    my @cl_list;
772    my %cl_hash = ();
773    %cl_hash = %CLIENTHASH;
774
775    my $count = @CLIENTLIST;
776    print "waiting for sync message: $seqnum from $count clients\n";
777    while ($count > 0) {
778	my $rcv_str = "";
779
780	sleep 5;
781
782	foreach my $client_name (keys %cl_hash)
783	{
784	    my $clientdata = $CLIENTHASH{$client_name};
785	    my $client_hndl = $$clientdata[0];
786	    print "recv sync: $client_name undefined handle\n" unless defined($client_hndl);
787	    my $client_iaddr = $$clientdata[1];
788	    my $sn = $$clientdata[2];
789	    my $rtn = 0;
790
791	    do {
792		my $tmp_str;
793		$rtn = recv($client_hndl, $tmp_str, 80, MSG_DONTWAIT);
794		if (defined($rtn)) {
795		    $rcv_str = $rcv_str.$tmp_str;
796		}
797	    } until (!defined($rtn) || ($rcv_str =~ /$EOL/s ));
798
799	    if (defined($rtn)) {
800		my %ophash = ();
801		my $ok;
802
803		my @oplist = split /,/,$rcv_str;
804		foreach my $opent (@oplist)
805		{
806		    my ($op, $val) = split /=/,$opent;
807		    $ophash{$op} = $val;
808		}
809		$ok = ($sn == $seqnum);
810		$ok &&= defined((my $cmd_val = $ophash{"cmd"}));
811		$ok &&= defined((my $samp_val = $ophash{"sample"}));
812		if ($ok && ($cmd_val eq "SYNC") && ($samp_val == $seqnum))
813		{
814		    delete $cl_hash{$client_name};
815		    $count--;
816		    print "received a sync request from $client_name\n";
817		    ${$CLIENTHASH{$client_name}}[2] = ($sn + 1);
818		} else {
819		    print "received invalid sync request string [".rcv_str."] from client $client_name\n";
820		}
821	    }
822	}
823    }
824    print "received all sync requests for seq $seqnum, sending responses\n";
825    foreach my $client_name (@CLIENTLIST)
826    {
827	my $clientdata = $CLIENTHASH{$client_name};
828	my $client_hndl = $$clientdata[0];
829	print "send resp: $client_name undefined handle\n" unless defined($client_hndl);
830
831	send ($client_hndl, "rsp=PASS,sample=$seqnum\n", 0);
832    }
833}
834
835#
836# waits for all known clients to connect, then calls sync_recieve(1) to
837# sync_receive(N) to wait for N sync requests for designated sync points
838# 1..N.
839#
840sub sync_server
841{
842    my $port = shift || 8001;
843    my $proto = getprotobyname('tcp');
844    my $paddr;
845    my $count;
846
847    socket(Server, PF_INET, SOCK_STREAM, $proto)     || die "socket: $!";
848    setsockopt(Server, SOL_SOCKET, SO_REUSEADDR,
849	       pack("l", 1))			     || die "setsockopt: $1";
850    bind(Server, sockaddr_in($port, INADDR_ANY))     || die "bind: $1";
851    listen(Server, SOMAXCONN)			     || die "listen: $1";
852
853# wait for connection requests from clients
854    print "sync: Waiting for ".@CLIENTLIST." Clients\n";
855    for ($count = @CLIENTLIST; $count > 0; $count--) {
856	$paddr = accept(my $client_hndl, Server);
857	die "bad socket address" unless $paddr;
858
859	my ($port, $iaddr) = sockaddr_in($paddr);
860	my $cl_name = gethostbyaddr($iaddr, AF_INET);
861
862	if (!exists($CLIENTHASH{$cl_name})) {
863	    die "sync from unknown client $cl_name";
864	}
865
866	print "received sync connection from client: $cl_name\n";
867	${$CLIENTHASH{$cl_name}}[0] = $client_hndl;
868	${$CLIENTHASH{$cl_name}}[1] = $iaddr;
869    }
870
871# indicate that all clients have checked in
872    sync_receive(1);
873    if (conf_exists("runtime") == 1) {
874	my $runtime =  conf_getval("runtime");
875	sleep $runtime;
876    }
877    sync_receive(2);
878}
879
880##############################################################################
881## Main program
882##############################################################################
883
884## Make sure arguments are okay
885$numargs = $#ARGV + 1;
886
887if($numargs < 1) {
888    print_usage();
889    exit(2);
890}
891
892if($ARGV[0] eq "-c") {
893    if($numargs < 2) {
894	print_usage();
895	exit(2);
896    }
897    shift(ARGV);
898    exec("$FILEBENCH/scripts/filebench_compare", @ARGV);
899}
900
901$PROFILENAME = $ARGV[0];
902$PROFILE = $PROFILENAME;
903$PROFILE =~ s/.*\/(.+)$/$1/;
904parse_profile("$PROFILENAME.prof");
905
906%CONFDATA = ();
907%CONFDATA = %DEFDATA;
908
909# get the name of the host this script is running on
910my $hostname = `hostname`;
911chomp($hostname);
912
913# Check for Multi-Client operation
914if ($MULTI_CLIENT == 1) {
915
916    if (multi_exists("targetpath")) {
917	$TARGETPATH = multi_getval("targetpath");
918    } else {
919	print "ERROR: Target pathname required for multi-client operation\n";
920	exit(1);
921    }
922
923    if (multi_exists("clients")) {
924	@CLIENTLIST = split(' ',multi_getval("clients"));
925    } else {
926	print "ERROR: client list required for multi-client operation\n";
927	exit(1);
928    }
929
930    if (multi_exists("sharefiles")) {
931	$SHAREDFILEALLOCATOR = multi_getval("sharefiles");
932    } else {
933	$SHAREDFILEALLOCATOR = "";
934    }
935
936    $TARGETDIR = $TARGETPATH.conf_getval("dir");
937
938    # Setup the multi client statistics base directory
939    $STATSBASE = $TARGETPATH.conf_reqval("stats");
940
941    multi_putval("masterhost", $hostname) unless multi_exists("masterhost");
942    multi_putval("masterpath", $STATSBASE) unless multi_exists("masterpath");
943
944    # create a path for filebench.pl to use to access the master directory
945    $FB_MASTERPATH = multi_getval("masterpath");
946
947    print "Target PathName = $TARGETPATH, path = ".multi_getval("masterpath")."\n";
948
949} else {
950    # Setup the single client statistics base directory
951    $STATSBASE = conf_reqval("stats");
952}
953
954my $filesystem = conf_reqval("filesystem");
955$STATSBASE = $STATSBASE . "/$hostname-$filesystem-$PROFILENAME-";
956my $timestamp = strftime "%b_%e_%Y-%Hh_%Mm_%Ss", localtime;
957$timestamp =~ s/ //;
958$STATSBASE = $STATSBASE . $timestamp;
959
960foreach $config_name (@CONFLIST)
961{
962    %CONFDATA = ();
963    %CONFDATA = %DEFDATA;
964    $CONFNAME = $config_name;
965    parse_config("$config_name");
966    my $function = conf_reqval("function");
967    my $statsdir;
968
969    if (-f "$function.func") {
970	require "$function.func";
971    } else {
972	require "$FILEBENCH/config/$function.func";
973    }
974
975    # Setup the final statistics directory
976    system("mkdir -p $STATSBASE");
977
978    # Leave a log of the run info
979    open (RUNLOG, ">$STATSBASE/thisrun.prof");
980    print RUNLOG "# " . conf_reqval("description") . "\n";
981    close (RUNLOG);
982
983    system ("cat $PROFILENAME.prof >>".$STATSBASE."/thisrun.prof");
984
985    $statsdir = $STATSBASE . "/" . $config_name;
986    system("mkdir -p $statsdir");
987    system("chmod a+w $statsdir");
988
989    if ($MULTI_CLIENT == 1) {
990	my @pidlist;
991	my %multi_confdata;
992	my $procpid;
993	my $syncclients = "";
994
995	%multi_confdata = %CONFDATA;
996
997	foreach my $thisclient (@CLIENTLIST) {
998	    my $tmpdir;
999	    my $tmpstatdir;
1000	    my @clientdata;
1001
1002	    %CONFDATA = ();
1003	    %CONFDATA = %multi_confdata;
1004	    printf "building client: " . $thisclient . "\n";
1005
1006	    # Setup the statistics directory for each client
1007	    $tmpstatdir = multi_getval("masterpath")."/".$thisclient;
1008
1009	    if ($SHAREDFILEALLOCATOR) {
1010		$tmpdir = $TARGETDIR;
1011	    } else {
1012		$tmpdir = $TARGETDIR."/".$thisclient;
1013	    }
1014
1015# add info to client hash
1016	    @clientdata = ();
1017	    $clientdata[2] = 1;
1018	    $CLIENTHASH{$thisclient} = \@clientdata;
1019	    $syncclients = $syncclients." --client ".$thisclient;
1020
1021	    push(@{ $CONFDATA{"myname"} }, $thisclient);
1022	    push(@{ $CONFDATA{"statsdir"} }, $tmpstatdir);
1023	    system("mkdir -p ".$FB_MASTERPATH."/".$thisclient);
1024	    system("chmod 0777 ".$FB_MASTERPATH."/".$thisclient);
1025
1026	    # modify dir config variable for multiclient
1027	    if (conf_exists("dir")) {
1028		@{$CONFDATA{"dir"}} = ($tmpdir);
1029	    }
1030	    build_run();
1031	}
1032
1033	# Begin the RUN!!!
1034	print "Running " . $STATSBASE . "\n";
1035
1036	#spawn the synchronization server
1037	print "Starting sync server on host ".$hostname."\n";
1038	if ($procpid = fork) {
1039	    push(@pidlist, $procpid);
1040	} else {
1041	    sync_server();
1042	    exit(0);
1043	}
1044
1045	sleep(3);
1046
1047	# remotely execute the run on each client
1048	foreach $thisclient (@CLIENTLIST) {
1049	    if($procpid = fork) {
1050		push(@pidlist, $procpid);
1051	    } else {
1052		if ($thisclient eq $hostname) {
1053		    print "Starting local client: $thisclient\n";
1054		    system(multi_getval("masterpath")."/".$thisclient."/thisrun.f");
1055		} else {
1056		    print "Starting remote client: $thisclient\n";
1057		    system("ssh ".$thisclient." ".multi_getval("masterpath")."/".$thisclient."/thisrun.f >> ".multi_getval("masterpath")."/".$thisclient."/runs.out");
1058		}
1059		exit(0);
1060	    }
1061	}
1062
1063	# wait for all of them to finish
1064	foreach $procpid (@pidlist) {
1065	    waitpid($procpid, 0);
1066	}
1067
1068	init_combined_stats();
1069
1070	foreach $thisclient (@CLIENTLIST) {
1071	    add_2combstats($config_name, $thisclient);
1072	}
1073
1074	# dump the combined client stats
1075	dump_combined_stats($config_name);
1076
1077    } else {
1078	push(@{ $CONFDATA{"statsdir"} }, $statsdir);
1079
1080	build_run();
1081
1082	# Execute the run
1083	print "Running " . conf_reqval("statsdir") . "/thisrun.f\n";
1084	system ($statsdir."/thisrun.f");
1085
1086
1087    }
1088
1089}
1090
1091# The following function is taken from the user's function file
1092post_run();
1093
1094print "\n";
1095