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
Original file line number Diff line number Diff line change
Expand Up @@ -395,8 +395,66 @@ public int SetCompilerFlags(ulong vmAssembly, Interop.BOOL fAllowJitOpts, Intero
return hr;
}

public int EnumerateAssembliesInAppDomain(ulong vmAppDomain, nint fpCallback, nint pUserData)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.EnumerateAssembliesInAppDomain(vmAppDomain, fpCallback, pUserData) : HResults.E_NOTIMPL;
public int EnumerateAssembliesInAppDomain(ulong vmAppDomain, delegate* unmanaged<ulong, nint, void> fpCallback, nint pUserData)
{
int hr = HResults.S_OK;
#if DEBUG
List<ulong>? cdacAssemblies = _legacy is not null ? new() : null;
#endif
try
{
if (fpCallback == null)
{
throw new ArgumentNullException(nameof(fpCallback));
}

if (vmAppDomain == 0)
{
return hr;
}

Contracts.ILoader loader = _target.Contracts.Loader;
foreach (Contracts.ModuleHandle handle in loader.GetModuleHandles(
new TargetPointer(vmAppDomain),
AssemblyIterationFlags.IncludeLoading | AssemblyIterationFlags.IncludeLoaded | AssemblyIterationFlags.IncludeExecution))
{
TargetPointer assembly = loader.GetAssembly(handle);
fpCallback(assembly.Value, pUserData);
#if DEBUG
cdacAssemblies?.Add(assembly.Value);
#endif
}
}
catch (System.Exception ex)
{
hr = ex.HResult;
}
#if DEBUG
if (_legacy is not null && fpCallback != null)
{
List<ulong> dacAssemblies = new();
GCHandle dacHandle = GCHandle.Alloc(dacAssemblies);
try
{
int hrLocal = _legacy.EnumerateAssembliesInAppDomain(vmAppDomain, &CollectEnumerationCallback, GCHandle.ToIntPtr(dacHandle));
Debug.ValidateHResult(hr, hrLocal);
if (hr == HResults.S_OK)
{
Debug.Assert(
cdacAssemblies!.SequenceEqual(dacAssemblies),
$"Assembly enumeration mismatch - "
+ $"cDAC: [{string.Join(",", cdacAssemblies!.Select(a => $"0x{a:x}"))}], "
+ $"DAC: [{string.Join(",", dacAssemblies.Select(a => $"0x{a:x}"))}]");
}
}
finally
{
dacHandle.Free();
}
}
#endif
return hr;
}

public int EnumerateModulesInAssembly(ulong vmAssembly, nint fpCallback, nint pUserData)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.EnumerateModulesInAssembly(vmAssembly, fpCallback, pUserData) : HResults.E_NOTIMPL;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ public unsafe partial interface IDacDbiInterface
int SetCompilerFlags(ulong vmAssembly, Interop.BOOL fAllowJitOpts, Interop.BOOL fEnableEnC);

[PreserveSig]
int EnumerateAssembliesInAppDomain(ulong vmAppDomain, nint fpCallback, nint pUserData);
int EnumerateAssembliesInAppDomain(ulong vmAppDomain, delegate* unmanaged<ulong, nint, void> fpCallback, nint pUserData);

[PreserveSig]
int EnumerateModulesInAssembly(ulong vmAssembly, nint fpCallback, nint pUserData);
Expand Down
144 changes: 144 additions & 0 deletions src/native/managed/cdac/tests/DacDbiImplTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Microsoft.Diagnostics.DataContractReader.Contracts;
using Microsoft.Diagnostics.DataContractReader.Legacy;
using Moq;
using Xunit;

namespace Microsoft.Diagnostics.DataContractReader.Tests;
Expand Down Expand Up @@ -253,4 +255,146 @@ public void SetCompilerFlags_EnCBlocked_NotificationProfiler(MockTarget.Architec
uint rawFlags = target.Read<uint>(moduleAddr + (ulong)flagsOffset);
Assert.Equal(0u, rawFlags & IsEditAndContinue);
}

private static DacDbiImpl CreateDacDbiWithMockLoader(
MockTarget.Architecture arch,
Mock<ILoader> mockLoader)
{
var target = new TestPlaceholderTarget.Builder(arch)
.UseReader((_, _) => -1)
.AddMockContract(mockLoader)
.Build();
return new DacDbiImpl(target, legacyObj: null);
}

