diff --git a/src/coreclr/gc/env/etmdummy.h b/src/coreclr/gc/env/etmdummy.h index 575e3067cf89a..b8be293e345c0 100644 --- a/src/coreclr/gc/env/etmdummy.h +++ b/src/coreclr/gc/env/etmdummy.h @@ -98,7 +98,9 @@ #define FireEtwExceptionThrownStop() 0 #define FireEtwContention() 0 #define FireEtwContentionStart_V1(ContentionFlags, ClrInstanceID) 0 +#define FireEtwContentionStart_V2(ContentionFlags, ClrInstanceID, LockID, AssociatedObjectID, LockOwnerThreadID) 0 #define FireEtwContentionStop(ContentionFlags, ClrInstanceID) 0 +#define FireEtwLockCreated(LockID, AssociatedObjectID, ClrInstanceID) 0 #define FireEtwCLRStackWalk(ClrInstanceID, Reserved1, Reserved2, FrameCount, Stack) 0 #define FireEtwAppDomainMemAllocated(AppDomainID, Allocated, ClrInstanceID) 0 #define FireEtwAppDomainMemSurvived(AppDomainID, Survived, ProcessSurvived, ClrInstanceID) 0 diff --git a/src/coreclr/vm/ClrEtwAll.man b/src/coreclr/vm/ClrEtwAll.man index cb7694234b5f0..66182b0977a84 100644 --- a/src/coreclr/vm/ClrEtwAll.man +++ b/src/coreclr/vm/ClrEtwAll.man @@ -1744,6 +1744,23 @@ + + + + + @@ -3644,6 +3675,11 @@ task="Contention" symbol="ContentionStart_V1" message="$(string.RuntimePublisher.ContentionStart_V1EventMessage)"/> + + + + + + + diff --git a/src/coreclr/vm/ClrEtwAllMeta.lst b/src/coreclr/vm/ClrEtwAllMeta.lst index 1d5c92cbce24b..3c145a93bc1ab 100644 --- a/src/coreclr/vm/ClrEtwAllMeta.lst +++ b/src/coreclr/vm/ClrEtwAllMeta.lst @@ -185,10 +185,12 @@ noclrinstanceid:Exception:::ExceptionThrown nomac:Contention:::Contention noclrinstanceid:Contention:::Contention nomac:Contention:::ContentionStart_V1 +nomac:Contention:::ContentionStart_V2 nostack:Contention:::ContentionStop nomac:Contention:::ContentionStop nostack:Contention:::ContentionStop_V1 nomac:Contention:::ContentionStop_V1 +nomac:Contention:::LockCreated ################## # StackWalk events @@ -698,4 +700,4 @@ nostack:MonoProfiler:::MonoProfilerThreadStopped nostack:MonoProfiler:::MonoProfilerThreadExited nostack:MonoProfiler:::MonoProfilerThreadName nostack:MonoProfiler:::MonoProfilerJitDoneVerbose -nostack:MonoProfiler:::MonoProfilerGCHeapDumpVTableClassReference \ No newline at end of file +nostack:MonoProfiler:::MonoProfilerGCHeapDumpVTableClassReference diff --git a/src/coreclr/vm/syncblk.cpp b/src/coreclr/vm/syncblk.cpp index fc164da69fba3..1048f51f6c05d 100644 --- a/src/coreclr/vm/syncblk.cpp +++ b/src/coreclr/vm/syncblk.cpp @@ -2202,13 +2202,20 @@ SyncBlock *ObjHeader::GetSyncBlock() _ASSERTE(lockThreadId != 0); Thread *pThread = g_pThinLockThreadIdDispenser->IdToThreadWithValidation(lockThreadId); + SIZE_T osThreadId; if (pThread == NULL) { // The lock is orphaned. pThread = (Thread*) -1; + osThreadId = (SIZE_T)-1; } - syncBlock->InitState(recursionLevel + 1, pThread); + else + { + osThreadId = pThread->GetOSThreadId64(); + } + + syncBlock->InitState(recursionLevel + 1, pThread, osThreadId); } } else if ((bits & BIT_SBLK_IS_HASHCODE) != 0) @@ -2237,8 +2244,8 @@ SyncBlock *ObjHeader::GetSyncBlock() LEAVE_SPIN_LOCK(this); } - // SyncBlockCache::LockHolder goes out of scope here } + // SyncBlockCache::LockHolder goes out of scope here } RETURN syncBlock; @@ -2372,6 +2379,7 @@ void AwareLock::Enter() { // We get here if we successfully acquired the mutex. m_HoldingThread = pCurThread; + m_HoldingOSThreadId = pCurThread->GetOSThreadId64(); m_Recursion = 1; #if defined(_DEBUG) && defined(TRACK_SYNC) @@ -2434,6 +2442,7 @@ BOOL AwareLock::TryEnter(INT32 timeOut) { // We get here if we successfully acquired the mutex. m_HoldingThread = pCurThread; + m_HoldingOSThreadId = pCurThread->GetOSThreadId64(); m_Recursion = 1; #if defined(_DEBUG) && defined(TRACK_SYNC) @@ -2541,22 +2550,32 @@ BOOL AwareLock::EnterEpilogHelper(Thread* pCurThread, INT32 timeOut) // the object associated with this lock. _ASSERTE(pCurThread->PreemptiveGCDisabled()); - BOOLEAN IsContentionKeywordEnabled = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, TRACE_LEVEL_INFORMATION, CLR_CONTENTION_KEYWORD); + LogContention(); + Thread::IncrementMonitorLockContentionCount(pCurThread); + + OBJECTREF obj = GetOwningObject(); + LARGE_INTEGER startTicks = { {0} }; + bool isContentionKeywordEnabled = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, TRACE_LEVEL_INFORMATION, CLR_CONTENTION_KEYWORD); - if (IsContentionKeywordEnabled) + if (isContentionKeywordEnabled) { QueryPerformanceCounter(&startTicks); + if (InterlockedCompareExchangeT(&m_emittedLockCreatedEvent, 1, 0) == 0) + { + FireEtwLockCreated(this, OBJECTREFToObject(obj), GetClrInstanceId()); + } + // Fire a contention start event for a managed contention - FireEtwContentionStart_V1(ETW::ContentionLog::ContentionStructs::ManagedContention, GetClrInstanceId()); + FireEtwContentionStart_V2( + ETW::ContentionLog::ContentionStructs::ManagedContention, + GetClrInstanceId(), + this, + OBJECTREFToObject(obj), + m_HoldingOSThreadId); } - LogContention(); - Thread::IncrementMonitorLockContentionCount(pCurThread); - - OBJECTREF obj = GetOwningObject(); - // We cannot allow the AwareLock to be cleaned up underneath us by the GC. IncrementTransientPrecious(); @@ -2684,7 +2703,7 @@ BOOL AwareLock::EnterEpilogHelper(Thread* pCurThread, INT32 timeOut) GCPROTECT_END(); DecrementTransientPrecious(); - if (IsContentionKeywordEnabled) + if (isContentionKeywordEnabled) { LARGE_INTEGER endTicks; QueryPerformanceCounter(&endTicks); @@ -2702,6 +2721,7 @@ BOOL AwareLock::EnterEpilogHelper(Thread* pCurThread, INT32 timeOut) } m_HoldingThread = pCurThread; + m_HoldingOSThreadId = pCurThread->GetOSThreadId64(); m_Recursion = 1; #if defined(_DEBUG) && defined(TRACK_SYNC) @@ -2765,7 +2785,6 @@ BOOL AwareLock::OwnedByCurrentThread() return (GetThread() == m_HoldingThread); } - // *************************************************************************** // // SyncBlock class implementation diff --git a/src/coreclr/vm/syncblk.h b/src/coreclr/vm/syncblk.h index 17d8074c810bf..a9008132ad495 100644 --- a/src/coreclr/vm/syncblk.h +++ b/src/coreclr/vm/syncblk.h @@ -436,6 +436,7 @@ class AwareLock ULONG m_Recursion; PTR_Thread m_HoldingThread; + SIZE_T m_HoldingOSThreadId; LONG m_TransientPrecious; @@ -447,6 +448,7 @@ class AwareLock CLREvent m_SemEvent; DWORD m_waiterStarvationStartTimeMs; + int m_emittedLockCreatedEvent; static const DWORD WaiterStarvationDurationMsBeforeStoppingPreemptingWaiters = 100; @@ -457,9 +459,11 @@ class AwareLock // PreFAST has trouble with initializing a NULL PTR_Thread. m_HoldingThread(NULL), #endif // DACCESS_COMPILE + m_HoldingOSThreadId(0), m_TransientPrecious(0), m_dwSyncIndex(indx), - m_waiterStarvationStartTimeMs(0) + m_waiterStarvationStartTimeMs(0), + m_emittedLockCreatedEvent(0) { LIMITED_METHOD_CONTRACT; } @@ -525,13 +529,14 @@ class AwareLock bool ShouldStopPreemptingWaiters() const; private: // friend access is required for this unsafe function - void InitializeToLockedWithNoWaiters(ULONG recursionLevel, PTR_Thread holdingThread) + void InitializeToLockedWithNoWaiters(ULONG recursionLevel, PTR_Thread holdingThread, SIZE_T holdingOSThreadId) { WRAPPER_NO_CONTRACT; m_lockState.InitializeToLockedWithNoWaiters(); m_Recursion = recursionLevel; m_HoldingThread = holdingThread; + m_HoldingOSThreadId = holdingOSThreadId; } public: @@ -1244,10 +1249,10 @@ class SyncBlock // This should ONLY be called when initializing a SyncBlock (i.e. ONLY from // ObjHeader::GetSyncBlock()), otherwise we'll have a race condition. // - void InitState(ULONG recursionLevel, PTR_Thread holdingThread) + void InitState(ULONG recursionLevel, PTR_Thread holdingThread, SIZE_T holdingOSThreadId) { WRAPPER_NO_CONTRACT; - m_Monitor.InitializeToLockedWithNoWaiters(recursionLevel, holdingThread); + m_Monitor.InitializeToLockedWithNoWaiters(recursionLevel, holdingThread, holdingOSThreadId); } #if defined(ENABLE_CONTRACTS_IMPL) diff --git a/src/coreclr/vm/syncblk.inl b/src/coreclr/vm/syncblk.inl index 0575305857722..af9b7d6c1642e 100644 --- a/src/coreclr/vm/syncblk.inl +++ b/src/coreclr/vm/syncblk.inl @@ -478,6 +478,7 @@ FORCEINLINE bool AwareLock::TryEnterHelper(Thread* pCurThread) if (m_lockState.InterlockedTryLock()) { m_HoldingThread = pCurThread; + m_HoldingOSThreadId = pCurThread->GetOSThreadId64(); m_Recursion = 1; return true; } @@ -523,6 +524,7 @@ FORCEINLINE AwareLock::EnterHelperResult AwareLock::TryEnterBeforeSpinLoopHelper // Lock was acquired and the spinner was not registered m_HoldingThread = pCurThread; + m_HoldingOSThreadId = pCurThread->GetOSThreadId64(); m_Recursion = 1; return EnterHelperResult_Entered; } @@ -554,6 +556,7 @@ FORCEINLINE AwareLock::EnterHelperResult AwareLock::TryEnterInsideSpinLoopHelper // Lock was acquired and spinner was unregistered m_HoldingThread = pCurThread; + m_HoldingOSThreadId = pCurThread->GetOSThreadId64(); m_Recursion = 1; return EnterHelperResult_Entered; } @@ -576,6 +579,7 @@ FORCEINLINE bool AwareLock::TryEnterAfterSpinLoopHelper(Thread *pCurThread) // Spinner was unregistered and the lock was acquired m_HoldingThread = pCurThread; + m_HoldingOSThreadId = pCurThread->GetOSThreadId64(); m_Recursion = 1; return true; } @@ -694,6 +698,7 @@ FORCEINLINE AwareLock::LeaveHelperAction AwareLock::LeaveHelper(Thread* pCurThre if (--m_Recursion == 0) { m_HoldingThread = NULL; + m_HoldingOSThreadId = 0; // Clear lock bit and determine whether we must signal a waiter to wake if (!m_lockState.InterlockedUnlock())