diff options
author | dotnet-bot <dotnet-bot@microsoft.com> | 2015-05-06 23:43:46 -0700 |
---|---|---|
committer | Jan Kotas <jkotas@microsoft.com> | 2015-05-07 12:03:00 -0700 |
commit | 484a2cf0b0c4e304a5093ec26e07fe41f8896c3c (patch) | |
tree | 348b56df4cdb235bb87ba9bc9118711c8db13bfd /src/vm | |
parent | c6efc7047edb38075310cfef8ea28b91717b8108 (diff) | |
download | coreclr-484a2cf0b0c4e304a5093ec26e07fe41f8896c3c.tar.gz coreclr-484a2cf0b0c4e304a5093ec26e07fe41f8896c3c.tar.bz2 coreclr-484a2cf0b0c4e304a5093ec26e07fe41f8896c3c.zip |
Merge changes from parent branch
[tfs-changeset: 1466545]
Diffstat (limited to 'src/vm')
31 files changed, 766 insertions, 75 deletions
diff --git a/src/vm/ClrEtwAll.man b/src/vm/ClrEtwAll.man index ea3c4da935..71b7346878 100644 --- a/src/vm/ClrEtwAll.man +++ b/src/vm/ClrEtwAll.man @@ -71,6 +71,8 @@ message="$(string.RuntimePublisher.ThreadTransferKeywordMessage)" symbol="CLR_THREADTRANSFER_KEYWORD"/> <keyword name="DebuggerKeyword" mask="0x100000000" message="$(string.RuntimePublisher.DebuggerKeywordMessage)" symbol="CLR_DEBUGGER_KEYWORD" /> + <keyword name="MonitoringKeyword" mask="0x200000000" + message="$(string.RuntimePublisher.MonitoringKeywordMessage)" symbol="CLR_MONITORING_KEYWORD" /> </keywords> <!--Tasks--> <tasks> @@ -163,7 +165,28 @@ </opcodes> </task> - <task name="Contention" symbol="CLR_CONTENTION_TASK" + <task name="ExceptionCatch" symbol="CLR_EXCEPTION_CATCH_TASK" + value="27" eventGUID="{5BBF9499-1715-4658-88DC-AFD7690A8711}" + message="$(string.RuntimePublisher.ExceptionCatchTaskMessage)"> + <opcodes> + </opcodes> + </task> + + <task name="ExceptionFinally" symbol="CLR_EXCEPTION_FINALLY_TASK" + value="28" eventGUID="{9565BC31-300F-4EA2-A532-30BCE9A14199}" + message="$(string.RuntimePublisher.ExceptionFinallyTaskMessage)"> + <opcodes> + </opcodes> + </task> + + <task name="ExceptionFilter" symbol="CLR_EXCEPTION_FILTER_TASK" + value="29" eventGUID="{72E72606-BB71-4290-A242-D5F36CE5312E}" + message="$(string.RuntimePublisher.ExceptionFilterTaskMessage)"> + <opcodes> + </opcodes> + </task> + + <task name="Contention" symbol="CLR_CONTENTION_TASK" value="8" eventGUID="{561410f5-a138-4ab3-945e-516483cddfbc}" message="$(string.RuntimePublisher.ContentionTaskMessage)"> <opcodes> @@ -339,6 +362,7 @@ <opcodes> </opcodes> </task> + <!--Next available ID is 30--> </tasks> <!--Maps--> <maps> @@ -1326,6 +1350,21 @@ </UserData> </template> + <template tid="ExceptionHandling"> + <data name="EntryEIP" inType="win:UInt64" outType="win:HexInt64" /> + <data name="MethodID" inType="win:UInt64" outType="win:HexInt64" /> + <data name="MethodName" inType="win:UnicodeString" /> + <data name="ClrInstanceID" inType="win:UInt16" /> + <UserData> + <ExceptionHandling xmlns="myNs"> + <EntryEIP> %1 </EntryEIP> + <MethodID> %2 </MethodID> + <MethodName> %3 </MethodName> + <ClrInstanceID> %4 </ClrInstanceID> + </ExceptionHandling> + </UserData> + </template> + <template tid="Contention"> <data name="ContentionFlags" inType="win:UInt8" map="ContentionFlagsMap" /> <data name="ClrInstanceID" inType="win:UInt16" /> @@ -2660,10 +2699,45 @@ symbol="ExceptionThrown" message="$(string.RuntimePublisher.ExceptionExceptionThrownEventMessage)"/> <event value="80" version="1" level="win:Error" template="Exception" - keywords ="ExceptionKeyword" opcode="win:Start" + keywords ="ExceptionKeyword MonitoringKeyword" opcode="win:Start" task="Exception" symbol="ExceptionThrown_V1" message="$(string.RuntimePublisher.ExceptionExceptionThrown_V1EventMessage)"/> + <event value="250" version="0" level="win:Informational" template="ExceptionHandling" + keywords ="ExceptionKeyword" opcode="win:Start" + task="ExceptionCatch" + symbol="ExceptionCatchStart" message="$(string.RuntimePublisher.ExceptionExceptionHandlingEventMessage)"/> + + <event value="251" version="0" level="win:Informational" + keywords ="ExceptionKeyword" opcode="win:Stop" + task="ExceptionCatch" + symbol="ExceptionCatchStop" message="$(string.RuntimePublisher.ExceptionExceptionHandlingNoneEventMessage)"/> + + <event value="252" version="0" level="win:Informational" template="ExceptionHandling" + keywords ="ExceptionKeyword" opcode="win:Start" + task="ExceptionFinally" + symbol="ExceptionFinallyStart" message="$(string.RuntimePublisher.ExceptionExceptionHandlingEventMessage)"/> + + <event value="253" version="0" level="win:Informational" + keywords ="ExceptionKeyword" opcode="win:Stop" + task="ExceptionFinally" + symbol="ExceptionFinallyStop" message="$(string.RuntimePublisher.ExceptionExceptionHandlingNoneEventMessage)"/> + + <event value="254" version="0" level="win:Informational" template="ExceptionHandling" + keywords ="ExceptionKeyword" opcode="win:Start" + task="ExceptionFilter" + symbol="ExceptionFilterStart" message="$(string.RuntimePublisher.ExceptionExceptionHandlingEventMessage)"/> + + <event value="255" version="0" level="win:Informational" + keywords ="ExceptionKeyword" opcode="win:Stop" + task="ExceptionFilter" + symbol="ExceptionFilterStop" message="$(string.RuntimePublisher.ExceptionExceptionHandlingNoneEventMessage)"/> + + <event value="256" version="0" level="win:Informational" + keywords ="ExceptionKeyword" opcode="win:Stop" + task="Exception" + symbol="ExceptionThrownStop" message="$(string.RuntimePublisher.ExceptionExceptionHandlingNoneEventMessage)"/> + <!-- CLR Contention events --> <event value="81" version="0" level="win:Informational" opcode="win:Start" @@ -6118,6 +6192,8 @@ <string id="RuntimePublisher.ThreadRunningEventMessage" value="ID=%1;%nClrInstanceID=%s" /> <string id="RuntimePublisher.ExceptionExceptionThrownEventMessage" value="NONE" /> <string id="RuntimePublisher.ExceptionExceptionThrown_V1EventMessage" value="ExceptionType=%1;%nExceptionMessage=%2;%nExceptionEIP=%3;%nExceptionHRESULT=%4;%nExceptionFlags=%5;%nClrInstanceID=%6" /> + <string id="RuntimePublisher.ExceptionExceptionHandlingEventMessage" value="EntryEIP=%1;%nMethodID=%2;%nMethodName=%3;%nClrInstanceID=%4" /> + <string id="RuntimePublisher.ExceptionExceptionHandlingNoneEventMessage" value="NONE" /> <string id="RuntimePublisher.ContentionStartEventMessage" value="NONE" /> <string id="RuntimePublisher.ContentionStart_V1EventMessage" value="ContentionFlags=%1;%nClrInstanceID=%2"/> <string id="RuntimePublisher.ContentionStopEventMessage" value="ContentionFlags=%1;%nClrInstanceID=%2"/> @@ -6321,6 +6397,9 @@ <string id="RuntimePublisher.ThreadPoolWorkerThreadRetirementTaskMessage" value="ThreadPoolWorkerThreadRetirement" /> <string id="RuntimePublisher.ThreadPoolWorkerThreadAdjustmentTaskMessage" value="ThreadPoolWorkerThreadAdjustment" /> <string id="RuntimePublisher.ExceptionTaskMessage" value="Exception" /> + <string id="RuntimePublisher.ExceptionCatchTaskMessage" value="ExceptionCatch" /> + <string id="RuntimePublisher.ExceptionFinallyTaskMessage" value="ExceptionFinally" /> + <string id="RuntimePublisher.ExceptionFilterTaskMessage" value="ExceptionFilter" /> <string id="RuntimePublisher.ContentionTaskMessage" value="Contention" /> <string id="RuntimePublisher.MethodTaskMessage" value="Method" /> <string id="RuntimePublisher.LoaderTaskMessage" value="Loader" /> @@ -6609,6 +6688,7 @@ <string id="RuntimePublisher.GCHandleKeywordMessage" value="GCHandle" /> <string id="RuntimePublisher.ThreadTransferKeywordMessage" value="ThreadTransfer" /> <string id="RuntimePublisher.DebuggerKeywordMessage" value="Debugger" /> + <string id="RuntimePublisher.MonitoringKeywordMessage" value="Monitoring" /> <string id="RundownPublisher.LoaderKeywordMessage" value="Loader" /> <string id="RundownPublisher.JitKeywordMessage" value="Jit" /> <string id="RundownPublisher.JittedMethodILToNativeMapRundownKeywordMessage" value="JittedMethodILToNativeMapRundown" /> diff --git a/src/vm/appdomain.hpp b/src/vm/appdomain.hpp index a48cafbac9..4be9dcc4b1 100644 --- a/src/vm/appdomain.hpp +++ b/src/vm/appdomain.hpp @@ -2032,6 +2032,7 @@ public: #ifndef FEATURE_CORECLR inline BOOL AppDomainManagerSetFromConfig(); Assembly *GetAppDomainManagerEntryAssembly(); + void ComputeTargetFrameworkName(); #endif // FEATURE_CORECLR #if defined(FEATURE_CORECLR) && defined(FEATURE_COMINTEROP) @@ -4520,7 +4521,7 @@ public: #endif // DACCESS_COMPILE #ifndef FEATURE_CORECLR - static void ExecuteMainMethod(HMODULE hMod, __in_opt LPWSTR path = NULL); + static void ExecuteMainMethod(HMODULE hMod, __in_opt LPWSTR path = NULL); #endif static void ActivateApplication(int *pReturnValue); diff --git a/src/vm/assembly.cpp b/src/vm/assembly.cpp index 3c41c45ddb..ba65f13bfa 100644 --- a/src/vm/assembly.cpp +++ b/src/vm/assembly.cpp @@ -73,6 +73,10 @@ #include "eventmsg.h" #endif +#ifdef FEATURE_TRACELOGGING +#include "clrtracelogging.h" +#endif // FEATURE_TRACELOGGING + // Define these macro's to do strict validation for jit lock and class init entry leaks. // This defines determine if the asserts that verify for these leaks are defined or not. @@ -179,6 +183,43 @@ Assembly::Assembly(BaseDomain *pDomain, PEAssembly* pFile, DebuggerAssemblyContr // which is used in AssemblyBuilder.InitManifestModule #define REFEMIT_MANIFEST_MODULE_NAME W("RefEmit_InMemoryManifestModule") + +#ifdef FEATURE_TRACELOGGING +//---------------------------------------------------------------------------------------------- +// Reads and logs the TargetFramework attribute for an assembly. For example: [assembly: TargetFramework(".NETFramework,Version=v4.0")] +//---------------------------------------------------------------------------------------------- +void Assembly::TelemetryLogTargetFrameworkAttribute() +{ + const BYTE *pbAttr; // Custom attribute data as a BYTE*. + ULONG cbAttr; // Size of custom attribute data. + HRESULT hr = GetManifestImport()->GetCustomAttributeByName(GetManifestToken(), TARGET_FRAMEWORK_TYPE, (const void**)&pbAttr, &cbAttr); + bool dataLogged = false; + if (hr == S_OK) + { + CustomAttributeParser cap(pbAttr, cbAttr); + LPCUTF8 lpTargetFramework; + ULONG cbTargetFramework; + if (SUCCEEDED(cap.ValidateProlog())) + { + if (SUCCEEDED(cap.GetString(&lpTargetFramework, &cbTargetFramework))) + { + if ((lpTargetFramework != NULL) && (cbTargetFramework != 0)) + { + SString s(SString::Utf8, lpTargetFramework, cbTargetFramework); + CLRTraceLog::Logger::LogTargetFrameworkAttribute(s.GetUnicode(), GetSimpleName()); + dataLogged = true; + } + } + } + } + if (!dataLogged) + { + CLRTraceLog::Logger::LogTargetFrameworkAttribute(L"", GetSimpleName()); + } +} + +#endif // FEATURE_TRACELOGGING + //---------------------------------------------------------------------------------------------- // Does most Assembly initialization tasks. It can assume the ctor has already run // and the assembly is safely destructable. Whether this function throws or succeeds, @@ -252,6 +293,13 @@ void Assembly::Init(AllocMemTracker *pamTracker, LoaderAllocator *pLoaderAllocat ReportAssemblyUse(); #endif +#ifdef FEATURE_TRACELOGGING + + TelemetryLogTargetFrameworkAttribute(); + +#endif // FEATURE_TRACELOGGING + + // Check for the special System.Numerics.Vectors assembly. // If we encounter a non-trusted assembly by this name, we will simply not recognize any of its // methods as intrinsics. @@ -2610,7 +2658,7 @@ INT32 Assembly::ExecuteMainMethod(PTRARRAYREF *stringArgs) INJECT_FAULT(COMPlusThrowOM()); } CONTRACTL_END; - + // reset the error code for std C errno=0; @@ -2662,7 +2710,6 @@ INT32 Assembly::ExecuteMainMethod(PTRARRAYREF *stringArgs) AppDomain * pDomain = pThread->GetDomain(); pDomain->SetRootAssembly(pMeth->GetAssembly()); #endif - hr = RunMain(pMeth, 1, &iRetVal, stringArgs); } } diff --git a/src/vm/assembly.hpp b/src/vm/assembly.hpp index 812efb3c2b..1fdc655c02 100644 --- a/src/vm/assembly.hpp +++ b/src/vm/assembly.hpp @@ -126,6 +126,10 @@ public: Assembly(BaseDomain *pDomain, PEAssembly *pFile, DebuggerAssemblyControlFlags debuggerFlags, BOOL fIsCollectible); void Init(AllocMemTracker *pamTracker, LoaderAllocator *pLoaderAllocator); +#if defined(FEATURE_TRACELOGGING) + void TelemetryLogTargetFrameworkAttribute(); +#endif // FEATURE_TRACELOGGING + void StartUnload(); void Terminate( BOOL signalProfiler = TRUE ); diff --git a/src/vm/ceeload.cpp b/src/vm/ceeload.cpp index f5bf61c270..b379d94365 100644 --- a/src/vm/ceeload.cpp +++ b/src/vm/ceeload.cpp @@ -3756,7 +3756,7 @@ void Module::FreeClassTables() if (!th.IsTypeDesc()) { MethodTable *pMT = th.AsMethodTable(); - if (pMT->HasCCWTemplate()) + if (pMT->HasCCWTemplate() && (!pMT->IsZapped() || pMT->GetZapModule() == this)) { // code:MethodTable::GetComCallWrapperTemplate() may go through canonical methodtable indirection cell. // The module load could be aborted before completing code:FILE_LOAD_EAGER_FIXUPS phase that's responsible @@ -3786,7 +3786,7 @@ void Module::FreeClassTables() if (!th.IsTypeDesc()) { MethodTable * pMT = th.AsMethodTable(); - if (pMT->IsCanonicalMethodTable()) + if (pMT->IsCanonicalMethodTable() && (!pMT->IsZapped() || pMT->GetZapModule() == this)) pMT->GetClass()->Destruct(pMT); } } @@ -6502,6 +6502,12 @@ mdTypeRef Module::LookupTypeRefByMethodTable(MethodTable *pMT) #ifdef FEATURE_READYTORUN_COMPILER if (IsReadyToRunCompilation()) { + if (pMT->GetClass()->IsEquivalentType()) + { + GetSvcLogger()->Log(W("ReadyToRun: Type reference to equivalent type cannot be encoded\n")); + ThrowHR(E_NOTIMPL); + } + // FUTURE: Encoding of new cross-module references for ReadyToRun // This warning is hit for recursive cross-module inlining. It is commented out to avoid noise. // GetSvcLogger()->Log(W("ReadyToRun: Type reference outside of current version bubble cannot be encoded\n")); diff --git a/src/vm/clrtracelogging.cpp b/src/vm/clrtracelogging.cpp new file mode 100644 index 0000000000..089e1c43f7 --- /dev/null +++ b/src/vm/clrtracelogging.cpp @@ -0,0 +1,41 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +//***************************************************************************** +// clrttracelogging.cpp +// Telemetry Logging for clr.dll +// +//***************************************************************************** + +#include "common.h" +#include "clrtracelogging.h" +#include "TraceLoggingProvider.h" +#include "MicrosoftTelemetry.h" + +TRACELOGGING_DEFINE_PROVIDER(g_hClrProvider, CLR_PROVIDER_NAME, CLR_PROVIDER_ID, TraceLoggingOptionMicrosoftTelemetry()); + +// Used for initialization and deconstruction. +static CLRTraceLog::Provider g_clrTraceProvider(g_hClrProvider); + +//--- CLRTraceLogProvider + +// static +void CLRTraceLog::Logger::LogTargetFrameworkAttribute(LPCWSTR targetFrameworkAttribute, const char * assemblyName) +{ + STANDARD_VM_CONTRACT; + + EX_TRY + { + TraceLoggingWrite(g_hClrProvider,"CLR.AssemblyInfo", + TraceLoggingWideString(targetFrameworkAttribute, "TARGET_FRAMEWORK_ATTRIBUTE"), + TraceLoggingString(assemblyName, "ASSEMBLY_NAME"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)); + } + EX_CATCH{} + EX_END_CATCH(SwallowAllExceptions) + +} +//--- CLRTraceLog + diff --git a/src/vm/codeman.cpp b/src/vm/codeman.cpp index dc6937f269..922dbcb7b8 100644 --- a/src/vm/codeman.cpp +++ b/src/vm/codeman.cpp @@ -1521,7 +1521,7 @@ BOOL EEJitManager::LoadJIT() // // See the document "RyuJIT Compatibility Fallback Specification.docx" for details. - bool fUseRyuJit = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_UseRyuJit) == 1); // uncached access, since this code is run no more than one time + bool fUseRyuJit = UseRyuJit(); if ((!IsCompilationProcess() || !fUseRyuJit) && // Use RyuJIT for all NGEN, unless we're falling back to JIT64 for everything. (newJitCompiler != nullptr)) // the main JIT must successfully load before we try loading the fallback JIT diff --git a/src/vm/compile.cpp b/src/vm/compile.cpp index 7d7892b3c7..d77a285a04 100644 --- a/src/vm/compile.cpp +++ b/src/vm/compile.cpp @@ -1033,7 +1033,7 @@ HRESULT CEECompileInfo::SetCompilationTarget(CORINFO_ASSEMBLY_HANDLE assembl mscorlib.InitializeSpec(SystemDomain::SystemFile()); GetAppDomain()->BindAssemblySpec(&mscorlib,TRUE,FALSE); - if (!SystemDomain::SystemFile()->HasNativeImage()) + if (!IsReadyToRunCompilation() && !SystemDomain::SystemFile()->HasNativeImage()) { if (!CLRConfig::GetConfigValue(CLRConfig::INTERNAL_NgenAllowMscorlibSoftbind)) { @@ -2527,6 +2527,11 @@ BOOL CEECompileInfo::NeedsTypeLayoutCheck(CORINFO_CLASS_HANDLE classHnd) if (!pMT->IsValueType()) return FALSE; + // Skip this check for equivalent types. Equivalent types are used for interop that ensures + // matching layout. + if (pMT->GetClass()->IsEquivalentType()) + return FALSE; + return !pMT->IsLayoutFixedInCurrentVersionBubble(); } @@ -8191,4 +8196,16 @@ HRESULT CompilationDomain::SetPlatformWinmdPaths(LPCWSTR pwzPlatformWinmdPaths) } #endif // CROSSGEN_COMPILE +#if defined(_TARGET_AMD64_) && !defined(FEATURE_CORECLR) +bool UseRyuJit() +{ +#ifdef CROSSGEN_COMPILE + return true; +#else + static ConfigDWORD useRyuJitValue; + return useRyuJitValue.val(CLRConfig::INTERNAL_UseRyuJit) == 1 || IsNgenOffline(); +#endif +} +#endif + #endif // FEATURE_PREJIT diff --git a/src/vm/coreassemblyspec.cpp b/src/vm/coreassemblyspec.cpp index be3d2841ed..238e359561 100644 --- a/src/vm/coreassemblyspec.cpp +++ b/src/vm/coreassemblyspec.cpp @@ -618,7 +618,7 @@ VOID AssemblySpec::Bind(AppDomain *pAppDomain, { SString sSimpleName(SString::Utf8, m_pAssemblyName); - fNativeImage = pAppDomain->ToCompilationDomain()->IsInHardBindList(sSimpleName); + fNativeImage = !IsReadyToRunCompilation() && pAppDomain->ToCompilationDomain()->IsInHardBindList(sSimpleName); SString sFileName(sSimpleName, fNativeImage ? W(".ni.dll") : W(".dll")); diff --git a/src/vm/crossgen/wks_crossgen.nativeproj b/src/vm/crossgen/wks_crossgen.nativeproj index a688b49304..205f2e2c95 100644 --- a/src/vm/crossgen/wks_crossgen.nativeproj +++ b/src/vm/crossgen/wks_crossgen.nativeproj @@ -157,7 +157,9 @@ <CppCompile Include="$(VmSourcesDir)\CrossgenRoParseTypeName.cpp" Condition="'$(FeatureCominterop)' == 'true'"/> <CppCompile Include="$(VmSourcesDir)\CrossgenRoResolveNamespace.cpp" Condition="'$(FeatureCominterop)' == 'true'"/> </ItemGroup> - + <ItemGroup> + <CppCompile Condition="'$(FeatureTraceLogging)' == 'true'" Include="$(VmSourcesDir)\clrtracelogging.cpp" /> + </ItemGroup> <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\vm\vm.targets" /> </Project> diff --git a/src/vm/domainfile.cpp b/src/vm/domainfile.cpp index 5423ebe63f..a558ee3900 100644 --- a/src/vm/domainfile.cpp +++ b/src/vm/domainfile.cpp @@ -3365,6 +3365,13 @@ void DomainAssembly::GetCurrentVersionInfo(CORCOMPILE_VERSION_INFO *pNativeVersi pNativeVersionInfo->wConfigFlags |= CORCOMPILE_CONFIG_INSTRUMENTATION_NONE; } +#if defined(_TARGET_AMD64_) && !defined(FEATURE_CORECLR) + if (UseRyuJit()) + { + pNativeVersionInfo->wCodegenFlags |= CORCOMPILE_CODEGEN_USE_RYUJIT; + } +#endif + GetTimeStampsForNativeImage(pNativeVersionInfo); // Store signature of source assembly. diff --git a/src/vm/dwreport.cpp b/src/vm/dwreport.cpp index 210e08240c..ccf41ea953 100644 --- a/src/vm/dwreport.cpp +++ b/src/vm/dwreport.cpp @@ -2776,52 +2776,71 @@ DWORD WINAPI DoFaultReportWorkerCallback(LPVOID pParam) } EX_END_CATCH(SwallowAllExceptions); } - - SetupThread(); - - GCX_COOP(); - if (pData->pThread != NULL && pExceptionInfo != NULL && - pExceptionInfo->ContextRecord == NULL && - pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW && - pExceptionInfo->ExceptionRecord->ExceptionAddress == 0) + // The purpose of the loop below is to avoid deadlocks during the abnormal process termination. + // We will try to acquire the lock for 100 times to see whether we can successfully grap it. If we + // can, then we can setup the thread and report the fault without worrying about the deadlock. + // Otherwise we won't report the fault. It's still possible that we can enter the critical section + // and report the fault without having deadlocks after this spin, but compared to the risky of + // having deadlock, we still prefer not to report the fault if we can't get the lock after spin. + BOOL isThreadSetup = false; + for (int i = 0; i < 100; i++) + { + if (ThreadStore::CanAcquireLock()) + { + SetupThread(); + isThreadSetup = true; + break; + } + __SwitchToThread(30, CALLER_LIMITS_SPINNING); + } + + if (isThreadSetup) { - // In the case of a soft SO on a managed thread, we set the ExceptionAddress to one of the following - // - // 1. The first method on the stack that is in a non-system module. - // 2. Failing that, the first method on the stack that is in a system module + GCX_COOP(); + + if (pData->pThread != NULL && pExceptionInfo != NULL && + pExceptionInfo->ContextRecord == NULL && + pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW && + pExceptionInfo->ExceptionRecord->ExceptionAddress == 0) + { + // In the case of a soft SO on a managed thread, we set the ExceptionAddress to one of the following + // + // 1. The first method on the stack that is in a non-system module. + // 2. Failing that, the first method on the stack that is in a system module - CONTEXT ContextRecord; - memset(&ContextRecord, 0, sizeof(CONTEXT)); + CONTEXT ContextRecord; + memset(&ContextRecord, 0, sizeof(CONTEXT)); - ExceptionInfo.ContextRecord = &ContextRecord; // To display the "Send" button, dw20 wants a non-NULL pointer - ExceptionRecord = *(pExceptionInfo->ExceptionRecord); - ExceptionInfo.ExceptionRecord = &ExceptionRecord; - pExceptionInfo = &ExceptionInfo; + ExceptionInfo.ContextRecord = &ContextRecord; // To display the "Send" button, dw20 wants a non-NULL pointer + ExceptionRecord = *(pExceptionInfo->ExceptionRecord); + ExceptionInfo.ExceptionRecord = &ExceptionRecord; + pExceptionInfo = &ExceptionInfo; - WatsonSOExceptionAddress WatsonExceptionAddresses; + WatsonSOExceptionAddress WatsonExceptionAddresses; - pData->pThread->StackWalkFrames( - WatsonSOStackCrawlCallback, - &WatsonExceptionAddresses, - FUNCTIONSONLY|ALLOW_ASYNC_STACK_WALK); + pData->pThread->StackWalkFrames( + WatsonSOStackCrawlCallback, + &WatsonExceptionAddresses, + FUNCTIONSONLY|ALLOW_ASYNC_STACK_WALK); - if (WatsonExceptionAddresses.m_UserMethod != NULL) - { - pExceptionInfo->ExceptionRecord->ExceptionAddress = WatsonExceptionAddresses.m_UserMethod; - } - else if (WatsonExceptionAddresses.m_SystemMethod != NULL) - { - pExceptionInfo->ExceptionRecord->ExceptionAddress = WatsonExceptionAddresses.m_SystemMethod; - } + if (WatsonExceptionAddresses.m_UserMethod != NULL) + { + pExceptionInfo->ExceptionRecord->ExceptionAddress = WatsonExceptionAddresses.m_UserMethod; + } + else if (WatsonExceptionAddresses.m_SystemMethod != NULL) + { + pExceptionInfo->ExceptionRecord->ExceptionAddress = WatsonExceptionAddresses.m_SystemMethod; + } - } + } - pData->result = DoFaultReportWorker( - pExceptionInfo, - pData->tore, - pData->pThread, - pData->dwThreadID); + pData->result = DoFaultReportWorker( + pExceptionInfo, + pData->tore, + pData->pThread, + pData->dwThreadID); + } return 0; @@ -3162,6 +3181,11 @@ FaultReportResult DoFaultReport( // Was Watson attempted, successful? GCX_PREEMP(); if (!g_pDebugInterface || + // When GC is in progress and current thread is either a GC thread or a managed + // thread under Coop mode, this will let the new generated DoFaultReportCallBack + // thread trigger a deadlock. So in this case, we should directly abort the fault + // report to avoid the deadlock. + ((IsGCThread() || pThread->PreemptiveGCDisabled()) && GCHeap::IsGCInProgress()) || FAILED(g_pDebugInterface->RequestFavor(DoFaultReportFavorWorker, pData))) { // If we can't initialize the debugger helper thread or we are running on the debugger helper diff --git a/src/vm/eetwain.cpp b/src/vm/eetwain.cpp index 84b267e806..5df7b6305a 100644 --- a/src/vm/eetwain.cpp +++ b/src/vm/eetwain.cpp @@ -4090,7 +4090,8 @@ bool EECodeManager::EnumGcRefs( PREGDISPLAY pContext, EECodeInfo *pCodeInfo, unsigned flags, GCEnumCallback pCallBack, - LPVOID hCallBack) + LPVOID hCallBack, + DWORD relOffsetOverride) { CONTRACTL { NOTHROW; @@ -4731,7 +4732,8 @@ bool EECodeManager::EnumGcRefs( PREGDISPLAY pRD, EECodeInfo *pCodeInfo, unsigned flags, GCEnumCallback pCallBack, - LPVOID hCallBack) + LPVOID hCallBack, + DWORD relOffsetOverride) { CONTRACTL { NOTHROW; @@ -4796,13 +4798,13 @@ bool EECodeManager::EnumGcRefs( PREGDISPLAY pRD, #ifdef USE_GC_INFO_DECODER /* If we are not in the active method, we are currently pointing - * to the return address; at the return address stack variables - * can become dead if the call is the last instruction of a try block - * and the return address is the jump around the catch block. Therefore - * we simply assume an offset inside of call instruction. - * NOTE: The GcInfoDecoder depends on this; if you change it, you must - * revisit the GcInfoEncoder/Decoder - */ + * to the return address; at the return address stack variables + * can become dead if the call is the last instruction of a try block + * and the return address is the jump around the catch block. Therefore + * we simply assume an offset inside of call instruction. + * NOTE: The GcInfoDecoder depends on this; if you change it, you must + * revisit the GcInfoEncoder/Decoder + */ if (!(flags & ExecutionAborted)) { @@ -4827,6 +4829,34 @@ bool EECodeManager::EnumGcRefs( PREGDISPLAY pRD, methodName, curOffs)); } } + + // Check if we have been given an override value for relOffset + if (relOffsetOverride != NO_OVERRIDE_OFFSET) + { + // We've been given an override offset for GC Info +#ifdef _DEBUG + GcInfoDecoder _gcInfoDecoder( + gcInfoAddr, + DECODE_CODE_LENGTH, + 0 + ); + + // We only use override offset for wantsReportOnlyLeaf + _ASSERTE(_gcInfoDecoder.WantsReportOnlyLeaf()); +#endif // _DEBUG + + curOffs = relOffsetOverride; + +#ifdef _TARGET_ARM_ + // On ARM, the low-order bit of an instruction pointer indicates Thumb vs. ARM mode. + // Mask this off; all instructions are two-byte aligned. + curOffs &= (~THUMB_CODE); +#endif // _TARGET_ARM_ + + LOG((LF_GCINFO, LL_INFO1000, "Adjusted GC reporting offset to provided override offset. Now reporting GC refs for %s at offset %04x.\n", + methodName, curOffs)); + } + #endif // USE_GC_INFO_DECODER #if defined(WIN64EXCEPTIONS) // funclets @@ -4948,7 +4978,8 @@ bool EECodeManager::EnumGcRefs( PREGDISPLAY pContext, EECodeInfo *pCodeInfo, unsigned flags, GCEnumCallback pCallBack, - LPVOID hCallBack) + LPVOID hCallBack, + DWORD relOffsetOverride) { PORTABILITY_ASSERT("EECodeManager::EnumGcRefs is not implemented on this platform."); return false; diff --git a/src/vm/eventtrace.cpp b/src/vm/eventtrace.cpp index c55c378005..dab4436923 100644 --- a/src/vm/eventtrace.cpp +++ b/src/vm/eventtrace.cpp @@ -4599,6 +4599,145 @@ VOID ETW::ExceptionLog::ExceptionThrown(CrawlFrame *pCf, BOOL bIsReThrownExcept } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions); } + +VOID ETW::ExceptionLog::ExceptionThrownEnd() +{ + CONTRACTL{ + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + if (!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ExceptionThrownStop)) + { + return; + } + + FireEtwExceptionThrownStop(); +} + +/****************************************************************************/ +/* This is called by the runtime when an exception is handled by the runtime */ +/****************************************************************************/ +VOID ETW::ExceptionLog::ExceptionCatchBegin(MethodDesc * pMethodDesc, PVOID pEntryEIP) +{ + CONTRACTL{ + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + if (!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ExceptionCatchStart)) + { + return; + } + + EX_TRY + { + SString methodName; + pMethodDesc->GetFullMethodInfo(methodName); + + FireEtwExceptionCatchStart((uint64_t)pEntryEIP, + (uint64_t)pMethodDesc, + methodName.GetUnicode(), + GetClrInstanceId()); + + } EX_CATCH{} EX_END_CATCH(SwallowAllExceptions); +} + +VOID ETW::ExceptionLog::ExceptionCatchEnd() +{ + CONTRACTL{ + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + if (!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ExceptionCatchStop)) + { + return; + } + + FireEtwExceptionCatchStop(); +} + +VOID ETW::ExceptionLog::ExceptionFinallyBegin(MethodDesc * pMethodDesc, PVOID pEntryEIP) +{ + CONTRACTL{ + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + if (!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ExceptionFinallyStart)) + { + return; + } + + EX_TRY + { + SString methodName; + pMethodDesc->GetFullMethodInfo(methodName); + + FireEtwExceptionFinallyStart((uint64_t)pEntryEIP, + (uint64_t)pMethodDesc, + methodName.GetUnicode(), + GetClrInstanceId()); + + } EX_CATCH{} EX_END_CATCH(SwallowAllExceptions); +} + +VOID ETW::ExceptionLog::ExceptionFinallyEnd() +{ + CONTRACTL{ + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + if (!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ExceptionFinallyStop)) + { + return; + } + + FireEtwExceptionFinallyStop(); +} + +VOID ETW::ExceptionLog::ExceptionFilterBegin(MethodDesc * pMethodDesc, PVOID pEntryEIP) +{ + CONTRACTL{ + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + if (!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ExceptionFilterStart)) + { + return; + } + + EX_TRY + { + SString methodName; + pMethodDesc->GetFullMethodInfo(methodName); + + FireEtwExceptionFilterStart((uint64_t)pEntryEIP, + (uint64_t)pMethodDesc, + methodName.GetUnicode(), + GetClrInstanceId()); + + } EX_CATCH{} EX_END_CATCH(SwallowAllExceptions); +} + +VOID ETW::ExceptionLog::ExceptionFilterEnd() +{ + CONTRACTL{ + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + if (!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ExceptionFilterStop)) + { + return; + } + + FireEtwExceptionFilterStop(); +} + /****************************************************************************/ /* This is called by the runtime when a domain is loaded */ /****************************************************************************/ diff --git a/src/vm/exceptionhandling.cpp b/src/vm/exceptionhandling.cpp index 02f60915c4..37f1369c5d 100644 --- a/src/vm/exceptionhandling.cpp +++ b/src/vm/exceptionhandling.cpp @@ -2053,6 +2053,13 @@ bool ExceptionTracker::HandleNestedExceptionEscape(StackFrame sf, bool fIsFirstP if (!fIsFirstPass) { + + // During unwind, at each frame we collapse exception trackers only once i.e. there cannot be multiple + // exception trackers that are collapsed at each frame. Store the information of collapsed exception + // tracker in current tracker to be able to find the parent frame when nested exception escapes. + m_csfEHClauseOfCollapsedTracker = m_pPrevNestedInfo->m_EHClauseInfo.GetCallerStackFrameForEHClause(); + m_EnclosingClauseInfoOfCollapsedTracker = m_pPrevNestedInfo->m_EnclosingClauseInfoForGCReporting; + EH_LOG((LL_INFO100, " - removing previous tracker\n")); ExceptionTracker* pTrackerToFree = m_pPrevNestedInfo; @@ -3258,6 +3265,19 @@ DWORD_PTR ExceptionTracker::CallHandler( this->m_EHClauseInfo.SetManagedCodeEntered(TRUE); this->m_EHClauseInfo.SetCallerStackFrame(csfFunclet); + switch(funcletType) + { + case EHFuncletType::Filter: + ETW::ExceptionLog::ExceptionFilterBegin(pMD, (PVOID)uHandlerStartPC); + break; + case EHFuncletType::FaultFinally: + ETW::ExceptionLog::ExceptionFinallyBegin(pMD, (PVOID)uHandlerStartPC); + break; + case EHFuncletType::Catch: + ETW::ExceptionLog::ExceptionCatchBegin(pMD, (PVOID)uHandlerStartPC); + break; + } + #if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_) // Invoke the funclet. We pass throwable only when invoking the catch block. // Since the actual caller of the funclet is the assembly helper, pass the reference @@ -3297,6 +3317,20 @@ DWORD_PTR ExceptionTracker::CallHandler( dwResumePC = pfnHandler(sf.SP, OBJECTREFToObject(throwable)); #endif // _TARGET_ARM_ + switch(funcletType) + { + case EHFuncletType::Filter: + ETW::ExceptionLog::ExceptionFilterEnd(); + break; + case EHFuncletType::FaultFinally: + ETW::ExceptionLog::ExceptionFinallyEnd(); + break; + case EHFuncletType::Catch: + ETW::ExceptionLog::ExceptionCatchEnd(); + ETW::ExceptionLog::ExceptionThrownEnd(); + break; + } + this->m_EHClauseInfo.SetManagedCodeEntered(FALSE); END_SO_TOLERANT_CODE; @@ -6213,7 +6247,11 @@ bool ExceptionTracker::HasFrameBeenUnwoundByAnyActiveException(CrawlFrame * pCF) // For case (2) above, sfLastUnwoundEstbalisherFrame would be the same as the managed frame's SP (or upper bound) // // For these scenarios, the frame is considered unwound. - if (GetRegdisplaySP(pCF->GetRegisterSet()) == sfUpperBound.SP) + + // For most cases which satisfy above condition GetRegdisplaySP(pCF->GetRegisterSet()) will be equal to sfUpperBound.SP. + // However, frames where Sp is modified after prolog ( eg. localloc) this might not be the case. For those scenarios, + // we need to check if sfUpperBound.SP is in between GetRegdisplaySP(pCF->GetRegisterSet()) & callerSp. + if (GetRegdisplaySP(pCF->GetRegisterSet()) <= sfUpperBound.SP && sfUpperBound < csfToCheck) { if (csfToCheck == sfCurrentEstablisherFrame) { @@ -6605,7 +6643,7 @@ StackFrame ExceptionTracker::FindParentStackFrameHelper(CrawlFrame* pCF, // Since the current frame is a non-filter funclet, determine if its caller is the same one // as was saved against the exception tracker before the funclet was invoked in ExceptionTracker::CallHandler. CallerStackFrame csfFunclet = pCurrentTracker->m_EHClauseInfo.GetCallerStackFrameForEHClause(); - if (csfCurrent == csfFunclet) + if (csfCurrent == csfFunclet) { // The EnclosingClauseCallerSP is initialized in ExceptionTracker::ProcessManagedCallFrame, just before // invoking the funclets. Basically, we are using the SP of the caller of the frame containing the funclet @@ -6636,6 +6674,17 @@ StackFrame ExceptionTracker::FindParentStackFrameHelper(CrawlFrame* pCF, break; } + // Check if this tracker was collapsed with another tracker and if caller of funclet clause for collapsed exception tracker matches. + else if (fForGCReporting && !(pCurrentTracker->m_csfEHClauseOfCollapsedTracker.IsNull()) && csfCurrent == pCurrentTracker->m_csfEHClauseOfCollapsedTracker) + { + EnclosingClauseInfo srcEnclosingClause = pCurrentTracker->m_EnclosingClauseInfoOfCollapsedTracker; + sfResult = (StackFrame)(CallerStackFrame(srcEnclosingClause.GetEnclosingClauseCallerSP())); + + _ASSERTE(!sfResult.IsNull()); + + break; + + } } lExit: ; diff --git a/src/vm/exceptionhandling.h b/src/vm/exceptionhandling.h index 72db3576a3..0f9e962ffe 100644 --- a/src/vm/exceptionhandling.h +++ b/src/vm/exceptionhandling.h @@ -108,6 +108,7 @@ public: m_sfLastUnwoundEstablisherFrame.Clear(); m_pInitialExplicitFrame = NULL; m_pLimitFrame = NULL; + m_csfEHClauseOfCollapsedTracker.Clear(); } ExceptionTracker(DWORD_PTR dwExceptionPc, @@ -166,6 +167,7 @@ public: m_sfCurrentEstablisherFrame.Clear(); m_sfLastUnwoundEstablisherFrame.Clear(); m_pInitialExplicitFrame = NULL; + m_csfEHClauseOfCollapsedTracker.Clear(); } ~ExceptionTracker() @@ -500,6 +502,11 @@ public: return m_uCatchToCallPC; } + EE_ILEXCEPTION_CLAUSE GetEHClauseForCatch() + { + return m_ClauseForCatch; + } + // Returns the topmost frame seen during the first pass. StackFrame GetTopmostStackFrameFromFirstPass() { @@ -757,6 +764,8 @@ private: ; StackFrame m_sfCurrentEstablisherFrame; StackFrame m_sfLastUnwoundEstablisherFrame; PTR_Frame m_pInitialExplicitFrame; + CallerStackFrame m_csfEHClauseOfCollapsedTracker; + EnclosingClauseInfo m_EnclosingClauseInfoOfCollapsedTracker; }; #if defined(WIN64EXCEPTIONS) diff --git a/src/vm/gcenv.cpp b/src/vm/gcenv.cpp index 68eee622f5..ea7d634ecf 100644 --- a/src/vm/gcenv.cpp +++ b/src/vm/gcenv.cpp @@ -146,6 +146,74 @@ inline bool SafeToReportGenericParamContext(CrawlFrame* pCF) return true; } +#if defined(WIN64EXCEPTIONS) + +struct FindFirstInterruptiblePointState +{ + unsigned offs; + unsigned endOffs; + unsigned returnOffs; +}; + +bool FindFirstInterruptiblePointStateCB( + UINT32 startOffset, + UINT32 stopOffset, + LPVOID hCallback) +{ + FindFirstInterruptiblePointState* pState = (FindFirstInterruptiblePointState*)hCallback; + + _ASSERTE(startOffset < stopOffset); + _ASSERTE(pState->offs < pState->endOffs); + + if (stopOffset <= pState->offs) + { + // The range ends before the requested offset. + return false; + } + + // The offset is in the range. + if (startOffset <= pState->offs && + pState->offs < stopOffset) + { + pState->returnOffs = pState->offs; + return true; + } + + // The range is completely after the desired offset. We use the range start offset, if + // it comes before the given endOffs. We assume that the callback is called with ranges + // in increasing order, so earlier ones are reported before later ones. That is, if we + // get to this case, it will be the closest interruptible range after the requested + // offset. + + _ASSERTE(pState->offs < startOffset); + if (startOffset < pState->endOffs) + { + pState->returnOffs = startOffset; + return true; + } + + return false; +} + +// Find the first interruptible point in the range [offs .. endOffs) (the beginning of the range is inclusive, +// the end is exclusive). Return -1 if no such point exists. +unsigned FindFirstInterruptiblePoint(CrawlFrame* pCF, unsigned offs, unsigned endOffs) +{ + PTR_BYTE gcInfoAddr = dac_cast<PTR_BYTE>(pCF->GetCodeInfo()->GetGCInfo()); + GcInfoDecoder gcInfoDecoder(gcInfoAddr, DECODE_FOR_RANGES_CALLBACK, 0); + + FindFirstInterruptiblePointState state; + state.offs = offs; + state.endOffs = endOffs; + state.returnOffs = -1; + + gcInfoDecoder.EnumerateInterruptibleRanges(&FindFirstInterruptiblePointStateCB, &state); + + return state.returnOffs; +} + +#endif // WIN64EXCEPTIONS + //----------------------------------------------------------------------------- StackWalkAction GcStackCrawlCallBack(CrawlFrame* pCF, VOID* pData) { @@ -209,15 +277,47 @@ StackWalkAction GcStackCrawlCallBack(CrawlFrame* pCF, VOID* pData) pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName)); #endif // _DEBUG -#if 0 - printf("Scanning Frame for method %s\n", pMD->m_pszDebugMethodName); -#endif // _DEBUG + DWORD relOffsetOverride = NO_OVERRIDE_OFFSET; +#if defined(WIN64EXCEPTIONS) + if (pCF->ShouldParentToFuncletUseUnwindTargetLocationForGCReporting()) + { + PTR_BYTE gcInfoAddr = dac_cast<PTR_BYTE>(pCF->GetCodeInfo()->GetGCInfo()); + GcInfoDecoder _gcInfoDecoder( + gcInfoAddr, + DECODE_CODE_LENGTH, + 0 + ); + + if(_gcInfoDecoder.WantsReportOnlyLeaf()) + { + // We're in a special case of unwinding from a funclet, and resuming execution in + // another catch funclet associated with same parent function. We need to report roots. + // Reporting at the original throw site gives incorrect liveness information. We choose to + // report the liveness information at the first interruptible instruction of the catch funclet + // that we are going to execute. We also only report stack slots, since no registers can be + // live at the first instruction of a handler, except the catch object, which the VM protects + // specially. If the catch funclet has not interruptible point, we fall back and just report + // what we used to: at the original throw instruction. This might lead to bad GC behavior + // if the liveness is not correct. + const EE_ILEXCEPTION_CLAUSE& ehClauseForCatch = pCF->GetEHClauseForCatch(); + relOffsetOverride = FindFirstInterruptiblePoint(pCF, ehClauseForCatch.HandlerStartPC, + ehClauseForCatch.HandlerEndPC); + _ASSERTE(relOffsetOverride != NO_OVERRIDE_OFFSET); + + STRESS_LOG3(LF_GCROOTS, LL_INFO1000, "Setting override offset = %u for method %pM ControlPC = %p\n", + relOffsetOverride, pMD, GetControlPC(pCF->GetRegisterSet())); + } + + } +#endif // WIN64EXCEPTIONS pCM->EnumGcRefs(pCF->GetRegisterSet(), pCF->GetCodeInfo(), flags, GcEnumObject, - pData); + pData, + relOffsetOverride); + } else { diff --git a/src/vm/i386/excepx86.cpp b/src/vm/i386/excepx86.cpp index ca7a18d8c3..6bf26a4790 100644 --- a/src/vm/i386/excepx86.cpp +++ b/src/vm/i386/excepx86.cpp @@ -1945,6 +1945,9 @@ LPVOID STDCALL COMPlusEndCatch(LPVOID ebp, DWORD ebx, DWORD edi, DWORD esi, LPVO STATIC_CONTRACT_MODE_COOPERATIVE; STATIC_CONTRACT_SO_INTOLERANT; + ETW::ExceptionLog::ExceptionCatchEnd(); + ETW::ExceptionLog::ExceptionThrownEnd(); + void* esp = COMPlusEndCatchWorker(GetThread()); // We are going to resume at a handler nesting level whose esp is dEsp. Pop off any SEH records below it. This @@ -3324,6 +3327,8 @@ void ResumeAtJitEH(CrawlFrame* pCf, // that the handle for the current ExInfo has been freed has been delivered pExInfo->m_EHClauseInfo.SetManagedCodeEntered(TRUE); + ETW::ExceptionLog::ExceptionCatchBegin(pCf->GetCodeInfo()->GetMethodDesc(), (PVOID)pCf->GetCodeInfo()->GetStartAddress()); + ResumeAtJitEHHelper(&context); UNREACHABLE_MSG("Should never return from ResumeAtJitEHHelper!"); @@ -3394,8 +3399,13 @@ int CallJitEHFilter(CrawlFrame* pCf, BYTE* startPC, EE_ILEXCEPTION_CLAUSE *EHCla // returning from UnwindFrames. FrameWithCookie<ExceptionFilterFrame> exceptionFilterFrame(pShadowSP); + + ETW::ExceptionLog::ExceptionFilterBegin(pCf->GetCodeInfo()->GetMethodDesc(), (PVOID)pCf->GetCodeInfo()->GetStartAddress()); + retVal = CallJitEHFilterWorker(pShadowSP, &context); + ETW::ExceptionLog::ExceptionFilterEnd(); + exceptionFilterFrame.Pop(); return retVal; @@ -3421,8 +3431,12 @@ void CallJitEHFinally(CrawlFrame* pCf, BYTE* startPC, EE_ILEXCEPTION_CLAUSE *EHC *pFinallyEnd = EHClausePtr->HandlerEndPC; } + ETW::ExceptionLog::ExceptionFinallyBegin(pCf->GetCodeInfo()->GetMethodDesc(), (PVOID)pCf->GetCodeInfo()->GetStartAddress()); + CallJitEHFinallyHelper(pShadowSP, &context); + ETW::ExceptionLog::ExceptionFinallyEnd(); + // // Update the registers using new context // diff --git a/src/vm/i386/stublinkerx86.cpp b/src/vm/i386/stublinkerx86.cpp index eb744615eb..e42f7d792f 100644 --- a/src/vm/i386/stublinkerx86.cpp +++ b/src/vm/i386/stublinkerx86.cpp @@ -5076,14 +5076,14 @@ VOID StubLinkerCPU::EmitArrayOpStub(const ArrayOpScript* pArrayOpScript) X86EmitOp(0x8b, kEAX, kValueReg, 0 AMD64_ARG(k64BitOp)); // mov EAX, [kValueReg] ; possibly trashes kValueReg // cmp EAX, [ESI/R10+m_ElementType] - X86_64BitOperands(); - X86EmitOp(0x3b, kEAX, kArrayMTReg, MethodTable::GetOffsetOfArrayElementTypeHandle()); + + X86EmitOp(0x3b, kEAX, kArrayMTReg, MethodTable::GetOffsetOfArrayElementTypeHandle() AMD64_ARG(k64BitOp)); X86EmitCondJump(CheckPassed, X86CondCode::kJZ); // Exact match is OK X86EmitRegLoad(kEAX, (UINT_PTR)g_pObjectClass); // mov EAX, g_pObjectMethodTable // cmp EAX, [ESI/R10+m_ElementType] - X86_64BitOperands(); - X86EmitOp(0x3b, kEAX, kArrayMTReg, MethodTable::GetOffsetOfArrayElementTypeHandle()); + + X86EmitOp(0x3b, kEAX, kArrayMTReg, MethodTable::GetOffsetOfArrayElementTypeHandle() AMD64_ARG(k64BitOp)); X86EmitCondJump(CheckPassed, X86CondCode::kJZ); // Assigning to array of object is OK // Try to call the fast helper first ( ObjIsInstanceOfNoGC ). diff --git a/src/vm/methodtablebuilder.cpp b/src/vm/methodtablebuilder.cpp index 02846290f1..e1d2dbb2e5 100644 --- a/src/vm/methodtablebuilder.cpp +++ b/src/vm/methodtablebuilder.cpp @@ -11807,11 +11807,15 @@ BOOL MethodTableBuilder::NeedsAlignedBaseOffset() if (IsValueClass()) return FALSE; - // READYTORUN: TODO: This logic is not correct when NGen image depends on ReadyToRun image. In this case, - // GetModule()->IsReadyToRun() flag is going to be false at NGen time, but it is going to be true at runtime. - // Thus, the offsets between the two cases are going to be different. - if (!(IsReadyToRunCompilation() || GetModule()->IsReadyToRun())) - return FALSE; + // Always use the ReadyToRun field layout algorithm if the source IL image was ReadyToRun, independent on + // whether ReadyToRun is actually enabled for the module. It is required to allow mixing and matching + // ReadyToRun images with NGen. + if (!GetModule()->GetFile()->IsILImageReadyToRun()) + { + // Always use ReadyToRun field layout algorithm to produce ReadyToRun images + if (!IsReadyToRunCompilation()) + return FALSE; + } MethodTable * pParentMT = GetParentMethodTable(); diff --git a/src/vm/pefile.cpp b/src/vm/pefile.cpp index 815071c486..658d7f5ca6 100644 --- a/src/vm/pefile.cpp +++ b/src/vm/pefile.cpp @@ -1948,6 +1948,13 @@ static BOOL RuntimeVerifyNativeImageTimestamps(const CORCOMPILE_VERSION_INFO *in if (hMod == NULL) { + // Unless this is an NGen worker process, we don't want to load JIT compiler just to do a timestamp check. + // In an ideal case, all assemblies have native images, and JIT compiler never needs to be loaded at runtime. + // Loading JIT compiler just to check its timestamp would reduce the benefits of have native images. + // Since CLR and JIT are intended to be serviced together, the possibility of accidentally using native + // images created by an older JIT is very small, and is deemed an acceptable risk. + // Note that when multiple JIT compilers are used (e.g., clrjit.dll and compatjit.dll on x64 in .NET 4.6), + // they must all be in the same patch family. if (!IsCompilationProcess()) continue; @@ -2160,6 +2167,19 @@ BOOL RuntimeVerifyNativeImageVersion(const CORCOMPILE_VERSION_INFO *info, Loggab } #endif // CROSSGEN_COMPILE +#if defined(_TARGET_AMD64_) && !defined(FEATURE_CORECLR) + // + // Check the right JIT compiler + // + + bool nativeImageBuiltWithRyuJit = ((info->wCodegenFlags & CORCOMPILE_CODEGEN_USE_RYUJIT) != 0); + if (UseRyuJit() != nativeImageBuiltWithRyuJit) + { + RuntimeVerifyLog(LL_ERROR, pLogAsm, W("JIT compiler used to generate native image doesn't match current JIT compiler.")); + return FALSE; + } +#endif + // // The zap is up to date. // diff --git a/src/vm/pefile.h b/src/vm/pefile.h index 8f8a5f3588..4a64d9aec0 100644 --- a/src/vm/pefile.h +++ b/src/vm/pefile.h @@ -303,6 +303,7 @@ public: BOOL HasSecurityDirectory(); BOOL IsIbcOptimized(); + BOOL IsILImageReadyToRun(); WORD GetSubsystem(); mdToken GetEntryPointToken( #ifdef _DEBUG diff --git a/src/vm/pefile.inl b/src/vm/pefile.inl index d43d2b7ca2..07d42d5859 100644 --- a/src/vm/pefile.inl +++ b/src/vm/pefile.inl @@ -772,6 +772,28 @@ inline BOOL PEFile::IsIbcOptimized() return FALSE; } +inline BOOL PEFile::IsILImageReadyToRun() +{ + WRAPPER_NO_CONTRACT; + +#ifdef FEATURE_PREJIT + if (IsNativeLoaded()) + { + CONSISTENCY_CHECK(HasNativeImage()); + + return GetLoadedNative()->GetNativeILHasReadyToRunHeader(); + } + else +#endif // FEATURE_PREJIT + if (HasOpenedILimage()) + { + return GetLoadedIL()->HasReadyToRunHeader(); + } + else + { + return FALSE; + } +} inline WORD PEFile::GetSubsystem() { diff --git a/src/vm/peimage.cpp b/src/vm/peimage.cpp index 66f89db6e0..705f165d00 100644 --- a/src/vm/peimage.cpp +++ b/src/vm/peimage.cpp @@ -1547,7 +1547,14 @@ PTR_PEImageLayout PEImage::GetLayoutInternal(DWORD imageLayoutMask,DWORD flags) SetLayout(IMAGE_FLAT,pFlatLayout); pLoadLayout = new ConvertedImageLayout(pFlatLayout); } -#endif +#ifdef FEATURE_READYTORUN + else if (ReadyToRunInfo::IsReadyToRunEnabled() && IsFile()) + { + pLoadLayout = PEImageLayout::Load(this, FALSE, FALSE); + } +#endif // FEATURE_READYTORUN + +#endif // FEATURE_CORECLR if (pLoadLayout != NULL) { diff --git a/src/vm/stackwalk.cpp b/src/vm/stackwalk.cpp index d39ce06ef4..3301dec092 100644 --- a/src/vm/stackwalk.cpp +++ b/src/vm/stackwalk.cpp @@ -1510,6 +1510,7 @@ void StackFrameIterator::ResetCrawlFrame() m_crawl.isFilterFunclet = false; m_crawl.isFilterFuncletCached = false; m_crawl.fShouldParentToFuncletSkipReportingGCReferences = false; + m_crawl.fShouldParentFrameUseUnwindTargetPCforGCReporting = false; #endif // WIN64EXCEPTIONS m_crawl.pThread = this->m_pThread; @@ -1669,6 +1670,10 @@ StackWalkAction StackFrameIterator::Filter(void) // CrawlFrame m_crawl.fShouldCrawlframeReportGCReferences = true; + // By default, assume that parent frame is going to report GC references from + // the actual location reported by the stack walk. + m_crawl.fShouldParentFrameUseUnwindTargetPCforGCReporting = false; + if (!m_sfParent.IsNull()) { // we are now skipping frames to get to the funclet's parent @@ -1887,7 +1892,7 @@ ProcessFuncletsForGCReporting: _ASSERTE(m_fDidFuncletReportGCReferences); m_fDidFuncletReportGCReferences = false; - + STRESS_LOG0(LF_GCROOTS, LL_INFO100, "Unwound funclet will skip reporting references\n"); } } @@ -1913,6 +1918,9 @@ ProcessFuncletsForGCReporting: if (m_sfParent.IsMaxVal() || ExceptionTracker::IsUnwoundToTargetParentFrame(&m_crawl, m_sfParent)) { + // Reset flag as we have reached target method frame so no more skipping required + fSkippingFunclet = false; + // We've finished skipping as told. Now check again. if ((m_fProcessIntermediaryNonFilterFunclet == true) || (m_fProcessNonFilterFunclet == true)) @@ -1997,6 +2005,23 @@ ProcessFuncletsForGCReporting: // now that we've found the parent that will report roots reset our state. m_fDidFuncletReportGCReferences = true; + + // After funclet gets unwound parent will begin to report gc references. Reporting GC references + // using the IP of throw in parent method can crash application. Parent could have locals objects + // which might not have been reported by funclet as live and would have already been collected + // when funclet was on stack. Now if parent starts using IP of throw to report gc references it + // would report garbage values as live objects. So instead parent can use the IP of the resume + // address of catch funclet to report live GC references. + m_crawl.fShouldParentFrameUseUnwindTargetPCforGCReporting = true; + // Store catch clause info. Helps retrieve IP of resume address. + m_crawl.ehClauseForCatch = pTracker->GetEHClauseForCatch(); + + STRESS_LOG3(LF_GCROOTS, LL_INFO100, + "STACKWALK: Parent of funclet which didn't report GC roots is handling an exception at 0x%p" + "(EH handler range [%x, %x) ), so we need to specially report roots to ensure variables alive" + " in its handler stay live.\n", + pTracker->GetCatchToCallPC(), m_crawl.ehClauseForCatch.HandlerStartPC, + m_crawl.ehClauseForCatch.HandlerEndPC); } else if (!m_crawl.IsFunclet()) { diff --git a/src/vm/stackwalk.h b/src/vm/stackwalk.h index 7fd882d3b6..d92cb9e374 100644 --- a/src/vm/stackwalk.h +++ b/src/vm/stackwalk.h @@ -405,6 +405,17 @@ public: return fShouldCrawlframeReportGCReferences; } + + bool ShouldParentToFuncletUseUnwindTargetLocationForGCReporting() + { + LIMITED_METHOD_CONTRACT; + return fShouldParentFrameUseUnwindTargetPCforGCReporting; + } + + const EE_ILEXCEPTION_CLAUSE& GetEHClauseForCatch() + { + return ehClauseForCatch; + } #endif // WIN64EXCEPTIONS @@ -452,6 +463,8 @@ private: bool isFilterFuncletCached; bool fShouldParentToFuncletSkipReportingGCReferences; bool fShouldCrawlframeReportGCReferences; + bool fShouldParentFrameUseUnwindTargetPCforGCReporting; + EE_ILEXCEPTION_CLAUSE ehClauseForCatch; #endif //WIN64EXCEPTIONS Thread* pThread; diff --git a/src/vm/threaddebugblockinginfo.cpp b/src/vm/threaddebugblockinginfo.cpp index a77c69b457..6ff0bab3e7 100644 --- a/src/vm/threaddebugblockinginfo.cpp +++ b/src/vm/threaddebugblockinginfo.cpp @@ -82,10 +82,17 @@ m_pThread(pThread) #endif //DACCESS_COMPILE // Holder destructor pops a blocking item off the blocking info stack +// NOTE: optimizations are disabled to work around a codegen bug on x86 #ifndef DACCESS_COMPILE +#ifdef _TARGET_X86_ +#pragma optimize( "", off ) +#endif // _TARGET_X86_ DebugBlockingItemHolder::~DebugBlockingItemHolder() { LIMITED_METHOD_CONTRACT; m_pThread->DebugBlockingInfo.PopBlockingItem(); } +#ifdef _TARGET_X86_ +#pragma optimize( "", on ) +#endif // _TARGET_X86_ #endif //DACCESS_COMPILE diff --git a/src/vm/threads.cpp b/src/vm/threads.cpp index 3fc78f1e8a..87ad9bc1f2 100644 --- a/src/vm/threads.cpp +++ b/src/vm/threads.cpp @@ -6309,6 +6309,21 @@ void ThreadStore::AddThread(Thread *newThread, BOOL bRequiresTSL) } } +// this function is just desgined to avoid deadlocks during abnormal process termination, and should not be used for any other purpose +BOOL ThreadStore::CanAcquireLock() +{ + WRAPPER_NO_CONTRACT; +#ifdef FEATURE_INCLUDE_ALL_INTERFACES + if (!s_pThreadStore->m_Crst.IsOSCritSec()) + { + return true; + } + else +#endif + { + return (s_pThreadStore->m_Crst.m_criticalsection.LockCount == -1 || (size_t)s_pThreadStore->m_Crst.m_criticalsection.OwningThread == (size_t)GetCurrentThreadId()); + } +} // Whenever one of the components of OtherThreadsComplete() has changed in the // correct direction, see whether we can now shutdown the EE because only background diff --git a/src/vm/threads.h b/src/vm/threads.h index b77c33bdda..28c593c103 100644 --- a/src/vm/threads.h +++ b/src/vm/threads.h @@ -5683,6 +5683,8 @@ public: // RemoveThread finds the thread in the ThreadStore and discards it. static BOOL RemoveThread(Thread *target); + static BOOL CanAcquireLock(); + // Transfer a thread from the unstarted to the started list. // WARNING : only GC calls this with bRequiresTSL set to FALSE. static void TransferStartedThread(Thread *target, BOOL bRequiresTSL=TRUE); diff --git a/src/vm/vm.settings b/src/vm/vm.settings index 5e7ac72cbe..b4799d1b37 100644 --- a/src/vm/vm.settings +++ b/src/vm/vm.settings @@ -26,6 +26,7 @@ $(ClrSrcDirectory)\debug\inc\dump; $(ClrSrcDirectory)\zap; $(ClrSrcDirectory)\strongname\inc; + $(ClrSrcDirectory)\TraceLog; $(ClrSrcDirectory)\gc </UserIncludes> diff --git a/src/vm/wks/wks.targets b/src/vm/wks/wks.targets index 55b404c0e7..5f2770b267 100644 --- a/src/vm/wks/wks.targets +++ b/src/vm/wks/wks.targets @@ -383,4 +383,7 @@ <AssembleArm64 Include="$(IntermediateOutputDirectory)\PInvokeStubs.i" /> <AssembleArm64 Include="$(IntermediateOutputDirectory)\crtHelpers.i" /> </ItemGroup> + <ItemGroup> + <CppCompile Condition="'$(FeatureTraceLogging)' == 'true'" Include="$(VmSourcesDir)\clrtracelogging.cpp" /> + </ItemGroup> </Project> |