diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.h b/src/coreclr/debug/runtimeinfo/datadescriptor.h index c51980d8cf4dae..8c36cd0560ad21 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.h +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.h @@ -249,8 +249,17 @@ CDAC_TYPE_INDETERMINATE(Assembly) #ifdef FEATURE_COLLECTIBLE_TYPES CDAC_TYPE_FIELD(Assembly, /*uint8*/, IsCollectible, cdac_data::IsCollectible) #endif +CDAC_TYPE_FIELD(Assembly, /*pointer*/, Module, cdac_data::Module) +CDAC_TYPE_FIELD(Assembly, /*pointer*/, Error, cdac_data::Error) +CDAC_TYPE_FIELD(Assembly, /*uint32*/, NotifyFlags, cdac_data::NotifyFlags) +CDAC_TYPE_FIELD(Assembly, /*uint32*/, Level, cdac_data::Level) CDAC_TYPE_END(Assembly) +CDAC_TYPE_BEGIN(LoaderAllocator) +CDAC_TYPE_INDETERMINATE(LoaderAllocator) +CDAC_TYPE_FIELD(LoaderAllocator, /*uint32*/, ReferenceCount, cdac_data::ReferenceCount) +CDAC_TYPE_END(LoaderAllocator) + CDAC_TYPE_BEGIN(PEAssembly) CDAC_TYPE_INDETERMINATE(PEAssembly) CDAC_TYPE_FIELD(PEAssembly, /*pointer*/, PEImage, cdac_data::PEImage) @@ -282,8 +291,22 @@ CDAC_TYPE_END(ProbeExtensionResult) CDAC_TYPE_BEGIN(AppDomain) CDAC_TYPE_INDETERMINATE(AppDomain) CDAC_TYPE_FIELD(AppDomain, /*pointer*/, RootAssembly, cdac_data::RootAssembly) +CDAC_TYPE_FIELD(AppDomain, /*DomainAssemblyList*/, DomainAssemblyList, cdac_data::DomainAssemblyList) CDAC_TYPE_END(AppDomain) +CDAC_TYPE_BEGIN(ArrayListBase) +CDAC_TYPE_INDETERMINATE(ArrayListBase) +CDAC_TYPE_FIELD(ArrayListBase, /*uint32*/, Count, cdac_data::Count) +CDAC_TYPE_FIELD(ArrayListBase, /*pointer*/, FirstBlock, cdac_data::FirstBlock) +CDAC_TYPE_END(ArrayListBase) + +CDAC_TYPE_BEGIN(ArrayListBlock) +CDAC_TYPE_INDETERMINATE(ArrayListBlock) +CDAC_TYPE_FIELD(ArrayListBlock, /*pointer*/, Next, cdac_data::Next) +CDAC_TYPE_FIELD(ArrayListBlock, /*uint32*/, Size, cdac_data::Size) +CDAC_TYPE_FIELD(ArrayListBlock, /*pointer*/, ArrayStart, cdac_data::ArrayStart) +CDAC_TYPE_END(ArrayListBlock) + // RuntimeTypeSystem CDAC_TYPE_BEGIN(MethodTable) @@ -818,6 +841,7 @@ CDAC_GLOBAL_STRING(Architecture, riscv64) CDAC_GLOBAL_STRING(RID, RID_STRING) CDAC_GLOBAL_POINTER(AppDomain, &AppDomain::m_pTheAppDomain) +CDAC_GLOBAL_POINTER(SystemDomain, cdac_data::SystemDomain) CDAC_GLOBAL_POINTER(ThreadStore, &ThreadStore::s_pThreadStore) CDAC_GLOBAL_POINTER(FinalizerThread, &::g_pFinalizerThread) CDAC_GLOBAL_POINTER(GCThread, &::g_pSuspensionThread) diff --git a/src/coreclr/inc/arraylist.h b/src/coreclr/inc/arraylist.h index f2ffe29d26f49a..9e2a360e210cfe 100644 --- a/src/coreclr/inc/arraylist.h +++ b/src/coreclr/inc/arraylist.h @@ -9,6 +9,9 @@ #include #include // offsetof +// Forward Declarations +template struct cdac_data; + // // ArrayList is a simple class which is used to contain a growable // list of pointers, stored in chunks. Modification is by appending @@ -263,8 +266,21 @@ class ArrayListBase return BlockIterator((ArrayListBlock *) &m_firstBlock, m_count); } + friend struct cdac_data; +}; + +template<> +struct cdac_data +{ + static constexpr size_t Count = offsetof(ArrayListBase, m_count); + static constexpr size_t FirstBlock = offsetof(ArrayListBase, m_firstBlock); + + static constexpr size_t Next = offsetof(ArrayListBase::ArrayListBlock, m_next); + static constexpr size_t Size = offsetof(ArrayListBase::ArrayListBlock, m_blockSize); + static constexpr size_t ArrayStart = offsetof(ArrayListBase::ArrayListBlock, m_array); }; + class ArrayList : public ArrayListBase { public: diff --git a/src/coreclr/vm/appdomain.hpp b/src/coreclr/vm/appdomain.hpp index 944ddf3e5a96cf..0cca6a90d25394 100644 --- a/src/coreclr/vm/appdomain.hpp +++ b/src/coreclr/vm/appdomain.hpp @@ -964,6 +964,8 @@ class AppDomain final { return m_array.Iterate(); } + + friend struct cdac_data; }; // class DomainAssemblyList // Conceptually a list of code:Assembly structures, protected by lock code:GetAssemblyListLock @@ -1621,6 +1623,7 @@ template<> struct cdac_data { static constexpr size_t RootAssembly = offsetof(AppDomain, m_pRootAssembly); + static constexpr size_t DomainAssemblyList = offsetof(AppDomain, m_Assemblies) + offsetof(AppDomain::DomainAssemblyList, m_array); }; typedef DPTR(class SystemDomain) PTR_SystemDomain; @@ -1953,8 +1956,15 @@ inline static BOOL IsUnderDomainLock() { LIMITED_METHOD_CONTRACT; return m_Syste bool enumThis); #endif + friend struct ::cdac_data; }; // class SystemDomain +template<> +struct cdac_data +{ + static constexpr void* const SystemDomain = (void*)&SystemDomain::m_pSystemDomain; +}; + #include "comreflectioncache.inl" #endif diff --git a/src/coreclr/vm/assembly.hpp b/src/coreclr/vm/assembly.hpp index 3b2708744e2351..58e63b72a4d91e 100644 --- a/src/coreclr/vm/assembly.hpp +++ b/src/coreclr/vm/assembly.hpp @@ -546,6 +546,10 @@ struct cdac_data #ifdef FEATURE_COLLECTIBLE_TYPES static constexpr size_t IsCollectible = offsetof(Assembly, m_isCollectible); #endif + static constexpr size_t Module = offsetof(Assembly, m_pModule); + static constexpr size_t Error = offsetof(Assembly, m_pError); + static constexpr size_t NotifyFlags = offsetof(Assembly, m_notifyFlags); + static constexpr size_t Level = offsetof(Assembly, m_level); }; #ifndef DACCESS_COMPILE diff --git a/src/coreclr/vm/loaderallocator.hpp b/src/coreclr/vm/loaderallocator.hpp index 133b0b9d38e535..2ce39a77ba259e 100644 --- a/src/coreclr/vm/loaderallocator.hpp +++ b/src/coreclr/vm/loaderallocator.hpp @@ -877,8 +877,16 @@ class LoaderAllocator virtual void UnregisterDependentHandleToNativeObjectFromCleanup(LADependentHandleToNativeObject *dependentHandle) {}; virtual void CleanupDependentHandlesToNativeObjects() {}; #endif + + friend struct ::cdac_data; }; // class LoaderAllocator +template<> +struct cdac_data +{ + static constexpr size_t ReferenceCount = offsetof(LoaderAllocator, m_cReferences); +}; + typedef VPTR(LoaderAllocator) PTR_LoaderAllocator; extern "C" BOOL QCALLTYPE LoaderAllocator_Destroy(QCall::LoaderAllocatorHandle pLoaderAllocator); diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ILoader.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ILoader.cs index 639ffb91904c91..a161e6cb36925e 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ILoader.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ILoader.cs @@ -19,6 +19,7 @@ public ModuleHandle(TargetPointer address) [Flags] public enum ModuleFlags { + Tenured = 0x00000001, // Set once we know for sure the Module will not be freed until the appdomain itself exits EditAndContinue = 0x00000008, // Edit and Continue is enabled for this module ReflectionEmit = 0x00000040, // Reflection.Emit was used to create this module } @@ -32,12 +33,34 @@ public record struct ModuleLookupTables( TargetPointer TypeRefToMethodTable, TargetPointer MethodDefToILCodeVersioningState); +[Flags] +public enum AssemblyIterationFlags +{ + // load status flags + IncludeLoaded = 0x00000001, // include assemblies that are already loaded + // (m_level >= code:FILE_LOAD_DELIVER_EVENTS) + IncludeLoading = 0x00000002, // include assemblies that are still in the process of loading + // (all m_level values) + IncludeAvailableToProfilers = 0x00000020, // include assemblies available to profilers + // See comment at code:DomainAssembly::IsAvailableToProfilers + + // Execution / introspection flags + IncludeExecution = 0x00000004, // include assemblies that are loaded for execution only + + IncludeFailedToLoad = 0x00000010, // include assemblies that failed to load + + // Collectible assemblies flags + ExcludeCollectible = 0x00000040, // Exclude all collectible assemblies + IncludeCollected = 0x00000080, // Include all collectible assemblies that have been collected +} + public interface ILoader : IContract { static string IContract.Name => nameof(Loader); ModuleHandle GetModuleHandle(TargetPointer modulePointer) => throw new NotImplementedException(); + List GetAssemblies(TargetPointer appDomain, AssemblyIterationFlags iterationFlags) => throw new NotImplementedException(); TargetPointer GetRootAssembly() => throw new NotImplementedException(); TargetPointer GetAssembly(ModuleHandle handle) => throw new NotImplementedException(); TargetPointer GetPEAssembly(ModuleHandle handle) => throw new NotImplementedException(); @@ -54,6 +77,7 @@ public interface ILoader : IContract TargetPointer GetModuleLookupMapElement(TargetPointer table, uint token, out TargetNUInt flags) => throw new NotImplementedException(); bool IsCollectible(ModuleHandle handle) => throw new NotImplementedException(); + bool IsAssemblyLoaded(ModuleHandle handle) => throw new NotImplementedException(); } public readonly struct Loader : ILoader diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs index 25c01c09f02533..5e67e0c569d552 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs @@ -32,6 +32,7 @@ public enum DataType ModuleLookupMap, AppDomain, Assembly, + LoaderAllocator, PEAssembly, PEImage, PEImageLayout, @@ -96,6 +97,9 @@ public enum DataType MethodImpl, NativeCodeSlot, GCCoverageInfo, + ArrayListBase, + ArrayListBlock, + TransitionBlock, DebuggerEval, CalleeSavedRegisters, diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs index ad050a57835632..a110102616bd8d 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs @@ -9,6 +9,7 @@ public static class Globals { // See src/coreclr/debug/runtimeinfo/datadescriptor.h public const string AppDomain = nameof(AppDomain); + public const string SystemDomain = nameof(SystemDomain); public const string ThreadStore = nameof(ThreadStore); public const string FinalizerThread = nameof(FinalizerThread); public const string GCThread = nameof(GCThread); diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Loader_1.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Loader_1.cs index 9448222025bb25..87b51c43e89b93 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Loader_1.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Loader_1.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; using Microsoft.Diagnostics.DataContractReader.Data; namespace Microsoft.Diagnostics.DataContractReader.Contracts; @@ -23,6 +24,71 @@ ModuleHandle ILoader.GetModuleHandle(TargetPointer modulePointer) return new ModuleHandle(modulePointer); } + List ILoader.GetAssemblies(TargetPointer appDomain, AssemblyIterationFlags iterationFlags) + { + if (appDomain == TargetPointer.Null) + throw new ArgumentNullException(nameof(appDomain)); + + Data.AppDomain domain = _target.ProcessedData.GetOrAdd(appDomain); + ArrayListBase arrayList = _target.ProcessedData.GetOrAdd(domain.DomainAssemblyList); + + List handles = []; + foreach (TargetPointer pAssembly in arrayList.Elements) + { + TargetPointer assemblyAddr = _target.ReadPointer(pAssembly); + Data.Assembly assembly = _target.ProcessedData.GetOrAdd(assemblyAddr); + + // following logic is based on AppDomain::AssemblyIterator::Next_Unlocked in appdomain.cpp + + if (assembly.IsError && !iterationFlags.HasFlag(AssemblyIterationFlags.IncludeFailedToLoad)) + continue; // skip assemblies with errors + + if ((assembly.NotifyFlags & 0x1 /*PROFILER_NOTIFIED*/) != 0 && !iterationFlags.HasFlag(AssemblyIterationFlags.IncludeAvailableToProfilers)) + { + // The assembly has reached the state at which we would notify profilers, + // and we're supposed to include such assemblies in the enumeration. So + // don't reject it (i.e., noop here, and don't bother with the rest of + // the load status checks). Check for this first, since + // IncludeAvailableToProfilers contains some loaded AND loading + // assemblies. + } + else if (assembly.IsLoaded) + { + if (!iterationFlags.HasFlag(AssemblyIterationFlags.IncludeLoaded)) + continue; // skip loaded assemblies + } + else + { + if (!iterationFlags.HasFlag(AssemblyIterationFlags.IncludeLoading)) + continue; // skip loading assemblies + } + + // Next, reject assemblies whose execution status is + // not to be included in the enumeration + + if (!iterationFlags.HasFlag(AssemblyIterationFlags.IncludeExecution)) + continue; // skip assemblies with execution status + + if (assembly.IsCollectible != 0) + { + if (iterationFlags.HasFlag(AssemblyIterationFlags.ExcludeCollectible)) + continue; // skip collectible assemblies + + Module module = _target.ProcessedData.GetOrAdd(assembly.Module); + if (((ModuleFlags)module.Flags).HasFlag(ModuleFlags.Tenured)) + continue; // skip tenured modules + + LoaderAllocator loaderAllocator = _target.ProcessedData.GetOrAdd(module.LoaderAllocator); + if (!loaderAllocator.IsAlive && !iterationFlags.HasFlag(AssemblyIterationFlags.IncludeCollected)) + continue; // skip collected assemblies + } + + handles.Add(new(assembly.Module)); + } + + return handles; + } + TargetPointer ILoader.GetRootAssembly() { TargetPointer appDomainPointer = _target.ReadGlobalPointer(Constants.Globals.AppDomain); @@ -192,4 +258,11 @@ bool ILoader.IsCollectible(ModuleHandle handle) Data.Assembly la = _target.ProcessedData.GetOrAdd(assembly); return la.IsCollectible != 0; } + + bool ILoader.IsAssemblyLoaded(ModuleHandle handle) + { + Data.Module module = _target.ProcessedData.GetOrAdd(handle.Address); + Data.Assembly assembly = _target.ProcessedData.GetOrAdd(module.Assembly); + return assembly.IsLoaded; + } } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/AppDomain.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/AppDomain.cs index 7d7f9b1827d980..a0df88ed1faa28 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/AppDomain.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/AppDomain.cs @@ -13,7 +13,9 @@ public AppDomain(Target target, TargetPointer address) Target.TypeInfo type = target.GetTypeInfo(DataType.AppDomain); RootAssembly = target.ReadPointer(address + (ulong)type.Fields[nameof(RootAssembly)].Offset); + DomainAssemblyList = address + (ulong)type.Fields[nameof(DomainAssemblyList)].Offset; } public TargetPointer RootAssembly { get; init; } + public TargetPointer DomainAssemblyList { get; init; } } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ArrayListBase.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ArrayListBase.cs new file mode 100644 index 00000000000000..e9585579371757 --- /dev/null +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ArrayListBase.cs @@ -0,0 +1,75 @@ +// 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; + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +/// +/// Encapsulates structure and logic for ArrayListBase implemented in arraylist.h +/// +internal sealed class ArrayListBase : IData +{ + static ArrayListBase IData.Create(Target target, TargetPointer address) => new ArrayListBase(target, address); + public ArrayListBase(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.ArrayListBase); + + Count = target.Read(address + (ulong)type.Fields[nameof(Count)].Offset); + FirstBlock = address + (ulong)type.Fields[nameof(FirstBlock)].Offset; + + TargetPointer next = FirstBlock; + while (next != TargetPointer.Null) + { + ArrayListBlock block = target.ProcessedData.GetOrAdd(next); + Blocks.Add(block); + next = block.Next; + } + + uint elementsFound = 0; + foreach (ArrayListBlock block in Blocks) + { + foreach (TargetPointer element in block.Elements) + { + if (elementsFound >= Count) + { + break; + } + + Elements.Add(element); + elementsFound++; + } + } + } + + public uint Count { get; init; } + public TargetPointer FirstBlock { get; init; } + + public List Blocks { get; init; } = []; + public List Elements { get; init; } = []; + + internal sealed class ArrayListBlock : IData + { + static ArrayListBlock IData.Create(Target target, TargetPointer address) => new ArrayListBlock(target, address); + public ArrayListBlock(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.ArrayListBlock); + + Next = target.ReadPointer(address + (ulong)type.Fields[nameof(Next)].Offset); + Size = target.Read(address + (ulong)type.Fields[nameof(Size)].Offset); + ArrayStart = address + (ulong)type.Fields[nameof(ArrayStart)].Offset; + + for (ulong i = 0; i < Size; i++) + { + Elements.Add(target.ReadPointer(ArrayStart + (i * (ulong)target.PointerSize))); + } + } + + public TargetPointer Next { get; init; } + public uint Size { get; init; } + public TargetPointer ArrayStart { get; init; } + + public List Elements { get; init; } = []; + } +} diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Assembly.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Assembly.cs index 20a9a9ed5a4c90..b3076c97786bdc 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Assembly.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Assembly.cs @@ -7,13 +7,39 @@ namespace Microsoft.Diagnostics.DataContractReader.Data; internal sealed class Assembly : IData { + public enum FileLoadLevel : uint + { + // Note that semantics here are description is the LAST step done, not what is + // currently being done. + + FILE_LOAD_CREATE, + FILE_LOAD_BEGIN, + FILE_LOAD_BEFORE_TYPE_LOAD, + FILE_LOAD_EAGER_FIXUPS, + FILE_LOAD_DELIVER_EVENTS, + FILE_LOAD_VTABLE_FIXUPS, + FILE_LOADED, // Loaded by not yet active + FILE_ACTIVE, // Fully active (constructors run & security checked) + }; + static Assembly IData.Create(Target target, TargetPointer address) => new Assembly(target, address); public Assembly(Target target, TargetPointer address) { Target.TypeInfo type = target.GetTypeInfo(DataType.Assembly); + Module = target.ReadPointer(address + (ulong)type.Fields[nameof(Module)].Offset); IsCollectible = target.Read(address + (ulong)type.Fields[nameof(IsCollectible)].Offset); + Error = target.ReadPointer(address + (ulong)type.Fields[nameof(Error)].Offset); + NotifyFlags = target.Read(address + (ulong)type.Fields[nameof(NotifyFlags)].Offset); + Level = target.Read(address + (ulong)type.Fields[nameof(Level)].Offset); } + public TargetPointer Module { get; init; } public byte IsCollectible { get; init; } + public TargetPointer Error { get; init; } + public uint NotifyFlags { get; init; } + public uint Level { get; init; } + + public bool IsError => Error != TargetPointer.Null; + public bool IsLoaded => Level >= (uint)FileLoadLevel.FILE_LOAD_DELIVER_EVENTS; } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/LoaderAllocator.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/LoaderAllocator.cs new file mode 100644 index 00000000000000..6a7ba15a630a8f --- /dev/null +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/LoaderAllocator.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal sealed class LoaderAllocator : IData +{ + static LoaderAllocator IData.Create(Target target, TargetPointer address) + => new LoaderAllocator(target, address); + + public LoaderAllocator(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.LoaderAllocator); + + ReferenceCount = target.Read(address + (ulong)type.Fields[nameof(ReferenceCount)].Offset); + + } + + public uint ReferenceCount { get; init; } + + public bool IsAlive => ReferenceCount != 0; +} diff --git a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs index 2bb42a964e66e3..315ff8d63c1c82 100644 --- a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs @@ -107,7 +107,90 @@ int ISOSDacInterface.GetApplicationBase(ulong appDomain, int count, char* appBas int ISOSDacInterface.GetAssemblyData(ulong baseDomainPtr, ulong assembly, void* data) => _legacyImpl is not null ? _legacyImpl.GetAssemblyData(baseDomainPtr, assembly, data) : HResults.E_NOTIMPL; int ISOSDacInterface.GetAssemblyList(ulong appDomain, int count, [In, MarshalUsing(CountElementName = "count"), Out] ulong[] values, int* pNeeded) - => _legacyImpl is not null ? _legacyImpl.GetAssemblyList(appDomain, count, values, pNeeded) : HResults.E_NOTIMPL; + { + if (appDomain == 0) + { + return HResults.E_INVALIDARG; + } + + int hr = HResults.S_OK; + + try + { + TargetPointer systemDomainPtr = _target.ReadGlobalPointer(Constants.Globals.SystemDomain); + TargetPointer systemDomain = _target.ReadPointer(systemDomainPtr); + if (appDomain == systemDomain) + { + // We shouldn't be asking for the assemblies in SystemDomain + hr = HResults.E_INVALIDARG; + } + else + { + ILoader loader = _target.Contracts.Loader; + List modules = loader.GetAssemblies( + appDomain, + AssemblyIterationFlags.IncludeLoading | + AssemblyIterationFlags.IncludeLoaded | + AssemblyIterationFlags.IncludeExecution); + + int n = 0; + if (values is not null) + { + for (int i = 0; i < modules.Count && n < count; i++) + { + Contracts.ModuleHandle module = modules[i]; + if (loader.IsAssemblyLoaded(module)) + { + values[n++] = loader.GetAssembly(module); + } + } + } + else + { + for (int i = 0; i < modules.Count && n < count; i++) + { + Contracts.ModuleHandle module = modules[i]; + if (loader.IsAssemblyLoaded(module)) + { + n++; + } + } + } + + if (pNeeded is not null) + { + *pNeeded = n; + } + } + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + +#if DEBUG + if (_legacyImpl is not null) + { + ulong[] valuesLocal = new ulong[count]; + int neededLocal; + int hrLocal = _legacyImpl.GetAssemblyList(appDomain, count, valuesLocal, &neededLocal); + Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); + if (hr == HResults.S_OK) + { + Debug.Assert(pNeeded == null || *pNeeded == neededLocal); + if (values is not null) + { + for (int i = 0; i < count; i++) + { + Debug.Assert(values[i] == valuesLocal[i], $"cDAC: {values[i]:x}, DAC: {valuesLocal[i]:x}"); + } + } + } + } +#endif + + return hr; + } int ISOSDacInterface.GetAssemblyLocation(ulong assembly, int count, char* location, uint* pNeeded) => _legacyImpl is not null ? _legacyImpl.GetAssemblyLocation(assembly, count, location, pNeeded) : HResults.E_NOTIMPL; int ISOSDacInterface.GetAssemblyModuleList(ulong assembly, uint count, [In, MarshalUsing(CountElementName = "count"), Out] ulong[] modules, uint* pNeeded)