зеркало из https://github.com/mozilla/pjs.git
839 строки
24 KiB
Perl
839 строки
24 KiB
Perl
# The contents of this file are subject to the Netscape Public
|
|
# License Version 1.1 (the "License"); you may not use this file
|
|
# except in compliance with the License. You may obtain a copy of
|
|
# the License at http://www.mozilla.org/NPL/
|
|
#
|
|
# Software distributed under the License is distributed on an "AS
|
|
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
# implied. See the License for the specific language governing
|
|
# rights and limitations under the License.
|
|
#
|
|
# The Original Code is the Netscape Mailstone utility,
|
|
# released March 17, 2000.
|
|
#
|
|
# The Initial Developer of the Original Code is Netscape
|
|
# Communications Corporation. Portions created by Netscape are
|
|
# Copyright (C) 1997-2000 Netscape Communications Corporation. All
|
|
# Rights Reserved.
|
|
#
|
|
# Contributor(s): Dan Christian <robodan@netscape.com>
|
|
# Marcel DePaolis <marcel@netcape.com>
|
|
#
|
|
# Alternatively, the contents of this file may be used under the
|
|
# terms of the GNU Public License (the "GPL"), in which case the
|
|
# provisions of the GPL are applicable instead of those above.
|
|
# If you wish to allow use of your version of this file only
|
|
# under the terms of the GPL and not to allow others to use your
|
|
# version of this file under the NPL, indicate your decision by
|
|
# deleting the provisions above and replace them with the notice
|
|
# and other provisions required by the GPL. If you do not delete
|
|
# the provisions above, a recipient may use your version of this
|
|
# file under either the NPL or the GPL.
|
|
#####################################################
|
|
|
|
# This file does argument processing, file IO, and other utility routines
|
|
|
|
# Where the online docs live
|
|
#$mailstoneURL =
|
|
# "http://home.netscape.com/eng/server/messaging/4.1/mailston/stone.htm";
|
|
$mailstoneURL =
|
|
"http://developer.iplanet.com/docs/manuals/messaging.html";
|
|
#$mailstoneURL =
|
|
# "http://docs.iplanet.com/docs/manuals/messaging/nms415/mailstone/stone.htm"
|
|
|
|
# Subdirs for results (each under a timestamp dir). Should just hardwire.
|
|
$tmpbase = "tmp";
|
|
$resultbase = "results";
|
|
|
|
# This holds everything about the test and system configuration
|
|
@workload = ();
|
|
# Setup the special CONFIG section
|
|
$params{"sectionTitle"} = "CONFIG";
|
|
$params{"sectionParams"} = "";
|
|
$params{"lineList"} = ();
|
|
push @workload, \%params;
|
|
|
|
# Get the lists discribing the data we process
|
|
do 'protoconf.pl' || die "$@\n";
|
|
|
|
# Utility functions
|
|
# Create a unique hash array. Programming Perl, 2nd edition, p291 (p217?)
|
|
package ArrayInstance;
|
|
sub new {
|
|
my $type = shift;
|
|
my %params = @_;
|
|
my $self = {};
|
|
return bless $self, $type;
|
|
}
|
|
|
|
package main;
|
|
|
|
# run a command in the background, return its PID
|
|
# Uses fork: will not run on NT in perl 5.004
|
|
# if the server is "localhost", ignore the rcmd part
|
|
# if stdin, stdout, and/or stderr is set, redirect those for the sub process
|
|
sub forkproc {
|
|
my $rcmd = shift;
|
|
my $server = shift;
|
|
my $command = shift;
|
|
my $stdin = shift;
|
|
my $stdout = shift;
|
|
my $stderr = shift;
|
|
|
|
if (my $pid = fork()) {
|
|
return $pid; # parent
|
|
}
|
|
|
|
# rest of this is in the child
|
|
if ($stdin) { # redirect stdin if needed
|
|
close (STDIN);
|
|
open STDIN, "<$stdin"
|
|
|| die "Couldn't open $stdin for input\n";
|
|
}
|
|
|
|
if ($stdout) { # redirect stdout if needed
|
|
close (STDOUT);
|
|
open STDOUT, ">>$stdout"
|
|
|| die "Couldn't open $stdout for output\n";
|
|
}
|
|
|
|
if ($stderr) { # redirect stderr if needed
|
|
close (STDERR);
|
|
open STDERR, ">>$stderr"
|
|
|| die "Couldn't open $stderr for output\n";
|
|
}
|
|
|
|
if ($server =~ /^localhost$/i) {
|
|
exec $command;
|
|
die "Coundn't exec $command:$!\n";
|
|
} else {
|
|
exec split (/\s+/, $rcmd), $server, $command;
|
|
die "Coundn't exec $rcmd $server $command:$!\n";
|
|
}
|
|
}
|
|
|
|
# Relocate file to tmp directory (if it is in the results directory),
|
|
# and put a ~ on the end of it.
|
|
# ASSUMES tmp and results are on the same partition (on NT, same drive).
|
|
# Usage: fileBackup (filename)
|
|
sub fileBackup {
|
|
my $filename = shift;
|
|
my $bfile = $filename;
|
|
|
|
(-f $filename) || return 0; # file doent exist
|
|
$bfile =~ s/$resultbase/$tmpbase/; # move to tmp
|
|
$bfile .= "~"; # indicate that this is a backup
|
|
(-f $bfile) && unlink ($bfile);
|
|
#print "Backing up $filename to $bfile\n"; # DEBUG
|
|
rename ($filename, $bfile) || unlink ($filename);
|
|
}
|
|
|
|
# Insert text into a file after a tagline
|
|
# fileInsertAfter (filename, tagstring, newtext)
|
|
sub fileInsertAfter {
|
|
my $filename = shift || die "fileInsertAfter: missing filename";
|
|
my $tagline = shift || die "fileInsertAfter: missing tagline";
|
|
my $newtext = shift || die "fileInsertAfter: missing text";
|
|
my $foundit = 0;
|
|
|
|
open(OLD, "<$filename") ||
|
|
open(OLD, "gunzip -c $filename |") ||
|
|
die "fileInsertAfter: Could not open input $filename: $!";
|
|
open(NEW, ">$filename+") ||
|
|
die "fileInsertAfter: Could not open output $filename+: $!";
|
|
|
|
while (<OLD>) {
|
|
print NEW $_; # copy (including tagline)
|
|
|
|
next unless (/$tagline/); # matched tagline
|
|
|
|
print NEW $newtext; # insert new text
|
|
$foundit++;
|
|
last; # only change first occurance
|
|
}
|
|
|
|
if ($foundit) { # copy rest of file
|
|
while (<OLD>) {
|
|
print NEW $_;
|
|
}
|
|
}
|
|
|
|
close (OLD);
|
|
close (NEW);
|
|
if ($foundit) {
|
|
fileBackup ($filename);
|
|
rename ("$filename+", "$filename");
|
|
#print "Updated $filename\n"; # DEBUG
|
|
return $foundit;
|
|
} else {
|
|
($params{DEBUG}) && print "No change to $filename\n"; # DEBUG
|
|
unlink ("$filename+");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
# Do text for text replacements in a file.
|
|
# Perl wildcards are automatically quoted.
|
|
# fileReplace (filename, matchPat, oldtext, newtext)
|
|
sub fileReplaceText {
|
|
my $filename = shift || die "fileReplaceText: missing filename";
|
|
my $tagline = shift || die "fileReplaceText: missing tagline ($filename)";
|
|
my $oldtext = shift;
|
|
my $newtext = shift;
|
|
my $foundit = 0;
|
|
|
|
return if ($newtext eq ""); # nothing to do
|
|
return if ($oldtext eq ""); # nothing can be done
|
|
|
|
open(OLD, "<$filename") ||
|
|
open(OLD, "gunzip -c $filename |") ||
|
|
die "fileReplaceText: Could not open input $filename: $!";
|
|
open(NEW, ">$filename+") ||
|
|
die "fileReplaceText: Could not open output $filename+: $!";
|
|
|
|
$oldtext =~ s/([][{}*+?^.\/])/\\$1/g; # quote regex syntax
|
|
|
|
while (<OLD>) {
|
|
if (/$tagline/i) { # matched tagline
|
|
$foundit++;
|
|
s/$oldtext/$newtext/; # do the replace
|
|
}
|
|
print NEW $_;
|
|
}
|
|
|
|
close (OLD);
|
|
close (NEW);
|
|
if ($foundit) {
|
|
fileBackup ($filename);
|
|
rename ("$filename+", "$filename");
|
|
#print "Updated $filename\n"; # DEBUG
|
|
return $foundit;
|
|
} else {
|
|
($params{DEBUG}) && print "No change to $filename\n"; # DEBUG
|
|
unlink ("$filename+");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
# copy a file to a new name. Handles possible compression. OS independent.
|
|
# fileCopy (filename, newname)
|
|
sub fileCopy {
|
|
my $filename = shift || die "fileReplaceText: missing filename";
|
|
my $newname = shift || die "fileReplaceText: missing newname ($filename)";
|
|
|
|
open(OLD, "<$filename") ||
|
|
open(OLD, "gunzip -c $filename |") ||
|
|
die "fileReplaceText: Could not open input $filename: $!";
|
|
open(NEW, ">$newname") ||
|
|
die "fileReplaceText: Could not open output $newname: $!";
|
|
|
|
while (<OLD>) { # copy it
|
|
print NEW $_;
|
|
}
|
|
|
|
close (OLD);
|
|
close (NEW);
|
|
return 0;
|
|
}
|
|
|
|
# display a file to STDOUT. Handles possible compression
|
|
sub fileShow {
|
|
my $filename = shift || die "fileShow: missing filename";
|
|
open(SHOWIT, "<$filename") ||
|
|
open(SHOWIT, "gunzip -c $filename.gz |") ||
|
|
die "fileShow: Couldn't open $filename: $!";
|
|
while (<SHOWIT>) { print; }
|
|
close(SHOWIT);
|
|
}
|
|
|
|
# sub function to figure time extents
|
|
# (start, end) = dataMinMax counterName \@protocols oldstarttime oldendtime
|
|
# Use 0 for uninitialized start or end
|
|
sub dataMinMax {
|
|
my $name = shift;
|
|
my $protos = shift;
|
|
my $start = shift;
|
|
my $end = shift;
|
|
|
|
# make global
|
|
# create the plot script and data files
|
|
# Figure out the encompassing time extent
|
|
foreach $p (@$protos) { # create the plot data files
|
|
my @times = sort numeric keys %{ $graphs{$p}->{$name}};
|
|
if ($#times <= 0) {
|
|
next;
|
|
}
|
|
if (($start == 0) || ($times[0] < $start)) {
|
|
$start = $times[0];
|
|
}
|
|
if (($end == 0) || ($times[0] > $end)) {
|
|
$end = $times[$#times];
|
|
}
|
|
}
|
|
#printf ("Data $name start=$start end=$end (%d points)...\n",
|
|
# $end - $start);
|
|
return ($start, $end);
|
|
}
|
|
|
|
# simple function to formatted a number into n, n K, n M, or n G
|
|
sub kformat {
|
|
my $n = shift;
|
|
my $r = "";
|
|
if ($n > (1024*1024*1024)) {
|
|
$r = sprintf "%.2fG", $n / (1024*1024*1024);
|
|
} elsif ($n > (1024*1024)) {
|
|
$r = sprintf "%.2fM", $n / (1024*1024);
|
|
} elsif ($n > 1024) {
|
|
$r = sprintf "%.2fK", $n / 1024;
|
|
} else {
|
|
$r = sprintf "%d ", $n;
|
|
}
|
|
return $r;
|
|
}
|
|
|
|
# simple function to formatted a time into Ns, Nms, or Nus
|
|
# the goal is to make a table of timss uncluttered and easy to read
|
|
# I dont convert to minutes or hours because the non-1000x multipliers
|
|
# are hard to back solve in your head for comparisons
|
|
sub tformat {
|
|
my $n = shift;
|
|
my $r = "";
|
|
if ($n == 0.0) {
|
|
$r = "0.0"; # make exactly 0 explicit
|
|
} elsif ($n < 0.001) {
|
|
$r = sprintf "%.2fus", $n * 1000 * 1000;
|
|
} elsif ($n < 1.0) {
|
|
$r = sprintf "%.2fms", $n * 1000;
|
|
} elsif ($n >= 1000.0) {
|
|
$r = sprintf "%.0fs", $n;
|
|
} elsif ($n >= 100.0) {
|
|
$r = sprintf "%.1fs", $n;
|
|
} else {
|
|
$r = sprintf "%.3fs", $n;
|
|
}
|
|
return $r;
|
|
}
|
|
|
|
#Usage: commify (1234567) returns 1,234,567
|
|
sub commify { # perl cookbook p64-65
|
|
my $text = reverse $_[0];
|
|
$text =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g;
|
|
return scalar reverse $text;
|
|
}
|
|
|
|
# subroutine to enable numeric sorts. Programming Perl p218
|
|
# Use: sort numeric ...
|
|
sub numeric { $a <=> $b; }
|
|
|
|
# on NT, turn slash to backslash, then print. Else print.
|
|
sub pathprint {
|
|
my $str = shift;
|
|
$str =~ s!/!\\!g if ($params{NT}); # turn slash to back slash
|
|
print $str;
|
|
}
|
|
|
|
# figureTimeNumber number
|
|
# Given an number like: 60m, 1h, 100s, 4d, 200
|
|
# Return 60, 1, 100, 4, 200
|
|
sub figureTimeNumber {
|
|
my $arg = shift;
|
|
|
|
($arg =~ /([0-9]+)(s|sec|second|seconds|m|min|minute|minutes|h|hr|hour|hours|d|day|days)$/i)
|
|
&& return $1;
|
|
return $arg; # return default
|
|
}
|
|
|
|
# figureTimeUnits number, default
|
|
# Given an number like: 60m, 1h, 100s, 4d
|
|
# Return a string of minutes, hours, seconds, days
|
|
# Else return the second argument
|
|
sub figureTimeUnits {
|
|
my $arg = shift;
|
|
|
|
($arg =~ /(s|sec|second|seconds)$/i) && return "seconds";
|
|
($arg =~ /(m|min|minute|minutes)$/i) && return "minutes";
|
|
($arg =~ /(h|hr|hour|hours)$/i) && return "hours";
|
|
($arg =~ /(d|day|days)$/i) && return "days";
|
|
|
|
return shift; # return default
|
|
}
|
|
|
|
# figureTimeSeconds number, defaultUnits
|
|
# Given an number like: 60m, 2h, 100s, 4d
|
|
# Return 60*60, 2*60*60, 100, 4*24*60*60
|
|
sub figureTimeSeconds {
|
|
my $arg = shift;
|
|
|
|
($arg =~ /([0-9]+)(s|sec|second|seconds)$/i) && return $1;
|
|
($arg =~ /([0-9]+)(m|min|minute|minutes)$/i) && return (60*$1);
|
|
($arg =~ /([0-9]+)(h|hr|hour|hours)$/i) && return (60*60*$1);
|
|
($arg =~ /([0-9]+)(d|day|days)$/i) && return (24*60*60*$1);
|
|
|
|
if ($_) {
|
|
my $def = shift;
|
|
return $arg * figureTimeSeconds ("1$def"); # return scaled by default
|
|
} else {
|
|
return $arg; # return it
|
|
}
|
|
}
|
|
|
|
# BACK COMPATIBILITY (everything now in the workload file)
|
|
# read the testbed conf file, convert to workload sections
|
|
# machine, how many processes, how many threads/proc, arch
|
|
# only the first 2 fields are required. Lines starting with # are ignored.
|
|
# You can include other files using <include conf/filename.tbd>
|
|
# exampe:
|
|
# client1 5 10 SunOS5.5.1
|
|
sub readTestbedFile {
|
|
my $filename = shift;
|
|
foreach $section (@workload) {
|
|
next unless ($section->{sectionTitle} =~ /CLIENT/o);
|
|
print "Testbed $filename skipped, clients read in workload\n";
|
|
return 1; # clients already read in workload
|
|
}
|
|
|
|
my $level = 0;
|
|
if ($_) {
|
|
$level = 1 + shift;
|
|
die "Too many nested includes ($level) in $filename!"
|
|
unless ($level < 100);
|
|
}
|
|
my $handle = "$filename$level";
|
|
open($handle, "<$filename") ||
|
|
open($handle, "gunzip -c $filename.gz |") ||
|
|
die "Couldn't open testbed $filename: $!";
|
|
|
|
while(<$handle>) {
|
|
chomp;
|
|
|
|
s/#.*//; # strip any comments from line
|
|
|
|
m/^\s*$/o && next; # continue if blank line
|
|
|
|
# handle include statement
|
|
if (m/^<(include|INCLUDE)\s+([^\s]+)\s*>/o) {
|
|
#print "Including $2 from $filename\n";
|
|
readTestbedFile ($2, $level) || die;
|
|
next;
|
|
}
|
|
|
|
# get the server name and number of processes
|
|
my @line = split(/\s+/);
|
|
# create CLIENT entry in workload
|
|
my $sparm = ArrayInstance->new();
|
|
if ($line[1]) {
|
|
$sparm->{"sectionTitle"} = "CLIENT";
|
|
$sparm->{"sectionParams"} = "HOSTS=$line[0]";
|
|
$sparm->{"PROCESSES"} = $line[1];
|
|
$sparm->{"THREADS"} = $line[2] if ($line[2]);
|
|
$sparm->{"ARCH"} = $line[3] if ($line[3]);
|
|
} else {
|
|
$sparm->{"sectionTitle"} = "MONITOR";
|
|
$sparm->{"sectionParams"} = "HOSTS=$line[0]";
|
|
$sparm->{"COMMAND"} = $line[2];
|
|
}
|
|
($params{DEBUG})
|
|
&& print "<$sparm->{sectionTitle} $sparm->{sectionParams}>\n";
|
|
push @workload, $sparm;
|
|
}
|
|
close ($handle);
|
|
}
|
|
|
|
# BACK COMPATIBILITY (everything now in the saved workload file)
|
|
# This is now only needed to process mailstone4.1 runs
|
|
sub readConfigFile {
|
|
my $filename = shift;
|
|
open(CONFIG, "<$filename") ||
|
|
open(CONFIG, "gunzip -c $filename.gz |") ||
|
|
die "Couldn't open config file $filename: $!";
|
|
|
|
while(<CONFIG>) {
|
|
chomp;
|
|
|
|
s/#.*//; # strip any comments from line
|
|
|
|
m/^\s*$/o && next; # continue if blank line
|
|
|
|
# get the property and value
|
|
my @line = split(/=/);
|
|
$params{$line[0]} = $line[1];
|
|
}
|
|
close CONFIG;
|
|
}
|
|
|
|
# read the workload file and store it as a list of hashes
|
|
# Each hash always has the fields: sectionTitle and sectionParams
|
|
# usage: readWorkloadFile filename, \@list
|
|
sub readWorkloadFile {
|
|
my $filename = shift || die "readWorkloadFile: Missing file name";
|
|
my $plist = shift || die "readWorkloadFile: Missing return list";
|
|
my $level = 0; # file inclusion level
|
|
my @handles;
|
|
|
|
my $fh = "$filename$level";
|
|
|
|
($params{DEBUG}) && print "Reading workload from $filename.\n";
|
|
open($fh, "<$filename") ||
|
|
open($fh, "gunzip -c $filename.gz |") ||
|
|
die "readWorkloadFile Couldn't open testbed $filename: $!";
|
|
|
|
my $sparm=0;
|
|
my $conline = "";
|
|
|
|
while($fh) {
|
|
while(<$fh>) {
|
|
s/#.*//; # strip any comments from line (quoting?)
|
|
s/\s*$//; # strip trailing white space
|
|
if ($conline) { # utilize line continue
|
|
$_ = $conline . "\\\n" . $_;
|
|
$conline = "";
|
|
}
|
|
if (m/\\$/o) { # check for quoted line continue
|
|
s/\\$//; #
|
|
$conline = $_;
|
|
next;
|
|
}
|
|
s/^\s*//; # strip initial white space
|
|
m/^$/o && next; # continue if blank line
|
|
|
|
# handle include statement
|
|
if (m/^<include\s+(\S+)\s*>/io) {
|
|
($params{DEBUG})
|
|
&& print "readWorkloadFile include $1 from $filename.\n";
|
|
push @handles, $fh; # push current handle on to stack
|
|
if ($level++ > 99) { # check recursion and make handles unique
|
|
die "readWorkloadFile: include level too deep: $filename $level\n";
|
|
}
|
|
$fh = "$1$level";
|
|
open($fh, "<$1") ||
|
|
open($fh, "gunzip -c $1.gz |") ||
|
|
die "readWorkloadFile Couldn't open testbed file $1: $!";
|
|
$filename = $1; # for error messages
|
|
next;
|
|
}
|
|
|
|
if (m!^</(\w+)>$!o) { # end of section
|
|
my $end = $1;
|
|
unless ($sparm->{"sectionTitle"} =~ /$end/i) {
|
|
die "readWorkloadFile Mismatched section $filename: $. '$sparm->{sectionTitle}' '$end'\n";
|
|
return 0;
|
|
}
|
|
($params{DEBUG}) && print "</$sparm->{sectionTitle}>\n";
|
|
push @$plist, $sparm;
|
|
$sparm = 0;
|
|
next;
|
|
}
|
|
|
|
if (m!^<(\w+)\s*(.*)>$!o) { # start of section
|
|
my $sec = $1;
|
|
my $more = $2;
|
|
|
|
if ($sparm) {
|
|
die "readWorkloadFile Missing section end $filename: $. '$sparm->{sectionTitle}'\n";
|
|
}
|
|
if ($sec =~ /CONFIG/i) { # special case, map to existing global
|
|
$sparm = \%params;
|
|
} elsif ($sec =~ /DEFAULT/i) { # special case, only one DEFAULT
|
|
if ($defaultSection) { # use existing defaultSection
|
|
$sparm = $defaultSection;
|
|
} else { # create a new one
|
|
$sparm = ArrayInstance->new();
|
|
$sparm->{"sectionTitle"} = uc $sec; # ignore case
|
|
$sparm->{"lineList"} = ();
|
|
$defaultSection = $sparm;
|
|
}
|
|
} else {
|
|
$sparm = ArrayInstance->new();
|
|
$sparm->{"sectionTitle"} = uc $sec; # ignore case
|
|
$sparm->{"lineList"} = ();
|
|
}
|
|
$sparm->{"sectionParams"} = $more; # take newest more info
|
|
($params{DEBUG})
|
|
&& print "<$sparm->{sectionTitle} $sparm->{sectionParams}>\n";
|
|
next;
|
|
}
|
|
|
|
# must be in a section, get parameters
|
|
unless ($sparm) {
|
|
die "readWorkloadFile Entry encountered outside a section $filename: $. $_\n";
|
|
return 0;
|
|
}
|
|
my ($nm, $val) = split (/[\s=]+/, $_, 2);
|
|
$nm = uc $nm; # ignore case
|
|
($params{DEBUG}) && print " $nm = $val\n";
|
|
if ($nm =~ /ACCOUNTFORMAT/) { # BACK COMPATIBILITY
|
|
print "WARNING: 'accountFormat' is obsolete. Use 'addressFormat' and 'loginFormat'\n";
|
|
$sparm->{"addressFormat"} = $val;
|
|
push @{$sparm->{"lineList"}}, "addressFormat $val";
|
|
$val =~ s/@.+$//; # strip at and everything after
|
|
$sparm->{"loginFormat"} = $val;
|
|
push @{$sparm->{"lineList"}}, "loginFormat $val";
|
|
next;
|
|
} elsif ($nm =~ /NUMACCOUNTS/) { # BACK COMPATIBILITY
|
|
print "WARNING: 'numAccounts' is obsolete. Use 'numAddresses' and 'numLogins'\n";
|
|
$sparm->{"numAddresses"} = $val;
|
|
push @{$sparm->{"lineList"}}, "numAddresses $val";
|
|
$sparm->{"numLogins"} = $val;
|
|
push @{$sparm->{"lineList"}}, "numLogins $val";
|
|
next;
|
|
} elsif ($nm =~ /BEGINACCOUNTS/) { # BACK COMPATIBILITY
|
|
print "WARNING: 'beginAccounts' is obsolete. Use 'firstAddress' and 'firstLogin'\n";
|
|
$sparm->{"firstAddress"} = $val;
|
|
push @{$sparm->{"lineList"}}, "firstAddress $val";
|
|
$sparm->{"firstLogin"} = $val;
|
|
push @{$sparm->{"lineList"}}, "firstLogin $val";
|
|
next;
|
|
}
|
|
push @{$sparm->{"lineList"}}, $_; # save lines in original order
|
|
$sparm->{$nm} = $val;
|
|
next;
|
|
}
|
|
close ($fh);
|
|
$fh = pop @handles || last; # empty include stack
|
|
$filename = $fh;
|
|
$sparm = 0; # can only include whole sections
|
|
}
|
|
return 1; # success
|
|
}
|
|
|
|
# Write out a workload list to a file
|
|
# Optionally, pass in a list of sectionTitle's it should ignore
|
|
# usage: writeWorkloadFile filename \@list [\@skipList]
|
|
sub writeWorkloadFile {
|
|
my $filename = shift || die "writeWorkloadFile: Missing file name";
|
|
my $plist = shift || die "writeWorkloadFile: Missing return list";
|
|
my $skip = shift;
|
|
my @skipH;
|
|
my $configSeen = 0;
|
|
my $defaultSeen = 0;
|
|
my @paramH;
|
|
|
|
if ($skip) {
|
|
foreach $s (@$skip) { # turn list into a hash
|
|
$skipH{(uc $s)} = $s; # fix case for index
|
|
}
|
|
}
|
|
foreach $s (@workloadParameters) { # turn list into a hash
|
|
$paramH{(uc $s)} = $s; # fix case for index
|
|
}
|
|
|
|
($params{DEBUG}) && print "Writing workload to $filename.\n";
|
|
unless (open(WORKOUT, ">$filename")) {
|
|
die "Couldn't open testbed $filename: $!";
|
|
}
|
|
|
|
foreach $sparm (@$plist) { # each hash reference in the list
|
|
if (($skip)
|
|
&& ($skipH{$sparm->{"sectionTitle"}})) {
|
|
#($params{DEBUG}) &&
|
|
#print "Skipping section $sparm->{sectionTitle}\n";
|
|
next;
|
|
}
|
|
|
|
# all CONFIG,DEFAULT sections point to the same hash, output once only
|
|
if ($sparm->{"sectionTitle"} =~ /^CONFIG$/) {
|
|
next if $configSeen;
|
|
$configSeen++;
|
|
}
|
|
if ($sparm->{"sectionTitle"} =~ /^DEFAULT$/) {
|
|
next if $defaultSeen;
|
|
$defaultSeen++;
|
|
}
|
|
if ($sparm->{sectionParams}) { # write section with extra args
|
|
print WORKOUT "<$sparm->{sectionTitle} $sparm->{sectionParams}>\n";
|
|
} else {
|
|
print WORKOUT "<$sparm->{sectionTitle}>\n";
|
|
}
|
|
if ($sparm->{"sectionTitle"} =~ /^(CONFIG|CLIENT)$/) {
|
|
# for Config or Client, output the hash to get computed config
|
|
foreach $k (sort keys %$sparm) { # output each parameter
|
|
# skip sectionTitle and sectionParams
|
|
($k =~ /^(sectionTitle|sectionParams|lineList)$/) && next;
|
|
printf WORKOUT " %s\t%s\n",
|
|
($paramH{$k}) ? $paramH{$k} : $k,
|
|
$sparm->{$k};
|
|
}
|
|
} else { # write out the line list
|
|
foreach $l (@{$sparm->{"lineList"}}) {
|
|
print WORKOUT " $l\n";
|
|
}
|
|
}
|
|
print WORKOUT "</$sparm->{sectionTitle}>\n\n";
|
|
}
|
|
close WORKOUT;
|
|
}
|
|
|
|
# Usage: getClientFilename hostname section
|
|
sub getClientFilename {
|
|
my $cli = shift || die "Missing client name";
|
|
my $section = shift || die "Missing section hash";
|
|
return "$tmpdir/$cli-$section->{GROUP}.out"
|
|
if ($params{USEGROUPS} && $section->{GROUP});
|
|
return "$tmpdir/$cli.out"
|
|
}
|
|
|
|
sub setConfigDefaults { # set CONFIG defaults
|
|
# These are set after writing out the test copy to avoid clutter
|
|
|
|
# Path to gnuplot executable
|
|
$params{GNUPLOT}="gnuplot/gnuplot"
|
|
unless ($params{GNUPLOT});
|
|
|
|
# This is the directory the client lives in
|
|
$params{TEMPDIR} = "/var/tmp"
|
|
unless($params{TEMPDIR});
|
|
|
|
# Set default remote shell
|
|
$params{RSH} = "/usr/bin/rsh"
|
|
unless($params{RSH});
|
|
|
|
# Set default remote copy
|
|
$params{RCP} = "/usr/bin/rcp"
|
|
unless($params{RCP});
|
|
|
|
# Size of generated gifs
|
|
$params{CHARTHEIGHT} = 480
|
|
unless($params{CHARTHEIGHT});
|
|
$params{CHARTWIDTH} = 640
|
|
unless($params{CHARTWIDTH});
|
|
$params{CHARTPOINTS} = int (($params{CHARTWIDTH}-60)*0.8)
|
|
unless($params{CHARTPOINTS});
|
|
|
|
|
|
# The name of the remote executable
|
|
$params{CLIENTCOMMAND} = "mailclient"
|
|
unless ($params{CLIENTCOMMAND});
|
|
|
|
# Set default monitoring command
|
|
$params{MONITORCOMMAND} = "vmstat %f"
|
|
unless($params{MONITORCOMMAND});
|
|
|
|
# Set default switches to makeusers
|
|
$params{MAKEUSERSARGS} = "-4"
|
|
unless ($params{MAKEUSERSARGS});
|
|
|
|
# Figure out @protocols, this sets the report order
|
|
@protocols = ();
|
|
{
|
|
my %skipH;
|
|
foreach $s (@nonProtocolSections) { # turn list into a hash
|
|
#print "$s ";
|
|
$skipH{(uc $s)} = $s; # fix case for index
|
|
}
|
|
print "\n";
|
|
foreach $sparm (@workload) { # each hash reference in the list
|
|
next if ($skipH{$sparm->{"sectionTitle"}});
|
|
|
|
($params{DEBUG}) &&
|
|
print "Found protocol ". $sparm->{"sectionTitle"} . "\n";
|
|
|
|
push @protocols, $sparm->{"sectionTitle"};
|
|
# add to skip list so only added once
|
|
$skipH{(uc $sparm->{"sectionTitle"})} = $sparm->{"sectionTitle"};
|
|
}
|
|
}
|
|
@protocolsAll = @protocols;
|
|
push @protocolsAll, "Total";
|
|
|
|
# figure out the graphs ???
|
|
}
|
|
|
|
sub parseArgs { # get args
|
|
while (@ARGV) {
|
|
my $arg = shift(@ARGV);
|
|
|
|
if ($arg =~ /^-a$/i) { # was undocumented feature in 4.1
|
|
$params{ADDGRAPHS} = shift(@ARGV); # extra graphs
|
|
next;
|
|
}
|
|
if ($arg =~ /^-b$/i) {
|
|
$params{TITLE} = shift(@ARGV); # banner
|
|
next;
|
|
}
|
|
|
|
# BACK COMPATIBILITY (everything now in the saved workload file)
|
|
if ($arg =~ /^-c$/i) { # config file, read when encountered
|
|
my $configFile = shift(@ARGV);
|
|
readConfigFile ($configFile);
|
|
next;
|
|
}
|
|
|
|
if ($arg =~ /^-d$/i) {
|
|
$params{DEBUG}++; # Debug
|
|
next;
|
|
}
|
|
|
|
if ($arg =~ /^-h$/i) { # Help
|
|
print "Usage: -w workfile [-t time] [-r ramptime] [-l load] [-v] [-d]\n";
|
|
print "\t[-b banner] [-n notes] [-s sysconfigfile] [-a add_graphs_file]\n";
|
|
print "\t[-c configfile] [-m machinefile] [-z] [PARAM=value]...\n";
|
|
|
|
die "Usage";
|
|
}
|
|
|
|
if ($arg =~ /^-l$/i) { # "load", FIX: naming conventions
|
|
$params{CLIENTCOUNT} = shift(@ARGV); # desired client count
|
|
next;
|
|
}
|
|
|
|
# BACK COMPATIBILITY (everything now in the saved workload file)
|
|
if ($arg =~ /^-m$/i) {
|
|
$params{TESTBED} = shift(@ARGV); # testbed machines file
|
|
next;
|
|
}
|
|
|
|
if ($arg =~ /^-n$/i) {
|
|
$params{COMMENTS} = shift(@ARGV); # notes
|
|
next;
|
|
}
|
|
|
|
if ($arg =~ /^-r$/i) {
|
|
$params{RAMPTIME} = shift(@ARGV); # ramptime
|
|
next;
|
|
}
|
|
if ($arg =~ /^-s$/i) {
|
|
$params{SYSCONFIG} = shift(@ARGV); # system config html file
|
|
next;
|
|
}
|
|
if ($arg =~ /^-t$/i) {
|
|
$params{TIME} = shift(@ARGV); # test time
|
|
next;
|
|
}
|
|
|
|
if ($arg =~ /^-v$/i) {
|
|
$params{VERBOSE} = 1; # verbose mode
|
|
next;
|
|
}
|
|
|
|
if ($arg =~ /^-w$/i) { # workload file (may occur multiple times)
|
|
my $f = shift(@ARGV);
|
|
readWorkloadFile ($f, \@workload) || die "Error reading workload: $@\n";
|
|
$params{WORKLOAD} = $f;
|
|
next;
|
|
}
|
|
|
|
if ($arg =~ /^-z$/i) {
|
|
$params{NT} = 1; # NT mode
|
|
next;
|
|
}
|
|
|
|
# any other CONFIG parameter: FIELD=value
|
|
if ($arg =~ /^(\w+)=(\S.*)$/) {
|
|
my $field = uc $1;
|
|
$params{$field} = $2;
|
|
next;
|
|
}
|
|
die "Unknown argument '$arg'";
|
|
}
|
|
|
|
if ($params{NT}) { # should use Cwd module
|
|
$cwd = `cd`; # NT get current directory
|
|
$cwd = `pwd` unless ($cwd); # in case we are really on UNIX
|
|
} else {
|
|
$cwd = `pwd`; # in case we are really on UNIX
|
|
}
|
|
chomp $cwd; # strip NL
|
|
}
|
|
|
|
return 1;
|