* Dump spmi string environment variables with -d key.
Sometimes it is not clear which environment variables are set during a replay. Add a possibility to dump all active string environment variables with "-v d" key.
It is especially useful when you debug one method and can't get output that you want (for example JitDump) because altJit was set during the collection.
* Dump int spmi environment variables with -d key.
The same change but for int variables that need an additional parsing.
* Fix environment reset in spmi.
The previous changes revealed that we have a problem with environment reset. The reset was added in #13596 to support a correct replay of mch files with mc that had different env variables during the collection.
It was based on Environment that was later deprecated by #13110.
That caused the environment to be reset for each mc file. The fix updates env only when it is necessary and decreases the replay time of an mch file with 200000 methods from 500s to 370s.
* Fix dumpHelp.
Fix formating and adds note that all keys and values are case sensetive.
* Delete retired packages logic.
These packages were retired a long time ago and there is no value to have a special logic for them.
LWM(EmbedMethodHandle, DWORDLONG, DLDL)
LWM(EmbedModuleHandle, DWORDLONG, DLDL)
DENSELWM(EmptyStringLiteral, DLD)
-DENSELWM(Environment, Agnostic_Environment)
DENSELWM(ErrorList, DWORD)
LWM(FilterException, DWORD, DWORD)
LWM(FindCallSiteSig, Agnostic_FindCallSiteSig, Agnostic_CORINFO_SIG_INFO)
#include "spmirecordhelper.h"
#include "spmidumphelper.h"
-struct
-{
- int packetID;
- const char* message;
-} retiredPackets[] = {
- {6, "CanEmbedModuleHandleForHelper id 6 superseded by GetLazyStringLiteralHelper id 147 on 12/20/2013"},
- {13, "CheckMethodModifier id 13 superseded by id 142 on 2013/07/04. Re-record input with newer shim."},
- {14, "CompileMethod id 14 superseded by id 141 on 2013/07/03. Re-record input with newer shim."},
- {24, "FindNameOfToken id 24 superseded by id 145 on 2013/07/19. Re-record input with newer shim. Adjusted members "
- "to be proper."},
- {28, "GetArgClass id 28 superseded by id 139 on 2013/07/03. Re-record input with newer shim."},
- {30, "GetArgType id 30 superseded by id 140 on 2013/07/03. Re-record input with newer shim."},
- {93, "GetUnBoxHelper2 id 93 unused. 2016/02/19. Re-record input with newer shim."},
- {104, "IsValidToken id 104 superseded by id 144 on 2013/07/19. Re-record input with newer shim. Adjusted members "
- "to be proper."},
- {141, "CompileMethod id 141 superseded by id 142 on 2013/07/09. Re-record input with newer shim. We basically "
- "reset lots of other stuff too. :-)"},
-};
-int retiredPacketCount = 7;
-
#define sparseMC // Support filling in details where guesses are okay and will still generate good code. (i.e. helper
// function addresses)
while (buffIndex < totalLen)
{
- unsigned char packetType = buff2[buffIndex++];
+ mcPackets packetType = (mcPackets)buff2[buffIndex++];
memcpy(&localsize, &buff2[buffIndex], sizeof(unsigned int));
buffIndex += 4;
#include "crlwmlist.h"
default:
- for (int i = 0; i < retiredPacketCount; i++)
- {
- AssertCodeMsg(retiredPackets[i].packetID != packetType, EXCEPTIONCODE_MC,
- "Ran into retired packet %u '%s'", packetType, retiredPackets[i].message);
- }
LogException(EXCEPTIONCODE_MC, "Read ran into unknown packet type %u. Are you using a newer recorder?",
packetType);
// break;
}
}
-void MethodContext::dmpEnvironment(DWORD key, const Agnostic_Environment& value)
-{
-}
-
void MethodContext::dumpToConsole(int mcNumber)
{
printf("*****************************************");
#endif // !FEATURE_PAL
}
-DenseLightWeightMap<MethodContext::Agnostic_Environment>* MethodContext::prevEnviroment = nullptr;
+MethodContext::Environment MethodContext::cloneEnvironment()
+{
+ MethodContext::Environment env;
+ if (GetIntConfigValue != nullptr)
+ {
+ env.getIntConfigValue = new LightWeightMap<MethodContext::Agnostic_ConfigIntInfo, DWORD>(*GetIntConfigValue);
+ }
+ if (GetStringConfigValue != nullptr)
+ {
+ env.getStingConfigValue = new LightWeightMap<DWORD, DWORD>(*GetStringConfigValue);
+ }
+ return env;
+}
-bool MethodContext::wasEnviromentChanged()
+// Check that there is a difference between the current enviroment variables maps and the prevEnv.
+bool MethodContext::WasEnvironmentChanged(const Environment& prevEnv)
{
- bool changed = false;
- if (prevEnviroment == nullptr)
+ if (!IsEnvironmentHeaderEqual(prevEnv))
{
- changed = true;
+ return true;
}
- else if (Environment->GetCount() != prevEnviroment->GetCount())
+ if (!IsEnvironmentContentEqual(prevEnv))
{
- changed = true;
+ return true;
}
- else
+ return false;
+}
+
+// Check that environment maps headers are equal to the prevEnv maps headers.
+bool MethodContext::IsEnvironmentHeaderEqual(const Environment& prevEnv)
+{
+ if (!AreLWMHeadersEqual(prevEnv.getIntConfigValue, GetIntConfigValue))
+ {
+ return false;
+ }
+ if (!AreLWMHeadersEqual(prevEnv.getStingConfigValue, GetStringConfigValue))
+ {
+ return false;
+ }
+ return true;
+}
+
+// Check that environment maps content is equal to the prevEnv content.
+bool MethodContext::IsEnvironmentContentEqual(const Environment& prevEnv)
+{
+ if (!IsIntConfigContentEqual(prevEnv.getIntConfigValue, GetIntConfigValue))
+ {
+ return false;
+ }
+ if (!IsStringContentEqual(prevEnv.getStingConfigValue, GetStringConfigValue))
+ {
+ return false;
+ }
+ return true;
+}
+
+// Check pointers to be both initizlized or null and number of keys to be equal.
+template <typename key, typename value>
+bool MethodContext::AreLWMHeadersEqual(LightWeightMap<key, value>* prev, LightWeightMap<key, value>* curr)
+{
+ if (prev == nullptr && curr == nullptr)
+ {
+ return true;
+ }
+ if (prev != nullptr && curr != nullptr)
{
- for (unsigned int i = 0; i < Environment->GetCount(); i++)
+ if (prev->GetCount() == curr->GetCount())
{
- Agnostic_Environment currEnvValue = Environment->Get(i);
- LPCSTR currKey = (LPCSTR)Environment->GetBuffer(currEnvValue.name_index);
- LPCSTR currVal = (LPCSTR)Environment->GetBuffer(currEnvValue.val_index);
-
- Agnostic_Environment prevEnvValue = prevEnviroment->Get(i);
- LPCSTR prevKey = (LPCSTR)prevEnviroment->GetBuffer(prevEnvValue.name_index);
- LPCSTR prevVal = (LPCSTR)prevEnviroment->GetBuffer(prevEnvValue.val_index);
- if (strcmp(currKey, prevKey) != 0 || strcmp(currVal, prevVal) != 0)
- {
- changed = true;
- break;
- }
+ return true;
}
}
- if (changed)
+ return false;
+}
+
+bool MethodContext::IsIntConfigContentEqual(LightWeightMap<Agnostic_ConfigIntInfo, DWORD>* prev,
+ LightWeightMap<Agnostic_ConfigIntInfo, DWORD>* curr)
+{
+ if (prev != nullptr && curr != nullptr)
{
- if (prevEnviroment != nullptr)
+ if (prev->GetCount() != curr->GetCount())
{
- delete prevEnviroment;
+ return false;
}
- if (Environment != nullptr)
+
+ for (unsigned i = 0; i < prev->GetCount(); ++i)
{
- prevEnviroment = new DenseLightWeightMap<Agnostic_Environment>(*Environment);
+ DWORD currValue = curr->GetItem(i);
+ DWORD prevValue = prev->GetItem(i);
+ if (currValue != prevValue)
+ {
+ return false;
+ }
+
+ Agnostic_ConfigIntInfo currKey = curr->GetKey(i);
+ Agnostic_ConfigIntInfo prevKey = prev->GetKey(i);
+
+ if (currKey.defaultValue != prevKey.defaultValue)
+ {
+ return false;
+ }
+
+ DWORD currNameIndex = currKey.nameIndex;
+ LPCSTR currName = (LPCSTR)curr->GetBuffer(currNameIndex);
+ DWORD prevNameIndex = prevKey.nameIndex;
+ LPCSTR prevName = (LPCSTR)prev->GetBuffer(currNameIndex);
+ if (strcmp(currName, prevName) != 0)
+ {
+ return false;
+ }
}
- else
+ return true;
+ }
+ else
+ {
+ return (prev == curr);
+ }
+}
+
+bool MethodContext::IsStringContentEqual(LightWeightMap<DWORD, DWORD>* prev, LightWeightMap<DWORD, DWORD>* curr)
+{
+ if (prev != nullptr && curr != nullptr)
+ {
+ if (prev->GetCount() != curr->GetCount())
+ {
+ return false;
+ }
+
+ for (unsigned i = 0; i < curr->GetCount(); ++i)
{
- prevEnviroment = nullptr;
+ DWORD currKeyIndex = curr->GetKey(i);
+ LPCSTR currKey = (LPCSTR)curr->GetBuffer(currKeyIndex);
+ DWORD prevKeyIndex = prev->GetKey(i);
+ LPCSTR prevKey = (LPCSTR)prev->GetBuffer(prevKeyIndex);
+ if (strcmp(currKey, prevKey) != 0)
+ {
+ return false;
+ }
+
+ DWORD currValueIndex = curr->GetItem(i);
+ LPCSTR currValue = (LPCSTR)curr->GetBuffer(currValueIndex);
+ DWORD prevValueIndex = prev->GetItem(i);
+ LPCSTR prevValue = (LPCSTR)prev->GetBuffer(prevValueIndex);
+ if (strcmp(currValue, prevValue) != 0)
+ {
+ return false;
+ }
}
return true;
}
- return false;
+ else
+ {
+ return (prev == curr);
+ }
}
void dmpGetStringConfigValue(DWORD nameIndex, DWORD result);
const wchar_t* repGetStringConfigValue(const wchar_t* name);
- bool wasEnviromentChanged();
- static DenseLightWeightMap<Agnostic_Environment>* prevEnviroment;
+ struct Environment
+ {
+ Environment() : getIntConfigValue(nullptr), getStingConfigValue(nullptr)
+ {
+ }
+
+ LightWeightMap<MethodContext::Agnostic_ConfigIntInfo, DWORD>* getIntConfigValue;
+ LightWeightMap<DWORD, DWORD>* getStingConfigValue;
+ };
+
+ Environment cloneEnvironment();
+
+ bool WasEnvironmentChanged(const Environment& prevEnv);
CompileResult* cr;
CompileResult* originalCR;
int index;
private:
+ bool IsEnvironmentHeaderEqual(const Environment& prevEnv);
+ bool IsEnvironmentContentEqual(const Environment& prevEnv);
+
+ template <typename key, typename value>
+ static bool AreLWMHeadersEqual(LightWeightMap<key, value>* prev, LightWeightMap<key, value>* curr);
+ static bool IsIntConfigContentEqual(LightWeightMap<Agnostic_ConfigIntInfo, DWORD>* prev,
+ LightWeightMap<Agnostic_ConfigIntInfo, DWORD>* curr);
+ static bool IsStringContentEqual(LightWeightMap<DWORD, DWORD>* prev, LightWeightMap<DWORD, DWORD>* curr);
+
#define LWM(map, key, value) LightWeightMap<key, value>* map;
#define DENSELWM(map, value) DenseLightWeightMap<value>* map;
#include "lwmlist.h"
Packet_EmbedMethodHandle = 19,
Packet_EmbedModuleHandle = 20,
Packet_EmptyStringLiteral = 21,
- Packet_Environment = 136, // Deprecated 7/29/2017
+ Retired9 = 136,
Packet_ErrorList = 22,
Packet_FilterException = 134,
Packet_FindCallSiteSig = 23,
MethodContextReader::~MethodContextReader()
{
- if (MethodContext::prevEnviroment != nullptr)
- {
- delete MethodContext::prevEnviroment;
- }
if (fileHandle != INVALID_HANDLE_VALUE)
{
CloseHandle(this->fileHandle);
#endif // USE_COREDISTOOLS
printf("\n");
printf(" -jitoption [force] key=value\n");
- printf(" Set the JIT option named \"key\" to \"value\" for JIT 1 if the option was not set.");
- printf(" With optional force flag overwrites the existing value if it was already set. NOTE: do not use a "
- "\"COMPlus_\" prefix!\n");
+ printf(" Set the JIT option named \"key\" to \"value\" for JIT 1 if the option was not set.\n");
+ printf(" With optional force flag overwrites the existing value if it was already set.\n");
+ printf(" NOTE: do not use a \"COMPlus_\" prefix, \"key\" and \"value\" are case sensitive!\n");
printf("\n");
printf(" -jit2option [force] key=value\n");
- printf(" Set the JIT option named \"key\" to \"value\" for JIT 2 if the option was not set.");
- printf(" With optional force flag overwrites the existing value if it was already set. NOTE: do not use a "
- "\"COMPlus_\" prefix!\n");
+ printf(" Set the JIT option named \"key\" to \"value\" for JIT 2 if the option was not set.\n");
+ printf(" With optional force flag overwrites the existing value if it was already set.\n");
+ printf(" NOTE: do not use a \"COMPlus_\" prefix, \"key\" and \"value\" are case sensitive!\n");
printf("\n");
printf("Inputs are case sensitive.\n");
printf("\n");
InitIEEMemoryManager(&jitInstance)->ClrVirtualFree(block, 0, 0);
}
+bool JitHost::convertStringValueToInt(const wchar_t* key, const wchar_t* stringValue, int& result)
+{
+ if (stringValue == nullptr)
+ {
+ return false;
+ }
+
+ wchar_t* endPtr;
+ unsigned long longResult = wcstoul(stringValue, &endPtr, 16);
+ bool succeeded = (errno != ERANGE) && (endPtr != stringValue) && (longResult <= INT_MAX);
+ if (!succeeded)
+ {
+ LogWarning("Can't convert int config value from string, key: %ws, string value: %ws\n", key, stringValue);
+ return false;
+ }
+
+ result = static_cast<int>(longResult);
+ return true;
+}
+
int JitHost::getIntConfigValue(const wchar_t* key, int defaultValue)
{
jitInstance.mc->cr->AddCall("getIntConfigValue");
+ // First check the force options, then the mc value. If value is not presented there, probe the JIT options, then
+ // check
+ // the special cases and then the environment.
+
int result = defaultValue;
- const wchar_t* forceValue = jitInstance.getForceOption(key);
+ bool valueFound;
- if (forceValue != nullptr)
+ valueFound = convertStringValueToInt(key, jitInstance.getForceOption(key), result);
+
+ if (!valueFound)
{
- wchar_t* endPtr;
- result = static_cast<int>(wcstoul(forceValue, &endPtr, 16));
- bool succeeded = (errno != ERANGE) && (endPtr != forceValue);
- if (succeeded)
+ // Right now we can't distinguish between the default value that was set explicitly and the default value
+ // from the key that was not set. See comments in CLRConfig::GetConfigValue.
+ result = jitInstance.mc->repGetIntConfigValue(key, defaultValue);
+ if (result != defaultValue)
{
- return result;
+ valueFound = true;
}
}
- result = jitInstance.mc->repGetIntConfigValue(key, defaultValue);
-
- if (result != defaultValue)
+ if (!valueFound)
{
- return result;
+ // Look for special case keys.
+ if (wcscmp(key, W("SuperPMIMethodContextNumber")) == 0)
+ {
+ result = jitInstance.mc->index;
+ valueFound = true;
+ }
}
- // Look for special case keys.
- if (wcscmp(key, W("SuperPMIMethodContextNumber")) == 0)
+ if (!valueFound)
{
- return jitInstance.mc->index;
+ valueFound = convertStringValueToInt(key, jitInstance.getOption(key), result);
}
- // If the result is the default value, probe the JIT options and then the environment. If a value is found, parse
- // it as a hex integer.
-
- const wchar_t* value = jitInstance.getOption(key);
-
- bool succeeded;
- if (value != nullptr)
+ if (!valueFound)
{
- wchar_t* endPtr;
- result = static_cast<int>(wcstoul(value, &endPtr, 16));
- succeeded = (errno != ERANGE) && (endPtr != value);
- }
- else
- {
- wchar_t* complus = GetCOMPlusVariable(key, jitInstance);
- if (complus == nullptr)
+ wchar_t* complusVar = GetCOMPlusVariable(key, jitInstance);
+ valueFound = convertStringValueToInt(key, complusVar, result);
+ if (complusVar != nullptr)
{
- return defaultValue;
+ jitInstance.freeLongLivedArray(complusVar);
}
- wchar_t* endPtr;
- result = static_cast<int>(wcstoul(complus, &endPtr, 16));
- succeeded = (errno != ERANGE) && (endPtr != complus);
- jitInstance.freeLongLivedArray(complus);
}
- return succeeded ? result : defaultValue;
+ if (valueFound)
+ {
+ LogDebug("Environment variable %ws=%d", key, result);
+ }
+
+ return valueFound ? result : defaultValue;
}
const wchar_t* JitHost::getStringConfigValue(const wchar_t* key)
{
jitInstance.mc->cr->AddCall("getStringConfigValue");
- const wchar_t* result = nullptr;
+ bool needToDup = true;
+ const wchar_t* result = nullptr;
// First check the force options, then mc value. If value is not presented there, probe the JIT options and then the
// environment.
if (result == nullptr)
{
result = jitInstance.getOption(key);
- if (result == nullptr)
- {
- return GetCOMPlusVariable(key, jitInstance);
- }
}
- // Now we need to dup it, so you can call freeStringConfigValue() on what we return.
- size_t resultLenInChars = wcslen(result) + 1;
- wchar_t* dupResult = (wchar_t*)jitInstance.allocateLongLivedArray((ULONG)(sizeof(wchar_t) * resultLenInChars));
- wcscpy_s(dupResult, resultLenInChars, result);
+ if (result == nullptr)
+ {
+ result = GetCOMPlusVariable(key, jitInstance);
+ needToDup = false;
+ }
- return dupResult;
+ if (result != nullptr && needToDup)
+ {
+ // Now we need to dup it, so you can call freeStringConfigValue() on what we return.
+ size_t resultLenInChars = wcslen(result) + 1;
+ wchar_t* dupResult = (wchar_t*)jitInstance.allocateLongLivedArray((ULONG)(sizeof(wchar_t) * resultLenInChars));
+ wcscpy_s(dupResult, resultLenInChars, result);
+ result = dupResult;
+ }
+
+ if (result != nullptr)
+ {
+ LogDebug("Environment variable %ws=%ws", key, result);
+ }
+ return result;
}
void JitHost::freeStringConfigValue(const wchar_t* value)
#include "icorjithostimpl.h"
private:
+ bool convertStringValueToInt(const wchar_t* key, const wchar_t* stringValue, int& result);
+
JitInstance& jitInstance;
};
jit->options = options;
+ jit->environment.getIntConfigValue = nullptr;
+ jit->environment.getStingConfigValue = nullptr;
+
if (st1 != nullptr)
st1->Start();
HRESULT hr = jit->StartUp(nameOfJit, false, breakOnAssert, firstContext);
}
PAL_ENDTRY
+ Assert(environment.getIntConfigValue == nullptr && environment.getStingConfigValue == nullptr);
+ environment = mc->cloneEnvironment();
+
return param.result;
}
return false;
}
+ if (environment.getIntConfigValue != nullptr)
+ {
+ delete environment.getIntConfigValue;
+ environment.getIntConfigValue = nullptr;
+ }
+
+ if (environment.getStingConfigValue != nullptr)
+ {
+ delete environment.getStingConfigValue;
+ environment.getStingConfigValue = nullptr;
+ }
+
mc = firstContext;
ICorJitHost* newHost = new JitHost(*this);
jitHost = newHost;
return true;
}
+
+const MethodContext::Environment& JitInstance::getEnvironment()
+{
+ return environment;
+}
LightWeightMap<DWORD, DWORD>* forceOptions;
LightWeightMap<DWORD, DWORD>* options;
+ MethodContext::Environment environment;
+
JitInstance(){};
void timeResult(CORINFO_METHOD_INFO info, unsigned flags);
const wchar_t* getOption(const wchar_t* key);
const wchar_t* getOption(const wchar_t* key, LightWeightMap<DWORD, DWORD>* options);
+ const MethodContext::Environment& getEnvironment();
+
void* allocateArray(ULONG size);
void* allocateLongLivedArray(ULONG size);
void freeArray(void* array);
mc->cr = new CompileResult();
mc->originalCR = crl;
- if (mc->wasEnviromentChanged())
+ if (mc->WasEnvironmentChanged(jit->getEnvironment()))
{
if (!jit->resetConfig(mc))
{