From 527ab8fe46d5701d78507794ad6318e9a0f8d3a5 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Tue, 13 Aug 2024 18:04:18 -0700 Subject: [PATCH] [cdac] Implement ISOSDacInterface::GetPEFileName in cDAC (#106358) - Include Module path in data descriptor - Implement `GetPath` in `Loader` contract - Make cDAC implement ISOSDacInterface::GetPEFileName --- docs/design/datacontracts/Loader.md | 9 ++++ src/coreclr/debug/daccess/dacimpl.h | 1 + src/coreclr/debug/daccess/request.cpp | 42 +++++++++++++++++-- .../debug/runtimeinfo/datadescriptor.h | 1 + src/coreclr/vm/ceeload.h | 1 + .../cdacreader/src/Contracts/Loader.cs | 4 +- .../cdacreader/src/Contracts/Loader_1.cs | 26 ++++++++++++ .../managed/cdacreader/src/Data/Module.cs | 2 + .../cdacreader/src/Legacy/SOSDacImpl.cs | 30 ++++++++++++- 9 files changed, 109 insertions(+), 7 deletions(-) diff --git a/docs/design/datacontracts/Loader.md b/docs/design/datacontracts/Loader.md index 5efe0dc002f..3e6cbe0d0dc 100644 --- a/docs/design/datacontracts/Loader.md +++ b/docs/design/datacontracts/Loader.md @@ -102,6 +102,7 @@ internal enum AvailableMetadataType ModuleHandle GetModuleHandle(TargetPointer); TargetPointer GetAssembly(ModuleHandle handle); ModuleFlags GetFlags(ModuleHandle handle); +string GetPath(ModuleHandle handle); TargetPointer GetLoaderAllocator(ModuleHandle handle); TargetPointer GetThunkHeap(ModuleHandle handle); TargetPointer GetILBase(ModuleHandle handle); @@ -122,6 +123,7 @@ Data descriptors used: | `Module` | `Flags` | Assembly of the Module | | `Module` | `LoaderAllocator` | LoaderAllocator of the Module | | `Module` | `ThunkHeap` | Pointer to the thunk heap | +| `Module` | `Path` | Path of the Module (UTF-16, null-terminated) | | `Module` | `DynamicMetadata` | Pointer to saved metadata for reflection emit modules | | `Module` | `FieldDefToDescMap` | Mapping table | | `Module` | `ManifestModuleReferencesMap` | Mapping table | @@ -149,6 +151,13 @@ ModuleFlags GetFlags(ModuleHandle handle) return target.Read(handle.Address + /* Module::Flags offset */); } +string GetPath(ModuleHandle handle) +{ + TargetPointer pathStart = target.ReadPointer(handle.Address + /* Module::Path offset */); + char[] path = // Read from target starting at pathStart until null terminator + return new string(path); +} + TargetPointer GetLoaderAllocator(ModuleHandle handle) { return target.ReadPointer(handle.Address + /* Module::LoaderAllocator offset */); diff --git a/src/coreclr/debug/daccess/dacimpl.h b/src/coreclr/debug/daccess/dacimpl.h index d09dab39dcc..0b4d875fd70 100644 --- a/src/coreclr/debug/daccess/dacimpl.h +++ b/src/coreclr/debug/daccess/dacimpl.h @@ -1243,6 +1243,7 @@ public: HRESULT GetObjectDataImpl(CLRDATA_ADDRESS addr, struct DacpObjectData *objectData); HRESULT GetObjectExceptionDataImpl(CLRDATA_ADDRESS objAddr, struct DacpExceptionObjectData *data); HRESULT GetObjectStringDataImpl(CLRDATA_ADDRESS obj, unsigned int count, _Inout_updates_z_(count) WCHAR *stringData, unsigned int *pNeeded); + HRESULT GetPEFileNameImpl(CLRDATA_ADDRESS moduleAddr, unsigned int count, _Inout_updates_z_(count) WCHAR *fileName, unsigned int *pNeeded); HRESULT GetUsefulGlobalsImpl(struct DacpUsefulGlobalsData *globalsData); HRESULT GetMethodDescDataImpl(CLRDATA_ADDRESS methodDesc, CLRDATA_ADDRESS ip, struct DacpMethodDescData *data, ULONG cRevertedRejitVersions, DacpReJitData * rgRevertedRejitData, ULONG * pcNeededRevertedRejitData); HRESULT GetMethodDescNameImpl(CLRDATA_ADDRESS methodDesc, unsigned int count, _Inout_updates_z_(count) WCHAR *name, unsigned int *pNeeded); diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp index 58fa58148f9..71c9df21dfa 100644 --- a/src/coreclr/debug/daccess/request.cpp +++ b/src/coreclr/debug/daccess/request.cpp @@ -2542,6 +2542,41 @@ ClrDataAccess::GetPEFileName(CLRDATA_ADDRESS moduleAddr, unsigned int count, _In return E_INVALIDARG; SOSDacEnter(); + + if (m_cdacSos != NULL) + { + hr = m_cdacSos->GetPEFileName(moduleAddr, count, fileName, pNeeded); + if (FAILED(hr)) + { + hr = GetPEFileNameImpl(moduleAddr, count, fileName, pNeeded); + } +#ifdef _DEBUG + else + { + NewArrayHolder fileNameLocal(new WCHAR[count]); + unsigned int neededLocal = 0; + HRESULT hrLocal = GetPEFileNameImpl(moduleAddr, count, fileNameLocal, &neededLocal); + + DacAssertsEnabledHolder assertsEnabled; + _ASSERTE(hr == hrLocal); + _ASSERTE(pNeeded == NULL || *pNeeded == neededLocal); + _ASSERTE(fileName == NULL || u16_strncmp(fileName, fileNameLocal, count) == 0); + } +#endif + } + else + { + hr = GetPEFileNameImpl(moduleAddr, count, fileName, pNeeded);; + } + + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetPEFileNameImpl(CLRDATA_ADDRESS moduleAddr, unsigned int count, _Inout_updates_z_(count) WCHAR *fileName, unsigned int *pNeeded) +{ PTR_Module pModule = PTR_Module(TO_TADDR(moduleAddr)); PEAssembly* pPEAssembly = pModule->GetPEAssembly(); @@ -2549,11 +2584,11 @@ ClrDataAccess::GetPEFileName(CLRDATA_ADDRESS moduleAddr, unsigned int count, _In if (!pPEAssembly->GetPath().IsEmpty()) { if (!pPEAssembly->GetPath().DacGetUnicode(count, fileName, pNeeded)) - hr = E_FAIL; + return E_FAIL; } else if (!pPEAssembly->IsReflectionEmit()) { - hr = E_NOTIMPL; + return E_NOTIMPL; } else { @@ -2564,8 +2599,7 @@ ClrDataAccess::GetPEFileName(CLRDATA_ADDRESS moduleAddr, unsigned int count, _In *pNeeded = 1; } - SOSDacLeave(); - return hr; + return S_OK; } HRESULT diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.h b/src/coreclr/debug/runtimeinfo/datadescriptor.h index 9d783313111..967df850abc 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.h +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.h @@ -220,6 +220,7 @@ CDAC_TYPE_FIELD(Module, /*pointer*/, Flags, cdac_data::Flags) CDAC_TYPE_FIELD(Module, /*pointer*/, LoaderAllocator, cdac_data::LoaderAllocator) CDAC_TYPE_FIELD(Module, /*pointer*/, ThunkHeap, cdac_data::ThunkHeap) CDAC_TYPE_FIELD(Module, /*pointer*/, DynamicMetadata, cdac_data::DynamicMetadata) +CDAC_TYPE_FIELD(Module, /*pointer*/, Path, cdac_data::Path) CDAC_TYPE_FIELD(Module, /*pointer*/, FieldDefToDescMap, cdac_data::FieldDefToDescMap) CDAC_TYPE_FIELD(Module, /*pointer*/, ManifestModuleReferencesMap, cdac_data::ManifestModuleReferencesMap) diff --git a/src/coreclr/vm/ceeload.h b/src/coreclr/vm/ceeload.h index 9ebc0ed9f39..5fee2e09f4b 100644 --- a/src/coreclr/vm/ceeload.h +++ b/src/coreclr/vm/ceeload.h @@ -1639,6 +1639,7 @@ struct cdac_data static constexpr size_t LoaderAllocator = offsetof(Module, m_loaderAllocator); static constexpr size_t ThunkHeap = offsetof(Module, m_pThunkHeap); static constexpr size_t DynamicMetadata = offsetof(Module, m_pDynamicMetadata); + static constexpr size_t Path = offsetof(Module, m_path); // Lookup map pointers static constexpr size_t FieldDefToDescMap = offsetof(Module, m_FieldDefToDescMap); diff --git a/src/native/managed/cdacreader/src/Contracts/Loader.cs b/src/native/managed/cdacreader/src/Contracts/Loader.cs index 4839e6d495c..39845918cfc 100644 --- a/src/native/managed/cdacreader/src/Contracts/Loader.cs +++ b/src/native/managed/cdacreader/src/Contracts/Loader.cs @@ -117,6 +117,8 @@ internal interface ILoader : IContract public virtual TargetPointer GetAssembly(ModuleHandle handle) => throw new NotImplementedException(); public virtual ModuleFlags GetFlags(ModuleHandle handle) => throw new NotImplementedException(); + public virtual string GetPath(ModuleHandle handle) => throw new NotImplementedException(); + public virtual TargetPointer GetLoaderAllocator(ModuleHandle handle) => throw new NotImplementedException(); public virtual TargetPointer GetThunkHeap(ModuleHandle handle) => throw new NotImplementedException(); @@ -131,8 +133,6 @@ internal interface ILoader : IContract public virtual TargetEcmaMetadata GetReadWriteMetadata(ModuleHandle handle) => throw new NotImplementedException(); public virtual ModuleLookupTables GetLookupTables(ModuleHandle handle) => throw new NotImplementedException(); - - public virtual string GetPath(ModuleHandle handle) => throw new NotImplementedException(); } internal readonly struct Loader : ILoader diff --git a/src/native/managed/cdacreader/src/Contracts/Loader_1.cs b/src/native/managed/cdacreader/src/Contracts/Loader_1.cs index bfbf219207c..3385748b4dc 100644 --- a/src/native/managed/cdacreader/src/Contracts/Loader_1.cs +++ b/src/native/managed/cdacreader/src/Contracts/Loader_1.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Runtime.InteropServices; namespace Microsoft.Diagnostics.DataContractReader.Contracts; @@ -35,6 +36,31 @@ internal readonly struct Loader_1 : ILoader return (ModuleFlags)module.Flags; } + string ILoader.GetPath(ModuleHandle handle) + { + Data.Module module = _target.ProcessedData.GetOrAdd(handle.Address); + + // TODO: [cdac] Add/use APIs on Target for reading strings in target endianness + TargetPointer addr = module.Path; + while (true) + { + // Read characters until we find the null terminator + char nameChar = _target.Read(addr); + if (nameChar == 0) + break; + + addr += sizeof(char); + } + + int length = (int)(addr.Value - module.Path.Value); + if (length == 0) + return string.Empty; + + Span span = stackalloc byte[length]; + _target.ReadBuffer(module.Path, span); + return new string(MemoryMarshal.Cast(span)); + } + TargetPointer ILoader.GetLoaderAllocator(ModuleHandle handle) { Data.Module module = _target.ProcessedData.GetOrAdd(handle.Address); diff --git a/src/native/managed/cdacreader/src/Data/Module.cs b/src/native/managed/cdacreader/src/Data/Module.cs index 3ceabdad209..2e3691e0cfd 100644 --- a/src/native/managed/cdacreader/src/Data/Module.cs +++ b/src/native/managed/cdacreader/src/Data/Module.cs @@ -23,6 +23,7 @@ internal sealed class Module : IData LoaderAllocator = target.ReadPointer(address + (ulong)type.Fields[nameof(LoaderAllocator)].Offset); ThunkHeap = target.ReadPointer(address + (ulong)type.Fields[nameof(ThunkHeap)].Offset); DynamicMetadata = target.ReadPointer(address + (ulong)type.Fields[nameof(DynamicMetadata)].Offset); + Path = target.ReadPointer(address + (ulong)type.Fields[nameof(Path)].Offset); FieldDefToDescMap = target.ReadPointer(address + (ulong)type.Fields[nameof(FieldDefToDescMap)].Offset); ManifestModuleReferencesMap = target.ReadPointer(address + (ulong)type.Fields[nameof(ManifestModuleReferencesMap)].Offset); @@ -39,6 +40,7 @@ internal sealed class Module : IData public TargetPointer LoaderAllocator { get; init; } public TargetPointer ThunkHeap { get; init; } public TargetPointer DynamicMetadata { get; init; } + public TargetPointer Path { get; init; } public TargetPointer FieldDefToDescMap { get; init; } public TargetPointer ManifestModuleReferencesMap { get; init; } diff --git a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs index 0e6062258de..e6d4c1da939 100644 --- a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs @@ -507,7 +507,35 @@ internal sealed partial class SOSDacImpl : ISOSDacInterface, ISOSDacInterface2, public unsafe int GetOOMData(ulong oomAddr, void* data) => HResults.E_NOTIMPL; public unsafe int GetOOMStaticData(void* data) => HResults.E_NOTIMPL; public unsafe int GetPEFileBase(ulong addr, ulong* peBase) => HResults.E_NOTIMPL; - public unsafe int GetPEFileName(ulong addr, uint count, char* fileName, uint* pNeeded) => HResults.E_NOTIMPL; + + public unsafe int GetPEFileName(ulong addr, uint count, char* fileName, uint* pNeeded) + { + try + { + Contracts.ILoader contract = _target.Contracts.Loader; + Contracts.ModuleHandle handle = contract.GetModuleHandle(addr); + string path = contract.GetPath(handle); + + // Return not implemented for empty paths for non-reflection emit assemblies (for example, loaded from memory) + if (string.IsNullOrEmpty(path)) + { + Contracts.ModuleFlags flags = contract.GetFlags(handle); + if (!flags.HasFlag(Contracts.ModuleFlags.ReflectionEmit)) + { + return HResults.E_NOTIMPL; + } + } + + CopyStringToTargetBuffer(fileName, count, pNeeded, path); + } + catch (System.Exception ex) + { + return ex.HResult; + } + + return HResults.S_OK; + } + public unsafe int GetPrivateBinPaths(ulong appDomain, int count, char* paths, uint* pNeeded) => HResults.E_NOTIMPL; public unsafe int GetRCWData(ulong addr, void* data) => HResults.E_NOTIMPL; public unsafe int GetRCWInterfaces(ulong rcw, uint count, void* interfaces, uint* pNeeded) => HResults.E_NOTIMPL;