From 350adb93479274accecf0259be80f5b912aed66e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Jul 2026 21:00:02 +0000 Subject: [PATCH 1/5] Apply remaining changes Co-authored-by: rcj1 <77995559+rcj1@users.noreply.github.com> --- .../ClrDataModule.cs | 31 +- .../tests/UnitTests/IXCLRDataProcessTests.cs | 275 ++++++++++++++++++ 2 files changed, 305 insertions(+), 1 deletion(-) create mode 100644 src/native/managed/cdac/tests/UnitTests/IXCLRDataProcessTests.cs diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataModule.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataModule.cs index 6e194d4b19155c..eba5d486ea2d2d 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataModule.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataModule.cs @@ -800,7 +800,36 @@ int IXCLRDataModule.EndEnumAppDomains(ulong handle) => LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.EndEnumAppDomains(handle) : HResults.E_NOTIMPL; int IXCLRDataModule.GetVersionId(Guid* vid) - => LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.GetVersionId(vid) : HResults.E_NOTIMPL; + { + int hr = HResults.S_OK; + try + { + if (vid is null) + throw new NullReferenceException(); + + ILoader loader = _target.Contracts.Loader; + Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(_address); + MetadataReader reader = _target.Contracts.EcmaMetadata.GetMetadata(moduleHandle) + ?? throw Marshal.GetExceptionForHR(HResults.E_FAIL)!; + *vid = reader.GetGuid(reader.GetModuleDefinition().Mvid); + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + +#if DEBUG + if (_legacyModule is not null) + { + Guid vidLocal = default; + int hrLocal = _legacyModule.GetVersionId(vid is null ? null : &vidLocal); + Debug.ValidateHResult(hr, hrLocal); + if (hr == HResults.S_OK) + Debug.Assert(*vid == vidLocal, $"cDAC: {*vid}, DAC: {vidLocal}"); + } +#endif + return hr; + } int IXCLRDataModule2.SetJITCompilerFlags(uint flags) { diff --git a/src/native/managed/cdac/tests/UnitTests/IXCLRDataProcessTests.cs b/src/native/managed/cdac/tests/UnitTests/IXCLRDataProcessTests.cs new file mode 100644 index 00000000000000..8bf8aa010e2b15 --- /dev/null +++ b/src/native/managed/cdac/tests/UnitTests/IXCLRDataProcessTests.cs @@ -0,0 +1,275 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using Microsoft.Diagnostics.DataContractReader.Contracts; +using Microsoft.Diagnostics.DataContractReader.Legacy; +using Microsoft.Diagnostics.DataContractReader.TestInfrastructure; +using Moq; +using Xunit; +using HResults = System.HResults; +using ModuleHandle = Microsoft.Diagnostics.DataContractReader.Contracts.ModuleHandle; + +namespace Microsoft.Diagnostics.DataContractReader.Tests; + +public unsafe class IXCLRDataProcessTests +{ + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void AppDomains(MockTarget.Architecture arch) + { + TargetPointer appDomainAddress = new(0x1000); + Mock loader = new(MockBehavior.Strict); + loader.Setup(l => l.GetAppDomain()).Returns(appDomainAddress); + IXCLRDataProcess process = CreateProcess(arch, loader: loader.Object); + + ulong handle; + int hr = process.StartEnumAppDomains(&handle); + Assert.Equal(HResults.S_OK, hr); + Assert.NotEqual(0ul, handle); + + try + { + DacComNullableByRef appDomainOut = new(isNullRef: false); + hr = process.EnumAppDomain(&handle, appDomainOut); + Assert.Equal(HResults.S_OK, hr); + ClrDataAppDomain appDomain = Assert.IsType(appDomainOut.Interface); + Assert.Equal(appDomainAddress, appDomain.Address); + + DacComNullableByRef endOut = new(isNullRef: false); + hr = process.EnumAppDomain(&handle, endOut); + Assert.Equal(HResults.S_FALSE, hr); + } + finally + { + hr = process.EndEnumAppDomains(handle); + Assert.Equal(HResults.S_OK, hr); + } + + DacComNullableByRef byIdOut = new(isNullRef: false); + hr = process.GetAppDomainByUniqueID(ClrDataAppDomain.DefaultAppDomainId, byIdOut); + Assert.Equal(HResults.S_OK, hr); + Assert.IsType(byIdOut.Interface); + + DacComNullableByRef invalidIdOut = new(isNullRef: false); + hr = process.GetAppDomainByUniqueID(ClrDataAppDomain.DefaultAppDomainId + 1, invalidIdOut); + Assert.Equal(HResults.E_INVALIDARG, hr); + + ulong emptyHandle = 0; + DacComNullableByRef emptyOut = new(isNullRef: false); + Assert.Equal(HResults.S_FALSE, process.EnumAppDomain(&emptyHandle, emptyOut)); + Assert.Equal(HResults.S_OK, process.EndEnumAppDomains(emptyHandle)); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void Modules(MockTarget.Architecture arch) + { + TargetPointer appDomainAddress = new(0x1000); + ModuleHandle firstModule = new(new TargetPointer(0x2000)); + ModuleHandle secondModule = new(new TargetPointer(0x3000)); + IReadOnlyList modules = [firstModule, secondModule]; + Mock loader = new(MockBehavior.Strict); + loader.Setup(l => l.GetAppDomain()).Returns(appDomainAddress); + loader.Setup(l => l.GetModuleHandles( + appDomainAddress, + AssemblyIterationFlags.IncludeLoaded | AssemblyIterationFlags.IncludeExecution)).Returns(modules); + TargetPointer firstBase = new(0x10000); + uint firstSize = 0x100; + uint firstFlags = 0; + loader.Setup(l => l.TryGetLoadedImageContents(firstModule, out firstBase, out firstSize, out firstFlags)).Returns(false); + TargetPointer secondBase = new(0x20000); + uint secondSize = 0x200; + uint secondFlags = 0; + loader.Setup(l => l.TryGetLoadedImageContents(secondModule, out secondBase, out secondSize, out secondFlags)).Returns(true); + IXCLRDataProcess process = CreateProcess(arch, loader: loader.Object); + + ulong handle; + int hr = process.StartEnumModules(&handle); + Assert.Equal(HResults.S_OK, hr); + Assert.NotEqual(0ul, handle); + + try + { + foreach (ModuleHandle expected in modules) + { + DacComNullableByRef moduleOut = new(isNullRef: false); + hr = process.EnumModule(&handle, moduleOut); + Assert.Equal(HResults.S_OK, hr); + ClrDataModule module = Assert.IsType(moduleOut.Interface); + Assert.Equal(expected.Address, module.Address); + } + + DacComNullableByRef endOut = new(isNullRef: false); + hr = process.EnumModule(&handle, endOut); + Assert.Equal(HResults.S_FALSE, hr); + } + finally + { + hr = process.EndEnumModules(handle); + Assert.Equal(HResults.S_OK, hr); + } + + DacComNullableByRef byAddressOut = new(isNullRef: false); + hr = process.GetModuleByAddress(secondBase.Value + 0x80, byAddressOut); + Assert.Equal(HResults.S_OK, hr); + ClrDataModule byAddress = Assert.IsType(byAddressOut.Interface); + Assert.Equal(secondModule.Address, byAddress.Address); + + DacComNullableByRef missingOut = new(isNullRef: false); + hr = process.GetModuleByAddress(secondBase.Value + secondSize, missingOut); + Assert.Equal(HResults.S_FALSE, hr); + Assert.Equal(HResults.S_OK, process.EndEnumModules(0)); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void ModuleVersionId(MockTarget.Architecture arch) + { + TargetPointer modulePointer = new(0x1000); + ModuleHandle moduleHandle = new(new TargetPointer(0x2000)); + Guid expected = Guid.NewGuid(); + using MetadataReaderProvider provider = CreateMetadata(expected); + Mock loader = new(MockBehavior.Strict); + loader.Setup(l => l.GetModuleHandleFromModulePtr(modulePointer)).Returns(moduleHandle); + Mock metadata = new(MockBehavior.Strict); + metadata.Setup(m => m.GetMetadata(moduleHandle)).Returns(provider.GetMetadataReader()); + IXCLRDataModule module = CreateModule(arch, modulePointer, loader.Object, metadata.Object); + + Guid actual; + Assert.Equal(HResults.S_OK, module.GetVersionId(&actual)); + Assert.Equal(expected, actual); + Assert.Equal(HResults.E_POINTER, module.GetVersionId(null)); + + Mock missingMetadata = new(MockBehavior.Strict); + missingMetadata.Setup(m => m.GetMetadata(moduleHandle)).Returns((MetadataReader?)null); + module = CreateModule(arch, modulePointer, loader.Object, missingMetadata.Object); + Assert.Equal(HResults.E_FAIL, module.GetVersionId(&actual)); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void GetTaskByUniqueID(MockTarget.Architecture arch) + { + TargetPointer threadAddress = new(0x5000); + Mock thread = new(MockBehavior.Strict); + thread.Setup(t => t.IdToThread(42)).Returns(threadAddress); + thread.Setup(t => t.IdToThread(43)).Returns(TargetPointer.Null); + IXCLRDataProcess process = CreateProcess(arch, thread: thread.Object); + DacComNullableByRef taskOut = new(isNullRef: false); + + int hr = process.GetTaskByUniqueID(0x1_0000_002a, taskOut); + + Assert.Equal(HResults.S_OK, hr); + Assert.IsType(taskOut.Interface); + + DacComNullableByRef missingOut = new(isNullRef: false); + hr = process.GetTaskByUniqueID(43, missingOut); + Assert.Equal(HResults.E_INVALIDARG, hr); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void GetAddressType(MockTarget.Architecture arch) + { + const ulong CodeAddress = 0x7000; + (CodeKind Kind, uint Expected)[] cases = + [ + (CodeKind.Unknown, 0), + (CodeKind.Jitted, 1), + (CodeKind.ReadyToRun, 1), + (CodeKind.Interpreter, 1), + (CodeKind.JumpStub, 6), + (CodeKind.StubPrecode, 6), + (CodeKind.VSD_DispatchStub, 6), + ]; + + foreach ((CodeKind kind, uint expected) in cases) + { + Mock executionManager = new(MockBehavior.Strict); + executionManager.Setup(e => e.GetCodeKind(new TargetCodePointer(CodeAddress))).Returns(kind); + IXCLRDataProcess process = CreateProcess( + arch, + executionManager: executionManager.Object, + readableAddress: CodeAddress); + uint type; + + int hr = process.GetAddressType(CodeAddress, &type); + + Assert.Equal(HResults.S_OK, hr); + Assert.Equal(expected, type); + } + + IXCLRDataProcess unreadableProcess = CreateProcess(arch); + uint unreadableType; + int unreadableHr = unreadableProcess.GetAddressType(CodeAddress, &unreadableType); + Assert.Equal(HResults.S_OK, unreadableHr); + Assert.Equal(0u, unreadableType); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void GetDataByAddress(MockTarget.Architecture arch) + { + IXCLRDataProcess process = CreateProcess(arch); + DacComNullableByRef value = new(isNullRef: false); + + int hr = process.GetDataByAddress(0, 0, null, null, 0, null, null, value, null); + Assert.Equal(HResults.E_NOTIMPL, hr); + + hr = process.GetDataByAddress(0, 1, null, null, 0, null, null, value, null); + Assert.Equal(HResults.E_INVALIDARG, hr); + } + + private static IXCLRDataProcess CreateProcess( + MockTarget.Architecture arch, + ILoader? loader = null, + IThread? thread = null, + IExecutionManager? executionManager = null, + ulong? readableAddress = null) + { + TestPlaceholderTarget.Builder builder = new(arch); + if (loader is not null) + builder.AddMockContract(loader); + if (thread is not null) + builder.AddMockContract(thread); + if (executionManager is not null) + builder.AddMockContract(executionManager); + if (readableAddress is not null) + { + builder.MemoryBuilder.AddHeapFragment(new MockMemorySpace.HeapFragment + { + Address = readableAddress.Value, + Data = [0], + Name = nameof(readableAddress), + }); + } + + return new SOSDacImpl(builder.Build(), legacyObj: null); + } + + private static IXCLRDataModule CreateModule( + MockTarget.Architecture arch, + TargetPointer modulePointer, + ILoader loader, + IEcmaMetadata metadata) + { + TestPlaceholderTarget.Builder builder = new(arch); + builder.AddMockContract(loader); + builder.AddMockContract(metadata); + return new ClrDataModule(modulePointer, builder.Build(), legacyImpl: null); + } + + private static MetadataReaderProvider CreateMetadata(Guid mvid) + { + MetadataBuilder builder = new(); + builder.AddModule(0, builder.GetOrAddString("TestModule"), builder.GetOrAddGuid(mvid), default, default); + BlobBuilder blob = new(); + new MetadataRootBuilder(builder).Serialize(blob, 0, 0); + return MetadataReaderProvider.FromMetadataImage(ImmutableArray.Create(blob.ToArray())); + } +} From 61a2c7949ab2b86c5e824dfdbb9ba477cdfdc7d6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Jul 2026 21:11:06 +0000 Subject: [PATCH 2/5] Apply selected cDAC PR #130954 changes Co-authored-by: rcj1 <77995559+rcj1@users.noreply.github.com> --- .../ClrDataModule.cs | 751 +++++++++++++++++- .../ClrDataTask.cs | 2 + .../ClrDataTypeDefinition.cs | 186 +++++ .../ClrDataTypeInstance.cs | 183 +++++ .../tests/UnitTests/IXCLRDataProcessTests.cs | 319 +++++++- 5 files changed, 1426 insertions(+), 15 deletions(-) create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataTypeDefinition.cs create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataTypeInstance.cs diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataModule.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataModule.cs index eba5d486ea2d2d..60ddc7d9a98f4d 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataModule.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataModule.cs @@ -177,7 +177,7 @@ public void Start(string fullName) Enumerator = IterateMethodDefinitions().GetEnumerator(); } - private bool StringEquals(string a, string b) + internal bool StringEquals(string a, string b) { StringComparison comparison = (_flags & (uint)CLRDataByNameFlag.CLRDATA_BYNAME_CASE_INSENSITIVE) != 0 ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; return string.Equals(a, b, comparison); @@ -193,7 +193,7 @@ private IEnumerable IterateMethodDefinitions() yield break; } - private TypeDefinitionHandle? ResolveType(MetadataReader reader, string typeFullName) + internal TypeDefinitionHandle? ResolveType(MetadataReader reader, string typeFullName) { string[] nestingParts = typeFullName.Split('+', '/'); @@ -237,6 +237,342 @@ private IEnumerable IterateMethodDefinitions() } } + private sealed class EnumTypeDefinitions : IEnum + { + public IEnumerator Enumerator { get; } + public nuint LegacyHandle { get; set; } + + public EnumTypeDefinitions(MetadataReader reader, nuint legacyHandle) + { + Enumerator = reader.TypeDefinitions + .Select(handle => (uint)MetadataTokens.GetToken(handle)) + .GetEnumerator(); + LegacyHandle = legacyHandle; + } + } + + private sealed class EnumTypeInstances : IEnum<(uint Token, TypeHandle TypeHandle)> + { + private readonly Target _target; + private readonly Contracts.ModuleHandle _module; + private readonly MetadataReader _reader; + + public IEnumerator<(uint Token, TypeHandle TypeHandle)> Enumerator { get; } + public nuint LegacyHandle { get; set; } + public TargetPointer AppDomain { get; } + + public EnumTypeInstances( + Target target, + Contracts.ModuleHandle module, + MetadataReader reader, + TargetPointer appDomain, + nuint legacyHandle) + { + _target = target; + _module = module; + _reader = reader; + AppDomain = appDomain; + LegacyHandle = legacyHandle; + Enumerator = IterateTypeInstances().GetEnumerator(); + } + + private IEnumerable<(uint Token, TypeHandle TypeHandle)> IterateTypeInstances() + { + ILoader loader = _target.Contracts.Loader; + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + TargetPointer map = loader.GetLookupTables(_module).TypeDefToMethodTable; + foreach (TypeDefinitionHandle handle in _reader.TypeDefinitions) + { + uint token = (uint)MetadataTokens.GetToken(handle); + TargetPointer typePointer = loader.GetModuleLookupMapElement(map, token, out _); + if (typePointer != TargetPointer.Null) + yield return (token, rts.GetTypeHandle(typePointer)); + } + } + } + + private sealed class EnumMethodInstancesByName : IEnum + { + private readonly Target _target; + private readonly Contracts.ModuleHandle _module; + private readonly EnumMethodDefinitions _definitions; + + public IEnumerator Enumerator { get; private set; } = Enumerable.Empty().GetEnumerator(); + public nuint LegacyHandle { get; set; } + public TargetPointer AppDomain { get; } + + public EnumMethodInstancesByName( + Target target, + Contracts.ModuleHandle module, + MetadataReader reader, + uint flags, + TargetPointer appDomain, + nuint legacyHandle) + { + _target = target; + _module = module; + _definitions = new EnumMethodDefinitions(reader, flags, 0); + AppDomain = appDomain; + LegacyHandle = legacyHandle; + } + + public void Start(string name) + { + _definitions.Start(name); + Enumerator = IterateMethodInstances().GetEnumerator(); + } + + private IEnumerable IterateMethodInstances() + { + ILoader loader = _target.Contracts.Loader; + TargetPointer map = loader.GetLookupTables(_module).MethodDefToDesc; + while (_definitions.Enumerator.MoveNext()) + { + TargetPointer methodDesc = loader.GetModuleLookupMapElement(map, _definitions.Enumerator.Current, out _); + if (methodDesc == TargetPointer.Null) + continue; + + SOSDacImpl.EnumMethodInstances instances = new(_target, methodDesc, AppDomain); + if (instances.Start() != HResults.S_OK) + continue; + + using IEnumerator enumerator = instances.Enumerator; + while (enumerator.MoveNext()) + yield return enumerator.Current; + } + } + + void IEnum.Dispose() + { + Enumerator.Dispose(); + _definitions.Enumerator.Dispose(); + } + } + + private sealed class EnumDataByName : IEnum<(TargetPointer FieldDesc, TypeHandle DeclaringType, bool IsInherited)> + { + private readonly Target _target; + private readonly Contracts.ModuleHandle _module; + private readonly MetadataReader _reader; + private readonly uint _flags; + private readonly TargetPointer _thread; + private string _fieldName = string.Empty; + private TypeHandle _typeHandle; + + public IEnumerator<(TargetPointer FieldDesc, TypeHandle DeclaringType, bool IsInherited)> Enumerator { get; private set; } + = Enumerable.Empty<(TargetPointer, TypeHandle, bool)>().GetEnumerator(); + public nuint LegacyHandle { get; set; } + + public EnumDataByName( + Target target, + Contracts.ModuleHandle module, + MetadataReader reader, + uint flags, + TargetPointer thread, + nuint legacyHandle) + { + _target = target; + _module = module; + _reader = reader; + _flags = flags; + _thread = thread; + LegacyHandle = legacyHandle; + } + + public void Start(string name) + { + int separator = name.LastIndexOf('.'); + if (separator <= 0 || separator == name.Length - 1) + throw new ArgumentException(); + + string typeName = name[..separator]; + _fieldName = name[(separator + 1)..]; + EnumMethodDefinitions resolver = new(_reader, _flags, 0); + TypeDefinitionHandle? typeDefinition = resolver.ResolveType(_reader, typeName); + if (typeDefinition is null) + throw new ArgumentException(); + + uint token = (uint)MetadataTokens.GetToken(typeDefinition.Value); + ILoader loader = _target.Contracts.Loader; + TargetPointer map = loader.GetLookupTables(_module).TypeDefToMethodTable; + TargetPointer typePointer = loader.GetModuleLookupMapElement(map, token, out _); + if (typePointer == TargetPointer.Null) + throw Marshal.GetExceptionForHR(unchecked((int)0x8000ffff))!; + + _typeHandle = _target.Contracts.RuntimeTypeSystem.GetTypeHandle(typePointer); + Enumerator = IterateFields().GetEnumerator(); + } + + public ClrDataValue CreateValue( + TargetPointer fieldDesc, + TypeHandle declaringType, + bool isInherited, + IXCLRDataValue? legacyValue) + { + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + bool isThreadStatic = rts.IsFieldDescThreadStatic(fieldDesc); + uint locationFlag = isThreadStatic ? 0x00000400u : 0x00000800u; + NativeVarLocation[] locations; + if (rts.ContainsGenericVariables(declaringType)) + { + locations = []; + } + else + { + TargetPointer address; + if (isThreadStatic) + { + if (_thread == TargetPointer.Null) + throw new ArgumentException(); + address = rts.GetFieldDescThreadStaticAddress(fieldDesc, _thread, unboxValueTypes: false); + } + else + { + address = rts.GetFieldDescStaticAddress(fieldDesc, unboxValueTypes: false); + } + + locations = + [ + new() + { + AddressOrValue = address.ToClrDataAddress(_target), + Size = GetFieldSize(rts, fieldDesc, rts.GetFieldDescType(fieldDesc)), + IsRegisterValue = false, + } + ]; + } + + CorElementType elementType = rts.GetFieldDescType(fieldDesc); + uint valueFlags = GetValueFlags(rts, fieldDesc, elementType) + | locationFlag + | (isInherited ? 0x00000100u : 0); + return new ClrDataValue(_target, valueFlags, locations, legacyValue); + } + + private IEnumerable<(TargetPointer FieldDesc, TypeHandle DeclaringType, bool IsInherited)> IterateFields() + { + ILoader loader = _target.Contracts.Loader; + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + TypeHandle current = _typeHandle; + bool isInherited = false; + while (!current.IsNull) + { + TargetPointer modulePointer = rts.GetModule(current); + Contracts.ModuleHandle module = loader.GetModuleHandleFromModulePtr(modulePointer); + MetadataReader? reader = _target.Contracts.EcmaMetadata.GetMetadata(module); + uint token = rts.GetTypeDefToken(current); + if (reader is not null && token != 0) + { + TypeDefinitionHandle typeDefinition = MetadataTokens.TypeDefinitionHandle((int)(token & 0x00ff_ffff)); + TargetPointer map = loader.GetLookupTables(module).FieldDefToDesc; + foreach (FieldDefinitionHandle fieldHandle in reader.GetTypeDefinition(typeDefinition).GetFields()) + { + FieldDefinition field = reader.GetFieldDefinition(fieldHandle); + if ((field.Attributes & System.Reflection.FieldAttributes.Static) == 0) + continue; + if (!StringEquals(reader.GetString(field.Name), _fieldName)) + continue; + + TargetPointer fieldDesc = loader.GetModuleLookupMapElement( + map, + (uint)MetadataTokens.GetToken(fieldHandle), + out _); + if (fieldDesc != TargetPointer.Null) + yield return (fieldDesc, current, isInherited); + } + } + + TargetPointer parent = rts.GetParentMethodTable(current); + current = parent == TargetPointer.Null ? default : rts.GetTypeHandle(parent); + isInherited = true; + } + } + + private bool StringEquals(string left, string right) + { + StringComparison comparison = (_flags & (uint)CLRDataByNameFlag.CLRDATA_BYNAME_CASE_INSENSITIVE) != 0 + ? StringComparison.OrdinalIgnoreCase + : StringComparison.Ordinal; + return string.Equals(left, right, comparison); + } + + private static uint GetValueFlags( + IRuntimeTypeSystem rts, + TargetPointer fieldDesc, + CorElementType elementType) + { + return elementType switch + { + CorElementType.Boolean or CorElementType.Char + or CorElementType.I1 or CorElementType.U1 + or CorElementType.I2 or CorElementType.U2 + or CorElementType.I4 or CorElementType.U4 + or CorElementType.I8 or CorElementType.U8 + or CorElementType.R4 or CorElementType.R8 + or CorElementType.I or CorElementType.U => (uint)ClrDataValueFlag.IS_PRIMITIVE, + CorElementType.Class or CorElementType.Object or CorElementType.String + or CorElementType.Array or CorElementType.SzArray => (uint)ClrDataValueFlag.IS_REFERENCE, + CorElementType.Ptr or CorElementType.FnPtr => (uint)ClrDataValueFlag.IS_POINTER, + CorElementType.ValueType => rts.IsEnum(rts.GetFieldDescApproxTypeHandle(fieldDesc)) + ? (uint)ClrDataValueFlag.IS_ENUM + : (uint)ClrDataValueFlag.IS_VALUE_TYPE, + _ => (uint)ClrDataValueFlag.DEFAULT, + }; + } + + private ulong GetFieldSize( + IRuntimeTypeSystem rts, + TargetPointer fieldDesc, + CorElementType elementType) + { + return elementType switch + { + CorElementType.Boolean or CorElementType.I1 or CorElementType.U1 => 1, + CorElementType.Char or CorElementType.I2 or CorElementType.U2 => 2, + CorElementType.I4 or CorElementType.U4 or CorElementType.R4 => 4, + CorElementType.I8 or CorElementType.U8 or CorElementType.R8 => 8, + CorElementType.ValueType => rts.GetNumInstanceFieldBytes(rts.GetFieldDescApproxTypeHandle(fieldDesc)), + _ => (ulong)_target.PointerSize, + }; + } + } + + private MetadataReader GetMetadataReader() + { + ILoader loader = _target.Contracts.Loader; + Contracts.ModuleHandle module = loader.GetModuleHandleFromModulePtr(_address); + return _target.Contracts.EcmaMetadata.GetMetadata(module) + ?? throw Marshal.GetExceptionForHR(HResults.E_FAIL)!; + } + + private TargetPointer GetAppDomainAddress(IXCLRDataAppDomain? appDomain) + { + return appDomain switch + { + null => _target.Contracts.Loader.GetAppDomain(), + ClrDataAppDomain clrDataAppDomain => clrDataAppDomain.Address, + _ => throw new ArgumentException(), + }; + } + + private static TargetPointer GetThreadAddress(IXCLRDataTask? task) + { + return task switch + { + null => TargetPointer.Null, + ClrDataTask clrDataTask => clrDataTask.Address, + _ => throw new ArgumentException(), + }; + } + + private static void ValidateNameArguments(char* name, uint flags) + { + if (name is null || *name == '\0') + throw new ArgumentException(); + if ((flags & ~(uint)CLRDataByNameFlag.CLRDATA_BYNAME_CASE_INSENSITIVE) != 0) + throw new ArgumentException(); + } + int IXCLRDataModule.StartEnumAssemblies(ulong* handle) => LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.StartEnumAssemblies(handle) : HResults.E_NOTIMPL; int IXCLRDataModule.EnumAssembly(ulong* handle, DacComNullableByRef assembly) @@ -245,18 +581,208 @@ int IXCLRDataModule.EndEnumAssemblies(ulong handle) => LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.EndEnumAssemblies(handle) : HResults.E_NOTIMPL; int IXCLRDataModule.StartEnumTypeDefinitions(ulong* handle) - => LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.StartEnumTypeDefinitions(handle) : HResults.E_NOTIMPL; + { + ulong legacyHandle = 0; + int hrLocal = HResults.S_OK; + try + { + if (handle is null) + throw new NullReferenceException(); + *handle = 0; + + if (_legacyModule is not null) + hrLocal = _legacyModule.StartEnumTypeDefinitions(&legacyHandle); + + MetadataReader reader = GetMetadataReader(); + EnumTypeDefinitions state = new(reader, (nuint)legacyHandle); + *handle = (ulong)((IEnum)state).GetHandle(); + legacyHandle = 0; + return HResults.S_OK; + } + catch (System.Exception ex) + { + return ex.HResult; + } + finally + { + if (_legacyModule is not null && legacyHandle != 0) + _legacyModule.EndEnumTypeDefinitions(legacyHandle); + } + } + int IXCLRDataModule.EnumTypeDefinition(ulong* handle, DacComNullableByRef typeDefinition) - => LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.EnumTypeDefinition(handle, typeDefinition) : HResults.E_NOTIMPL; + { + try + { + if (handle is null || typeDefinition.IsNullRef) + throw new NullReferenceException(); + + GCHandle gcHandle = GCHandle.FromIntPtr((IntPtr)(*handle)); + if (gcHandle.Target is not EnumTypeDefinitions state) + throw new ArgumentException(); + + IXCLRDataTypeDefinition? legacyType = null; + int hrLocal = HResults.S_OK; + if (_legacyModule is not null) + { + ulong legacyHandle = state.LegacyHandle; + DacComNullableByRef legacyOut = new(isNullRef: false); + hrLocal = _legacyModule.EnumTypeDefinition(&legacyHandle, legacyOut); + state.LegacyHandle = (nuint)legacyHandle; + legacyType = legacyOut.Interface; + } + + int hr; + if (state.Enumerator.MoveNext()) + { + typeDefinition.Interface = new ClrDataTypeDefinition( + _target, + _address, + state.Enumerator.Current, + typeHandle: null, + legacyType); + hr = HResults.S_OK; + } + else + { + hr = HResults.S_FALSE; + } + +#if DEBUG + if (_legacyModule is not null) + Debug.ValidateHResult(hr, hrLocal); +#endif + return hr; + } + catch (System.Exception ex) + { + return ex.HResult; + } + } + int IXCLRDataModule.EndEnumTypeDefinitions(ulong handle) - => LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.EndEnumTypeDefinitions(handle) : HResults.E_NOTIMPL; + { + try + { + GCHandle gcHandle = GCHandle.FromIntPtr((IntPtr)handle); + if (gcHandle.Target is not EnumTypeDefinitions state) + throw new ArgumentException(); + + ((IEnum)state).Dispose(); + gcHandle.Free(); + return _legacyModule is not null && state.LegacyHandle != 0 + ? _legacyModule.EndEnumTypeDefinitions(state.LegacyHandle) + : HResults.S_OK; + } + catch (System.Exception ex) + { + return ex.HResult; + } + } int IXCLRDataModule.StartEnumTypeInstances(IXCLRDataAppDomain? appDomain, ulong* handle) - => LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.StartEnumTypeInstances(appDomain, handle) : HResults.E_NOTIMPL; + { + ulong legacyHandle = 0; + try + { + if (handle is null) + throw new NullReferenceException(); + *handle = 0; + + if (_legacyModule is not null) + _legacyModule.StartEnumTypeInstances(appDomain, &legacyHandle); + + ILoader loader = _target.Contracts.Loader; + Contracts.ModuleHandle module = loader.GetModuleHandleFromModulePtr(_address); + EnumTypeInstances state = new( + _target, + module, + GetMetadataReader(), + GetAppDomainAddress(appDomain), + (nuint)legacyHandle); + *handle = (ulong)((IEnum<(uint Token, TypeHandle TypeHandle)>)state).GetHandle(); + legacyHandle = 0; + return HResults.S_OK; + } + catch (System.Exception ex) + { + return ex.HResult; + } + finally + { + if (_legacyModule is not null && legacyHandle != 0) + _legacyModule.EndEnumTypeInstances(legacyHandle); + } + } + int IXCLRDataModule.EnumTypeInstance(ulong* handle, DacComNullableByRef typeInstance) - => LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.EnumTypeInstance(handle, typeInstance) : HResults.E_NOTIMPL; + { + try + { + if (handle is null || typeInstance.IsNullRef) + throw new NullReferenceException(); + + GCHandle gcHandle = GCHandle.FromIntPtr((IntPtr)(*handle)); + if (gcHandle.Target is not EnumTypeInstances state) + throw new ArgumentException(); + + IXCLRDataTypeInstance? legacyType = null; + int hrLocal = HResults.S_OK; + if (_legacyModule is not null) + { + ulong legacyHandle = state.LegacyHandle; + DacComNullableByRef legacyOut = new(isNullRef: false); + hrLocal = _legacyModule.EnumTypeInstance(&legacyHandle, legacyOut); + state.LegacyHandle = (nuint)legacyHandle; + legacyType = legacyOut.Interface; + } + + int hr; + if (state.Enumerator.MoveNext()) + { + typeInstance.Interface = new ClrDataTypeInstance( + _target, + state.Enumerator.Current.TypeHandle, + state.AppDomain, + legacyType); + hr = HResults.S_OK; + } + else + { + hr = HResults.S_FALSE; + } + +#if DEBUG + if (_legacyModule is not null) + Debug.ValidateHResult(hr, hrLocal); +#endif + return hr; + } + catch (System.Exception ex) + { + return ex.HResult; + } + } + int IXCLRDataModule.EndEnumTypeInstances(ulong handle) - => LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.EndEnumTypeInstances(handle) : HResults.E_NOTIMPL; + { + try + { + GCHandle gcHandle = GCHandle.FromIntPtr((IntPtr)handle); + if (gcHandle.Target is not EnumTypeInstances state) + throw new ArgumentException(); + + ((IEnum<(uint Token, TypeHandle TypeHandle)>)state).Dispose(); + gcHandle.Free(); + return _legacyModule is not null && state.LegacyHandle != 0 + ? _legacyModule.EndEnumTypeInstances(state.LegacyHandle) + : HResults.S_OK; + } + catch (System.Exception ex) + { + return ex.HResult; + } + } int IXCLRDataModule.StartEnumTypeDefinitionsByName(char* name, uint flags, ulong* handle) => LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.StartEnumTypeDefinitionsByName(name, flags, handle) : HResults.E_NOTIMPL; @@ -403,11 +929,111 @@ int IXCLRDataModule.EndEnumMethodDefinitionsByName(ulong handle) return hr; } int IXCLRDataModule.StartEnumMethodInstancesByName(char* name, uint flags, IXCLRDataAppDomain? appDomain, ulong* handle) - => LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.StartEnumMethodInstancesByName(name, flags, appDomain, handle) : HResults.E_NOTIMPL; + { + ulong legacyHandle = 0; + try + { + if (handle is null) + throw new NullReferenceException(); + *handle = 0; + + if (_legacyModule is not null) + _legacyModule.StartEnumMethodInstancesByName(name, flags, appDomain, &legacyHandle); + ValidateNameArguments(name, flags); + + ILoader loader = _target.Contracts.Loader; + Contracts.ModuleHandle module = loader.GetModuleHandleFromModulePtr(_address); + EnumMethodInstancesByName state = new( + _target, + module, + GetMetadataReader(), + flags, + GetAppDomainAddress(appDomain), + (nuint)legacyHandle); + state.Start(new string(name)); + *handle = (ulong)((IEnum)state).GetHandle(); + legacyHandle = 0; + return HResults.S_OK; + } + catch (System.Exception ex) + { + return ex.HResult; + } + finally + { + if (_legacyModule is not null && legacyHandle != 0) + _legacyModule.EndEnumMethodInstancesByName(legacyHandle); + } + } + int IXCLRDataModule.EnumMethodInstanceByName(ulong* handle, DacComNullableByRef method) - => LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.EnumMethodInstanceByName(handle, method) : HResults.E_NOTIMPL; + { + try + { + if (handle is null || method.IsNullRef) + throw new NullReferenceException(); + + GCHandle gcHandle = GCHandle.FromIntPtr((IntPtr)(*handle)); + if (gcHandle.Target is not EnumMethodInstancesByName state) + throw new ArgumentException(); + + IXCLRDataMethodInstance? legacyMethod = null; + int hrLocal = HResults.S_OK; + if (_legacyModule is not null) + { + ulong legacyHandle = state.LegacyHandle; + DacComNullableByRef legacyOut = new(isNullRef: false); + hrLocal = _legacyModule.EnumMethodInstanceByName(&legacyHandle, legacyOut); + state.LegacyHandle = (nuint)legacyHandle; + legacyMethod = legacyOut.Interface; + } + + int hr; + if (state.Enumerator.MoveNext()) + { + method.Interface = new ClrDataMethodInstance( + _target, + state.Enumerator.Current, + state.AppDomain, + legacyMethod); + hr = HResults.S_OK; + } + else + { + hr = HResults.S_FALSE; + } + +#if DEBUG + if (_legacyModule is not null) + Debug.ValidateHResult(hr, hrLocal); +#endif + return hr; + } + catch (System.Exception ex) + { + return ex.HResult; + } + } + int IXCLRDataModule.EndEnumMethodInstancesByName(ulong handle) - => LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.EndEnumMethodInstancesByName(handle) : HResults.E_NOTIMPL; + { + try + { + GCHandle gcHandle = GCHandle.FromIntPtr((IntPtr)handle); + if (gcHandle.Target is not EnumMethodInstancesByName state) + throw new ArgumentException(); + + ((IEnum)state).Dispose(); + gcHandle.Free(); + return _legacyModule is not null && state.LegacyHandle != 0 + ? _legacyModule.EndEnumMethodInstancesByName(state.LegacyHandle) + : HResults.S_OK; + } + catch (System.Exception ex) + { + return ex.HResult; + } + } int IXCLRDataModule.GetMethodDefinitionByToken(/*mdMethodDef*/ uint token, DacComNullableByRef methodDefinition) { @@ -444,11 +1070,108 @@ int IXCLRDataModule.GetMethodDefinitionByToken(/*mdMethodDef*/ uint token, DacCo } int IXCLRDataModule.StartEnumDataByName(char* name, uint flags, IXCLRDataAppDomain? appDomain, IXCLRDataTask? tlsTask, ulong* handle) - => LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.StartEnumDataByName(name, flags, appDomain, tlsTask, handle) : HResults.E_NOTIMPL; + { + ulong legacyHandle = 0; + try + { + if (handle is null) + throw new NullReferenceException(); + *handle = 0; + + if (_legacyModule is not null) + _legacyModule.StartEnumDataByName(name, flags, appDomain, tlsTask, &legacyHandle); + ValidateNameArguments(name, flags); + + ILoader loader = _target.Contracts.Loader; + Contracts.ModuleHandle module = loader.GetModuleHandleFromModulePtr(_address); + EnumDataByName state = new( + _target, + module, + GetMetadataReader(), + flags, + GetThreadAddress(tlsTask), + (nuint)legacyHandle); + state.Start(new string(name)); + *handle = (ulong)((IEnum<(TargetPointer FieldDesc, TypeHandle DeclaringType, bool IsInherited)>)state).GetHandle(); + legacyHandle = 0; + return HResults.S_OK; + } + catch (System.Exception ex) + { + return ex.HResult; + } + finally + { + if (_legacyModule is not null && legacyHandle != 0) + _legacyModule.EndEnumDataByName(legacyHandle); + } + } + int IXCLRDataModule.EnumDataByName(ulong* handle, DacComNullableByRef value) - => LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.EnumDataByName(handle, value) : HResults.E_NOTIMPL; + { + try + { + if (handle is null || value.IsNullRef) + throw new NullReferenceException(); + + GCHandle gcHandle = GCHandle.FromIntPtr((IntPtr)(*handle)); + if (gcHandle.Target is not EnumDataByName state) + throw new ArgumentException(); + + IXCLRDataValue? legacyValue = null; + int hrLocal = HResults.S_OK; + if (_legacyModule is not null) + { + ulong legacyHandle = state.LegacyHandle; + DacComNullableByRef legacyOut = new(isNullRef: false); + hrLocal = _legacyModule.EnumDataByName(&legacyHandle, legacyOut); + state.LegacyHandle = (nuint)legacyHandle; + legacyValue = legacyOut.Interface; + } + + int hr; + if (state.Enumerator.MoveNext()) + { + (TargetPointer fieldDesc, TypeHandle declaringType, bool isInherited) = state.Enumerator.Current; + value.Interface = state.CreateValue(fieldDesc, declaringType, isInherited, legacyValue); + hr = HResults.S_OK; + } + else + { + hr = HResults.S_FALSE; + } + +#if DEBUG + if (_legacyModule is not null) + Debug.ValidateHResult(hr, hrLocal); +#endif + return hr; + } + catch (System.Exception ex) + { + return ex.HResult; + } + } + int IXCLRDataModule.EndEnumDataByName(ulong handle) - => LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.EndEnumDataByName(handle) : HResults.E_NOTIMPL; + { + try + { + GCHandle gcHandle = GCHandle.FromIntPtr((IntPtr)handle); + if (gcHandle.Target is not EnumDataByName state) + throw new ArgumentException(); + + ((IEnum<(TargetPointer FieldDesc, TypeHandle DeclaringType, bool IsInherited)>)state).Dispose(); + gcHandle.Free(); + return _legacyModule is not null && state.LegacyHandle != 0 + ? _legacyModule.EndEnumDataByName(state.LegacyHandle) + : HResults.S_OK; + } + catch (System.Exception ex) + { + return ex.HResult; + } + } int IXCLRDataModule.GetName(uint bufLen, uint* nameLen, char* name) { diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataTask.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataTask.cs index 08ac8cc706117e..f49d8c60f5348b 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataTask.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataTask.cs @@ -16,6 +16,8 @@ public sealed unsafe partial class ClrDataTask : IXCLRDataTask private readonly Target _target; private readonly IXCLRDataTask? _legacyImpl; + internal TargetPointer Address => _address; + public ClrDataTask(TargetPointer address, Target target, IXCLRDataTask? legacyImpl) { _address = address; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataTypeDefinition.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataTypeDefinition.cs new file mode 100644 index 00000000000000..14dbf2ba411deb --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataTypeDefinition.cs @@ -0,0 +1,186 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Runtime.InteropServices.Marshalling; +using System.Text; +using Microsoft.Diagnostics.DataContractReader.Contracts; + +namespace Microsoft.Diagnostics.DataContractReader.Legacy; + +[GeneratedComClass] +public sealed unsafe partial class ClrDataTypeDefinition : IXCLRDataTypeDefinition +{ + private readonly Target _target; + private readonly TargetPointer _module; + private readonly uint _token; + private readonly TypeHandle? _typeHandle; + private readonly IXCLRDataTypeDefinition? _legacyImpl; + + public ClrDataTypeDefinition( + Target target, + TargetPointer module, + uint token, + TypeHandle? typeHandle, + IXCLRDataTypeDefinition? legacyImpl) + { + _target = target; + _module = module; + _token = token; + _typeHandle = typeHandle; + _legacyImpl = legacyImpl; + } + + int IXCLRDataTypeDefinition.GetModule(DacComNullableByRef mod) + { + try + { + IXCLRDataModule? legacyModule = null; + if (_legacyImpl is not null) + { + DacComNullableByRef legacyOut = new(isNullRef: false); + int hr = _legacyImpl.GetModule(legacyOut); + if (hr < 0) + return hr; + legacyModule = legacyOut.Interface; + } + + mod.Interface = new ClrDataModule(_module, _target, legacyModule); + return HResults.S_OK; + } + catch (System.Exception ex) + { + return ex.HResult; + } + } + + int IXCLRDataTypeDefinition.GetName(uint flags, uint bufLen, uint* nameLen, char* nameBuf) + { + try + { + if (flags != 0) + throw new ArgumentException(); + + string name = GetMetadataName(); + OutputBufferHelpers.CopyStringToBuffer(nameBuf, bufLen, nameLen, name); + return nameBuf is not null && bufLen < name.Length + 1 ? HResults.S_FALSE : HResults.S_OK; + } + catch (System.Exception ex) + { + return ex.HResult; + } + } + + int IXCLRDataTypeDefinition.GetTokenAndScope(uint* token, DacComNullableByRef mod) + { + try + { + if (token is not null) + *token = _token; + + if (!mod.IsNullRef) + return ((IXCLRDataTypeDefinition)this).GetModule(mod); + + return HResults.S_OK; + } + catch (System.Exception ex) + { + return ex.HResult; + } + } + + private string GetMetadataName() + { + ILoader loader = _target.Contracts.Loader; + Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(_module); + MetadataReader reader = _target.Contracts.EcmaMetadata.GetMetadata(moduleHandle) + ?? throw new InvalidOperationException(); + TypeDefinitionHandle handle = MetadataTokens.TypeDefinitionHandle((int)(_token & 0x00ff_ffff)); + StringBuilder builder = new(); + AppendTypeName(reader, handle, builder); + return builder.ToString(); + } + + private static void AppendTypeName(MetadataReader reader, TypeDefinitionHandle handle, StringBuilder builder) + { + TypeDefinition type = reader.GetTypeDefinition(handle); + TypeDefinitionHandle declaringType = type.GetDeclaringType(); + if (!declaringType.IsNil) + { + AppendTypeName(reader, declaringType, builder); + builder.Append('+'); + } + else + { + string @namespace = reader.GetString(type.Namespace); + if (@namespace.Length != 0) + { + builder.Append(@namespace); + builder.Append('.'); + } + } + + builder.Append(reader.GetString(type.Name)); + } + + int IXCLRDataTypeDefinition.StartEnumMethodDefinitions(ulong* handle) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.StartEnumMethodDefinitions(handle) : HResults.E_NOTIMPL; + int IXCLRDataTypeDefinition.EnumMethodDefinition(ulong* handle, DacComNullableByRef methodDefinition) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.EnumMethodDefinition(handle, methodDefinition) : HResults.E_NOTIMPL; + int IXCLRDataTypeDefinition.EndEnumMethodDefinitions(ulong handle) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.EndEnumMethodDefinitions(handle) : HResults.E_NOTIMPL; + int IXCLRDataTypeDefinition.StartEnumMethodDefinitionsByName(char* name, uint flags, ulong* handle) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.StartEnumMethodDefinitionsByName(name, flags, handle) : HResults.E_NOTIMPL; + int IXCLRDataTypeDefinition.EnumMethodDefinitionByName(ulong* handle, DacComNullableByRef method) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.EnumMethodDefinitionByName(handle, method) : HResults.E_NOTIMPL; + int IXCLRDataTypeDefinition.EndEnumMethodDefinitionsByName(ulong handle) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.EndEnumMethodDefinitionsByName(handle) : HResults.E_NOTIMPL; + int IXCLRDataTypeDefinition.GetMethodDefinitionByToken(uint token, DacComNullableByRef methodDefinition) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetMethodDefinitionByToken(token, methodDefinition) : HResults.E_NOTIMPL; + int IXCLRDataTypeDefinition.StartEnumInstances(IXCLRDataAppDomain? appDomain, ulong* handle) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.StartEnumInstances(appDomain, handle) : HResults.E_NOTIMPL; + int IXCLRDataTypeDefinition.EnumInstance(ulong* handle, DacComNullableByRef instance) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.EnumInstance(handle, instance) : HResults.E_NOTIMPL; + int IXCLRDataTypeDefinition.EndEnumInstances(ulong handle) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.EndEnumInstances(handle) : HResults.E_NOTIMPL; + int IXCLRDataTypeDefinition.GetCorElementType(uint* type) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetCorElementType(type) : HResults.E_NOTIMPL; + int IXCLRDataTypeDefinition.GetFlags(uint* flags) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetFlags(flags) : HResults.E_NOTIMPL; + int IXCLRDataTypeDefinition.IsSameObject(IXCLRDataTypeDefinition? type) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.IsSameObject(type) : HResults.E_NOTIMPL; + int IXCLRDataTypeDefinition.Request(uint reqCode, uint inBufferSize, byte* inBuffer, uint outBufferSize, byte* outBuffer) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.Request(reqCode, inBufferSize, inBuffer, outBufferSize, outBuffer) : HResults.E_NOTIMPL; + int IXCLRDataTypeDefinition.GetArrayRank(uint* rank) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetArrayRank(rank) : HResults.E_NOTIMPL; + int IXCLRDataTypeDefinition.GetBase(DacComNullableByRef @base) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetBase(@base) : HResults.E_NOTIMPL; + int IXCLRDataTypeDefinition.GetNumFields(uint flags, uint* numFields) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetNumFields(flags, numFields) : HResults.E_NOTIMPL; + int IXCLRDataTypeDefinition.StartEnumFields(uint flags, ulong* handle) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.StartEnumFields(flags, handle) : HResults.E_NOTIMPL; + int IXCLRDataTypeDefinition.EnumField(ulong* handle, uint nameBufLen, uint* nameLen, char* nameBuf, DacComNullableByRef type, uint* flags, uint* token) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.EnumField(handle, nameBufLen, nameLen, nameBuf, type, flags, token) : HResults.E_NOTIMPL; + int IXCLRDataTypeDefinition.EndEnumFields(ulong handle) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.EndEnumFields(handle) : HResults.E_NOTIMPL; + int IXCLRDataTypeDefinition.StartEnumFieldsByName(char* name, uint nameFlags, uint fieldFlags, ulong* handle) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.StartEnumFieldsByName(name, nameFlags, fieldFlags, handle) : HResults.E_NOTIMPL; + int IXCLRDataTypeDefinition.EnumFieldByName(ulong* handle, DacComNullableByRef type, uint* flags, uint* token) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.EnumFieldByName(handle, type, flags, token) : HResults.E_NOTIMPL; + int IXCLRDataTypeDefinition.EndEnumFieldsByName(ulong handle) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.EndEnumFieldsByName(handle) : HResults.E_NOTIMPL; + int IXCLRDataTypeDefinition.GetFieldByToken(uint token, uint nameBufLen, uint* nameLen, char* nameBuf, DacComNullableByRef type, uint* flags) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetFieldByToken(token, nameBufLen, nameLen, nameBuf, type, flags) : HResults.E_NOTIMPL; + int IXCLRDataTypeDefinition.GetTypeNotification(uint* flags) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetTypeNotification(flags) : HResults.E_NOTIMPL; + int IXCLRDataTypeDefinition.SetTypeNotification(uint flags) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.SetTypeNotification(flags) : HResults.E_NOTIMPL; + int IXCLRDataTypeDefinition.EnumField2(ulong* handle, uint nameBufLen, uint* nameLen, char* nameBuf, DacComNullableByRef type, uint* flags, DacComNullableByRef tokenScope, uint* token) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.EnumField2(handle, nameBufLen, nameLen, nameBuf, type, flags, tokenScope, token) : HResults.E_NOTIMPL; + int IXCLRDataTypeDefinition.EnumFieldByName2(ulong* handle, DacComNullableByRef type, uint* flags, DacComNullableByRef tokenScope, uint* token) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.EnumFieldByName2(handle, type, flags, tokenScope, token) : HResults.E_NOTIMPL; + int IXCLRDataTypeDefinition.GetFieldByToken2(IXCLRDataModule? tokenScope, uint token, uint nameBufLen, uint* nameLen, char* nameBuf, DacComNullableByRef type, uint* flags) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetFieldByToken2(tokenScope, token, nameBufLen, nameLen, nameBuf, type, flags) : HResults.E_NOTIMPL; +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataTypeInstance.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataTypeInstance.cs new file mode 100644 index 00000000000000..10e808c2d1e840 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataTypeInstance.cs @@ -0,0 +1,183 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices.Marshalling; +using System.Text; +using Microsoft.Diagnostics.DataContractReader.Contracts; + +namespace Microsoft.Diagnostics.DataContractReader.Legacy; + +[GeneratedComClass] +public sealed unsafe partial class ClrDataTypeInstance : IXCLRDataTypeInstance +{ + private readonly Target _target; + private readonly TypeHandle _typeHandle; + private readonly TargetPointer _appDomain; + private readonly IXCLRDataTypeInstance? _legacyImpl; + + public ClrDataTypeInstance(Target target, TypeHandle typeHandle, TargetPointer appDomain, IXCLRDataTypeInstance? legacyImpl) + { + _target = target; + _typeHandle = typeHandle; + _appDomain = appDomain; + _legacyImpl = legacyImpl; + } + + int IXCLRDataTypeInstance.GetName(uint flags, uint bufLen, uint* nameLen, char* nameBuf) + { + try + { + if (flags != 0) + throw new ArgumentException(); + + StringBuilder builder = new(); + TypeNameBuilder.AppendType( + _target, + builder, + _typeHandle, + TypeNameFormat.FormatNamespace | TypeNameFormat.FormatFullInst); + OutputBufferHelpers.CopyStringToBuffer(nameBuf, bufLen, nameLen, builder.ToString()); + return nameBuf is not null && bufLen < builder.Length + 1 ? HResults.S_FALSE : HResults.S_OK; + } + catch (System.Exception ex) + { + return ex.HResult; + } + } + + int IXCLRDataTypeInstance.GetModule(DacComNullableByRef mod) + { + try + { + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + IXCLRDataModule? legacyModule = null; + if (_legacyImpl is not null) + { + DacComNullableByRef legacyOut = new(isNullRef: false); + int hr = _legacyImpl.GetModule(legacyOut); + if (hr < 0) + return hr; + legacyModule = legacyOut.Interface; + } + + mod.Interface = new ClrDataModule(rts.GetModule(_typeHandle), _target, legacyModule); + return HResults.S_OK; + } + catch (System.Exception ex) + { + return ex.HResult; + } + } + + int IXCLRDataTypeInstance.GetDefinition(DacComNullableByRef typeDefinition) + { + try + { + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + IXCLRDataTypeDefinition? legacyDefinition = null; + if (_legacyImpl is not null) + { + DacComNullableByRef legacyOut = new(isNullRef: false); + int hr = _legacyImpl.GetDefinition(legacyOut); + if (hr < 0) + return hr; + legacyDefinition = legacyOut.Interface; + } + + typeDefinition.Interface = new ClrDataTypeDefinition( + _target, + rts.GetModule(_typeHandle), + rts.GetTypeDefToken(_typeHandle), + _typeHandle, + legacyDefinition); + return HResults.S_OK; + } + catch (System.Exception ex) + { + return ex.HResult; + } + } + + int IXCLRDataTypeInstance.GetNumTypeArguments(uint* numTypeArgs) + { + try + { + *numTypeArgs = (uint)_target.Contracts.RuntimeTypeSystem.GetInstantiation(_typeHandle).Length; + return HResults.S_OK; + } + catch (System.Exception ex) + { + return ex.HResult; + } + } + + int IXCLRDataTypeInstance.GetTypeArgumentByIndex(uint index, DacComNullableByRef typeArg) + { + try + { + ReadOnlySpan arguments = _target.Contracts.RuntimeTypeSystem.GetInstantiation(_typeHandle); + if (index >= (uint)arguments.Length) + throw new ArgumentException(); + typeArg.Interface = new ClrDataTypeInstance(_target, arguments[(int)index], _appDomain, legacyImpl: null); + return HResults.S_OK; + } + catch (System.Exception ex) + { + return ex.HResult; + } + } + + int IXCLRDataTypeInstance.StartEnumMethodInstances(ulong* handle) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.StartEnumMethodInstances(handle) : HResults.E_NOTIMPL; + int IXCLRDataTypeInstance.EnumMethodInstance(ulong* handle, DacComNullableByRef methodInstance) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.EnumMethodInstance(handle, methodInstance) : HResults.E_NOTIMPL; + int IXCLRDataTypeInstance.EndEnumMethodInstances(ulong handle) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.EndEnumMethodInstances(handle) : HResults.E_NOTIMPL; + int IXCLRDataTypeInstance.StartEnumMethodInstancesByName(char* name, uint flags, ulong* handle) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.StartEnumMethodInstancesByName(name, flags, handle) : HResults.E_NOTIMPL; + int IXCLRDataTypeInstance.EnumMethodInstanceByName(ulong* handle, DacComNullableByRef method) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.EnumMethodInstanceByName(handle, method) : HResults.E_NOTIMPL; + int IXCLRDataTypeInstance.EndEnumMethodInstancesByName(ulong handle) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.EndEnumMethodInstancesByName(handle) : HResults.E_NOTIMPL; + int IXCLRDataTypeInstance.GetNumStaticFields(uint* numFields) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetNumStaticFields(numFields) : HResults.E_NOTIMPL; + int IXCLRDataTypeInstance.GetStaticFieldByIndex(uint index, IXCLRDataTask? tlsTask, DacComNullableByRef field, uint bufLen, uint* nameLen, char* nameBuf, uint* token) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetStaticFieldByIndex(index, tlsTask, field, bufLen, nameLen, nameBuf, token) : HResults.E_NOTIMPL; + int IXCLRDataTypeInstance.StartEnumStaticFieldsByName(char* name, uint flags, IXCLRDataTask? tlsTask, ulong* handle) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.StartEnumStaticFieldsByName(name, flags, tlsTask, handle) : HResults.E_NOTIMPL; + int IXCLRDataTypeInstance.EnumStaticFieldByName(ulong* handle, DacComNullableByRef value) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.EnumStaticFieldByName(handle, value) : HResults.E_NOTIMPL; + int IXCLRDataTypeInstance.EndEnumStaticFieldsByName(ulong handle) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.EndEnumStaticFieldsByName(handle) : HResults.E_NOTIMPL; + int IXCLRDataTypeInstance.GetFlags(uint* flags) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetFlags(flags) : HResults.E_NOTIMPL; + int IXCLRDataTypeInstance.IsSameObject(IXCLRDataTypeInstance? type) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.IsSameObject(type) : HResults.E_NOTIMPL; + int IXCLRDataTypeInstance.Request(uint reqCode, uint inBufferSize, byte* inBuffer, uint outBufferSize, byte* outBuffer) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.Request(reqCode, inBufferSize, inBuffer, outBufferSize, outBuffer) : HResults.E_NOTIMPL; + int IXCLRDataTypeInstance.GetNumStaticFields2(uint flags, uint* numFields) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetNumStaticFields2(flags, numFields) : HResults.E_NOTIMPL; + int IXCLRDataTypeInstance.StartEnumStaticFields(uint flags, IXCLRDataTask? tlsTask, ulong* handle) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.StartEnumStaticFields(flags, tlsTask, handle) : HResults.E_NOTIMPL; + int IXCLRDataTypeInstance.EnumStaticField(ulong* handle, DacComNullableByRef value) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.EnumStaticField(handle, value) : HResults.E_NOTIMPL; + int IXCLRDataTypeInstance.EndEnumStaticFields(ulong handle) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.EndEnumStaticFields(handle) : HResults.E_NOTIMPL; + int IXCLRDataTypeInstance.StartEnumStaticFieldsByName2(char* name, uint nameFlags, uint fieldFlags, IXCLRDataTask? tlsTask, ulong* handle) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.StartEnumStaticFieldsByName2(name, nameFlags, fieldFlags, tlsTask, handle) : HResults.E_NOTIMPL; + int IXCLRDataTypeInstance.EnumStaticFieldByName2(ulong* handle, DacComNullableByRef value) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.EnumStaticFieldByName2(handle, value) : HResults.E_NOTIMPL; + int IXCLRDataTypeInstance.EndEnumStaticFieldsByName2(ulong handle) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.EndEnumStaticFieldsByName2(handle) : HResults.E_NOTIMPL; + int IXCLRDataTypeInstance.GetStaticFieldByToken(uint token, IXCLRDataTask? tlsTask, DacComNullableByRef field, uint bufLen, uint* nameLen, char* nameBuf) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetStaticFieldByToken(token, tlsTask, field, bufLen, nameLen, nameBuf) : HResults.E_NOTIMPL; + int IXCLRDataTypeInstance.GetBase(DacComNullableByRef @base) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetBase(@base) : HResults.E_NOTIMPL; + int IXCLRDataTypeInstance.EnumStaticField2(ulong* handle, DacComNullableByRef value, uint bufLen, uint* nameLen, char* nameBuf, DacComNullableByRef tokenScope, uint* token) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.EnumStaticField2(handle, value, bufLen, nameLen, nameBuf, tokenScope, token) : HResults.E_NOTIMPL; + int IXCLRDataTypeInstance.EnumStaticFieldByName3(ulong* handle, DacComNullableByRef value, DacComNullableByRef tokenScope, uint* token) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.EnumStaticFieldByName3(handle, value, tokenScope, token) : HResults.E_NOTIMPL; + int IXCLRDataTypeInstance.GetStaticFieldByToken2(IXCLRDataModule? tokenScope, uint token, IXCLRDataTask? tlsTask, DacComNullableByRef field, uint bufLen, uint* nameLen, char* nameBuf) + => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetStaticFieldByToken2(tokenScope, token, tlsTask, field, bufLen, nameLen, nameBuf) : HResults.E_NOTIMPL; +} diff --git a/src/native/managed/cdac/tests/UnitTests/IXCLRDataProcessTests.cs b/src/native/managed/cdac/tests/UnitTests/IXCLRDataProcessTests.cs index 8bf8aa010e2b15..4eb63fa3f222e1 100644 --- a/src/native/managed/cdac/tests/UnitTests/IXCLRDataProcessTests.cs +++ b/src/native/managed/cdac/tests/UnitTests/IXCLRDataProcessTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using Microsoft.Diagnostics.DataContractReader.Contracts; @@ -151,6 +152,195 @@ public void ModuleVersionId(MockTarget.Architecture arch) Assert.Equal(HResults.E_FAIL, module.GetVersionId(&actual)); } + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void ModuleEnumerations(MockTarget.Architecture arch) + { + const uint ModuleTypeToken = 0x02000001; + const uint TypeToken = 0x02000002; + const uint MethodToken = 0x06000001; + const uint FieldToken = 0x04000001; + const uint ThreadStaticFieldToken = 0x04000002; + const uint ValueTypeFieldToken = 0x04000003; + TargetPointer modulePointer = new(0x1000); + ModuleHandle moduleHandle = new(new TargetPointer(0x2000)); + TargetPointer typeMap = new(0x3000); + TargetPointer methodMap = new(0x3100); + TargetPointer fieldMap = new(0x3200); + TargetPointer methodTable = new(0x4000); + TargetPointer methodDesc = new(0x5000); + TargetPointer fieldDesc = new(0x6000); + TargetPointer threadStaticFieldDesc = new(0x6100); + TargetPointer valueTypeFieldDesc = new(0x6200); + TargetPointer fieldAddress = new(0x7000); + TargetPointer threadStaticFieldAddress = new(0x7100); + TargetPointer valueTypeFieldAddress = new(0x7200); + TargetPointer appDomain = new(0x8000); + TargetPointer thread = new(0x8100); + TypeHandle typeHandle = new(methodTable); + MethodDescHandle methodHandle = new(methodDesc); + ModuleLookupTables maps = new( + fieldMap, + TargetPointer.Null, + TargetPointer.Null, + methodMap, + typeMap, + TargetPointer.Null, + TargetPointer.Null, + 0); + + using MetadataReaderProvider provider = CreateEnumerationMetadata(); + MetadataReader reader = provider.GetMetadataReader(); + Mock loader = new(MockBehavior.Strict); + loader.Setup(l => l.GetModuleHandleFromModulePtr(modulePointer)).Returns(moduleHandle); + loader.Setup(l => l.GetLookupTables(moduleHandle)).Returns(maps); + loader.Setup(l => l.GetAppDomain()).Returns(appDomain); + loader.Setup(l => l.GetModuleLookupMapElement(typeMap, ModuleTypeToken, out It.Ref.IsAny)).Returns(TargetPointer.Null); + loader.Setup(l => l.GetModuleLookupMapElement(typeMap, TypeToken, out It.Ref.IsAny)).Returns(methodTable); + loader.Setup(l => l.GetModuleLookupMapElement(methodMap, MethodToken, out It.Ref.IsAny)).Returns(methodDesc); + loader.Setup(l => l.GetModuleLookupMapElement(fieldMap, FieldToken, out It.Ref.IsAny)).Returns(fieldDesc); + loader.Setup(l => l.GetModuleLookupMapElement(fieldMap, ThreadStaticFieldToken, out It.Ref.IsAny)).Returns(threadStaticFieldDesc); + loader.Setup(l => l.GetModuleLookupMapElement(fieldMap, ValueTypeFieldToken, out It.Ref.IsAny)).Returns(valueTypeFieldDesc); + + Mock metadata = new(MockBehavior.Strict); + metadata.Setup(m => m.GetMetadata(moduleHandle)).Returns(reader); + + IRuntimeTypeSystem rts = new EnumerationRuntimeTypeSystem( + modulePointer, + typeHandle, + TypeToken, + methodHandle, + MethodToken, + fieldDesc, + fieldAddress, + threadStaticFieldDesc, + threadStaticFieldAddress, + valueTypeFieldDesc, + valueTypeFieldAddress); + + ILCodeVersionHandle ilCodeVersion = ILCodeVersionHandle.CreateSynthetic(modulePointer, MethodToken); + NativeCodeVersionHandle nativeCodeVersion = NativeCodeVersionHandle.CreateSynthetic(methodDesc); + Mock codeVersions = new(); + codeVersions.Setup(c => c.GetILCodeVersions(methodDesc)).Returns([ilCodeVersion]); + codeVersions.Setup(c => c.GetNativeCodeVersions(methodDesc, ilCodeVersion)).Returns([nativeCodeVersion]); + codeVersions.Setup(c => c.GetNativeCode(nativeCodeVersion)).Returns(new TargetCodePointer(0x9000)); + + IXCLRDataModule module = CreateModule( + arch, + modulePointer, + loader.Object, + metadata.Object, + rts, + codeVersions.Object); + + ulong handle; + Assert.Equal(HResults.S_OK, module.StartEnumTypeDefinitions(&handle)); + foreach (uint expectedToken in new[] { ModuleTypeToken, TypeToken }) + { + DacComNullableByRef definitionOut = new(isNullRef: false); + Assert.Equal(HResults.S_OK, module.EnumTypeDefinition(&handle, definitionOut)); + IXCLRDataTypeDefinition definition = Assert.IsType(definitionOut.Interface); + uint actualToken; + Assert.Equal( + HResults.S_OK, + definition.GetTokenAndScope(&actualToken, new DacComNullableByRef(isNullRef: true))); + Assert.Equal(expectedToken, actualToken); + } + Assert.Equal( + HResults.S_FALSE, + module.EnumTypeDefinition(&handle, new DacComNullableByRef(isNullRef: false))); + Assert.Equal(HResults.S_OK, module.EndEnumTypeDefinitions(handle)); + + Assert.Equal(HResults.S_OK, module.StartEnumTypeInstances(null, &handle)); + DacComNullableByRef typeOut = new(isNullRef: false); + Assert.Equal(HResults.S_OK, module.EnumTypeInstance(&handle, typeOut)); + IXCLRDataTypeInstance type = Assert.IsType(typeOut.Interface); + DacComNullableByRef loadedDefinitionOut = new(isNullRef: false); + Assert.Equal(HResults.S_OK, type.GetDefinition(loadedDefinitionOut)); + uint loadedToken; + Assert.Equal( + HResults.S_OK, + loadedDefinitionOut.Interface!.GetTokenAndScope( + &loadedToken, + new DacComNullableByRef(isNullRef: true))); + Assert.Equal(TypeToken, loadedToken); + Assert.Equal( + HResults.S_FALSE, + module.EnumTypeInstance(&handle, new DacComNullableByRef(isNullRef: false))); + Assert.Equal(HResults.S_OK, module.EndEnumTypeInstances(handle)); + + fixed (char* methodName = "TestNamespace.TestType.Method") + { + Assert.Equal(HResults.S_OK, module.StartEnumMethodInstancesByName(methodName, 0, null, &handle)); + } + DacComNullableByRef methodOut = new(isNullRef: false); + Assert.Equal(HResults.S_OK, module.EnumMethodInstanceByName(&handle, methodOut)); + IXCLRDataMethodInstance method = Assert.IsType(methodOut.Interface); + uint actualMethodToken; + Assert.Equal( + HResults.S_OK, + method.GetTokenAndScope( + &actualMethodToken, + new DacComNullableByRef(isNullRef: true))); + Assert.Equal(MethodToken, actualMethodToken); + Assert.Equal( + HResults.S_FALSE, + module.EnumMethodInstanceByName( + &handle, + new DacComNullableByRef(isNullRef: false))); + Assert.Equal(HResults.S_OK, module.EndEnumMethodInstancesByName(handle)); + + fixed (char* fieldName = "TestNamespace.TestType.StaticField") + { + Assert.Equal(HResults.S_OK, module.StartEnumDataByName(fieldName, 0, null, null, &handle)); + } + DacComNullableByRef valueOut = new(isNullRef: false); + Assert.Equal(HResults.S_OK, module.EnumDataByName(&handle, valueOut)); + IXCLRDataValue value = Assert.IsType(valueOut.Interface); + ClrDataAddress actualAddress; + ulong actualSize; + uint actualFlags; + Assert.Equal(HResults.S_OK, value.GetAddress(&actualAddress)); + Assert.Equal(HResults.S_OK, value.GetSize(&actualSize)); + Assert.Equal(HResults.S_OK, value.GetFlags(&actualFlags)); + Assert.Equal(fieldAddress.Value, actualAddress.Value); + Assert.Equal(4ul, actualSize); + Assert.Equal((uint)ClrDataValueFlag.IS_PRIMITIVE | 0x00000800u, actualFlags); + Assert.Equal( + HResults.S_FALSE, + module.EnumDataByName(&handle, new DacComNullableByRef(isNullRef: false))); + Assert.Equal(HResults.S_OK, module.EndEnumDataByName(handle)); + + ClrDataTask task = new(thread, new TestPlaceholderTarget.Builder(arch).Build(), legacyImpl: null); + fixed (char* fieldName = "TestNamespace.TestType.ThreadStaticField") + { + Assert.Equal(HResults.S_OK, module.StartEnumDataByName(fieldName, 0, null, task, &handle)); + } + valueOut = new DacComNullableByRef(isNullRef: false); + Assert.Equal(HResults.S_OK, module.EnumDataByName(&handle, valueOut)); + value = Assert.IsType(valueOut.Interface); + Assert.Equal(HResults.S_OK, value.GetAddress(&actualAddress)); + Assert.Equal(HResults.S_OK, value.GetFlags(&actualFlags)); + Assert.Equal(threadStaticFieldAddress.Value, actualAddress.Value); + Assert.Equal((uint)ClrDataValueFlag.IS_PRIMITIVE | 0x00000400u, actualFlags); + Assert.Equal(HResults.S_OK, module.EndEnumDataByName(handle)); + + fixed (char* fieldName = "TestNamespace.TestType.ValueTypeField") + { + Assert.Equal(HResults.S_OK, module.StartEnumDataByName(fieldName, 0, null, null, &handle)); + } + valueOut = new DacComNullableByRef(isNullRef: false); + Assert.Equal(HResults.S_OK, module.EnumDataByName(&handle, valueOut)); + value = Assert.IsType(valueOut.Interface); + Assert.Equal(HResults.S_OK, value.GetAddress(&actualAddress)); + Assert.Equal(HResults.S_OK, value.GetSize(&actualSize)); + Assert.Equal(HResults.S_OK, value.GetFlags(&actualFlags)); + Assert.Equal(valueTypeFieldAddress.Value, actualAddress.Value); + Assert.Equal(3ul, actualSize); + Assert.Equal((uint)ClrDataValueFlag.IS_VALUE_TYPE | 0x00000800u, actualFlags); + Assert.Equal(HResults.S_OK, module.EndEnumDataByName(handle)); + } + [Theory] [ClassData(typeof(MockTarget.StdArch))] public void GetTaskByUniqueID(MockTarget.Architecture arch) @@ -256,11 +446,17 @@ private static IXCLRDataModule CreateModule( MockTarget.Architecture arch, TargetPointer modulePointer, ILoader loader, - IEcmaMetadata metadata) + IEcmaMetadata metadata, + IRuntimeTypeSystem? runtimeTypeSystem = null, + ICodeVersions? codeVersions = null) { TestPlaceholderTarget.Builder builder = new(arch); builder.AddMockContract(loader); builder.AddMockContract(metadata); + if (runtimeTypeSystem is not null) + builder.AddMockContract(runtimeTypeSystem); + if (codeVersions is not null) + builder.AddMockContract(codeVersions); return new ClrDataModule(modulePointer, builder.Build(), legacyImpl: null); } @@ -272,4 +468,125 @@ private static MetadataReaderProvider CreateMetadata(Guid mvid) new MetadataRootBuilder(builder).Serialize(blob, 0, 0); return MetadataReaderProvider.FromMetadataImage(ImmutableArray.Create(blob.ToArray())); } + + private static MetadataReaderProvider CreateEnumerationMetadata() + { + MetadataBuilder builder = new(); + builder.AddModule(0, builder.GetOrAddString("TestModule"), builder.GetOrAddGuid(Guid.Empty), default, default); + + BlobBuilder methodSignature = new(); + methodSignature.WriteByte(0x00); + methodSignature.WriteCompressedInteger(0); + methodSignature.WriteByte((byte)SignatureTypeCode.Void); + BlobHandle methodSignatureHandle = builder.GetOrAddBlob(methodSignature); + + BlobBuilder fieldSignature = new(); + fieldSignature.WriteByte(0x06); + fieldSignature.WriteByte((byte)SignatureTypeCode.Int32); + BlobHandle fieldSignatureHandle = builder.GetOrAddBlob(fieldSignature); + + builder.AddTypeDefinition( + default, + default, + builder.GetOrAddString(""), + default, + MetadataTokens.FieldDefinitionHandle(1), + MetadataTokens.MethodDefinitionHandle(1)); + builder.AddTypeDefinition( + TypeAttributes.Public, + builder.GetOrAddString("TestNamespace"), + builder.GetOrAddString("TestType"), + default, + MetadataTokens.FieldDefinitionHandle(1), + MetadataTokens.MethodDefinitionHandle(1)); + builder.AddFieldDefinition( + FieldAttributes.Public | FieldAttributes.Static, + builder.GetOrAddString("StaticField"), + fieldSignatureHandle); + builder.AddFieldDefinition( + FieldAttributes.Public | FieldAttributes.Static, + builder.GetOrAddString("ThreadStaticField"), + fieldSignatureHandle); + builder.AddFieldDefinition( + FieldAttributes.Public | FieldAttributes.Static, + builder.GetOrAddString("ValueTypeField"), + fieldSignatureHandle); + builder.AddMethodDefinition( + MethodAttributes.Public | MethodAttributes.Static, + MethodImplAttributes.IL, + builder.GetOrAddString("Method"), + methodSignatureHandle, + -1, + MetadataTokens.ParameterHandle(1)); + + BlobBuilder blob = new(); + new MetadataRootBuilder(builder).Serialize(blob, 0, 0); + return MetadataReaderProvider.FromMetadataImage(ImmutableArray.Create(blob.ToArray())); + } + + private sealed class EnumerationRuntimeTypeSystem : IRuntimeTypeSystem + { + private readonly TargetPointer _module; + private readonly TypeHandle _typeHandle; + private readonly uint _typeToken; + private readonly MethodDescHandle _methodHandle; + private readonly uint _methodToken; + private readonly TargetPointer _fieldDesc; + private readonly TargetPointer _fieldAddress; + private readonly TargetPointer _threadStaticFieldDesc; + private readonly TargetPointer _threadStaticFieldAddress; + private readonly TargetPointer _valueTypeFieldDesc; + private readonly TargetPointer _valueTypeFieldAddress; + + public EnumerationRuntimeTypeSystem( + TargetPointer module, + TypeHandle typeHandle, + uint typeToken, + MethodDescHandle methodHandle, + uint methodToken, + TargetPointer fieldDesc, + TargetPointer fieldAddress, + TargetPointer threadStaticFieldDesc, + TargetPointer threadStaticFieldAddress, + TargetPointer valueTypeFieldDesc, + TargetPointer valueTypeFieldAddress) + { + _module = module; + _typeHandle = typeHandle; + _typeToken = typeToken; + _methodHandle = methodHandle; + _methodToken = methodToken; + _fieldDesc = fieldDesc; + _fieldAddress = fieldAddress; + _threadStaticFieldDesc = threadStaticFieldDesc; + _threadStaticFieldAddress = threadStaticFieldAddress; + _valueTypeFieldDesc = valueTypeFieldDesc; + _valueTypeFieldAddress = valueTypeFieldAddress; + } + + public TypeHandle GetTypeHandle(TargetPointer targetPointer) => _typeHandle; + public TargetPointer GetModule(TypeHandle typeHandle) => _module; + public uint GetTypeDefToken(TypeHandle typeHandle) => _typeToken; + public TargetPointer GetParentMethodTable(TypeHandle typeHandle) => TargetPointer.Null; + public ReadOnlySpan GetInstantiation(TypeHandle typeHandle) => []; + public MethodDescHandle GetMethodDescHandle(TargetPointer targetPointer) => _methodHandle; + public TargetPointer GetMethodTable(MethodDescHandle methodDesc) => _typeHandle.Address; + public bool IsGenericMethodDefinition(MethodDescHandle methodDesc) => false; + public ReadOnlySpan GetGenericMethodInstantiation(MethodDescHandle methodDesc) => []; + public uint GetMethodToken(MethodDescHandle methodDesc) => _methodToken; + public bool ContainsGenericVariables(TypeHandle typeHandle) => false; + public bool IsFieldDescThreadStatic(TargetPointer fieldDescPointer) + => fieldDescPointer == _threadStaticFieldDesc; + public TargetPointer GetFieldDescStaticAddress(TargetPointer fieldDescPointer, bool unboxValueTypes = true) + => fieldDescPointer == _fieldDesc + ? _fieldAddress + : fieldDescPointer == _valueTypeFieldDesc ? _valueTypeFieldAddress : TargetPointer.Null; + public TargetPointer GetFieldDescThreadStaticAddress(TargetPointer fieldDescPointer, TargetPointer thread, bool unboxValueTypes = true) + => fieldDescPointer == _threadStaticFieldDesc ? _threadStaticFieldAddress : TargetPointer.Null; + public CorElementType GetFieldDescType(TargetPointer fieldDescPointer) + => fieldDescPointer == _valueTypeFieldDesc ? CorElementType.ValueType : CorElementType.I4; + public TypeHandle GetFieldDescApproxTypeHandle(TargetPointer fieldDescPointer) => _typeHandle; + public uint GetNumInstanceFieldBytes(TypeHandle typeHandle) => 3; + public bool IsEnum(TypeHandle typeHandle) => false; + } } From cc9058f42065f83ac0aa85f26b0fa2fc28ea2a64 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Jul 2026 21:16:44 +0000 Subject: [PATCH 3/5] Implement missing cDAC process APIs for module/task/address handling Co-authored-by: rcj1 <77995559+rcj1@users.noreply.github.com> --- .../ClrDataAppDomain.cs | 2 +- .../IXCLRData.cs | 4 +- .../SOSDacImpl.IXCLRDataProcess.cs | 81 +++++++++++++++++-- 3 files changed, 78 insertions(+), 9 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataAppDomain.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataAppDomain.cs index 5864cda1d1f049..297739de43084f 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataAppDomain.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataAppDomain.cs @@ -12,7 +12,7 @@ namespace Microsoft.Diagnostics.DataContractReader.Legacy; [GeneratedComClass] public sealed unsafe partial class ClrDataAppDomain : IXCLRDataAppDomain { - private const uint DefaultAppDomainId = 1; + internal const uint DefaultAppDomainId = 1; private readonly Target _target; private readonly TargetPointer _appDomain; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/IXCLRData.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/IXCLRData.cs index b8065b21707cd1..bfb5e119588086 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/IXCLRData.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/IXCLRData.cs @@ -215,11 +215,11 @@ int GetRuntimeNameByAddress( [PreserveSig] int StartEnumAppDomains(ulong* handle); [PreserveSig] - int EnumAppDomain(ulong* handle, /*IXCLRDataAppDomain*/ void** appDomain); + int EnumAppDomain(ulong* handle, DacComNullableByRef appDomain); [PreserveSig] int EndEnumAppDomains(ulong handle); [PreserveSig] - int GetAppDomainByUniqueID(ulong id, /*IXCLRDataAppDomain*/ void** appDomain); + int GetAppDomainByUniqueID(ulong id, DacComNullableByRef appDomain); [PreserveSig] int StartEnumAssemblies(ulong* handle); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.IXCLRDataProcess.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.IXCLRDataProcess.cs index 0b451de4d47b6f..28d9d8cec1296f 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.IXCLRDataProcess.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.IXCLRDataProcess.cs @@ -213,17 +213,86 @@ int IXCLRDataProcess.GetRuntimeNameByAddress( return codeKind.ToString(); } + private sealed class EnumAppDomains : IEnum + { + public IEnumerator Enumerator { get; } + public nuint LegacyHandle { get; set; } + + public EnumAppDomains(IEnumerable appDomains, nuint legacyHandle) + { + Enumerator = appDomains.GetEnumerator(); + LegacyHandle = legacyHandle; + } + } + int IXCLRDataProcess.StartEnumAppDomains(ulong* handle) - => LegacyFallbackHelper.CanFallback() && _legacyProcess is not null ? _legacyProcess.StartEnumAppDomains(handle) : HResults.E_NOTIMPL; + { + if (handle is null) + throw new ArgumentNullException(nameof(handle)); + + *handle = 0; + IReadOnlyList appDomains = [_target.Contracts.Loader.GetAppDomain()]; + EnumAppDomains domains = new(appDomains, 0); + *handle = (ulong)((IEnum)domains).GetHandle(); + return HResults.S_OK; + } + + int IXCLRDataProcess.EnumAppDomain(ulong* handle, DacComNullableByRef appDomain) + { + if (handle is null) + throw new ArgumentNullException(nameof(handle)); + if (*handle == 0) + return HResults.S_FALSE; + if (appDomain.IsNullRef) + throw new NullReferenceException(); + + GCHandle gcHandle = GCHandle.FromIntPtr((IntPtr)(*handle)); + if (gcHandle.Target is not EnumAppDomains domains) + throw new ArgumentException(); - int IXCLRDataProcess.EnumAppDomain(ulong* handle, /*IXCLRDataAppDomain*/ void** appDomain) - => LegacyFallbackHelper.CanFallback() && _legacyProcess is not null ? _legacyProcess.EnumAppDomain(handle, appDomain) : HResults.E_NOTIMPL; + if (domains.Enumerator.MoveNext()) + { + appDomain.Interface = new ClrDataAppDomain(_target, domains.Enumerator.Current, legacyImpl: null); + return HResults.S_OK; + } + + return HResults.S_FALSE; + } int IXCLRDataProcess.EndEnumAppDomains(ulong handle) - => LegacyFallbackHelper.CanFallback() && _legacyProcess is not null ? _legacyProcess.EndEnumAppDomains(handle) : HResults.E_NOTIMPL; + { + if (handle == 0) + return HResults.S_OK; + + try + { + GCHandle gcHandle = GCHandle.FromIntPtr((IntPtr)handle); + if (gcHandle.Target is not EnumAppDomains domains) + throw new ArgumentException(); + + ((IEnum)domains).Dispose(); + gcHandle.Free(); + } + catch (System.Exception ex) + { + return ex.HResult; + } + + return HResults.S_OK; + } + + int IXCLRDataProcess.GetAppDomainByUniqueID(ulong id, DacComNullableByRef appDomain) + { + if (appDomain.IsNullRef) + throw new NullReferenceException(); + + if (id != ClrDataAppDomain.DefaultAppDomainId) + return HResults.E_INVALIDARG; - int IXCLRDataProcess.GetAppDomainByUniqueID(ulong id, /*IXCLRDataAppDomain*/ void** appDomain) - => LegacyFallbackHelper.CanFallback() && _legacyProcess is not null ? _legacyProcess.GetAppDomainByUniqueID(id, appDomain) : HResults.E_NOTIMPL; + TargetPointer domain = _target.Contracts.Loader.GetAppDomain(); + appDomain.Interface = new ClrDataAppDomain(_target, domain, legacyImpl: null); + return HResults.S_OK; + } int IXCLRDataProcess.StartEnumAssemblies(ulong* handle) => LegacyFallbackHelper.CanFallback() && _legacyProcess is not null ? _legacyProcess.StartEnumAssemblies(handle) : HResults.E_NOTIMPL; From 9200b4f0a66f57ef5df20d6fd64be346ccfd05e1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Jul 2026 21:18:26 +0000 Subject: [PATCH 4/5] Complete cDAC process API parity for module and task enumeration Co-authored-by: rcj1 <77995559+rcj1@users.noreply.github.com> --- .../SOSDacImpl.IXCLRDataProcess.cs | 149 +++++++++++++++++- 1 file changed, 142 insertions(+), 7 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.IXCLRDataProcess.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.IXCLRDataProcess.cs index 28d9d8cec1296f..9b6706e5648bb9 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.IXCLRDataProcess.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.IXCLRDataProcess.cs @@ -75,7 +75,28 @@ int IXCLRDataProcess.GetTaskByOSThreadID(uint osThreadID, DacComNullableByRef task) - => LegacyFallbackHelper.CanFallback() && _legacyProcess is not null ? _legacyProcess.GetTaskByUniqueID(taskID, task) : HResults.E_NOTIMPL; + { + int hr = HResults.S_OK; + IXCLRDataTask? legacyTask = null; + + if (task.IsNullRef) + throw new NullReferenceException(); + + try + { + TargetPointer thread = _target.Contracts.Thread.IdToThread((uint)taskID); + if (thread == TargetPointer.Null) + throw new ArgumentException(); + + task.Interface = new ClrDataTask(thread, _target, legacyTask); + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + + return hr; + } int IXCLRDataProcess.GetFlags(uint* flags) => LegacyFallbackHelper.CanFallback() && _legacyProcess is not null ? _legacyProcess.GetFlags(flags) : HResults.E_NOTIMPL; @@ -93,7 +114,35 @@ int IXCLRDataProcess.SetDesiredExecutionState(uint state) => LegacyFallbackHelper.CanFallback() && _legacyProcess is not null ? _legacyProcess.SetDesiredExecutionState(state) : HResults.E_NOTIMPL; int IXCLRDataProcess.GetAddressType(ClrDataAddress address, /*CLRDataAddressType*/ uint* type) - => LegacyFallbackHelper.CanFallback() && _legacyProcess is not null ? _legacyProcess.GetAddressType(address, type) : HResults.E_NOTIMPL; + { + const uint ClrDataAddressUnrecognized = 0; + const uint ClrDataAddressManagedMethod = 1; + const uint ClrDataAddressRuntimeUnmanagedStub = 6; + + try + { + if (type is null) + throw new ArgumentNullException(nameof(type)); + + *type = ClrDataAddressUnrecognized; + TargetCodePointer codeAddress = address.ToTargetCodePointer(_target); + if (_target.TryRead(codeAddress, out byte _)) + { + *type = _target.Contracts.ExecutionManager.GetCodeKind(codeAddress) switch + { + CodeKind.Unknown => ClrDataAddressUnrecognized, + CodeKind.Jitted or CodeKind.ReadyToRun or CodeKind.Interpreter => ClrDataAddressManagedMethod, + _ => ClrDataAddressRuntimeUnmanagedStub, + }; + } + + return HResults.S_OK; + } + catch (System.Exception ex) + { + return ex.HResult; + } + } int IXCLRDataProcess.GetRuntimeNameByAddress( ClrDataAddress address, @@ -225,6 +274,18 @@ public EnumAppDomains(IEnumerable appDomains, nuint legacyHandle) } } + private sealed class ProcessEnum : IEnum + { + public IEnumerator Enumerator { get; } + public nuint LegacyHandle { get; set; } + + public ProcessEnum(IEnumerable values, nuint legacyHandle) + { + Enumerator = values.GetEnumerator(); + LegacyHandle = legacyHandle; + } + } + int IXCLRDataProcess.StartEnumAppDomains(ulong* handle) { if (handle is null) @@ -304,16 +365,88 @@ int IXCLRDataProcess.EndEnumAssemblies(ulong handle) => LegacyFallbackHelper.CanFallback() && _legacyProcess is not null ? _legacyProcess.EndEnumAssemblies(handle) : HResults.E_NOTIMPL; int IXCLRDataProcess.StartEnumModules(ulong* handle) - => LegacyFallbackHelper.CanFallback() && _legacyProcess is not null ? _legacyProcess.StartEnumModules(handle) : HResults.E_NOTIMPL; + { + if (handle is null) + throw new ArgumentNullException(nameof(handle)); + + *handle = 0; + ILoader loader = _target.Contracts.Loader; + IEnumerable modules = loader.GetModuleHandles( + loader.GetAppDomain(), + AssemblyIterationFlags.IncludeLoaded | AssemblyIterationFlags.IncludeExecution); + ProcessEnum moduleEnum = new(modules, 0); + *handle = (ulong)((IEnum)moduleEnum).GetHandle(); + return HResults.S_OK; + } int IXCLRDataProcess.EnumModule(ulong* handle, DacComNullableByRef mod) - => LegacyFallbackHelper.CanFallback() && _legacyProcess is not null ? _legacyProcess.EnumModule(handle, mod) : HResults.E_NOTIMPL; + { + if (handle is null) + throw new ArgumentNullException(nameof(handle)); + if (*handle == 0) + return HResults.S_FALSE; + if (mod.IsNullRef) + throw new NullReferenceException(); + + GCHandle gcHandle = GCHandle.FromIntPtr((IntPtr)(*handle)); + if (gcHandle.Target is not ProcessEnum modules) + throw new ArgumentException(); + + if (modules.Enumerator.MoveNext()) + { + mod.Interface = new ClrDataModule(modules.Enumerator.Current.Address, _target, legacyImpl: null); + return HResults.S_OK; + } + + return HResults.S_FALSE; + } int IXCLRDataProcess.EndEnumModules(ulong handle) - => LegacyFallbackHelper.CanFallback() && _legacyProcess is not null ? _legacyProcess.EndEnumModules(handle) : HResults.E_NOTIMPL; + { + if (handle == 0) + return HResults.S_OK; + + try + { + GCHandle gcHandle = GCHandle.FromIntPtr((IntPtr)handle); + if (gcHandle.Target is not ProcessEnum modules) + throw new ArgumentException(); + + ((IEnum)modules).Dispose(); + gcHandle.Free(); + } + catch (System.Exception ex) + { + return ex.HResult; + } + + return HResults.S_OK; + } int IXCLRDataProcess.GetModuleByAddress(ClrDataAddress address, DacComNullableByRef mod) - => LegacyFallbackHelper.CanFallback() && _legacyProcess is not null ? _legacyProcess.GetModuleByAddress(address, mod) : HResults.E_NOTIMPL; + { + if (mod.IsNullRef) + throw new NullReferenceException(); + + ILoader loader = _target.Contracts.Loader; + IEnumerable modules = loader.GetModuleHandles( + loader.GetAppDomain(), + AssemblyIterationFlags.IncludeLoaded | AssemblyIterationFlags.IncludeExecution); + foreach (Contracts.ModuleHandle module in modules) + { + if (!loader.TryGetLoadedImageContents(module, out TargetPointer baseAddress, out uint size, out _)) + continue; + + ClrDataAddress imageBase = baseAddress.ToClrDataAddress(_target); + if (imageBase <= address && address - imageBase < size) + { + mod.Interface = new ClrDataModule(module.Address, _target, legacyImpl: null); + return HResults.S_OK; + } + } + + return HResults.S_FALSE; + } internal sealed class EnumMethodInstances : IEnum { @@ -670,7 +803,9 @@ int IXCLRDataProcess.GetDataByAddress( char* nameBuf, DacComNullableByRef value, ClrDataAddress* displacement) - => LegacyFallbackHelper.CanFallback() && _legacyProcess is not null ? _legacyProcess.GetDataByAddress(address, flags, appDomain, tlsTask, bufLen, nameLen, nameBuf, value, displacement) : HResults.E_NOTIMPL; + { + return flags == 0 ? HResults.E_NOTIMPL : HResults.E_INVALIDARG; + } int IXCLRDataProcess.GetExceptionStateByExceptionRecord(EXCEPTION_RECORD64* record, DacComNullableByRef exState) => LegacyFallbackHelper.CanFallback() && _legacyProcess is not null ? _legacyProcess.GetExceptionStateByExceptionRecord(record, exState) : HResults.E_NOTIMPL; From 0022cc67a71e9ec4a4d90e86ad8361a9f97c7460 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Jul 2026 21:23:16 +0000 Subject: [PATCH 5/5] Finalize cDAC process parity changes and validation Co-authored-by: rcj1 <77995559+rcj1@users.noreply.github.com> --- .../SOSDacImpl.IXCLRDataProcess.cs | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.IXCLRDataProcess.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.IXCLRDataProcess.cs index 9b6706e5648bb9..0248d9933910d4 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.IXCLRDataProcess.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.IXCLRDataProcess.cs @@ -86,11 +86,19 @@ int IXCLRDataProcess.GetTaskByUniqueID(ulong taskID, DacComNullableByRef values, nuint legacyHandle) int IXCLRDataProcess.StartEnumAppDomains(ulong* handle) { if (handle is null) - throw new ArgumentNullException(nameof(handle)); + throw new NullReferenceException(); *handle = 0; IReadOnlyList appDomains = [_target.Contracts.Loader.GetAppDomain()]; @@ -301,7 +317,7 @@ int IXCLRDataProcess.StartEnumAppDomains(ulong* handle) int IXCLRDataProcess.EnumAppDomain(ulong* handle, DacComNullableByRef appDomain) { if (handle is null) - throw new ArgumentNullException(nameof(handle)); + throw new NullReferenceException(); if (*handle == 0) return HResults.S_FALSE; if (appDomain.IsNullRef) @@ -367,7 +383,7 @@ int IXCLRDataProcess.EndEnumAssemblies(ulong handle) int IXCLRDataProcess.StartEnumModules(ulong* handle) { if (handle is null) - throw new ArgumentNullException(nameof(handle)); + throw new NullReferenceException(); *handle = 0; ILoader loader = _target.Contracts.Loader; @@ -382,7 +398,7 @@ int IXCLRDataProcess.StartEnumModules(ulong* handle) int IXCLRDataProcess.EnumModule(ulong* handle, DacComNullableByRef mod) { if (handle is null) - throw new ArgumentNullException(nameof(handle)); + throw new NullReferenceException(); if (*handle == 0) return HResults.S_FALSE; if (mod.IsNullRef)