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