[UnmanagedCallersOnly]
private static unsafe void CollectAssemblyCallback(ulong value, nint pUserData)
{
GCHandle handle = GCHandle.FromIntPtr(pUserData);
((List<ulong>)handle.Target!).Add(value);
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void EnumerateAssembliesInAppDomain_ZeroAppDomain(MockTarget.Architecture arch)
{
var mockLoader = new Mock<ILoader>();
DacDbiImpl dacDbi = CreateDacDbiWithMockLoader(arch, mockLoader);

List<ulong> assemblies = new();
GCHandle gcHandle = GCHandle.Alloc(assemblies);
int hr = dacDbi.EnumerateAssembliesInAppDomain(0, &CollectAssemblyCallback, GCHandle.ToIntPtr(gcHandle));
gcHandle.Free();

Assert.Equal(System.HResults.S_OK, hr);
Assert.Empty(assemblies);
mockLoader.Verify(
l => l.GetModuleHandles(It.IsAny<TargetPointer>(), It.IsAny<AssemblyIterationFlags>()),
Times.Never);
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void EnumerateAssembliesInAppDomain_NullCallback(MockTarget.Architecture arch)
{
var mockLoader = new Mock<ILoader>();
DacDbiImpl dacDbi = CreateDacDbiWithMockLoader(arch, mockLoader);

int hr = dacDbi.EnumerateAssembliesInAppDomain(0x1000, null, nint.Zero);

Assert.NotEqual(System.HResults.S_OK, hr);
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void EnumerateAssembliesInAppDomain_SingleAssembly_CallsCallback(MockTarget.Architecture arch)
{
ulong appDomainAddr = 0x1000;
ulong assemblyAddr = 0x2000;
TargetPointer moduleAddr = new(0x3000);

var mockLoader = new Mock<ILoader>();
mockLoader
.Setup(l => l.GetModuleHandles(
new TargetPointer(appDomainAddr),
AssemblyIterationFlags.IncludeLoading | AssemblyIterationFlags.IncludeLoaded | AssemblyIterationFlags.IncludeExecution))
.Returns(new[] { new Contracts.ModuleHandle(moduleAddr) });
mockLoader
.Setup(l => l.GetAssembly(It.Is<Contracts.ModuleHandle>(h => h.Address == moduleAddr)))
.Returns(new TargetPointer(assemblyAddr));

DacDbiImpl dacDbi = CreateDacDbiWithMockLoader(arch, mockLoader);

List<ulong> assemblies = new();
GCHandle gcHandle = GCHandle.Alloc(assemblies);
int hr = dacDbi.EnumerateAssembliesInAppDomain(appDomainAddr, &CollectAssemblyCallback, GCHandle.ToIntPtr(gcHandle));
gcHandle.Free();

Assert.Equal(System.HResults.S_OK, hr);
Assert.Single(assemblies);
Assert.Equal(assemblyAddr, assemblies[0]);
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void EnumerateAssembliesInAppDomain_MultipleAssemblies(MockTarget.Architecture arch)
{
ulong appDomainAddr = 0x1000;
ulong[] expectedAssemblies = [0x2000, 0x3000, 0x4000];
TargetPointer[] moduleAddrs = [new(0x5000), new(0x6000), new(0x7000)];

var mockLoader = new Mock<ILoader>();
mockLoader
.Setup(l => l.GetModuleHandles(
new TargetPointer(appDomainAddr),
AssemblyIterationFlags.IncludeLoading | AssemblyIterationFlags.IncludeLoaded | AssemblyIterationFlags.IncludeExecution))
.Returns(new[]
{
new Contracts.ModuleHandle(moduleAddrs[0]),
new Contracts.ModuleHandle(moduleAddrs[1]),
new Contracts.ModuleHandle(moduleAddrs[2]),
});

for (int i = 0; i < 3; i++)
{
int index = i;
mockLoader
.Setup(l => l.GetAssembly(It.Is<Contracts.ModuleHandle>(h => h.Address == moduleAddrs[index])))
.Returns(new TargetPointer(expectedAssemblies[index]));
}

DacDbiImpl dacDbi = CreateDacDbiWithMockLoader(arch, mockLoader);

List<ulong> assemblies = new();
GCHandle gcHandle = GCHandle.Alloc(assemblies);
int hr = dacDbi.EnumerateAssembliesInAppDomain(appDomainAddr, &CollectAssemblyCallback, GCHandle.ToIntPtr(gcHandle));
gcHandle.Free();

Assert.Equal(System.HResults.S_OK, hr);
Assert.Equal(expectedAssemblies, assemblies.ToArray());
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void EnumerateAssembliesInAppDomain_NoAssemblies(MockTarget.Architecture arch)
{
ulong appDomainAddr = 0x1000;

var mockLoader = new Mock<ILoader>();
mockLoader
.Setup(l => l.GetModuleHandles(
new TargetPointer(appDomainAddr),
AssemblyIterationFlags.IncludeLoading | AssemblyIterationFlags.IncludeLoaded | AssemblyIterationFlags.IncludeExecution))
.Returns(Array.Empty<Contracts.ModuleHandle>());

DacDbiImpl dacDbi = CreateDacDbiWithMockLoader(arch, mockLoader);

List<ulong> assemblies = new();
GCHandle gcHandle = GCHandle.Alloc(assemblies);
int hr = dacDbi.EnumerateAssembliesInAppDomain(appDomainAddr, &CollectAssemblyCallback, GCHandle.ToIntPtr(gcHandle));
gcHandle.Free();

Assert.Equal(System.HResults.S_OK, hr);
Assert.Empty(assemblies);
}
}
Loading