From 19183a8e980b1d0811d5a392232ac6eddad84ffa Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Fri, 18 Aug 2023 13:53:47 -0700 Subject: [PATCH 1/6] Events for IL methods without IL headers Dynamically generated methods like UnsafeAccessor functions are marked as IL, but don't contain an IL header. The lack of header is an indication the IL must be generated at runtime. --- src/coreclr/vm/eventtrace.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/coreclr/vm/eventtrace.cpp b/src/coreclr/vm/eventtrace.cpp index b9a82cfb7c28e..4f976238a1734 100644 --- a/src/coreclr/vm/eventtrace.cpp +++ b/src/coreclr/vm/eventtrace.cpp @@ -4566,9 +4566,13 @@ VOID ETW::MethodLog::SendMethodJitStartEvent(MethodDesc *pMethodDesc, SString *n ulMethodToken = (ULONG)0; } else + { ulMethodToken = (ULONG)pMethodDesc->GetMemberDef(); + } - if(pMethodDesc->IsIL()) + // An IL method that has no IL header can occur for dynamically + // generated code during JIT (for example, UnsafeAccessor). + if(pMethodDesc->IsIL() && pMethodDesc->GetILHeader() != NULL) { COR_ILMETHOD_DECODER::DecoderStatus decoderstatus = COR_ILMETHOD_DECODER::FORMAT_ERROR; COR_ILMETHOD_DECODER ILHeader(pMethodDesc->GetILHeader(), pMethodDesc->GetMDImport(), &decoderstatus); From 616ae0bc7b6e6e465cf57be46a86d02437dd03fa Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Fri, 18 Aug 2023 14:33:26 -0700 Subject: [PATCH 2/6] Debugger check for no IL header --- src/coreclr/debug/daccess/stack.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/coreclr/debug/daccess/stack.cpp b/src/coreclr/debug/daccess/stack.cpp index 9402d529eb8ea..8e836c852350e 100644 --- a/src/coreclr/debug/daccess/stack.cpp +++ b/src/coreclr/debug/daccess/stack.cpp @@ -1253,7 +1253,10 @@ ClrDataFrame::GetLocalSig(MetaSig** sig, { // It turns out we cannot really get rid of this check. Dynamic methods // (including IL stubs) do not have their local sig's available after JIT time. - if (!m_methodDesc->IsIL()) + // IL Methods with dynamically generated IL (for example, UnsafeAccessors) can + // may have not have an IL header. + if (!m_methodDesc->IsIL() + || m_methodDesc->GetILHeader() == NULL) { *sig = NULL; *count = 0; From 4586f5d2a3d789f9c3cb34346eee7bca834c3317 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 18 Aug 2023 14:49:05 -0700 Subject: [PATCH 3/6] Update src/coreclr/debug/daccess/stack.cpp Co-authored-by: Tlakaelel Axayakatl Ceja --- src/coreclr/debug/daccess/stack.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/debug/daccess/stack.cpp b/src/coreclr/debug/daccess/stack.cpp index 8e836c852350e..21dfaffbfd1e0 100644 --- a/src/coreclr/debug/daccess/stack.cpp +++ b/src/coreclr/debug/daccess/stack.cpp @@ -1253,8 +1253,8 @@ ClrDataFrame::GetLocalSig(MetaSig** sig, { // It turns out we cannot really get rid of this check. Dynamic methods // (including IL stubs) do not have their local sig's available after JIT time. - // IL Methods with dynamically generated IL (for example, UnsafeAccessors) can - // may have not have an IL header. + // IL Methods with dynamically generated IL (for example, UnsafeAccessors) may + // not have an IL header. if (!m_methodDesc->IsIL() || m_methodDesc->GetILHeader() == NULL) { From c9d9c6ac1996e2af9f98591a0636ac4c224379ab Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Fri, 18 Aug 2023 15:37:49 -0700 Subject: [PATCH 4/6] Review feedback --- src/coreclr/inc/eventtracebase.h | 6 +- src/coreclr/vm/eventtrace.cpp | 21 +++--- src/coreclr/vm/jitinterface.cpp | 2 +- src/coreclr/vm/method.hpp | 2 +- src/coreclr/vm/prestub.cpp | 116 +++++++++++++++---------------- 5 files changed, 73 insertions(+), 74 deletions(-) diff --git a/src/coreclr/inc/eventtracebase.h b/src/coreclr/inc/eventtracebase.h index 97c3135153038..3648b1f3a7213 100644 --- a/src/coreclr/inc/eventtracebase.h +++ b/src/coreclr/inc/eventtracebase.h @@ -905,7 +905,7 @@ namespace ETW BOOL fSendRichDebugInfoEvent, BOOL fGetCodeIds); static VOID SendEventsForNgenMethods(Module *pModule, DWORD dwEventOptions); - static VOID SendMethodJitStartEvent(MethodDesc *pMethodDesc, SString *namespaceOrClassName=NULL, SString *methodName=NULL, SString *methodSignature=NULL); + static VOID SendMethodJitStartEvent(MethodDesc *pMethodDesc, COR_ILMETHOD_DECODER* methodDecoder, SString *namespaceOrClassName=NULL, SString *methodName=NULL, SString *methodSignature=NULL); static VOID SendMethodILToNativeMapEvent(MethodDesc * pMethodDesc, DWORD dwEventOptions, PCODE pNativeCodeStartAddress, DWORD nativeCodeId, ReJITID ilCodeId); static VOID SendMethodRichDebugInfo(MethodDesc * pMethodDesc, PCODE pNativeCodeStartAddress, DWORD nativeCodeId, ReJITID ilCodeId, MethodDescSet* sentMethodDetailsSet); static VOID SendMethodEvent(MethodDesc *pMethodDesc, DWORD dwEventOptions, BOOL bIsJit, SString *namespaceOrClassName=NULL, SString *methodName=NULL, SString *methodSignature=NULL, PCODE pNativeCodeStartAddress = 0, PrepareCodeConfig *pConfig = NULL, MethodDescSet* sentMethodDetailsSet = NULL); @@ -938,7 +938,7 @@ namespace ETW static VOID GetR2RGetEntryPointStart(MethodDesc *pMethodDesc); static VOID GetR2RGetEntryPoint(MethodDesc *pMethodDesc, PCODE pEntryPoint); - static VOID MethodJitting(MethodDesc *pMethodDesc, SString *namespaceOrClassName, SString *methodName, SString *methodSignature); + static VOID MethodJitting(MethodDesc *pMethodDesc, COR_ILMETHOD_DECODER* methodDecoder, SString *namespaceOrClassName, SString *methodName, SString *methodSignature); static VOID MethodJitted(MethodDesc *pMethodDesc, SString *namespaceOrClassName, SString *methodName, SString *methodSignature, PCODE pNativeCodeStartAddress, PrepareCodeConfig *pConfig); static VOID SendMethodDetailsEvent(MethodDesc *pMethodDesc); static VOID SendNonDuplicateMethodDetailsEvent(MethodDesc* pMethodDesc, MethodDescSet* set); @@ -952,7 +952,7 @@ namespace ETW public: static VOID GetR2RGetEntryPointStart(MethodDesc *pMethodDesc) {}; static VOID GetR2RGetEntryPoint(MethodDesc *pMethodDesc, PCODE pEntryPoint) {}; - static VOID MethodJitting(MethodDesc *pMethodDesc, SString *namespaceOrClassName, SString *methodName, SString *methodSignature); + static VOID MethodJitting(MethodDesc *pMethodDesc, COR_ILMETHOD_DECODER* methodDecoder, SString *namespaceOrClassName, SString *methodName, SString *methodSignature); static VOID MethodJitted(MethodDesc *pMethodDesc, SString *namespaceOrClassName, SString *methodName, SString *methodSignature, PCODE pNativeCodeStartAddress, PrepareCodeConfig *pConfig); static VOID StubInitialized(ULONGLONG ullHelperStartAddress, LPCWSTR pHelperName) {}; static VOID StubsInitialized(PVOID *pHelperStartAddress, PVOID *pHelperNames, LONG ulNoOfHelpers) {}; diff --git a/src/coreclr/vm/eventtrace.cpp b/src/coreclr/vm/eventtrace.cpp index 4f976238a1734..9498f00edf432 100644 --- a/src/coreclr/vm/eventtrace.cpp +++ b/src/coreclr/vm/eventtrace.cpp @@ -3555,7 +3555,7 @@ VOID ETW::MethodLog::MethodJitted(MethodDesc *pMethodDesc, SString *namespaceOrC /*************************************************/ /* This is called by the runtime when method jitting started */ /*************************************************/ -VOID ETW::MethodLog::MethodJitting(MethodDesc *pMethodDesc, SString *namespaceOrClassName, SString *methodName, SString *methodSignature) +VOID ETW::MethodLog::MethodJitting(MethodDesc *pMethodDesc, COR_ILMETHOD_DECODER* methodDecoder, SString *namespaceOrClassName, SString *methodName, SString *methodSignature) { CONTRACTL { NOTHROW; @@ -3570,7 +3570,7 @@ VOID ETW::MethodLog::MethodJitting(MethodDesc *pMethodDesc, SString *namespaceOr CLR_JIT_KEYWORD)) { pMethodDesc->GetMethodInfo(*namespaceOrClassName, *methodName, *methodSignature); - ETW::MethodLog::SendMethodJitStartEvent(pMethodDesc, namespaceOrClassName, methodName, methodSignature); + ETW::MethodLog::SendMethodJitStartEvent(pMethodDesc, methodDecoder, namespaceOrClassName, methodName, methodSignature); } } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions); } @@ -4528,7 +4528,12 @@ VOID ETW::MethodLog::SendNonDuplicateMethodDetailsEvent(MethodDesc* pMethodDesc, /*****************************************************************/ /* This routine is used to send an ETW event just before a method starts jitting*/ /*****************************************************************/ -VOID ETW::MethodLog::SendMethodJitStartEvent(MethodDesc *pMethodDesc, SString *namespaceOrClassName, SString *methodName, SString *methodSignature) +VOID ETW::MethodLog::SendMethodJitStartEvent( + MethodDesc *pMethodDesc, + COR_ILMETHOD_DECODER* methodDecoder, + SString *namespaceOrClassName, + SString *methodName, + SString *methodSignature) { CONTRACTL { THROWS; @@ -4570,14 +4575,8 @@ VOID ETW::MethodLog::SendMethodJitStartEvent(MethodDesc *pMethodDesc, SString *n ulMethodToken = (ULONG)pMethodDesc->GetMemberDef(); } - // An IL method that has no IL header can occur for dynamically - // generated code during JIT (for example, UnsafeAccessor). - if(pMethodDesc->IsIL() && pMethodDesc->GetILHeader() != NULL) - { - COR_ILMETHOD_DECODER::DecoderStatus decoderstatus = COR_ILMETHOD_DECODER::FORMAT_ERROR; - COR_ILMETHOD_DECODER ILHeader(pMethodDesc->GetILHeader(), pMethodDesc->GetMDImport(), &decoderstatus); - ulMethodILSize = (ULONG)ILHeader.GetCodeSize(); - } + if (methodDecoder != NULL) + ulMethodILSize = methodDecoder->GetCodeSize(); SString tNamespace, tMethodName, tMethodSignature; if(!namespaceOrClassName|| !methodName|| !methodSignature || (methodName->IsEmpty() && namespaceOrClassName->IsEmpty() && methodSignature->IsEmpty())) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 1a3439bdd235e..74ee2f7482e74 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -12286,7 +12286,7 @@ static CorJitResult CompileMethodWithEtwWrapper(EEJitManager *jitMgr, SString namespaceOrClassName, methodName, methodSignature; // Fire an ETW event to mark the beginning of JIT'ing - ETW::MethodLog::MethodJitting(reinterpret_cast(info->ftn), &namespaceOrClassName, &methodName, &methodSignature); + ETW::MethodLog::MethodJitting(reinterpret_cast(info->ftn), NULL, &namespaceOrClassName, &methodName, &methodSignature); CorJitResult ret = jitMgr->m_jit->compileMethod(comp, info, flags, nativeEntry, nativeSizeOfCode); diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index 4b34045b57671..e51d9f7453d35 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -1819,7 +1819,7 @@ class MethodDesc PCODE GetMulticoreJitCode(PrepareCodeConfig* pConfig, bool* pWasTier0); PCODE JitCompileCode(PrepareCodeConfig* pConfig); PCODE JitCompileCodeLockedEventWrapper(PrepareCodeConfig* pConfig, JitListLockEntry* pEntry); - PCODE JitCompileCodeLocked(PrepareCodeConfig* pConfig, JitListLockEntry* pLockEntry, ULONG* pSizeOfCode); + PCODE JitCompileCodeLocked(PrepareCodeConfig* pConfig, COR_ILMETHOD_DECODER* pilHeader, JitListLockEntry* pLockEntry, ULONG* pSizeOfCode); public: bool TryGenerateUnsafeAccessor(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder); diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 174e48565f31b..3294495230164 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -709,6 +709,53 @@ PCODE MethodDesc::JitCompileCode(PrepareCodeConfig* pConfig) } } +namespace +{ + COR_ILMETHOD_DECODER* GetAndVerifyMetadataILHeader(MethodDesc* pMD, PrepareCodeConfig* pConfig, COR_ILMETHOD_DECODER* pDecoderMemory) + { + STANDARD_VM_CONTRACT; + _ASSERTE(pMD != NULL); + _ASSERTE(!pMD->IsNoMetadata()); + _ASSERTE(pConfig != NULL); + _ASSERTE(pDecoderMemory != NULL); + + COR_ILMETHOD_DECODER* pHeader = NULL; + COR_ILMETHOD* ilHeader = pConfig->GetILHeader(); + if (ilHeader == NULL) + return NULL; + + COR_ILMETHOD_DECODER::DecoderStatus status = COR_ILMETHOD_DECODER::FORMAT_ERROR; + { + // Decoder ctor can AV on a malformed method header + AVInRuntimeImplOkayHolder AVOkay; + pHeader = new (pDecoderMemory) COR_ILMETHOD_DECODER(ilHeader, pMD->GetMDImport(), &status); + } + + if (status == COR_ILMETHOD_DECODER::FORMAT_ERROR) + COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_BAD_IL); + + return pHeader; + } + + COR_ILMETHOD_DECODER* GetAndVerifyILHeader(MethodDesc* pMD, PrepareCodeConfig* pConfig, COR_ILMETHOD_DECODER* pIlDecoderMemory) + { + STANDARD_VM_CONTRACT; + _ASSERTE(pMD != NULL); + if (pMD->IsIL()) + { + return GetAndVerifyMetadataILHeader(pMD, pConfig, pIlDecoderMemory); + } + else if (pMD->IsILStub()) + { + ILStubResolver* pResolver = pMD->AsDynamicMethodDesc()->GetILStubResolver(); + return pResolver->GetILHeader(); + } + + _ASSERTE(pMD->IsNoMetadata()); + return NULL; + } +} + PCODE MethodDesc::JitCompileCodeLockedEventWrapper(PrepareCodeConfig* pConfig, JitListLockEntry* pEntry) { STANDARD_VM_CONTRACT; @@ -759,11 +806,18 @@ PCODE MethodDesc::JitCompileCodeLockedEventWrapper(PrepareCodeConfig* pConfig, J } #endif // PROFILING_SUPPORTED + // The profiler may have changed the code on the callback. Need to + // pick up the new code. + // + // (don't want this for OSR, need to see how it works) + COR_ILMETHOD_DECODER ilDecoderTemp; + COR_ILMETHOD_DECODER* pilHeader = GetAndVerifyILHeader(this, pConfig, &ilDecoderTemp); + if (!ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, TRACE_LEVEL_VERBOSE, CLR_JIT_KEYWORD)) { - pCode = JitCompileCodeLocked(pConfig, pEntry, &sizeOfCode); + pCode = JitCompileCodeLocked(pConfig, pilHeader, pEntry, &sizeOfCode); } else { @@ -778,12 +832,13 @@ PCODE MethodDesc::JitCompileCodeLockedEventWrapper(PrepareCodeConfig* pConfig, J // a small stub of native code but no native-IL mapping. #ifndef FEATURE_INTERPRETER ETW::MethodLog::MethodJitting(this, + pilHeader, &namespaceOrClassName, &methodName, &methodSignature); #endif - pCode = JitCompileCodeLocked(pConfig, pEntry, &sizeOfCode); + pCode = JitCompileCodeLocked(pConfig, pilHeader, pEntry, &sizeOfCode); // Interpretted methods skip this notification #ifdef FEATURE_INTERPRETER @@ -869,66 +924,11 @@ PCODE MethodDesc::JitCompileCodeLockedEventWrapper(PrepareCodeConfig* pConfig, J return pCode; } -namespace -{ - COR_ILMETHOD_DECODER* GetAndVerifyMetadataILHeader(MethodDesc* pMD, PrepareCodeConfig* pConfig, COR_ILMETHOD_DECODER* pDecoderMemory) - { - STANDARD_VM_CONTRACT; - _ASSERTE(pMD != NULL); - _ASSERTE(!pMD->IsNoMetadata()); - _ASSERTE(pConfig != NULL); - _ASSERTE(pDecoderMemory != NULL); - - COR_ILMETHOD_DECODER* pHeader = NULL; - COR_ILMETHOD* ilHeader = pConfig->GetILHeader(); - if (ilHeader == NULL) - return NULL; - - COR_ILMETHOD_DECODER::DecoderStatus status = COR_ILMETHOD_DECODER::FORMAT_ERROR; - { - // Decoder ctor can AV on a malformed method header - AVInRuntimeImplOkayHolder AVOkay; - pHeader = new (pDecoderMemory) COR_ILMETHOD_DECODER(ilHeader, pMD->GetMDImport(), &status); - } - - if (status == COR_ILMETHOD_DECODER::FORMAT_ERROR) - COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_BAD_IL); - - return pHeader; - } - - COR_ILMETHOD_DECODER* GetAndVerifyILHeader(MethodDesc* pMD, PrepareCodeConfig* pConfig, COR_ILMETHOD_DECODER* pIlDecoderMemory) - { - STANDARD_VM_CONTRACT; - _ASSERTE(pMD != NULL); - if (pMD->IsIL()) - { - return GetAndVerifyMetadataILHeader(pMD, pConfig, pIlDecoderMemory); - } - else if (pMD->IsILStub()) - { - ILStubResolver* pResolver = pMD->AsDynamicMethodDesc()->GetILStubResolver(); - return pResolver->GetILHeader(); - } - - _ASSERTE(pMD->IsNoMetadata()); - return NULL; - } -} - -PCODE MethodDesc::JitCompileCodeLocked(PrepareCodeConfig* pConfig, JitListLockEntry* pEntry, ULONG* pSizeOfCode) +PCODE MethodDesc::JitCompileCodeLocked(PrepareCodeConfig* pConfig, COR_ILMETHOD_DECODER* pilHeader, JitListLockEntry* pEntry, ULONG* pSizeOfCode) { STANDARD_VM_CONTRACT; PCODE pCode = NULL; - - // The profiler may have changed the code on the callback. Need to - // pick up the new code. - // - // (don't want this for OSR, need to see how it works) - COR_ILMETHOD_DECODER ilDecoderTemp; - COR_ILMETHOD_DECODER* pilHeader = GetAndVerifyILHeader(this, pConfig, &ilDecoderTemp); - CORJIT_FLAGS jitFlags; PCODE pOtherCode = NULL; From 5513fb86e99add5d262d45044820062c660e6556 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Fri, 18 Aug 2023 16:15:09 -0700 Subject: [PATCH 5/6] Remove redundent calls and another spot to check. --- src/coreclr/debug/daccess/stack.cpp | 10 ++++++---- src/coreclr/vm/versionresilienthashcode.cpp | 8 +++++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/coreclr/debug/daccess/stack.cpp b/src/coreclr/debug/daccess/stack.cpp index 21dfaffbfd1e0..6b9f1a491c291 100644 --- a/src/coreclr/debug/daccess/stack.cpp +++ b/src/coreclr/debug/daccess/stack.cpp @@ -1253,17 +1253,19 @@ ClrDataFrame::GetLocalSig(MetaSig** sig, { // It turns out we cannot really get rid of this check. Dynamic methods // (including IL stubs) do not have their local sig's available after JIT time. - // IL Methods with dynamically generated IL (for example, UnsafeAccessors) may + // IL methods with dynamically generated IL (for example, UnsafeAccessors) may // not have an IL header. - if (!m_methodDesc->IsIL() - || m_methodDesc->GetILHeader() == NULL) + COR_ILMETHOD* ilHeader = m_methodDesc->IsIL() + ? m_methodDesc->GetILHeader() + : NULL; + if (ilHeader == NULL) { *sig = NULL; *count = 0; return E_FAIL; } - COR_ILMETHOD_DECODER methodDecoder(m_methodDesc->GetILHeader()); + COR_ILMETHOD_DECODER methodDecoder(ilHeader); mdSignature localSig = methodDecoder.GetLocalVarSigTok() ? methodDecoder.GetLocalVarSigTok() : mdSignatureNil; if (localSig == mdSignatureNil) diff --git a/src/coreclr/vm/versionresilienthashcode.cpp b/src/coreclr/vm/versionresilienthashcode.cpp index b3ba764baac59..85bd146d8dc46 100644 --- a/src/coreclr/vm/versionresilienthashcode.cpp +++ b/src/coreclr/vm/versionresilienthashcode.cpp @@ -286,7 +286,7 @@ bool AddVersionResilientHashCodeForInstruction(ILInstructionParser *parser, xxHa hash->Add(varValue); break; } - + case InlineVar: // 2 byte value which is token change resilient { uint16_t varValue; @@ -388,6 +388,12 @@ bool GetVersionResilientILCodeHashCode(MethodDesc *pMD, int* hashCode, unsigned* initLocals = (options & CORINFO_OPT_INIT_LOCALS) == CORINFO_OPT_INIT_LOCALS; } + else if (!pMD->HasILHeader()) + { + // Dynamically generated IL methods like UnsafeAccessors may not have + // an IL header. + return false; + } else { COR_ILMETHOD_DECODER header(pMD->GetILHeader(TRUE), pMD->GetMDImport(), NULL); From fb29886464e65fa54c905c4d2dd039a20b46136f Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Fri, 18 Aug 2023 19:03:21 -0700 Subject: [PATCH 6/6] Move header include --- src/coreclr/utilcode/stresslog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/utilcode/stresslog.cpp b/src/coreclr/utilcode/stresslog.cpp index c55c5afe9249c..90ad5900473ed 100644 --- a/src/coreclr/utilcode/stresslog.cpp +++ b/src/coreclr/utilcode/stresslog.cpp @@ -12,9 +12,9 @@ #include "switches.h" #include "stresslog.h" #include "clrhost.h" +#include "ex.h" #define DONOT_DEFINE_ETW_CALLBACK #include "eventtracebase.h" -#include "ex.h" #if !defined(STRESS_LOG_READONLY) #ifdef HOST_WINDOWS