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 2007 Sun Microsystems, Inc. All rights reserved. 24# Use is subject to license terms. 25# 26# ident "%Z%%M% %I% %E% SMI" 27# 28 29use POSIX; 30 31my $QUIT = 0; 32my $USE_XANADU = 0; 33my $TIMEOUT = 60; 34my $FILEBENCH = "/usr/benchmarks/filebench"; 35my $PROG = "/usr/benchmarks/filebench/bin/go_filebench"; 36my $FSCRIPT; 37my $SCRIPT_NO; 38my @CONFLIST; 39my %DEFDATA = (); 40my %CONFDATA = (); 41my %STATSHASH = (); 42@ext_stats=(); 43@file_stats=(); 44@arg_stats=(); 45@pid_arr=(); 46 47# The following if test caters for running benchpoint from an alternative path 48#if (-r $ENV{"FILEBENCH") { 49# $FILEBENCH = $ENV{"FILEBENCH"}; 50#} 51 52############################################################################## 53## Configuration hash data operations 54############################################################################## 55 56# This sub allows a function program to extract the base directory for filebench 57sub get_FILEBENCH { 58 return ($FILEBENCH); 59} 60 61sub get_STATSBASE { 62 return ($STATSBASE); 63} 64 65sub get_CONFNAME { 66 return ($CONFNAME); 67} 68 69sub get_CONFNAME { 70 return ($CONFNAME); 71} 72 73sub conf_getval { 74 my ($key) = shift; 75 return ("@{$CONFDATA{$key}}"); 76} 77 78sub conf_reqval { 79 my ($key) = shift; 80 81 if (exists($CONFDATA{$key})) { 82 return ("@{$CONFDATA{$key}}"); 83 } 84 print "ERROR: required key \"$key\" missing from configuration\n"; 85 exit(1); 86} 87 88sub conf_exists { 89 my ($key) = shift; 90 if (exists($CONFDATA{$key})) { 91 return (1); 92 } 93 return (0); 94} 95 96sub conf_hash { 97 return(%CONFDATA); 98} 99 100############################################################################## 101## Filebench Operations 102############################################################################## 103 104sub op_init { 105} 106 107sub op_load { 108 my ($workload) = shift; 109 my $scriptname = conf_reqval("statsdir") . "/thisrun.f"; 110 if($workload ne '') { 111 open (FSCRIPT, ">$scriptname"); 112 chmod (0755, $scriptname); 113 print FSCRIPT "#!$PROG -f\n\n"; 114 # Load the df 115 print FSCRIPT "load $workload\n"; 116 # Load the user defined defaults 117 op_load_defaults(); 118 119 # Create the associated files and filesets 120 print FSCRIPT "create files\n"; 121 122 print FSCRIPT "create filesets\n"; 123 124 } 125 $SCRIPT_NO = 1; 126 return(0); 127} 128 129sub op_set { 130 my ($var, $val) = @_; 131 if($var eq 'debug') { 132 print FSCRIPT "debug $val\n"; 133 } elsif($var ne '') { 134 print FSCRIPT "set \$$var=$val\n"; 135 } 136 return(0); 137} 138 139sub op_eventrate { 140 my ($eventrate) = shift; 141 if ($eventrate ne '') { 142 print FSCRIPT "eventgen rate=$eventrate\n"; 143 return(0); 144 } 145} 146 147sub op_run { 148 my ($time) = shift; 149 print FSCRIPT "run $time\n"; 150 return(0); 151} 152 153sub op_sleep { 154 my ($time) = shift; 155 print FSCRIPT "sleep $time\n"; 156 return(0); 157} 158 159sub op_msg { 160 my ($msg) = shift; 161 print FSCRIPT "echo \"$msg\"\n"; 162 return(0); 163} 164 165sub op_quit { 166 if($QUIT) { 167 # Shutdown the appropriate processes 168 print FSCRIPT "shutdown processes\n"; 169 170 # Quit filebench 171 print FSCRIPT "quit\n"; 172 close(FSCRIPT); 173 print "Running " . conf_reqval("statsdir") . "/thisrun.f\n"; 174 system (conf_reqval("statsdir") . "/thisrun.f"); 175 } else { 176 print STDOUT "ERROR: pre-mature call to op_quit\n"; 177 } 178} 179 180sub op_statsdir { 181 print FSCRIPT "stats directory ".conf_reqval("statsdir")."\n"; 182 return(0); 183} 184 185sub op_indiv_vars { 186 my ($ivar) = shift; 187 print FSCRIPT "echo \"\$$ivar\"\n"; 188 my ($imatch, $ierr, $ibefore, $iafter) = &expect(FSCRIPT, 189 $TIMEOUT, "filebench>"); 190 191 $ibefore =~ /(.*): (.*): (-*\d+)/; 192 $imatch = $3; 193 $imatch =~ s/^\s+//; 194 chomp($imatch); 195 return($imatch); 196} 197 198sub op_indiv_stats { 199 my ($var) = shift; 200 print FSCRIPT "echo \"\${stats.$var}\"\n"; 201 my ($match, $err, $before, $after) = &expect(FSCRIPT, 202 $TIMEOUT, "filebench>"); 203 204 $before =~ /(.*): (.*): (-*\d+)/; 205 $match = $3; 206 $match =~ s/^\s+//; 207 chomp($match); 208 return($match); 209} 210 211sub op_stats { 212 my ($time) = shift; 213 my ($statsfile) = shift; 214 215 # Create the associated processes and start them running 216 print FSCRIPT "create processes\n"; 217 218 if (($time ne '') && ($statsfile ne '')) { 219 # Clear the current statistics buffers 220 print FSCRIPT "stats clear\n"; 221 222 # Start external statistics collection (if any) 223 # Note all statistics arrays MUST be the same length ! 224 if (@ext_stats != ()) { 225 if (($#ext_stats == $#file_stats) && ($#ext_stats == $#arg_stats)) { 226 $script = conf_reqval("statsdir") . "/stats$SCRIPT_NO.sh"; 227 open (RUNSCRIPT, ">$script"); 228 chmod (0755, $script); 229 print FSCRIPT "system \"$script\"\n"; 230 $SCRIPT_NO++; 231 $index=0; 232 foreach my $ext (@ext_stats) { 233 print RUNSCRIPT "$FILEBENCH/scripts/collect_$ext $ext $file_stats[$index] "; 234 print RUNSCRIPT conf_reqval("statsdir"); 235 print RUNSCRIPT " $time $FILEBENCH $arg_stats[$index] &\n"; 236 $index++; 237 } 238 } 239 } 240 close(RUNSCRIPT); 241 242 # Sleep for the run time 243 print FSCRIPT "sleep $time\n"; 244 245 # Snap the statistics 246 print FSCRIPT "stats snap\n"; 247 248 # Dump the statistics to a raw file - out required due to filename constraint 249 print FSCRIPT "stats dump \"$statsfile.out\"\n"; 250 251 # Statistics reaping occurs here 252 if (@ext_stats != ()) { 253 if (($#ext_stats == $#file_stats) && ($#ext_stats == $#arg_stats)) { 254 $script = conf_reqval("statsdir") . "/stats$SCRIPT_NO.sh"; 255 open (RUNSCRIPT, ">$script"); 256 chmod (0755, $script); 257 print FSCRIPT "system \"$script\"\n"; 258 $SCRIPT_NO++; 259 foreach my $ext (@ext_stats) { 260 print RUNSCRIPT "$FILEBENCH/scripts/kill_stats $ext &\n"; 261 } 262 close(RUNSCRIPT); 263 } 264 } 265 266 # Dump the statistics to a Xanadu compatible XML file 267 if ($USE_XANADU) { 268 op_xmlstats($statsfile); 269 270 $script = conf_reqval("statsdir") . "/stats$SCRIPT_NO.pl"; 271 open (RUNSCRIPT, ">$script"); 272 chmod (0755, $script); 273 print FSCRIPT "system \"$script\"\n"; 274 $SCRIPT_NO++; 275 276 # The following loop adds the benchpoint run parameters and statistics into the filebench XML file 277 # We capture the meta data from the start of the filebench xml file 278 print RUNSCRIPT "#!/usr/bin/perl\n"; 279 print RUNSCRIPT "\$phase=1;\n"; 280 print RUNSCRIPT "open(STATSFILE,\"<".conf_reqval("statsdir")."/$statsfile.xml\");\n"; 281 print RUNSCRIPT "open(OSTATSFILE,\">".conf_reqval("statsdir")."/$statsfile.new.xml\");\n"; 282 print RUNSCRIPT "while (<STATSFILE>) {\n"; 283 print RUNSCRIPT "\t\$temp=\$_;\n"; 284 print RUNSCRIPT "\tif ((!((/.*meta.*/) || (/.*stat_doc.*/))) && (\$phase == 1)) {\n"; 285 print RUNSCRIPT "\t\topen(XMLFILE,\"<".conf_reqval("statsdir")."/$statsfile.config.xml\");\n"; 286 print RUNSCRIPT "\t\twhile (<XMLFILE>) {\n"; 287 print RUNSCRIPT "\t\t\tprint OSTATSFILE \$_;\n"; 288 print RUNSCRIPT "\t\t}\n"; 289 print RUNSCRIPT "\t\tclose(XMLFILE);\n"; 290 print RUNSCRIPT "\t\t\$phase++;\n"; 291 print RUNSCRIPT "\t}\n"; 292 print RUNSCRIPT "\tprint OSTATSFILE \$temp;\n"; 293 print RUNSCRIPT "}\n"; 294 print RUNSCRIPT "close(STATSFILE);\n"; 295 print RUNSCRIPT "close(OSTATSFILE);\n"; 296 print RUNSCRIPT "unlink(\"".conf_reqval("statsdir")."/$statsfile.xml\");\n"; 297 print RUNSCRIPT "unlink(\"".conf_reqval("statsdir")."/$statsfile.config.xml\");\n"; 298 print RUNSCRIPT "system(\"mv ".conf_reqval("statsdir")."/$statsfile.new.xml ".conf_reqval("statsdir")."/$statsfile.xml\");\n"; 299 300 $script = conf_reqval("statsdir") . "/stats$SCRIPT_NO.sh"; 301 open (RUNSCRIPT, ">$script"); 302 chmod (0755, $script); 303 print FSCRIPT "system \"$script\"\n"; 304 $SCRIPT_NO++; 305 306 print RUNSCRIPT "mkdir ".conf_reqval("statsdir")."/xml\n"; 307 print RUNSCRIPT "mkdir ".conf_reqval("statsdir")."/html\n"; 308 309 print RUNSCRIPT "mv ".conf_reqval("statsdir")."/$statsfile.xml ".conf_reqval("statsdir")."/xml/$statsfile.xml\n"; 310 311 # Process XML file using Xanadu 2 312 print RUNSCRIPT "$FILEBENCH/xanadu/scripts/xanadu import ".conf_reqval("statsdir")." ".conf_reqval("statsdir")."/xml ".conf_reqval("function")."-".conf_reqval("statsdir")."\n"; 313 print RUNSCRIPT "$FILEBENCH/xanadu/scripts/xanadu export ".conf_reqval("statsdir")."/xml ".conf_reqval("statsdir")."/html\n"; 314 close(RUNSCRIPT); 315 } 316 } 317 return(0); 318} 319 320sub op_xmlstats { 321 my ($statsfile) = shift; 322 if($statsfile ne '') { 323 print FSCRIPT "stats xmldump \"$statsfile.xml\"\n"; 324 325 # The following loop adds the benchpoint run parameters and statistics into a temporary XML file 326 open(OSTATSFILE,">".conf_reqval("statsdir")."/$statsfile.config.xml"); 327 %CONFHASH = conf_hash(); 328 # There is no test for whether CONFHASH contains no keys 329 # The following two lines is to obtain the stats run directory name for xanadu meta data 330 print OSTATSFILE "<meta name=\"RunId\" value=\"".conf_reqval("function")."-".conf_reqval("statsdir")."\"/>\n"; 331 print OSTATSFILE "<stat_group name=\"Benchpoint Configuration\">\n"; 332 print OSTATSFILE "<cell_list>\n"; 333 foreach $k (keys(%CONFHASH)) { 334 print OSTATSFILE "<cell>@{ $CONFHASH{$k} }</cell>\n"; 335 } 336 print OSTATSFILE "</cell_list>\n"; 337 print OSTATSFILE "<dim_list>\n"; 338 print OSTATSFILE "<dim>\n"; 339 print OSTATSFILE "<dimval>Value</dimval>\n"; 340 print OSTATSFILE "</dim>\n"; 341 print OSTATSFILE "<dim>\n"; 342 foreach $k (keys(%CONFHASH)) { 343 print OSTATSFILE "<dimval>$k</dimval>\n"; 344 } 345 print OSTATSFILE "</dim>\n"; 346 print OSTATSFILE "</dim_list>\n"; 347 print OSTATSFILE "</stat_group>\n"; 348 close(OSTATSFILE); 349 350 return(0); 351 } 352 return(1); 353} 354 355sub op_command { 356 my ($command) = shift; 357 if($command ne '') { 358 print FSCRIPT "$command\n"; 359 } 360 return(0); 361} 362 363sub op_statshash { 364 op_indiv_stats("iocount"); 365 $STATSHASH{"iocount"} = op_indiv_stats("iocount"); 366 $STATSHASH{"iorate"} = op_indiv_stats("iorate"); 367 $STATSHASH{"ioreadrate"} = op_indiv_stats("ioreadrate"); 368 $STATSHASH{"iowriterate"} = op_indiv_stats("iowriterate"); 369 $STATSHASH{"iobandwidth"} = op_indiv_stats("iobandwidth"); 370 $STATSHASH{"iolatency"} = op_indiv_stats("iolatency"); 371 $STATSHASH{"iocpu"} = op_indiv_stats("iocpu"); 372 $STATSHASH{"oheadcpu"} = op_indiv_stats("oheadcpu"); 373 $STATSHASH{"iowait"} = op_indiv_stats("iowait"); 374 $STATSHASH{"syscpu"} = op_indiv_stats("syscpu"); 375 $STATSHASH{"iocpusys"} = op_indiv_stats("iocpusys"); 376 return(%STATSHASH); 377} 378 379sub op_load_defaults { 380# The following code causes an intermittent bug - may be fixed at a later date 381# Prevents the capture of filebench default parameters 382# print FSCRIPT "vars\n"; 383# my ($match, $err, $before, $after) = &expect(FSCRIPT, 384# $TIMEOUT, "filebench>"); 385# chomp($before); 386# $before =~ /(.*): (.*): (.*)/; 387# $match = $3; 388# my @vars = split(/ /, $match); 389# my $value = ""; 390# # Cater for the default filebench commands 391# foreach my $var (@vars) { 392# if (!conf_exists($var)) { 393# $var =~ s/ //g; 394# if ($var ne '') { 395# $value = op_indiv_vars($var); 396# push(@{ $CONFDATA{$var} }, $value); 397# } 398# } 399# } 400 401 # Cater for the user defined defaults 402# foreach my $var (@vars) { 403 404 405 # Cater for the user defined defaults 406 foreach $var (keys(%CONFDATA)) { 407 if (conf_exists($var)) { 408 $var =~ s/ //g; 409 my $val = conf_getval($var); 410 op_set($var, $val); 411 } 412 } 413} 414 415############################################################################## 416## Local functions 417############################################################################## 418 419sub parse_profile { 420 my ($profile) = shift; 421 my ($config_section, $default_section); 422 423 open(CFILE, "$profile") or 424 die "ERROR: couldn't open profile"; 425 426 while(<CFILE>) { 427 my ($line) = $_; 428 chomp($line); 429 $line =~ s/^\s+//; # Get rid of spaces 430 431 if($line =~ /^#/ or $line eq "") { 432 } else { 433 if($line =~ /}/) { 434 if($default_section == 1) { 435 $default_section = 0; 436 } 437 if($config_section == 1) { 438 $config_section = 0; 439 } 440 } elsif($default_section) { 441 $line =~ /(.+) = (.+);/; 442 my $opt = $1; 443 my $val = $2; 444 chomp($opt); 445 chomp($val); 446 my @vals = (); 447 # Check to see if this needs to be a list 448 if($val =~ /,/) { 449 push(@vals, $+) while $val =~ 450 m{"([^\"\\]*(?:\\.[^\"\\]*)*)",? | ([^,]+),? | , }gx; 451 push(@vals, undef) if substr($val, -1,1) eq ','; 452 @{ $DEFDATA{$opt} } = @vals; 453 } else { 454 @{CONFDATA{$opt}} = (); 455 push(@{ $DEFDATA{$opt} }, $val); 456 } 457 } else { 458 if($line =~ /^CONFIG /) { 459 my $config = $line; 460 $config =~ s/CONFIG[ ]+(.+) {/$1/; 461 push(@CONFLIST, $config); 462 $config_section = 1; 463 } elsif($line =~ /DEFAULTS {/) { 464 $default_section = 1; 465 } 466 } 467 } 468 } 469} 470 471 472# 473# Parse the configuration file 474# 475sub parse_config { 476 my ($config) = shift; 477 478 my $config_section = 0; 479 480 print "parsing profile for config: $config\n"; 481 482 # Howdy 483 seek(CFILE, 0, 0); 484 485 while(<CFILE>) { 486 # Read in the line and chomp...munch...chomp 487 my ($line) = $_; 488 chomp($line); 489 $line =~ s/^\s+//; # Get rid of spaces 490 491 # look for our stuff 492 if ($line =~ /CONFIG $config /) { 493 $config_section = 1; 494 } 495 496 if($line =~ /}/) { 497 $config_section = 0; 498 } 499 500 # Skip until our config is found 501 next if (!$config_section); 502 503 next if ($line =~ /^#/ or $line eq ""); 504 505 $line =~ /(.+) = (.+);/; 506 my $opt = $1; 507 my $val = $2; 508 chomp($opt); 509 chomp($val); 510 my @vals = (); 511 # Check to see if this needs to be a list 512 if($val =~ /,/) { 513 push(@vals, $+) while $val =~ 514 m{"([^\"\\]*(?:\\.[^\"\\]*)*)",? | ([^,]+),? | , }gx; 515 push(@vals, undef) if substr($val, -1,1) eq ','; 516 @{ $CONFDATA{$opt} } = @vals; 517 } else { 518 @{CONFDATA{$opt}} = (); 519 push(@{ $CONFDATA{$opt} }, $val); 520 } 521 } 522 523 # Bye, bye 524 #close(CFILE) or die "ERROR: config file closing difficulties"; 525} 526 527sub print_usage 528{ 529 print "Usage:\n\tfilebench <profile name>\n\tfilebench -c <stat_dir> ...\n"; 530} 531 532############################################################################## 533## Main program 534############################################################################## 535 536## Make sure arguments are okay 537$numargs = $#ARGV + 1; 538 539if($numargs < 1) { 540 print_usage(); 541 exit(2); 542} 543 544if($ARGV[0] eq "-c") { 545 if($numargs < 2) { 546 print_usage(); 547 exit(2); 548 } 549 shift(ARGV); 550 exec("$FILEBENCH/scripts/filebench_compare", @ARGV); 551} 552 553$PROFILENAME = $ARGV[0]; 554$PROFILE = $PROFILENAME; 555$PROFILE =~ s/.*\/(.+)$/$1/; 556parse_profile("$PROFILENAME.prof"); 557 558%CONFDATA = (); 559%CONFDATA = %DEFDATA; 560 561# Setup the statistics base directory 562$STATSBASE = conf_reqval("stats"); 563my $filesystem = conf_reqval("filesystem"); 564my $hostname = `hostname`; 565chomp($hostname); 566$STATSBASE = $STATSBASE . "/$hostname-$filesystem-$PROFILENAME-"; 567my $timestamp = strftime "%b_%e_%Y-%Hh_%Mm_%Ss", localtime; 568$timestamp =~ s/ //; 569$STATSBASE = $STATSBASE . $timestamp; 570 571foreach $CONFNAME (@CONFLIST) { 572 %CONFDATA = (); 573 %CONFDATA = %DEFDATA; 574 parse_config("$CONFNAME"); 575 my $function = conf_reqval("function"); 576 if (-f "$function.func") { 577 require "$function.func"; 578 } else { 579 require "$FILEBENCH/config/$function.func"; 580 } 581 $QUIT = 0; 582 583 # Setup the statistics directory 584 $statsdir = $STATSBASE . "/" . $CONFNAME; 585 push(@{ $CONFDATA{"statsdir"} }, $statsdir); 586 system("mkdir -p $statsdir"); 587 588 # The following function is taken from the user's function file 589 pre_run(); 590 591 # Leave a log of the run info 592 open (RUNLOG, ">$STATSBASE/thisrun.prof"); 593 print RUNLOG "# " . conf_reqval("description") . "\n"; 594 close (RUNLOG); 595 system ("cat $PROFILENAME.prof >>$STATSBASE/thisrun.prof"); 596 597 # Set the global statistics directory for this run 598 op_statsdir(); 599 600 # The following function is taken from the user's function file 601 bm_run(); 602 603 $QUIT = 1; 604 605 # The following function is taken from the user's function file 606 post_run(); 607 print "\n"; 608} 609