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