Skip to content

Commit 56df10e

Browse files
authored
[cDAC] Implement GetThreadOwningMonitorLock for cDAC (#128966)
### Summary Implement `GetThreadOwningMonitorLock` in `DacDbiImpl`. ### Changes - `DacDbiImpl.cs` - Implement `GetThreadOwningMonitorLock` with `#if DEBUG` legacy validation. - `DacDbiImplTests.cs` - Add 8 tests for lock-not-held and lock-held scenarios.
1 parent a37889c commit 56df10e

3 files changed

Lines changed: 117 additions & 2 deletions

File tree

src/coreclr/debug/daccess/dacdbiimpl.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6052,7 +6052,8 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetThreadOwningMonitorLock(VMPTR_
60526052

60536053
DWORD threadId = 0;
60546054
DWORD recursionCount = 0;
6055-
BOOL isLockHeld = pObj->GetHeader()->PassiveGetSyncBlock()->TryGetLockInfo(&threadId, &recursionCount);
6055+
SyncBlock* psb = pObj->GetHeader()->PassiveGetSyncBlock();
6056+
BOOL isLockHeld = psb != NULL && psb->TryGetLockInfo(&threadId, &recursionCount);
60566057

60576058
if (!isLockHeld)
60586059
{

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3131,7 +3131,53 @@ public int GetHandleAddressFromVmHandle(ulong vmHandle, ulong* pRetVal)
31313131
}
31323132

31333133
public int GetThreadOwningMonitorLock(ulong vmObject, DacDbiMonitorLockInfo* pRetVal)
3134-
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetThreadOwningMonitorLock(vmObject, pRetVal) : HResults.E_NOTIMPL;
3134+
{
3135+
int hr = HResults.S_OK;
3136+
*pRetVal = default;
3137+
try
3138+
{
3139+
DacDbiMonitorLockInfo info = default;
3140+
uint threadId = 0;
3141+
uint recursionCount = 0;
3142+
TargetPointer syncBlock = _target.Contracts.Object.GetSyncBlockAddress(vmObject);
3143+
3144+
if (syncBlock == TargetPointer.Null || !_target.Contracts.SyncBlock.TryGetLockInfo(syncBlock, out threadId, out recursionCount))
3145+
{
3146+
*pRetVal = info;
3147+
}
3148+
else
3149+
{
3150+
TargetPointer threadPtr = _target.Contracts.Thread.IdToThread(threadId);
3151+
Debug.Assert(threadPtr != TargetPointer.Null, "A thread should have been found");
3152+
if (threadPtr != TargetPointer.Null)
3153+
{
3154+
info.lockOwner = threadPtr;
3155+
info.acquisitionCount = recursionCount + 1; // The runtime tracks recursion count starting at 0, but diagnostics users expect it to start at 1.
3156+
}
3157+
*pRetVal = info;
3158+
}
3159+
}
3160+
catch (System.Exception ex)
3161+
{
3162+
hr = ex.HResult;
3163+
}
3164+
#if DEBUG
3165+
if (_legacy is not null)
3166+
{
3167+
DacDbiMonitorLockInfo pRetValLocal;
3168+
int hrLocal = _legacy.GetThreadOwningMonitorLock(vmObject, &pRetValLocal);
3169+
Debug.ValidateHResult(hr, hrLocal);
3170+
if (hr == HResults.S_OK)
3171+
{
3172+
Debug.Assert(pRetVal->lockOwner == pRetValLocal.lockOwner,
3173+
$"lockOwner mismatch: cDAC={pRetVal->lockOwner}, DAC={pRetValLocal.lockOwner}");
3174+
Debug.Assert(pRetVal->acquisitionCount == pRetValLocal.acquisitionCount,
3175+
$"acquisitionCount mismatch: cDAC={pRetVal->acquisitionCount}, DAC={pRetValLocal.acquisitionCount}");
3176+
}
3177+
}
3178+
#endif
3179+
return hr;
3180+
}
31353181

31363182
public int EnumerateMonitorEventWaitList(ulong vmObject, nint fpCallback, nint pUserData)
31373183
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.EnumerateMonitorEventWaitList(vmObject, fpCallback, pUserData) : HResults.E_NOTIMPL;

src/native/managed/cdac/tests/UnitTests/DacDbiImplTests.cs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,74 @@ public void AreOptimizationsDisabled_WithMethodDesc(MockTarget.Architecture arch
720720
Assert.Equal(deoptimized ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, result);
721721
}
722722

723+
private delegate void TryGetLockInfoCallback(TargetPointer syncBlock, out uint owningThreadId, out uint recursion);
724+
725+
public static IEnumerable<object[]> GetThreadOwningMonitorLockData()
726+
{
727+
foreach (var arch in new MockTarget.StdArch())
728+
{
729+
yield return new object[] { arch[0], false, false, 0u, 0ul, 0u };
730+
yield return new object[] { arch[0], true, false, 0u, 0ul, 0u };
731+
yield return new object[] { arch[0], true, true, 3u, 0x7000ul, 4u };
732+
}
733+
}
734+
735+
[Theory]
736+
[MemberData(nameof(GetThreadOwningMonitorLockData))]
737+
public void GetThreadOwningMonitorLock(MockTarget.Architecture arch, bool hasSyncBlock, bool isLockHeld, uint recursionCount, ulong expectedOwner, uint expectedAcquisitionCount)
738+
{
739+
const ulong ObjectAddr = 0x5000;
740+
const uint OwnerThreadId = 42;
741+
TargetPointer syncBlockAddr = new(0x6000);
742+
TargetPointer ownerThreadPtr = new(0x7000);
743+
744+
var mockObject = new Mock<IObject>();
745+
mockObject.Setup(o => o.GetSyncBlockAddress(new TargetPointer(ObjectAddr)))
746+
.Returns(hasSyncBlock ? syncBlockAddr : TargetPointer.Null);
747+
748+
var builder = new TestPlaceholderTarget.Builder(arch)
749+
.UseReader((_, _) => -1)
750+
.AddMockContract(mockObject);
751+
752+
if (hasSyncBlock)
753+
{
754+
var mockSyncBlock = new Mock<ISyncBlock>();
755+
var lockSetup = mockSyncBlock
756+
.Setup(s => s.TryGetLockInfo(syncBlockAddr, out It.Ref<uint>.IsAny, out It.Ref<uint>.IsAny));
757+
if (isLockHeld)
758+
{
759+
lockSetup
760+
.Callback(new TryGetLockInfoCallback((TargetPointer _, out uint threadId, out uint recursion) =>
761+
{
762+
threadId = OwnerThreadId;
763+
recursion = recursionCount;
764+
}))
765+
.Returns(true);
766+
}
767+
else
768+
{
769+
lockSetup.Returns(false);
770+
}
771+
builder.AddMockContract(mockSyncBlock);
772+
}
773+
774+
if (isLockHeld)
775+
{
776+
var mockThread = new Mock<IThread>();
777+
mockThread.Setup(t => t.IdToThread(OwnerThreadId))
778+
.Returns(ownerThreadPtr);
779+
builder.AddMockContract(mockThread);
780+
}
781+
782+
var dacDbi = new DacDbiImpl(builder.Build(), legacyObj: null);
783+
784+
DacDbiMonitorLockInfo result;
785+
int hr = dacDbi.GetThreadOwningMonitorLock(ObjectAddr, &result);
786+
Assert.Equal(System.HResults.S_OK, hr);
787+
Assert.Equal(expectedOwner, result.lockOwner);
788+
Assert.Equal(expectedAcquisitionCount, result.acquisitionCount);
789+
}
790+
723791
[Theory]
724792
[ClassData(typeof(MockTarget.StdArch))]
725793
public void AreOptimizationsDisabled_NullMethodDesc_ReturnsFalse(MockTarget.Architecture arch)

0 commit comments

Comments
 (0)