xref: /onnv-gate/usr/src/cmd/filebench/fbscript/filebench.pl (revision 5184:da60d2b4a9e2)
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