diff --git a/README.md b/README.md
index 7d344e0..b9aa9df 100644
--- a/README.md
+++ b/README.md
@@ -63,6 +63,14 @@ You must download and install the following or StorScore will not work:
The Visual Studio 2015 C++ runtime libraries for x86 & x64:
https://www.microsoft.com/en-us/download/details.aspx?id=48145
+StorScore requires libXML. Install it from the command line:
+
+ For ActiveState perl installs:
+ $> ppm install XML::LibXML
+
+ For Strawberry perl installs:
+ $> cpan XML::LibXML
+
StorScore will work without these components, but some features will be
disabled:
@@ -107,4 +115,4 @@ Open Source Code of Conduct
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
-or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
\ No newline at end of file
+or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
diff --git a/StorScore.cmd b/StorScore.cmd
index 3d8e8a7..6c0e349 100644
--- a/StorScore.cmd
+++ b/StorScore.cmd
@@ -115,7 +115,7 @@ else
"$prefix-" . strftime( "%Y-%m-%d_%H-%M-%S", localtime );
}
-print "Targeting " . uc( $target->type ) . ": " . $target->model . "\n";
+print "\nTargeting " . uc( $target->type ) . ": " . $target->model . "\n\n";
my $recipe = Recipe->new(
file_name => $recipe_file,
diff --git a/StorScore.ver b/StorScore.ver
index 546d892..ad12a2d 100644
--- a/StorScore.ver
+++ b/StorScore.ver
@@ -2,4 +2,4 @@ The results in this directory were generated from
the version of storscore published on the date listed
below. If there is no date, the results were generated
from an unpublished & untracked version of code.
-Tue 01/24/2017 9:00:53.83
+Tue 02/21/2017 16:48:27.05
diff --git a/bin/DiskSpd.exe b/bin/DiskSpd.exe
index ff63ed5..c00c244 100644
Binary files a/bin/DiskSpd.exe and b/bin/DiskSpd.exe differ
diff --git a/lib/CommandLine.pm b/lib/CommandLine.pm
index f2df997..d0c9542 100644
--- a/lib/CommandLine.pm
+++ b/lib/CommandLine.pm
@@ -363,8 +363,8 @@ END
if( $self->demo_mode )
{
$self->_active_range( 1 );
- $self->_test_time_override( 5 );
- $self->_warmup_time_override( 0 );
+ $self->_test_time_override( 10 ); # seconds
+ $self->_warmup_time_override( 5 ); # seconds
}
if( defined $self->test_time_override )
diff --git a/lib/DiskSpdParser.pm b/lib/DiskSpdParser.pm
index 9149469..6ff8c2b 100644
--- a/lib/DiskSpdParser.pm
+++ b/lib/DiskSpdParser.pm
@@ -31,36 +31,166 @@ use warnings;
use Moose;
use Util;
-sub parse_cmd_line($$)
+sub parse_workload($$$)
{
my $self = shift;
+ my $dir = shift;
my $cmd_line = shift;
my $stats_ref = shift;
+
+ $cmd_line =~ /.+-X(.+).xml/;
+ my $xml_profile_path = $1;
+
+ if( $xml_profile_path )
+ {
+ # NB: we can't use the filename as-is from the result file because
+ # the results directory probably moved from its original location
+
+ $xml_profile_path =~ /.*(profile-.*)/;
+ my $xml_profile = $1;
+
+ parse_cmd_xml($dir, "$xml_profile.xml", $stats_ref);
+ }
+ else
+ {
+ parse_cmd_flags($cmd_line, $stats_ref);
+ }
+}
+
+sub parse_cmd_xml($$$)
+{
+ my $dir = shift;
+ my $profile_filename = shift;
+ my $stats_ref = shift;
+
+ my %target_stats = ();
+ my $target_count = 0;
+ my $sum_of_write_mix = 0;
+ my $sum_of_QD = 0;
+ my $sum_of_entropy = 0;
+
+ my $xml_parser = XML::LibXML->new();
+ my $doc = $xml_parser->parse_file( "$dir\\$profile_filename" );
+
+ foreach my $target ( $doc->findnodes( '//Target' ) )
+ {
+ $target_count++;
+
+ my $path = $target->findvalue( './Path' );
+ #
+ #cannot target multiple files when there is no filesystem
+ $target_stats{$path}{'Raw Disk'} = 0;
+
+ my $write_ratio = $target->findvalue( './WriteRatio' );
+ $target_stats{$path}{'W Mix'} = $write_ratio;
+ $target_stats{$path}{'R Mix'} = 100 - $write_ratio;
+ $sum_of_write_mix += $write_ratio;
+
+ my $io_size_B = $target->findvalue( './BlockSize' );
+ $target_stats{$path}{'Access Size'} = $io_size_B / BYTES_PER_KB_BASE2;
+
+ my $alignment = $io_size_B;
+ if( $target->findvalue( './Random' ) )
+ {
+ $target_stats{$path}{'Access Type'} = 'random';
+ $alignment = $target->findvalue( './Random' );
+ }
+ else
+ {
+ $target_stats{$path}{'Access Type'} = 'sequential';
+ $alignment = $target->findvalue( './StrideSize' );
+ }
+ $target_stats{$path}{'Alignment'} = $alignment / BYTES_PER_KB_BASE2;
+
+ my $ios_per_thread = $target->findvalue( './RequestCount' );
+ my $num_threads = $target->findvalue( './ThreadsPerFile' );
+ $target_stats{$path}{'QD'} = $ios_per_thread * $num_threads;
+ $sum_of_QD += $target_stats{$path}{'QD'};
+
+ my $entropy_filename = $target->findvalue( './/FilePath' );
+
+ #search for the format of Storscore's pre-generated entropy files
+ if( $entropy_filename =~ /.?([^\\]*)_pct_comp\.bin/ )
+ {
+ my $file_name = $1;
+
+ $file_name =~ /(\d+)/;
+
+ $target_stats{$path}{'Compressibility'} = $1;
+ $sum_of_entropy += $1;
+ }
+ else
+ {
+ #file name format is not recognized
+ $target_stats{$path}{'Compressibility'} = "unknown";
+ $sum_of_entropy = "unknown";
+ }
+ }
+
+ # Issue review: the aggregate workload of multiple targets is random
+ # Is there anything more intersting we can call it? For example, if all the
+ # individual workloads are sequential, should we call it something like
+ # "streaming"?
+
+ # This entry representing the aggregate workload
+ $target_stats{'Total'}{'Raw Disk'} = 0;
+ $target_stats{'Total'}{'Access Type'} = 'random';
+ $target_stats{'Total'}{'W Mix'} = $sum_of_write_mix / $target_count;
+ $target_stats{'Total'}{'R Mix'} = 100 - $target_stats{'Total'}{'W Mix'};
+ $target_stats{'Total'}{'QD'} = $sum_of_QD;
+ $target_stats{'Total'}{'Compressibility'} = $sum_of_entropy / $target_count;
+
+ $stats_ref->{'Workloads'} = \%target_stats;
+}
+sub parse_cmd_flags($$)
+{
+ my $cmd_line = shift;
+ my $stats_ref = shift;
+
+ my %target_stats = ();
+
+ $cmd_line =~ /\s+(".*"|\S+)$/ or die;
+ my $target = $1;
+
+ if( $target =~ /^\#\d+$/ )
+ {
+ $target_stats{'Total'}{'Raw Disk'} = 1;
+ }
+ else
+ {
+ $target_stats{'Total'}{'Raw Disk'} = 0;
+ }
if( $cmd_line =~ /-w(\d+)/ )
{
- $stats_ref->{'W Mix'} = $1;
- $stats_ref->{'R Mix'} = 100 - $stats_ref->{'W Mix'};
+ $target_stats{'Total'}{'W Mix'} = $1;
+ $target_stats{'Total'}{'R Mix'} = 100 - $1;
}
else
{
- $stats_ref->{'W Mix'} = 0;
- $stats_ref->{'R Mix'} = 100;
+ $target_stats{'Total'}{'W Mix'} = 0;
+ $target_stats{'Total'}{'R Mix'} = 100;
}
- if( $cmd_line =~ /-r/ )
+ $cmd_line =~ /-b(\w+)/;
+ $target_stats{'Total'}{'Access Size'} = human_to_kilobytes( $1 );
+
+ $target_stats{'Total'}{'Alignment'} = human_to_kilobytes( $1 );
+ if( $cmd_line =~ /-r(\d+.*)\s+/ or $cmd_line =~ /-si(\d+.*)\s+/ )
{
- $stats_ref->{'Access Type'} = 'random';
+ $target_stats{'Total'}{'Alignment'} = human_to_kilobytes( $1 );
+ }
+
+ if( $cmd_line =~ /-r\s+/ )
+ {
+ $target_stats{'Total'}{'Access Type'} = 'random';
}
else
{
- $stats_ref->{'Access Type'} = 'sequential';
+ $target_stats{'Total'}{'Access Type'} = 'sequential';
}
- $cmd_line =~ /-b(\w+)/;
- $stats_ref->{'Access Size'} = human_to_kilobytes( $1 );
-
my $num_threads = 1;
if( $cmd_line =~ /-t(\d+)/ )
@@ -71,52 +201,31 @@ sub parse_cmd_line($$)
$cmd_line =~ /-o(\d+)/;
my $ios_per_thread = $1;
- $stats_ref->{'QD'} = $ios_per_thread * $num_threads;
+ $target_stats{'Total'}{'QD'} = $ios_per_thread * $num_threads;
- if( $cmd_line =~ /-Z.*?([^\\]*)\.bin/ )
+ #search for the format of Storscore's pre-generated entropy files
+ if( $cmd_line =~ /-Z.*?([^\\]*)_pct_comp\.bin/ )
{
my $file_name = $1;
$file_name =~ /(\d+)/;
- $stats_ref->{'Compressibility'} = $1;
+ $target_stats{'Total'}{'Compressibility'} = $1;
}
else
{
- $stats_ref->{'Compressibility'} = 0;
+ #file name format is not recognized
+ $target_stats{'Total'}{'Compressibility'} = "unknown";
}
- $cmd_line =~ /\s+(".*"|\S+)$/ or die;
- my $target = $1;
+ $stats_ref->{'Workloads'} = \%target_stats;
- if( $target =~ /^\#\d+$/ )
- {
- $stats_ref->{'Raw Disk'} = 1;
- }
- else
- {
- $stats_ref->{'Raw Disk'} = 0;
- }
}
my $pct_table_pat = qr/\|\s+(.+)\s+\|\s+(.+)\s+\|\s+(.+)\s+/;
my @extract_rules =
(
- {
- match => qr/.+alignment:\s+(\d+).+/,
- store =>
- [
- 'Alignment'
- ]
- },
- {
- match => qr/.+stride:\s+(\d+).+/,
- store =>
- [
- 'Alignment'
- ]
- },
{
match => qr/min $pct_table_pat/,
store =>
@@ -236,7 +345,7 @@ my @extract_rules =
},
);
-sub remove_na($@)
+sub remove_na($)
{
my $stats_ref = shift;
@@ -255,34 +364,103 @@ sub parse($$)
my $LOG = shift;
my $stats_ref = shift;
-
+
+ my %target_stats = ();
+ my $previous_line = "";
+ my $target_count = 0;
+
while( my $line = <$LOG> )
{
+
+ if( $line =~ /path:/ )
+ {
+ $target_count++;
+ }
+
if( $line =~ /^(\w+) IO/ )
{
my $io_type = $1;
while( $line = <$LOG> )
{
+ # This line contains per-thread throughput information
+ if( $line =~ /\s(\d+.*B)/ and $target_count > 1 )
+ {
+ $line =~ s/\s+//g;
+ my @values = split /\|/, $line;
+
+ my $mb = $values[1] / BYTES_PER_MB_BASE2;
+ my $ios = $values[2];
+ my $mb_s = $values[3];
+ my $iops = $values[4];
+ my $lat = $values[5];
+
+ my $target_with_size = $values[7];
+ $target_with_size =~ /(.*)\(.*\)/;
+ my $target = $1;
+
+ $target_stats{$target}{"IOs $io_type"} += $ios;
+ $target_stats{$target}{"MB/sec $io_type"} += $mb_s;
+ $target_stats{$target}{"IOPS $io_type"} += $iops;
+ $target_stats{$target}{"Avg Latency $io_type"} += $ios*$lat;
+ }
+
+ # This line contains the throughput totals for this IO type
if( $line =~ /total:/ )
{
$line =~ s/\s+//g;
my @values = split /\|/, $line;
-
- $stats_ref->{"IOs $io_type"} = $values[1];
- $stats_ref->{"MB/sec $io_type"} = $values[2];
- $stats_ref->{"IOPS $io_type"} = $values[3];
- $stats_ref->{"Avg Latency $io_type"} = $values[4];
+
+ $target_stats{"Total"}{"IOs $io_type"} = $values[1];
+ $target_stats{"Total"}{"MB/sec $io_type"} = $values[2];
+ $target_stats{"Total"}{"IOPS $io_type"} = $values[3];
+ $target_stats{"Total"}{"Avg Latency $io_type"} = $values[4];
last;
}
}
+
+ foreach my $path ( keys %target_stats )
+ {
+ unless ( $path eq "Total" or
+ $target_stats{$path}{"IOs $io_type"} == 0 )
+ {
+ $target_stats{$path}{"Avg Latency $io_type"} =
+ $target_stats{$path}{"Avg Latency $io_type"} /
+ $target_stats{$path}{"IOs $io_type"};
+ }
+ }
+
}
- do_simple_extract( $line, $stats_ref, \@extract_rules );
+
+ if( $line =~ /-ile/ )
+ {
+
+ my $target = "Total";
+ $previous_line =~ s/\s+//g;
+ $target = $previous_line
+ unless ( $previous_line eq "total:" ) or
+ ( $previous_line eq "" );
+
+ while( $line = <$LOG> )
+ {
+ do_simple_extract( $line,
+ $target_stats{$target},
+ \@extract_rules );
+ last if( $line =~ /max/ );
+ }
+ }
+
+ $previous_line = $line;
+ }
+
+ foreach my $path ( keys %target_stats )
+ {
+ remove_na( $target_stats{$path} );
}
- remove_na( $stats_ref, \@extract_rules );
+ $stats_ref->{"Measurements"} = \%target_stats;
}
no Moose;
diff --git a/lib/DiskSpdRunner.pm b/lib/DiskSpdRunner.pm
index 032bd99..0515e4c 100644
--- a/lib/DiskSpdRunner.pm
+++ b/lib/DiskSpdRunner.pm
@@ -66,10 +66,7 @@ if( hyperthreading_enabled() )
$affinity_string = join ',', ( @physical_cores, @logical_cores );
}
-else
-{
- # Do nothing, DiskSpd uses simple round-robin affinity by default
-}
+# Else do nothing, DiskSpd uses simple round-robin affinity by default
sub run($$)
{
@@ -78,18 +75,130 @@ sub run($$)
my $test_ref = shift;
my $run_type = shift;
+ my $run_time = $test_ref->{'run_time'};
+
+ if( $run_type eq 'warmup' )
+ {
+ $run_time = $test_ref->{'warmup_time'};
+ }
+ elsif( $run_type eq 'precondition' )
+ {
+ $run_time = '3600'
+ unless ( $pretend or $self->cmd_line->demo_mode );
+ }
+
+ my $cmd;
+
+ if( $test_ref->{'target_count'} > 1 )
+ {
+ $cmd = $self->get_xml_cmdline($test_ref, $run_time);
+ }
+ else
+ {
+ $cmd = $self->get_cmdline($test_ref, $run_time);
+ }
+
+ my $out_file = $self->output_dir .
+ "\\$run_type-$test_ref->{'description'}.txt";
+
+ open( my $OUT, ">$out_file" )
+ or die "could not open $out_file: $!";
+
+ # Save the command line as line 1 of the file
+ print $OUT "$cmd\n";
+
+ my ( $errorlevel, $stdout, $stderr ) = execute_task( $cmd );
+
+ print $OUT "$stdout\n";
+ print $OUT "$stderr\n";
+
+ close( $OUT );
+
+ print STDERR "\n\tDiskSpd returned non-zero errorlevel" if $errorlevel;
+
+}
+
+sub get_xml_cmdline($$$)
+{
+ my $self = shift;
+ my $test_ref = shift;
+ my $run_time = shift;
+
+ my $compressibility = $test_ref->{'compressibility'};
+ my $profile_template = $recipes_dir .
+ "\\xmlProfiles\\$test_ref->{'xml_profile'}";
+
+ my $parser = XML::LibXML->new();
+ my $doc = $parser->parse_file( $profile_template );
+
+ for my $timespan ( $doc->findnodes( '//TimeSpan' ) )
+ {
+ my $duration_node = XML::LibXML::Element->new( 'Duration' );
+ $duration_node->appendTextNode( "$run_time" );
+ $timespan->addChild( $duration_node );
+ }
+
+ # Use default unless compressibility is specified by the test
+ my $entropy_file =
+ Compressibility::get_filename(
+ $compressibility // $self->cmd_line->compressibility
+ );
+
+ my $entropy_file_size = int Compressibility::FILE_SIZE;
+
+ for my $data_source ( $doc->findnodes( '//RandomDataSource' ) )
+ {
+ my $file_node = XML::LibXML::Element->new('FilePath');
+ $file_node->appendTextNode( "$entropy_file" );
+ $data_source->addChild( $file_node );
+
+ my $file_size_node = XML::LibXML::Element->new( 'SizeInBytes' );
+ $file_size_node->appendTextNode( "$entropy_file_size" );
+ $data_source->addChild( $file_size_node );
+ }
+
+ my $profile_target_nodes = $doc->findnodes( '//Target' );
+ my $profile_target_count = $profile_target_nodes->size();
+ my $recipe_target_count = $test_ref->{'target_count'};
+
+ my $msg = "Number of targets in recipe ($recipe_target_count)" .
+ "does not match profile ($profile_target_count)\n";
+ die $msg unless( $profile_target_count == $recipe_target_count );
+
+ my $file_num = 0;
+ for my $target ( $profile_target_nodes->get_nodelist )
+ {
+ $file_num++;
+
+ my $path = $self->target->volume . "\\$TEST_FILE_STUB$file_num.dat";
+
+ my $path_node = XML::LibXML::Element->new( 'Path' );
+ $path_node->appendTextNode( $path );
+ $target->addChild( $path_node );
+ }
+
+ my $profile_file = $self->output_dir .
+ "\\profile-$test_ref->{'description'}.xml";
+
+ $doc->toFile($profile_file);
+
+ my $cmd = "DiskSpd.exe -X$profile_file";
+
+ return $cmd;
+}
+
+sub get_cmdline($$$)
+{
+ my $self = shift;
+ my $test_ref = shift;
+ my $run_time = shift;
+
my $write_percentage = $test_ref->{'write_percentage'};
my $access_pattern = $test_ref->{'access_pattern'};
my $block_size = $test_ref->{'block_size'};
my $queue_depth = $test_ref->{'queue_depth'};
- my $run_time = $test_ref->{'run_time'};
my $compressibility = $test_ref->{'compressibility'};
- if( $run_type eq 'warmup' )
- {
- $run_time = $test_ref->{'warmup_time'};
- }
-
# Gradually increase the number of threads as queue_depth
# increases in order to prevent CPU limitation.
my $num_threads = 1;
@@ -151,23 +260,7 @@ sub run($$)
$cmd .= $self->target->file_name;
}
- my $out_file = $self->output_dir .
- "\\$run_type-$test_ref->{'description'}.txt";
-
- open( my $OUT, ">$out_file" )
- or die "could not open $out_file: $!";
-
- # Save the command line as line 1 of the file
- print $OUT "$cmd\n";
-
- my ( $errorlevel, $stdout, $stderr ) = execute_task( $cmd );
-
- print $OUT "$stdout\n";
- print $OUT "$stderr\n";
-
- close( $OUT );
-
- print STDERR "\n\tDiskSpd returned non-zero errorlevel" if $errorlevel;
+ return $cmd;
}
no Moose;
diff --git a/lib/Endurance.pm b/lib/Endurance.pm
index 893b5f5..87f2402 100644
--- a/lib/Endurance.pm
+++ b/lib/Endurance.pm
@@ -72,10 +72,10 @@ sub compute_host_writes($$)
my $stats_ref = shift;
my $ddb_ref = shift;
- return unless exists $stats_ref->{'Host Writes Before'};
+ return unless exists $stats_ref->{'Measurements'}{'Total'}{'Host Writes Before'};
- my $before = $stats_ref->{'Host Writes Before'};
- my $after = $stats_ref->{'Host Writes After'};
+ my $before = $stats_ref->{'Measurements'}{'Total'}{'Host Writes Before'};
+ my $after = $stats_ref->{'Measurements'}{'Total'}{'Host Writes After'};
my $diff = $after - $before;
# if the device db doesn't define a unit, assume it's an NVMe drive with the default units
@@ -87,7 +87,8 @@ sub compute_host_writes($$)
$stats_ref->{'Notes'} .= "Host writes too small ($diff). WAF is wrong; "
if $diff < 5;
- $stats_ref->{'Host Writes'} = ( $after - $before ) * $units;
+ $stats_ref->{'Measurements'}{'Total'}{'Host Writes'} =
+ ( $after - $before ) * $units;
}
sub compute_controller_writes($$)
@@ -95,10 +96,10 @@ sub compute_controller_writes($$)
my $stats_ref = shift;
my $ddb_ref = shift;
- return unless exists $stats_ref->{'Controller Writes Before'};
+ return unless exists $stats_ref->{'Measurements'}{'Total'}{'Controller Writes Before'};
- my $before = $stats_ref->{'Controller Writes Before'};
- my $after = $stats_ref->{'Controller Writes After'};
+ my $before = $stats_ref->{'Measurements'}{'Total'}{'Controller Writes Before'};
+ my $after = $stats_ref->{'Measurements'}{'Total'}{'Controller Writes After'};
my $diff = $after - $before;
my $units = $ddb_ref->{'Controller Writes'}{'Unit'} // BYTES_PER_MB_BASE2;
@@ -109,9 +110,11 @@ sub compute_controller_writes($$)
$stats_ref->{'Notes'} .= "Ctlr writes too small ($diff). WAF is wrong; "
if $diff < 5;
- $stats_ref->{'Controller Writes'} = ( $after - $before ) * $units;
+ $stats_ref->{'Measurements'}{'Total'}{'Controller Writes'} =
+ ( $after - $before ) * $units;
- $stats_ref->{'Controller Writes'} += $stats_ref->{'Host Writes'}
+ $stats_ref->{'Measurements'}{'Total'}{'Controller Writes'} +=
+ $stats_ref->{'Measurements'}{'Total'}{'Host Writes'}
if exists $ddb_ref->{'Controller Writes'}{'Additive'};
}
@@ -120,13 +123,14 @@ sub compute_file_system_waf($$)
my $stats_ref = shift;
my $ddb_ref = shift;
- return unless exists $stats_ref->{'Host Writes'};
+ return unless exists $stats_ref->{'Measurements'}{'Total'}{'Host Writes'};
my $host_writes_in_GB =
- $stats_ref->{'Host Writes'} / BYTES_PER_GB_BASE2;
+ $stats_ref->{'Measurements'}{'Total'}{'Host Writes'} /
+ BYTES_PER_GB_BASE2;
- $stats_ref->{'Filesystem Write Amplification'} =
- $host_writes_in_GB / $stats_ref->{'GB Write'};
+ $stats_ref->{'Measurements'}{'Total'}{'Filesystem Write Amplification'} =
+ $host_writes_in_GB / $stats_ref->{'Measurements'}{'Total'}{'GB Write'};
}
sub compute_drive_waf($$)
@@ -135,13 +139,14 @@ sub compute_drive_waf($$)
my $ddb_ref = shift;
return unless
- exists $stats_ref->{'Host Writes'} and
- exists $stats_ref->{'Controller Writes'};
+ exists $stats_ref->{'Measurements'}{'Total'}{'Host Writes'} and
+ exists $stats_ref->{'Measurements'}{'Total'}{'Controller Writes'};
- my $ctrl_writes = $stats_ref->{'Controller Writes'};
- my $host_writes = $stats_ref->{'Host Writes'};
+ my $ctrl_writes = $stats_ref->{'Measurements'}{'Total'}{'Controller Writes'};
+ my $host_writes = $stats_ref->{'Measurements'}{'Total'}{'Host Writes'};
- $stats_ref->{'Drive Write Amplification'} = $ctrl_writes / $host_writes
+ $stats_ref->{'Measurements'}{'Total'}{'Drive Write Amplification'} =
+ $ctrl_writes / $host_writes
unless $host_writes == 0;
}
@@ -150,13 +155,15 @@ sub compute_total_waf($$)
my $stats_ref = shift;
my $ddb_ref = shift;
- return unless exists $stats_ref->{'Controller Writes'};
+ return
+ unless exists $stats_ref->{'Measurements'}{'Total'}{'Controller Writes'};
my $ctrl_writes_in_GB =
- $stats_ref->{'Controller Writes'} / BYTES_PER_GB_BASE2;
+ $stats_ref->{'Measurements'}{'Total'}{'Controller Writes'} /
+ BYTES_PER_GB_BASE2;
- $stats_ref->{'Total Write Amplification'} =
- $ctrl_writes_in_GB / $stats_ref->{'GB Write'};
+ $stats_ref->{'Measurements'}{'Total'}{'Total Write Amplification'} =
+ $ctrl_writes_in_GB / $stats_ref->{'Measurements'}{'Total'}{'GB Write'};
}
sub compute_nand_metrics($$)
@@ -164,16 +171,22 @@ sub compute_nand_metrics($$)
my $stats_ref = shift;
my $ddb_ref = shift;
- return unless exists $stats_ref->{'Drive Write Amplification'};
+ return unless exists
+ $stats_ref->{'Measurements'}{'Total'}{'Drive Write Amplification'};
- my $drive_waf = $stats_ref->{'Drive Write Amplification'};
- my $app_write_bw = $stats_ref->{'MB/sec Write'};
- my $app_gb_written = $stats_ref->{'GB Write'};
+ my $drive_waf =
+ $stats_ref->{'Measurements'}{'Total'}{'Drive Write Amplification'};
+ my $app_write_bw =
+ $stats_ref->{'Measurements'}{'Total'}{'MB/sec Write'};
+ my $app_gb_written =
+ $stats_ref->{'Measurements'}{'Total'}{'GB Write'};
- $stats_ref->{'NAND Writes (GB)'} = $app_gb_written * $drive_waf;
+ $stats_ref->{'Measurements'}{'Total'}{'NAND Writes (GB)'} =
+ $app_gb_written * $drive_waf;
- $stats_ref->{'NAND Write BW (MB/sec)'} = $app_write_bw * $drive_waf
- if exists $stats_ref->{'MB/sec Write'};
+ $stats_ref->{'Measurements'}{'Total'}{'NAND Write BW (MB/sec)'} =
+ $app_write_bw * $drive_waf
+ if exists $stats_ref->{'Measurements'}{'Total'}{'MB/sec Write'};
}
sub compute_dwpd($$)
@@ -183,10 +196,10 @@ sub compute_dwpd($$)
return unless
exists $ddb_ref->{'Rated PE Cycles'} and
- exists $stats_ref->{'Drive Write Amplification'};
+ exists $stats_ref->{'Measurements'}{'Total'}{'Drive Write Amplification'};
my $rated_cycles = $ddb_ref->{'Rated PE Cycles'};
- my $waf = $stats_ref->{'Drive Write Amplification'};
+ my $waf = $stats_ref->{'Measurements'}{'Total'}{'Drive Write Amplification'};
return unless $waf > 0;
@@ -213,8 +226,10 @@ sub compute_dwpd($$)
my $adjusted_cycles = $rated_cycles *
( $total_nand_bytes / $mapped_bytes );
- $stats_ref->{'DWPD 3yr'} = $adjusted_cycles / ( $waf * 3 * 365 );
- $stats_ref->{'DWPD 5yr'} = $adjusted_cycles / ( $waf * 5 * 365 );
+ $stats_ref->{'Measurements'}{'Total'}{'DWPD 3yr'} =
+ $adjusted_cycles / ( $waf * 3 * 365 );
+ $stats_ref->{'Measurements'}{'Total'}{'DWPD 5yr'} =
+ $adjusted_cycles / ( $waf * 5 * 365 );
}
sub test_contains_writes($)
@@ -222,9 +237,9 @@ sub test_contains_writes($)
my $stats_ref = shift;
die "Writes Mix is not defined\n"
- unless exists $stats_ref->{'W Mix'};
+ unless exists $stats_ref->{'Workloads'}{'Total'}{'W Mix'};
- return $stats_ref->{'W Mix'} > 0;
+ return $stats_ref->{'Workloads'}{'Total'}{'W Mix'} > 0;
}
1;
diff --git a/lib/LogmanParser.pm b/lib/LogmanParser.pm
index 842a0d4..5214fc4 100644
--- a/lib/LogmanParser.pm
+++ b/lib/LogmanParser.pm
@@ -80,18 +80,20 @@ sub parse($$)
if( $counter_name =~ /Processor Time/ )
{
- $stats_ref->{'CPU Util'} = $average / 100;
+ $stats_ref->{'Measurements'}{'Total'}{'CPU Util'} = $average / 100;
$cpu_seen = 1;
}
elsif( $counter_name =~ /Disk Queue Length/ )
{
- $stats_ref->{'Measured QD'} = $average;
+ $stats_ref->{'Measurements'}{'Total'}{'Measured QD'} = $average;
$qd_seen = 1;
# Raise a warning if the percentage difference between
# actual QD and measured QD exceeds 90%
- my $target_qd = $stats_ref->{'QD'};
- my $measured_qd = $stats_ref->{'Measured QD'};
+ my $target_qd =
+ $stats_ref->{'Workloads'}{'Total'}{'QD'};
+ my $measured_qd =
+ $stats_ref->{'Measurements'}{'Total'}{'Measured QD'};
my $qd_threshold = 90;
my $qd_diff = abs($target_qd - $measured_qd) / $target_qd * 100;
@@ -105,11 +107,11 @@ sub parse($$)
}
elsif( $counter_name =~ /System Driver Total Bytes/ )
{
- $stats_ref->{'Driver Memory (MB)'} = $average / ( 1024 * 1024 );
+ $stats_ref->{'Measurements'}{'Total'}{'Driver Memory (MB)'} = $average / ( 1024 * 1024 );
}
elsif( $counter_name =~ /System Driver Resident Bytes/ )
{
- $stats_ref->{'Driver Resident Memory (MB)'} = $average / ( 1024 * 1024 );
+ $stats_ref->{'Measurements'}{'Total'}{'Driver Resident Memory (MB)'} = $average / ( 1024 * 1024 );
}
else
{
diff --git a/lib/Power.pm b/lib/Power.pm
index 362d76c..15dc841 100644
--- a/lib/Power.pm
+++ b/lib/Power.pm
@@ -122,7 +122,7 @@ sub parse()
$average = $sum / $count;
}
- $stats_ref->{'System Power (W)'} = $average;
+ $stats_ref->{'Measurements'}{'Total'}{'System Power (W)'} = $average;
return 1;
}
diff --git a/lib/PreconditionRunner.pm b/lib/PreconditionRunner.pm
index b3b9d11..abf6ae8 100644
--- a/lib/PreconditionRunner.pm
+++ b/lib/PreconditionRunner.pm
@@ -30,6 +30,7 @@ use strict;
use warnings;
use Moose;
use Util;
+use DiskSpdRunner;
has 'target' => (
is => 'ro',
@@ -95,12 +96,53 @@ sub write_num_passes
sub run_to_steady_state($)
{
my $self = shift;
-
my %args = @_;
my $msg_prefix = $args{'msg_prefix'};
- my $output_dir = $args{'output_dir'};
my $test_ref = $args{'test_ref'};
+ my $output_dir = $args{'output_dir'};
+
+ # preconditioner.exe does not support multiple files
+ # Future work: implement preconditioning in diskspd
+ # and consolidate this
+ if( $test_ref->{'target_count'} > 1 )
+ {
+ $self->run_with_diskspd( $msg_prefix, $test_ref, $output_dir );
+ }
+ else
+ {
+ $self->run_with_preconditioner( $msg_prefix, $test_ref, $output_dir );
+ }
+}
+
+sub run_with_diskspd($$$)
+{
+ my $self = shift;
+ my $msg_prefix = shift;
+ my $test_ref = shift;
+ my $output_dir = shift;
+
+ my $run_time = '3600';
+ $run_time = '10' if ( $self->cmd_line->demo_mode );
+ $run_time = '0' if ( $pretend );
+
+ print $msg_prefix . "static-length ($run_time sec)\n";
+
+ my $iogen = DiskSpdRunner->new(
+ target => $self->target,
+ cmd_line => $self->cmd_line,
+ output_dir => $output_dir
+ );
+
+ $iogen->run( $test_ref, 'precondition' );
+}
+
+sub run_with_preconditioner($$$)
+{
+ my $self = shift;
+ my $msg_prefix = shift;
+ my $test_ref = shift;
+ my $output_dir = shift;
my $write_percentage = $test_ref->{'write_percentage'} // die;
my $access_pattern = $test_ref->{'access_pattern'} // die;
diff --git a/lib/Recipe.pm b/lib/Recipe.pm
index 66a3232..7bb8c90 100644
--- a/lib/Recipe.pm
+++ b/lib/Recipe.pm
@@ -147,7 +147,13 @@ sub try_legalize_arguments
}
}
- # N.B.: You can return zero here to skip this test
+ # skip multitarget tests that are ill-defined
+ if( $step_ref->{'target_count'} > 1 )
+ {
+ return 0 unless $step_ref->{'xml_profile'};
+ return 0 unless $self->cmd_line->io_generator eq 'diskspd';
+ return 0 if $self->cmd_line->raw_disk;
+ }
return 1;
}
@@ -183,6 +189,37 @@ sub warn_illegal_args
warn "$msg\n\n";
}
}
+
+ # warn about poorly-defined multitarget tests
+ if( $step_ref->{'target_count'} > 1 )
+ {
+ unless( $step_ref->{'xml_profile'} )
+ {
+ my $msg = "\tWarning!\n";
+ $msg .= "\tRecipe defines multiple targets and no xml profile.\n";
+ $msg .= "\tSkipping step ". $self->current_step . ".";
+
+ warn "$msg\n\n";
+ }
+
+ unless( $self->cmd_line->io_generator eq 'diskspd' )
+ {
+ my $msg = "\tWarning!\n";
+ $msg .= "\tMultitarget tests must use Diskspd.\n";
+ $msg .= "\tSkipping step ". $self->current_step . ".";
+
+ warn "$msg\n\n";
+ }
+
+ if( $self->cmd_line->raw_disk )
+ {
+ my $msg = "\tWarning!\n";
+ $msg .= "\tMultitarget tests cannot run without a file system.\n";
+ $msg .= "\tSkipping step ". $self->current_step . ".";
+
+ warn "$msg\n\n";
+ }
+ }
}
sub canonicalize_step
@@ -194,12 +231,16 @@ sub canonicalize_step
return unless $kind ~~ [qw( test precondition )];
+ $step_ref->{'target_count'} = $step_ref->{'target_count'} // 1;
+
# create read_percentage from write_percentage
$step_ref->{'read_percentage'} =
- 100 - $step_ref->{'write_percentage'};
+ 100 - $step_ref->{'write_percentage'}
+ unless $step_ref->{'target_count'} > 1;
# ensure description can be used as a unique filename
- $step_ref->{'description'} = make_legal_filename( $step_ref->{'description'});
+ $step_ref->{'description'} =
+ make_legal_filename( $step_ref->{'description'});
}
sub apply_overrides
@@ -210,13 +251,15 @@ sub apply_overrides
my $kind = $step_ref->{'kind'};
- return unless $kind eq 'test';
+ return unless $kind eq 'test' or 'precondition';
if( defined $self->cmd_line->test_time_override )
{
$step_ref->{'run_time'} = $self->cmd_line->test_time_override;
}
+ return unless $kind eq 'test';
+
if( defined $self->cmd_line->warmup_time_override )
{
$step_ref->{'warmup_time'} = $self->cmd_line->warmup_time_override;
@@ -258,7 +301,7 @@ sub handle_step
my $step_number = $self->current_step;
my %step = make_step( $kind, $step_number, @step_args );
-
+
canonicalize_step( \%step );
$self->apply_overrides( \%step );
$self->warn_illegal_args( \%step ) if $do_warnings;
@@ -283,17 +326,18 @@ sub handle_step
$self->advance_current_step();
}
-
+
return $scalar_retval if $context eq 'scalar';
return @list_retval if $context eq 'list';
}
-sub generate_header($$$$)
+sub generate_header($$$$$)
{
my $file_name = shift;
my $package = shift;
my $be_permissive = shift;
- my $be_quiet = shift;
+ my $quiet_stderr = shift;
+ my $quiet_stdout = shift;
my $header = <<"HEADER";
package $package;
@@ -322,12 +366,15 @@ HEADER
$header .= "no warnings 'uninitialized';\n";
}
- if( $be_quiet )
+ # Replace STDOUT and STDERR with NUL
+ if( $quiet_stderr )
{
- # Replace STDOUT and STDERR with NUL
- $header .= "local *STDOUT; open STDOUT, '>NUL';\n";
$header .= "local *STDERR; open STDERR, '>NUL';\n";
}
+ if( $quiet_stdout )
+ {
+ $header .= "local *STDOUT; open STDOUT, '>NUL';\n";
+ }
# Improve the quality of diagnostic messages
$header .= qq(\n# line 1 "$file_name"\n);
@@ -354,7 +401,8 @@ sub execute_recipe
my $recipe_warnings = $args{'recipe_warnings'} // 0;
my $permissive_perl = $args{'permissive_perl'} // 0;
- my $quiet_stdio = $args{'quiet_stdio'} // 0;
+ my $quiet_stderr = $args{'quiet_stderr'} // 0;
+ my $quiet_stdout = $args{'quiet_stdout'} // 0;
state $eval_count = 0;
@@ -414,7 +462,8 @@ sub execute_recipe
$file_name,
$package,
$permissive_perl,
- $quiet_stdio
+ $quiet_stderr,
+ $quiet_stdout
);
$eval_string .= $recipe_str;
@@ -445,7 +494,8 @@ sub execute_recipe
$self->file_name,
$package,
$permissive_perl,
- $quiet_stdio
+ $quiet_stderr,
+ $quiet_stdout
);
$eval_string .= $self->recipe_string;
@@ -466,22 +516,22 @@ sub execute_recipe
sub check_unique_test_descriptions
{
- my $self = shift;
+ my $self = shift;
- my %desc_hash = ();
+ my %desc_hash = ();
- foreach my $step (@{$self->steps})
- {
-
- my $desc = $step->{'description'};
- if ( $desc && $step->{'kind'} eq "test" )
- {
- die "Test description '$desc' is not unique"
- if ( $desc_hash{ $desc } );
- $desc_hash{ $desc } = 1;
- }
- }
+ foreach my $step (@{$self->steps})
+ {
+
+ my $desc = $step->{'description'};
+ if ( $desc && $step->{'kind'} eq "test" )
+ {
+ die "Test description '$desc' is not unique"
+ if ( $desc_hash{ $desc } );
+ $desc_hash{ $desc } = 1;
+ }
+ }
}
sub get_num_steps
@@ -583,7 +633,7 @@ sub get_test_macro_string
$str .= q( purge() if $args{'purge'} // 1; )
if $self->target->do_purge;
- $str .= q( initialize() if $args{'initialize'} // 1; )
+ $str .= q( initialize( %args ) if $args{'initialize'} // 1; )
if $self->target->do_initialize;
$str .= q( precondition( %args ) if $args{'precondition'} // 1; )
@@ -641,7 +691,8 @@ sub BUILD
$self->execute_recipe(
recipe_warnings => 1,
permissive_perl => 1,
- quiet_stdio => 1,
+ quiet_stderr => 0,
+ quiet_stdout => 1,
callback => sub { push @{$self->steps}, {@_}; }
);
}
@@ -687,20 +738,29 @@ sub get_announcement_message
my $step_ref = shift;
my $msg;
- my $command = $step_ref->{'command'};
+ my $command = $step_ref->{'command'};
if( $step_ref->{'kind'} eq 'test' )
{
- my $short_pattern =
- $step_ref->{'access_pattern'} eq "random" ? "rnd" : "seq";
-
- $msg = sprintf( " %4s, %3s, %3d%% read, %3d%% wri, QD=%3d",
- $step_ref->{'block_size'},
- $short_pattern,
- $step_ref->{'read_percentage'},
- $step_ref->{'write_percentage'},
- $step_ref->{'queue_depth'}
- );
+ if( $step_ref->{'target_count'} > 1 )
+ {
+ $msg = $step_ref->{'target_count'} .
+ "-target test using profile " .
+ $step_ref->{'xml_profile'};
+ }
+ else
+ {
+ my $short_pattern =
+ $step_ref->{'access_pattern'} eq "random" ? "rnd" : "seq";
+
+ $msg = sprintf( " %4s, %3s, %3d%% read, %3d%% wri, QD=%3d",
+ $step_ref->{'block_size'},
+ $short_pattern,
+ $step_ref->{'read_percentage'},
+ $step_ref->{'write_percentage'},
+ $step_ref->{'queue_depth'}
+ );
+ }
}
elsif( $step_ref->{'kind'} eq 'idle' )
{
@@ -788,6 +848,7 @@ sub run_step
{
$self->target->initialize(
msg_prefix => $progress,
+ test_ref => $step_ref
);
}
elsif( $kind eq 'precondition' )
@@ -800,7 +861,7 @@ sub run_step
}
elsif( $kind eq 'test' )
{
- $self->target->prepare()
+ $self->target->prepare( $step_ref )
unless $self->target->is_prepared();
unless( $pretend or $self->cmd_line->raw_disk )
diff --git a/lib/SmartCtlParser.pm b/lib/SmartCtlParser.pm
index b767a2f..7a83184 100644
--- a/lib/SmartCtlParser.pm
+++ b/lib/SmartCtlParser.pm
@@ -148,15 +148,15 @@ sub parse_attributes($$$)
if( $attribute_id == $host_writes )
{
- $stats_ref->{"Host Writes$suffix"} = $raw_value;
+ $stats_ref->{'Measurements'}{'Total'}{"Host Writes$suffix"} = $raw_value;
}
elsif( $attribute_id == $ctrl_writes )
{
- $stats_ref->{"Controller Writes$suffix"} = $raw_value;
+ $stats_ref->{'Measurements'}{'Total'}{"Controller Writes$suffix"} = $raw_value;
}
elsif( $attribute_id == $wear_range )
{
- $stats_ref->{"Wear Range$suffix"} = $raw_value;
+ $stats_ref->{'Measurements'}{'Total'}{"Wear Range$suffix"} = $raw_value;
}
}
diff --git a/lib/SqlioParser.pm b/lib/SqlioParser.pm
index ff65836..aadf842 100644
--- a/lib/SqlioParser.pm
+++ b/lib/SqlioParser.pm
@@ -182,10 +182,11 @@ sub parse($$)
process_percentiles( $stats_ref );
}
-sub parse_cmd_line($$)
+sub parse_workload($$$)
{
my $self = shift;
+ my $dir = shift;
my $cmd_line = shift;
my $stats_ref = shift;
diff --git a/lib/StorageToolParser.pm b/lib/StorageToolParser.pm
index 8b7f2da..8f6da50 100644
--- a/lib/StorageToolParser.pm
+++ b/lib/StorageToolParser.pm
@@ -162,7 +162,7 @@ sub parse_attributes($$$)
{
do_simple_extract(
$line,
- $stats_ref,
+ $stats_ref->{'Measurements'}{'Total'},
\@extract_rules_log_entries,
suffix => $suffix );
}
diff --git a/lib/Target.pm b/lib/Target.pm
index f9d076d..80dbf24 100644
--- a/lib/Target.pm
+++ b/lib/Target.pm
@@ -369,6 +369,7 @@ sub purge
sub prepare
{
my $self = shift;
+ my $test_ref = shift;
return if $self->is_prepared;
@@ -397,21 +398,30 @@ sub prepare
# Support testing less then 100% of the disk
$size = int( $size * $self->cmd_line->active_range / 100 );
- # Round to an even increment of 2MB.
- # Idea is to ensure the file size is an even multiple
- # of pretty much any block size we might test.
- $size = int( $size / BYTES_IN_2MB ) * BYTES_IN_2MB;
+ my $file_count = $test_ref->{'target_count'} // 1;
- $self->_file_name( $self->volume . "\\$TEST_FILE_NAME" );
+ # Divide active range evenly among all files
+ $size = int( $size / $file_count );
- if( -e $self->file_name and not $pretend )
+ for my $file_num ( 1 .. $file_count )
{
- die "Error: target file " . $self->file_name . " exists!\n";
+ # Round to an even increment of 2MB.
+ # Idea is to ensure the file size is an even multiple
+ # of pretty much any block size we might test.
+ $size = int( $size / BYTES_IN_2MB ) * BYTES_IN_2MB;
+
+ my $file_name = $self->volume . "\\$TEST_FILE_STUB$file_num.dat";
+ $self->_file_name( $file_name );
+
+ if( -e $self->file_name and not $pretend )
+ {
+ die "Error: target file " . $self->file_name . " exists!\n";
+ }
+
+ fast_create_file( $self->file_name, $size )
+ or die "Couldn't create $size byte file: " .
+ $self->file_name . "\n";
}
-
- fast_create_file( $self->file_name, $size )
- or die "Couldn't create $size byte file: " .
- $self->file_name . "\n";
}
if( defined $self->volume )
@@ -476,7 +486,7 @@ sub initialize
if( defined $test_ref )
{
# Future work: allow for custom init pattern
- ...
+ # ...
}
unless( $self->do_initialize )
@@ -497,7 +507,7 @@ sub initialize
return;
}
- $self->prepare() unless $self->is_prepared();
+ $self->prepare( $test_ref ) unless $self->is_prepared();
$self->precondition_runner->write_num_passes(
msg_prefix => $msg_prefix . "Initializing: ",
@@ -515,7 +525,7 @@ sub precondition
my $msg_prefix = $args{'msg_prefix'} // die;
my $output_dir = $args{'output_dir'} // die;
my $test_ref = $args{'test_ref'} // die;
-
+
unless( $self->do_precondition )
{
my $skip_requested =
@@ -534,8 +544,8 @@ sub precondition
return;
}
- $self->prepare() unless $self->is_prepared();
-
+ $self->prepare( $test_ref ) unless $self->is_prepared();
+
$self->precondition_runner->run_to_steady_state(
msg_prefix => $msg_prefix . "Preconditioning: ",
output_dir => $output_dir,
diff --git a/lib/Util.pm b/lib/Util.pm
index 0520246..e7790cd 100644
--- a/lib/Util.pm
+++ b/lib/Util.pm
@@ -43,6 +43,22 @@ use Win32::API;
use Win32::Process;
use Encode;
+use Module::Load::Conditional 'check_install';
+
+if( check_install( module => 'XML::LibXML' ) )
+{
+ # require runs at execution time, whereas use runs at compile time
+ require XML::LibXML;
+}
+else
+{
+ warn "The XML::LibXML package from CPAN is required and not installed.\n";
+ warn "Please install it via command line:\n";
+ warn "\t\$> ppm install XML::LibXML \\\\ for ActiveState perl installs\n";
+ warn "\t\$> cpan XML::LibXML \\\\ for Strawberry perl installs\n";
+ exit( -1 );
+}
+
no if $PERL_VERSION >= 5.017011,
warnings => 'experimental::smartmatch';
@@ -59,7 +75,7 @@ use vars qw(@ISA @EXPORT);
BYTES_PER_KB_BASE10
BYTES_PER_SECTOR
BYTES_IN_2MB
- $TEST_FILE_NAME
+ $TEST_FILE_STUB
$script_name
$script_dir
$recipes_dir
@@ -135,7 +151,7 @@ use constant BYTES_PER_KB_BASE10 => 1000;
use constant BYTES_PER_SECTOR => 512;
use constant BYTES_IN_2MB => 1024 * 1024 * 2;
-our $TEST_FILE_NAME = 'testfile.dat';
+our $TEST_FILE_STUB = 'testfile';
our $script_name = basename( $PROGRAM_NAME, ".cmd" );
our $script_dir = dirname( $PROGRAM_NAME );
@@ -333,7 +349,7 @@ sub human_to_bytes($)
return ( $human << 10 ) if $human =~ s/(\d+)\s*KB?$/$1/i;
return $human if $human =~ s/(\d+)\s*B?$/$1/i;
- die "Cannot parse the human-readable value";
+ die "Cannot parse the human-readable value, \"$human\"";
}
sub human_to_kilobytes($)
@@ -500,7 +516,7 @@ sub detect_scep_and_warn()
You have the following options:
- Run on a machine without SCEP
- Disable SCEP real-time protection
- - Exclude $TEST_FILE_NAME from SCEP scan.
+ - Exclude $TEST_FILE_STUB n.dat from SCEP scan.
WARNING
}
diff --git a/parse_results.cmd b/parse_results.cmd
index e1459ad..9528f7a 100644
--- a/parse_results.cmd
+++ b/parse_results.cmd
@@ -129,8 +129,9 @@ EXAMPLES
END_USAGE
}
-sub parse_warmup_file($$)
+sub parse_warmup_file($$$)
{
+ my $dir = shift;
my $file_name = shift;
my $stats_ref = shift;
@@ -138,7 +139,7 @@ sub parse_warmup_file($$)
my %warmup_stats;
- parse_test_file( $file_name, \%warmup_stats );
+ parse_test_file( $dir, $file_name, \%warmup_stats );
# Inject into the main stats hash with "Warmup" prefix
foreach my $key ( keys %warmup_stats )
@@ -179,8 +180,9 @@ sub parse_background_file($$)
close $LOG;
}
-sub parse_test_file($$)
+sub parse_test_file($$$)
{
+ my $dir = shift;
my $file_name = shift;
my $stats_ref = shift;
@@ -208,57 +210,64 @@ sub parse_test_file($$)
die "Unknown IO generator\n";
}
- $iogen->parse_cmd_line( $cmd_line, $stats_ref );
+ $iogen->parse_workload( $dir, $cmd_line, $stats_ref );
$iogen->parse( $LOG, $stats_ref );
close $LOG;
}
-sub compute_rw_amounts($)
+sub compute_rw_amounts($$)
{
- my $stats_ref = shift;
+ my $workload_stats = shift;
+ my $measurement_stats = shift;
- my $total_IOs = $stats_ref->{'IOs Total'} // 0;
- my $total_GB = $total_IOs * $stats_ref->{'Access Size'} / KB_PER_GB;
- $stats_ref->{'GB Total'} = $total_GB;
-
- my $read_frac = $stats_ref->{'R Mix'} / 100;
- my $write_frac = $stats_ref->{'W Mix'} / 100;
+ # aggregate data from multitarget test does not have one IO size
+ my $total_IOs = $measurement_stats->{'IOs Total'} // 0;
+ my $io_size = $workload_stats->{'Access Size'} // 0;
+ my $total_GB = $total_IOs * $io_size / KB_PER_GB;
+
+ $measurement_stats->{'GB Total'} = $total_GB;
+
+ my $read_frac = $workload_stats->{'R Mix'} / 100;
+ my $write_frac = $workload_stats->{'W Mix'} / 100;
# The following are *estimates* (not measured)
# If the workload generator measures this directly, do not overwrite
- unless( exists $stats_ref->{'IOs Read'} )
+ unless( exists $measurement_stats->{'IOs Read'} )
{
- $stats_ref->{'IOs Read'} = $total_IOs * $read_frac;
- $stats_ref->{'Notes'} .= "IOs Read is an estimate; ";
+ $measurement_stats->{'IOs Read'} = $total_IOs * $read_frac;
+ $measurement_stats->{'Notes'} .= "IOs Read is an estimate; ";
}
- unless( exists $stats_ref->{'IOs Write'} )
+ unless( exists $measurement_stats->{'IOs Write'} )
{
- $stats_ref->{'IOs Write'} = $total_IOs * $write_frac;
- $stats_ref->{'Notes'} .= "IOs Write is an estimate; ";
+ $measurement_stats->{'IOs Write'} = $total_IOs * $write_frac;
+ $measurement_stats->{'Notes'} .= "IOs Write is an estimate; ";
}
- unless( exists $stats_ref->{'GB Read'} )
+ unless( exists $measurement_stats->{'GB Read'} )
{
- $stats_ref->{'GB Read'} = $total_GB * $read_frac;
- $stats_ref->{'Notes'} .= "GB Read is an estimate; ";
+ $measurement_stats->{'GB Read'} = $total_GB * $read_frac;
+ $measurement_stats->{'Notes'} .= "GB Read is an estimate; ";
}
- unless( exists $stats_ref->{'GB Write'} )
+ unless( exists $measurement_stats->{'GB Write'} )
{
- $stats_ref->{'GB Write'} = $total_GB * $write_frac;
- $stats_ref->{'Notes'} .= "GB Write is an estimate; ";
+ $measurement_stats->{'GB Write'} = $total_GB * $write_frac;
+ $measurement_stats->{'Notes'} .= "GB Write is an estimate; ";
}
+
}
-sub compute_steady_state_error_and_warn($)
+sub compute_steady_state_error_and_warn($$$)
{
my $stats_ref = shift;
+ my $warmup_stats = shift;
+ my $measurement_stats = shift;
- my $warmup_IOPS = $stats_ref->{'Warmup IOPS Total'};
- my $test_IOPS = $stats_ref->{'IOPS Total'};
+ my $warmup_IOPS = $warmup_stats->{'IOPS Total'};
+ my $test_IOPS = $measurement_stats->{'IOPS Total'};
return unless defined $warmup_IOPS;
@@ -278,7 +287,7 @@ sub compute_steady_state_error_and_warn($)
$stats_ref->{'Notes'} .= "No steady-state?; ";
}
- $stats_ref->{'Steady-State Error'} = $rel_diff;
+ $measurement_stats->{'Steady-State Error'} = $rel_diff;
}
sub get_timestamp_from_smart_file($)
@@ -338,7 +347,9 @@ my @cols =
{ name => 'Display Name'},
{ name => 'User Capacity (GB)' },
{ name => 'Partition Size (GB)' },
+ { name => 'Raw Disk' },
{ name => 'Test Description' },
+ { name => 'Target' },
{
name => 'Timestamp',
format => 'mm/dd/yyyy HH:mm:ss',
@@ -365,6 +376,7 @@ my @cols =
},
{
name => 'Alignment',
+ type => 'io_pattern'
},
{
name => 'Access Type',
@@ -684,6 +696,7 @@ sub parse_directories(@)
generate_timestamp( $base_name, \%file_stats );
parse_warmup_file(
+ $dir,
"warmup-$base_name.txt",
\%file_stats
);
@@ -694,13 +707,25 @@ sub parse_directories(@)
);
parse_test_file(
+ $dir,
$test_file_name,
\%file_stats
);
- compute_rw_amounts( \%file_stats );
-
- compute_steady_state_error_and_warn( \%file_stats );
+ foreach my $workload ( keys $file_stats{'Workloads'} )
+ {
+ compute_rw_amounts(
+ $file_stats{'Workloads'}{$workload},
+ $file_stats{'Measurements'}{$workload}
+ );
+
+ compute_steady_state_error_and_warn(
+ \%file_stats,
+ $file_stats{'Warmup Measurements'}{$workload},
+ $file_stats{'Measurements'}{$workload}
+ );
+
+ }
if( $try_precondition )
{
@@ -780,11 +805,11 @@ sub parse_directories(@)
}
# When extracting, we store a statistic's value directly in a hash.
-# Later, we might want to track other information related to that
+# Later, we track other information related to that
# statistic. For example, outlier status, or formatting rules. To
# make this possible, but still enable straight-forward extraction,
# we run this helper function post-extraction to create a 'value'
-# namespace, making room for other things.
+# namespace and to make room for other things.
sub inject_value_namespace($)
{
# BEFORE:
@@ -803,10 +828,26 @@ sub inject_value_namespace($)
foreach my $key ( keys %$stats_ref )
{
- my $value = $stats_ref->{$key};
- delete $stats_ref->{$key};
+ if( $key =~ /Measurements/ or $key =~ /Workloads/ )
+ {
+ foreach my $target ( keys $stats_ref->{$key} )
+ {
+ foreach my $sub_key ( keys $stats_ref->{$key}{$target} )
+ {
+ my $value = $stats_ref->{$key}{$target}{$sub_key};
+ delete $stats_ref->{$key}{$target}{$sub_key};
- $stats_ref->{$key}{'value'} = $value;
+ $stats_ref->{$key}{$target}{$sub_key}{'value'} = $value;
+ }
+ }
+ }
+ else
+ {
+ my $value = $stats_ref->{$key};
+ delete $stats_ref->{$key};
+
+ $stats_ref->{$key}{'value'} = $value;
+ }
}
}
@@ -831,7 +872,7 @@ sub build_test_description_to_io_pattern_hash(@)
next if defined $test_description_to_io_pattern{$test_desc};
my %fields =
- map { $_ => $stats_ref->{$_} } get_io_pattern_cols();
+ map { $_ => $stats_ref->{'Workloads'}{'Total'}{$_} } get_io_pattern_cols();
$test_description_to_io_pattern{$test_desc} = \%fields
}
@@ -859,11 +900,11 @@ sub inject_default_compressibility(@)
foreach my $stats_ref ( @all_stats )
{
my $directory = $stats_ref->{'Directory'};
- my $compress = $stats_ref->{'Compressibility'};
+ my $compress = $stats_ref->{'Workloads'}{'Total'}{'Compressibility'};
$all_compress{$directory}{$compress}++;
}
-
+
my %default_compress;
foreach my $directory ( keys %all_compress )
@@ -1203,7 +1244,7 @@ sub get_matching_values($$\@)
return
grep { defined $_ } # prune undefined vals
- map { $_->{ $col_name }{'value'} } @matching;
+ map { $_->{'Measurements'}{'Total'}{ $col_name }{'value'} } @matching;
}
sub is_good_outlier($$$$)
@@ -1234,9 +1275,8 @@ sub is_bad_outlier($$$$)
);
}
-sub find_outliers($\@)
+sub find_outliers(\@)
{
- my $workbook = shift;
my @all_stats = @{+shift};
my %outliers;
@@ -1261,7 +1301,7 @@ sub find_outliers($\@)
foreach my $test_stats (
filter_by_test_description( $test_desc, @all_stats ) )
{
- my $val = $test_stats->{$col_name}{'value'};
+ my $val = $test_stats->{'Measurements'}{'Total'}{$col_name}{'value'};
next unless defined $val;
@@ -1368,46 +1408,60 @@ sub generate_raw_data_sheet($\@)
foreach my $test_stats ( @all_stats )
{
- my $col_num = 0;
-
- foreach my $col ( @cols )
+ foreach my $target ( keys $test_stats->{'Workloads'} )
{
- my $col_name = $col->{'name'};
- my $format_str = $col->{'format'};
-
- my $value = $test_stats->{$col_name}{'value'};
-
- if( defined $col->{'protect'} )
- {
- $value = protect_excel_string( $value );
- }
+ my $col_num = 0;
- # Convert Time::Piece object to Excel date format
- if( ref $value eq 'Time::Piece' )
+ foreach my $col ( @cols )
{
- $value = unix_date_to_excel_date( $value->epoch )
- }
+ my $col_name = $col->{'name'};
+ my $format_str = $col->{'format'};
+
+ my $value = $test_stats->{$col_name}{'value'};
+
+ # special-case entries
+ $value = $target if $col_name eq 'Target';
+ $value = $test_stats->{'Warmup Measurements'}{$target}{'MB/sec Total'}{'value'}
+ if $col_name eq 'Warmup MB/sec Total';
+
+ # entries in the workload and measurement sub-arrays
+ $value = $test_stats->{'Workloads'}{$target}{$col_name}{'value'}
+ if exists $test_stats->{'Workloads'}{$target}{$col_name};
+ $value = $test_stats->{'Measurements'}{$target}{$col_name}{'value'}
+ if exists $test_stats->{'Measurements'}{$target}{$col_name};
+
+ if( defined $col->{'protect'} )
+ {
+ $value = protect_excel_string( $value );
+ }
- my $format_obj;
+ # Convert Time::Piece object to Excel date format
+ if( ref $value eq 'Time::Piece' )
+ {
+ $value = unix_date_to_excel_date( $value->epoch )
+ }
- if( defined $format_str )
- {
- $format_obj =
- get_format_obj( $workbook, $test_stats, $col_name );
+ my $format_obj;
- $format_obj->set_num_format( $format_str );
-
- # Our percentages are 0 to 100. Excel prefers 0.0 to 1.0.
- $value /= 100 if defined $value and $format_str =~ /%/;
+ if( defined $format_str )
+ {
+ $format_obj =
+ get_format_obj( $workbook, $test_stats, $col_name );
- }
+ $format_obj->set_num_format( $format_str );
+
+ # Our percentages are 0 to 100. Excel prefers 0.0 to 1.0.
+ $value /= 100 if defined $value and $format_str =~ /%/;
- $raw_sheet->write( $row_num, $col_num, $value, $format_obj );
+ }
- $col_num++;
- }
+ $raw_sheet->write( $row_num, $col_num, $value, $format_obj );
+
+ $col_num++;
+ }
- $row_num++;
+ $row_num++;
+ }
}
$raw_sheet->autofilter( 0, 0, 0, $#cols );
@@ -1467,7 +1521,7 @@ sub extract_raw_scores(\@)
my $metric = $col->{'name'};
- my $value = $test_stats->{$metric}{'value'};
+ my $value = $test_stats->{'Measurements'}{'Total'}{$metric}{'value'};
# Every metric doesn't make sense for every test
$raw_scores{$test_desc}{$metric}{$device_id} = $value
@@ -1642,7 +1696,7 @@ sub compute_weights_as_fractions(\%)
foreach my $test_desc ( @unique_test_descriptions )
{
my %io_pattern = %{$test_description_to_io_pattern{$test_desc}};
-
+
foreach my $metric ( @metrics )
{
$total_weight += $weight_func->( $metric, %io_pattern );
@@ -2013,15 +2067,21 @@ die "Nothing to do\n" unless scalar @all_stats > 0;
post_process_stats( @all_stats );
+generate_raw_data_sheet( $workbook, @all_stats );
+
+# process the data for outliers and scores
print "Detecting outliers...\n";
-my %outliers = find_outliers( $workbook, @all_stats );
+my %outliers = find_outliers( @all_stats );
-print "Computing scores...\n";
+print "Writing XLSX file...\n";
+hilight_outliers( $workbook, %outliers, @all_stats );
-my $scores_invalid = 0;
+print "Computing scores...\n";
my %raw_scores = extract_raw_scores( @all_stats );
+my $scores_invalid = 0;
+
unless( scores_hash_is_valid( %raw_scores ) )
{
warn "Error while generating raw scores\n";
@@ -2045,8 +2105,6 @@ unless( scores_hash_is_valid( %normalized_scores ) )
warn "Error while generating normalized scores\n";
$scores_invalid = 1;
}
-
-warn "Scores sheets will not be generated\n" if $scores_invalid;
# Scoring policies compute a weight with arbitrary range.
# Here we convert to a fractional weight between 0 and 1.
@@ -2061,10 +2119,6 @@ my %final_scores = combine_scores( %weighted_score_components );
keys %final_scores == $num_input_dirs
or warn "Warning: could not generate score for 1+ directories.\n";
-print "Writing XLSX file...\n";
-hilight_outliers( $workbook, %outliers, @all_stats );
-generate_raw_data_sheet( $workbook, @all_stats );
-
generate_scores_sheets(
$workbook,
%raw_scores,
@@ -2076,6 +2130,8 @@ generate_scores_sheets(
)
unless $scores_invalid;
+warn "Scores sheets will not be generated\n" if $scores_invalid;
+
$workbook->close() or die "Error closing workbook $outfile: $!\n";
print_sanitization_decoder_ring() if $sanitize;
diff --git a/recipes/14hr_multitarget.rcp b/recipes/14hr_multitarget.rcp
new file mode 100644
index 0000000..4586757
--- /dev/null
+++ b/recipes/14hr_multitarget.rcp
@@ -0,0 +1,56 @@
+# vim: set filetype=perl:
+
+# StorScore
+#
+# Copyright (c) Microsoft Corporation
+#
+# All rights reserved.
+#
+# MIT License
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+#include 'four_corners.rcp';
+
+test(
+ description => "All Sequential Streams",
+ target_count => 64,
+ xml_profile => "64seq.xml",
+ initialize => 0,
+ warmup_time => 60,
+ run_time => 3600,
+);
+
+test(
+ description => "Single Random Stream",
+ target_count => 64,
+ xml_profile => "63seq_1rand.xml",
+ initialize => 0,
+ warmup_time => 60,
+ run_time => 3600,
+);
+
+test(
+ description => "Single Sequential Stream",
+ target_count => 64,
+ xml_profile => "1seq_63rand.xml",
+ initialize => 0,
+ warmup_time => 60,
+ run_time => 3600,
+);
diff --git a/recipes/xmlProfiles/1seq_63rand.xml b/recipes/xmlProfiles/1seq_63rand.xml
new file mode 100644
index 0000000..688e6a7
--- /dev/null
+++ b/recipes/xmlProfiles/1seq_63rand.xml
@@ -0,0 +1,1490 @@
+
+
+ text
+ false
+
+
+ false
+ true
+ false
+ false
+ 0
+ 0
+ 1000
+ 0
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+
+
+
diff --git a/recipes/xmlProfiles/63seq_1rand.xml b/recipes/xmlProfiles/63seq_1rand.xml
new file mode 100644
index 0000000..688e6a7
--- /dev/null
+++ b/recipes/xmlProfiles/63seq_1rand.xml
@@ -0,0 +1,1490 @@
+
+
+ text
+ false
+
+
+ false
+ true
+ false
+ false
+ 0
+ 0
+ 1000
+ 0
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+
+
+
diff --git a/recipes/xmlProfiles/64seq.xml b/recipes/xmlProfiles/64seq.xml
new file mode 100644
index 0000000..50edf82
--- /dev/null
+++ b/recipes/xmlProfiles/64seq.xml
@@ -0,0 +1,1427 @@
+
+
+ text
+ false
+
+
+ false
+ true
+ false
+ false
+ 0
+ 0
+ 1000
+ 0
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+ 4096
+ 100
+ 1
+ 1
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 0
+ 0
+ 0
+ 3
+
+
+
+
+
diff --git a/recipes/xmlProfiles/sample.xml b/recipes/xmlProfiles/sample.xml
new file mode 100644
index 0000000..484ab25
--- /dev/null
+++ b/recipes/xmlProfiles/sample.xml
@@ -0,0 +1,65 @@
+
+
+ text
+ false
+
+
+ false
+ true
+ false
+ false
+ 0
+ 0
+ 1000
+ 0
+
+
+ 4096
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 4096
+ 0
+ 0
+ 1
+ 0
+ 0
+ 2
+ 3
+
+
+ 4096
+ 0
+ false
+ false
+ false
+ false
+ true
+ true
+
+ random
+
+
+ false
+ 4096
+ 0
+ 0
+ 1
+ 0
+ 0
+ 2
+ 3
+
+
+
+
+
diff --git a/recipes/xml_sample.rcp b/recipes/xml_sample.rcp
new file mode 100644
index 0000000..1352739
--- /dev/null
+++ b/recipes/xml_sample.rcp
@@ -0,0 +1,46 @@
+# vim: set filetype=perl:
+
+# StorScore
+#
+# Copyright (c) Microsoft Corporation
+#
+# All rights reserved.
+#
+# MIT License
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+test(
+ description => "Multitarget Sample"
+ target_count => 2,
+ xml_profile => "sample.xml",
+ initialize => 0,
+ warmup_time => 5,
+ run_time => 60,
+);
+
+test(
+ description => "4k Random Reads",
+ write_percentage => 0,
+ access_pattern => 'random',
+ block_size => '4K',
+ queue_depth => 1,
+ warmup_time => 10,
+ run_time => 60,
+);