Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/coreclr/debug/daccess/dacdbiimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6052,7 +6052,8 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetThreadOwningMonitorLock(VMPTR_

DWORD threadId = 0;
DWORD recursionCount = 0;
BOOL isLockHeld = pObj->GetHeader()->PassiveGetSyncBlock()->TryGetLockInfo(&threadId, &recursionCount);
SyncBlock* psb = pObj->GetHeader()->PassiveGetSyncBlock();
BOOL isLockHeld = psb != NULL && psb->TryGetLockInfo(&threadId, &recursionCount);

if (!isLockHeld)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3131,7 +3131,53 @@ public int GetHandleAddressFromVmHandle(ulong vmHandle, ulong* pRetVal)
}

public int GetThreadOwningMonitorLock(ulong vmObject, DacDbiMonitorLockInfo* pRetVal)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetThreadOwningMonitorLock(vmObject, pRetVal) : HResults.E_NOTIMPL;
{
int hr = HResults.S_OK;
*pRetVal = default;
try
{
DacDbiMonitorLockInfo info = default;
uint threadId = 0;
uint recursionCount = 0;
TargetPointer syncBlock = _target.Contracts.Object.GetSyncBlockAddress(vmObject);

if (syncBlock == TargetPointer.Null || !_target.Contracts.SyncBlock.TryGetLockInfo(syncBlock, out threadId, out recursionCount))
{
*pRetVal = info;
}
else
{
Comment thread
barosiak marked this conversation as resolved.
TargetPointer threadPtr = _target.Contracts.Thread.IdToThread(threadId);
Debug.Assert(threadPtr != TargetPointer.Null, "A thread should have been found");
if (threadPtr != TargetPointer.Null)
{
info.lockOwner = threadPtr;
info.acquisitionCount = recursionCount + 1; // The runtime tracks recursion count starting at 0, but diagnostics users expect it to start at 1.
}
*pRetVal = info;
}
}
catch (System.Exception ex)
{
hr = ex.HResult;
}
#if DEBUG
if (_legacy is not null)
{
DacDbiMonitorLockInfo pRetValLocal;
int hrLocal = _legacy.GetThreadOwningMonitorLock(vmObject, &pRetValLocal);
Debug.ValidateHResult(hr, hrLocal);
if (hr == HResults.S_OK)
{
Debug.Assert(pRetVal->lockOwner == pRetValLocal.lockOwner,
$"lockOwner mismatch: cDAC={pRetVal->lockOwner}, DAC={pRetValLocal.lockOwner}");
Debug.Assert(pRetVal->acquisitionCount == pRetValLocal.acquisitionCount,
$"acquisitionCount mismatch: cDAC={pRetVal->acquisitionCount}, DAC={pRetValLocal.acquisitionCount}");
}
}
#endif
return hr;
}

public int EnumerateMonitorEventWaitList(ulong vmObject, nint fpCallback, nint pUserData)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.EnumerateMonitorEventWaitList(vmObject, fpCallback, pUserData) : HResults.E_NOTIMPL;
Expand Down
68 changes: 68 additions & 0 deletions src/native/managed/cdac/tests/UnitTests/DacDbiImplTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,74 @@ public void AreOptimizationsDisabled_WithMethodDesc(MockTarget.Architecture arch
Assert.Equal(deoptimized ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, result);
}

private delegate void TryGetLockInfoCallback(TargetPointer syncBlock, out uint owningThreadId, out uint recursion);

public static IEnumerable<object[]> GetThreadOwningMonitorLockData()
{
foreach (var arch in new MockTarget.StdArch())
{
yield return new object[] { arch[0], false, false, 0u, 0ul, 0u };
yield return new object[] { arch[0], true, false, 0u, 0ul, 0u };
yield return new object[] { arch[0], true, true, 3u, 0x7000ul, 4u };
}
}

[Theory]
[MemberData(nameof(GetThreadOwningMonitorLockData))]
public void GetThreadOwningMonitorLock(MockTarget.Architecture arch, bool hasSyncBlock, bool isLockHeld, uint recursionCount, ulong expectedOwner, uint expectedAcquisitionCount)
{
const ulong ObjectAddr = 0x5000;
const uint OwnerThreadId = 42;
TargetPointer syncBlockAddr = new(0x6000);
TargetPointer ownerThreadPtr = new(0x7000);

var mockObject = new Mock<IObject>();
mockObject.Setup(o => o.GetSyncBlockAddress(new TargetPointer(ObjectAddr)))
.Returns(hasSyncBlock ? syncBlockAddr : TargetPointer.Null);

var builder = new TestPlaceholderTarget.Builder(arch)
.UseReader((_, _) => -1)
.AddMockContract(mockObject);

if (hasSyncBlock)
{
var mockSyncBlock = new Mock<ISyncBlock>();
var lockSetup = mockSyncBlock
.Setup(s => s.TryGetLockInfo(syncBlockAddr, out It.Ref<uint>.IsAny, out It.Ref<uint>.IsAny));
if (isLockHeld)
{
lockSetup
.Callback(new TryGetLockInfoCallback((TargetPointer _, out uint threadId, out uint recursion) =>
{
threadId = OwnerThreadId;
recursion = recursionCount;
}))
.Returns(true);
}
else
{
lockSetup.Returns(false);
}
builder.AddMockContract(mockSyncBlock);
}

if (isLockHeld)
{
var mockThread = new Mock<IThread>();
mockThread.Setup(t => t.IdToThread(OwnerThreadId))
.Returns(ownerThreadPtr);
builder.AddMockContract(mockThread);
}

var dacDbi = new DacDbiImpl(builder.Build(), legacyObj: null);

DacDbiMonitorLockInfo result;
int hr = dacDbi.GetThreadOwningMonitorLock(ObjectAddr, &result);
Assert.Equal(System.HResults.S_OK, hr);
Assert.Equal(expectedOwner, result.lockOwner);
Assert.Equal(expectedAcquisitionCount, result.acquisitionCount);
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void AreOptimizationsDisabled_NullMethodDesc_ReturnsFalse(MockTarget.Architecture arch)
Expand Down
Loading