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