Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@ private bool IsComWrappersCCW(TargetPointer ccw)
if (!GetComWrappersCCWVTableQIAddress(ccw, out _, out TargetPointer qiAddress))
return false;

TargetPointer comWrappersVtablePtrs = _target.ReadGlobalPointer(Constants.Globals.ComWrappersVtablePtrs);
Data.ComWrappersVtablePtrs comWrappersVtableStruct = _target.ProcessedData.GetOrAdd<Data.ComWrappersVtablePtrs>(comWrappersVtablePtrs);
if (!_target.TryReadGlobalPointer(Constants.Globals.ComWrappersVtablePtrs, out TargetPointer? comWrappersVtablePtrs))
return false;
Data.ComWrappersVtablePtrs comWrappersVtableStruct = _target.ProcessedData.GetOrAdd<Data.ComWrappersVtablePtrs>(comWrappersVtablePtrs.Value);
return comWrappersVtableStruct.ComWrappersInterfacePointers.Contains(CodePointerUtils.CodePointerFromAddress(qiAddress, _target));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,53 @@ public int GetCurrentException(ulong vmThread, ulong* pRetVal)
}

public int GetObjectForCCW(ulong ccwPtr, ulong* pRetVal)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetObjectForCCW(ccwPtr, pRetVal) : HResults.E_NOTIMPL;
{
*pRetVal = 0;
int hr = HResults.S_OK;
try
{
TargetPointer objectHandle = TargetPointer.Null;
TargetPointer ccwAddress = new(ccwPtr);
bool comWrappersSuccess = false;

if (_target.Contracts.TryGetContract<IComWrappers>(out IComWrappers? comWrappers))
{
TargetPointer managedObjectWrapper = comWrappers.GetManagedObjectWrapperFromCCW(ccwAddress);
if (managedObjectWrapper != TargetPointer.Null)
{
comWrappersSuccess = _target.TryReadPointer(managedObjectWrapper, out objectHandle);
}
Comment thread
rcj1 marked this conversation as resolved.
}

if (!comWrappersSuccess && _target.Contracts.TryGetContract<IBuiltInCOM>(out IBuiltInCOM? builtInCOM))
{
TargetPointer ccw = builtInCOM.GetCCWFromInterfacePointer(ccwAddress);
if (ccw == TargetPointer.Null)
{
ccw = ccwAddress;
}
ccw = builtInCOM.GetStartWrapper(ccw);
objectHandle = builtInCOM.GetObjectHandle(ccw);
}

*pRetVal = objectHandle.Value;
}
catch (System.Exception ex)
{
hr = ex.HResult;
}
#if DEBUG
if (_legacy is not null)
{
ulong retValLocal;
int hrLocal = _legacy.GetObjectForCCW(ccwPtr, &retValLocal);
Debug.ValidateHResult(hr, hrLocal);
if (hr == HResults.S_OK)
Debug.Assert(*pRetVal == retValLocal, $"cDAC: {*pRetVal:x}, DAC: {retValLocal:x}");
}
#endif
return hr;
}

