diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.h b/src/coreclr/debug/runtimeinfo/datadescriptor.h index c51980d8cf4dae..43b3909822c25a 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.h +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.h @@ -227,6 +227,8 @@ CDAC_TYPE_FIELD(Module, /*pointer*/, Path, cdac_data::Path) CDAC_TYPE_FIELD(Module, /*pointer*/, FileName, cdac_data::FileName) CDAC_TYPE_FIELD(Module, /*pointer*/, ReadyToRunInfo, cdac_data::ReadyToRunInfo) CDAC_TYPE_FIELD(Module, /*pointer*/, GrowableSymbolStream, cdac_data::GrowableSymbolStream) +CDAC_TYPE_FIELD(Module, /*pointer*/, AvailableTypeParams, offsetof(Module, m_pAvailableParamTypes)) +CDAC_TYPE_FIELD(Module, /*pointer*/, InstMethodHashTable, offsetof(Module, m_pInstMethodHashTable)) CDAC_TYPE_FIELD(Module, /*pointer*/, FieldDefToDescMap, cdac_data::FieldDefToDescMap) CDAC_TYPE_FIELD(Module, /*pointer*/, ManifestModuleReferencesMap, cdac_data::ManifestModuleReferencesMap) @@ -249,8 +251,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 +293,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) @@ -309,6 +334,7 @@ CDAC_TYPE_END(MethodTableAuxiliaryData) CDAC_TYPE_BEGIN(EEClass) CDAC_TYPE_INDETERMINATE(EEClass) CDAC_TYPE_FIELD(EEClass, /*pointer*/, MethodTable, cdac_data::MethodTable) +CDAC_TYPE_FIELD(EEClass, /*pointer*/, MethodDescChunk, cdac_data::MethodDescChunk) CDAC_TYPE_FIELD(EEClass, /*uint16*/, NumMethods, cdac_data::NumMethods) CDAC_TYPE_FIELD(EEClass, /*uint32*/, CorTypeAttr, cdac_data::CorTypeAttr) CDAC_TYPE_FIELD(EEClass, /*uint8*/, InternalCorElementType, cdac_data::InternalCorElementType) @@ -787,6 +813,22 @@ CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, Lr, offsetof(CalleeSavedRegiste #endif // Platform switch CDAC_TYPE_END(CalleeSavedRegisters) +CDAC_TYPE_BEGIN(EETypeHashTable) +CDAC_TYPE_INDETERMINATE(EETypeHashTable) +CDAC_TYPE_FIELD(EETypeHashTable, /*pointer*/, Buckets, cdac_data::Buckets) +CDAC_TYPE_FIELD(EETypeHashTable, /*uint32*/, Count, cdac_data::Count) +CDAC_TYPE_FIELD(EETypeHashTable, /*pointer*/, VolatileEntryValue, cdac_data::VolatileEntryValue) +CDAC_TYPE_FIELD(EETypeHashTable, /*pointer*/, VolatileEntryNextEntry, cdac_data::VolatileEntryNextEntry) +CDAC_TYPE_END(EETypeHashTable) + +CDAC_TYPE_BEGIN(InstMethodHashTable) +CDAC_TYPE_INDETERMINATE(InstMethodHashTable) +CDAC_TYPE_FIELD(InstMethodHashTable, /*pointer*/, Buckets, cdac_data::Buckets) +CDAC_TYPE_FIELD(InstMethodHashTable, /*uint32*/, Count, cdac_data::Count) +CDAC_TYPE_FIELD(InstMethodHashTable, /*pointer*/, VolatileEntryValue, cdac_data::VolatileEntryValue) +CDAC_TYPE_FIELD(InstMethodHashTable, /*pointer*/, VolatileEntryNextEntry, cdac_data::VolatileEntryNextEntry) +CDAC_TYPE_END(InstMethodHashTable) + CDAC_TYPES_END() CDAC_GLOBALS_BEGIN() @@ -818,6 +860,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) @@ -878,6 +921,7 @@ CDAC_GLOBAL(StressLogEnabled, uint8, 0) CDAC_GLOBAL_POINTER(ExecutionManagerCodeRangeMapAddress, cdac_data::CodeRangeMapAddress) CDAC_GLOBAL_POINTER(PlatformMetadata, &::g_cdacPlatformMetadata) CDAC_GLOBAL_POINTER(ProfilerControlBlock, &::g_profControlBlock) +CDAC_GLOBAL_POINTER(MethodDescSizeTable, &MethodDesc::s_ClassificationSizeTable) CDAC_GLOBALS_END() #undef CDAC_BASELINE 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..c6624b5fef470b 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,17 @@ inline static BOOL IsUnderDomainLock() { LIMITED_METHOD_CONTRACT; return m_Syste bool enumThis); #endif + friend struct ::cdac_data; }; // class SystemDomain +#ifndef DACCESS_COMPILE +template<> +struct cdac_data +{ + static constexpr PTR_SystemDomain* SystemDomain = &SystemDomain::m_pSystemDomain; +}; +#endif // DACCESS_COMPILE + #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/class.h b/src/coreclr/vm/class.h index d95129ab096a00..e16a12b952644b 100644 --- a/src/coreclr/vm/class.h +++ b/src/coreclr/vm/class.h @@ -1760,6 +1760,7 @@ template<> struct cdac_data { static constexpr size_t InternalCorElementType = offsetof(EEClass, m_NormType); static constexpr size_t MethodTable = offsetof(EEClass, m_pMethodTable); + static constexpr size_t MethodDescChunk = offsetof(EEClass, m_pChunks); static constexpr size_t NumMethods = offsetof(EEClass, m_NumMethods); static constexpr size_t CorTypeAttr = offsetof(EEClass, m_dwAttrClass); static constexpr size_t NumNonVirtualSlots = offsetof(EEClass, m_NumNonVirtualSlots); diff --git a/src/coreclr/vm/dacenumerablehash.h b/src/coreclr/vm/dacenumerablehash.h index dda541ed951bf4..a1295cccc14b29 100644 --- a/src/coreclr/vm/dacenumerablehash.h +++ b/src/coreclr/vm/dacenumerablehash.h @@ -55,7 +55,7 @@ // // Synchronization: It is permissable to read data from the hash without taking a lock as long as: // 1) Any hash modifications are performed under a lock or otherwise serialized. -// 2) Any miss on a lookup is handled by taking a lock are retry-ing the lookup. +// 2) Any miss on a lookup is handled by taking a lock and retry-ing the lookup. // // OVERALL DESIGN // @@ -314,7 +314,7 @@ class DacEnumerableHashTable static const int SLOT_ENDSENTINEL = 2; // normal slots start at slot #3 static const int SKIP_SPECIAL_SLOTS = 3; - + static DWORD GetLength(DPTR(PTR_VolatileEntry) buckets) { return (DWORD)dac_cast(buckets[SLOT_LENGTH]); @@ -335,6 +335,8 @@ class DacEnumerableHashTable DPTR(PTR_VolatileEntry) m_pBuckets; // Pointer to a simple bucket list (array of VolatileEntry pointers) DWORD m_cEntries; // Count of elements + + friend struct ::cdac_data; }; #endif // __DAC_ENUMERABLE_HASH_INCLUDED diff --git a/src/coreclr/vm/instmethhash.h b/src/coreclr/vm/instmethhash.h index dc77d892ee63fc..ef59081fc2a947 100644 --- a/src/coreclr/vm/instmethhash.h +++ b/src/coreclr/vm/instmethhash.h @@ -14,6 +14,7 @@ #define _INSTMETHHASH_H #include "dacenumerablehash.h" +#include "cdacdata.h" class AllocMemTracker; @@ -145,6 +146,18 @@ class InstMethodHashTable : public DacEnumerableHashTable; +}; + +template<> +struct cdac_data +{ + static constexpr size_t Buckets = offsetof(InstMethodHashTable, m_pBuckets); + static constexpr size_t Count = offsetof(InstMethodHashTable, m_cEntries); + + static constexpr size_t VolatileEntryValue = offsetof(InstMethodHashTable::VolatileEntry, m_sValue); + static constexpr size_t VolatileEntryNextEntry = offsetof(InstMethodHashTable::VolatileEntry, m_pNextEntry); }; #endif /* _INSTMETHHASH_H */ 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/coreclr/vm/typehash.h b/src/coreclr/vm/typehash.h index 86a97b888dd2c8..23dc5d9e364823 100644 --- a/src/coreclr/vm/typehash.h +++ b/src/coreclr/vm/typehash.h @@ -10,6 +10,7 @@ #define _TYPE_HASH_H #include "dacenumerablehash.h" +#include "cdacdata.h" //======================================================================================== // This hash table is used by class loaders to look up constructed types: @@ -136,6 +137,18 @@ class EETypeHashTable : public DacEnumerableHashTable; +}; + +template<> +struct cdac_data +{ + static constexpr size_t Buckets = offsetof(EETypeHashTable, m_pBuckets); + static constexpr size_t Count = offsetof(EETypeHashTable, m_cEntries); + + static constexpr size_t VolatileEntryValue = offsetof(EETypeHashTable::VolatileEntry, m_sValue); + static constexpr size_t VolatileEntryNextEntry = offsetof(EETypeHashTable::VolatileEntry, m_pNextEntry); }; #endif /* _TYPE_HASH_H */ diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ILoader.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ILoader.cs index 639ffb91904c91..b74ae1556b8ee1 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ILoader.cs +++ b/src/native/managed/cdac/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(); @@ -47,6 +70,8 @@ public interface ILoader : IContract ModuleFlags GetFlags(ModuleHandle handle) => throw new NotImplementedException(); string GetPath(ModuleHandle handle) => throw new NotImplementedException(); string GetFileName(ModuleHandle handle) => throw new NotImplementedException(); + List GetAvailableTypeParams(ModuleHandle handle) => throw new NotImplementedException(); + List GetInstantiatedMethods(ModuleHandle handle) => throw new NotImplementedException(); TargetPointer GetLoaderAllocator(ModuleHandle handle) => throw new NotImplementedException(); TargetPointer GetILBase(ModuleHandle handle) => throw new NotImplementedException(); @@ -54,6 +79,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/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs index 61c4274fc763e5..0d5fac5d2c7522 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs @@ -88,6 +88,8 @@ public interface IRuntimeTypeSystem : IContract TargetPointer GetCanonicalMethodTable(TypeHandle typeHandle) => throw new NotImplementedException(); TargetPointer GetParentMethodTable(TypeHandle typeHandle) => throw new NotImplementedException(); + MethodDescHandle GetMethodDescForSlot(TypeHandle methodTable, ushort slot) => throw new NotImplementedException(); + uint GetBaseSize(TypeHandle typeHandle) => throw new NotImplementedException(); // The component size is only available for strings and arrays. It is the size of the element type of the array, or the size of an ECMA 335 character (2 bytes) uint GetComponentSize(TypeHandle typeHandle) => throw new NotImplementedException(); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs index 25c01c09f02533..6039bf6e8aed3b 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs @@ -32,6 +32,7 @@ public enum DataType ModuleLookupMap, AppDomain, Assembly, + LoaderAllocator, PEAssembly, PEImage, PEImageLayout, @@ -96,6 +97,11 @@ public enum DataType MethodImpl, NativeCodeSlot, GCCoverageInfo, + ArrayListBase, + ArrayListBlock, + EETypeHashTable, + InstMethodHashTable, + TransitionBlock, DebuggerEval, CalleeSavedRegisters, diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs index ad050a57835632..c90e55cf517d0c 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs +++ b/src/native/managed/cdac/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); @@ -54,6 +55,8 @@ public static class Globals public const string PlatformMetadata = nameof(PlatformMetadata); public const string ProfilerControlBlock = nameof(ProfilerControlBlock); + public const string MethodDescSizeTable = nameof(MethodDescSizeTable); + public const string HashMapSlotsPerBucket = nameof(HashMapSlotsPerBucket); public const string HashMapValueMask = nameof(HashMapValueMask); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Loader_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Loader_1.cs index 9448222025bb25..6c2d8977182b2b 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Loader_1.cs +++ b/src/native/managed/cdac/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); @@ -89,6 +155,38 @@ bool ILoader.TryGetSymbolStream(ModuleHandle handle, out TargetPointer buffer, o return true; } + List ILoader.GetAvailableTypeParams(ModuleHandle handle) + { + Data.Module module = _target.ProcessedData.GetOrAdd(handle.Address); + List typeParams = []; + + if (module.AvailableTypeParams == TargetPointer.Null) + return typeParams; + + EETypeHashTable typeHashTable = _target.ProcessedData.GetOrAdd(module.AvailableTypeParams); + typeParams.AddRange(typeHashTable.Entries); + + return typeParams; + } + + List ILoader.GetInstantiatedMethods(ModuleHandle handle) + { + Data.Module module = _target.ProcessedData.GetOrAdd(handle.Address); + List typeParams = []; + + if (module.InstMethodHashTable == TargetPointer.Null) + return typeParams; + + InstMethodHashTable methodHashTable = _target.ProcessedData.GetOrAdd(module.InstMethodHashTable); + + foreach (InstMethodHashTable.InstMethodHashTableEntry entry in methodHashTable.Entries) + { + typeParams.Add(entry.MethodDesc); + } + + return typeParams; + } + bool ILoader.IsProbeExtensionResultValid(ModuleHandle handle) { Data.Module module = _target.ProcessedData.GetOrAdd(handle.Address); @@ -192,4 +290,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/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs index 1225c281df73ee..35d30ddc380a1c 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs @@ -109,11 +109,13 @@ internal MethodDesc(Target target, TargetPointer methodDescPointer, Data.MethodD Address = methodDescPointer; Token = ComputeToken(target, desc, chunk); + Size = ComputeSize(target, desc); } public TargetPointer MethodTable => _chunk.MethodTable; public ushort Slot => _desc.Slot; public uint Token { get; } + public uint Size { get; } private static uint ComputeToken(Target target, Data.MethodDesc desc, Data.MethodDescChunk chunk) { @@ -128,6 +130,21 @@ private static uint ComputeToken(Target target, Data.MethodDesc desc, Data.Metho return EcmaMetadataUtils.CreateMethodDef(tokenRange | tokenRemainder); } + private static uint ComputeSize(Target target, Data.MethodDesc desc) + { + // Size of the MethodDesc is variable, read it from the targets lookup table + // See MethodDesc::SizeOf in method.cpp for details + // TODO(cdac): make sure this value is stored in minidumps + TargetPointer methodDescSizeTable = target.ReadGlobalPointer(Constants.Globals.MethodDescSizeTable); + + ushort arrayOffset = (ushort)(desc.Flags & (ushort)( + MethodDescFlags_1.MethodDescFlags.ClassificationMask | + MethodDescFlags_1.MethodDescFlags.HasNonVtableSlot | + MethodDescFlags_1.MethodDescFlags.HasMethodImpl | + MethodDescFlags_1.MethodDescFlags.HasNativeCodeSlot)); + return target.Read(methodDescSizeTable + arrayOffset); + } + public MethodClassification Classification => (MethodClassification)((int)_desc.Flags & (int)MethodDescFlags_1.MethodDescFlags.ClassificationMask); private bool HasFlags(MethodDescFlags_1.MethodDescFlags flags) => (_desc.Flags & (ushort)flags) != 0; @@ -327,7 +344,7 @@ private TargetPointer GetClassPointer(TypeHandle typeHandle) case MethodTableFlags_1.EEClassOrCanonMTBits.EEClass: return methodTable.EEClassOrCanonMT; case MethodTableFlags_1.EEClassOrCanonMTBits.CanonMT: - TargetPointer canonMTPtr =MethodTableFlags_1.UntagEEClassOrCanonMT(methodTable.EEClassOrCanonMT); + TargetPointer canonMTPtr = MethodTableFlags_1.UntagEEClassOrCanonMT(methodTable.EEClassOrCanonMT); TypeHandle canonMTHandle = GetTypeHandle(canonMTPtr); MethodTable canonMT = _methodTables[canonMTHandle.Address]; return canonMT.EEClassOrCanonMT; // canonical method table EEClassOrCanonMT is always EEClass @@ -966,6 +983,72 @@ bool IRuntimeTypeSystem.HasNativeCodeSlot(MethodDescHandle methodDesc) return md.HasNativeCodeSlot; } + // Based on MethodTable::IntroducedMethodIterator + private IEnumerable GetIntroducedMethods(TypeHandle typeHandle) + { + Debug.Assert(typeHandle.IsMethodTable()); + + EEClass eeClass = GetClassData(typeHandle); + + TargetPointer chunkAddr = eeClass.MethodDescChunk; + while (chunkAddr != TargetPointer.Null) + { + MethodDescChunk chunk = _target.ProcessedData.GetOrAdd(chunkAddr); + TargetPointer methodDescPtr = chunk.FirstMethodDesc; + // chunk.Count is the number of MethodDescs in the chunk - 1 + for (int i = 0; i < chunk.Count + 1; i++) + { + MethodDescHandle methodDescHandle = GetMethodDescHandle(methodDescPtr); + MethodDesc md = _methodDescs[methodDescHandle.Address]; + methodDescPtr += md.Size; + yield return methodDescHandle; + } + + chunkAddr = chunk.Next; + } + } + + MethodDescHandle IRuntimeTypeSystem.GetMethodDescForSlot(TypeHandle typeHandle, ushort slot) + { + if (!typeHandle.IsMethodTable()) + throw new ArgumentException($"{nameof(typeHandle)} is not a MethodTable"); + + TargetPointer slotPtr = GetAddressOfMethodTableSlot(GetCanonicalMethodTable(typeHandle), slot); + TargetCodePointer pCode = _target.ReadCodePointer(slotPtr); + + if (pCode == TargetCodePointer.Null) + { + // if pCode is null, we iterate through the method descs in the MT. + foreach (MethodDescHandle mdh in GetIntroducedMethods(typeHandle)) + { + MethodDesc md = _methodDescs[mdh.Address]; + if (md.Slot == slot) + { + return mdh; + } + } + } + + // standard path, ask ExecutionManager for the MethodDesc + IExecutionManager executionManager = _target.Contracts.ExecutionManager; + if (executionManager.GetCodeBlockHandle(pCode) is CodeBlockHandle cbh) + { + TargetPointer methodDescPtr = executionManager.GetMethodDesc(cbh); + return GetMethodDescHandle(methodDescPtr); + } + + // FCall path, look up address in the FCall table + // TODO(cdac): FCall path if relevant + + // stub path, read address as a Precode and read MethodDesc from it + { + TargetPointer methodDescPtr = _target.Contracts.PrecodeStubs.GetMethodDescFromStubAddress(pCode); + return GetMethodDescHandle(methodDescPtr); + } + + throw new NotImplementedException("MethodDesc for slot is not implemented yet"); + } + TargetPointer IRuntimeTypeSystem.GetAddressOfNativeCodeSlot(MethodDescHandle methodDesc) { MethodDesc md = _methodDescs[methodDesc.Address]; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/AppDomain.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/AppDomain.cs index 7d7f9b1827d980..a0df88ed1faa28 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/AppDomain.cs +++ b/src/native/managed/cdac/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/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ArrayListBase.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ArrayListBase.cs new file mode 100644 index 00000000000000..e9585579371757 --- /dev/null +++ b/src/native/managed/cdac/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/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Assembly.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Assembly.cs index 20a9a9ed5a4c90..b3076c97786bdc 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Assembly.cs +++ b/src/native/managed/cdac/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/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/DacEnumerableHash.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/DacEnumerableHash.cs new file mode 100644 index 00000000000000..af7cb1fcd9a555 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/DacEnumerableHash.cs @@ -0,0 +1,105 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +/// +/// Parses hash tables that are implemented by DacEnumerableHash defined in dacenumerablehash.h +/// Requires the following datadescriptor fields on the inherited type: +/// Buckets - Pointer to array of VolatileEntry pointers +/// Count - Count of elements +/// VolatileEntryValue - Offset of the value in the VolatileEntry struct +/// VolatileEntryNextEntry - Offset of the next entry pointer in the VolatileEntry struct +/// +internal sealed class DacEnumerableHash +{ + private const int SLOT_LENGTH = 0; + // private const int SLOT_NIEXT = 1; + private const int SLOT_ENDSENTINEL = 2; + private const int SKIP_SPECIAL_SLOTS = 3; + + private readonly Target _target; + private readonly Target.TypeInfo _type; + + public DacEnumerableHash(Target target, TargetPointer address, Target.TypeInfo type) + { + // init fields + _target = target; + _type = type; + + Buckets = _target.ReadPointer(address + (ulong)_type.Fields[nameof(Buckets)].Offset); + Count = _target.Read(address + (ulong)_type.Fields[nameof(Count)].Offset); + + // read items in the hash table + uint length = GetLength(); + + List entries = []; + for (int i = 0; i < length; i++) + { + // indexes 0, 1, 2 have special purposes. buckets start at SKIP_SPECIAL_SLOTS + int bucketOffset = i + SKIP_SPECIAL_SLOTS; + TargetPointer chainElement = _target.ReadPointer(Buckets + (ulong)(bucketOffset * _target.PointerSize)); + List elements = ReadChain(chainElement); + entries.AddRange(elements); + } + + Debug.Assert(Count == entries.Count); + + Entries = entries; + } + + public TargetPointer Buckets { get; init; } + public uint Count { get; init; } + + public IReadOnlyList Entries { get; init; } + + internal sealed class VolatileEntry + { + public VolatileEntry(Target target, TargetPointer address, Target.TypeInfo type) + { + // offsets are stored on the parent type + VolatileEntryValue = address + (ulong)type.Fields[nameof(VolatileEntryValue)].Offset; + VolatileEntryNextEntry = target.ReadPointer(address + (ulong)type.Fields[nameof(VolatileEntryNextEntry)].Offset); + } + + public TargetPointer VolatileEntryValue { get; init; } + public TargetPointer VolatileEntryNextEntry { get; init; } + } + + private uint GetLength() + { + // First pointer is a size_t length + TargetPointer length = _target.ReadPointer(Buckets + (ulong)(SLOT_LENGTH * _target.PointerSize)); + return (uint)length; + } + + private TargetPointer GetBaseSentinel() + { + // Second pointer is a size_t base sentinel + TargetPointer baseSentinel = _target.ReadPointer(Buckets + (ulong)(SLOT_ENDSENTINEL * _target.PointerSize)); + return baseSentinel; + } + + private static bool IsEndSentinel(TargetPointer value) + { + return ((ulong)value & 0x1) == 0x1; + } + + private List ReadChain(TargetPointer chainElement) + { + List elements = []; + + while (!IsEndSentinel(chainElement)) + { + VolatileEntry volatileEntry = new(_target, chainElement, _type); + elements.Add(volatileEntry.VolatileEntryValue); + + chainElement = volatileEntry.VolatileEntryNextEntry; + } + + return elements; + } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EEClass.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EEClass.cs index c9e62eb39e85ef..2669984b62ce0a 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EEClass.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EEClass.cs @@ -11,6 +11,7 @@ public EEClass(Target target, TargetPointer address) Target.TypeInfo type = target.GetTypeInfo(DataType.EEClass); MethodTable = target.ReadPointer(address + (ulong)type.Fields[nameof(MethodTable)].Offset); + MethodDescChunk = target.ReadPointer(address + (ulong)type.Fields[nameof(MethodDescChunk)].Offset); NumMethods = target.Read(address + (ulong)type.Fields[nameof(NumMethods)].Offset); CorTypeAttr = target.Read(address + (ulong)type.Fields[nameof(CorTypeAttr)].Offset); InternalCorElementType = target.Read(address + (ulong)type.Fields[nameof(InternalCorElementType)].Offset); @@ -18,6 +19,7 @@ public EEClass(Target target, TargetPointer address) } public TargetPointer MethodTable { get; init; } + public TargetPointer MethodDescChunk { get; init; } public ushort NumMethods { get; init; } public uint CorTypeAttr { get; init; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EETypeHashTable.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EETypeHashTable.cs new file mode 100644 index 00000000000000..4c099b54d18fab --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EETypeHashTable.cs @@ -0,0 +1,28 @@ +// 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; + +internal sealed class EETypeHashTable : IData +{ + static EETypeHashTable IData.Create(Target target, TargetPointer address) => new EETypeHashTable(target, address); + public EETypeHashTable(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.EETypeHashTable); + + DacEnumerableHash baseHashTable = new(target, address, type); + + List entries = []; + foreach (TargetPointer entry in baseHashTable.Entries) + { + TargetPointer typeHandle = target.ReadPointer(entry); + entries.Add(typeHandle.Value & ~0x1ul); + } + Entries = entries; + } + + public IReadOnlyList Entries { get; init; } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/InstMethodHashTable.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/InstMethodHashTable.cs new file mode 100644 index 00000000000000..81cba4eb6da11d --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/InstMethodHashTable.cs @@ -0,0 +1,47 @@ +// 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.Reflection.Metadata; + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal sealed class InstMethodHashTable : IData +{ + static InstMethodHashTable IData.Create(Target target, TargetPointer address) => new InstMethodHashTable(target, address); + public InstMethodHashTable(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.InstMethodHashTable); + + DacEnumerableHash baseHashTable = new(target, address, type); + + List entries = []; + foreach (TargetPointer entry in baseHashTable.Entries) + { + TargetPointer methodDescPtr = target.ReadPointer(entry); + InstMethodHashTableEntry instMethodHashTableEntry = new() + { + MethodDesc = methodDescPtr.Value & ~0x3ul, + Flags = (InstMethodHashTableFlags)(methodDescPtr.Value & 0x3ul) + }; + entries.Add(instMethodHashTableEntry); + } + Entries = entries; + } + + public IReadOnlyList Entries { get; init; } + + public readonly struct InstMethodHashTableEntry + { + public TargetPointer MethodDesc { get; init; } + public InstMethodHashTableFlags Flags { get; init; } + } + + [Flags] + public enum InstMethodHashTableFlags + { + UnboxingStub = 0x1, + RequiresInstArg = 0x2, + } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/LoaderAllocator.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/LoaderAllocator.cs new file mode 100644 index 00000000000000..6a7ba15a630a8f --- /dev/null +++ b/src/native/managed/cdac/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/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/MethodDescChunk.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/MethodDescChunk.cs index 028050c40d9d42..a157f76e2c31fc 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/MethodDescChunk.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/MethodDescChunk.cs @@ -17,6 +17,9 @@ public MethodDescChunk(Target target, TargetPointer address) Size = target.Read(address + (ulong)type.Fields[nameof(Size)].Offset); Count = target.Read(address + (ulong)type.Fields[nameof(Count)].Offset); FlagsAndTokenRange = target.Read(address + (ulong)type.Fields[nameof(FlagsAndTokenRange)].Offset); + + // The first MethodDesc is at the end of the MethodDescChunk + FirstMethodDesc = address + type.Size!.Value; } public TargetPointer MethodTable { get; init; } @@ -24,4 +27,6 @@ public MethodDescChunk(Target target, TargetPointer address) public byte Size { get; init; } public byte Count { get; init; } public ushort FlagsAndTokenRange { get; init; } + + public TargetPointer FirstMethodDesc { get; init; } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Module.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Module.cs index c9b986a20c135d..3fe2a7fbb0274e 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Module.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Module.cs @@ -24,6 +24,8 @@ public Module(Target target, TargetPointer address) FileName = target.ReadPointer(address + (ulong)type.Fields[nameof(FileName)].Offset); ReadyToRunInfo = target.ReadPointer(address + (ulong)type.Fields[nameof(ReadyToRunInfo)].Offset); GrowableSymbolStream = target.ReadPointer(address + (ulong)type.Fields[nameof(GrowableSymbolStream)].Offset); + AvailableTypeParams = target.ReadPointer(address + (ulong)type.Fields[nameof(AvailableTypeParams)].Offset); + InstMethodHashTable = target.ReadPointer(address + (ulong)type.Fields[nameof(InstMethodHashTable)].Offset); FieldDefToDescMap = address + (ulong)type.Fields[nameof(FieldDefToDescMap)].Offset; ManifestModuleReferencesMap = address + (ulong)type.Fields[nameof(ManifestModuleReferencesMap)].Offset; @@ -44,6 +46,8 @@ public Module(Target target, TargetPointer address) public TargetPointer FileName { get; init; } public TargetPointer ReadyToRunInfo { get; init; } public TargetPointer GrowableSymbolStream { get; init; } + public TargetPointer AvailableTypeParams { get; init; } + public TargetPointer InstMethodHashTable { get; init; } public TargetPointer FieldDefToDescMap { get; init; } public TargetPointer ManifestModuleReferencesMap { get; init; } diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataMethodInstance.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataMethodInstance.cs new file mode 100644 index 00000000000000..ed87cf26571a33 --- /dev/null +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataMethodInstance.cs @@ -0,0 +1,177 @@ +// 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.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; +using Microsoft.Diagnostics.DataContractReader.Contracts; + +namespace Microsoft.Diagnostics.DataContractReader.Legacy; + +[GeneratedComClass] +internal sealed unsafe partial class ClrDataMethodInstance : IXCLRDataMethodInstance +{ + private readonly Target _target; + private readonly MethodDescHandle _methodDesc; + private readonly TargetPointer _appDomain; + private readonly IXCLRDataMethodInstance? _legacyImpl; + public ClrDataMethodInstance( + Target target, + MethodDescHandle methodDesc, + TargetPointer appDomain, + IXCLRDataMethodInstance? legacyImpl) + { + _target = target; + _methodDesc = methodDesc; + _appDomain = appDomain; + _legacyImpl = legacyImpl; + } + + int IXCLRDataMethodInstance.GetTypeInstance(void** typeInstance) + => _legacyImpl is not null ? _legacyImpl.GetTypeInstance(typeInstance) : HResults.E_NOTIMPL; + + int IXCLRDataMethodInstance.GetDefinition(void** methodDefinition) + => _legacyImpl is not null ? _legacyImpl.GetDefinition(methodDefinition) : HResults.E_NOTIMPL; + + int IXCLRDataMethodInstance.GetTokenAndScope(uint* token, void** /*IXCLRDataModule*/ mod) + { + int hr = HResults.S_OK; + StrategyBasedComWrappers cw = new(); + + try + { + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + if (token is not null) + { + *token = rts.GetMethodToken(_methodDesc); + } + if (mod is not null) + { + void* legacyModPtr = null; + if (_legacyImpl is not null) + { + int hrLegacy = _legacyImpl.GetTokenAndScope(token, &legacyModPtr); + if (hrLegacy < 0) + return hrLegacy; + } + + object obj = cw.GetOrCreateObjectForComInstance((nint)legacyModPtr, CreateObjectFlags.None); + if (obj is not IXCLRDataModule legacyMod) + { + throw new ArgumentException("Invalid module object", nameof(mod)); + } + + TargetPointer mtAddr = rts.GetMethodTable(_methodDesc); + TypeHandle mainMT = rts.GetTypeHandle(mtAddr); + TargetPointer module = rts.GetModule(mainMT); + IXCLRDataModule modImpl = new ClrDataModule(module, _target, legacyMod); + nint modImplPtr = cw.GetOrCreateComInterfaceForObject(modImpl, CreateComInterfaceFlags.None); + Marshal.QueryInterface(modImplPtr, typeof(IXCLRDataModule).GUID, out nint ptrToMod); + Marshal.Release(modImplPtr); + *mod = (void*)ptrToMod; + } + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + +#if DEBUG + if (_legacyImpl is not null) + { + uint tokenLocal; + void* legacyModPtr = null; + int hrLocal = _legacyImpl.GetTokenAndScope(&tokenLocal, &legacyModPtr); + + Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); + Debug.Assert(tokenLocal == *token, $"cDAC: {*token:x}, DAC: {tokenLocal:x}"); + + if (hr == HResults.S_OK) + { + Marshal.Release((nint)legacyModPtr); // release the legacy module + } + } +#endif + + return hr; + } + + int IXCLRDataMethodInstance.GetName(uint flags, uint bufLen, uint* nameLen, char* nameBuf) + => _legacyImpl is not null ? _legacyImpl.GetName(flags, bufLen, nameLen, nameBuf) : HResults.E_NOTIMPL; + + int IXCLRDataMethodInstance.GetFlags(uint* flags) + => _legacyImpl is not null ? _legacyImpl.GetFlags(flags) : HResults.E_NOTIMPL; + + int IXCLRDataMethodInstance.IsSameObject(IXCLRDataMethodInstance* method) + => _legacyImpl is not null ? _legacyImpl.IsSameObject(method) : HResults.E_NOTIMPL; + + int IXCLRDataMethodInstance.GetEnCVersion(uint* version) + => _legacyImpl is not null ? _legacyImpl.GetEnCVersion(version) : HResults.E_NOTIMPL; + + int IXCLRDataMethodInstance.GetNumTypeArguments(uint* numTypeArgs) + => _legacyImpl is not null ? _legacyImpl.GetNumTypeArguments(numTypeArgs) : HResults.E_NOTIMPL; + + int IXCLRDataMethodInstance.GetTypeArgumentByIndex(uint index, void** typeArg) + => _legacyImpl is not null ? _legacyImpl.GetTypeArgumentByIndex(index, typeArg) : HResults.E_NOTIMPL; + + int IXCLRDataMethodInstance.GetILOffsetsByAddress(ulong address, uint offsetsLen, uint* offsetsNeeded, uint* ilOffsets) + => _legacyImpl is not null ? _legacyImpl.GetILOffsetsByAddress(address, offsetsLen, offsetsNeeded, ilOffsets) : HResults.E_NOTIMPL; + + int IXCLRDataMethodInstance.GetAddressRangesByILOffset(uint ilOffset, uint rangesLen, uint* rangesNeeded, void* addressRanges) + => _legacyImpl is not null ? _legacyImpl.GetAddressRangesByILOffset(ilOffset, rangesLen, rangesNeeded, addressRanges) : HResults.E_NOTIMPL; + + int IXCLRDataMethodInstance.GetILAddressMap(uint mapLen, uint* mapNeeded, void* maps) + => _legacyImpl is not null ? _legacyImpl.GetILAddressMap(mapLen, mapNeeded, maps) : HResults.E_NOTIMPL; + + int IXCLRDataMethodInstance.StartEnumExtents(ulong* handle) + => _legacyImpl is not null ? _legacyImpl.StartEnumExtents(handle) : HResults.E_NOTIMPL; + + int IXCLRDataMethodInstance.EnumExtent(ulong* handle, void* extent) + => _legacyImpl is not null ? _legacyImpl.EnumExtent(handle, extent) : HResults.E_NOTIMPL; + + int IXCLRDataMethodInstance.EndEnumExtents(ulong handle) + => _legacyImpl is not null ? _legacyImpl.EndEnumExtents(handle) : HResults.E_NOTIMPL; + + int IXCLRDataMethodInstance.Request(uint reqCode, uint inBufferSize, byte* inBuffer, uint outBufferSize, byte* outBuffer) + => _legacyImpl is not null ? _legacyImpl.Request(reqCode, inBufferSize, inBuffer, outBufferSize, outBuffer) : HResults.E_NOTIMPL; + + int IXCLRDataMethodInstance.GetRepresentativeEntryAddress(ulong* addr) + { + int hr = HResults.S_OK; + + try + { + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + + TargetCodePointer addrCode = rts.GetNativeCode(_methodDesc); + + if (addrCode.Value != 0) + { + *addr = addrCode.Value; + } + else + { + hr = unchecked((int)0x8000FFFF); // E_UNEXPECTED + } + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + +#if DEBUG + if (_legacyImpl is not null) + { + ulong addrLocal; + int hrLocal = _legacyImpl.GetRepresentativeEntryAddress(&addrLocal); + + Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); + Debug.Assert(addrLocal == *addr, $"cDAC: {*addr:x}, DAC: {addrLocal:x}"); + } +#endif + + return hr; + } +} diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataModule.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataModule.cs index 7dba1c96f68549..a5c079839c70b9 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataModule.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataModule.cs @@ -107,8 +107,11 @@ int IXCLRDataModule.EndEnumMethodDefinitionsByName(ulong handle) int IXCLRDataModule.StartEnumMethodInstancesByName(char* name, uint flags, /*IXCLRDataAppDomain*/ void* appDomain, ulong* handle) => _legacyModule is not null ? _legacyModule.StartEnumMethodInstancesByName(name, flags, appDomain, handle) : HResults.E_NOTIMPL; - int IXCLRDataModule.EnumMethodInstanceByName(ulong* handle, /*IXCLRDataMethodInstance*/ void** method) - => _legacyModule is not null ? _legacyModule.EnumMethodInstanceByName(handle, method) : HResults.E_NOTIMPL; + int IXCLRDataModule.EnumMethodInstanceByName(ulong* handle, out IXCLRDataMethodInstance? method) + { + method = default; + return _legacyModule is not null ? _legacyModule.EnumMethodInstanceByName(handle, out method) : HResults.E_NOTIMPL; + } int IXCLRDataModule.EndEnumMethodInstancesByName(ulong handle) => _legacyModule is not null ? _legacyModule.EndEnumMethodInstancesByName(handle) : HResults.E_NOTIMPL; diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/IXCLRData.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/IXCLRData.cs index 2212af540bb44e..3f728bdcce2788 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/IXCLRData.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/IXCLRData.cs @@ -81,7 +81,7 @@ internal unsafe partial interface IXCLRDataModule [PreserveSig] int StartEnumMethodInstancesByName(char* name, uint flags, /*IXCLRDataAppDomain*/ void* appDomain, ulong* handle); [PreserveSig] - int EnumMethodInstanceByName(ulong* handle, /*IXCLRDataMethodInstance*/ void** method); + int EnumMethodInstanceByName(ulong* handle, out IXCLRDataMethodInstance? method); [PreserveSig] int EndEnumMethodInstancesByName(ulong handle); @@ -208,7 +208,7 @@ int GetRuntimeNameByAddress( [PreserveSig] int StartEnumMethodInstancesByAddress(ulong address, /*IXCLRDataAppDomain*/ void* appDomain, ulong* handle); [PreserveSig] - int EnumMethodInstanceByAddress(ulong* handle, /*IXCLRDataMethodInstance*/ void** method); + int EnumMethodInstanceByAddress(ulong* handle, out IXCLRDataMethodInstance? method); [PreserveSig] int EndEnumMethodInstancesByAddress(ulong handle); @@ -412,3 +412,79 @@ internal unsafe partial interface IXCLRDataTask [PreserveSig] int GetLastExceptionState(/*IXCLRDataExceptionState*/ void** exception); } + +[GeneratedComInterface] +[Guid("ECD73800-22CA-4b0d-AB55-E9BA7E6318A5")] +internal unsafe partial interface IXCLRDataMethodInstance +{ + [PreserveSig] + int GetTypeInstance(/*IXCLRDataTypeInstance*/ void** typeInstance); + + [PreserveSig] + int GetDefinition(/*IXCLRDataMethodDefinition*/ void** methodDefinition); + + [PreserveSig] + int GetTokenAndScope(uint* token, void** /*IXCLRDataModule*/ mod); + + [PreserveSig] + int GetName( + uint flags, + uint bufLen, + uint* nameLen, + char* nameBuf); + + [PreserveSig] + int GetFlags(uint* flags); + + [PreserveSig] + int IsSameObject(IXCLRDataMethodInstance* method); + + [PreserveSig] + int GetEnCVersion(uint* version); + + [PreserveSig] + int GetNumTypeArguments(uint* numTypeArgs); + + [PreserveSig] + int GetTypeArgumentByIndex(uint index, /*IXCLRDataTypeInstance*/ void** typeArg); + + [PreserveSig] + int GetILOffsetsByAddress( + ulong address, + uint offsetsLen, + uint* offsetsNeeded, + uint* ilOffsets); + + [PreserveSig] + int GetAddressRangesByILOffset( + uint ilOffset, + uint rangesLen, + uint* rangesNeeded, + /*CLRDATA_ADDRESS_RANGE* */ void* addressRanges); + + [PreserveSig] + int GetILAddressMap( + uint mapLen, + uint* mapNeeded, + /*CLRDATA_IL_ADDRESS_MAP* */ void* maps); + + [PreserveSig] + int StartEnumExtents(ulong* handle); + + [PreserveSig] + int EnumExtent(ulong* handle, /*CLRDATA_ADDRESS_RANGE*/ void* extent); + + [PreserveSig] + int EndEnumExtents(ulong handle); + + [PreserveSig] + int Request( + uint reqCode, + uint inBufferSize, + byte* inBuffer, + uint outBufferSize, + byte* outBuffer); + + [PreserveSig] + int GetRepresentativeEntryAddress(ulong* addr); +} diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.IXCLRDataProcess.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.IXCLRDataProcess.cs index f04d39aa8000a1..90d5b623ed7342 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.IXCLRDataProcess.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.IXCLRDataProcess.cs @@ -2,8 +2,16 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; +using Microsoft.Diagnostics.DataContractReader.Contracts; +using Microsoft.Diagnostics.DataContractReader.Contracts.Extensions; +using Microsoft.Diagnostics.DataContractReader.RuntimeTypeSystemHelpers; + namespace Microsoft.Diagnostics.DataContractReader.Legacy; @@ -131,14 +139,313 @@ int IXCLRDataProcess.EndEnumModules(ulong handle) int IXCLRDataProcess.GetModuleByAddress(ulong address, /*IXCLRDataModule*/ void** mod) => _legacyProcess is not null ? _legacyProcess.GetModuleByAddress(address, mod) : HResults.E_NOTIMPL; + internal class EnumMethodInstances + { + private readonly Target _target; + private readonly TargetPointer _mainMethodDesc; + public readonly TargetPointer _appDomain; + private readonly ILoader _loader; + private readonly IRuntimeTypeSystem _rts; + public IEnumerator methodEnumerator = Enumerable.Empty().GetEnumerator(); + public TargetPointer LegacyHandle { get; set; } = TargetPointer.Null; + + public EnumMethodInstances(Target target, TargetPointer methodDesc, TargetPointer appDomain) + { + _target = target; + _mainMethodDesc = methodDesc; + if (appDomain == TargetPointer.Null) + { + TargetPointer appDomainPointer = _target.ReadGlobalPointer(Constants.Globals.AppDomain); + _appDomain = _target.ReadPointer(appDomainPointer); + } + else + { + _appDomain = appDomain; + } + + _loader = _target.Contracts.Loader; + _rts = _target.Contracts.RuntimeTypeSystem; + } + + public int Start() + { + MethodDescHandle mainMD = _rts.GetMethodDescHandle(_mainMethodDesc); + if (!HasClassOrMethodInstantiation(mainMD) && !HasNativeCodeAnyVersion(mainMD)) + { + return HResults.S_FALSE; + } + + methodEnumerator = IterateMethodInstances().GetEnumerator(); + + return HResults.S_OK; + } + + private IEnumerable IterateMethodInstantiations(Contracts.ModuleHandle moduleHandle) + { + List methodInstantiations = _loader.GetInstantiatedMethods(moduleHandle); + + foreach (TargetPointer methodPtr in methodInstantiations) + { + yield return _rts.GetMethodDescHandle(methodPtr); + } + } + + private IEnumerable IterateTypeParams(Contracts.ModuleHandle moduleHandle) + { + List typeParams = _loader.GetAvailableTypeParams(moduleHandle); + + foreach (TargetPointer type in typeParams) + { + yield return _rts.GetTypeHandle(type); + } + } + + private IEnumerable IterateModules() + { + ILoader loader = _target.Contracts.Loader; + List modules = loader.GetAssemblies( + _appDomain, + AssemblyIterationFlags.IncludeLoaded | AssemblyIterationFlags.IncludeExecution); + + foreach (Contracts.ModuleHandle moduleHandle in modules) + { + yield return moduleHandle; + } + } + + private IEnumerable IterateMethodInstances() + { + /* + There are 4 cases for method instances: + 1. Non-generic method on non-generic type (There is 1 MethodDesc for the method (excluding unboxing stubs, and such) + 2. Generic method on non-generic type (There is a generic defining method + a instantiated method for each particular instantiation) + 3. Non-generic method on generic type (There is 1 method for each generic instance created + 1 for the method on the uninstantiated generic type) + 4. Generic method on Generic type (There are N generic defining methods where N is the number of generic instantiations of the generic type + 1 on the uninstantiated generic types + M different generic instances of the method) + */ + + MethodDescHandle mainMD = _rts.GetMethodDescHandle(_mainMethodDesc); + + if (!HasClassOrMethodInstantiation(mainMD)) + { + // case 1 + // no method or class instantiation, then it's not generic. + if (HasNativeCodeAnyVersion(mainMD)) + { + yield return mainMD; + } + yield break; + } + + TargetPointer mtAddr = _rts.GetMethodTable(mainMD); + TypeHandle mainMT = _rts.GetTypeHandle(mtAddr); + TargetPointer mainModule = _rts.GetModule(mainMT); + uint mainMTToken = _rts.GetTypeDefToken(mainMT); + uint mainMDToken = _rts.GetMethodToken(mainMD); + ushort slotNum = _rts.GetSlotNumber(mainMD); + + if (HasMethodInstantiation(mainMD)) + { + // case 2/4 + // 2 is trivial, 4 is covered because the defining method on a generic type is not instantiated + foreach (Contracts.ModuleHandle moduleHandle in IterateModules()) + { + foreach (MethodDescHandle methodDesc in IterateMethodInstantiations(moduleHandle)) + { + TypeHandle methodTypeHandle = _rts.GetTypeHandle(_rts.GetMethodTable(methodDesc)); + + if (mainModule != _rts.GetModule(methodTypeHandle)) continue; + if (mainMDToken != _rts.GetMethodToken(methodDesc)) continue; + + if (HasNativeCodeAnyVersion(methodDesc)) + { + yield return methodDesc; + } + } + } + + yield break; + } + + if (HasClassInstantiation(mainMD)) + { + // case 3 + // class instantiations are only interesting if the method is not generic + foreach (Contracts.ModuleHandle moduleHandle in IterateModules()) + { + if (HasClassInstantiation(mainMD)) + { + foreach (Contracts.TypeHandle typeParam in IterateTypeParams(moduleHandle)) + { + uint typeParamToken = _rts.GetTypeDefToken(typeParam); + + // not a MethodTable + if (typeParamToken == 0) continue; + + // Check the class token + if (mainMTToken != typeParamToken) continue; + + // Check the module is correct + if (mainModule != _rts.GetModule(typeParam)) continue; + + TargetPointer cmt = _rts.GetCanonicalMethodTable(typeParam); + TypeHandle cmtHandle = _rts.GetTypeHandle(cmt); + + MethodDescHandle methodDesc = _rts.GetMethodDescForSlot(cmtHandle, slotNum); + if (HasNativeCodeAnyVersion(methodDesc)) + { + yield return methodDesc; + } + } + } + } + + yield break; + } + + } + + private bool HasNativeCodeAnyVersion(MethodDescHandle mdHandle) + { + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + ICodeVersions cv = _target.Contracts.CodeVersions; + + TargetCodePointer pcode = rts.GetNativeCode(mdHandle); + + if (pcode == TargetCodePointer.Null) + { + // I think this is equivalent to get any native code version + NativeCodeVersionHandle nativeCodeVersion = cv.GetActiveNativeCodeVersion(mdHandle.Address); + if (nativeCodeVersion.Valid) + { + pcode = cv.GetNativeCode(nativeCodeVersion); + } + } + + return pcode != TargetCodePointer.Null; + } + + private bool HasClassOrMethodInstantiation(MethodDescHandle md) + { + return HasClassInstantiation(md) || HasMethodInstantiation(md); + } + + private bool HasClassInstantiation(MethodDescHandle md) + { + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + + TargetPointer mtAddr = rts.GetMethodTable(md); + TypeHandle mt = rts.GetTypeHandle(mtAddr); + return !rts.GetInstantiation(mt).IsEmpty; + } + + private bool HasMethodInstantiation(MethodDescHandle md) + { + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + + if (rts.IsGenericMethodDefinition(md)) return true; + return !rts.GetGenericMethodInstantiation(md).IsEmpty; + } + } + int IXCLRDataProcess.StartEnumMethodInstancesByAddress(ulong address, /*IXCLRDataAppDomain*/ void* appDomain, ulong* handle) - => _legacyProcess is not null ? _legacyProcess.StartEnumMethodInstancesByAddress(address, appDomain, handle) : HResults.E_NOTIMPL; + { + int hr = HResults.S_OK; - int IXCLRDataProcess.EnumMethodInstanceByAddress(ulong* handle, /*IXCLRDataMethodInstance*/ void** method) - => _legacyProcess is not null ? _legacyProcess.EnumMethodInstanceByAddress(handle, method) : HResults.E_NOTIMPL; + *handle = 0; + hr = HResults.S_FALSE; + + int hrLocal = default; + ulong handleLocal = default; + if (_legacyProcess is not null) + { + hrLocal = _legacyProcess.StartEnumMethodInstancesByAddress(address, appDomain, &handleLocal); + if (hrLocal < 0) + return hrLocal; + } + + IExecutionManager eman = _target.Contracts.ExecutionManager; + if (eman.GetCodeBlockHandle(address) is CodeBlockHandle cbh && eman.GetMethodDesc(cbh) is TargetPointer methodDesc) + { + EnumMethodInstances emi = new(_target, methodDesc, TargetPointer.Null); + + emi.LegacyHandle = handleLocal; + + GCHandle gcHandle = GCHandle.Alloc(emi); + *handle = (ulong)GCHandle.ToIntPtr(gcHandle).ToInt64(); + hr = emi.Start(); + } + +#if DEBUG + if (_legacyProcess is not null) + { + Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); + } +#endif + return hr; + } + + int IXCLRDataProcess.EnumMethodInstanceByAddress(ulong* handle, out IXCLRDataMethodInstance? method) + { + method = default; + int hr = HResults.S_OK; + + GCHandle gcHandle = GCHandle.FromIntPtr((IntPtr)(*handle)); + if (gcHandle.Target is not EnumMethodInstances emi) return HResults.E_INVALIDARG; + + IXCLRDataMethodInstance? legacyMethod = null; + int hrLocal = default; + if (_legacyProcess is not null) + { + ulong legacyHandle = emi.LegacyHandle; + hrLocal = _legacyProcess.EnumMethodInstanceByAddress(&legacyHandle, out legacyMethod); + emi.LegacyHandle = legacyHandle; + if (hrLocal < 0) + return hrLocal; + } + + try + { + if (emi.methodEnumerator.MoveNext()) + { + MethodDescHandle methodDesc = emi.methodEnumerator.Current; + method = new ClrDataMethodInstance(_target, methodDesc, emi._appDomain, legacyMethod); + } + else + { + hr = HResults.S_FALSE; + } + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + + + if (legacyMethod is not null) + { + Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); + } + + return hr; + } int IXCLRDataProcess.EndEnumMethodInstancesByAddress(ulong handle) - => _legacyProcess is not null ? _legacyProcess.EndEnumMethodInstancesByAddress(handle) : HResults.E_NOTIMPL; + { + int hr = HResults.S_OK; + + GCHandle gcHandle = GCHandle.FromIntPtr((IntPtr)handle); + if (gcHandle.Target is not EnumMethodInstances emi) return HResults.E_INVALIDARG; + gcHandle.Free(); + + if (_legacyProcess != null && emi.LegacyHandle != TargetPointer.Null) + { + int hrLocal = _legacyProcess.EndEnumMethodInstancesByAddress(emi.LegacyHandle); + if (hrLocal < 0) + return hrLocal; + } + + return hr; + } int IXCLRDataProcess.GetDataByAddress( ulong address, diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs index 988056e4512ac1..bb5d007568040e 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/mscordaccore_universal/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)