in Common/Common.cpp [738:1052]
bool Profile::Validate(bool fSingleSpec, SystemInformation *pSystem) const
{
bool fOk = true;
if (GetTimeSpans().size() == 0)
{
fprintf(stderr, "ERROR: no timespans specified\n");
fOk = false;
}
else
{
for (const auto& timeSpan : GetTimeSpans())
{
if (pSystem != nullptr)
{
for (const auto& Affinity : timeSpan.GetAffinityAssignments())
{
if (Affinity.wGroup >= pSystem->processorTopology._vProcessorGroupInformation.size())
{
fprintf(stderr, "ERROR: affinity assignment to group %u; system only has %u groups\n",
Affinity.wGroup,
(int) pSystem->processorTopology._vProcessorGroupInformation.size());
fOk = false;
}
if (fOk && !pSystem->processorTopology._vProcessorGroupInformation[Affinity.wGroup].IsProcessorValid(Affinity.bProc))
{
fprintf(stderr, "ERROR: affinity assignment to group %u core %u not possible; group only has %u cores\n",
Affinity.wGroup,
Affinity.bProc,
pSystem->processorTopology._vProcessorGroupInformation[Affinity.wGroup]._maximumProcessorCount);
fOk = false;
}
if (fOk && !pSystem->processorTopology._vProcessorGroupInformation[Affinity.wGroup].IsProcessorActive(Affinity.bProc))
{
fprintf(stderr, "ERROR: affinity assignment to group %u core %u not possible; core is not active (current mask 0x%Ix)\n",
Affinity.wGroup,
Affinity.bProc,
pSystem->processorTopology._vProcessorGroupInformation[Affinity.wGroup]._activeProcessorMask);
fOk = false;
}
}
}
if (timeSpan.GetDisableAffinity() && timeSpan.GetAffinityAssignments().size() > 0)
{
fprintf(stderr, "ERROR: -n and -a parameters cannot be used together\n");
fOk = false;
}
for (const auto& target : timeSpan.GetTargets())
{
const bool targetHasMultipleThreads = (timeSpan.GetThreadCount() > 1) || (target.GetThreadsPerFile() > 1);
if (timeSpan.GetThreadCount() > 0 && target.GetThreadsPerFile() > 1)
{
fprintf(stderr, "ERROR: -F and -t parameters cannot be used together\n");
fOk = false;
}
if (target.GetThroughputInBytesPerMillisecond() > 0 && timeSpan.GetCompletionRoutines())
{
fprintf(stderr, "ERROR: -g throughput control cannot be used with -x completion routines\n");
fOk = false;
}
// If burst size is specified think time must be specified and If think time is specified burst size should be non zero
if ((target.GetThinkTime() == 0 && target.GetBurstSize() > 0) || (target.GetThinkTime() > 0 && target.GetBurstSize() == 0))
{
fprintf(stderr, "ERROR: need to specify -j<think time> with -i<burst size>\n");
fOk = false;
}
if (timeSpan.GetThreadCount() > 0 && timeSpan.GetRequestCount() > 0)
{
if (target.GetThroughputInBytesPerMillisecond() > 0)
{
fprintf(stderr, "ERROR: -g throughput control cannot be used with -O outstanding requests per thread\n");
fOk = false;
}
if (target.GetThinkTime() > 0)
{
fprintf(stderr, "ERROR: -j think time cannot be used with -O outstanding requests per thread\n");
fOk = false;
}
if (target.GetUseParallelAsyncIO())
{
fprintf(stderr, "ERROR: -p parallel IO cannot be used with -O outstanding requests per thread\n");
fOk = false;
}
if (target.GetWeight() == 0)
{
fprintf(stderr, "ERROR: a non-zero target Weight must be specified\n");
fOk = false;
}
for (const auto& threadTarget : target.GetThreadTargets())
{
if (threadTarget.GetThread() >= timeSpan.GetThreadCount())
{
fprintf(stderr, "ERROR: illegal thread specified for ThreadTarget\n");
fOk = false;
}
}
}
else if (target.GetThreadTargets().size() != 0)
{
fprintf(stderr, "ERROR: ThreadTargets can only be specified when the timespan ThreadCount and RequestCount are specified\n");
fOk = false;
}
if (target.GetRandomRatio())
{
if (target.GetThreadStrideInBytes() > 0)
{
fprintf(stderr, "ERROR: -T conflicts with -r\n");
fOk = false;
// although ullThreadStride==0 is a valid value, it's interpreted as "not provided" for this warning
}
if (target.GetUseInterlockedSequential())
{
fprintf(stderr, "ERROR: -si conflicts with -r\n");
fOk = false;
}
if (target.GetUseParallelAsyncIO())
{
fprintf(stderr, "ERROR: -p conflicts with -r\n");
fOk = false;
}
}
else
{
if (target.GetDistributionRange().size() != 0)
{
fprintf(stderr, "ERROR: random distribution ranges (-rd) do not apply to sequential-only IO patterns\n");
fOk = false;
}
if (target.GetUseParallelAsyncIO() && target.GetRequestCount() == 1)
{
fprintf(stderr, "WARNING: -p does not have effect unless outstanding I/O count (-o) is > 1\n");
}
if (target.GetUseInterlockedSequential())
{
if (target.GetThreadStrideInBytes() > 0)
{
fprintf(stderr, "ERROR: -si conflicts with -T\n");
fOk = false;
}
if (target.GetUseParallelAsyncIO())
{
fprintf(stderr, "ERROR: -si conflicts with -p\n");
fOk = false;
}
if (!targetHasMultipleThreads)
{
fprintf(stderr, "WARNING: single-threaded test, -si ignored\n");
}
}
else
{
if (targetHasMultipleThreads && !target.GetThreadStrideInBytes())
{
fprintf(stderr, "WARNING: target access pattern will not be sequential, consider -si\n");
}
if (!targetHasMultipleThreads && target.GetThreadStrideInBytes())
{
fprintf(stderr, "ERROR: -T has no effect unless multiple threads per target are used\n");
fOk = false;
}
}
}
// Distribution ranges are only applied to random loads. Note validation failure in the sequential case.
// TBD this should be moved to a proper Distribution class.
{
UINT32 ioAcc = 0;
UINT64 targetAcc = 0;
bool absZero = false, absZeroLast = false;
for (const auto& r : target.GetDistributionRange())
{
if (target.GetDistributionType() == DistributionType::Absolute)
{
// allow zero target span in last position
absZeroLast = false;
if (r._dst.second == 0 && !absZero)
{
// legal in last position
absZero = absZeroLast = true;
}
else if (r._dst.second < target.GetBlockSizeInBytes())
{
fprintf(stderr, "ERROR: invalid random distribution target range %I64u - must be a minimum of the specified block size (%u bytes)\n", r._dst.second, target.GetBlockSizeInBytes());
fOk = false;
break;
}
}
// Validate accumulating IO%
if (ioAcc + r._span > 100)
{
fprintf(stderr, "ERROR: invalid random distribution IO%% %u: can be at most %u - total must be <= 100%%\n", r._span, 100 - ioAcc);
fOk = false;
break;
}
// Validate accumulating Target%
// Note that absolute range needs no additional validation - known nonzero/large enough for IO
if (target.GetDistributionType() == DistributionType::Percent)
{
if (targetAcc + r._dst.second > 100)
{
fprintf(stderr, "ERROR: invalid random distribution Target%% %I64u: can be at most %I64u - total must be <= 100%%\n", r._dst.second, 100 - targetAcc);
fOk = false;
break;
}
// Consuming the target before consuming the IO is invalid.
// No holes in IO.
if (targetAcc + r._dst.second == 100 && ioAcc + r._span < 100)
{
fprintf(stderr, "ERROR: invalid random distribution: the target is covered with %u%% IO left to distribute\n", 100 - (ioAcc + r._span));
fOk = false;
break;
}
}
ioAcc += r._span;
targetAcc += r._dst.second;
}
// Percent dist Target% must sum to 100%. IO% underflow (either due to early Target% 100% or Target% overflow) is handled above.
if (target.GetDistributionType() == DistributionType::Percent &&
targetAcc != 100)
{
fprintf(stderr, "ERROR: invalid random distribution span: Target%% (%I64u%%) must total 100%%\n", targetAcc);
fOk = false;
}
if (absZero && !absZeroLast)
{
fprintf(stderr, "ERROR: invalid zero target range in random distribution - must be a minimum of the specified block size (%u bytes)\n", target.GetBlockSizeInBytes());
fOk = false;
}
}
if (target.GetRandomDataWriteBufferSize() > 0)
{
if (target.GetRandomDataWriteBufferSize() < target.GetBlockSizeInBytes())
{
fprintf(stderr, "ERROR: custom write buffer (-Z) is smaller than the block size. Write buffer size: %I64u block size: %u\n",
target.GetRandomDataWriteBufferSize(),
target.GetBlockSizeInBytes());
fOk = false;
}
}
if (target.GetMemoryMappedIoMode() == MemoryMappedIoMode::On)
{
if (timeSpan.GetCompletionRoutines())
{
fprintf(stderr, "ERROR: completion routines (-x) can't be used with memory mapped IO (-Sm)\n");
fOk = false;
}
if (target.GetCacheMode() == TargetCacheMode::DisableOSCache)
{
fprintf(stderr, "ERROR: unbuffered IO (-Su or -Sh) can't be used with memory mapped IO (-Sm)\n");
fOk = false;
}
}
if (target.GetMemoryMappedIoMode() == MemoryMappedIoMode::Off &&
target.GetMemoryMappedIoFlushMode() != MemoryMappedIoFlushMode::Undefined)
{
fprintf(stderr, "ERROR: memory mapped flush mode (-N) can only be specified with memory mapped IO (-Sm)\n");
fOk = false;
}
if (GetProfileOnly() == false)
{
auto sPath = target.GetPath();
if (sPath[0] == TEMPLATE_TARGET_PREFIX)
{
fprintf(stderr, "ERROR: template target '%s' was not substituted - all template targets must be substituted to run a profile\n", sPath.c_str());
fOk = false;
}
}
// in the cases where there is only a single configuration specified for each target (e.g., cmdline),
// currently there are no validations specific to individual targets (e.g., pre-existing files)
// so we can stop validation now. this allows us to only warn/error once, as opposed to repeating
// it for each target.
if (fSingleSpec)
{
break;
}
}
}
}
return fOk;
}