public int GetCurrentCustomDebuggerNotification(ulong vmThread, ulong* pRetVal)
{
Expand Down Expand Up @@ -1011,7 +1057,31 @@ public int GetStackFramesFromException(ulong vmObject, nint pDacStackFrames)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetStackFramesFromException(vmObject, pDacStackFrames) : HResults.E_NOTIMPL;

public int IsRcw(ulong vmObject, Interop.BOOL* pResult)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.IsRcw(vmObject, pResult) : HResults.E_NOTIMPL;
{
*pResult = Interop.BOOL.FALSE;
int hr = HResults.S_OK;
try
{
IObject obj = _target.Contracts.Object;
_ = obj.GetBuiltInComData(new TargetPointer(vmObject), out TargetPointer rcw, out _, out _);
*pResult = rcw != TargetPointer.Null ? Interop.BOOL.TRUE : Interop.BOOL.FALSE;
}
catch (System.Exception ex)
{
hr = ex.HResult;
}
#if DEBUG
if (_legacy is not null)
{
Interop.BOOL resultLocal;
int hrLocal = _legacy.IsRcw(vmObject, &resultLocal);
Debug.ValidateHResult(hr, hrLocal);
if (hr == HResults.S_OK)
Debug.Assert(*pResult == resultLocal, $"cDAC: {*pResult}, DAC: {resultLocal}");
}
#endif
return hr;
}

public int GetRcwCachedInterfacePointers(ulong vmObject, Interop.BOOL bIInspectableOnly, nint pDacItfPtrs)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetRcwCachedInterfacePointers(vmObject, bIInspectableOnly, pDacItfPtrs) : HResults.E_NOTIMPL;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Linq;
using Microsoft.Diagnostics.DataContractReader.Contracts;
using Microsoft.Diagnostics.DataContractReader.Legacy;
using Microsoft.DotNet.XUnitExtensions;
using Xunit;

namespace Microsoft.Diagnostics.DataContractReader.DumpTests;

/// <summary>
/// Dump-based integration tests for DacDbiImpl.GetObjectForCCW.
/// Uses the CCW debuggee (full dump).
/// </summary>
public class DacDbiCCWDumpTests : DumpTestBase
{
protected override string DebuggeeName => "CCW";
protected override string DumpType => "full";

private DacDbiImpl CreateDacDbi() => new DacDbiImpl(Target, legacyObj: null);

private (TargetPointer Ccw, TargetPointer InterfacePointer) FindBuiltInComCcwWithInterface()
{
IGC gc = Target.Contracts.GC;
IObject obj = Target.Contracts.Object;
IBuiltInCOM builtInCOM = Target.Contracts.BuiltInCOM;

foreach (HandleData handleData in gc.GetHandles([HandleType.Strong]))
{
TargetPointer objectAddress = Target.ReadPointer(handleData.Handle);
if (objectAddress == TargetPointer.Null)
continue;

if (!obj.GetBuiltInComData(objectAddress, out _, out TargetPointer ccw, out _)
|| ccw == TargetPointer.Null)
{
continue;
}

// Normalize to the start wrapper, matching what DacDbiImpl.GetObjectForCCW does
// before calling GetObjectHandle, so the expected handle in the test is consistent.
TargetPointer startCcw = builtInCOM.GetStartWrapper(ccw);

List<COMInterfacePointerData> interfaces = builtInCOM.GetCCWInterfaces(startCcw).ToList();
if (interfaces.Count == 0)
continue;

return (startCcw, interfaces[0].InterfacePointerAddress);
}

throw new SkipTestException("No BuiltInCOM CCW interface pointer found in dump.");
}

[ConditionalTheory]
[MemberData(nameof(TestConfigurations))]
[SkipOnOS(IncludeOnly = "windows", Reason = "COM callable wrappers require Windows")]
public unsafe void GetObjectForCCW_ReturnsBuiltInComObjectHandle(TestConfiguration config)
{
InitializeDumpTest(config);
DacDbiImpl dbi = CreateDacDbi();
IBuiltInCOM builtInCOM = Target.Contracts.BuiltInCOM;

(TargetPointer ccw, TargetPointer interfacePointer) = FindBuiltInComCcwWithInterface();

ulong resultHandle;
int hr = dbi.GetObjectForCCW(interfacePointer.Value, &resultHandle);

Assert.Equal(System.HResults.S_OK, hr);
Assert.Equal(builtInCOM.GetObjectHandle(ccw).Value, resultHandle);
Assert.NotEqual(TargetPointer.Null, Target.ReadPointer(new TargetPointer(resultHandle)));
Comment thread
rcj1 marked this conversation as resolved.
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using Microsoft.Diagnostics.DataContractReader.Contracts;
using Microsoft.Diagnostics.DataContractReader.Legacy;
using Microsoft.DotNet.XUnitExtensions;
using Xunit;

namespace Microsoft.Diagnostics.DataContractReader.DumpTests;

/// <summary>
/// Dump-based integration tests for DacDbiImpl.GetObjectForCCW via the ComWrappers path.
/// Uses the ComWrappers debuggee (full dump).
/// </summary>
public class DacDbiComWrappersDumpTests : DumpTestBase
{
protected override string DebuggeeName => "ComWrappers";
protected override string DumpType => "full";

private DacDbiImpl CreateDacDbi() => new DacDbiImpl(Target, legacyObj: null);

[ConditionalTheory]
[MemberData(nameof(TestConfigurations))]
[SkipOnVersion("net10.0", "ComWrappers cDAC support not available in .NET 10")]
public unsafe void GetObjectForCCW_ComWrappersIdentityPointer_ReturnsManagedObjectWrapperHandle(TestConfiguration config)
{
InitializeDumpTest(config);
DacDbiImpl dbi = CreateDacDbi();
IGC gc = Target.Contracts.GC;
IComWrappers comWrappers = Target.Contracts.ComWrappers;

foreach (HandleData handleData in gc.GetHandles([HandleType.Strong]))
{
TargetPointer objectAddress = Target.ReadPointer(handleData.Handle);
if (objectAddress == TargetPointer.Null)
continue;

List<TargetPointer> mows = comWrappers.GetMOWs(objectAddress, out bool hasMowTable);
if (!hasMowTable || mows.Count == 0)
continue;

TargetPointer mow = mows[0];
TargetPointer identity = comWrappers.GetIdentityForMOW(mow);
if (identity == TargetPointer.Null)
continue;

ulong expectedHandle = Target.ReadPointer(mow).Value;
ulong actualHandle;
int hr = dbi.GetObjectForCCW(identity.Value, &actualHandle);

Assert.Equal(System.HResults.S_OK, hr);
Assert.Equal(expectedHandle, actualHandle);
Comment thread
rcj1 marked this conversation as resolved.

// Verify the returned handle dereferences to a live object in the dump.
TargetPointer actualHandleTarget = new(actualHandle);
TargetPointer actualObjectAddress = Target.ReadPointer(actualHandleTarget);
Assert.NotEqual(TargetPointer.Null, actualObjectAddress);
return;
}

throw new SkipTestException("No ComWrappers MOW/CCW identity found in dump.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.Diagnostics.DataContractReader.Contracts;
using Microsoft.Diagnostics.DataContractReader.Legacy;
using Microsoft.DotNet.XUnitExtensions;
using Xunit;

namespace Microsoft.Diagnostics.DataContractReader.DumpTests;

/// <summary>
/// Dump-based integration tests for DacDbiImpl.IsRcw.
/// Uses the RCW debuggee (full dump).
/// </summary>
public class DacDbiRCWDumpTests : DumpTestBase
{
protected override string DebuggeeName => "RCW";
protected override string DumpType => "full";

private DacDbiImpl CreateDacDbi() => new DacDbiImpl(Target, legacyObj: null);

[ConditionalTheory]
[MemberData(nameof(TestConfigurations))]
[SkipOnOS(IncludeOnly = "windows", Reason = "COM interop (RCW) is only supported on Windows")]
public unsafe void IsRcw_ReturnsTrueForBuiltInRcwObject(TestConfiguration config)
{
InitializeDumpTest(config);
DacDbiImpl dbi = CreateDacDbi();
IGC gc = Target.Contracts.GC;
IObject obj = Target.Contracts.Object;

foreach (HandleData handleData in gc.GetHandles([HandleType.Strong]))
{
TargetPointer objectAddress = Target.ReadPointer(handleData.Handle);
if (objectAddress == TargetPointer.Null)
continue;

if (!obj.GetBuiltInComData(objectAddress, out TargetPointer rcw, out _, out _) || rcw == TargetPointer.Null)
continue;

Interop.BOOL result;
int hr = dbi.IsRcw(objectAddress.Value, &result);
Assert.Equal(System.HResults.S_OK, hr);
Assert.Equal(Interop.BOOL.TRUE, result);
Comment thread
rcj1 marked this conversation as resolved.
return;
}

throw new SkipTestException("No built-in RCW object found in dump.");
}

[ConditionalTheory]
[MemberData(nameof(TestConfigurations))]
[SkipOnOS(IncludeOnly = "windows", Reason = "COM interop (RCW) is only supported on Windows")]
public unsafe void IsRcw_ReturnsFalseForNonRcwObject(TestConfiguration config)
{
InitializeDumpTest(config);
DacDbiImpl dbi = CreateDacDbi();
IGC gc = Target.Contracts.GC;
IObject obj = Target.Contracts.Object;

foreach (HandleData handleData in gc.GetHandles([HandleType.Strong]))
{
TargetPointer objectAddress = Target.ReadPointer(handleData.Handle);
if (objectAddress == TargetPointer.Null)
continue;

_ = obj.GetBuiltInComData(objectAddress, out TargetPointer rcw, out _, out _);
if (rcw != TargetPointer.Null)
continue;

Interop.BOOL result;
int hr = dbi.IsRcw(objectAddress.Value, &result);
Assert.Equal(System.HResults.S_OK, hr);
Assert.Equal(Interop.BOOL.FALSE, result);
return;
}

throw new SkipTestException("No non-RCW object found in dump.");
}
}
Loading