This commit is contained in:
Mark Santaniello 2015-08-31 15:42:44 -07:00
Родитель 0e55770223
Коммит 49e282ca08
22 изменённых файлов: 957 добавлений и 335 удалений

Просмотреть файл

@ -14,8 +14,9 @@ if %ERRORLEVEL% NEQ 0 (
for /F "tokens=4" %%I IN ('powercfg -getactivescheme') DO set ORIG_SCHEME=%%I
powercfg -setactive SCHEME_MIN
start /B /WAIT /HIGH perl "%~f0" %*
set PERL_ERROR_LEVEL=%ERRORLEVEL%
powercfg -setactive %ORIG_SCHEME%
exit /B %ERRORLEVEL%
exit /B %PERL_ERROR_LEVEL%
);
# StorScore
@ -115,24 +116,6 @@ else
print "Targeting " . uc( $target->type ) . ": " . $target->model . "\n";
if( $target->is_ssd )
{
if( $target->supports_smart and
$target->is_sata and
!$target->is_6Gbps_sata )
{
my $msg;
$msg .= "\n\tWarning!\n";
$msg .= "\tSSD target is not 6Gb/s SATA III.\n";
$msg .= "\tThroughput will be limited.\n";
warn $msg;
}
}
print "\n";
my $recipe = Recipe->new(
file_name => $recipe_file,
output_dir => $output_dir,
@ -152,7 +135,23 @@ $recipe->warn_expected_run_time();
detect_scep_and_warn();
if( $target->must_clean_disk )
if( $target->is_ssd )
{
if( $target->supports_smart and
$target->is_sata and
!$target->is_6Gbps_sata )
{
my $msg;
$msg .= "\tWarning!\n";
$msg .= "\tSSD target is not 6Gb/s SATA III.\n";
$msg .= "\tThroughput will be limited.\n\n";
warn $msg;
}
}
if( $target->do_purge )
{
my $msg;
@ -162,21 +161,47 @@ if( $target->must_clean_disk )
warn $msg;
}
elsif( defined $target->file_name and
( $cmd_line->initialize or $recipe->contains_writes ) )
else
{
unless( -w $target->file_name or $pretend )
if( $target->is_existing_file_or_volume )
{
die "Target is not writable\n"
my $msg;
$msg .= "\tWarning!\n";
$msg .= "\tTarget is an existing file/volume.\n";
$msg .= "\tCan not purge without destroying target.\n";
$msg .= "\tPurge steps will be skipped.\n\n";
warn $msg;
}
my $msg;
if( $target->is_ssd )
{
my $msg;
$msg .= "\tWarning!\n";
$msg .= "\tThis will destroy ";
$msg .= $target->file_name . "\n\n";
$msg .= "\tWarning!\n";
$msg .= "\tCan not eliminate SSD history effect without purge.\n";
$msg .= "\tFor best results target a physical drive and purge.\n\n";
warn $msg;
warn $msg;
}
if( defined $target->file_name and
( $target->do_initialize or $recipe->contains_writes ) )
{
unless( -w $target->file_name or $pretend )
{
die "Target is not writable\n"
}
my $msg;
$msg .= "\tWarning!\n";
$msg .= "\tThis will destroy ";
$msg .= $target->file_name . "\n\n";
warn $msg;
}
}
exit 0 unless should_proceed();
@ -217,13 +242,20 @@ if( $cmd_line->collect_power )
}
}
# TODO: SECURE ERASE
#
# In the future when we support SECURE ERASE, the line below
# should change to be conditional, like this:
# $target->prepare() if $target->is_hdd();
if( defined $cmd_line->purge and not $cmd_line->purge )
{
print "Will skip purging as requested.\n";
}
$target->prepare();
if( defined $cmd_line->initialize and not $cmd_line->initialize )
{
print "Will skip initialization as requested.\n";
}
if( defined $cmd_line->precondition and not $cmd_line->precondition )
{
print "Will skip preconditioning as requested.\n";
}
my $wmic_runner = WmicRunner->new(
target => $target,

5
bin/write_forever.cmd Normal file
Просмотреть файл

@ -0,0 +1,5 @@
@echo off
REM Poor man's "run forever": pass a giant duration
set DISKSPD_CMD=DiskSpd.exe -W10 -w100 -h -d2000000000 %*
echo %DISKSPD_CMD%
%DISKSPD_CMD%

Просмотреть файл

@ -1,3 +0,0 @@
@echo off
REM low bandwidth (100 KB/sec) sequential 64K writes
DiskSpd.exe -W10 -w100 -b64K -h -g100 -d2000000000 %*

Просмотреть файл

@ -65,13 +65,23 @@ has 'recipe' => (
writer => '_recipe'
);
# In most cases you really want $target->do_purge
has 'purge' => (
is => 'ro',
isa => 'Maybe[Bool]',
default => undef,
writer => '_purge'
);
# In most cases you really want $target->do_initialize
has 'initialize' => (
is => 'ro',
isa => 'Bool',
default => 1,
isa => 'Maybe[Bool]',
default => undef,
writer => '_initialize'
);
# In most cases you really want $target->do_precondition
has 'precondition' => (
is => 'ro',
isa => 'Maybe[Bool]',
@ -234,7 +244,12 @@ sub attr
my $name = shift;
my $val = shift;
my $retval = eval( qq(\$self->_$name( '$val' );) );
# The command line argument may contain backslashes,
# for example, in --target. We must escape them,
# before we eval, by converting "\" to "\\" here.
$val =~ s|\\|\\\\|g;
my $retval = eval( qq(\$self->_$name( q($val) );) );
die $EVAL_ERROR unless defined $retval;
}
@ -253,6 +268,7 @@ sub BUILD
"recipe=s" => sub { $self->attr(@_) },
"verbose!" => \$verbose,
"pretend!" => \$pretend,
"purge!" => sub { $self->attr(@_) },
"initialize!" => sub { $self->attr(@_) },
"precondition!" => sub { $self->attr(@_) },
"prompt!" => \$prompt,
@ -408,16 +424,18 @@ USAGE
$script_name [options]
OPTIONS
--target Indicates the drive or file to test (required)
--initialize Write the whole drive before testing. Defaults to true.
--precondition Performs workload-dependent preconditioning. Defaults to true.
--recipe=A.rcp Use the test list defined in "A.rcp"
--collect_smart Collect drive's SMART metadata. Defaults to true.
--collect_logman Collect performance counters from logman. Defaults to true.
--collect_power Collect system power usage statistics. Defaults to true.
--target Indicates the drive or file to test (required).
--purge Erase target before test. Default off for existing volumes.
--initialize Write whole target before testing. Defaults on for SSD.
--precondition Drive to steady-state before test. Defaults on for SSD.
--recipe=A.rcp Use the test list defined in "A.rcp".
--collect_smart Collect drive's SMART metadata. Defaults on.
--collect_logman Collect performance counters from logman. Defaults on.
--collect_power Collect system power usage statistics. Defaults on.
--keep_logman_raw Prevent StorScore from deleting the logman data files.
--target_type Force target type to "ssd" or "hdd". Defaults to "auto".
--start_on_step=n Begin testing on step n of the recipe.
--target_type Force target type to "ssd" or "hdd". Defaults on.
--start_on_step=n Start testing on step n of the recipe.
--stop_on_step=n Stop testing on step n of the recipe.
--pretend For testing, run without touching the target at all.
EXAMPLES

Просмотреть файл

@ -31,39 +31,16 @@ use warnings;
use Moose;
use Util;
has 'raw_disk' => (
is => 'ro',
isa => 'Bool',
has 'target' => (
is => 'ro',
isa => 'Target',
required => 1
);
has 'pdnum' => (
is => 'ro',
isa => 'Str'
);
has 'volume' => (
has 'cmd_line' => (
is => 'ro',
isa => 'Maybe[Str]',
default => undef
);
has 'target_file' => (
is => 'ro',
isa => 'Maybe[Str]',
default => undef
);
has 'demo_mode' => (
is => 'ro',
isa => 'Maybe[Bool]',
default => 0
);
has 'is_target_ssd' => (
is => 'ro',
isa => 'Bool',
default => undef
isa => 'CommandLine',
required => 1
);
use constant NORMAL_GATHER_SECONDS => 540;
@ -78,59 +55,37 @@ use constant QUICK_TEST_MIN_RUN_SECONDS =>
use constant QUICK_TEST_SLOPE_TOLERANCE => 0.5;
sub initialize()
# Future work: take an arbitrary workload here
# but override the write-percentage to 100%
sub write_num_passes
{
my $self = shift;
my $target = $self->raw_disk ? $self->pdnum : $self->target_file;
my %args = @_;
my $num_passes = 1;
if( $self->is_target_ssd and not $self->demo_mode )
{
if ( $self->raw_disk )
{
$num_passes = 2;
}
else
{
my $file_size;
if( $pretend )
{
# Ficticious 42GB test file for pretend mode
$file_size = 42 * BYTES_PER_GB_BASE2;
}
else
{
$file_size = -s $self->target_file;
}
$file_size > 0 or die "Target file has zero size?";
my $vol_size = get_volume_size( $self->volume );
# We want to dirty all of the NAND, including the OP
# to avoid measuring the fresh-out-of-the-box condition.
#
# Writing the drive 2x is overkill, but we do it only once.
#
# Note that in cases where the file is much smaller than
# the drive, we will need to write the file many times in
# order to write the drive once.
$num_passes = int( 2 * ( $vol_size / $file_size ) );
}
}
my $msg_prefix = $args{'msg_prefix'};
my $num_passes = $args{'num_passes'};
# Fake progress message for pretend mode
print "Initializing target: 100% [xx.x MB/s]\n" if $pretend;
if( $pretend )
{
print $msg_prefix . "100% [xx.x MB/s]\n";
}
my $cmd = "precondition.exe ";
$cmd .= "-n$num_passes ";
$cmd .= q(-p"Initializing target: " );
$cmd .= qq(-p"$msg_prefix" );
$cmd .= "-Y ";
$cmd .= $target;
if( $self->cmd_line->raw_disk )
{
$cmd .= $self->target->physical_drive;
}
else
{
$cmd .= $self->target->file_name;
}
my $failed = execute_task( $cmd );
@ -147,12 +102,11 @@ sub run_to_steady_state($)
my $output_dir = $args{'output_dir'};
my $test_ref = $args{'test_ref'};
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 $target = $self->raw_disk ? $self->pdnum : $self->target_file;
my $write_percentage = $test_ref->{'write_percentage'} // die;
my $access_pattern = $test_ref->{'access_pattern'} // die;
my $block_size = $test_ref->{'block_size'} // die;
my $queue_depth = $test_ref->{'queue_depth'} // die;
my $name_string = $test_ref->{'name_string'} // die;
my $block_size_kB = human_to_kilobytes( $block_size );
@ -165,7 +119,7 @@ sub run_to_steady_state($)
$cmd .= "-w$write_percentage ";
$cmd .= "-ss ";
if( $self->demo_mode )
if( $self->cmd_line->demo_mode )
{
$cmd .= "-g" . QUICK_TEST_GATHER_SECONDS . " ";
$cmd .= "-d" . QUICK_TEST_DWELL_SECONDS . " ";
@ -174,10 +128,17 @@ sub run_to_steady_state($)
$cmd .= qq(-p"$msg_prefix" );
$cmd .= $target;
if( $self->cmd_line->raw_disk )
{
$cmd .= $self->target->physical_drive;
}
else
{
$cmd .= $self->target->file_name;
}
my $out_file =
"$output_dir\\precondition-$test_ref->{'name_string'}.txt";
"$output_dir\\precondition-$name_string.txt";
open( my $OUT, ">$out_file" )
or die "could not open $out_file: $!";
@ -186,7 +147,10 @@ sub run_to_steady_state($)
print $OUT "$cmd\n";
# Fake progress message for pretend mode
print "Preconditioning, achieved steady-state after 0 sec\n" if $pretend;
if( $pretend )
{
print $msg_prefix . "achieved steady-state after 0 sec\n";
}
# Don't capture stderr so progress message goes to console
my ( $errorlevel, $stdout ) =

Просмотреть файл

@ -33,8 +33,12 @@ use English;
use feature 'state';
use POSIX 'strftime';
use Symbol 'delete_package';
use Text::Balanced 'extract_bracketed';
use Cwd;
use Util;
use PreconditionRunner;
use DiskSpdParser;
use SqlioParser;
no if $PERL_VERSION >= 5.017011,
warnings => 'experimental::smartmatch';
@ -69,13 +73,6 @@ has 'recipe_string' => (
writer => '_recipe_string'
);
has 'do_precondition' => (
is => 'ro',
isa => 'Bool',
default => 1,
writer => '_do_precondition'
);
has 'steps' => (
is => 'ro',
isa => 'ArrayRef',
@ -120,7 +117,7 @@ has 'current_step' => (
},
);
has 'bg_pids' => (
has 'bg_processes' => (
is => 'ro',
isa => 'ArrayRef',
default => sub { [] },
@ -193,16 +190,17 @@ sub canonicalize_step
my $step_ref = shift;
my $kind = $step_ref->{'kind'};
my $number = $step_ref->{'number'};
return unless $kind eq 'test';
return unless $kind ~~ [qw( test precondition )];
# create read_percentage from write_percentage
$step_ref->{'read_percentage'} =
100 - $step_ref->{'write_percentage'};
# ensure name_string can be used as a filename
# ensure name_string can be used as a unique filename
$step_ref->{'name_string'} =
make_legal_filename( $step_ref->{'name_string'} );
make_legal_filename( $step_ref->{'name_string'} . "-step-$number" );
}
sub apply_overrides
@ -229,21 +227,24 @@ sub apply_overrides
sub make_step
{
my $kind = shift;
my $number = shift;
my @args = @_;
# test uses named-parameter idiom
return ( kind => $kind, @args ) if $kind eq 'test';
my %step_args;
# bg_exec has a single unnamed argument, the command
return ( kind => $kind, command => shift @args )
if $kind eq 'bg_exec';
# Every step records its kind and number
$step_args{'kind'} = $kind;
$step_args{'number'} = $number;
# These kinds use the named-parameter idiom
%step_args = ( %step_args, @args )
if $kind ~~ [qw( test initialize precondition bg_exec )];
# idle has a single unnamed argument, the time
return ( kind => $kind, run_time => shift @args )
%step_args = ( %step_args, run_time => shift @args )
if $kind eq 'idle';
# default: no arguments, just store the kind
return ( kind => $kind );
return %step_args;
}
sub handle_step
@ -255,26 +256,45 @@ sub handle_step
my $kind = shift;
my @step_args = @_;
my %step = make_step( $kind, @step_args );
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;
# Keep track of the context in which we were called
my $context;
$context = 'list' if wantarray;
$context = 'void' unless defined wantarray;
$context = 'scalar' unless wantarray;
my $scalar_retval;
my @list_retval;
my $is_legal = $self->try_legalize_arguments( \%step );
if( $is_legal )
{
$callback->( %step );
# Carry forward our caller's context
$callback->( %step ) if $context eq 'void';
$scalar_retval = $callback->( %step ) if $context eq 'scalar';
@list_retval = $callback->( %step ) if $context eq 'list';
$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 $header = <<"HEADER";
package $package;
@ -297,12 +317,34 @@ HEADER
$header .= "no warnings 'prototype';\n";
}
if( $pretend )
{
# Hide warning "Use of unitialized value"
$header .= "no warnings 'uninitialized';\n";
}
if( $be_quiet )
{
# Replace STDOUT and STDERR with NUL
$header .= "local *STDOUT; open STDOUT, '>NUL';\n";
$header .= "local *STDERR; open STDERR, '>NUL';\n";
}
# Improve the quality of diagnostic messages
$header .= qq(\n# line 1 "$file_name"\n);
return $header;
}
sub generate_footer
{
my $footer = <<"FOOTER";
1; # Return true if everything goes well
FOOTER
return $footer;
}
sub execute_recipe
{
my $self = shift;
@ -313,13 +355,15 @@ sub execute_recipe
my $recipe_warnings = $args{'recipe_warnings'} // 0;
my $permissive_perl = $args{'permissive_perl'} // 0;
my $quiet_stdio = $args{'quiet_stdio'} // 0;
state $eval_count = 0;
# Throw-away package to "insulate" us from the eval code
my $package = "RecipeEval" . $eval_count++;
my @step_kinds = qw(test bg_exec bg_killall idle);
my @step_kinds =
qw(test purge initialize precondition bg_exec bg_killall idle);
{
no strict 'refs';
@ -331,7 +375,7 @@ sub execute_recipe
*$sym = sub
{
$self->handle_step(
return $self->handle_step(
$callback,
$recipe_warnings,
$kind,
@ -347,10 +391,36 @@ sub execute_recipe
{
my $file_name = shift;
my $eval_string =
generate_header( $file_name, $package, $permissive_perl );
# Emulate the behavior of Perl's require statement
foreach my $prefix (@INC)
{
if( -e "$prefix\\$file_name" )
{
$file_name = "$prefix\\$file_name";
last;
}
}
$eval_string .= slurp_file( $file_name );
my $recipe_str = slurp_file( $file_name );
# For SSD: expand test macros to follow proper methodology
# We must do this every time we include a new file.
$recipe_str = $self->expand_test_macro( $recipe_str )
if $self->target->is_ssd();
my $eval_string;
$eval_string .=
generate_header(
$file_name,
$package,
$permissive_perl,
$quiet_stdio
);
$eval_string .= $recipe_str;
$eval_string .= generate_footer();
my $retval = eval( $eval_string );
@ -368,11 +438,19 @@ sub execute_recipe
my $previous_cwd = getcwd();
chdir( $recipes_dir );
my $eval_string;
# Prefix our header to recipe string
my $eval_string =
generate_header( $self->file_name, $package, $permissive_perl );
$eval_string =
generate_header(
$self->file_name,
$package,
$permissive_perl,
$quiet_stdio
);
$eval_string .= $self->recipe_string;
$eval_string .= generate_footer();
# Eval recipe code with our hooks installed
my $retval = eval( $eval_string );
@ -428,7 +506,7 @@ sub estimate_run_time(;$)
$total += $self->calculate_step_run_time( $step_ref );
}
if( $self->do_precondition )
if( $self->target->do_precondition )
{
foreach my $step_ref ( @{$self->steps} )
{
@ -456,17 +534,84 @@ sub contains_writes()
return 0;
}
sub prefix_target_init
{
my $self = shift;
my $input = shift;
my $str = "";
# It's important that we don't add extra newlines otherwise
# error messages may reference the wrong recipe line number.
$str .= "purge();" if $self->target->do_purge;
$str .= "initialize();" if $self->target->do_initialize;
return $str . $input;
}
sub get_test_macro_string
{
my $self = shift;
my $test_args = shift;
my $str;
# It's important that we don't add extra newlines otherwise
# error messages may reference the wrong recipe line number.
$str .= q( do { );
$str .= q( my %args = ) . $test_args . ';';
$str .= q( purge() if $args{'purge'} // 1; )
if $self->target->do_purge;
$str .= q( initialize() if $args{'initialize'} // 1; )
if $self->target->do_initialize;
$str .= q( precondition( %args ) if $args{'precondition'} // 1; )
if $self->target->do_precondition;
$str .= q( test( %args ); );
$str .= q( } );
return $str;
}
sub expand_test_macro
{
my $self = shift;
my $input = shift;
my $output;
while( $input =~ s/(.*?)\btest(\(.*)/$2/s )
{
$output .= $1;
my $test_args = extract_bracketed( $input, '()' );
die $EVAL_ERROR unless defined $test_args;
$output .= $self->get_test_macro_string( $test_args );
}
$output .= $input; # Handle remainder
return $output;
}
sub BUILD
{
my $self = shift;
$self->_recipe_string( slurp_file( $self->file_name ) );
my $recipe_str = slurp_file( $self->file_name );
# Default behavior is to precondition to steady-state when targeting
# an SSD. Allow the command line to override this with --noprecondition.
$self->_do_precondition(
$self->cmd_line->precondition // $self->target->is_ssd
);
# For HDD: do this exactly once before running the recipe
$recipe_str = $self->prefix_target_init( $recipe_str )
if $self->target->is_hdd();
# For SSD: expand test macros to follow proper methodology
$recipe_str = $self->expand_test_macro( $recipe_str )
if $self->target->is_ssd();
$self->_recipe_string( $recipe_str );
# Phase 1: Parse the recipe to estimate runtime, etc.
#
@ -477,7 +622,8 @@ sub BUILD
$self->execute_recipe(
recipe_warnings => 1,
permissive_perl => 1,
callback => sub { push @{$self->steps}, {@_}; }
quiet_stdio => 1,
callback => sub { push @{$self->steps}, {@_}; }
);
}
@ -504,9 +650,21 @@ sub get_time_message
return $msg;
}
sub get_announcement_style
{
my $step_ref = shift;
my $kind = $step_ref->{'kind'};
# These step "kinds" announce their own progress
return 'internal'
if $kind ~~ [qw( purge precondition initialize )];
return 'external';
}
sub get_announcement_message
{
my $self = shift;
my $step_ref = shift;
my $msg;
@ -526,21 +684,34 @@ sub get_announcement_message
}
elsif( $step_ref->{'kind'} eq 'idle' )
{
$msg = "Idling...";
$msg = "Idling";
}
elsif( $step_ref->{'kind'} eq 'bg_exec' )
{
my $command = $step_ref->{'command'};
$msg = qq{Executing "$command" in the background...};
$msg = qq{Executing "$command" in the background};
}
elsif( $step_ref->{'kind'} eq 'bg_killall' )
{
$msg = "Killing all background processes...";
$msg = "Killing all background processes";
}
return $msg;
}
sub bg_killall
{
my $self = shift;
foreach my $proc ( @{$self->bg_processes} )
{
my $pid = $proc->{'pid'};
kill_task( $pid );
}
@{$self->bg_processes} = ();
}
sub run_step
{
my $self = shift;
@ -548,56 +719,95 @@ sub run_step
my $kind = $step_ref->{'kind'};
# TODO: SECURE ERASE
#
# In the future when we support SECURE ERASE, here we will add:
# $target->prepare() if $target->is_ssd();
# Keep track of the context in which we were called
my $context;
$context = 'list' if wantarray;
$context = 'void' unless defined wantarray;
$context = 'scalar' unless wantarray;
if( $kind eq 'test' and $self->do_precondition )
{
my $pc = PreconditionRunner->new(
raw_disk => $self->cmd_line->raw_disk,
pdnum => $self->target->physical_drive,
volume => $self->target->volume,
target_file => $self->target->file_name,
demo_mode => $self->cmd_line->demo_mode,
is_target_ssd => $self->target->is_ssd
);
my $scalar_retval;
my @list_retval;
$pc->run_to_steady_state(
msg_prefix => "Preconditioning, ",
output_dir => $self->output_dir,
test_ref => $step_ref
);
}
my $num_step_digits = length( $self->get_num_steps );
my $progress = sprintf(
"[%3d/%3d] ", $self->current_step, $self->get_num_steps );
"%${num_step_digits}d/%${num_step_digits}d: ",
$self->current_step, $self->get_num_steps
);
my $announce = $self->get_announcement_message( $step_ref );
my $announcement_style = get_announcement_style( $step_ref );
my $time = $self->get_time_message( $step_ref );
if( $announcement_style eq 'external' )
{
my $announce = get_announcement_message( $step_ref );
my $cols_remaining = 80;
my $time = $self->get_time_message( $step_ref );
$cols_remaining -=
my $cols_remaining = 80;
$cols_remaining -=
length( $progress ) +
length( $announce ) +
length( $time ) + 1;
my $pad = ' ' x $cols_remaining;
my $pad = ' ' x $cols_remaining;
print $progress . $announce . $pad . $time;
print $progress . $announce . $pad . $time;
}
if( $kind eq 'test' )
if( $kind eq 'purge' )
{
$self->target->purge(
msg_prefix => $progress,
);
}
elsif( $kind eq 'initialize' )
{
$self->target->initialize(
msg_prefix => $progress,
);
}
elsif( $kind eq 'precondition' )
{
$self->target->precondition(
msg_prefix => $progress,
output_dir => $self->output_dir,
test_ref => $step_ref
);
}
elsif( $kind eq 'test' )
{
$self->target->prepare()
unless $self->target->is_prepared();
unless( $pretend )
{
die "Attempt to test non-existent target file?\n"
unless -e $self->target->file_name;
}
my $ns = $step_ref->{'name_string'};
# Record background activity, if any, during this test
if( scalar @{$self->bg_processes} > 0 )
{
open my $FH, '>', $self->output_dir . "\\background-$ns.txt";
foreach my $proc ( @{$self->bg_processes} )
{
print $FH $proc->{'description'} . "\n";
print $FH $proc->{'command'} . "\n";
print $FH $proc->{'pid'} . "\n\n";
}
close $FH;
}
if( $step_ref->{'warmup_time'} > 0 )
{
$self->io_generator->run( $step_ref, 'warmup' );
}
my $ns = $step_ref->{'name_string'};
$self->smartctl_runner->collect(
file_name => "smart-before-$ns.txt",
output_dir => $self->output_dir,
@ -626,6 +836,38 @@ sub run_step
$self->logman_runner->stop() if defined $self->logman_runner;
$self->power->stop() if defined $self->power;
# If test() called in list context, parse and return results
if( $context eq 'list' )
{
my $iogen_parser;
$iogen_parser = DiskSpdParser->new()
if $self->cmd_line->io_generator eq 'diskspd';
$iogen_parser = SqlioParser->new()
if $self->cmd_line->io_generator eq 'sqlio';
# ISSUE-REVIEW: is this guaranteed to be correct?
my $iogen_outfile = $self->output_dir . "\\test-$ns.txt";
open my $IOGEN_OUT, "<$iogen_outfile"
or die "Error opening $iogen_outfile";
my %stats;
$iogen_parser->parse( $IOGEN_OUT, \%stats );
@list_retval = %stats;
close $IOGEN_OUT;
}
# Allow test to specify that output files be discarded
my $discard_results = $step_ref->{'discard_results'} // 0;
unlink( glob( $self->output_dir . "\\*$ns*" ) )
if $discard_results;
}
elsif( $kind eq 'idle' )
{
@ -635,6 +877,23 @@ sub run_step
}
elsif( $kind eq 'bg_exec' )
{
# ISSUE-REVIEW:
#
# Ensure that the target file exists to support stuff like
# purge();
# bg_exec( do something to target file here );
#
# Does this make sense in a general purpose bg_exec?
$self->target->prepare()
unless $self->target->is_prepared();
unless( $pretend )
{
die "Attempt to test non-existent target file?\n"
unless -e $self->target->file_name;
}
my $command = $step_ref->{'command'};
my $pid = execute_task(
@ -643,15 +902,24 @@ sub run_step
new_window => 1
);
push @{$self->bg_pids}, $pid;
my $description = $step_ref->{'description'};
push @{$self->bg_processes}, {
pid => $pid,
command => $command,
description => $description
};
}
elsif( $kind eq "bg_killall" )
{
kill_task( $_ ) foreach @{$self->bg_pids};
@{$self->bg_pids} = ();
$self->bg_killall();
}
print "\n";
print "\n" if $announcement_style eq 'external';
return $scalar_retval if $context eq 'scalar';
return @list_retval if $context eq 'list';
}
sub run
@ -678,13 +946,17 @@ sub run
unless( ($self->current_step < $self->cmd_line->start_on_step) or
($self->current_step > $self->cmd_line->stop_on_step) )
{
$self->run_step( {@_} );
return $self->run_step( {@_} );
}
# Maintain proper context on skipped steps.
# Avoids "Odd number of elements in hash assignment" warning.
return () if wantarray;
}
);
# kill any leftover background processes
kill_task( $_ ) foreach @{$self->bg_pids};
$self->bg_killall();
}
sub warn_expected_run_time
@ -692,7 +964,7 @@ sub warn_expected_run_time
my $self = shift;
my $num_pc =
$self->do_precondition ? $self->get_num_test_steps() : 0;
$self->target->do_precondition ? $self->get_num_test_steps() : 0;
my $est_pc_time = 0;
@ -715,25 +987,21 @@ sub warn_expected_run_time
$self->estimate_run_time( $est_pc_time )
);
print "\tRun time will be >= $time_string";
print "\tRun time will be >= $time_string.\n";
if( $self->cmd_line->initialize )
if( $self->target->do_initialize )
{
print " after target init";
print "\tThis does not include target init ";
if( $self->target->is_ssd )
{
print " (2 overwrites).\n";
print "(2 overwrites per-test).\n";
}
else
{
print " (1 overwrite).\n";
print "(1 overwrite).\n";
}
}
else
{
print ".\n";
}
if( $num_pc > 0 )
{

Просмотреть файл

@ -36,6 +36,7 @@ no if $PERL_VERSION >= 5.017011,
use Util;
use SmartCtlRunner;
use PreconditionRunner;
has 'cmd_line' => (
is => 'ro',
@ -92,25 +93,67 @@ has 'model' => (
writer => '_model'
);
has 'must_clean_disk' => (
has 'do_create_new_filesystem' => (
is => 'ro',
isa => 'Bool',
default => 0,
writer => '_must_clean_disk'
writer => '_do_create_new_filesystem'
);
has 'must_create_new_filesystem' => (
has 'do_create_new_file' => (
is => 'ro',
isa => 'Bool',
default => 0,
writer => '_must_create_new_filesystem'
writer => '_do_create_new_file'
);
has 'must_create_new_file' => (
has 'do_initialize' => (
is => 'ro',
isa => 'Maybe[Bool]',
default => undef,
writer => '_do_initialize'
);
has 'do_purge' => (
is => 'ro',
isa => 'Maybe[Bool]',
default => undef,
writer => '_do_purge'
);
has 'do_precondition' => (
is => 'ro',
isa => 'Maybe[Bool]',
default => undef,
writer => '_do_precondition'
);
has 'precondition_runner' => (
is => 'ro',
isa => 'Maybe[PreconditionRunner]',
default => undef,
writer => '_precondition_runner'
);
has 'is_purged' => (
is => 'ro',
isa => 'Bool',
default => 0,
writer => '_must_create_new_file'
writer => '_is_purged'
);
has 'is_prepared' => (
is => 'ro',
isa => 'Bool',
default => 0,
writer => '_is_prepared'
);
has 'is_existing_file_or_volume' => (
is => 'ro',
isa => 'Bool',
default => 1,
writer => '_is_existing_file_or_volume'
);
sub is_ssd()
@ -167,8 +210,9 @@ sub BUILD
{
my $self = shift;
my $target_str = $self->cmd_line->target;
my $raw_disk = $self->cmd_line->raw_disk;
my $cmd_line = $self->cmd_line;
my $target_str = $cmd_line->target;
my $raw_disk = $cmd_line->raw_disk;
if( $target_str =~ /(\\\\\.\\PHYSICALDRIVE)?(\d+)$/ )
{
@ -177,17 +221,15 @@ sub BUILD
die qq(Error: target "$target_str" does not exist.\n)
unless physical_drive_exists( $self->physical_drive );
# We *must* ensure the disk is free of any partitions.
# Otherwise, writes can silently fail and appear extremely fast.
$self->_must_clean_disk( 1 );
$self->_is_existing_file_or_volume( 0 );
unless( $raw_disk )
{
$self->_must_create_new_filesystem( 1 );
$self->_must_create_new_file( 1 );
$self->_do_create_new_filesystem( 1 );
$self->_do_create_new_file( 1 );
}
}
elsif( uc( $target_str ) =~ /^([A-Z]{1}\:)$/ )
elsif( uc( $target_str ) =~ /^([A-Z]{1}\:)\\?$/ )
{
die "Error: --raw_disk unsupported with existing volumes.\n"
if $raw_disk;
@ -198,7 +240,7 @@ sub BUILD
$pdname =~ /(\d+$)/;
$self->_physical_drive( $1 );
$self->_must_create_new_file( 1 );
$self->_do_create_new_file( 1 );
}
elsif( -r $target_str or $pretend )
{
@ -231,39 +273,94 @@ sub BUILD
$self->_rotation_rate( $smartctl->rotation_rate );
$self->_sata_version( $smartctl->sata_version );
}
if( $self->is_existing_file_or_volume )
{
# Targeting an existing file/volume, not a whole physical drive.
# We cannot purge without destroying the existing file/volume.
$self->_do_purge( 0 );
}
else
{
# Targeting a whole physical drive, not an existing volume.
# Purge by default, unless the command line specified otherwise.
$self->_do_purge( $cmd_line->purge // 1 );
}
# Default policy (can be overridden by command line):
# SSD: both precondition and initialize
# HDD: do not precondition or initialize
$self->_do_initialize( $cmd_line->initialize // $self->is_ssd );
$self->_do_precondition( $cmd_line->precondition // $self->is_ssd );
$self->_precondition_runner(
PreconditionRunner->new(
cmd_line => $cmd_line,
target => $self
)
);
}
sub purge
{
my $self = shift;
my %args = @_;
my $msg_prefix = $args{'msg_prefix'} // die;
print $msg_prefix;
if( $self->is_purged )
{
print "Skipping purge of already-purged target\n";
return;
}
if( $self->is_existing_file_or_volume )
{
print "Skipping purge of existing file/volume\n";
return;
}
unless( $self->do_purge )
{
my $skip_requested =
( defined $self->cmd_line->purge and
( $self->cmd_line->purge == 0 ) );
if( $skip_requested )
{
print "Skipping purge as requested\n";
}
else
{
print "Skipping purge\n";
}
return;
}
print "Purging\n";
# TODO: SECURE ERASE
#
# When the target is an SSD, we should SECURE ERASE here instead of
# the "diskpart clean":
clean_disk( $self->physical_drive );
$self->_is_purged( 1 );
$self->_is_prepared( 0 );
}
sub prepare
{
my $self = shift;
if( $self->must_clean_disk )
return if $self->is_prepared;
if( $self->do_create_new_filesystem )
{
# TODO: SECURE ERASE
#
# When the target is an SSD, we should SECURE ERASE here instead of
# the "diskpart clean":
#
# if( $self->is_ssd )
# {
# print "Secure erasing disk...\n";
# secure_erase( $self->physical_drive );
# }
# else
# {
# print "Cleaning disk...\n";
# clean_disk( $self->physical_drive );
# }
print "Cleaning disk...\n";
clean_disk( $self->physical_drive );
}
if( $self->must_create_new_filesystem )
{
print "Creating new filesystem...\n";
create_filesystem(
$self->physical_drive,
$self->cmd_line->partition_bytes
@ -272,14 +369,12 @@ sub prepare
$self->_volume( physical_drive_to_volume( $self->physical_drive ) );
}
if( $self->must_create_new_file )
if( $self->do_create_new_file )
{
print "Creating test file...\n";
my $free_bytes = get_volume_free_space( $self->volume );
die "Couldn't determine free space"
unless defined $free_bytes;
unless defined $free_bytes;
# Reserve 1GB right off the top.
# When we tried to use the whole drive, we saw odd errors.
@ -308,28 +403,131 @@ sub prepare
if( defined $self->volume )
{
print "Syncing target volume...\n";
execute_task( "sync.cmd " . $self->volume, quiet => 1 );
}
if( $self->cmd_line->initialize )
{
my $pc = PreconditionRunner->new(
raw_disk => $self->cmd_line->raw_disk,
pdnum => $self->physical_drive,
volume => $self->volume,
target_file => $self->file_name,
demo_mode => $self->cmd_line->demo_mode,
is_target_ssd => $self->is_ssd
);
$self->_is_purged( 0 );
$self->_is_prepared( 1 );
}
$pc->initialize();
sub calculate_num_init_passes
{
my $self = shift;
# Go fast in demo mode
return 1 if $self->cmd_line->demo_mode;
# For HDD: if we choose to initialize, one pass is sufficient
return 1 if $self->is_hdd;
# For SSD: we want to dirty all of the NAND, including the OP
# to avoid measuring the fresh-out-of-the-box condition.
#
# Writing the drive 2x is overkill, but we do it only once.
#
# Note that in cases where the file is much smaller than
# the drive, we will need to write the file many times in
# order to write the drive once.
return 2 if $self->cmd_line->raw_disk;
my $file_size;
if( $pretend )
{
# Ficticious 42GB test file for pretend mode
$file_size = 42 * BYTES_PER_GB_BASE2;
}
else
{
print "Skipping initialization as requested.\n";
$file_size = -s $self->file_name;
}
$file_size > 0 or die "Target file has zero size?";
my $vol_size = get_volume_size( $self->volume );
return( int( 2 * ( $vol_size / $file_size ) ) );
}
# Similar to SNIA "workload independent preconditioning"
sub initialize
{
my $self = shift;
my %args = @_;
my $msg_prefix = $args{'msg_prefix'} // die;
my $test_ref = $args{'test_ref'};
if( defined $test_ref )
{
# Future work: allow for custom init pattern
...
}
unless( $self->do_initialize )
{
my $skip_requested =
( defined $self->cmd_line->initialize and
( $self->cmd_line->initialize == 0 ) );
if( $skip_requested )
{
print $msg_prefix . "Skipping initialization as requested\n";
}
else
{
print $msg_prefix . "Skipping initialization\n";
}
return;
}
$self->prepare() unless $self->is_prepared();
$self->precondition_runner->write_num_passes(
msg_prefix => $msg_prefix . "Initializing: ",
num_passes => $self->calculate_num_init_passes(),
);
}
# Similar to SNIA "workload dependent preconditioning"
sub precondition
{
my $self = shift;
my %args = @_;
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 =
( defined $self->cmd_line->precondition and
( $self->cmd_line->precondition == 0 ) );
if( $skip_requested )
{
print $msg_prefix . "Skipping preconditioning as requested\n";
}
else
{
print $msg_prefix . "Skipping preconditioning\n";
}
return;
}
$self->prepare() unless $self->is_prepared();
$self->precondition_runner->run_to_steady_state(
msg_prefix => $msg_prefix . "Preconditioning: ",
output_dir => $output_dir,
test_ref => $test_ref
);
}
no Moose;

Просмотреть файл

@ -146,6 +146,40 @@ sub parse_warmup_file($$)
}
}
sub parse_background_file($$)
{
my $file_name = shift;
my $stats_ref = shift;
unless( -e $file_name )
{
$stats_ref->{'Background Processes'} = "None";
return;
}
return 0 unless -e $file_name;
open my $LOG, "<$file_name"
or die "Error opening $file_name";
while( my $line = <$LOG> )
{
my $description = $line;
<$LOG>; # Command line is currently ignored
<$LOG>; # PID is currently ignored
<$LOG>; # Blank line expected here
if( defined $stats_ref->{'Background Processes'} )
{
$stats_ref->{'Background Processes'} .= ", ";
}
$stats_ref->{'Background Processes'} .= $description;
}
close $LOG;
}
sub parse_test_file($$)
{
my $file_name = shift;
@ -348,6 +382,7 @@ my @cols =
name => "Warmup MB/sec Total",
format => '#,##0.00',
},
{ name => 'Background Processes' },
);
sub generate_cols_section($)
@ -611,6 +646,11 @@ sub parse_directories(@)
\%file_stats
);
parse_background_file(
"background-$base_name.txt",
\%file_stats
);
parse_test_file(
$test_file_name,
\%file_stats

Просмотреть файл

@ -26,5 +26,5 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
require 'stress.rpm';
include 'stress.rpm';
do_stress_tests( 12 );

Просмотреть файл

@ -26,5 +26,5 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
require 'stress.rpm';
include 'stress.rpm';
do_stress_tests( 24 );

Просмотреть файл

@ -26,5 +26,5 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
require 'stress.rpm';
include 'stress.rpm';
do_stress_tests( 48 );

Просмотреть файл

@ -26,7 +26,7 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
require 'matrix.rpm';
include 'matrix.rpm';
my @block_sizes;

Просмотреть файл

@ -48,7 +48,10 @@ unless( $cmd_line->raw_disk )
my $drive_letter = $target->volume;
$drive_letter =~ s/://;
bg_exec( "sync_loop.cmd $drive_letter" );
bg_exec(
description => "Sync Loop",
command => "sync_loop.cmd $drive_letter"
);
do_workload( "Targeted Test Background Flush" );
bg_killall();
}

Просмотреть файл

@ -26,7 +26,7 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
require 'matrix.rpm';
include 'matrix.rpm';
do_matrix(
access_patterns => [qw( sequential random )],
write_percentages => [qw( 30 )],

Просмотреть файл

@ -45,11 +45,17 @@ if( $target->supports_smart )
{
do_workload( "Targeted Test Read Baseline" );
bg_exec( "wmi_loop.cmd" );
bg_exec(
description => "WMI Loop",
command => "wmi_loop.cmd",
);
do_workload( "Targeted Test SMART Return Status" );
bg_killall();
bg_exec( "smart_loop.cmd " . $target->physical_drive );
bg_exec(
description => "SMART Loop",
command => "smart_loop.cmd " . $target->physical_drive,
);
do_workload( "Targeted Test SMART Read Data " );
bg_killall();
}

Просмотреть файл

@ -26,7 +26,7 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
require 'matrix.rpm';
include 'matrix.rpm';
do_matrix(
access_patterns => [ 'sequential' ],
write_percentages => [ 0 ],

Просмотреть файл

@ -26,7 +26,7 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
require 'matrix.rpm';
include 'matrix.rpm';
do_matrix(
access_patterns => [qw( sequential )],

Просмотреть файл

@ -26,7 +26,7 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
require 'matrix.rpm';
include 'matrix.rpm';
do_matrix(
access_patterns => [qw( sequential )],

Просмотреть файл

@ -26,7 +26,7 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
require 'matrix.rpm';
include 'matrix.rpm';
do_matrix(
access_patterns => [qw( random )],

Просмотреть файл

@ -26,7 +26,7 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
require 'matrix.rpm';
include 'matrix.rpm';
do_matrix(
access_patterns => [qw( random )],

Просмотреть файл

@ -26,27 +26,69 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
sub do_workload($)
{
my $name_string = shift;
test(
name_string => $name_string,
write_percentage => 0,
access_pattern => 'random',
block_size => '8K',
queue_depth => 4,
warmup_time => 60,
run_time => 3600
);
}
# ISSUE-REVIEW: This could be made to work w/raw_disk if its ever important
unless( $cmd_line->raw_disk )
{
do_workload( "Targeted Test Write Impact Baseline" );
my $ns_prefix = "Targeted Test Write Impact";
bg_exec( "write_slowly.cmd " . $target->file_name );
do_workload( "Targeted Test Write Impact" );
bg_killall();
foreach my $block_size ( qw( 4K 8K 64K ) )
{
foreach my $access_pattern ( qw( Random Sequential ) )
{
my %workload = (
write_percentage => 0,
access_pattern => lc( $access_pattern ),
block_size => $block_size,
queue_depth => 4,
warmup_time => 60,
run_time => 3600
);
my $ns = "$ns_prefix $block_size $access_pattern";
# Run the workload once unmolested as a baseline.
my %baseline =
test( %workload, name_string => "$ns Baseline" );
my $read_tput_KBps = $baseline{'MB/sec Total'} * 1000;
# Run again, this time with an aggressor process injecting
# writes in the background. The aggressor is rate-limited
# to a fraction of the baseline read throughput.
foreach my $write_pct ( qw( 5 10 ) )
{
my $write_tput_KBps =
int ( ( $write_pct / 100 ) * $read_tput_KBps );
my $bg_ns = "$write_pct% Injected Writes";
my $cmd = "write_forever.cmd ";
$cmd .= "-b$block_size ";
$cmd .= "-r " if $access_pattern =~ /random/i;
$cmd .= "-g$write_tput_KBps "; # Rate limit
$cmd .= $target->file_name;
# We must do our own purge/initialize, so we can "sneak"
# the bg_exec in between these steps and the actual test.
# If we didn't do this, the purge would occur after the
# bg_exec, effectively destroying the target volume and
# causing the backgrounded process to die with an error.
purge();
initialize();
bg_exec(
description => $bg_ns,
command => $cmd,
);
test(
%workload,
name_string => "$ns $bg_ns",
purge => 0,
initialize => 0,
);
bg_killall();
}
}
}
}

Просмотреть файл

@ -41,6 +41,11 @@ use File::Basename;
use Digest::MD5 'md5_hex';
use English;
use FindBin;
use lib "$FindBin::Bin\\..\\lib";
use Util;
my $script_name = basename( $PROGRAM_NAME );
my $script_dir = dirname( $PROGRAM_NAME );
@ -52,6 +57,9 @@ unless( scalar @ARGV == 1 )
my $outdir = "$script_dir\\$ARGV[0]";
my $total_tests = 0;
my $num_nonzero_exits = 0;
die "Directory $outdir exists\n" if -d $outdir;
mkdir( $outdir );
@ -65,8 +73,15 @@ sub my_exec
my $tmp_filename = "$base_filename.tmp";
my $out_filename = "$base_filename.txt";
system( "echo $cmd > $tmp_filename" );
system( "$cmd >> $tmp_filename 2>&1" );
execute_task( "echo $cmd > $tmp_filename" );
my $errorlevel = execute_task( "$cmd >> $tmp_filename 2>&1" );
execute_task( "echo ERRORLEVEL=$errorlevel >> $tmp_filename" );
if( $errorlevel != 0 )
{
warn qq(Errorlevel $errorlevel while running "$cmd"\n);
$num_nonzero_exits++;
}
# Post process raw output file to remove noise
open( my $in, "<$tmp_filename" );
@ -110,22 +125,45 @@ sub run_one
$cmd .= "--verbose ";
$cmd .= "--noprompt ";
system( "rmdir /S /Q results >NUL 2>&1" );
my_exec( "$cmd $args" );
$total_tests++;
}
sub run_matrix
{
my $base_args = shift;
foreach my $target ( undef, 'P:', 'P:\\fake' )
my @targets = ( undef );
unless( $base_args =~ /--target=/ )
{
foreach my $target_type ( qw( auto ssd hdd ) )
push @targets, 'P:';
push @targets, 'P:\\fake';
}
my @target_types;
if( $base_args =~ /--target_type=/ )
{
@target_types = ( undef );
}
else
{
@target_types = ( qw( auto ssd hdd ) );
}
foreach my $target ( @targets )
{
foreach my $target_type ( @target_types )
{
foreach my $recipe ( undef, 'recipes\\corners.rcp' )
{
my $matrix_args;
my $matrix_args = "";
$matrix_args .= " --target_type=$target_type";
$matrix_args .= " --target_type=$target_type" if defined $target_type;
$matrix_args .= " --target=$target" if defined $target;
$matrix_args .= " --recipe=$recipe" if defined $recipe;
@ -135,6 +173,8 @@ sub run_matrix
}
}
my $overall_start = time();
chdir( ".." );
# Preserve existing results directory
@ -148,10 +188,10 @@ run_one( "--active_range=0" );
run_one( "--active_range=110" );
run_one( "--partition_bytes=1000000000 --raw_disk" );
run_one( "--compressibility=110" );
run_one( "--raw_disk --target=P:" );
run_one( "--raw_disk --target=P:\\fake" );
# These are the defaults anyway, so just run them once
run_one( "--initialize" );
run_one( "--precondition" );
run_one( "--active_range=100" );
run_one( "--collect_smart" );
run_one( "--collect_logman" );
@ -160,9 +200,11 @@ run_one( "--io_generator=diskspd" );
# Run the full matrix on these
run_matrix( "" );
run_matrix( "--noinitialize" );
run_matrix( "--noprecondition" );
run_matrix( "--raw_disk" );
run_matrix( "--initialize --target_type=hdd" );
run_matrix( "--precondition --target_type=hdd" );
run_matrix( "--noinitialize --target_type=ssd" );
run_matrix( "--noprecondition --target_type=ssd" );
run_matrix( "--raw_disk --target=1234" );
run_matrix( "--active_range=1" );
run_matrix( "--active_range=50" );
run_matrix( "--partition_bytes=1000000000" );
@ -181,9 +223,16 @@ run_matrix( "--compressibility=1" );
run_matrix( "--compressibility=20" );
run_matrix( "--results_share=\\\\share\\dir" );
run_matrix( "--io_generator=sqlio" );
run_matrix( "--nopurge --target=1234" );
run_matrix( "--purge --target=P:" );
run_matrix( "--purge --target=P:\\fake" );
# Restore original results directory
system( "rmdir /S /Q results >NUL 2>&1" );
rename( "results.orig", "results" );
print "Done! Diff $outdir directory against another run.\n";
my $dstr = seconds_to_human( time() - $overall_start );
print "Done (took $dstr)\n";
print "Ran $total_tests tests\n";
print "Number of non-zero exits: $num_nonzero_exits\n";
print "Diff $outdir directory against another run.\n";