in CmdLineParser/CmdLineParser.cpp [718:1909]
bool CmdLineParser::_ReadParametersFromCmdLine(const int argc, const char *argv[], Profile *pProfile, struct Synchronization *synch, bool& fXMLProfile)
{
int nParamCnt = argc - 1;
const char** args = argv + 1;
bool fError = false;
TimeSpan timeSpan;
//
// Pass 1 - determine parameter set type: cmdline specification or XML, and preparse targets/blocksize
//
ParseState isXMLSet = ParseState::Unknown;
ParseState isXMLResultFormat = ParseState::Unknown;
ParseState isProfileOnly = ParseState::Unknown;
ParseState isVerbose = ParseState::Unknown;
ParseState isRandomSeed = ParseState::Unknown;
ParseState isWarmupTime = ParseState::Unknown;
ParseState isDurationTime = ParseState::Unknown;
ParseState isCooldownTime = ParseState::Unknown;
ULONG randomSeedValue = 0;
ULONG warmupTime = 0;
ULONG durationTime = 0;
ULONG cooldownTime = 0;
const char *xmlProfile = nullptr;
//
// Find all target specifications. Note that this assumes all non-target
// parameters are single tokens; e.g. "-Hsomevalue" and never "-H somevalue".
// Targets follow parameter specifications.
//
vector<Target> vTargets;
for (int i = 0, inTargets = false; i < nParamCnt; ++i)
{
if (!_IsSwitchChar(args[i][0]))
{
inTargets = true;
Target target;
target.SetPath(args[i]);
vTargets.push_back(target);
}
else if (inTargets)
{
fprintf(stderr, "ERROR: parameters (%s) must come before targets on the command line\n", args[i]);
return false;
}
}
//
// Find composable and dependent parameters as we resolve the parameter set.
//
for (int i = 0; i < nParamCnt; ++i)
{
if (_IsSwitchChar(args[i][0]))
{
const char *arg = &args[i][2];
switch(args[i][1])
{
case 'b':
// Block size does not compose with XML profile spec
if (isXMLSet == ParseState::True)
{
fprintf(stderr, "ERROR: -b is not compatible with -X XML profile specification\n");
return false;
}
else
{
UINT64 ullBlockSize;
if (_GetSizeInBytes(arg, ullBlockSize, nullptr) && ullBlockSize < MAXUINT32)
{
for (auto i = vTargets.begin(); i != vTargets.end(); i++)
{
i->SetBlockSizeInBytes((DWORD)ullBlockSize);
}
}
else
{
fprintf(stderr, "ERROR: invalid block size passed to -b\n");
return false;
}
_dwBlockSize = (DWORD)ullBlockSize;
isXMLSet = ParseState::False;
}
break;
case 'C':
{
int c = atoi(arg);
if (c >= 0)
{
cooldownTime = c;
isCooldownTime = ParseState::True;
}
else
{
fprintf(stderr, "ERROR: invalid cooldown time (-C): '%s'\n", arg);
return false;
}
}
break;
case 'd':
{
int c = atoi(arg);
if (c >= 0)
{
durationTime = c;
isDurationTime = ParseState::True;
}
else
{
fprintf(stderr, "ERROR: invalid measured duration time (-d): '%s'\n", arg);
return false;
}
}
break;
case 'W':
{
int c = atoi(arg);
if (c >= 0)
{
warmupTime = c;
isWarmupTime = ParseState::True;
}
else
{
fprintf(stderr, "ERROR: invalid warmup time (-W): '%s'\n", arg);
return false;
}
}
break;
case 'R':
// re-output profile only (no run)
if ('p' == *arg)
{
isProfileOnly = ParseState::True;
++arg;
}
if ('\0' != *arg)
{
// Explicit results format
if (strcmp(arg, "xml") == 0)
{
isXMLResultFormat = ParseState::True;
}
else if (strcmp(arg, "text") != 0)
{
fprintf(stderr, "ERROR: invalid results format (-R): '%s'\n", arg);
return false;
}
else
{
isXMLResultFormat = ParseState::False;
}
}
else
{
// allow for -Rp shorthand for default profile-only format
if (isProfileOnly != ParseState::True)
{
fprintf(stderr, "ERROR: unspecified results format -R: use [p]<text|xml>\n");
return false;
}
}
break;
case 'v':
isVerbose = ParseState::True;
break;
case 'X':
if (isXMLSet == ParseState::Unknown)
{
isXMLSet = ParseState::True;
}
else
{
fprintf(stderr, "ERROR: multiple XML profiles specified (-X)\n");
return false;
}
xmlProfile = arg;
break;
case 'z':
{
char *endPtr = nullptr;
if (*arg == '\0')
{
randomSeedValue = (ULONG) GetTickCount64();
}
else
{
randomSeedValue = strtoul(arg, &endPtr, 10);
if (*endPtr != '\0')
{
fprintf(stderr, "ERROR: invalid random seed value '%s' specified - must be a valid 32 bit integer\n", arg);
return false;
}
}
isRandomSeed = ParseState::True;
}
break;
default:
// no other switches are valid in combination with -X
// if we've seen X, this means it is bad
// if not, we know it will not be X
if (isXMLSet == ParseState::True)
{
fprintf(stderr, "ERROR: invalid XML profile specification; parameter %s not compatible with -X\n", args[i]);
return false;
}
else
{
isXMLSet = ParseState::False;
}
}
}
}
// XML profile?
if (isXMLSet == ParseState::True)
{
if (!_ReadParametersFromXmlFile(xmlProfile, pProfile, &vTargets))
{
return false;
}
}
//
// Apply profile common parameters - note that results format is unmodified if R not explicitly provided
//
if (isXMLResultFormat == ParseState::True)
{
pProfile->SetResultsFormat(ResultsFormat::Xml);
}
else if (isXMLResultFormat == ParseState::False)
{
pProfile->SetResultsFormat(ResultsFormat::Text);
}
if (isProfileOnly == ParseState::True)
{
pProfile->SetProfileOnly(true);
}
if (isVerbose == ParseState::True)
{
pProfile->SetVerbose(true);
}
//
// Apply timespan common composable parameters
//
if (isXMLSet == ParseState::True)
{
for (auto& ts : const_cast<vector<TimeSpan> &>(pProfile->GetTimeSpans()))
{
if (isRandomSeed == ParseState::True) { ts.SetRandSeed(randomSeedValue); }
if (isWarmupTime == ParseState::True) { ts.SetWarmup(warmupTime); }
if (isDurationTime == ParseState::True) { ts.SetDuration(durationTime); }
if (isCooldownTime == ParseState::True) { ts.SetCooldown(cooldownTime); }
}
}
else
{
if (isRandomSeed == ParseState::True) { timeSpan.SetRandSeed(randomSeedValue); }
if (isWarmupTime == ParseState::True) { timeSpan.SetWarmup(warmupTime); }
if (isDurationTime == ParseState::True) { timeSpan.SetDuration(durationTime); }
if (isCooldownTime == ParseState::True) { timeSpan.SetCooldown(cooldownTime); }
}
// Now done if XML profile
if (isXMLSet == ParseState::True)
{
fXMLProfile = true;
return true;
}
//
// Parse full command line for profile
//
// initial parse for cache/writethrough
// these are built up across the entire cmd line and applied at the end.
// this allows for conflicts to be thrown for mixed -h/-S as needed.
TargetCacheMode t = TargetCacheMode::Undefined;
WriteThroughMode w = WriteThroughMode::Undefined;
MemoryMappedIoMode m = MemoryMappedIoMode::Undefined;
MemoryMappedIoFlushMode f = MemoryMappedIoFlushMode::Undefined;
bool bExit = false;
while (nParamCnt)
{
const char* arg = *args;
const char* const carg = arg; // save for error reporting, arg is modified during parse
// Targets follow parameters on command line. If this is a target, we are done now.
if (!_IsSwitchChar(*arg))
{
break;
}
// skip switch character, provide length
++arg;
const size_t argLen = strlen(arg);
switch (*arg)
{
case '?':
_DisplayUsageInfo(argv[0]);
exit(0);
case 'a': //affinity
//-a1,2,3,4 (assign threads to cpus 1,2,3,4 (round robin))
if (!_ParseAffinity(arg, &timeSpan))
{
fError = true;
}
break;
case 'b': //block size
// nop - block size has been taken care of before the loop
break;
case 'B': //base file offset (offset from the beginning of the file)
if (*(arg + 1) != '\0')
{
UINT64 cb;
if (_GetSizeInBytes(arg + 1, cb, nullptr))
{
for (auto i = vTargets.begin(); i != vTargets.end(); i++)
{
i->SetBaseFileOffsetInBytes(cb);
}
}
else
{
fprintf(stderr, "ERROR: invalid base file offset passed to -B\n");
fError = true;
}
}
else
{
fError = true;
}
break;
case 'c': //create file of the given size
if (*(arg + 1) != '\0')
{
UINT64 cb;
if (_GetSizeInBytes(arg + 1, cb, nullptr))
{
for (auto i = vTargets.begin(); i != vTargets.end(); i++)
{
i->SetFileSize(cb);
i->SetCreateFile(true);
}
}
else
{
fprintf(stderr, "ERROR: invalid file size passed to -c\n");
fError = true;
}
}
else
{
fError = true;
}
break;
case 'C': //cool down time - pass 1 composable
break;
case 'd': //duration - pass 1 composable
break;
case 'D': //standard deviation
{
timeSpan.SetCalculateIopsStdDev(true);
int x = atoi(arg + 1);
if (x > 0)
{
timeSpan.SetIoBucketDurationInMilliseconds(x);
}
}
break;
case 'e': //etw
if (!_ParseETWParameter(arg, pProfile))
{
fError = true;
}
break;
case 'f':
if (isdigit(*(arg + 1)))
{
UINT64 cb;
if (_GetSizeInBytes(arg + 1, cb, nullptr))
{
for (auto i = vTargets.begin(); i != vTargets.end(); i++)
{
i->SetMaxFileSize(cb);
}
}
else
{
fprintf(stderr, "ERROR: invalid max file size passed to -f\n");
fError = true;
}
}
else
{
if ('\0' == *(arg + 1))
{
fError = true;
}
else
{
// while -frs (or -fsr) are generally conflicting intentions as far as
// the OS is concerned, do not enforce
while (*(++arg) != '\0')
{
switch (*arg)
{
case 'r':
for (auto i = vTargets.begin(); i != vTargets.end(); i++)
{
i->SetRandomAccessHint(true);
}
break;
case 's':
for (auto i = vTargets.begin(); i != vTargets.end(); i++)
{
i->SetSequentialScanHint(true);
}
break;
case 't':
for (auto i = vTargets.begin(); i != vTargets.end(); i++)
{
i->SetTemporaryFileHint(true);
}
break;
default:
fError = true;
break;
}
}
}
}
break;
case 'F': //total number of threads
{
int c = atoi(arg + 1);
if (c > 0)
{
timeSpan.SetThreadCount(c);
}
else
{
fError = true;
}
}
break;
case 'g': //throughput in bytes per millisecond (gNNN) OR iops (gNNNi)
{
// units?
bool isBpms = false;
if (isdigit(arg[argLen - 1]))
{
isBpms = true;
}
else if (arg[argLen - 1] != 'i')
{
// not IOPS, so its bad
fError = true;
}
if (!fError)
{
int c = atoi(arg + 1);
if (c > 0)
{
for (auto i = vTargets.begin(); i != vTargets.end(); i++)
{
if (isBpms)
{
i->SetThroughput(c);
}
else
{
i->SetThroughputIOPS(c);
}
}
}
else
{
fError = true;
}
}
}
break;
case 'h': // compat: disable os cache and set writethrough; now equivalent to -Sh
if (t == TargetCacheMode::Undefined &&
w == WriteThroughMode::Undefined)
{
t = TargetCacheMode::DisableOSCache;
w = WriteThroughMode::On;
}
else
{
fprintf(stderr, "ERROR: -h conflicts with earlier specification of cache/writethrough\n");
fError = true;
}
break;
case 'i': //number of IOs to issue before think time
{
int c = atoi(arg + 1);
if (c > 0)
{
for (auto i = vTargets.begin(); i != vTargets.end(); i++)
{
i->SetBurstSize(c);
i->SetUseBurstSize(true);
}
}
else
{
fError = true;
}
}
break;
case 'j': //time to wait between bursts of IOs
{
int c = atoi(arg + 1);
if (c > 0)
{
for (auto i = vTargets.begin(); i != vTargets.end(); i++)
{
i->SetThinkTime(c);
i->SetEnableThinkTime(true);
}
}
else
{
fError = true;
}
}
break;
case 'I': //io priority
{
int x = atoi(arg + 1);
if (x > 0 && x < 4)
{
PRIORITY_HINT hint[] = { IoPriorityHintVeryLow, IoPriorityHintLow, IoPriorityHintNormal };
for (auto i = vTargets.begin(); i != vTargets.end(); i++)
{
i->SetIOPriorityHint(hint[x - 1]);
}
}
else
{
fError = true;
}
}
break;
case 'l': //large pages
for (auto i = vTargets.begin(); i != vTargets.end(); i++)
{
i->SetUseLargePages(true);
}
break;
case 'L': //measure latency
timeSpan.SetMeasureLatency(true);
break;
case 'n': //disable affinity (by default simple affinity is turned on)
timeSpan.SetDisableAffinity(true);
break;
case 'N':
if (!_ParseFlushParameter(arg, &f))
{
fError = true;
}
break;
case 'o': //request count (1==synchronous)
{
int c = atoi(arg + 1);
if (c > 0)
{
for (auto i = vTargets.begin(); i != vTargets.end(); i++)
{
i->SetRequestCount(c);
}
}
else
{
fError = true;
}
}
break;
case 'O': //total number of IOs/thread - for use with -F
{
int c = atoi(arg + 1);
if (c > 0)
{
timeSpan.SetRequestCount(c);
}
else
{
fError = true;
}
}
break;
case 'p': //start async IO operations with the same offset
//makes sense only for -o2 and greater
for (auto i = vTargets.begin(); i != vTargets.end(); i++)
{
i->SetUseParallelAsyncIO(true);
}
break;
case 'P': //show progress every x IO operations
{
int c = atoi(arg + 1);
if (c < 1)
{
c = 65536;
}
pProfile->SetProgress(c);
}
break;
case 'r': //random access
{
// mixed random/sequential pct split?
if (*(arg + 1) == 's')
{
int c = 0;
++arg;
if (*(arg + 1) == '\0')
{
fprintf(stderr, "ERROR: no random percentage passed to -rs\n");
fError = true;
}
else
{
c = atoi(arg + 1);
if (c <= 0 || c > 100)
{
fprintf(stderr, "ERROR: random percentage passed to -rs should be between 1 and 100\n");
fError = true;
}
}
if (!fError)
{
for (auto i = vTargets.begin(); i != vTargets.end(); i++)
{
// if random ratio is unset and actual alignment is already specified,
// -s was used: don't allow this for clarity of intent
if (!i->GetRandomRatio() &&
i->GetBlockAlignmentInBytes(true))
{
fprintf(stderr, "ERROR: use -r to specify IO alignment when using mixed random/sequential IO (-rs)\n");
fError = true;
break;
}
// if random ratio was already set to something other than 100% (-r)
// then -rs was specified multiple times: catch and block this
if (i->GetRandomRatio() &&
i->GetRandomRatio() != 100)
{
fprintf(stderr, "ERROR: mixed random/sequential IO (-rs) specified multiple times\n");
fError = true;
break;
}
// Note that -rs100 is the same as -r. It will not result in the <RandomRatio> element
// in the XML profile; we will still only emit/accept 1-99 there.
//
// Saying -rs0 (sequential) would create an ambiguity between that and -r[nnn]. Rather
// than bend the intepretation of -r[nnn] for the special case of -rs0 we will error
// it out in the bounds check above.
i->SetRandomRatio(c);
}
}
}
// random distribution
else if (*(arg + 1) == 'd')
{
// advance past the d
arg += 2;
fError = !_ParseRandomDistribution(arg, vTargets);
}
// random block alignment
// if mixed random/sequential not already specified, set to 100%
else
{
UINT64 cb = _dwBlockSize;
if (*(arg + 1) != '\0')
{
if (!_GetSizeInBytes(arg + 1, cb, nullptr) || (cb == 0))
{
fprintf(stderr, "ERROR: invalid alignment passed to -r\n");
fError = true;
}
}
if (!fError)
{
for (auto i = vTargets.begin(); i != vTargets.end(); i++)
{
// Do not override -rs specification
if (!i->GetRandomRatio())
{
i->SetRandomRatio(100);
}
// Multiple -rNN?
// Note that -rs100 -r[NN] will pass since -rs does not set alignment.
// We are only validating a single -rNN specification.
else if (i->GetRandomRatio() == 100 &&
i->GetBlockAlignmentInBytes(true))
{
fprintf(stderr, "ERROR: random IO (-r) specified multiple times\n");
fError = true;
break;
}
// -s already set the alignment?
if (i->GetBlockAlignmentInBytes(true))
{
fprintf(stderr, "ERROR: sequential IO (-s) conflicts with random IO (-r/-rs)\n");
fError = true;
break;
}
i->SetBlockAlignmentInBytes(cb);
}
}
}
}
break;
case 'R': // output profile/results format engine - handled in pass 1
break;
case 's': //stride size
{
int idx = 1;
if ('i' == *(arg + idx))
{
// do interlocked sequential mode
// ISSUE-REVIEW: this does nothing if -r is specified
// ISSUE-REVIEW: this does nothing if -p is specified
// ISSUE-REVIEW: this does nothing if we are single-threaded
for (auto i = vTargets.begin(); i != vTargets.end(); i++)
{
i->SetUseInterlockedSequential(true);
}
idx++;
}
for (auto i = vTargets.begin(); i != vTargets.end(); i++)
{
// conflict -s with -rs/-s
if (i->GetRandomRatio())
{
if (i->GetRandomRatio() == 100) {
fprintf(stderr, "ERROR: sequential IO (-s) conflicts with random IO (-r/-rs)\n");
}
else
{
fprintf(stderr, "ERROR: use -r to specify IO alignment for -rs\n");
}
fError = true;
break;
}
// conflict with multiple -s
if (i->GetBlockAlignmentInBytes(true))
{
fprintf(stderr, "ERROR: sequential IO (-s) specified multiple times\n");
fError = true;
break;
}
}
if (*(arg + idx) != '\0')
{
UINT64 cb;
// Note that we allow -s0, as unusual as that would be.
// The counter-case of -r0 is invalid and checked for.
if (_GetSizeInBytes(arg + idx, cb, nullptr))
{
for (auto i = vTargets.begin(); i != vTargets.end(); i++)
{
i->SetBlockAlignmentInBytes(cb);
}
}
else
{
fprintf(stderr, "ERROR: invalid stride size passed to -s\n");
fError = true;
}
}
else
{
// explicitly pass through the block size so that we can detect
// -rs/-s intent conflicts when attempting to set -rs
for (auto i = vTargets.begin(); i != vTargets.end(); i++)
{
i->SetBlockAlignmentInBytes(i->GetBlockSizeInBytes());
}
}
}
break;
case 'S': //control os/hw/remote caching and writethrough
{
// parse flags - it is an error to multiply specify either property, which
// can be detected simply by checking if we move one from !undefined.
// this also handles conflict cases.
int idx;
for (idx = 1; !fError && *(arg + idx) != '\0'; idx++)
{
switch (*(arg + idx))
{
case 'b':
if (t == TargetCacheMode::Undefined)
{
t = TargetCacheMode::Cached;
}
else
{
fprintf(stderr, "ERROR: -Sb conflicts with earlier specification of cache mode\n");
fError = true;
}
break;
case 'h':
if (t == TargetCacheMode::Undefined &&
w == WriteThroughMode::Undefined &&
m == MemoryMappedIoMode::Undefined)
{
t = TargetCacheMode::DisableOSCache;
w = WriteThroughMode::On;
}
else
{
fprintf(stderr, "ERROR: -Sh conflicts with earlier specification of cache/writethrough/memory mapped\n");
fError = true;
}
break;
case 'm':
if (m == MemoryMappedIoMode::Undefined &&
t != TargetCacheMode::DisableOSCache)
{
m = MemoryMappedIoMode::On;
}
else
{
fprintf(stderr, "ERROR: -Sm conflicts with earlier specification of memory mapped IO/unbuffered IO\n");
fError = true;
}
break;
case 'r':
if (t == TargetCacheMode::Undefined)
{
t = TargetCacheMode::DisableLocalCache;
}
else
{
fprintf(stderr, "ERROR: -Sr conflicts with earlier specification of cache mode\n");
fError = true;
}
break;
case 'u':
if (t == TargetCacheMode::Undefined &&
m == MemoryMappedIoMode::Undefined)
{
t = TargetCacheMode::DisableOSCache;
}
else
{
fprintf(stderr, "ERROR: -Su conflicts with earlier specification of cache mode/memory mapped IO\n");
fError = true;
}
break;
case 'w':
if (w == WriteThroughMode::Undefined)
{
w = WriteThroughMode::On;
}
else
{
fprintf(stderr, "ERROR -Sw conflicts with earlier specification of write through\n");
fError = true;
}
break;
default:
fprintf(stderr, "ERROR: unrecognized option provided to -S\n");
fError = true;
break;
}
}
// bare -S, parse loop did not advance
if (!fError && idx == 1)
{
if (t == TargetCacheMode::Undefined &&
m == MemoryMappedIoMode::Undefined)
{
t = TargetCacheMode::DisableOSCache;
}
else
{
fprintf(stderr, "ERROR: -S conflicts with earlier specification of cache mode\n");
fError = true;
}
}
}
break;
case 't': //number of threads per file
{
int c = atoi(arg + 1);
if (c > 0)
{
for (auto i = vTargets.begin(); i != vTargets.end(); i++)
{
i->SetThreadsPerFile(c);
}
}
else
{
fError = true;
}
}
break;
case 'T': //offsets between threads reading the same file
{
UINT64 cb;
if (_GetSizeInBytes(arg + 1, cb, nullptr) && (cb > 0))
{
for (auto i = vTargets.begin(); i != vTargets.end(); i++)
{
i->SetThreadStrideInBytes(cb);
}
}
else
{
fprintf(stderr, "ERROR: invalid offset passed to -T\n");
fError = true;
}
}
break;
case 'v': //verbose mode - handled in pass 1
break;
case 'w': //write test [default=read]
{
int c = 0;
if (*(arg + 1) == '\0')
{
fprintf(stderr, "ERROR: no write ratio passed to -w\n");
fError = true;
}
else
{
c = atoi(arg + 1);
if (c < 0 || c > 100)
{
fprintf(stderr, "ERROR: write ratio passed to -w must be between 0 and 100 (percent)\n");
fError = true;
}
}
if (!fError)
{
for (auto i = vTargets.begin(); i != vTargets.end(); i++)
{
i->SetWriteRatio(c);
}
}
}
break;
case 'W': //warm up time - pass 1 composable
break;
case 'x': //completion routines
timeSpan.SetCompletionRoutines(true);
break;
case 'y': //external synchronization
switch (*(arg + 1))
{
case 's':
_hEventStarted = CreateEvent(NULL, TRUE, FALSE, arg + 2);
if (NULL == _hEventStarted)
{
fprintf(stderr, "Error creating/opening start notification event: '%s'\n", arg + 2);
exit(1); // TODO: this class shouldn't terminate the process
}
break;
case 'f':
_hEventFinished = CreateEvent(NULL, TRUE, FALSE, arg + 2);
if (NULL == _hEventFinished)
{
fprintf(stderr, "Error creating/opening finish notification event: '%s'\n", arg + 2);
exit(1); // TODO: this class shouldn't terminate the process
}
break;
case 'r':
synch->hStartEvent = CreateEvent(NULL, TRUE, FALSE, arg + 2);
if (NULL == synch->hStartEvent)
{
fprintf(stderr, "Error creating/opening wait-for-start event: '%s'\n", arg + 2);
exit(1); // TODO: this class shouldn't terminate the process
}
break;
case 'p':
synch->hStopEvent = CreateEvent(NULL, TRUE, FALSE, arg + 2);
if (NULL == synch->hStopEvent)
{
fprintf(stderr, "Error creating/opening force-stop event: '%s'\n", arg + 2);
exit(1); // TODO: this class shouldn't terminate the process
}
break;
case 'e':
{
HANDLE hEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, arg + 2);
if (NULL == hEvent)
{
fprintf(stderr, "Error opening event '%s'\n", arg + 2);
exit(1); // TODO: this class shouldn't terminate the process
}
if (!SetEvent(hEvent))
{
fprintf(stderr, "Error setting event '%s'\n", arg + 2);
exit(1); // TODO: this class shouldn't terminate the process
}
CloseHandle(hEvent);
printf("Succesfully set event: '%s'\n", arg + 2);
bExit = true;
break;
}
default:
fError = true;
}
case 'z': //random seed - pass 1 composable
break;
case 'Z': //zero write buffers
if (*(arg + 1) == '\0')
{
for (auto i = vTargets.begin(); i != vTargets.end(); i++)
{
i->SetZeroWriteBuffers(true);
}
}
else if (*(arg + 1) == 'r' && *(arg + 2) == '\0')
{
timeSpan.SetRandomWriteData(true);
}
else
{
UINT64 cb = 0;
string sPath;
if (_GetRandomDataWriteBufferData(string(arg + 1), cb, sPath) && (cb > 0))
{
for (auto i = vTargets.begin(); i != vTargets.end(); i++)
{
i->SetRandomDataWriteBufferSize(cb);
i->SetRandomDataWriteBufferSourcePath(sPath);
}
}
else
{
fprintf(stderr, "ERROR: invalid size passed to -Z\n");
fError = true;
}
}
break;
default:
fprintf(stderr, "ERROR: invalid option: '%s'\n", carg);
return false;
}
if (fError)
{
// note: original pointer to the cmdline argument, without parse movement
fprintf(stderr, "ERROR: incorrectly provided option: '%s'\n", carg);
return false;
}
--nParamCnt;
++args;
}
//
// exit if a user specified an action which was already satisfied and doesn't require running test
//
if (bExit)
{
printf("Now exiting...\n");
exit(1); // TODO: this class shouldn't terminate the process
}
if (vTargets.size() < 1)
{
fprintf(stderr, "ERROR: need to provide at least one filename\n");
return false;
}
// apply resultant cache/writethrough/memory mapped io modes to the targets
for (auto i = vTargets.begin(); i != vTargets.end(); i++)
{
if (t != TargetCacheMode::Undefined)
{
i->SetCacheMode(t);
}
if (w != WriteThroughMode::Undefined)
{
i->SetWriteThroughMode(w);
}
if (m != MemoryMappedIoMode::Undefined)
{
i->SetMemoryMappedIoMode(m);
}
if (f != MemoryMappedIoFlushMode::Undefined)
{
i->SetMemoryMappedIoFlushMode(f);
}
}
// ... and apply targets to the timespan
for (auto i = vTargets.begin(); i != vTargets.end(); i++)
{
timeSpan.AddTarget(*i);
}
pProfile->AddTimeSpan(timeSpan);
return true;
}