From f9abfcc8ca0e849c8c84e24dd79dac5f365cd5af Mon Sep 17 00:00:00 2001 From: Barbara Rosiak Date: Wed, 10 Jun 2026 16:36:04 -0700 Subject: [PATCH 01/12] Implement GetNativeCodeSequencePointsAndVarInfo for cDAC --- src/coreclr/debug/daccess/dacdbiimpl.cpp | 163 ++++------- src/coreclr/debug/daccess/dacdbiimpl.h | 13 +- src/coreclr/debug/di/module.cpp | 63 ++++- src/coreclr/debug/inc/dacdbiinterface.h | 19 +- src/coreclr/inc/cordebuginfo.h | 4 + src/coreclr/inc/dacdbi.idl | 12 +- .../Dbi/DacDbiImpl.NativeCodeInfo.cs | 261 ++++++++++++++++++ .../Dbi/DacDbiImpl.cs | 69 ++++- .../Dbi/IDacDbiInterface.cs | 85 +++++- .../cdac/tests/UnitTests/DacDbiImplTests.cs | 85 ++++++ 10 files changed, 635 insertions(+), 139 deletions(-) create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.NativeCodeInfo.cs diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index 472dcc358bea2a..a6cb0fa422f324 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -797,8 +797,14 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::SetCompilerFlags(VMPTR_Assembly v // sequence points and var info //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// Initialize the native/IL sequence points and native var info for a function. -HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetNativeCodeSequencePointsAndVarInfo(VMPTR_MethodDesc vmMethodDesc, CORDB_ADDRESS startAddress, BOOL fCodeAvailable, OUT NativeVarData * pNativeVarData, OUT SequencePoints * pSequencePoints) +// Allocator to pass to the debug-info-stores... +BYTE* InfoStoreNew(void * pData, size_t cBytes) +{ + return new BYTE[cBytes]; +} + +// Get the native/IL sequence points and native var info for a function via callbacks. +HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetNativeCodeSequencePointsAndVarInfo(VMPTR_MethodDesc vmMethodDesc, CORDB_ADDRESS startAddress, BOOL fCodeAvailable, OUT ULONG32 * pFixedArgCount, FP_NATIVEVARINFO_CALLBACK fpVarInfoCallback, FP_SEQUENCEPOINT_CALLBACK fpSeqPointCallback, CALLBACK_DATA pUserData) { DD_ENTER_MAY_THROW; @@ -812,11 +818,54 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetNativeCodeSequencePointsAndVar _ASSERTE(fCodeAvailable != 0); - // get information about the locations of arguments and local variables - GetNativeVarData(pMD, startAddress, GetArgCount(pMD), pNativeVarData); + // Return the fixed argument count + if (pFixedArgCount != NULL) + { + *pFixedArgCount = (ULONG32)GetArgCount(pMD); + } - // get the sequence points - GetSequencePoints(pMD, startAddress, pSequencePoints); + DebugInfoRequest request; + request.InitFromStartingAddr(pMD, CORDB_ADDRESS_TO_TADDR(startAddress)); + + // Get native variable info and invoke callback for each entry + if (fpVarInfoCallback != NULL) + { + NewArrayHolder nativeVars(NULL); + ULONG32 varEntryCount; + + BOOL success = DebugInfoManager::GetBoundariesAndVars(request, + InfoStoreNew, NULL, + BoundsType::Instrumented, + NULL, NULL, + &varEntryCount, &nativeVars); + if (!success) + ThrowHR(E_FAIL); + + for (ULONG32 i = 0; i < varEntryCount; i++) + { + fpVarInfoCallback(&nativeVars[i], pUserData); + } + } + + // Get sequence points and invoke callback for each entry + if (fpSeqPointCallback != NULL) + { + NewArrayHolder mapCopy(NULL); + ULONG32 seqEntryCount; + + BOOL success = DebugInfoManager::GetBoundariesAndVars(request, + InfoStoreNew, NULL, + BoundsType::Uninstrumented, + &seqEntryCount, &mapCopy, + NULL, NULL); + if (!success) + ThrowHR(E_FAIL); + + for (ULONG32 i = 0; i < seqEntryCount; i++) + { + fpSeqPointCallback(&mapCopy[i], pUserData); + } + } } EX_CATCH_HRESULT(hr); @@ -870,108 +919,6 @@ SIZE_T DacDbiInterfaceImpl::GetArgCount(MethodDesc * pMD) return NumArguments; } //GetArgCount -// Allocator to pass to the debug-info-stores... -BYTE* InfoStoreNew(void * pData, size_t cBytes) -{ - return new BYTE[cBytes]; -} - -//----------------------------------------------------------------------------- -// Get locations and code offsets for local variables and arguments in a function -// This information is used to find the location of a value at a given IP. -// Arguments: -// input: -// pMethodDesc pointer to the method desc for the function -// startAddr starting address of the function--used to differentiate -// EnC versions -// fixedArgCount number of fixed arguments to the function -// output: -// pVarInfo data structure containing a list of variable and -// argument locations by range of IP offsets -// Note: this function may throw -//----------------------------------------------------------------------------- -void DacDbiInterfaceImpl::GetNativeVarData(MethodDesc * pMethodDesc, - CORDB_ADDRESS startAddr, - SIZE_T fixedArgCount, - NativeVarData * pVarInfo) -{ - // make sure we haven't done this already - if (pVarInfo->IsInitialized()) - { - return; - } - - NewArrayHolder nativeVars(NULL); - - DebugInfoRequest request; - request.InitFromStartingAddr(pMethodDesc, CORDB_ADDRESS_TO_TADDR(startAddr)); - - ULONG32 entryCount; - - BOOL success = DebugInfoManager::GetBoundariesAndVars(request, - InfoStoreNew, NULL, // allocator - BoundsType::Instrumented, - NULL, NULL, - &entryCount, &nativeVars); - - if (!success) - ThrowHR(E_FAIL); - - // set key fields of pVarInfo - pVarInfo->InitVarDataList(nativeVars, (int)fixedArgCount, (int)entryCount); -} // GetNativeVarData - - - - -//----------------------------------------------------------------------------- -// Get the native/IL sequence points for a function -// Arguments: -// input: -// pMethodDesc pointer to the method desc for the function -// startAddr starting address of the function--used to differentiate -// output: -// pNativeMap data structure containing a list of sequence points -// Note: this function may throw -//----------------------------------------------------------------------------- -void DacDbiInterfaceImpl::GetSequencePoints(MethodDesc * pMethodDesc, - CORDB_ADDRESS startAddr, - SequencePoints * pSeqPoints) -{ - - // make sure we haven't done this already - if (pSeqPoints->IsInitialized()) - { - return; - } - - // Use the DebugInfoStore to get IL->Native maps. - // It doesn't matter whether we're jitted, ngenned etc. - DebugInfoRequest request; - request.InitFromStartingAddr(pMethodDesc, CORDB_ADDRESS_TO_TADDR(startAddr)); - - - // Bounds info. - NewArrayHolder mapCopy(NULL); - - ULONG32 entryCount; - BOOL success = DebugInfoManager::GetBoundariesAndVars(request, - InfoStoreNew, - NULL, // allocator - BoundsType::Uninstrumented, - &entryCount, &mapCopy, - NULL, NULL); - if (!success) - ThrowHR(E_FAIL); - - pSeqPoints->InitSequencePoints(entryCount); - - // mapCopy and pSeqPoints have elements of different types. Thus, we - // need to copy the individual members from the elements of mapCopy to the - // elements of pSeqPoints. Once we're done, we can release mapCopy - pSeqPoints->CopyAndSortSequencePoints(mapCopy); - -} // GetSequencePoints //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Function Data diff --git a/src/coreclr/debug/daccess/dacdbiimpl.h b/src/coreclr/debug/daccess/dacdbiimpl.h index a59bedda6e60c3..6ab8ea81341873 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.h +++ b/src/coreclr/debug/daccess/dacdbiimpl.h @@ -105,7 +105,7 @@ class DacDbiInterfaceImpl : // Initialize the native/IL sequence points and native var info for a function. - HRESULT STDMETHODCALLTYPE GetNativeCodeSequencePointsAndVarInfo(VMPTR_MethodDesc vmMethodDesc, CORDB_ADDRESS startAddress, BOOL fCodeAvailable, OUT NativeVarData * pNativeVarData, OUT SequencePoints * pSequencePoints); + HRESULT STDMETHODCALLTYPE GetNativeCodeSequencePointsAndVarInfo(VMPTR_MethodDesc vmMethodDesc, CORDB_ADDRESS startAddress, BOOL fCodeAvailable, OUT ULONG32 * pFixedArgCount, FP_NATIVEVARINFO_CALLBACK fpVarInfoCallback, FP_SEQUENCEPOINT_CALLBACK fpSeqPointCallback, CALLBACK_DATA pUserData); HRESULT STDMETHODCALLTYPE IsThreadSuspendedOrHijacked(VMPTR_Thread vmThread, OUT BOOL * pResult); @@ -162,17 +162,6 @@ class DacDbiInterfaceImpl : // Get the number of fixed arguments to a function, i.e., the explicit args and the "this" pointer. SIZE_T GetArgCount(MethodDesc * pMD); - // Get locations and code offsets for local variables and arguments in a function - void GetNativeVarData(MethodDesc * pMethodDesc, - CORDB_ADDRESS startAddr, - SIZE_T fixedArgCount, - NativeVarData * pVarInfo); - - // Get the native/IL sequence points for a function - void GetSequencePoints(MethodDesc * pMethodDesc, - CORDB_ADDRESS startAddr, - SequencePoints * pNativeMap); - public: //---------------------------------------------------------------------------------- // class MapSortILMap: A template class that will sort an array of DebuggerILToNativeMap. diff --git a/src/coreclr/debug/di/module.cpp b/src/coreclr/debug/di/module.cpp index 3bd2ea636603b3..143ca80c8c120e 100644 --- a/src/coreclr/debug/di/module.cpp +++ b/src/coreclr/debug/di/module.cpp @@ -4745,6 +4745,22 @@ CordbNativeCode * CordbModule::LookupOrCreateNativeCode(mdMethodDef methodToken, return pNativeCode; } // CordbNativeCode::LookupOrCreateFromJITData +// Doubles the array capacity when full, then appends the item. +template +static void GrowAndAppend(NewArrayHolder& pArray, ULONG32& count, ULONG32& capacity, const T* pItem) +{ + if (count >= capacity) + { + ULONG32 newCapacity = (capacity == 0) ? 32 : capacity * 2; + T *pNew = new T[newCapacity]; + if (count > 0) + memcpy(pNew, pArray.GetValue(), count * sizeof(T)); + pArray = pNew; + capacity = newCapacity; + } + pArray[count++] = *pItem; +} + // LoadNativeInfo loads from the left side any native variable info // from the JIT. // @@ -4771,11 +4787,48 @@ void CordbNativeCode::LoadNativeInfo() if (m_fCodeAvailable) { RSLockHolder lockHolder(pProcess->GetProcessLock()); - IfFailThrow(pProcess->GetDAC()->GetNativeCodeSequencePointsAndVarInfo(GetVMNativeCodeMethodDescToken(), - GetAddress(), - m_fCodeAvailable, - &m_nativeVarData, - &m_sequencePoints)); + + struct CallbackData + { + NewArrayHolder pVarInfos; + ULONG32 varInfoCount; + ULONG32 varInfoCapacity; + NewArrayHolder pSeqPoints; + ULONG32 seqPointCount; + ULONG32 seqPointCapacity; + + CallbackData() : varInfoCount(0), varInfoCapacity(0), seqPointCount(0), seqPointCapacity(0) {} + }; + + CallbackData data; + + ULONG32 fixedArgCount = 0; + IfFailThrow(pProcess->GetDAC()->GetNativeCodeSequencePointsAndVarInfo( + GetVMNativeCodeMethodDescToken(), + GetAddress(), + m_fCodeAvailable, + &fixedArgCount, + [](ICorDebugInfo::NativeVarInfo *pVarInfo, void *pUserData) + { + CallbackData *pData = static_cast(pUserData); + GrowAndAppend(pData->pVarInfos, pData->varInfoCount, pData->varInfoCapacity, pVarInfo); + }, + [](ICorDebugInfo::OffsetMapping *pMapping, void *pUserData) + { + CallbackData *pData = static_cast(pUserData); + GrowAndAppend(pData->pSeqPoints, pData->seqPointCount, pData->seqPointCapacity, pMapping); + }, + &data)); + + // Initialize native var data from collected entries + m_nativeVarData.InitVarDataList(data.pVarInfos, (int)fixedArgCount, (int)data.varInfoCount); + + // Initialize sequence points from collected entries + m_sequencePoints.InitSequencePoints(data.seqPointCount); + if (data.seqPointCount > 0) + { + m_sequencePoints.CopyAndSortSequencePoints(data.pSeqPoints); + } } } // CordbNativeCode::LoadNativeInfo diff --git a/src/coreclr/debug/inc/dacdbiinterface.h b/src/coreclr/debug/inc/dacdbiinterface.h index 5665c617629706..b695ee730aee12 100644 --- a/src/coreclr/debug/inc/dacdbiinterface.h +++ b/src/coreclr/debug/inc/dacdbiinterface.h @@ -1058,22 +1058,27 @@ IDacDbiInterface : public IUnknown // for a function. // Arguments: // input: - // vmMethodDesc MethodDesc of the function - // startAddr starting address of the function--this serves to - // differentiate various EnC versions of the function + // vmMethodDesc MethodDesc of the function + // startAddr starting address of the function--this serves to + // differentiate various EnC versions of the function // fCodeAvailable + // fpVarInfoCallback callback invoked once per native variable info entry + // fpSeqPointCallback callback invoked once per sequence point entry + // pUserData user data passed to the callbacks // output: - // pNativeVarData space for the native code offset information for locals - // pSequencePoints space for the IL/native sequence points + // pFixedArgCount number of fixed arguments (explicit args + this) // Return value: // S_OK on success; otherwise, an appropriate failure HRESULT. // Assumptions: - // vmMethodDesc, pNativeVarInfo and pSequencePoints are non-NULL + // vmMethodDesc is non-NULL // Notes: //----------------------------------------------------------------------------- - virtual HRESULT STDMETHODCALLTYPE GetNativeCodeSequencePointsAndVarInfo(VMPTR_MethodDesc vmMethodDesc, CORDB_ADDRESS startAddress, BOOL fCodeAvailable, OUT NativeVarData * pNativeVarData, OUT SequencePoints * pSequencePoints) = 0; + typedef void (*FP_NATIVEVARINFO_CALLBACK)(ICorDebugInfo::NativeVarInfo *pVarInfo, CALLBACK_DATA pUserData); + typedef void (*FP_SEQUENCEPOINT_CALLBACK)(ICorDebugInfo::OffsetMapping *pMapping, CALLBACK_DATA pUserData); + + virtual HRESULT STDMETHODCALLTYPE GetNativeCodeSequencePointsAndVarInfo(VMPTR_MethodDesc vmMethodDesc, CORDB_ADDRESS startAddress, BOOL fCodeAvailable, OUT ULONG32 * pFixedArgCount, FP_NATIVEVARINFO_CALLBACK fpVarInfoCallback, FP_SEQUENCEPOINT_CALLBACK fpSeqPointCallback, CALLBACK_DATA pUserData) = 0; // // Get the filter CONTEXT on the LS. Once we move entirely over to the new managed pipeline diff --git a/src/coreclr/inc/cordebuginfo.h b/src/coreclr/inc/cordebuginfo.h index ba5a545e3ed909..063afb063806a5 100644 --- a/src/coreclr/inc/cordebuginfo.h +++ b/src/coreclr/inc/cordebuginfo.h @@ -51,6 +51,7 @@ class ICorDebugInfo }; + // [cDAC]: Mirrored in managed code (IDacDbiInterface.cs). struct OffsetMapping { uint32_t nativeOffset; @@ -248,6 +249,7 @@ class ICorDebugInfo // VarLoc describes the location of a native variable. Note that currently, VLT_REG_BYREF and VLT_STK_BYREF // are only used for value types on X64. + // [cDAC]: Mirrored in managed code (IDacDbiInterface.cs). enum VarLocType { @@ -360,6 +362,7 @@ class ICorDebugInfo // location of the value. }; + // [cDAC]: Mirrored in managed code (IDacDbiInterface.cs). struct VarLoc { VarLocType vlType; @@ -401,6 +404,7 @@ class ICorDebugInfo uint32_t varNumber; }; + // [cDAC]: Mirrored in managed code (IDacDbiInterface.cs). struct NativeVarInfo { uint32_t startOffset; diff --git a/src/coreclr/inc/dacdbi.idl b/src/coreclr/inc/dacdbi.idl index 700bd4c85e2618..929e41777c4ff3 100644 --- a/src/coreclr/inc/dacdbi.idl +++ b/src/coreclr/inc/dacdbi.idl @@ -16,8 +16,8 @@ struct TypeRefData; struct TargetBuffer; struct ModuleInfo; struct DacThreadAllocInfo; -struct NativeVarData; -struct SequencePoints; +struct NativeVarInfo; +struct OffsetMapping; struct Debugger_STRData; struct DebuggerREGDISPLAY; struct NativeCodeFunctionData; @@ -143,6 +143,8 @@ typedef void (*FP_HEAPSEGMENT_CALLBACK)(CORDB_ADDRESS rangeStart, CORDB_ADDRESS typedef void (*FP_RCW_INTERFACE_CALLBACK)(CORDB_ADDRESS itfPtr, CALLBACK_DATA pUserData); typedef void (*FP_EXCEPTION_STACK_FRAME_CALLBACK)(VMPTR_AppDomain vmAppDomain, VMPTR_Assembly vmAssembly, CORDB_ADDRESS ip, mdMethodDef methodDef, BOOL isLastForeignExceptionFrame, CALLBACK_DATA pUserData); typedef void (*FP_FIELDDATA_CALLBACK)(struct FieldData * pFieldData, CALLBACK_DATA pUserData); +typedef void (*FP_NATIVEVARINFO_CALLBACK)(struct NativeVarInfo * pVarInfo, CALLBACK_DATA pUserData); +typedef void (*FP_SEQUENCEPOINT_CALLBACK)(struct OffsetMapping * pMapping, CALLBACK_DATA pUserData); // @@ -265,8 +267,10 @@ interface IDacDbiInterface : IUnknown [in] VMPTR_MethodDesc vmMethodDesc, [in] CORDB_ADDRESS startAddress, [in] BOOL fCodeAvailable, - [out] struct NativeVarData * pNativeVarData, - [out] struct SequencePoints * pSequencePoints); + [out] ULONG32 * pFixedArgCount, + [in] FP_NATIVEVARINFO_CALLBACK fpVarInfoCallback, + [in] FP_SEQUENCEPOINT_CALLBACK fpSeqPointCallback, + [in] CALLBACK_DATA pUserData); // Context and Stack Walking HRESULT GetManagedStoppedContext([in] VMPTR_Thread vmThread, [out] VMPTR_CONTEXT * pRetVal); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.NativeCodeInfo.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.NativeCodeInfo.cs new file mode 100644 index 00000000000000..4218209920c9d7 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.NativeCodeInfo.cs @@ -0,0 +1,261 @@ +// 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.Diagnostics; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Runtime.InteropServices; +using Microsoft.Diagnostics.DataContractReader.Contracts; + +namespace Microsoft.Diagnostics.DataContractReader.Legacy; + +public sealed unsafe partial class DacDbiImpl +{ + // Gets the number of fixed arguments (i.e., the explicit args and the "this" pointer) from the method signature. + // This does not include other implicit arguments or varargs. + private uint GetArgCount(ulong vmMethodDesc) + { + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + MethodDescHandle mdh = rts.GetMethodDescHandle(new TargetPointer(vmMethodDesc)); + uint token = rts.GetMethodToken(mdh); + TargetPointer mtAddr = rts.GetMethodTable(mdh); + TypeHandle typeHandle = rts.GetTypeHandle(mtAddr); + TargetPointer modulePtr = rts.GetModule(typeHandle); + ILoader loader = _target.Contracts.Loader; + Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(modulePtr); + IEcmaMetadata ecmaMetadata = _target.Contracts.EcmaMetadata; + MetadataReader? mdReader = ecmaMetadata.GetMetadata(moduleHandle); + if (mdReader is null) + throw Marshal.GetExceptionForHR(HResults.E_FAIL)!; + + MethodDefinitionHandle methodDefHandle = MetadataTokens.MethodDefinitionHandle((int)token); + MethodDefinition methodDef = mdReader.GetMethodDefinition(methodDefHandle); + BlobReader blobReader = mdReader.GetBlobReader(methodDef.Signature); + SignatureHeader sigHeader = blobReader.ReadSignatureHeader(); + if (sigHeader.IsGeneric) + blobReader.ReadCompressedInteger(); // skip generic arity + uint paramCount = (uint)blobReader.ReadCompressedInteger(); + + return paramCount + (sigHeader.IsInstance ? 1u : 0u); + } + + internal static NativeVarInfo ConvertToNativeVarInfo(DebugVarInfo varInfo) + { + NativeVarInfo nvi = default; + nvi.startOffset = varInfo.StartOffset; + nvi.endOffset = varInfo.EndOffset; + nvi.varNumber = varInfo.VarNumber; + nvi.loc = ConvertToVarLoc(varInfo); + return nvi; + } + + internal static DbiOffsetMapping ConvertToDbiOffsetMapping(Contracts.OffsetMapping mapping) + { + DbiOffsetMapping nativeMapping = default; + nativeMapping.nativeOffset = mapping.NativeOffset; + nativeMapping.ilOffset = mapping.ILOffset; + nativeMapping.source = ConvertSourceTypesToNative(mapping.SourceType); + return nativeMapping; + } + + internal static VarLoc ConvertToVarLoc(DebugVarInfo varInfo) + { + VarLoc loc = default; + loc.vlType = (varInfo.Kind, varInfo.IsByRef) switch + { + (DebugVarLocKind.Register, false) => VarLocType.VLT_REG, + (DebugVarLocKind.Register, true) => VarLocType.VLT_REG_BYREF, + (DebugVarLocKind.Stack, false) => VarLocType.VLT_STK, + (DebugVarLocKind.Stack, true) => VarLocType.VLT_STK_BYREF, + (DebugVarLocKind.RegisterRegister, _) => VarLocType.VLT_REG_REG, + (DebugVarLocKind.RegisterStack, _) => VarLocType.VLT_REG_STK, + (DebugVarLocKind.StackRegister, _) => VarLocType.VLT_STK_REG, + (DebugVarLocKind.DoubleStack, _) => VarLocType.VLT_STK2, + _ => VarLocType.VLT_INVALID, + }; + + switch (varInfo.Kind) + { + case DebugVarLocKind.Register: + loc.vlrReg = varInfo.Register; + break; + case DebugVarLocKind.Stack: + loc.vlsBaseReg = varInfo.BaseRegister; + loc.vlsOffset = varInfo.StackOffset; + break; + case DebugVarLocKind.RegisterRegister: + loc.vlrrReg1 = varInfo.Register; + loc.vlrrReg2 = varInfo.Register2; + break; + case DebugVarLocKind.RegisterStack: + loc.vlrsReg = varInfo.Register; + loc.vlrssBaseReg = varInfo.BaseRegister2; + loc.vlrssOffset = varInfo.StackOffset2; + break; + case DebugVarLocKind.StackRegister: + loc.vlsrsBaseReg = varInfo.BaseRegister; + loc.vlsrsOffset = varInfo.StackOffset; + loc.vlsrReg = varInfo.Register; + break; + case DebugVarLocKind.DoubleStack: + loc.vlsBaseReg = varInfo.BaseRegister; + loc.vlsOffset = varInfo.StackOffset; + break; + } + + return loc; + } + + // Converts cDAC Contracts.SourceTypes to native ICorDebugInfo::SourceTypes values. + // The cDAC uses compact bit positions while the native enum uses different bit values. + internal static uint ConvertSourceTypesToNative(Contracts.SourceTypes source) + { + const uint NativeStackEmpty = 0x02; + const uint NativeCallInstruction = 0x10; + const uint NativeAsync = 0x20; + + uint result = 0; + if ((source & Contracts.SourceTypes.StackEmpty) != 0) + result |= NativeStackEmpty; + if ((source & Contracts.SourceTypes.CallInstruction) != 0) + result |= NativeCallInstruction; + if ((source & Contracts.SourceTypes.Async) != 0) + result |= NativeAsync; + + return result; + } + +#if DEBUG + private void ValidateNativeCodeInfoAgainstLegacy( + ulong vmMethodDesc, + ulong startAddress, + Interop.BOOL fCodeAvailable, + uint* pFixedArgCount, + List cdacVarInfos, + List cdacSeqPoints, + int hr) + { + uint dacFixedArgCount = 0; + var dacData = new DebugNativeCodeData(); + GCHandle dacHandle = GCHandle.Alloc(dacData); + int hrLocal = _legacy!.GetNativeCodeSequencePointsAndVarInfo( + vmMethodDesc, startAddress, fCodeAvailable, &dacFixedArgCount, + (delegate* unmanaged)&CollectNativeVarInfoCallback, + (delegate* unmanaged)&CollectOffsetMappingCallback, + GCHandle.ToIntPtr(dacHandle)); + dacHandle.Free(); + + Debug.ValidateHResult(hr, hrLocal); + if (hr == HResults.S_OK) + { + if (pFixedArgCount != null) + { + Debug.Assert(*pFixedArgCount == dacFixedArgCount, + $"fixedArgCount mismatch - cDAC: {*pFixedArgCount}, DAC: {dacFixedArgCount}"); + } + + AssertSeqPointsEqual(cdacSeqPoints, dacData.SeqPoints); + AssertVarInfosEqual(cdacVarInfos, dacData.VarInfos); + } + } + + private static void AssertSeqPointsEqual(List cdac, List dac) + { + Debug.Assert(cdac.Count == dac.Count, + $"SeqPoint count mismatch - cDAC: {cdac.Count}, DAC: {dac.Count}"); + int n = Math.Min(cdac.Count, dac.Count); + for (int i = 0; i < n; i++) + { + DbiOffsetMapping c = cdac[i]; + DbiOffsetMapping d = dac[i]; + Debug.Assert(c.nativeOffset == d.nativeOffset, + $"SeqPoint[{i}] nativeOffset mismatch - cDAC: {c.nativeOffset}, DAC: {d.nativeOffset}"); + Debug.Assert(c.ilOffset == d.ilOffset, + $"SeqPoint[{i}] ilOffset mismatch - cDAC: {c.ilOffset}, DAC: {d.ilOffset}"); + Debug.Assert(c.source == d.source, + $"SeqPoint[{i}] source mismatch - cDAC: 0x{c.source:X}, DAC: 0x{d.source:X}"); + } + } + + private static void AssertVarInfosEqual(List cdac, List dac) + { + Debug.Assert(cdac.Count == dac.Count, + $"VarInfo count mismatch - cDAC: {cdac.Count}, DAC: {dac.Count}"); + int n = Math.Min(cdac.Count, dac.Count); + for (int i = 0; i < n; i++) + { + NativeVarInfo c = cdac[i]; + NativeVarInfo d = dac[i]; + Debug.Assert(c.startOffset == d.startOffset, + $"VarInfo[{i}] startOffset mismatch - cDAC: {c.startOffset}, DAC: {d.startOffset}"); + Debug.Assert(c.endOffset == d.endOffset, + $"VarInfo[{i}] endOffset mismatch - cDAC: {c.endOffset}, DAC: {d.endOffset}"); + Debug.Assert(c.varNumber == d.varNumber, + $"VarInfo[{i}] varNumber mismatch - cDAC: {c.varNumber}, DAC: {d.varNumber}"); + Debug.Assert(c.loc.vlType == d.loc.vlType, + $"VarInfo[{i}] vlType mismatch - cDAC: {c.loc.vlType}, DAC: {d.loc.vlType}"); + + switch (c.loc.vlType) + { + case VarLocType.VLT_REG: + case VarLocType.VLT_REG_BYREF: + Debug.Assert(c.loc.vlrReg == d.loc.vlrReg, + $"VarInfo[{i}] vlrReg mismatch - cDAC: {c.loc.vlrReg}, DAC: {d.loc.vlrReg}"); + break; + case VarLocType.VLT_STK: + case VarLocType.VLT_STK_BYREF: + case VarLocType.VLT_STK2: + Debug.Assert(c.loc.vlsBaseReg == d.loc.vlsBaseReg, + $"VarInfo[{i}] vlsBaseReg mismatch - cDAC: {c.loc.vlsBaseReg}, DAC: {d.loc.vlsBaseReg}"); + Debug.Assert(c.loc.vlsOffset == d.loc.vlsOffset, + $"VarInfo[{i}] vlsOffset mismatch - cDAC: {c.loc.vlsOffset}, DAC: {d.loc.vlsOffset}"); + break; + case VarLocType.VLT_REG_REG: + Debug.Assert(c.loc.vlrrReg1 == d.loc.vlrrReg1, + $"VarInfo[{i}] vlrrReg1 mismatch - cDAC: {c.loc.vlrrReg1}, DAC: {d.loc.vlrrReg1}"); + Debug.Assert(c.loc.vlrrReg2 == d.loc.vlrrReg2, + $"VarInfo[{i}] vlrrReg2 mismatch - cDAC: {c.loc.vlrrReg2}, DAC: {d.loc.vlrrReg2}"); + break; + case VarLocType.VLT_REG_STK: + Debug.Assert(c.loc.vlrsReg == d.loc.vlrsReg, + $"VarInfo[{i}] vlrsReg mismatch - cDAC: {c.loc.vlrsReg}, DAC: {d.loc.vlrsReg}"); + Debug.Assert(c.loc.vlrssBaseReg == d.loc.vlrssBaseReg, + $"VarInfo[{i}] vlrssBaseReg mismatch - cDAC: {c.loc.vlrssBaseReg}, DAC: {d.loc.vlrssBaseReg}"); + Debug.Assert(c.loc.vlrssOffset == d.loc.vlrssOffset, + $"VarInfo[{i}] vlrssOffset mismatch - cDAC: {c.loc.vlrssOffset}, DAC: {d.loc.vlrssOffset}"); + break; + case VarLocType.VLT_STK_REG: + Debug.Assert(c.loc.vlsrsBaseReg == d.loc.vlsrsBaseReg, + $"VarInfo[{i}] vlsrsBaseReg mismatch - cDAC: {c.loc.vlsrsBaseReg}, DAC: {d.loc.vlsrsBaseReg}"); + Debug.Assert(c.loc.vlsrsOffset == d.loc.vlsrsOffset, + $"VarInfo[{i}] vlsrsOffset mismatch - cDAC: {c.loc.vlsrsOffset}, DAC: {d.loc.vlsrsOffset}"); + Debug.Assert(c.loc.vlsrReg == d.loc.vlsrReg, + $"VarInfo[{i}] vlsrReg mismatch - cDAC: {c.loc.vlsrReg}, DAC: {d.loc.vlsrReg}"); + break; + } + } + } + + private sealed class DebugNativeCodeData + { + public List VarInfos { get; } = new(); + public List SeqPoints { get; } = new(); + } + + [UnmanagedCallersOnly] + private static void CollectNativeVarInfoCallback(NativeVarInfo* data, void* pUserData) + { + GCHandle handle = GCHandle.FromIntPtr((nint)pUserData); + ((DebugNativeCodeData)handle.Target!).VarInfos.Add(*data); + } + + [UnmanagedCallersOnly] + private static void CollectOffsetMappingCallback(DbiOffsetMapping* data, void* pUserData) + { + GCHandle handle = GCHandle.FromIntPtr((nint)pUserData); + ((DebugNativeCodeData)handle.Target!).SeqPoints.Add(*data); + } +#endif +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs index aa2afe8d47391a..18ce26ce94a023 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs @@ -1193,8 +1193,73 @@ public int ResolveAssembly(ulong vmScope, uint tkAssemblyRef, ulong* pRetVal) return hr; } - public int GetNativeCodeSequencePointsAndVarInfo(ulong vmMethodDesc, ulong startAddress, Interop.BOOL fCodeAvailable, nint pNativeVarData, nint pSequencePoints) - => LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetNativeCodeSequencePointsAndVarInfo(vmMethodDesc, startAddress, fCodeAvailable, pNativeVarData, pSequencePoints) : HResults.E_NOTIMPL; + public int GetNativeCodeSequencePointsAndVarInfo( + ulong vmMethodDesc, + ulong startAddress, + Interop.BOOL fCodeAvailable, + uint* pFixedArgCount, + delegate* unmanaged fpVarInfoCallback, + delegate* unmanaged fpSeqPointCallback, + nint pUserData) + { +#if DEBUG + List cdacVarInfos = new(); + List cdacSeqPoints = new(); +#endif + int hr = HResults.S_OK; + try + { + Debug.Assert(vmMethodDesc != 0, $"vmMethodDesc is null"); + Debug.Assert(fCodeAvailable != 0, $"fCodeAvailable is false"); + + Contracts.IDebugInfo debugInfo = _target.Contracts.DebugInfo; + TargetCodePointer codePointer = new TargetCodePointer(startAddress); + + if (pFixedArgCount != null) + *pFixedArgCount = GetArgCount(vmMethodDesc); + + // Get native variable info and invoke callback for each entry + if (fpVarInfoCallback != null) + { + IEnumerable varInfos = debugInfo.GetMethodVarInfo(codePointer, out _); + foreach (DebugVarInfo varInfo in varInfos) + { + NativeVarInfo nvi = ConvertToNativeVarInfo(varInfo); + fpVarInfoCallback(&nvi, (void*)pUserData); +#if DEBUG + cdacVarInfos.Add(nvi); +#endif + } + } + + // Get sequence points and invoke callback for each entry + if (fpSeqPointCallback != null) + { + IEnumerable sequencePoints = debugInfo.GetMethodNativeMap(codePointer, preferUninstrumented: true, out _); + foreach (Contracts.OffsetMapping mapping in sequencePoints) + { + DbiOffsetMapping nativeMapping = ConvertToDbiOffsetMapping(mapping); + fpSeqPointCallback(&nativeMapping, (void*)pUserData); +#if DEBUG + cdacSeqPoints.Add(nativeMapping); +#endif + } + } + } + catch (System.Exception ex) + { + hr = ex.HResult; + } +#if DEBUG + if (_legacy is not null) + { + ValidateNativeCodeInfoAgainstLegacy( + vmMethodDesc, startAddress, fCodeAvailable, + pFixedArgCount, cdacVarInfos, cdacSeqPoints, hr); + } +#endif + return hr; + } public int GetManagedStoppedContext(ulong vmThread, ulong* pRetVal) { diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs index 48f9c08fe109b8..9aa2461e38d0f8 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs @@ -44,6 +44,89 @@ public struct FieldData public ulong m_vmFieldDesc; } +public enum VarLocType +{ + VLT_REG, + VLT_REG_BYREF, + VLT_REG_FP, + VLT_STK, + VLT_STK_BYREF, + VLT_REG_REG, + VLT_REG_STK, + VLT_STK_REG, + VLT_STK2, + VLT_FPSTK, + VLT_FIXED_VA, + VLT_COUNT, + VLT_INVALID, +} + +[StructLayout(LayoutKind.Explicit)] +public struct VarLoc +{ + [FieldOffset(0)] + public VarLocType vlType; + + // vlReg + [FieldOffset(4)] + public uint vlrReg; + + // vlStk + [FieldOffset(4)] + public uint vlsBaseReg; + [FieldOffset(8)] + public int vlsOffset; + + // vlRegReg + [FieldOffset(4)] + public uint vlrrReg1; + [FieldOffset(8)] + public uint vlrrReg2; + + // vlRegStk + [FieldOffset(4)] + public uint vlrsReg; + [FieldOffset(8)] + public uint vlrssBaseReg; + [FieldOffset(12)] + public int vlrssOffset; + + // vlStkReg + [FieldOffset(4)] + public uint vlsrsBaseReg; + [FieldOffset(8)] + public int vlsrsOffset; + [FieldOffset(12)] + public uint vlsrReg; + + // vlStk2 (same layout as vlStk) + + // vlFPstk + [FieldOffset(4)] + public uint vlfReg; + + // vlFixedVarArg + [FieldOffset(4)] + public uint vlfvOffset; +} + +[StructLayout(LayoutKind.Sequential)] +public struct NativeVarInfo +{ + public uint startOffset; + public uint endOffset; + public uint varNumber; + public VarLoc loc; +} + +[StructLayout(LayoutKind.Sequential)] +public struct DbiOffsetMapping +{ + public uint nativeOffset; + public uint ilOffset; + public uint source; // ICorDebugInfo::SourceTypes +} + #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value [StructLayout(LayoutKind.Sequential)] @@ -481,7 +564,7 @@ public unsafe partial interface IDacDbiInterface int ResolveAssembly(ulong vmScope, uint tkAssemblyRef, ulong* pRetVal); [PreserveSig] - int GetNativeCodeSequencePointsAndVarInfo(ulong vmMethodDesc, ulong startAddress, Interop.BOOL fCodeAvailable, nint pNativeVarData, nint pSequencePoints); + int GetNativeCodeSequencePointsAndVarInfo(ulong vmMethodDesc, ulong startAddress, Interop.BOOL fCodeAvailable, uint* pFixedArgCount, delegate* unmanaged fpVarInfoCallback, delegate* unmanaged fpSeqPointCallback, nint pUserData); [PreserveSig] int GetManagedStoppedContext(ulong vmThread, ulong* pRetVal); diff --git a/src/native/managed/cdac/tests/UnitTests/DacDbiImplTests.cs b/src/native/managed/cdac/tests/UnitTests/DacDbiImplTests.cs index 291e498a3e6c97..5f6cfe6f50313a 100644 --- a/src/native/managed/cdac/tests/UnitTests/DacDbiImplTests.cs +++ b/src/native/managed/cdac/tests/UnitTests/DacDbiImplTests.cs @@ -901,4 +901,89 @@ public void GetManagedStoppedContext_NoContextAvailable(MockTarget.Architecture Assert.Equal(System.HResults.S_OK, hr); Assert.Equal(0UL, retVal); } + + [Theory] + [InlineData(DebugVarLocKind.Register, false, VarLocType.VLT_REG)] + [InlineData(DebugVarLocKind.Register, true, VarLocType.VLT_REG_BYREF)] + [InlineData(DebugVarLocKind.Stack, false, VarLocType.VLT_STK)] + [InlineData(DebugVarLocKind.Stack, true, VarLocType.VLT_STK_BYREF)] + [InlineData(DebugVarLocKind.RegisterRegister, false, VarLocType.VLT_REG_REG)] + [InlineData(DebugVarLocKind.RegisterStack, false, VarLocType.VLT_REG_STK)] + [InlineData(DebugVarLocKind.StackRegister, false, VarLocType.VLT_STK_REG)] + [InlineData(DebugVarLocKind.DoubleStack, false, VarLocType.VLT_STK2)] + public void ConvertToVarLoc_MapsVarLocTypeCorrectly(DebugVarLocKind kind, bool isByRef, VarLocType expected) + { + var varInfo = new DebugVarInfo { Kind = kind, IsByRef = isByRef }; + VarLoc result = DacDbiImpl.ConvertToVarLoc(varInfo); + Assert.Equal(expected, result.vlType); + } + + [Fact] + public void ConvertToVarLoc_Register_SetsRegisterField() + { + var varInfo = new DebugVarInfo { Kind = DebugVarLocKind.Register, Register = 7 }; + VarLoc result = DacDbiImpl.ConvertToVarLoc(varInfo); + Assert.Equal(7u, result.vlrReg); + } + + [Fact] + public void ConvertToVarLoc_Stack_SetsBaseRegAndOffset() + { + var varInfo = new DebugVarInfo { Kind = DebugVarLocKind.Stack, BaseRegister = 5, StackOffset = -0x28 }; + VarLoc result = DacDbiImpl.ConvertToVarLoc(varInfo); + Assert.Equal(5u, result.vlsBaseReg); + Assert.Equal(-0x28, result.vlsOffset); + } + + [Fact] + public void ConvertToVarLoc_RegisterRegister_SetsBothRegisters() + { + var varInfo = new DebugVarInfo { Kind = DebugVarLocKind.RegisterRegister, Register = 3, Register2 = 4 }; + VarLoc result = DacDbiImpl.ConvertToVarLoc(varInfo); + Assert.Equal(3u, result.vlrrReg1); + Assert.Equal(4u, result.vlrrReg2); + } + + [Fact] + public void ConvertToVarLoc_RegisterStack_SetsRegAndStack() + { + var varInfo = new DebugVarInfo { Kind = DebugVarLocKind.RegisterStack, Register = 2, BaseRegister2 = 6, StackOffset2 = 0x10 }; + VarLoc result = DacDbiImpl.ConvertToVarLoc(varInfo); + Assert.Equal(2u, result.vlrsReg); + Assert.Equal(6u, result.vlrssBaseReg); + Assert.Equal(0x10, result.vlrssOffset); + } + + [Fact] + public void ConvertToVarLoc_StackRegister_SetsStackAndReg() + { + var varInfo = new DebugVarInfo { Kind = DebugVarLocKind.StackRegister, BaseRegister = 5, StackOffset = -8, Register = 1 }; + VarLoc result = DacDbiImpl.ConvertToVarLoc(varInfo); + Assert.Equal(5u, result.vlsrsBaseReg); + Assert.Equal(-8, result.vlsrsOffset); + Assert.Equal(1u, result.vlsrReg); + } + + [Fact] + public void ConvertToVarLoc_DoubleStack_SetsBaseRegAndOffset() + { + var varInfo = new DebugVarInfo { Kind = DebugVarLocKind.DoubleStack, BaseRegister = 4, StackOffset = 0x20 }; + VarLoc result = DacDbiImpl.ConvertToVarLoc(varInfo); + Assert.Equal(VarLocType.VLT_STK2, result.vlType); + Assert.Equal(4u, result.vlsBaseReg); + Assert.Equal(0x20, result.vlsOffset); + } + + [Theory] + [InlineData(SourceTypes.Default, 0x00u)] + [InlineData(SourceTypes.StackEmpty, 0x02u)] + [InlineData(SourceTypes.CallInstruction, 0x10u)] + [InlineData(SourceTypes.Async, 0x20u)] + [InlineData(SourceTypes.StackEmpty | SourceTypes.CallInstruction, 0x12u)] + [InlineData(SourceTypes.StackEmpty | SourceTypes.CallInstruction | SourceTypes.Async, 0x32u)] + public void ConvertSourceTypesToNative_MapsCorrectly(SourceTypes source, uint expected) + { + uint result = DacDbiImpl.ConvertSourceTypesToNative(source); + Assert.Equal(expected, result); + } } From 2a071d1fb04086c184391b5a516d8bf2a9a1aa86 Mon Sep 17 00:00:00 2001 From: Barbara Rosiak Date: Fri, 12 Jun 2026 16:35:14 -0700 Subject: [PATCH 02/12] Address pr review comments --- src/coreclr/debug/di/module.cpp | 42 +++--------- .../ClrDataFrame.cs | 27 +------- .../Dbi/DacDbiImpl.NativeCodeInfo.cs | 29 ++------ .../Dbi/DacDbiImpl.cs | 2 + .../Dbi/IDacDbiInterface.cs | 68 ++++++++++--------- .../MethodDescInfoHelpers.cs | 60 ++++++++++++++++ .../cdac/tests/UnitTests/DacDbiImplTests.cs | 24 +++++++ 7 files changed, 140 insertions(+), 112 deletions(-) create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/MethodDescInfoHelpers.cs diff --git a/src/coreclr/debug/di/module.cpp b/src/coreclr/debug/di/module.cpp index 143ca80c8c120e..dc79eac2db9b11 100644 --- a/src/coreclr/debug/di/module.cpp +++ b/src/coreclr/debug/di/module.cpp @@ -4745,22 +4745,6 @@ CordbNativeCode * CordbModule::LookupOrCreateNativeCode(mdMethodDef methodToken, return pNativeCode; } // CordbNativeCode::LookupOrCreateFromJITData -// Doubles the array capacity when full, then appends the item. -template -static void GrowAndAppend(NewArrayHolder& pArray, ULONG32& count, ULONG32& capacity, const T* pItem) -{ - if (count >= capacity) - { - ULONG32 newCapacity = (capacity == 0) ? 32 : capacity * 2; - T *pNew = new T[newCapacity]; - if (count > 0) - memcpy(pNew, pArray.GetValue(), count * sizeof(T)); - pArray = pNew; - capacity = newCapacity; - } - pArray[count++] = *pItem; -} - // LoadNativeInfo loads from the left side any native variable info // from the JIT. // @@ -4790,14 +4774,8 @@ void CordbNativeCode::LoadNativeInfo() struct CallbackData { - NewArrayHolder pVarInfos; - ULONG32 varInfoCount; - ULONG32 varInfoCapacity; - NewArrayHolder pSeqPoints; - ULONG32 seqPointCount; - ULONG32 seqPointCapacity; - - CallbackData() : varInfoCount(0), varInfoCapacity(0), seqPointCount(0), seqPointCapacity(0) {} + CallbackAccumulator varInfos; + CallbackAccumulator seqPoints; }; CallbackData data; @@ -4810,24 +4788,24 @@ void CordbNativeCode::LoadNativeInfo() &fixedArgCount, [](ICorDebugInfo::NativeVarInfo *pVarInfo, void *pUserData) { - CallbackData *pData = static_cast(pUserData); - GrowAndAppend(pData->pVarInfos, pData->varInfoCount, pData->varInfoCapacity, pVarInfo); + static_cast(pUserData)->varInfos.Push(*pVarInfo); }, [](ICorDebugInfo::OffsetMapping *pMapping, void *pUserData) { - CallbackData *pData = static_cast(pUserData); - GrowAndAppend(pData->pSeqPoints, pData->seqPointCount, pData->seqPointCapacity, pMapping); + static_cast(pUserData)->seqPoints.Push(*pMapping); }, &data)); + IfFailThrow(data.varInfos.hrError); + IfFailThrow(data.seqPoints.hrError); // Initialize native var data from collected entries - m_nativeVarData.InitVarDataList(data.pVarInfos, (int)fixedArgCount, (int)data.varInfoCount); + m_nativeVarData.InitVarDataList(data.varInfos.items.Ptr(), (int)fixedArgCount, (int)data.varInfos.items.Size()); // Initialize sequence points from collected entries - m_sequencePoints.InitSequencePoints(data.seqPointCount); - if (data.seqPointCount > 0) + m_sequencePoints.InitSequencePoints((ULONG32)data.seqPoints.items.Size()); + if (data.seqPoints.items.Size() > 0) { - m_sequencePoints.CopyAndSortSequencePoints(data.pSeqPoints); + m_sequencePoints.CopyAndSortSequencePoints(data.seqPoints.items.Ptr()); } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs index d7444056bc5f6e..5c05aba68d7a05 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs @@ -411,37 +411,14 @@ private void GetMethodInfo(out MethodDescHandle mdh, out MetadataReader mdReader IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; mdh = rts.GetMethodDescHandle(methodDescPtr); - TargetPointer mtAddr = rts.GetMethodTable(mdh); - TypeHandle typeHandle = rts.GetTypeHandle(mtAddr); - TargetPointer modulePtr = rts.GetModule(typeHandle); - ILoader loader = _target.Contracts.Loader; - moduleHandle = loader.GetModuleHandleFromModulePtr(modulePtr); - token = rts.GetMethodToken(mdh); - - IEcmaMetadata ecmaMetadataContract = _target.Contracts.EcmaMetadata; - MetadataReader? reader = ecmaMetadataContract.GetMetadata(moduleHandle); - if (reader is null) - throw new NotImplementedException(); - mdReader = reader; - - MethodDefinitionHandle methodDefHandle = MetadataTokens.MethodDefinitionHandle((int)token); - methodDef = mdReader.GetMethodDefinition(methodDefHandle); + MethodDescInfoHelpers.GetMethodInfo(_target, mdh, out mdReader, out methodDef, out moduleHandle, out token); } /// /// Parses the method signature to determine argument count and signature header. /// private static void GetMethodSignatureInfo(MetadataReader mdReader, MethodDefinition methodDef, out SignatureHeader header, out uint numArgs) - { - BlobReader blobReader = mdReader.GetBlobReader(methodDef.Signature); - header = blobReader.ReadSignatureHeader(); - if (header.Kind != SignatureKind.Method) - throw new BadImageFormatException(); - if (header.IsGeneric) - blobReader.ReadCompressedInteger(); // skip generic arity - uint paramCount = (uint)blobReader.ReadCompressedInteger(); - numArgs = paramCount + (header.IsInstance ? 1u : 0u); - } + => MethodDescInfoHelpers.GetMethodSignatureInfo(mdReader, methodDef, out header, out numArgs); /// /// Creates a ClrDataValue by resolving variable locations for the current frame diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.NativeCodeInfo.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.NativeCodeInfo.cs index 4218209920c9d7..bb8d97e8160064 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.NativeCodeInfo.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.NativeCodeInfo.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Reflection.Metadata; -using System.Reflection.Metadata.Ecma335; using System.Runtime.InteropServices; using Microsoft.Diagnostics.DataContractReader.Contracts; @@ -17,28 +16,11 @@ public sealed unsafe partial class DacDbiImpl // This does not include other implicit arguments or varargs. private uint GetArgCount(ulong vmMethodDesc) { - IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; - MethodDescHandle mdh = rts.GetMethodDescHandle(new TargetPointer(vmMethodDesc)); - uint token = rts.GetMethodToken(mdh); - TargetPointer mtAddr = rts.GetMethodTable(mdh); - TypeHandle typeHandle = rts.GetTypeHandle(mtAddr); - TargetPointer modulePtr = rts.GetModule(typeHandle); - ILoader loader = _target.Contracts.Loader; - Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(modulePtr); - IEcmaMetadata ecmaMetadata = _target.Contracts.EcmaMetadata; - MetadataReader? mdReader = ecmaMetadata.GetMetadata(moduleHandle); - if (mdReader is null) - throw Marshal.GetExceptionForHR(HResults.E_FAIL)!; + MethodDescHandle mdh = _target.Contracts.RuntimeTypeSystem.GetMethodDescHandle(new TargetPointer(vmMethodDesc)); + MethodDescInfoHelpers.GetMethodInfo(_target, mdh, out MetadataReader mdReader, out MethodDefinition methodDef, out _, out _); + MethodDescInfoHelpers.GetMethodSignatureInfo(mdReader, methodDef, out _, out uint numArgs); - MethodDefinitionHandle methodDefHandle = MetadataTokens.MethodDefinitionHandle((int)token); - MethodDefinition methodDef = mdReader.GetMethodDefinition(methodDefHandle); - BlobReader blobReader = mdReader.GetBlobReader(methodDef.Signature); - SignatureHeader sigHeader = blobReader.ReadSignatureHeader(); - if (sigHeader.IsGeneric) - blobReader.ReadCompressedInteger(); // skip generic arity - uint paramCount = (uint)blobReader.ReadCompressedInteger(); - - return paramCount + (sigHeader.IsInstance ? 1u : 0u); + return numArgs; } internal static NativeVarInfo ConvertToNativeVarInfo(DebugVarInfo varInfo) @@ -46,6 +28,7 @@ internal static NativeVarInfo ConvertToNativeVarInfo(DebugVarInfo varInfo) NativeVarInfo nvi = default; nvi.startOffset = varInfo.StartOffset; nvi.endOffset = varInfo.EndOffset; + nvi.callReturnValueILOffset = varInfo.CallReturnValueILOffset; nvi.varNumber = varInfo.VarNumber; nvi.loc = ConvertToVarLoc(varInfo); return nvi; @@ -192,6 +175,8 @@ private static void AssertVarInfosEqual(List cdac, List cdacSeqPoints = new(); #endif int hr = HResults.S_OK; + if (pFixedArgCount != null) + *pFixedArgCount = 0; try { Debug.Assert(vmMethodDesc != 0, $"vmMethodDesc is null"); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs index 9aa2461e38d0f8..abadf1971144f9 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs @@ -61,53 +61,54 @@ public enum VarLocType VLT_INVALID, } -[StructLayout(LayoutKind.Explicit)] +// The native VarLoc struct contains a union with a void* member (vlMemory), +// which causes pointer-size alignment. On 64-bit, the union starts at offset 8 +// (after vlType + padding). We use nint for the first field to absorb the +// architecture-dependent padding, matching the layout in CorInfoTypes.VarInfo.cs. +[StructLayout(LayoutKind.Sequential)] public struct VarLoc { - [FieldOffset(0)] - public VarLocType vlType; + // vlType (lower 32 bits) + pointer-size padding + private nint _vlTypeAndPadding; - // vlReg - [FieldOffset(4)] - public uint vlrReg; + // Union data: three positional slots covering all union variants. + // Different VarLocType values interpret these as different named fields. + private uint _field1; + private int _field2; + private int _field3; - // vlStk - [FieldOffset(4)] - public uint vlsBaseReg; - [FieldOffset(8)] - public int vlsOffset; + public VarLocType vlType + { + get => (VarLocType)(int)(_vlTypeAndPadding & 0xFFFFFFFF); + set => _vlTypeAndPadding = (nint)(int)value; + } + + // vlReg / vlReg_BYREF + public uint vlrReg { get => _field1; set => _field1 = value; } + + // vlStk / vlStk_BYREF / vlStk2 + public uint vlsBaseReg { get => _field1; set => _field1 = value; } + public int vlsOffset { get => _field2; set => _field2 = value; } // vlRegReg - [FieldOffset(4)] - public uint vlrrReg1; - [FieldOffset(8)] - public uint vlrrReg2; + public uint vlrrReg1 { get => _field1; set => _field1 = value; } + public uint vlrrReg2 { get => (uint)_field2; set => _field2 = (int)value; } // vlRegStk - [FieldOffset(4)] - public uint vlrsReg; - [FieldOffset(8)] - public uint vlrssBaseReg; - [FieldOffset(12)] - public int vlrssOffset; + public uint vlrsReg { get => _field1; set => _field1 = value; } + public uint vlrssBaseReg { get => (uint)_field2; set => _field2 = (int)value; } + public int vlrssOffset { get => _field3; set => _field3 = value; } // vlStkReg - [FieldOffset(4)] - public uint vlsrsBaseReg; - [FieldOffset(8)] - public int vlsrsOffset; - [FieldOffset(12)] - public uint vlsrReg; - - // vlStk2 (same layout as vlStk) + public uint vlsrsBaseReg { get => _field1; set => _field1 = value; } + public int vlsrsOffset { get => _field2; set => _field2 = value; } + public uint vlsrReg { get => (uint)_field3; set => _field3 = (int)value; } // vlFPstk - [FieldOffset(4)] - public uint vlfReg; + public uint vlfReg { get => _field1; set => _field1 = value; } // vlFixedVarArg - [FieldOffset(4)] - public uint vlfvOffset; + public uint vlfvOffset { get => _field1; set => _field1 = value; } } [StructLayout(LayoutKind.Sequential)] @@ -115,6 +116,7 @@ public struct NativeVarInfo { public uint startOffset; public uint endOffset; + public uint callReturnValueILOffset; public uint varNumber; public VarLoc loc; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/MethodDescInfoHelpers.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/MethodDescInfoHelpers.cs new file mode 100644 index 00000000000000..1893f6d96bb873 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/MethodDescInfoHelpers.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using Microsoft.Diagnostics.DataContractReader.Contracts; + +namespace Microsoft.Diagnostics.DataContractReader.Legacy; + +/// +/// Shared helpers for resolving MethodDesc metadata and signature information. +/// +internal static class MethodDescInfoHelpers +{ + /// + /// Resolves a MethodDescHandle into its module-level metadata objects. + /// Throws on failure (no metadata, etc.). + /// + public static void GetMethodInfo( + Target target, + MethodDescHandle mdh, + out MetadataReader mdReader, + out MethodDefinition methodDef, + out Contracts.ModuleHandle moduleHandle, + out uint token) + { + IRuntimeTypeSystem rts = target.Contracts.RuntimeTypeSystem; + TargetPointer mtAddr = rts.GetMethodTable(mdh); + TypeHandle typeHandle = rts.GetTypeHandle(mtAddr); + TargetPointer modulePtr = rts.GetModule(typeHandle); + ILoader loader = target.Contracts.Loader; + moduleHandle = loader.GetModuleHandleFromModulePtr(modulePtr); + token = rts.GetMethodToken(mdh); + + IEcmaMetadata ecmaMetadata = target.Contracts.EcmaMetadata; + MetadataReader? reader = ecmaMetadata.GetMetadata(moduleHandle); + if (reader is null) + throw new NotImplementedException(); + mdReader = reader; + + MethodDefinitionHandle methodDefHandle = MetadataTokens.MethodDefinitionHandle((int)token); + methodDef = mdReader.GetMethodDefinition(methodDefHandle); + } + + /// + /// Parses the method signature to determine argument count and signature header. + /// + public static void GetMethodSignatureInfo(MetadataReader mdReader, MethodDefinition methodDef, out SignatureHeader header, out uint numArgs) + { + BlobReader blobReader = mdReader.GetBlobReader(methodDef.Signature); + header = blobReader.ReadSignatureHeader(); + if (header.Kind != SignatureKind.Method) + throw new BadImageFormatException(); + if (header.IsGeneric) + blobReader.ReadCompressedInteger(); // skip generic arity + uint paramCount = (uint)blobReader.ReadCompressedInteger(); + numArgs = paramCount + (header.IsInstance ? 1u : 0u); + } +} diff --git a/src/native/managed/cdac/tests/UnitTests/DacDbiImplTests.cs b/src/native/managed/cdac/tests/UnitTests/DacDbiImplTests.cs index 5f6cfe6f50313a..3079f00d468849 100644 --- a/src/native/managed/cdac/tests/UnitTests/DacDbiImplTests.cs +++ b/src/native/managed/cdac/tests/UnitTests/DacDbiImplTests.cs @@ -986,4 +986,28 @@ public void ConvertSourceTypesToNative_MapsCorrectly(SourceTypes source, uint ex uint result = DacDbiImpl.ConvertSourceTypesToNative(source); Assert.Equal(expected, result); } + + [Fact] + public void ConvertToNativeVarInfo_MapsAllFields() + { + var varInfo = new DebugVarInfo + { + Kind = DebugVarLocKind.Stack, + IsByRef = false, + StartOffset = 10, + EndOffset = 50, + CallReturnValueILOffset = 42, + VarNumber = 3, + BaseRegister = 5, + StackOffset = -0x28, + }; + NativeVarInfo nvi = DacDbiImpl.ConvertToNativeVarInfo(varInfo); + Assert.Equal(10u, nvi.startOffset); + Assert.Equal(50u, nvi.endOffset); + Assert.Equal(42u, nvi.callReturnValueILOffset); + Assert.Equal(3u, nvi.varNumber); + Assert.Equal(VarLocType.VLT_STK, nvi.loc.vlType); + Assert.Equal(5u, nvi.loc.vlsBaseReg); + Assert.Equal(-0x28, nvi.loc.vlsOffset); + } } From c7b725c138b8723599bd6e413eb75e85f29f931d Mon Sep 17 00:00:00 2001 From: Barbara Rosiak Date: Tue, 16 Jun 2026 16:32:26 -0700 Subject: [PATCH 03/12] Fix GetArgCount failure for methods without metadata tokens --- .../design/datacontracts/RuntimeTypeSystem.md | 3 + .../vm/datadescriptor/datadescriptor.inc | 4 +- src/coreclr/vm/method.hpp | 10 ++- src/coreclr/vm/siginfo.hpp | 2 + .../Contracts/IRuntimeTypeSystem.cs | 4 + .../Contracts/RuntimeTypeSystem_1.cs | 45 +++++++++++ .../Data/AsyncMethodData.cs | 2 + .../ClrDataFrame.cs | 79 +++++++++++-------- .../Dbi/DacDbiImpl.NativeCodeInfo.cs | 9 ++- .../MethodDescInfoHelpers.cs | 23 +++--- .../TypeNameBuilder.cs | 9 +-- .../cdac/tests/UnitTests/MethodDescTests.cs | 2 + .../MockDescriptors.MethodDescriptors.cs | 2 +- 13 files changed, 142 insertions(+), 52 deletions(-) diff --git a/docs/design/datacontracts/RuntimeTypeSystem.md b/docs/design/datacontracts/RuntimeTypeSystem.md index e333f0c0b5875c..c9f4a1e5e71fa6 100644 --- a/docs/design/datacontracts/RuntimeTypeSystem.md +++ b/docs/design/datacontracts/RuntimeTypeSystem.md @@ -1232,6 +1232,9 @@ We depend on the following data descriptors: | `StoredSigMethodDesc` | `cSig` | Count of bytes in the metadata signature | | `StoredSigMethodDesc` | `ExtendedFlags` | Flags field for the `StoredSigMethodDesc` | | `DynamicMethodDesc` | `MethodName` | Pointer to Null-terminated UTF8 string describing the Method desc | +| `AsyncMethodData` | `Flags` | Async method flags | +| `AsyncMethodData` | `Sig` | Pointer to the async variant's signature blob | +| `AsyncMethodData` | `cSig` | Count of bytes in the async variant's signature blob | | `GCCoverageInfo` | `SavedCode` | Pointer to the GCCover saved code copy, if supported | The following data descriptor types are used only for their sizes when computing the total size of a `MethodDesc` instance. diff --git a/src/coreclr/vm/datadescriptor/datadescriptor.inc b/src/coreclr/vm/datadescriptor/datadescriptor.inc index 0cc320903e7560..c5058cac1017ae 100644 --- a/src/coreclr/vm/datadescriptor/datadescriptor.inc +++ b/src/coreclr/vm/datadescriptor/datadescriptor.inc @@ -646,7 +646,9 @@ CDAC_TYPE_END(NativeCodeSlot) CDAC_TYPE_BEGIN(AsyncMethodData) CDAC_TYPE_SIZE(sizeof(AsyncMethodData)) -CDAC_TYPE_FIELD(AsyncMethodData, T_UINT32, Flags, offsetof(AsyncMethodData, flags)) +CDAC_TYPE_FIELD(AsyncMethodData, T_UINT32, Flags, cdac_data::Flags) +CDAC_TYPE_FIELD(AsyncMethodData, T_POINTER, Sig, cdac_data::Sig) +CDAC_TYPE_FIELD(AsyncMethodData, T_UINT32, cSig, cdac_data::cSig) CDAC_TYPE_END(AsyncMethodData) CDAC_TYPE_BEGIN(InstantiatedMethodDesc) diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index dc0c6b3cef2b08..e5c15e69db99df 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -60,7 +60,7 @@ EXTERN_C VOID STDCALL PInvokeImportThunk(); #define METHOD_TOKEN_RANGE_BIT_COUNT (24 - METHOD_TOKEN_REMAINDER_BIT_COUNT) #define METHOD_TOKEN_RANGE_MASK ((1 << METHOD_TOKEN_RANGE_BIT_COUNT) - 1) -// [cDAC] [RuntimeTypeSystem]: Contract depends on the values of Thunk, None. +// [cDAC] [RuntimeTypeSystem]: Contract depends on the values of Thunk, None, IsAsyncVariant. enum class AsyncMethodFlags { // Method uses CORINFO_CALLCONV_ASYNCCALL call convention. @@ -142,6 +142,14 @@ struct AsyncMethodData typedef DPTR(struct AsyncMethodData) PTR_AsyncMethodData; +template<> +struct cdac_data +{ + static constexpr size_t Flags = offsetof(AsyncMethodData, flags); + static constexpr size_t Sig = offsetof(AsyncMethodData, sig) + offsetof(Signature, m_pSig); + static constexpr size_t cSig = offsetof(AsyncMethodData, sig) + offsetof(Signature, m_cbSig); +}; + //============================================================= // Splits methoddef token into two pieces for // storage inside a methoddesc. diff --git a/src/coreclr/vm/siginfo.hpp b/src/coreclr/vm/siginfo.hpp index c19a75c5f8fefc..a4d421350f1a12 100644 --- a/src/coreclr/vm/siginfo.hpp +++ b/src/coreclr/vm/siginfo.hpp @@ -297,6 +297,7 @@ class SigPointer : public SigParser // forward declarations needed for the friends declared in Signature struct FrameInfo; struct VASigCookie; +struct AsyncMethodData; #if defined(DACCESS_COMPILE) class DacDbiInterfaceImpl; #endif // DACCESS_COMPILE @@ -354,6 +355,7 @@ class Signature private: friend struct ::cdac_data; + friend struct ::cdac_data; PCCOR_SIGNATURE m_pSig; DWORD m_cbSig; 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 2c4c61dea61faa..d6ef2760f9e19a 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 @@ -233,6 +233,10 @@ public interface IRuntimeTypeSystem : IContract // A StoredSigMethodDesc is a MethodDesc for which the signature isn't found in metadata. bool IsStoredSigMethodDesc(MethodDescHandle methodDesc, out ReadOnlySpan signature) => throw new NotImplementedException(); + // Gets the raw signature bytes for a MethodDesc by checking stored signature, async variant signature, then metadata. + // Returns false if no signature could be resolved. + bool TryGetMethodSignature(MethodDescHandle methodDesc, out ReadOnlySpan signature) => throw new NotImplementedException(); + // Return true for a MethodDesc that describes a method represented by the System.Reflection.Emit.DynamicMethod class // A DynamicMethod is also a StoredSigMethodDesc, and a NoMetadataMethod bool IsDynamicMethod(MethodDescHandle methodDesc) => throw new NotImplementedException(); 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 5af7d7c40ed550..01655cf9d5a126 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 @@ -143,6 +143,7 @@ internal enum AsyncMethodFlags : uint { None = 0, AsyncCall = 0x1, + IsAsyncVariant = 0x4, Thunk = 16, } @@ -1517,6 +1518,50 @@ public bool IsStoredSigMethodDesc(MethodDescHandle methodDescHandle, out ReadOnl return true; } + public bool TryGetMethodSignature(MethodDescHandle methodDescHandle, out ReadOnlySpan signature) + { + MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; + + if (methodDesc.Classification is MethodClassification.Dynamic or MethodClassification.EEImpl or MethodClassification.Array) + { + signature = AsStoredSigMethodDesc(methodDesc).Signature; + return true; + } + + if (methodDesc.HasAsyncMethodData) + { + Data.AsyncMethodData asyncData = _target.ProcessedData.GetOrAdd(methodDesc.GetAddressOfAsyncMethodData()); + if (((AsyncMethodFlags)asyncData.Flags).HasFlag(AsyncMethodFlags.IsAsyncVariant)) + { + byte[] sig = new byte[asyncData.cSig]; + _target.ReadBuffer(asyncData.Sig, sig.AsSpan()); + signature = sig; + return true; + } + } + + uint token = methodDesc.Token; + if (EcmaMetadataUtils.GetRowId(token) == 0) + { + signature = default; + return false; + } + + TargetPointer modulePtr = GetOrCreateMethodTable(methodDesc).Module; + ModuleHandle moduleHandle = _target.Contracts.Loader.GetModuleHandleFromModulePtr(modulePtr); + MetadataReader? mdReader = _target.Contracts.EcmaMetadata.GetMetadata(moduleHandle); + if (mdReader is null) + { + signature = default; + return false; + } + + MethodDefinitionHandle methodDefHandle = MetadataTokens.MethodDefinitionHandle((int)EcmaMetadataUtils.GetRowId(token)); + MethodDefinition methodDef = mdReader.GetMethodDefinition(methodDefHandle); + signature = mdReader.GetBlobBytes(methodDef.Signature); + return true; + } + public bool IsDynamicMethod(MethodDescHandle methodDescHandle) { MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/AsyncMethodData.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/AsyncMethodData.cs index f91f93bfcc7a23..6b54d41196f0ff 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/AsyncMethodData.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/AsyncMethodData.cs @@ -7,4 +7,6 @@ namespace Microsoft.Diagnostics.DataContractReader.Data; internal sealed partial class AsyncMethodData : IData { [Field] public uint Flags { get; } + [Field] public TargetPointer Sig { get; } + [Field] public uint cSig { get; } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs index 5c05aba68d7a05..8465f6f54e366f 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs @@ -141,8 +141,12 @@ int IXCLRDataFrame.GetNumArguments(uint* numArgs) try { *numArgs = 0; - GetMethodInfo(out _, out MetadataReader mdReader, out MethodDefinition methodDef, out _, out _); - GetMethodSignatureInfo(mdReader, methodDef, out _, out uint numArgsResult); + MethodDescHandle mdh = GetFrameMethodDesc(out _); + + if (!_target.Contracts.RuntimeTypeSystem.TryGetMethodSignature(mdh, out ReadOnlySpan signature)) + throw Marshal.GetExceptionForHR(HResults.E_FAIL)!; + + MethodDescInfoHelpers.GetSignatureInfo(signature, out _, out uint numArgsResult); *numArgs = numArgsResult; if (*numArgs == 0) hr = HResults.S_FALSE; @@ -190,8 +194,13 @@ int IXCLRDataFrame.GetArgumentByIndex( if (nameLen is not null) *nameLen = 0; - GetMethodInfo(out MethodDescHandle mdh, out MetadataReader mdReader, out MethodDefinition methodDef, out Contracts.ModuleHandle moduleHandle, out _); - GetMethodSignatureInfo(mdReader, methodDef, out SignatureHeader header, out uint numArgs); + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + MethodDescHandle mdh = GetFrameMethodDesc(out Contracts.ModuleHandle moduleHandle); + + if (!rts.TryGetMethodSignature(mdh, out ReadOnlySpan signature)) + throw Marshal.GetExceptionForHR(HResults.E_FAIL)!; + + MethodDescInfoHelpers.GetSignatureInfo(signature, out SignatureHeader header, out uint numArgs); if (index >= numArgs) throw Marshal.GetExceptionForHR(HResults.E_INVALIDARG)!; @@ -199,7 +208,6 @@ int IXCLRDataFrame.GetArgumentByIndex( // Resolve parameter name if ((bufLen > 0 && name is not null) || nameLen is not null) { - IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; if (index == 0 && header.IsInstance) { OutputBufferHelpers.CopyStringToBuffer(name, bufLen, nameLen, "this"); @@ -209,8 +217,20 @@ int IXCLRDataFrame.GetArgumentByIndex( // Param indexing is 1-based in metadata. 'this' isn't in the // signature, so for instance methods adjust the index down. int mdIndex = (int)(header.IsInstance ? index : index + 1); - string? paramName = GetParameterName(mdReader, methodDef, mdIndex); - OutputBufferHelpers.CopyStringToBuffer(name, bufLen, nameLen, paramName ?? string.Empty); + uint token = rts.GetMethodToken(mdh); + MetadataReader? mdReader = (EcmaMetadataUtils.GetRowId(token) != 0) + ? _target.Contracts.EcmaMetadata.GetMetadata(moduleHandle) + : null; + if (mdReader is not null) + { + MethodDefinition methodDef = mdReader.GetMethodDefinition(MetadataTokens.MethodDefinitionHandle((int)token)); + string? paramName = GetParameterName(mdReader, methodDef, mdIndex); + OutputBufferHelpers.CopyStringToBuffer(name, bufLen, nameLen, paramName ?? string.Empty); + } + else + { + OutputBufferHelpers.CopyStringToBuffer(name, bufLen, nameLen, string.Empty); + } } else { @@ -247,7 +267,7 @@ int IXCLRDataFrame.GetNumLocalVariables(uint* numLocals) try { *numLocals = 0; - GetMethodInfo(out MethodDescHandle mdh, out _, out _, out Contracts.ModuleHandle moduleHandle, out _); + MethodDescHandle mdh = GetFrameMethodDesc(out Contracts.ModuleHandle moduleHandle); *numLocals = GetLocalVariableCount(mdh, moduleHandle); } catch (System.Exception ex) @@ -293,8 +313,13 @@ int IXCLRDataFrame.GetLocalVariableByIndex( if (nameLen is not null) *nameLen = 0; - GetMethodInfo(out MethodDescHandle mdh, out MetadataReader mdReader, out MethodDefinition methodDef, out Contracts.ModuleHandle moduleHandle, out _); - GetMethodSignatureInfo(mdReader, methodDef, out SignatureHeader argHeader, out uint numArgs); + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + MethodDescHandle mdh = GetFrameMethodDesc(out Contracts.ModuleHandle moduleHandle); + + if (!rts.TryGetMethodSignature(mdh, out ReadOnlySpan signature)) + throw Marshal.GetExceptionForHR(HResults.E_FAIL)!; + + MethodDescInfoHelpers.GetSignatureInfo(signature, out SignatureHeader argHeader, out uint numArgs); uint numLocals = GetLocalVariableCount(mdh, moduleHandle); @@ -349,15 +374,8 @@ int IXCLRDataFrame.GetMethodInstance(DacComNullableByRef // ========== Metadata resolution helpers ========== /// - /// Resolves the frame's MethodDesc into its module-level metadata objects. + /// Resolves the frame's MethodDesc and its containing module. /// Throws on failure (no MethodDesc, no metadata, etc.). /// - private void GetMethodInfo(out MethodDescHandle mdh, out MetadataReader mdReader, out MethodDefinition methodDef, out Contracts.ModuleHandle moduleHandle, out uint token) + private MethodDescHandle GetFrameMethodDesc(out Contracts.ModuleHandle moduleHandle) { - IStackWalk stackWalk = _target.Contracts.StackWalk; - TargetPointer methodDescPtr = stackWalk.GetMethodDescPtr(_dataFrame); + TargetPointer methodDescPtr = _target.Contracts.StackWalk.GetMethodDescPtr(_dataFrame); if (methodDescPtr == TargetPointer.Null) throw new InvalidCastException(); // E_NOINTERFACE IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; - mdh = rts.GetMethodDescHandle(methodDescPtr); - MethodDescInfoHelpers.GetMethodInfo(_target, mdh, out mdReader, out methodDef, out moduleHandle, out token); - } + MethodDescHandle mdh = rts.GetMethodDescHandle(methodDescPtr); - /// - /// Parses the method signature to determine argument count and signature header. - /// - private static void GetMethodSignatureInfo(MetadataReader mdReader, MethodDefinition methodDef, out SignatureHeader header, out uint numArgs) - => MethodDescInfoHelpers.GetMethodSignatureInfo(mdReader, methodDef, out header, out numArgs); + TargetPointer mtAddr = rts.GetMethodTable(mdh); + TypeHandle typeHandle = rts.GetTypeHandle(mtAddr); + TargetPointer modulePtr = rts.GetModule(typeHandle); + moduleHandle = _target.Contracts.Loader.GetModuleHandleFromModulePtr(modulePtr); + + return mdh; + } /// /// Creates a ClrDataValue by resolving variable locations for the current frame @@ -554,7 +571,7 @@ private uint GetLocalVariableCount(MethodDescHandle mdh, Contracts.ModuleHandle { try { - GetMethodInfo(out _, out MetadataReader mdReader, out MethodDefinition methodDef, out _, out _); + MethodDescInfoHelpers.GetMethodInfo(_target, mdh, out MetadataReader mdReader, out MethodDefinition methodDef, out _, out _); FlagSignatureTypeProvider provider = new(_target, moduleHandle); SignatureDecoder<(uint Flags, int Size), MethodDescHandle> decoder = new(provider, mdReader, mdh); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.NativeCodeInfo.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.NativeCodeInfo.cs index bb8d97e8160064..191ce399973c2f 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.NativeCodeInfo.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.NativeCodeInfo.cs @@ -16,10 +16,13 @@ public sealed unsafe partial class DacDbiImpl // This does not include other implicit arguments or varargs. private uint GetArgCount(ulong vmMethodDesc) { - MethodDescHandle mdh = _target.Contracts.RuntimeTypeSystem.GetMethodDescHandle(new TargetPointer(vmMethodDesc)); - MethodDescInfoHelpers.GetMethodInfo(_target, mdh, out MetadataReader mdReader, out MethodDefinition methodDef, out _, out _); - MethodDescInfoHelpers.GetMethodSignatureInfo(mdReader, methodDef, out _, out uint numArgs); + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + MethodDescHandle mdh = rts.GetMethodDescHandle(new TargetPointer(vmMethodDesc)); + if (!rts.TryGetMethodSignature(mdh, out ReadOnlySpan signature)) + throw Marshal.GetExceptionForHR(HResults.E_FAIL)!; + + MethodDescInfoHelpers.GetSignatureInfo(signature, out _, out uint numArgs); return numArgs; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/MethodDescInfoHelpers.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/MethodDescInfoHelpers.cs index 1893f6d96bb873..15214481a7dbba 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/MethodDescInfoHelpers.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/MethodDescInfoHelpers.cs @@ -44,17 +44,20 @@ public static void GetMethodInfo( } /// - /// Parses the method signature to determine argument count and signature header. + /// Parses raw signature bytes to determine the signature header and argument count. /// - public static void GetMethodSignatureInfo(MetadataReader mdReader, MethodDefinition methodDef, out SignatureHeader header, out uint numArgs) + public static unsafe void GetSignatureInfo(ReadOnlySpan signature, out SignatureHeader header, out uint numArgs) { - BlobReader blobReader = mdReader.GetBlobReader(methodDef.Signature); - header = blobReader.ReadSignatureHeader(); - if (header.Kind != SignatureKind.Method) - throw new BadImageFormatException(); - if (header.IsGeneric) - blobReader.ReadCompressedInteger(); // skip generic arity - uint paramCount = (uint)blobReader.ReadCompressedInteger(); - numArgs = paramCount + (header.IsInstance ? 1u : 0u); + fixed (byte* pSig = signature) + { + BlobReader blobReader = new BlobReader(pSig, signature.Length); + header = blobReader.ReadSignatureHeader(); + if (header.Kind != SignatureKind.Method) + throw new BadImageFormatException(); + if (header.IsGeneric) + blobReader.ReadCompressedInteger(); // skip generic arity + uint paramCount = (uint)blobReader.ReadCompressedInteger(); + numArgs = paramCount + (header.IsInstance ? 1u : 0u); + } } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/TypeNameBuilder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/TypeNameBuilder.cs index 4372ca366ad388..bfb4cf9da4d820 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/TypeNameBuilder.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/TypeNameBuilder.cs @@ -136,13 +136,12 @@ public static void AppendMethodImpl(Target target, StringBuilder stringBuilder, { ReadOnlySpan signature; MetadataReader? reader = default; - if (!runtimeTypeSystem.IsStoredSigMethodDesc(method, out signature)) + if (runtimeTypeSystem.TryGetMethodSignature(method, out signature)) { - reader = target.Contracts.EcmaMetadata.GetMetadata(module); - if (reader is not null) + // Stored signature methods don't have metadata to resolve type references + if (!runtimeTypeSystem.IsStoredSigMethodDesc(method, out _)) { - MethodDefinition methodDef = reader.GetMethodDefinition(MetadataTokens.MethodDefinitionHandle((int)runtimeTypeSystem.GetMethodToken(method))); - signature = reader.GetBlobBytes(methodDef.Signature); + reader = target.Contracts.EcmaMetadata.GetMetadata(module); } } diff --git a/src/native/managed/cdac/tests/UnitTests/MethodDescTests.cs b/src/native/managed/cdac/tests/UnitTests/MethodDescTests.cs index e16b21181b980f..529c1dcaa986d8 100644 --- a/src/native/managed/cdac/tests/UnitTests/MethodDescTests.cs +++ b/src/native/managed/cdac/tests/UnitTests/MethodDescTests.cs @@ -32,6 +32,8 @@ public class MethodDescTests Fields = new Dictionary { [nameof(Data.AsyncMethodData.Flags)] = new Target.FieldInfo { Offset = 0 }, + [nameof(Data.AsyncMethodData.Sig)] = new Target.FieldInfo { Offset = (int)methodDescBuilder.TargetTestHelpers.PointerSize }, + [nameof(Data.AsyncMethodData.cSig)] = new Target.FieldInfo { Offset = (int)methodDescBuilder.TargetTestHelpers.PointerSize * 2 }, }, }, [DataType.ArrayMethodDesc] = new Target.TypeInfo { Size = methodDescBuilder.ArrayMethodDescSize }, diff --git a/src/native/managed/cdac/tests/UnitTests/MockDescriptors/MockDescriptors.MethodDescriptors.cs b/src/native/managed/cdac/tests/UnitTests/MockDescriptors/MockDescriptors.MethodDescriptors.cs index e388a570c63bbe..b9a4a9607ca0a9 100644 --- a/src/native/managed/cdac/tests/UnitTests/MockDescriptors/MockDescriptors.MethodDescriptors.cs +++ b/src/native/managed/cdac/tests/UnitTests/MockDescriptors/MockDescriptors.MethodDescriptors.cs @@ -259,7 +259,7 @@ internal sealed class MockMethodDescriptorsBuilder public uint NonVtableSlotSize => (uint)TargetTestHelpers.PointerSize; public uint MethodImplSize => (uint)(TargetTestHelpers.PointerSize * 2); public uint NativeCodeSlotSize => (uint)TargetTestHelpers.PointerSize; - public uint AsyncMethodDataSize => (uint)(TargetTestHelpers.PointerSize * 2); + public uint AsyncMethodDataSize => (uint)(TargetTestHelpers.PointerSize * 3); public uint ArrayMethodDescSize => (uint)StoredSigMethodDescLayout.Size; public uint FCallMethodDescSize => (uint)(MethodDescLayout.Size + TargetTestHelpers.PointerSize); public uint PInvokeMethodDescSize => (uint)(MethodDescLayout.Size + TargetTestHelpers.PointerSize); From a6ed374f4959ede08f37761fbdbab1d7f95bb557 Mon Sep 17 00:00:00 2001 From: Barbara Rosiak Date: Mon, 22 Jun 2026 16:44:16 -0700 Subject: [PATCH 04/12] Address pr review comments --- .../design/datacontracts/RuntimeTypeSystem.md | 49 +++++++++++++++ src/coreclr/debug/daccess/dacdbiimpl.cpp | 40 +++++------- src/coreclr/inc/cordebuginfo.h | 9 --- src/coreclr/inc/jiteeversionguid.h | 10 +-- src/coreclr/jit/codegeninterface.h | 8 --- .../JitInterface/CorInfoTypes.VarInfo.cs | 13 +--- .../ClrDataFrame.cs | 15 +++-- .../Dbi/DacDbiImpl.NativeCodeInfo.cs | 35 ++++++----- .../Dbi/IDacDbiInterface.cs | 38 ++++++----- .../MethodDescInfoHelpers.cs | 63 ------------------- .../MethodSignatureHelpers.cs | 31 +++++++++ .../TypeNameBuilder.cs | 13 ++-- .../cdac/tests/UnitTests/DacDbiImplTests.cs | 4 +- 13 files changed, 160 insertions(+), 168 deletions(-) delete mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/MethodDescInfoHelpers.cs create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/MethodSignatureHelpers.cs diff --git a/docs/design/datacontracts/RuntimeTypeSystem.md b/docs/design/datacontracts/RuntimeTypeSystem.md index 139e05f0b66f34..9d04852ad34ac8 100644 --- a/docs/design/datacontracts/RuntimeTypeSystem.md +++ b/docs/design/datacontracts/RuntimeTypeSystem.md @@ -250,6 +250,11 @@ partial interface IRuntimeTypeSystem : IContract // Corresponds to native MethodDesc::IsAsyncMethod(). public virtual bool IsAsyncMethod(MethodDescHandle methodDesc); + // Gets the raw signature bytes for a MethodDesc by checking the stored signature, + // then the async variant signature, then ECMA metadata. Returns false if no + // signature could be resolved. + public virtual bool TryGetMethodSignature(MethodDescHandle methodDesc, out ReadOnlySpan signature); + // Return true if a MethodDesc is in a collectible module public virtual bool IsCollectibleMethod(MethodDescHandle methodDesc); @@ -1552,6 +1557,50 @@ And the various apis are implemented with the following algorithms return 0x06000000 | tokenRange | tokenRemainder; } + public bool TryGetMethodSignature(MethodDescHandle methodDescHandle, out ReadOnlySpan signature) + { + MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; + + // Dynamic, EEImpl and Array methods store their signature directly on the MethodDesc. + MethodClassification classification = (MethodClassification)(methodDesc.Flags & MethodDescFlags.ClassificationMask); + if (classification is MethodClassification.Dynamic or MethodClassification.EEImpl or MethodClassification.Array) + { + signature = // StoredSigMethodDesc.Signature blob for methodDesc + return true; + } + + // Async variant methods carry their signature in the AsyncMethodData. + if (HasFlag(methodDesc, MethodDescFlags.HasAsyncMethodData)) + { + AsyncMethodData asyncData = // Read AsyncMethodData for methodDesc + if (((AsyncMethodFlags)asyncData.Flags).HasFlag(AsyncMethodFlags.IsAsyncVariant)) + { + signature = // Read asyncData.cSig bytes from asyncData.Sig + return true; + } + } + + // Otherwise resolve the signature from ECMA metadata using the MethodDef token. + uint token = GetMethodToken(methodDescHandle); + if (EcmaMetadataUtils.GetRowId(token) == 0) + { + signature = default; + return false; + } + + MetadataReader? mdReader = // Get metadata reader for the method's module, or null if unavailable + if (mdReader is null) + { + signature = default; + return false; + } + + MethodDefinitionHandle methodDefHandle = MetadataTokens.MethodDefinitionHandle((int)EcmaMetadataUtils.GetRowId(token)); + MethodDefinition methodDef = mdReader.GetMethodDefinition(methodDefHandle); + signature = mdReader.GetBlobBytes(methodDef.Signature); + return true; + } + public uint GetMethodDescSize(MethodDescHandle methodDescHandle) { MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index bca8e59c2de403..e2f5432c320735 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -813,40 +813,32 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetNativeCodeSequencePointsAndVar DebugInfoRequest request; request.InitFromStartingAddr(pMD, CORDB_ADDRESS_TO_TADDR(startAddress)); - // Get native variable info and invoke callback for each entry + // Retrieve sequence points and native variable info in a single request + NewArrayHolder mapCopy(NULL); + NewArrayHolder nativeVars(NULL); + ULONG32 seqEntryCount = 0; + ULONG32 varEntryCount = 0; + + BOOL success = DebugInfoManager::GetBoundariesAndVars(request, + InfoStoreNew, NULL, + BoundsType::Uninstrumented, + &seqEntryCount, &mapCopy, + &varEntryCount, &nativeVars); + if (!success) + ThrowHR(E_FAIL); + + // Invoke the native variable info callback for each entry if (fpVarInfoCallback != NULL) { - NewArrayHolder nativeVars(NULL); - ULONG32 varEntryCount; - - BOOL success = DebugInfoManager::GetBoundariesAndVars(request, - InfoStoreNew, NULL, - BoundsType::Instrumented, - NULL, NULL, - &varEntryCount, &nativeVars); - if (!success) - ThrowHR(E_FAIL); - for (ULONG32 i = 0; i < varEntryCount; i++) { fpVarInfoCallback(&nativeVars[i], pUserData); } } - // Get sequence points and invoke callback for each entry + // Invoke the sequence point callback for each entry if (fpSeqPointCallback != NULL) { - NewArrayHolder mapCopy(NULL); - ULONG32 seqEntryCount; - - BOOL success = DebugInfoManager::GetBoundariesAndVars(request, - InfoStoreNew, NULL, - BoundsType::Uninstrumented, - &seqEntryCount, &mapCopy, - NULL, NULL); - if (!success) - ThrowHR(E_FAIL); - for (ULONG32 i = 0; i < seqEntryCount; i++) { fpSeqPointCallback(&mapCopy[i], pUserData); diff --git a/src/coreclr/inc/cordebuginfo.h b/src/coreclr/inc/cordebuginfo.h index 063afb063806a5..1d6bdfcec3bfcc 100644 --- a/src/coreclr/inc/cordebuginfo.h +++ b/src/coreclr/inc/cordebuginfo.h @@ -354,14 +354,6 @@ class ICorDebugInfo unsigned vlfvOffset; }; - // VLT_MEMORY - - struct vlMemory - { - void *rpValue; // pointer to the in-process - // location of the value. - }; - // [cDAC]: Mirrored in managed code (IDacDbiInterface.cs). struct VarLoc { @@ -377,7 +369,6 @@ class ICorDebugInfo ICorDebugInfo::vlStk2 vlStk2; ICorDebugInfo::vlFPstk vlFPstk; ICorDebugInfo::vlFixedVarArg vlFixedVarArg; - ICorDebugInfo::vlMemory vlMemory; }; }; diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 74b2c30bccd41f..35c834c6a3cfc1 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -37,11 +37,11 @@ #include -constexpr GUID JITEEVersionIdentifier = { /* 2fa4c0dd-1b2b-4c3f-8a88-cc7b79c4667f */ - 0x2fa4c0dd, - 0x1b2b, - 0x4c3f, - {0x8a, 0x88, 0xcc, 0x7b, 0x79, 0xc4, 0x66, 0x7f} +constexpr GUID JITEEVersionIdentifier = { /* 257c022f-41d8-42c6-b4a7-51ae952821bf */ + 0x257c022f, + 0x41d8, + 0x42c6, + {0xb4, 0xa7, 0x51, 0xae, 0x95, 0x28, 0x21, 0xbf} }; #endif // JIT_EE_VERSIONING_GUID_H diff --git a/src/coreclr/jit/codegeninterface.h b/src/coreclr/jit/codegeninterface.h index fac4b90855a287..5214749d6b2eaa 100644 --- a/src/coreclr/jit/codegeninterface.h +++ b/src/coreclr/jit/codegeninterface.h @@ -614,14 +614,6 @@ class CodeGenInterface { unsigned vlfvOffset; } vlFixedVarArg; - - // VLT_MEMORY - - struct - { - void* rpValue; // pointer to the in-process - // location of the value. - } vlMemory; }; // Helper functions diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.VarInfo.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.VarInfo.cs index e307bcb2844616..7b72cc95bece2b 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.VarInfo.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.VarInfo.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Runtime.InteropServices; // @@ -43,12 +42,12 @@ public struct NativeVarInfo [StructLayout(LayoutKind.Sequential)] public struct VarLoc { - public IntPtr A; // vlType + padding + public uint A; // vlType public int B; public int C; public int D; - public VarLocType LocationType => (VarLocType)(A.ToInt64() & 0xFFFFFFFF); + public VarLocType LocationType => (VarLocType)A; /* Changes to the following types may require revisiting the above layout. @@ -167,14 +166,6 @@ struct VarLoc { unsigned vlfvOffset; } vlFixedVarArg; - - // VLT_MEMORY - - struct - { - void *rpValue; // pointer to the in-process - // location of the value. - } vlMemory; }; }; */ diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs index 243ebe9d0840e3..9e8cc8dccbba50 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs @@ -145,7 +145,7 @@ int IXCLRDataFrame.GetNumArguments(uint* numArgs) if (!_target.Contracts.RuntimeTypeSystem.TryGetMethodSignature(mdh, out ReadOnlySpan signature)) throw Marshal.GetExceptionForHR(HResults.E_FAIL)!; - MethodDescInfoHelpers.GetSignatureInfo(signature, out _, out uint numArgsResult); + MethodSignatureHelpers.GetSignatureInfo(signature, out _, out uint numArgsResult); *numArgs = numArgsResult; if (*numArgs == 0) hr = HResults.S_FALSE; @@ -199,7 +199,7 @@ int IXCLRDataFrame.GetArgumentByIndex( if (!rts.TryGetMethodSignature(mdh, out ReadOnlySpan signature)) throw Marshal.GetExceptionForHR(HResults.E_FAIL)!; - MethodDescInfoHelpers.GetSignatureInfo(signature, out SignatureHeader header, out uint numArgs); + MethodSignatureHelpers.GetSignatureInfo(signature, out SignatureHeader header, out uint numArgs); if (index >= numArgs) throw Marshal.GetExceptionForHR(HResults.E_INVALIDARG)!; @@ -217,12 +217,15 @@ int IXCLRDataFrame.GetArgumentByIndex( // signature, so for instance methods adjust the index down. int mdIndex = (int)(header.IsInstance ? index : index + 1); uint token = rts.GetMethodToken(mdh); + // Array Get/Set/Address methods aren't no-metadata methods but still + // carry a nil MethodDef token. Resolving a name for one would throw, so + // name resolution is skipped in that case to match native behavior. MetadataReader? mdReader = (EcmaMetadataUtils.GetRowId(token) != 0) ? _target.Contracts.EcmaMetadata.GetMetadata(moduleHandle) : null; if (mdReader is not null) { - MethodDefinition methodDef = mdReader.GetMethodDefinition(MetadataTokens.MethodDefinitionHandle((int)token)); + MethodDefinition methodDef = mdReader.GetMethodDefinition(MetadataTokens.MethodDefinitionHandle((int)EcmaMetadataUtils.GetRowId(token))); string? paramName = GetParameterName(mdReader, methodDef, mdIndex); OutputBufferHelpers.CopyStringToBuffer(name, bufLen, nameLen, paramName ?? string.Empty); } @@ -318,7 +321,7 @@ int IXCLRDataFrame.GetLocalVariableByIndex( if (!rts.TryGetMethodSignature(mdh, out ReadOnlySpan signature)) throw Marshal.GetExceptionForHR(HResults.E_FAIL)!; - MethodDescInfoHelpers.GetSignatureInfo(signature, out SignatureHeader argHeader, out uint numArgs); + MethodSignatureHelpers.GetSignatureInfo(signature, out SignatureHeader argHeader, out uint numArgs); uint numLocals = GetLocalVariableCount(mdh, moduleHandle); @@ -569,7 +572,9 @@ private uint GetLocalVariableCount(MethodDescHandle mdh, Contracts.ModuleHandle { try { - MethodDescInfoHelpers.GetMethodInfo(_target, mdh, out MetadataReader mdReader, out MethodDefinition methodDef, out _, out _); + MetadataReader mdReader = _target.Contracts.EcmaMetadata.GetMetadata(moduleHandle) ?? throw new NotImplementedException(); + uint token = _target.Contracts.RuntimeTypeSystem.GetMethodToken(mdh); + MethodDefinition methodDef = mdReader.GetMethodDefinition(MetadataTokens.MethodDefinitionHandle((int)EcmaMetadataUtils.GetRowId(token))); FlagSignatureTypeProvider provider = new(_target, moduleHandle); SignatureDecoder<(uint Flags, int Size), MethodDescHandle> decoder = new(provider, mdReader, mdh); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.NativeCodeInfo.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.NativeCodeInfo.cs index 191ce399973c2f..ef4d884078bfe0 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.NativeCodeInfo.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.NativeCodeInfo.cs @@ -22,7 +22,7 @@ private uint GetArgCount(ulong vmMethodDesc) if (!rts.TryGetMethodSignature(mdh, out ReadOnlySpan signature)) throw Marshal.GetExceptionForHR(HResults.E_FAIL)!; - MethodDescInfoHelpers.GetSignatureInfo(signature, out _, out uint numArgs); + MethodSignatureHelpers.GetSignatureInfo(signature, out _, out uint numArgs); return numArgs; } @@ -96,19 +96,15 @@ internal static VarLoc ConvertToVarLoc(DebugVarInfo varInfo) // Converts cDAC Contracts.SourceTypes to native ICorDebugInfo::SourceTypes values. // The cDAC uses compact bit positions while the native enum uses different bit values. - internal static uint ConvertSourceTypesToNative(Contracts.SourceTypes source) + internal static DbiSourceTypes ConvertSourceTypesToNative(Contracts.SourceTypes source) { - const uint NativeStackEmpty = 0x02; - const uint NativeCallInstruction = 0x10; - const uint NativeAsync = 0x20; - - uint result = 0; + DbiSourceTypes result = DbiSourceTypes.SourceTypeInvalid; if ((source & Contracts.SourceTypes.StackEmpty) != 0) - result |= NativeStackEmpty; + result |= DbiSourceTypes.StackEmpty; if ((source & Contracts.SourceTypes.CallInstruction) != 0) - result |= NativeCallInstruction; + result |= DbiSourceTypes.CallInstruction; if ((source & Contracts.SourceTypes.Async) != 0) - result |= NativeAsync; + result |= DbiSourceTypes.Async; return result; } @@ -126,12 +122,19 @@ private void ValidateNativeCodeInfoAgainstLegacy( uint dacFixedArgCount = 0; var dacData = new DebugNativeCodeData(); GCHandle dacHandle = GCHandle.Alloc(dacData); - int hrLocal = _legacy!.GetNativeCodeSequencePointsAndVarInfo( - vmMethodDesc, startAddress, fCodeAvailable, &dacFixedArgCount, - (delegate* unmanaged)&CollectNativeVarInfoCallback, - (delegate* unmanaged)&CollectOffsetMappingCallback, - GCHandle.ToIntPtr(dacHandle)); - dacHandle.Free(); + int hrLocal; + try + { + hrLocal = _legacy!.GetNativeCodeSequencePointsAndVarInfo( + vmMethodDesc, startAddress, fCodeAvailable, &dacFixedArgCount, + (delegate* unmanaged)&CollectNativeVarInfoCallback, + (delegate* unmanaged)&CollectOffsetMappingCallback, + GCHandle.ToIntPtr(dacHandle)); + } + finally + { + dacHandle.Free(); + } Debug.ValidateHResult(hr, hrLocal); if (hr == HResults.S_OK) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs index 335ded04dff369..7b6b5f2d97042d 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs @@ -61,27 +61,19 @@ public enum VarLocType VLT_INVALID, } -// The native VarLoc struct contains a union with a void* member (vlMemory), -// which causes pointer-size alignment. On 64-bit, the union starts at offset 8 -// (after vlType + padding). We use nint for the first field to absorb the -// architecture-dependent padding, matching the layout in CorInfoTypes.VarInfo.cs. -[StructLayout(LayoutKind.Sequential)] +// Mirrors the native ICorDebugInfo::VarLoc tagged union: a VarLocType selector +// followed by a union of location variants. The union begins at offset 4 and the +// struct is 16 bytes: the union contains only 4-byte members, so it is 4-byte aligned. +[StructLayout(LayoutKind.Explicit, Size = 16)] public struct VarLoc { - // vlType (lower 32 bits) + pointer-size padding - private nint _vlTypeAndPadding; + [FieldOffset(0)] public VarLocType vlType; // Union data: three positional slots covering all union variants. // Different VarLocType values interpret these as different named fields. - private uint _field1; - private int _field2; - private int _field3; - - public VarLocType vlType - { - get => (VarLocType)(int)(_vlTypeAndPadding & 0xFFFFFFFF); - set => _vlTypeAndPadding = (nint)(int)value; - } + [FieldOffset(4)] private uint _field1; + [FieldOffset(8)] private int _field2; + [FieldOffset(12)] private int _field3; // vlReg / vlReg_BYREF public uint vlrReg { get => _field1; set => _field1 = value; } @@ -121,12 +113,24 @@ public struct NativeVarInfo public VarLoc loc; } +[Flags] +public enum DbiSourceTypes : uint +{ + SourceTypeInvalid = 0x00, + SequencePoint = 0x01, + StackEmpty = 0x02, + CallSite = 0x04, + NativeEndOffsetUnknown = 0x08, + CallInstruction = 0x10, + Async = 0x20, +} + [StructLayout(LayoutKind.Sequential)] public struct DbiOffsetMapping { public uint nativeOffset; public uint ilOffset; - public uint source; // ICorDebugInfo::SourceTypes + public DbiSourceTypes source; } #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/MethodDescInfoHelpers.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/MethodDescInfoHelpers.cs deleted file mode 100644 index 15214481a7dbba..00000000000000 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/MethodDescInfoHelpers.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Reflection.Metadata; -using System.Reflection.Metadata.Ecma335; -using Microsoft.Diagnostics.DataContractReader.Contracts; - -namespace Microsoft.Diagnostics.DataContractReader.Legacy; - -/// -/// Shared helpers for resolving MethodDesc metadata and signature information. -/// -internal static class MethodDescInfoHelpers -{ - /// - /// Resolves a MethodDescHandle into its module-level metadata objects. - /// Throws on failure (no metadata, etc.). - /// - public static void GetMethodInfo( - Target target, - MethodDescHandle mdh, - out MetadataReader mdReader, - out MethodDefinition methodDef, - out Contracts.ModuleHandle moduleHandle, - out uint token) - { - IRuntimeTypeSystem rts = target.Contracts.RuntimeTypeSystem; - TargetPointer mtAddr = rts.GetMethodTable(mdh); - TypeHandle typeHandle = rts.GetTypeHandle(mtAddr); - TargetPointer modulePtr = rts.GetModule(typeHandle); - ILoader loader = target.Contracts.Loader; - moduleHandle = loader.GetModuleHandleFromModulePtr(modulePtr); - token = rts.GetMethodToken(mdh); - - IEcmaMetadata ecmaMetadata = target.Contracts.EcmaMetadata; - MetadataReader? reader = ecmaMetadata.GetMetadata(moduleHandle); - if (reader is null) - throw new NotImplementedException(); - mdReader = reader; - - MethodDefinitionHandle methodDefHandle = MetadataTokens.MethodDefinitionHandle((int)token); - methodDef = mdReader.GetMethodDefinition(methodDefHandle); - } - - /// - /// Parses raw signature bytes to determine the signature header and argument count. - /// - public static unsafe void GetSignatureInfo(ReadOnlySpan signature, out SignatureHeader header, out uint numArgs) - { - fixed (byte* pSig = signature) - { - BlobReader blobReader = new BlobReader(pSig, signature.Length); - header = blobReader.ReadSignatureHeader(); - if (header.Kind != SignatureKind.Method) - throw new BadImageFormatException(); - if (header.IsGeneric) - blobReader.ReadCompressedInteger(); // skip generic arity - uint paramCount = (uint)blobReader.ReadCompressedInteger(); - numArgs = paramCount + (header.IsInstance ? 1u : 0u); - } - } -} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/MethodSignatureHelpers.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/MethodSignatureHelpers.cs new file mode 100644 index 00000000000000..6eaf8164ef6efc --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/MethodSignatureHelpers.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection.Metadata; + +namespace Microsoft.Diagnostics.DataContractReader.Legacy; + +/// +/// Shared helpers for resolving MethodDesc signature information. +/// +internal static class MethodSignatureHelpers +{ + /// + /// Parses raw signature bytes to determine the signature header and argument count. + /// + public static unsafe void GetSignatureInfo(ReadOnlySpan signature, out SignatureHeader header, out uint numArgs) + { + fixed (byte* pSig = signature) + { + BlobReader blobReader = new BlobReader(pSig, signature.Length); + header = blobReader.ReadSignatureHeader(); + if (header.Kind != SignatureKind.Method) + throw new BadImageFormatException(); + if (header.IsGeneric) + blobReader.ReadCompressedInteger(); // skip generic arity + uint paramCount = (uint)blobReader.ReadCompressedInteger(); + numArgs = paramCount + (header.IsInstance ? 1u : 0u); + } + } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/TypeNameBuilder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/TypeNameBuilder.cs index bfb4cf9da4d820..525a029ca5ffdd 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/TypeNameBuilder.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/TypeNameBuilder.cs @@ -68,7 +68,6 @@ public static void AppendMethodImpl(Target target, StringBuilder stringBuilder, ILoader loader = target.Contracts.Loader; string methodName; TypeHandle th = default; - Contracts.ModuleHandle module = default; bool isNoMetadataMethod = runtimeTypeSystem.IsNoMetadataMethod(method, out methodName); if (isNoMetadataMethod) @@ -120,9 +119,9 @@ public static void AppendMethodImpl(Target target, StringBuilder stringBuilder, } else { - module = loader.GetModuleHandleFromModulePtr(runtimeTypeSystem.GetModule(th)); + Contracts.ModuleHandle module = loader.GetModuleHandleFromModulePtr(runtimeTypeSystem.GetModule(th)); MetadataReader reader = target.Contracts.EcmaMetadata.GetMetadata(module)!; - MethodDefinition methodDef = reader.GetMethodDefinition(MetadataTokens.MethodDefinitionHandle((int)runtimeTypeSystem.GetMethodToken(method))); + MethodDefinition methodDef = reader.GetMethodDefinition(MetadataTokens.MethodDefinitionHandle((int)EcmaMetadataUtils.GetRowId(runtimeTypeSystem.GetMethodToken(method)))); stringBuilder.Append(reader.GetString(methodDef.Name)); } @@ -138,11 +137,9 @@ public static void AppendMethodImpl(Target target, StringBuilder stringBuilder, MetadataReader? reader = default; if (runtimeTypeSystem.TryGetMethodSignature(method, out signature)) { - // Stored signature methods don't have metadata to resolve type references - if (!runtimeTypeSystem.IsStoredSigMethodDesc(method, out _)) - { - reader = target.Contracts.EcmaMetadata.GetMetadata(module); - } + Contracts.ModuleHandle methodModule = loader.GetModuleHandleFromModulePtr( + runtimeTypeSystem.GetModule(runtimeTypeSystem.GetTypeHandle(runtimeTypeSystem.GetMethodTable(method)))); + reader = target.Contracts.EcmaMetadata.GetMetadata(methodModule); } ReadOnlySpan typeInstantiationSigFormat = default; diff --git a/src/native/managed/cdac/tests/UnitTests/DacDbiImplTests.cs b/src/native/managed/cdac/tests/UnitTests/DacDbiImplTests.cs index a5f8a26ace59d9..9c046b1c07ec78 100644 --- a/src/native/managed/cdac/tests/UnitTests/DacDbiImplTests.cs +++ b/src/native/managed/cdac/tests/UnitTests/DacDbiImplTests.cs @@ -1019,8 +1019,8 @@ public void ConvertToVarLoc_DoubleStack_SetsBaseRegAndOffset() [InlineData(SourceTypes.StackEmpty | SourceTypes.CallInstruction | SourceTypes.Async, 0x32u)] public void ConvertSourceTypesToNative_MapsCorrectly(SourceTypes source, uint expected) { - uint result = DacDbiImpl.ConvertSourceTypesToNative(source); - Assert.Equal(expected, result); + DbiSourceTypes result = DacDbiImpl.ConvertSourceTypesToNative(source); + Assert.Equal(expected, (uint)result); } [Fact] From 3d695b7578067a1755475dabe28a24bb0fc30752 Mon Sep 17 00:00:00 2001 From: Barbara Rosiak Date: Mon, 22 Jun 2026 17:11:52 -0700 Subject: [PATCH 05/12] Address pr review comments --- src/coreclr/debug/daccess/dacdbiimpl.cpp | 2 +- .../ClrDataFrame.cs | 2 +- .../TypeNameBuilder.cs | 26 +++++++++---------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index e2f5432c320735..a4894971bddc52 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -786,7 +786,7 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::SetCompilerFlags(VMPTR_Assembly v // Allocator to pass to the debug-info-stores... BYTE* InfoStoreNew(void * pData, size_t cBytes) { - return new BYTE[cBytes]; + return new (nothrow) BYTE[cBytes]; } // Get the native/IL sequence points and native var info for a function via callbacks. diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs index 9e8cc8dccbba50..bc0b81957fa06d 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs @@ -419,7 +419,7 @@ int IXCLRDataFrame2.GetExactGenericArgsToken(DacComNullableByRef /// /// Resolves the frame's MethodDesc and its containing module. - /// Throws on failure (no MethodDesc, no metadata, etc.). + /// Throws on failure (no MethodDesc). /// private MethodDescHandle GetFrameMethodDesc(out Contracts.ModuleHandle moduleHandle) { diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/TypeNameBuilder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/TypeNameBuilder.cs index 525a029ca5ffdd..e481d0cc471fe3 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/TypeNameBuilder.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/TypeNameBuilder.cs @@ -119,10 +119,14 @@ public static void AppendMethodImpl(Target target, StringBuilder stringBuilder, } else { - Contracts.ModuleHandle module = loader.GetModuleHandleFromModulePtr(runtimeTypeSystem.GetModule(th)); - MetadataReader reader = target.Contracts.EcmaMetadata.GetMetadata(module)!; - MethodDefinition methodDef = reader.GetMethodDefinition(MetadataTokens.MethodDefinitionHandle((int)EcmaMetadataUtils.GetRowId(runtimeTypeSystem.GetMethodToken(method)))); - stringBuilder.Append(reader.GetString(methodDef.Name)); + uint rowId = EcmaMetadataUtils.GetRowId(runtimeTypeSystem.GetMethodToken(method)); + if (rowId != 0) + { + Contracts.ModuleHandle module = loader.GetModuleHandleFromModulePtr(runtimeTypeSystem.GetModule(th)); + MetadataReader reader = target.Contracts.EcmaMetadata.GetMetadata(module)!; + MethodDefinition methodDef = reader.GetMethodDefinition(MetadataTokens.MethodDefinitionHandle((int)rowId)); + stringBuilder.Append(reader.GetString(methodDef.Name)); + } } ReadOnlySpan genericMethodInstantiation = runtimeTypeSystem.GetGenericMethodInstantiation(method); @@ -131,16 +135,12 @@ public static void AppendMethodImpl(Target target, StringBuilder stringBuilder, AppendInst(target, stringBuilder, genericMethodInstantiation, format); } - if (format.HasFlag(TypeNameFormat.FormatSignature)) + if (format.HasFlag(TypeNameFormat.FormatSignature) && + runtimeTypeSystem.TryGetMethodSignature(method, out ReadOnlySpan signature)) { - ReadOnlySpan signature; - MetadataReader? reader = default; - if (runtimeTypeSystem.TryGetMethodSignature(method, out signature)) - { - Contracts.ModuleHandle methodModule = loader.GetModuleHandleFromModulePtr( - runtimeTypeSystem.GetModule(runtimeTypeSystem.GetTypeHandle(runtimeTypeSystem.GetMethodTable(method)))); - reader = target.Contracts.EcmaMetadata.GetMetadata(methodModule); - } + Contracts.ModuleHandle methodModule = loader.GetModuleHandleFromModulePtr( + runtimeTypeSystem.GetModule(runtimeTypeSystem.GetTypeHandle(runtimeTypeSystem.GetMethodTable(method)))); + MetadataReader? reader = target.Contracts.EcmaMetadata.GetMetadata(methodModule); ReadOnlySpan typeInstantiationSigFormat = default; if (!th.IsNull) From 47dc7f268ff636b9b20599a653ad53bf1394f2ff Mon Sep 17 00:00:00 2001 From: Barbara Rosiak <76071368+barosiak@users.noreply.github.com> Date: Mon, 22 Jun 2026 17:44:18 -0700 Subject: [PATCH 06/12] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/coreclr/debug/daccess/dacdbiimpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index efeec81a88ae0f..442c3e76c350b0 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -784,7 +784,7 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::SetCompilerFlags(VMPTR_Assembly v //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Allocator to pass to the debug-info-stores... -BYTE* InfoStoreNew(void * pData, size_t cBytes) +static BYTE* InfoStoreNew(void * pData, size_t cBytes) { return new (nothrow) BYTE[cBytes]; } From 90d969787ebb73cbf44dbe271e43c53fb4a65116 Mon Sep 17 00:00:00 2001 From: Barbara Rosiak Date: Tue, 23 Jun 2026 11:10:11 -0700 Subject: [PATCH 07/12] Address pr review comments --- .../Dbi/DacDbiImpl.cs | 29 +++--- .../cdac/tests/UnitTests/MethodDescTests.cs | 94 +++++++++++++++++++ .../MockDescriptors.MethodDescriptors.cs | 9 ++ 3 files changed, 118 insertions(+), 14 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs index 18e9939a14b739..32fdea23db5514 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs @@ -1234,10 +1234,9 @@ public int GetNativeCodeSequencePointsAndVarInfo( delegate* unmanaged fpSeqPointCallback, nint pUserData) { -#if DEBUG + // Fully materialize both arrays before invoking any callback to avoid delivering partial results on failure. List cdacVarInfos = new(); List cdacSeqPoints = new(); -#endif int hr = HResults.S_OK; if (pFixedArgCount != null) *pFixedArgCount = 0; @@ -1252,33 +1251,35 @@ public int GetNativeCodeSequencePointsAndVarInfo( if (pFixedArgCount != null) *pFixedArgCount = GetArgCount(vmMethodDesc); - // Get native variable info and invoke callback for each entry if (fpVarInfoCallback != null) { IEnumerable varInfos = debugInfo.GetMethodVarInfo(codePointer, out _); foreach (DebugVarInfo varInfo in varInfos) { - NativeVarInfo nvi = ConvertToNativeVarInfo(varInfo); - fpVarInfoCallback(&nvi, (void*)pUserData); -#if DEBUG - cdacVarInfos.Add(nvi); -#endif + cdacVarInfos.Add(ConvertToNativeVarInfo(varInfo)); } } - // Get sequence points and invoke callback for each entry if (fpSeqPointCallback != null) { IEnumerable sequencePoints = debugInfo.GetMethodNativeMap(codePointer, preferUninstrumented: true, out _); foreach (Contracts.OffsetMapping mapping in sequencePoints) { - DbiOffsetMapping nativeMapping = ConvertToDbiOffsetMapping(mapping); - fpSeqPointCallback(&nativeMapping, (void*)pUserData); -#if DEBUG - cdacSeqPoints.Add(nativeMapping); -#endif + cdacSeqPoints.Add(ConvertToDbiOffsetMapping(mapping)); } } + + foreach (NativeVarInfo nvi in cdacVarInfos) + { + NativeVarInfo entry = nvi; + fpVarInfoCallback(&entry, (void*)pUserData); + } + + foreach (DbiOffsetMapping mapping in cdacSeqPoints) + { + DbiOffsetMapping entry = mapping; + fpSeqPointCallback(&entry, (void*)pUserData); + } } catch (System.Exception ex) { diff --git a/src/native/managed/cdac/tests/UnitTests/MethodDescTests.cs b/src/native/managed/cdac/tests/UnitTests/MethodDescTests.cs index 529c1dcaa986d8..7fa25eef8c3a1c 100644 --- a/src/native/managed/cdac/tests/UnitTests/MethodDescTests.cs +++ b/src/native/managed/cdac/tests/UnitTests/MethodDescTests.cs @@ -255,6 +255,100 @@ public void IsDynamicMethod(MockTarget.Architecture arch) } } + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void TryGetMethodSignature_StoredSig_ReturnsStoredSignature(MockTarget.Architecture arch) + { + byte[] expectedSig = [0x20, 0x01, 0x01, 0x0e]; + TargetPointer dynamicMethod = TargetPointer.Null; + + IRuntimeTypeSystem rts = CreateRuntimeTypeSystemContract(arch, methodDescBuilder => + { + TargetPointer methodTable = AddMethodTable(methodDescBuilder.RTSBuilder); + + uint methodDescSize = (uint)methodDescBuilder.DynamicMethodDescLayout.Size; + byte chunkSize = (byte)(methodDescSize / methodDescBuilder.MethodDescAlignment); + MockMethodDescChunk chunk = methodDescBuilder.AddMethodDescChunk("storedSig", chunkSize); + chunk.MethodTable = methodTable.Value; + chunk.Size = chunkSize; + chunk.Count = 1; + + MockDynamicMethodDesc md = chunk.GetMethodDescAtChunkIndex(0, methodDescBuilder.DynamicMethodDescLayout); + md.Flags = (ushort)MethodClassification.Dynamic; + md.Sig = methodDescBuilder.AddSignatureBuffer(expectedSig).Value; + md.CSig = (uint)expectedSig.Length; + dynamicMethod = new TargetPointer(md.Address); + }); + + MethodDescHandle handle = rts.GetMethodDescHandle(dynamicMethod); + Assert.True(rts.TryGetMethodSignature(handle, out ReadOnlySpan signature)); + Assert.Equal(expectedSig, signature.ToArray()); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void TryGetMethodSignature_AsyncVariant_ReturnsAsyncSignature(MockTarget.Architecture arch) + { + byte[] expectedSig = [0x00, 0x02, 0x08, 0x08, 0x0e]; + TargetPointer asyncVariantMethod = TargetPointer.Null; + + IRuntimeTypeSystem rts = CreateRuntimeTypeSystemContract(arch, methodDescBuilder => + { + TargetTestHelpers helpers = methodDescBuilder.TargetTestHelpers; + TargetPointer methodTable = AddMethodTable(methodDescBuilder.RTSBuilder); + + uint mdBaseSize = (uint)methodDescBuilder.MethodDescLayout.Size; + uint totalSize = mdBaseSize + methodDescBuilder.AsyncMethodDataSize; + byte chunkSize = (byte)(totalSize / methodDescBuilder.MethodDescAlignment); + MockMethodDescChunk chunk = methodDescBuilder.AddMethodDescChunk("asyncVariant", chunkSize); + chunk.MethodTable = methodTable.Value; + chunk.Size = chunkSize; + chunk.Count = 1; + + MockMethodDesc md = chunk.GetMethodDescAtChunkIndex(0, methodDescBuilder.MethodDescLayout); + md.Flags = (ushort)((ushort)MethodClassification.IL | (ushort)MethodDescFlags_1.MethodDescFlags.HasAsyncMethodData); + asyncVariantMethod = new TargetPointer(md.Address); + + int pointerSize = helpers.PointerSize; + int asyncDataOffset = (int)(md.Address - chunk.Address) + (int)mdBaseSize; + TargetPointer sigBuffer = methodDescBuilder.AddSignatureBuffer(expectedSig); + helpers.Write(chunk.Memory.Span.Slice(asyncDataOffset, sizeof(uint)), (uint)RuntimeTypeSystem_1.AsyncMethodFlags.IsAsyncVariant); + helpers.WritePointer(chunk.Memory.Span.Slice(asyncDataOffset + pointerSize, pointerSize), sigBuffer.Value); + helpers.Write(chunk.Memory.Span.Slice(asyncDataOffset + pointerSize * 2, sizeof(uint)), (uint)expectedSig.Length); + }); + + MethodDescHandle handle = rts.GetMethodDescHandle(asyncVariantMethod); + Assert.True(rts.TryGetMethodSignature(handle, out ReadOnlySpan signature)); + Assert.Equal(expectedSig, signature.ToArray()); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void TryGetMethodSignature_NilToken_ReturnsFalse(MockTarget.Architecture arch) + { + TargetPointer ilMethod = TargetPointer.Null; + + IRuntimeTypeSystem rts = CreateRuntimeTypeSystemContract(arch, methodDescBuilder => + { + TargetPointer methodTable = AddMethodTable(methodDescBuilder.RTSBuilder); + + byte methodDescSize = (byte)(methodDescBuilder.MethodDescLayout.Size / methodDescBuilder.MethodDescAlignment); + MockMethodDescChunk chunk = methodDescBuilder.AddMethodDescChunk("ilMethod", methodDescSize); + chunk.MethodTable = methodTable.Value; + chunk.Size = methodDescSize; + chunk.Count = 1; + + // Leave FlagsAndTokenRange / Flags3AndTokenRemainder at 0 so the MethodDef token has rowId 0. + MockMethodDesc md = chunk.GetMethodDescAtChunkIndex(0, methodDescBuilder.MethodDescLayout); + md.Flags = (ushort)MethodClassification.IL; + ilMethod = new TargetPointer(md.Address); + }); + + MethodDescHandle handle = rts.GetMethodDescHandle(ilMethod); + Assert.False(rts.TryGetMethodSignature(handle, out ReadOnlySpan signature)); + Assert.True(signature.IsEmpty); + } + [Theory] [ClassData(typeof(MockTarget.StdArch))] public void IsGenericMethodDefinition(MockTarget.Architecture arch) diff --git a/src/native/managed/cdac/tests/UnitTests/MockDescriptors/MockDescriptors.MethodDescriptors.cs b/src/native/managed/cdac/tests/UnitTests/MockDescriptors/MockDescriptors.MethodDescriptors.cs index b9a4a9607ca0a9..969201fe756068 100644 --- a/src/native/managed/cdac/tests/UnitTests/MockDescriptors/MockDescriptors.MethodDescriptors.cs +++ b/src/native/managed/cdac/tests/UnitTests/MockDescriptors/MockDescriptors.MethodDescriptors.cs @@ -302,6 +302,15 @@ internal MockMethodDescChunk AddMethodDescChunk(string name, byte size) return chunk; } + internal TargetPointer AddSignatureBuffer(byte[] signature) + { + ArgumentNullException.ThrowIfNull(signature); + + MockMemorySpace.HeapFragment fragment = _allocator.Allocate((ulong)signature.Length, "MethodSignature"); + signature.CopyTo(fragment.Data.AsSpan()); + return new TargetPointer(fragment.Address); + } + internal MockPerInstInfo AddPerInstInfo(ulong[] typeArgs) { ArgumentNullException.ThrowIfNull(typeArgs); From b43b990cad5d8aa95b0881749c9a31c12d09b8d4 Mon Sep 17 00:00:00 2001 From: Barbara Rosiak Date: Thu, 2 Jul 2026 12:12:03 -0700 Subject: [PATCH 08/12] Address pr review comments, add an IData type for Signature, change VarLoc layout to Sequential --- .../design/datacontracts/RuntimeTypeSystem.md | 11 +++--- docs/design/datacontracts/Signature.md | 9 ++--- src/coreclr/inc/jiteeversionguid.h | 10 +++--- src/coreclr/vm/ceeload.h | 3 +- .../vm/datadescriptor/datadescriptor.inc | 12 ++++--- src/coreclr/vm/method.hpp | 3 +- src/coreclr/vm/siginfo.hpp | 8 +++++ .../Contracts/RuntimeTypeSystem_1.cs | 11 +++--- .../Contracts/Signature/Signature_1.cs | 4 +-- .../Data/AsyncMethodData.cs | 3 +- .../Data/Signature.cs | 11 ++++++ .../Data/VASigCookie.cs | 3 +- .../DataType.cs | 1 + .../Dbi/IDacDbiInterface.cs | 17 ++++----- .../cdac/tests/UnitTests/MethodDescTests.cs | 12 +++++-- .../cdac/tests/UnitTests/SignatureTests.cs | 36 +++++++++++++++---- 16 files changed, 100 insertions(+), 54 deletions(-) create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Signature.cs diff --git a/docs/design/datacontracts/RuntimeTypeSystem.md b/docs/design/datacontracts/RuntimeTypeSystem.md index 5acfe0d9f489f4..91aad2d353595d 100644 --- a/docs/design/datacontracts/RuntimeTypeSystem.md +++ b/docs/design/datacontracts/RuntimeTypeSystem.md @@ -257,9 +257,7 @@ partial interface IRuntimeTypeSystem : IContract // Corresponds to native MethodDesc::IsAsyncMethod(). public virtual bool IsAsyncMethod(MethodDescHandle methodDesc); - // Gets the raw signature bytes for a MethodDesc by checking the stored signature, - // then the async variant signature, then ECMA metadata. Returns false if no - // signature could be resolved. + // Return true and the raw signature bytes for a MethodDesc, or false if no signature could be resolved. public virtual bool TryGetMethodSignature(MethodDescHandle methodDesc, out ReadOnlySpan signature); // Return true if a MethodDesc is in a collectible module @@ -1351,8 +1349,9 @@ We depend on the following data descriptors: | `StoredSigMethodDesc` | `ExtendedFlags` | Flags field for the `StoredSigMethodDesc` | | `DynamicMethodDesc` | `MethodName` | Pointer to Null-terminated UTF8 string describing the Method desc | | `AsyncMethodData` | `Flags` | Async method flags | -| `AsyncMethodData` | `Sig` | Pointer to the async variant's signature blob | -| `AsyncMethodData` | `cSig` | Count of bytes in the async variant's signature blob | +| `AsyncMethodData` | `Signature` | The async variant's signature (see `Signature`) | +| `Signature` | `SignaturePointer` | Pointer to the raw signature blob | +| `Signature` | `SignatureLength` | Count of bytes in the raw signature blob | | `GCCoverageInfo` | `SavedCode` | Pointer to the GCCover saved code copy, if supported | The following data descriptor types are used only for their sizes when computing the total size of a `MethodDesc` instance. @@ -1634,7 +1633,7 @@ And the various apis are implemented with the following algorithms AsyncMethodData asyncData = // Read AsyncMethodData for methodDesc if (((AsyncMethodFlags)asyncData.Flags).HasFlag(AsyncMethodFlags.IsAsyncVariant)) { - signature = // Read asyncData.cSig bytes from asyncData.Sig + signature = // Read asyncData.Signature.SignatureLength bytes from asyncData.Signature.SignaturePointer return true; } } diff --git a/docs/design/datacontracts/Signature.md b/docs/design/datacontracts/Signature.md index 6d91152e913c1d..1af22ad18e4dc1 100644 --- a/docs/design/datacontracts/Signature.md +++ b/docs/design/datacontracts/Signature.md @@ -31,8 +31,9 @@ Data descriptors used: | Data Descriptor Name | Field | Meaning | | --- | --- | --- | | `VASigCookie` | `SizeOfArgs` | Total size in bytes of the pushed argument list. Used on x86 to locate the args base. | -| `VASigCookie` | `SignaturePointer` | Target address of the raw vararg signature blob. | -| `VASigCookie` | `SignatureLength` | Length in bytes of the raw vararg signature blob. | +| `VASigCookie` | `Signature` | The raw vararg signature (see `Signature`). | +| `Signature` | `SignaturePointer` | Target address of the raw signature blob. | +| `Signature` | `SignatureLength` | Length in bytes of the raw signature blob. | Global variables used: | Global Name | Type | Purpose | @@ -104,7 +105,7 @@ void ISignature.GetVarArgSignature(TargetPointer vaSigCookieAddr, out TargetPoin TargetPointer vaSigCookie = _target.ReadPointer(vaSigCookieAddr); VASigCookie cookie = _target.ProcessedData.GetOrAdd(vaSigCookie); - signatureAddress = cookie.SignaturePointer; - signatureLength = cookie.SignatureLength; + signatureAddress = cookie.Signature.SignaturePointer; + signatureLength = cookie.Signature.SignatureLength; } ``` diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index e0177d76cd5826..2e2b7b91b04503 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -37,11 +37,11 @@ #include -constexpr GUID JITEEVersionIdentifier = { /* fcb1b400-696c-4425-a8a7-bb082430a217 */ - 0xfcb1b400, - 0x696c, - 0x4425, - {0xa8, 0xa7, 0xbb, 0x08, 0x24, 0x30, 0xa2, 0x17} +constexpr GUID JITEEVersionIdentifier = { /* a09d20fa-2c93-476b-86aa-7daaa3e52ddf */ + 0xa09d20fa, + 0x2c93, + 0x476b, + {0x86, 0xaa, 0x7d, 0xaa, 0xa3, 0xe5, 0x2d, 0xdf} }; #endif // JIT_EE_VERSIONING_GUID_H diff --git a/src/coreclr/vm/ceeload.h b/src/coreclr/vm/ceeload.h index 8a8349272025bc..f6d50544407342 100644 --- a/src/coreclr/vm/ceeload.h +++ b/src/coreclr/vm/ceeload.h @@ -354,8 +354,7 @@ struct VASigCookie template<> struct cdac_data { - static constexpr size_t SignaturePointer = offsetof(VASigCookie, signature) + offsetof(Signature, m_pSig); - static constexpr size_t SignatureLength = offsetof(VASigCookie, signature) + offsetof(Signature, m_cbSig); + static constexpr size_t Signature = offsetof(VASigCookie, signature); }; // diff --git a/src/coreclr/vm/datadescriptor/datadescriptor.inc b/src/coreclr/vm/datadescriptor/datadescriptor.inc index 6a176d1f13f600..dd9d54609d57e9 100644 --- a/src/coreclr/vm/datadescriptor/datadescriptor.inc +++ b/src/coreclr/vm/datadescriptor/datadescriptor.inc @@ -523,11 +523,16 @@ CDAC_TYPE_FIELD(ArrayListBlock, T_UINT32, Size, cdac_data::Size) CDAC_TYPE_FIELD(ArrayListBlock, T_POINTER, ArrayStart, cdac_data::ArrayStart) CDAC_TYPE_END(ArrayListBlock) +CDAC_TYPE_BEGIN(Signature) +CDAC_TYPE_SIZE(sizeof(Signature)) +CDAC_TYPE_FIELD(Signature, T_POINTER, SignaturePointer, cdac_data::SignaturePointer) +CDAC_TYPE_FIELD(Signature, T_UINT32, SignatureLength, cdac_data::SignatureLength) +CDAC_TYPE_END(Signature) + CDAC_TYPE_BEGIN(VASigCookie) CDAC_TYPE_INDETERMINATE(VASigCookie) CDAC_TYPE_FIELD(VASigCookie, T_UINT32, SizeOfArgs, offsetof(VASigCookie, sizeOfArgs)) -CDAC_TYPE_FIELD(VASigCookie, T_POINTER, SignaturePointer, cdac_data::SignaturePointer) -CDAC_TYPE_FIELD(VASigCookie, T_UINT32, SignatureLength, cdac_data::SignatureLength) +CDAC_TYPE_FIELD(VASigCookie, TYPE(Signature), Signature, cdac_data::Signature) CDAC_TYPE_END(VASigCookie) // RuntimeTypeSystem @@ -707,8 +712,7 @@ CDAC_TYPE_END(NativeCodeSlot) CDAC_TYPE_BEGIN(AsyncMethodData) CDAC_TYPE_SIZE(sizeof(AsyncMethodData)) CDAC_TYPE_FIELD(AsyncMethodData, T_UINT32, Flags, cdac_data::Flags) -CDAC_TYPE_FIELD(AsyncMethodData, T_POINTER, Sig, cdac_data::Sig) -CDAC_TYPE_FIELD(AsyncMethodData, T_UINT32, cSig, cdac_data::cSig) +CDAC_TYPE_FIELD(AsyncMethodData, TYPE(Signature), Signature, cdac_data::Signature) CDAC_TYPE_END(AsyncMethodData) CDAC_TYPE_BEGIN(InstantiatedMethodDesc) diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index dd112c18405acf..2212404eef48b8 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -153,8 +153,7 @@ template<> struct cdac_data { static constexpr size_t Flags = offsetof(AsyncMethodData, flags); - static constexpr size_t Sig = offsetof(AsyncMethodData, sig) + offsetof(Signature, m_pSig); - static constexpr size_t cSig = offsetof(AsyncMethodData, sig) + offsetof(Signature, m_cbSig); + static constexpr size_t Signature = offsetof(AsyncMethodData, sig); }; //============================================================= diff --git a/src/coreclr/vm/siginfo.hpp b/src/coreclr/vm/siginfo.hpp index a4d421350f1a12..94ad9143303ed9 100644 --- a/src/coreclr/vm/siginfo.hpp +++ b/src/coreclr/vm/siginfo.hpp @@ -356,11 +356,19 @@ class Signature private: friend struct ::cdac_data; friend struct ::cdac_data; + friend struct ::cdac_data; PCCOR_SIGNATURE m_pSig; DWORD m_cbSig; }; // class Signature +template<> +struct cdac_data +{ + static constexpr size_t SignaturePointer = offsetof(Signature, m_pSig); + static constexpr size_t SignatureLength = offsetof(Signature, m_cbSig); +}; + #ifdef _DEBUG #define MAX_CACHED_SIG_SIZE 3 // To excercize non-cached code path 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 e9eead0a3acf70..5c00d3edbf24c7 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 @@ -1696,21 +1696,20 @@ public bool IsStoredSigMethodDesc(MethodDescHandle methodDescHandle, out ReadOnl public bool TryGetMethodSignature(MethodDescHandle methodDescHandle, out ReadOnlySpan signature) { - MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; - - if (methodDesc.Classification is MethodClassification.Dynamic or MethodClassification.EEImpl or MethodClassification.Array) + if (IsStoredSigMethodDesc(methodDescHandle, out signature)) { - signature = AsStoredSigMethodDesc(methodDesc).Signature; return true; } + MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; + if (methodDesc.HasAsyncMethodData) { Data.AsyncMethodData asyncData = _target.ProcessedData.GetOrAdd(methodDesc.GetAddressOfAsyncMethodData()); if (((AsyncMethodFlags)asyncData.Flags).HasFlag(AsyncMethodFlags.IsAsyncVariant)) { - byte[] sig = new byte[asyncData.cSig]; - _target.ReadBuffer(asyncData.Sig, sig.AsSpan()); + byte[] sig = new byte[asyncData.Signature.SignatureLength]; + _target.ReadBuffer(asyncData.Signature.SignaturePointer, sig.AsSpan()); signature = sig; return true; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/Signature_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/Signature_1.cs index ee7cf0485ee0e8..e3c2531d8d4be6 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/Signature_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/Signature_1.cs @@ -73,8 +73,8 @@ void ISignature.GetVarArgSignature(TargetPointer vaSigCookieAddr, out TargetPoin { Data.VASigCookie cookie = GetCookie(vaSigCookieAddr); - signatureAddress = cookie.SignaturePointer; - signatureLength = cookie.SignatureLength; + signatureAddress = cookie.Signature.SignaturePointer; + signatureLength = cookie.Signature.SignatureLength; Debug.Assert(signatureAddress != TargetPointer.Null || signatureLength == 0, "VASigCookie has a non-zero signature length but a null signature pointer."); } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/AsyncMethodData.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/AsyncMethodData.cs index 6b54d41196f0ff..8818534b094456 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/AsyncMethodData.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/AsyncMethodData.cs @@ -7,6 +7,5 @@ namespace Microsoft.Diagnostics.DataContractReader.Data; internal sealed partial class AsyncMethodData : IData { [Field] public uint Flags { get; } - [Field] public TargetPointer Sig { get; } - [Field] public uint cSig { get; } + [Field] public Signature Signature { get; } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Signature.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Signature.cs new file mode 100644 index 00000000000000..44a58fd646dce8 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Signature.cs @@ -0,0 +1,11 @@ +// 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; + +[CdacType(nameof(DataType.Signature))] +internal sealed partial class Signature : IData +{ + [Field] public TargetPointer SignaturePointer { get; } + [Field] public uint SignatureLength { get; } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/VASigCookie.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/VASigCookie.cs index ed7fa43b07401f..137ad880517a6e 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/VASigCookie.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/VASigCookie.cs @@ -7,6 +7,5 @@ namespace Microsoft.Diagnostics.DataContractReader.Data; internal sealed partial class VASigCookie : IData { [Field] public uint SizeOfArgs { get; } - [Field] public TargetPointer SignaturePointer { get; } - [Field] public uint SignatureLength { get; } + [Field] public Signature Signature { get; } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/DataType.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/DataType.cs index e7d2e874b36bc4..aec7483233d07c 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/DataType.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/DataType.cs @@ -191,6 +191,7 @@ public enum DataType InternalComInterfaceDispatch, AuxiliarySymbolInfo, VASigCookie, + Signature, CodeRangeMapRangeList, /* GC Data Types */ diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs index dfe7951e593f98..44cccd0fccaa48 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs @@ -52,19 +52,16 @@ public enum VarLocType VLT_INVALID, } -// Mirrors the native ICorDebugInfo::VarLoc tagged union: a VarLocType selector -// followed by a union of location variants. The union begins at offset 4 and the -// struct is 16 bytes: the union contains only 4-byte members, so it is 4-byte aligned. -[StructLayout(LayoutKind.Explicit, Size = 16)] +// Mirrors the native ICorDebugInfo::VarLoc tagged union: a VarLocType selector plus a union +// payload, modelled as three 4-byte slots with typed accessors per VarLocType below. +[StructLayout(LayoutKind.Sequential)] public struct VarLoc { - [FieldOffset(0)] public VarLocType vlType; + public VarLocType vlType; - // Union data: three positional slots covering all union variants. - // Different VarLocType values interpret these as different named fields. - [FieldOffset(4)] private uint _field1; - [FieldOffset(8)] private int _field2; - [FieldOffset(12)] private int _field3; + private uint _field1; + private int _field2; + private int _field3; // vlReg / vlReg_BYREF public uint vlrReg { get => _field1; set => _field1 = value; } diff --git a/src/native/managed/cdac/tests/UnitTests/MethodDescTests.cs b/src/native/managed/cdac/tests/UnitTests/MethodDescTests.cs index 7fa25eef8c3a1c..4abb890b8a5b12 100644 --- a/src/native/managed/cdac/tests/UnitTests/MethodDescTests.cs +++ b/src/native/managed/cdac/tests/UnitTests/MethodDescTests.cs @@ -32,8 +32,16 @@ public class MethodDescTests Fields = new Dictionary { [nameof(Data.AsyncMethodData.Flags)] = new Target.FieldInfo { Offset = 0 }, - [nameof(Data.AsyncMethodData.Sig)] = new Target.FieldInfo { Offset = (int)methodDescBuilder.TargetTestHelpers.PointerSize }, - [nameof(Data.AsyncMethodData.cSig)] = new Target.FieldInfo { Offset = (int)methodDescBuilder.TargetTestHelpers.PointerSize * 2 }, + [nameof(Data.AsyncMethodData.Signature)] = new Target.FieldInfo { Offset = (int)methodDescBuilder.TargetTestHelpers.PointerSize, TypeName = nameof(DataType.Signature) }, + }, + }, + [DataType.Signature] = new Target.TypeInfo + { + Size = (uint)(methodDescBuilder.TargetTestHelpers.PointerSize * 2), + Fields = new Dictionary + { + [nameof(Data.Signature.SignaturePointer)] = new Target.FieldInfo { Offset = 0 }, + [nameof(Data.Signature.SignatureLength)] = new Target.FieldInfo { Offset = (int)methodDescBuilder.TargetTestHelpers.PointerSize }, }, }, [DataType.ArrayMethodDesc] = new Target.TypeInfo { Size = methodDescBuilder.ArrayMethodDescSize }, diff --git a/src/native/managed/cdac/tests/UnitTests/SignatureTests.cs b/src/native/managed/cdac/tests/UnitTests/SignatureTests.cs index 6f54a3e86a7030..a4df278d3d5871 100644 --- a/src/native/managed/cdac/tests/UnitTests/SignatureTests.cs +++ b/src/native/managed/cdac/tests/UnitTests/SignatureTests.cs @@ -11,16 +11,35 @@ namespace Microsoft.Diagnostics.DataContractReader.Tests; public class SignatureTests { - private static TargetTestHelpers.LayoutResult GetVASigCookieLayout(TargetTestHelpers helpers) + private static TargetTestHelpers.LayoutResult GetSignatureLayout(TargetTestHelpers helpers) { return helpers.LayoutFields( [ - new(nameof(Data.VASigCookie.SizeOfArgs), DataType.uint32), - new(nameof(Data.VASigCookie.SignaturePointer), DataType.pointer), - new(nameof(Data.VASigCookie.SignatureLength), DataType.uint32), + new(nameof(Data.Signature.SignaturePointer), DataType.pointer), + new(nameof(Data.Signature.SignatureLength), DataType.uint32), ]); } + // Built manually rather than via LayoutFields, which can't model the embedded Signature struct. + private static TargetTestHelpers.LayoutResult GetVASigCookieLayout(TargetTestHelpers helpers, TargetTestHelpers.LayoutResult signatureLayout) + { + int sizeOfArgsOffset = 0; + int signatureOffset = AlignUp(sizeof(uint), (int)signatureLayout.MaxAlign); + int stride = signatureOffset + (int)signatureLayout.Stride; + return new TargetTestHelpers.LayoutResult + { + Fields = new Dictionary + { + [nameof(Data.VASigCookie.SizeOfArgs)] = new Target.FieldInfo { Offset = sizeOfArgsOffset, TypeName = DataType.uint32.ToString() }, + [nameof(Data.VASigCookie.Signature)] = new Target.FieldInfo { Offset = signatureOffset, TypeName = nameof(DataType.Signature) }, + }, + Stride = (uint)stride, + MaxAlign = signatureLayout.MaxAlign, + }; + } + + private static int AlignUp(int offset, int align) => (offset + align - 1) & ~(align - 1); + /// /// Build a target with a single VASigCookie at a known address and a slot containing /// a pointer to it (i.e., the "VASigCookieAddr" passed to the contract APIs). @@ -40,17 +59,20 @@ private static TestPlaceholderTarget BuildTarget( MockMemorySpace.Builder memBuilder = builder.MemoryBuilder; MockMemorySpace.BumpAllocator allocator = memBuilder.CreateAllocator(0x1_0000, 0x2_0000); - TargetTestHelpers.LayoutResult layout = GetVASigCookieLayout(helpers); + TargetTestHelpers.LayoutResult signatureLayout = GetSignatureLayout(helpers); + TargetTestHelpers.LayoutResult layout = GetVASigCookieLayout(helpers, signatureLayout); builder.AddTypes(new Dictionary { [DataType.VASigCookie] = new Target.TypeInfo() { Fields = layout.Fields, Size = layout.Stride }, + [DataType.Signature] = new Target.TypeInfo() { Fields = signatureLayout.Fields, Size = signatureLayout.Stride }, }); // Allocate and populate the VASigCookie struct. + int signatureOffset = layout.Fields[nameof(Data.VASigCookie.Signature)].Offset; MockMemorySpace.HeapFragment cookieFrag = allocator.Allocate(layout.Stride, "VASigCookie"); helpers.Write(cookieFrag.Data.AsSpan(layout.Fields[nameof(Data.VASigCookie.SizeOfArgs)].Offset, sizeof(uint)), sizeOfArgs); - helpers.WritePointer(cookieFrag.Data.AsSpan(layout.Fields[nameof(Data.VASigCookie.SignaturePointer)].Offset, helpers.PointerSize), signaturePointer); - helpers.Write(cookieFrag.Data.AsSpan(layout.Fields[nameof(Data.VASigCookie.SignatureLength)].Offset, sizeof(uint)), signatureLength); + helpers.WritePointer(cookieFrag.Data.AsSpan(signatureOffset + signatureLayout.Fields[nameof(Data.Signature.SignaturePointer)].Offset, helpers.PointerSize), signaturePointer); + helpers.Write(cookieFrag.Data.AsSpan(signatureOffset + signatureLayout.Fields[nameof(Data.Signature.SignatureLength)].Offset, sizeof(uint)), signatureLength); vaSigCookiePtr = cookieFrag.Address; // Allocate the slot that holds the pointer to the VASigCookie. This is the address From 1c583f7f36c20a2e50aa2b34ef80746a97a1ba41 Mon Sep 17 00:00:00 2001 From: Barbara Rosiak Date: Thu, 2 Jul 2026 12:31:38 -0700 Subject: [PATCH 09/12] Fix cDAC DAC mismatch in DEBUG --- .../Dbi/DacDbiImpl.NativeCodeInfo.cs | 11 ++++++++--- .../Dbi/DacDbiImpl.cs | 4 +++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.NativeCodeInfo.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.NativeCodeInfo.cs index ef4d884078bfe0..cee9698df16334 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.NativeCodeInfo.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.NativeCodeInfo.cs @@ -117,7 +117,9 @@ private void ValidateNativeCodeInfoAgainstLegacy( uint* pFixedArgCount, List cdacVarInfos, List cdacSeqPoints, - int hr) + int hr, + bool varInfoRequested, + bool seqPointsRequested) { uint dacFixedArgCount = 0; var dacData = new DebugNativeCodeData(); @@ -145,8 +147,11 @@ private void ValidateNativeCodeInfoAgainstLegacy( $"fixedArgCount mismatch - cDAC: {*pFixedArgCount}, DAC: {dacFixedArgCount}"); } - AssertSeqPointsEqual(cdacSeqPoints, dacData.SeqPoints); - AssertVarInfosEqual(cdacVarInfos, dacData.VarInfos); + // Only compare lists whose callback was supplied. + if (seqPointsRequested) + AssertSeqPointsEqual(cdacSeqPoints, dacData.SeqPoints); + if (varInfoRequested) + AssertVarInfosEqual(cdacVarInfos, dacData.VarInfos); } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs index a00f4e8876d7a1..00d9a6c7c09916 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs @@ -1361,7 +1361,9 @@ public int GetNativeCodeSequencePointsAndVarInfo( { ValidateNativeCodeInfoAgainstLegacy( vmMethodDesc, startAddress, fCodeAvailable, - pFixedArgCount, cdacVarInfos, cdacSeqPoints, hr); + pFixedArgCount, cdacVarInfos, cdacSeqPoints, hr, + varInfoRequested: fpVarInfoCallback != null, + seqPointsRequested: fpSeqPointCallback != null); } #endif return hr; From 802f96008d8f54945dcffca6f89a426ec9310ef2 Mon Sep 17 00:00:00 2001 From: rcj1 Date: Thu, 9 Jul 2026 10:35:41 -0700 Subject: [PATCH 10/12] fix bug --- docs/design/datacontracts/DebugInfo.md | 1 + .../Contracts/IDebugInfo.cs | 1 + .../Contracts/DebugInfo/DebugInfoHelpers.cs | 7 ++++- .../Dbi/DacDbiImpl.NativeCodeInfo.cs | 20 +++++++------ .../cdac/tests/UnitTests/DacDbiImplTests.cs | 30 ++++++++++++------- 5 files changed, 39 insertions(+), 20 deletions(-) diff --git a/docs/design/datacontracts/DebugInfo.md b/docs/design/datacontracts/DebugInfo.md index 33c837b655dc97..fd5539bdd6a5ae 100644 --- a/docs/design/datacontracts/DebugInfo.md +++ b/docs/design/datacontracts/DebugInfo.md @@ -337,6 +337,7 @@ public readonly struct DebugVarInfo public uint VarNumber { get; init; } public DebugVarLocKind Kind { get; init; } public bool IsByRef { get; init; } + public bool IsFloatingPoint { get; init; } public uint Register { get; init; } public uint Register2 { get; init; } public uint BaseRegister { get; init; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IDebugInfo.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IDebugInfo.cs index 23d777bd9d1532..7bfcf45c0d000a 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IDebugInfo.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IDebugInfo.cs @@ -59,6 +59,7 @@ public readonly struct DebugVarInfo public uint VarNumber { get; init; } public DebugVarLocKind Kind { get; init; } public bool IsByRef { get; init; } + public bool IsFloatingPoint { get; init; } /// Primary register number (Register, RegisterRegister, RegisterStack, StackRegister). public uint Register { get; init; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/DebugInfo/DebugInfoHelpers.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/DebugInfo/DebugInfoHelpers.cs index 0bb840dae0328e..9dc0686c83ccb9 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/DebugInfo/DebugInfoHelpers.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/DebugInfo/DebugInfoHelpers.cs @@ -139,11 +139,16 @@ internal static IEnumerable DoVars(NativeReader nativeReader, bool yield return locType switch { - VarLocType.VLT_REG or VarLocType.VLT_REG_FP => new DebugVarInfo + VarLocType.VLT_REG => new DebugVarInfo { StartOffset = startOffset, EndOffset = endOffset, VarNumber = varNumber, CallReturnValueILOffset = callReturnValueILOffset, Kind = DebugVarLocKind.Register, Register = reader.ReadUInt(), }, + VarLocType.VLT_REG_FP => new DebugVarInfo + { + StartOffset = startOffset, EndOffset = endOffset, VarNumber = varNumber, CallReturnValueILOffset = callReturnValueILOffset, + Kind = DebugVarLocKind.Register, IsFloatingPoint = true, Register = reader.ReadUInt(), + }, VarLocType.VLT_REG_BYREF => new DebugVarInfo { StartOffset = startOffset, EndOffset = endOffset, VarNumber = varNumber, CallReturnValueILOffset = callReturnValueILOffset, diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.NativeCodeInfo.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.NativeCodeInfo.cs index cee9698df16334..b5f9e266d7eee9 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.NativeCodeInfo.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.NativeCodeInfo.cs @@ -49,16 +49,17 @@ internal static DbiOffsetMapping ConvertToDbiOffsetMapping(Contracts.OffsetMappi internal static VarLoc ConvertToVarLoc(DebugVarInfo varInfo) { VarLoc loc = default; - loc.vlType = (varInfo.Kind, varInfo.IsByRef) switch + loc.vlType = (varInfo.Kind, varInfo.IsByRef, varInfo.IsFloatingPoint) switch { - (DebugVarLocKind.Register, false) => VarLocType.VLT_REG, - (DebugVarLocKind.Register, true) => VarLocType.VLT_REG_BYREF, - (DebugVarLocKind.Stack, false) => VarLocType.VLT_STK, - (DebugVarLocKind.Stack, true) => VarLocType.VLT_STK_BYREF, - (DebugVarLocKind.RegisterRegister, _) => VarLocType.VLT_REG_REG, - (DebugVarLocKind.RegisterStack, _) => VarLocType.VLT_REG_STK, - (DebugVarLocKind.StackRegister, _) => VarLocType.VLT_STK_REG, - (DebugVarLocKind.DoubleStack, _) => VarLocType.VLT_STK2, + (DebugVarLocKind.Register, false, false) => VarLocType.VLT_REG, + (DebugVarLocKind.Register, false, true) => VarLocType.VLT_REG_FP, + (DebugVarLocKind.Register, true, _) => VarLocType.VLT_REG_BYREF, + (DebugVarLocKind.Stack, false, _) => VarLocType.VLT_STK, + (DebugVarLocKind.Stack, true, _) => VarLocType.VLT_STK_BYREF, + (DebugVarLocKind.RegisterRegister, _, _) => VarLocType.VLT_REG_REG, + (DebugVarLocKind.RegisterStack, _, _) => VarLocType.VLT_REG_STK, + (DebugVarLocKind.StackRegister, _, _) => VarLocType.VLT_STK_REG, + (DebugVarLocKind.DoubleStack, _, _) => VarLocType.VLT_STK2, _ => VarLocType.VLT_INVALID, }; @@ -196,6 +197,7 @@ private static void AssertVarInfosEqual(List cdac, List Date: Thu, 9 Jul 2026 10:46:22 -0700 Subject: [PATCH 11/12] remove stored sig --- .../design/datacontracts/RuntimeTypeSystem.md | 27 ---------- .../Contracts/IRuntimeTypeSystem.cs | 2 - .../CallingConvention/CallingConvention_1.cs | 51 +++++-------------- .../Contracts/RuntimeTypeSystem_1.cs | 2 +- .../cdac/tests/UnitTests/MethodDescTests.cs | 4 -- 5 files changed, 15 insertions(+), 71 deletions(-) diff --git a/docs/design/datacontracts/RuntimeTypeSystem.md b/docs/design/datacontracts/RuntimeTypeSystem.md index 91aad2d353595d..867731ca76feda 100644 --- a/docs/design/datacontracts/RuntimeTypeSystem.md +++ b/docs/design/datacontracts/RuntimeTypeSystem.md @@ -232,9 +232,6 @@ partial interface IRuntimeTypeSystem : IContract // A no metadata method is also a StoredSigMethodDesc public virtual bool IsNoMetadataMethod(MethodDescHandle methodDesc, out string methodName); - // A StoredSigMethodDesc is a MethodDesc for which the signature isn't found in metadata. - public virtual bool IsStoredSigMethodDesc(MethodDescHandle methodDesc, out ReadOnlySpan signature); - // Return true for a MethodDesc that describes a method represented by the System.Reflection.Emit.DynamicMethod class // A DynamicMethod is also a StoredSigMethodDesc, and a NoMetadataMethod public virtual bool IsDynamicMethod(MethodDescHandle methodDesc); @@ -1731,30 +1728,6 @@ And the various apis are implemented with the following algorithms return true; } - public bool IsStoredSigMethodDesc(MethodDescHandle methodDescHandle, out ReadOnlySpan signature) - { - MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; - - switch (methodDesc.Classification) - { - case MethodDescClassification.Dynamic: - case MethodDescClassification.EEImpl: - case MethodDescClassification.Array: - break; // These have stored sigs - - default: - signature = default; - return false; - } - - TargetPointer Sig = // Read Sig field from StoredSigMethodDesc contract using address methodDescHandle.Address - uint cSig = // Read cSig field from StoredSigMethodDesc contract using address methodDescHandle.Address - - TargetPointer methodNamePointer = // Read S field from DynamicMethodDesc contract using address methodDescHandle.Address - signature = // Read buffer from target memory starting at address Sig, with cSig bytes in it. - return true; - } - public bool IsDynamicMethod(MethodDescHandle methodDescHandle) { MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; 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 6af06e2e9b2730..fc3d8b031c6051 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 @@ -258,8 +258,6 @@ bool TryGetHFAElementSize(TypeHandle typeHandle, out int elementSize) // Or something else similar. // A no metadata method is also a StoredSigMethodDesc bool IsNoMetadataMethod(MethodDescHandle methodDesc, out string methodName) => throw new NotImplementedException(); - // A StoredSigMethodDesc is a MethodDesc for which the signature isn't found in metadata. - bool IsStoredSigMethodDesc(MethodDescHandle methodDesc, out ReadOnlySpan signature) => throw new NotImplementedException(); // Gets the raw signature bytes for a MethodDesc by checking stored signature, async variant signature, then metadata. // Returns false if no signature could be resolved. diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/CallingConvention/CallingConvention_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/CallingConvention/CallingConvention_1.cs index 969d2875f6217d..1ed40a702b5a5e 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/CallingConvention/CallingConvention_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/CallingConvention/CallingConvention_1.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Reflection.Metadata; -using System.Reflection.Metadata.Ecma335; using Internal.CallingConvention; using Internal.CorConstants; using Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; @@ -290,27 +289,17 @@ private MethodSignature DecodeMethodSignature( RuntimeSignatureDecoder decoder = new( provider, _target, mdReader, context); - if (rts.IsStoredSigMethodDesc(methodDesc, out ReadOnlySpan storedSig)) + if (!rts.TryGetMethodSignature(methodDesc, out ReadOnlySpan methodSig)) + throw new InvalidOperationException("Method has no signature"); + + unsafe { - unsafe + fixed (byte* pSig = methodSig) { - fixed (byte* pStoredSig = storedSig) - { - BlobReader blobReader = new(pStoredSig, storedSig.Length); - return decoder.DecodeMethodSignature(ref blobReader); - } + BlobReader blobReader = new(pSig, methodSig.Length); + return decoder.DecodeMethodSignature(ref blobReader); } } - - uint methodToken = rts.GetMethodToken(methodDesc); - if (methodToken == (uint)EcmaMetadataUtils.TokenType.mdtMethodDef) - throw new InvalidOperationException("Method has no token"); - - MethodDefinitionHandle methodDefHandle = MetadataTokens.MethodDefinitionHandle( - (int)EcmaMetadataUtils.GetRowId(methodToken)); - MethodDefinition methodDef = mdReader.GetMethodDefinition(methodDefHandle); - BlobReader sigReader = mdReader.GetBlobReader(methodDef.Signature); - return decoder.DecodeMethodSignature(ref sigReader); } // Re-decode the method signature using a wrapper provider that records @@ -338,30 +327,18 @@ private ParamTypeInfo[] DecodeParamTypeInfo(IRuntimeTypeSystem rts, MethodDescHa RuntimeSignatureDecoder decoder = new( provider, _target, mdReader, context); + if (!rts.TryGetMethodSignature(methodDesc, out ReadOnlySpan methodSig)) + return new ParamTypeInfo[paramCount]; + MethodSignature sig; - if (rts.IsStoredSigMethodDesc(methodDesc, out ReadOnlySpan storedSig)) + unsafe { - unsafe + fixed (byte* pSig = methodSig) { - fixed (byte* pStoredSig = storedSig) - { - BlobReader blobReader = new(pStoredSig, storedSig.Length); - sig = decoder.DecodeMethodSignature(ref blobReader); - } + BlobReader blobReader = new(pSig, methodSig.Length); + sig = decoder.DecodeMethodSignature(ref blobReader); } } - else - { - uint methodToken = rts.GetMethodToken(methodDesc); - if (methodToken == (uint)EcmaMetadataUtils.TokenType.mdtMethodDef) - return new ParamTypeInfo[paramCount]; - - MethodDefinitionHandle methodDefHandle = MetadataTokens.MethodDefinitionHandle( - (int)EcmaMetadataUtils.GetRowId(methodToken)); - MethodDefinition methodDef = mdReader.GetMethodDefinition(methodDefHandle); - BlobReader sigReader = mdReader.GetBlobReader(methodDef.Signature); - sig = decoder.DecodeMethodSignature(ref sigReader); - } ParamTypeInfo[] result = new ParamTypeInfo[paramCount]; int count = Math.Min(paramCount, sig.ParameterTypes.Length); 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 5c00d3edbf24c7..781bd4f0bf35d6 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 @@ -1674,7 +1674,7 @@ public bool IsNoMetadataMethod(MethodDescHandle methodDescHandle, out string met return true; } - public bool IsStoredSigMethodDesc(MethodDescHandle methodDescHandle, out ReadOnlySpan signature) + private bool IsStoredSigMethodDesc(MethodDescHandle methodDescHandle, out ReadOnlySpan signature) { MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; diff --git a/src/native/managed/cdac/tests/UnitTests/MethodDescTests.cs b/src/native/managed/cdac/tests/UnitTests/MethodDescTests.cs index 4abb890b8a5b12..3eab3dd5c4914e 100644 --- a/src/native/managed/cdac/tests/UnitTests/MethodDescTests.cs +++ b/src/native/managed/cdac/tests/UnitTests/MethodDescTests.cs @@ -159,7 +159,6 @@ public void GetMethodDescHandle_ILMethod_GetBasicData(MockTarget.Architecture ar TargetPointer gcStressCodeCopy = rts.GetGCStressCodeCopy(handle); Assert.Equal(TargetPointer.Null, gcStressCodeCopy); - Assert.False(rts.IsStoredSigMethodDesc(handle, out _)); Assert.False(rts.IsNoMetadataMethod(handle, out _)); Assert.False(rts.IsDynamicMethod(handle)); Assert.False(rts.IsILStub(handle)); @@ -202,7 +201,6 @@ public void IsArrayMethod(MockTarget.Architecture arch) { MethodDescHandle handle = rts.GetMethodDescHandle(arrayMethods[i]); Assert.NotEqual(TargetPointer.Null, handle.Address); - Assert.True(rts.IsStoredSigMethodDesc(handle, out _)); Assert.True(rts.IsArrayMethod(handle, out ArrayFunctionType functionType)); ArrayFunctionType expectedFunctionType = i <= (byte)ArrayFunctionType.Constructor @@ -248,7 +246,6 @@ public void IsDynamicMethod(MockTarget.Architecture arch) { MethodDescHandle handle = rts.GetMethodDescHandle(dynamicMethod); Assert.NotEqual(TargetPointer.Null, handle.Address); - Assert.True(rts.IsStoredSigMethodDesc(handle, out _)); Assert.True(rts.IsNoMetadataMethod(handle, out _)); Assert.True(rts.IsDynamicMethod(handle)); Assert.False(rts.IsILStub(handle)); @@ -256,7 +253,6 @@ public void IsDynamicMethod(MockTarget.Architecture arch) { MethodDescHandle handle = rts.GetMethodDescHandle(ilStubMethod); Assert.NotEqual(TargetPointer.Null, handle.Address); - Assert.True(rts.IsStoredSigMethodDesc(handle, out _)); Assert.True(rts.IsNoMetadataMethod(handle, out _)); Assert.False(rts.IsDynamicMethod(handle)); Assert.True(rts.IsILStub(handle)); From bfd43ff501c6ca5ef436521a6110cdb43b46d4a6 Mon Sep 17 00:00:00 2001 From: Rachel Jarvi Date: Thu, 9 Jul 2026 11:53:54 -0700 Subject: [PATCH 12/12] Update DacDbiImpl.cs --- .../Dbi/DacDbiImpl.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs index a61630d1cee995..039ea8b30b3ba5 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs @@ -1360,7 +1360,13 @@ public int GetNativeCodeSequencePointsAndVarInfo( if (pFixedArgCount != null) *pFixedArgCount = GetArgCount(vmMethodDesc); - if (fpVarInfoCallback != null) + bool hasDebugInfo = debugInfo.HasDebugInfo(codePointer); + if (!hasDebugInfo && (fpVarInfoCallback != null || fpSeqPointCallback != null)) + { + hr = HResults.E_FAIL; + } + + if (fpVarInfoCallback != null && hasDebugInfo) { IEnumerable varInfos = debugInfo.GetMethodVarInfo(codePointer, out _); foreach (DebugVarInfo varInfo in varInfos) @@ -1369,7 +1375,7 @@ public int GetNativeCodeSequencePointsAndVarInfo( } } - if (fpSeqPointCallback != null) + if (fpSeqPointCallback != null && hasDebugInfo) { IEnumerable sequencePoints = debugInfo.GetMethodNativeMap(codePointer, preferUninstrumented: true, out _); foreach (Contracts.OffsetMapping mapping in sequencePoints)