bool Profile::Validate()

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;
}