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