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 @@
+
+
+
+
+
+
+
+
+ %1
+ %2
+ %3
+ %4
+ %5
+
+
+
+
@@ -3096,6 +3113,20 @@
+
+
+
+
+
+
+
+ %1
+ %2
+ %3
+
+
+
+
@@ -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())