in src/coreclr/tools/superpmi/superpmi/commandline.cpp [212:868]
bool CommandLine::Parse(int argc, char* argv[], /* OUT */ Options* o)
{
size_t argLen = 0;
size_t tempLen = 0;
bool foundJit = false;
bool foundFile = false;
if (argc == 1) // Print help when no args are passed
{
DumpHelp(argv[0]);
return false;
}
for (int i = 1; i < argc; i++)
{
bool isASwitch = (argv[i][0] == '-');
#ifndef TARGET_UNIX
if (argv[i][0] == '/') // Also accept "/" on Windows
{
isASwitch = true;
}
#endif // !TARGET_UNIX
// Process a switch
if (isASwitch)
{
argLen = strlen(argv[i]);
if (argLen > 1)
argLen--; // adjust for leading switch
else
{
DumpHelp(argv[0]);
return false;
}
if ((_strnicmp(&argv[i][1], "help", argLen) == 0) || (_strnicmp(&argv[i][1], "HELP", argLen) == 0) ||
(_strnicmp(&argv[i][1], "?", argLen) == 0))
{
DumpHelp(argv[0]);
return false;
}
else if ((_strnicmp(&argv[i][1], "load", argLen) == 0))
{
if (++i >= argc)
{
DumpHelp(argv[0]);
return false;
}
processMethodContext:
tempLen = strlen(argv[i]);
if (tempLen == 0)
{
LogError("Arg '%s' is invalid, name of file missing.", argv[i]);
DumpHelp(argv[0]);
return false;
}
o->nameOfInputMethodContextFile = new char[tempLen + 1];
strcpy_s(o->nameOfInputMethodContextFile, tempLen + 1, argv[i]);
foundFile = true;
}
else if ((_strnicmp(&argv[i][1], "jit", argLen) == 0))
{
if (++i >= argc)
{
DumpHelp(argv[0]);
return false;
}
processJit:
tempLen = strlen(argv[i]);
if (tempLen == 0)
{
LogError("Arg '%s' is invalid, name of jit missing.", argv[i]);
DumpHelp(argv[0]);
return false;
}
char* tempStr = new char[tempLen + 1];
strcpy_s(tempStr, tempLen + 1, argv[i]);
if (!foundJit)
{
o->nameOfJit = tempStr;
foundJit = true;
}
else
{
if (o->nameOfJit2 != nullptr)
{
LogError("Too many JITs specified.");
DumpHelp(argv[0]);
return false;
}
o->nameOfJit2 = tempStr;
}
}
else if ((_strnicmp(&argv[i][1], "reproName", argLen) == 0))
{
if (++i >= argc)
{
DumpHelp(argv[0]);
return false;
}
tempLen = strlen(argv[i]);
if (tempLen == 0)
{
LogError("Arg '%s' is invalid, name of prefix missing.", argv[i]);
DumpHelp(argv[0]);
return false;
}
char* tempStr = new char[tempLen + 1];
strcpy_s(tempStr, tempLen + 1, argv[i]);
o->reproName = tempStr;
}
else if ((_strnicmp(&argv[i][1], "failingMCList", argLen) == 0))
{
if (++i >= argc)
{
DumpHelp(argv[0]);
return false;
}
o->mclFilename = argv[i];
}
else if ((_strnicmp(&argv[i][1], "target", 6) == 0))
{
if (++i >= argc)
{
DumpHelp(argv[0]);
return false;
}
o->targetArchitecture = argv[i];
}
else if ((_strnicmp(&argv[i][1], "boe", 3) == 0))
{
o->breakOnError = true;
}
else if ((_strnicmp(&argv[i][1], "boa", 3) == 0))
{
o->breakOnAssert = true;
}
else if ((_strnicmp(&argv[i][1], "box", 3) == 0))
{
o->breakOnException = true;
}
else if ((_strnicmp(&argv[i][1], "ignoreStoredConfig", 18) == 0))
{
o->ignoreStoredConfig = true;
}
else if ((_strnicmp(&argv[i][1], "verbosity", argLen) == 0))
{
if (++i >= argc)
{
DumpHelp(argv[0]);
return false;
}
o->verbosity = argv[i];
Logger::SetLogLevel(Logger::ParseLogLevelString(o->verbosity));
}
else if ((_strnicmp(&argv[i][1], "writeLogFile", argLen) == 0))
{
if (++i >= argc)
{
DumpHelp(argv[0]);
return false;
}
o->writeLogFile = argv[i];
Logger::OpenLogFile(argv[i]);
}
else if ((_strnicmp(&argv[i][1], "emitMethodStats", argLen) == 0))
{
if (++i >= argc)
{
DumpHelp(argv[0]);
return false;
}
o->methodStatsTypes = argv[i];
}
else if ((_strnicmp(&argv[i][1], "details", argLen) == 0))
{
if (++i >= argc)
{
DumpHelp(argv[0]);
return false;
}
o->details = argv[i];
}
else if ((_strnicmp(&argv[i][1], "applyDiff", argLen) == 0))
{
o->applyDiff = true;
}
else if ((_strnicmp(&argv[i][1], "compile", argLen) == 0))
{
if (++i >= argc)
{
DumpHelp(argv[0]);
return false;
}
bool isValidList = MCList::processArgAsMCL(argv[i], &o->indexCount, &o->indexes);
if (!isValidList)
{
LogError("Arg '%s' is invalid, needed method context list.", argv[i]);
DumpHelp(argv[0]);
return false;
}
if (o->hash != nullptr)
{
LogError("Cannot use both method context list and method context hash.");
DumpHelp(argv[0]);
return false;
}
if (o->offset > 0 && o->increment > 0)
{
LogError("Cannot use method context list in parallel mode.");
DumpHelp(argv[0]);
return false;
}
o->compileList = argv[i]; // Save this in case we need it for -parallel.
}
else if ((_strnicmp(&argv[i][1], "coredistools", argLen) == 0))
{
#ifndef USE_COREDISTOOLS // If USE_COREDISTOOLS is not defined, then allow the switch, but ignore it.
o->useCoreDisTools = true;
#endif // USE_COREDISTOOLS
}
else if ((_strnicmp(&argv[i][1], "matchHash", argLen) == 0))
{
if (++i >= argc)
{
DumpHelp(argv[0]);
return false;
}
if (strlen(argv[i]) != (MM3_HASH_BUFFER_SIZE - 1))
{
LogError("Arg '%s' is invalid, needed a valid method context hash.", argv[i]);
DumpHelp(argv[0]);
return false;
}
if (o->indexCount > 0)
{
LogError("Cannot use both method context list and method context hash.");
DumpHelp(argv[0]);
return false;
}
if (o->offset > 0 && o->increment > 0)
{
LogError("Cannot use method context hash in parallel mode.");
DumpHelp(argv[0]);
return false;
}
o->hash = argv[i];
}
else if ((_strnicmp(&argv[i][1], "streaming", argLen) == 0))
{
if (++i >= argc)
{
LogError("'-streaming' must be followed by a file name or 'stdin'.");
DumpHelp(argv[0]);
return false;
}
o->streamFile = argv[i];
}
else if ((_strnicmp(&argv[i][1], "parallel", argLen) == 0))
{
o->parallel = true;
// Is there another argument?
if (i + 1 < argc)
{
// If so, does it look like a worker count?
bool isWorkerCount = true;
size_t nextlen = strlen(argv[i + 1]);
for (size_t j = 0; j < nextlen; j++)
{
if (!isdigit(argv[i + 1][j]))
{
isWorkerCount =
false; // Doesn't look like a worker count; bail out and let someone else handle it.
break;
}
}
if (isWorkerCount)
{
++i;
o->workerCount = atoi(argv[i]);
if (o->workerCount < 1)
{
LogError("Invalid workers count specified, workers count must be at least 1.");
DumpHelp(argv[0]);
return false;
}
if (o->workerCount > MAXIMUM_WAIT_OBJECTS)
{
LogError("Invalid workers count specified, workers count cannot be more than %d.",
MAXIMUM_WAIT_OBJECTS);
DumpHelp(argv[0]);
return false;
}
}
}
}
else if ((_strnicmp(&argv[i][1], "failureLimit", argLen) == 0))
{
if (++i >= argc)
{
DumpHelp(argv[0]);
return false;
}
o->failureLimit = atoi(argv[i]);
if (o->failureLimit < 1)
{
LogError(
"Incorrect limit specified for -failureLimit. Limit must be > 0.");
DumpHelp(argv[0]);
return false;
}
}
else if ((_stricmp(&argv[i][1], "skipCleanup") == 0))
{
o->skipCleanup = true;
}
else if ((_stricmp(&argv[i][1], "repeatCount") == 0))
{
if (++i >= argc)
{
DumpHelp(argv[0]);
return false;
}
bool isValidRepeatCount = true;
size_t nextlen = strlen(argv[i]);
for (size_t j = 0; j < nextlen; j++)
{
if (!isdigit(argv[i][j]))
{
isValidRepeatCount = false;
break;
}
}
if (isValidRepeatCount)
{
o->repeatCount = atoi(argv[i]);
if (o->repeatCount < 1)
{
isValidRepeatCount = false;
}
}
if (!isValidRepeatCount)
{
LogError("Invalid repeat count specified. Repeat count must be between 1 and INT_MAX.");
DumpHelp(argv[0]);
return false;
}
}
else if ((_strnicmp(&argv[i][1], "stride", argLen) == 0))
{
// "-stride" is an internal switch used by -parallel. Usage is:
//
// -stride offset increment
//
// It compiles methods in this series until end-of-file:
// offset, offset+increment, offset+2*increment, offset+3*increment, ...
if (++i >= argc)
{
DumpHelp(argv[0]);
return false;
}
o->offset = atoi(argv[i]);
if (++i >= argc)
{
DumpHelp(argv[0]);
return false;
}
o->increment = atoi(argv[i]);
if (o->offset < 1 || o->increment < 1)
{
LogError(
"Incorrect offset/increment specified for -stride. Offset and increment both must be > 0.");
DumpHelp(argv[0]);
return false;
}
if (o->indexCount > 0)
{
LogError("Cannot use method context list in parallel mode.");
DumpHelp(argv[0]);
return false;
}
if (o->hash != nullptr)
{
LogError("Cannot use method context hash in parallel mode.");
DumpHelp(argv[0]);
return false;
}
}
else if (_strnicmp(&argv[i][1], "jitoption", argLen) == 0)
{
i++;
if (!AddJitOption(i, argc, argv, &o->jitOptions, &o->forceJitOptions))
{
return false;
}
}
else if (_strnicmp(&argv[i][1], "jit2option", argLen) == 0)
{
i++;
if (!AddJitOption(i, argc, argv, &o->jit2Options, &o->forceJit2Options))
{
return false;
}
}
else
{
LogError("Unknown switch '%s' passed as argument.", argv[i]);
DumpHelp(argv[0]);
return false;
}
}
// Process an input filename
// String comparisons on file extensions must be case-insensitive since we run on Windows
else
{
char* lastdot = strrchr(argv[i], '.');
if (lastdot == nullptr)
{
DumpHelp(argv[0]);
return false;
}
if (_stricmp(lastdot, PLATFORM_SHARED_LIB_SUFFIX_A) == 0)
goto processJit;
else if (_stricmp(lastdot, ".mc") == 0)
goto processMethodContext;
else if (_stricmp(lastdot, ".mch") == 0)
goto processMethodContext;
else if (_stricmp(lastdot, ".mct") == 0)
goto processMethodContext;
else
{
LogError("Unknown file type passed as argument, '%s'.", argv[i]);
DumpHelp(argv[0]);
return false;
}
}
}
// Do some argument validation.
if (o->targetArchitecture != nullptr)
{
if ((0 != _stricmp(o->targetArchitecture, "amd64")) &&
(0 != _stricmp(o->targetArchitecture, "x64")) &&
(0 != _stricmp(o->targetArchitecture, "x86")) &&
(0 != _stricmp(o->targetArchitecture, "arm64")) &&
(0 != _stricmp(o->targetArchitecture, "arm")) &&
(0 != _stricmp(o->targetArchitecture, "arm32")))
{
LogError("Illegal target architecture specified with -target (use 'x64', 'x86', 'arm64', or 'arm').");
DumpHelp(argv[0]);
return false;
}
}
if (o->streamFile != nullptr)
{
if (o->parallel)
{
LogError("streaming mode and parallel mode are incompatible.");
return false;
}
if (o->nameOfJit2 != nullptr)
{
LogError("streaming mode and diff mode are incompatible.");
return false;
}
}
SPMI_TARGET_ARCHITECTURE defaultSpmiTargetArchitecture = GetSpmiTargetArchitecture();
SetSuperPmiTargetArchitecture(o->targetArchitecture);
if (o->nameOfInputMethodContextFile == nullptr)
{
LogError("Missing name of an input file.");
DumpHelp(argv[0]);
return false;
}
if (o->nameOfJit == nullptr)
{
// Try to find a JIT in the same directory as superpmi.exe. If found (based on targetArchitecture), use it.
bool allowDefaultJIT = true;
const char* hostOSTag = "";
const char* jitHostOSPrefix = "";
const char* jitHostOSExtension = "";
#if defined(HOST_OSX) // NOTE: HOST_UNIX is also defined for HOST_OSX
hostOSTag = "unix";
jitHostOSPrefix = "lib";
jitHostOSExtension = ".dylib";
#elif defined(HOST_UNIX)
hostOSTag = "unix";
jitHostOSPrefix = "lib";
jitHostOSExtension = ".so";
#elif defined(HOST_WINDOWS)
hostOSTag = "win";
jitHostOSExtension = ".dll";
#else
allowDefaultJIT = false;
#endif
const char* hostArch = "";
#if defined(HOST_AMD64)
hostArch = "x64";
#elif defined(HOST_X86)
hostArch = "x86";
#elif defined(HOST_ARM)
hostArch = "arm";
#elif defined(HOST_ARM64)
hostArch = "arm64";
#else
allowDefaultJIT = false;
#endif
const char* targetArch = "";
switch (GetSpmiTargetArchitecture())
{
case SPMI_TARGET_ARCHITECTURE_AMD64:
targetArch = "x64";
break;
case SPMI_TARGET_ARCHITECTURE_X86:
targetArch = "x86";
break;
case SPMI_TARGET_ARCHITECTURE_ARM:
targetArch = "arm";
break;
case SPMI_TARGET_ARCHITECTURE_ARM64:
targetArch = "arm64";
break;
default:
allowDefaultJIT = false;
break;
}
size_t programPathLen = 0;
char* programPath = nullptr;
const char* lastSlash = strrchr(argv[0], DIRECTORY_SEPARATOR_CHAR_A);
if (lastSlash == nullptr)
{
allowDefaultJIT = false;
}
else
{
programPathLen = lastSlash - argv[0] + 1;
programPath = new char[programPathLen];
strncpy_s(programPath, programPathLen, argv[0], programPathLen - 1);
}
if (allowDefaultJIT)
{
const char* jitOSName = nullptr;
if (defaultSpmiTargetArchitecture != GetSpmiTargetArchitecture())
{
// SuperPMI doesn't know about "target OS" so always assume host OS.
switch (GetSpmiTargetArchitecture())
{
case SPMI_TARGET_ARCHITECTURE_AMD64:
case SPMI_TARGET_ARCHITECTURE_X86:
jitOSName = hostOSTag;
break;
case SPMI_TARGET_ARCHITECTURE_ARM:
case SPMI_TARGET_ARCHITECTURE_ARM64:
jitOSName = "universal";
break;
default:
// Can't get here if `allowDefaultJIT` was properly set above.
break;
}
}
const char* const jitBaseName = "clrjit";
size_t len = programPathLen + strlen(jitHostOSPrefix) + strlen(jitBaseName) + strlen(jitHostOSExtension) + 1;
if (jitOSName != nullptr)
{
len += 3 /* underscores */ + strlen(jitOSName) + strlen(targetArch) + strlen(hostArch);
}
char* tempStr = new char[len];
if (jitOSName == nullptr)
{
sprintf_s(tempStr, len, "%s%c%s%s%s",
programPath,
DIRECTORY_SEPARATOR_CHAR_A,
jitHostOSPrefix,
jitBaseName,
jitHostOSExtension);
}
else
{
sprintf_s(tempStr, len, "%s%c%s%s_%s_%s_%s%s",
programPath,
DIRECTORY_SEPARATOR_CHAR_A,
jitHostOSPrefix,
jitBaseName,
jitOSName,
targetArch,
hostArch,
jitHostOSExtension);
}
o->nameOfJit = tempStr;
LogInfo("Using default JIT: %s", o->nameOfJit);
}
else
{
LogError("Missing name of a Jit.");
DumpHelp(argv[0]);
return false;
}
}
if (o->skipCleanup && !o->parallel)
{
LogError("-skipCleanup requires -parallel.");
DumpHelp(argv[0]);
return false;
}
return true;
}