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";