diff --git a/src/coreclr/inc/sospriv.idl b/src/coreclr/inc/sospriv.idl index f56be4132f1e47..dd32bf649ec868 100644 --- a/src/coreclr/inc/sospriv.idl +++ b/src/coreclr/inc/sospriv.idl @@ -657,3 +657,82 @@ interface ISOSDacInterface17 : IUnknown [out] ISOSStressLogMsgEnum **ppEnum); HRESULT GetStressLogMemoryRanges([out] ISOSMemoryEnum **ppEnum); } + +cpp_quote("#ifndef _SOS_GCInfoData") +cpp_quote("#define _SOS_GCInfoData") + +typedef struct _SOSCodeRange +{ + unsigned int BeginOffset; + unsigned int EndOffset; +} SOSCodeRange; + +typedef struct _SOSGCInfoHeader +{ + ULONG SizeOf; + + unsigned int GcInfoVersion; + unsigned int CodeSize; + unsigned int PrologSize; + unsigned int StackBaseRegister; + unsigned int SizeOfStackParameterArea; + + BOOL IsVarArg; + BOOL WantsReportOnlyLeaf; + BOOL HasTailCalls; + + BOOL GSCookieIsPresent; + int GSCookieStackSlot; + unsigned int GSCookieValidRangeStart; + unsigned int GSCookieValidRangeEnd; + + BOOL PSPSymIsPresent; + int PSPSymStackSlot; + + BOOL GenericsInstContextIsPresent; + int GenericsInstContextStackSlot; + unsigned int GenericsInstContextKind; +} SOSGCInfoHeader; + +typedef struct _SOSGCSlotLifetime +{ + unsigned int BeginOffset; + unsigned int EndOffset; + int IsRegister; + unsigned int RegisterNumber; + int SpOffset; + unsigned int BaseRegister; + unsigned int GcFlags; +} SOSGCSlotLifetime; + +cpp_quote("#endif //_SOS_GCInfoData") + +[ + object, + local, + uuid(3dccf95b-bca2-40ee-8b83-d8d7574a1df0) +] +interface ISOSDacInterface18 : IUnknown +{ + HRESULT GetGCInfoHeader( + [in] CLRDATA_ADDRESS ip, + [in, out] SOSGCInfoHeader* header); + + HRESULT GetGCInfoInterruptibleRanges( + [in] CLRDATA_ADDRESS ip, + [in] ULONG count, + [out, size_is(count), length_is(*pNeeded)] SOSCodeRange* ranges, + [out] ULONG* pNeeded); + + HRESULT GetGCInfoSafePoints( + [in] CLRDATA_ADDRESS ip, + [in] ULONG count, + [out, size_is(count), length_is(*pNeeded)] unsigned int* offsets, + [out] ULONG* pNeeded); + + HRESULT GetGCInfoSlotLifetimes( + [in] CLRDATA_ADDRESS ip, + [in] ULONG count, + [out, size_is(count), length_is(*pNeeded)] SOSGCSlotLifetime* lifetimes, + [out] ULONG* pNeeded); +} diff --git a/src/coreclr/pal/prebuilt/idl/sospriv_i.cpp b/src/coreclr/pal/prebuilt/idl/sospriv_i.cpp index f3c4867a3a0ace..9e11072b2ffef9 100644 --- a/src/coreclr/pal/prebuilt/idl/sospriv_i.cpp +++ b/src/coreclr/pal/prebuilt/idl/sospriv_i.cpp @@ -130,6 +130,18 @@ MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface15,0x7ed81261,0x52a9,0x4a23,0xa3,0x58, MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface16,0x4ba12ff8,0xdaac,0x4e43,0xac,0x56,0x98,0xcf,0x8d,0x5c,0x59,0x5d); + +MIDL_DEFINE_GUID(IID, IID_ISOSStressLogThreadEnum,0x94a2bd3d,0xab3d,0x43bf,0x81,0xd8,0x3a,0xe9,0x6b,0x8e,0x33,0xcd); + + +MIDL_DEFINE_GUID(IID, IID_ISOSStressLogMsgEnum,0x437cb033,0xafe7,0x4c0f,0xa4,0xa7,0x82,0xc8,0x91,0xbc,0x04,0x9e); + + +MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface17,0x2f4bb585,0xed50,0x479e,0xbb,0xe0,0x10,0xa9,0x5a,0x5d,0xa3,0xbb); + + +MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface18,0x3dccf95b,0xbca2,0x40ee,0x8b,0x83,0xd8,0xd7,0x57,0x4a,0x1d,0xf0); + #undef MIDL_DEFINE_GUID #ifdef __cplusplus @@ -137,4 +149,3 @@ MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface16,0x4ba12ff8,0xdaac,0x4e43,0xac,0x56, #endif - diff --git a/src/coreclr/pal/prebuilt/inc/sospriv.h b/src/coreclr/pal/prebuilt/inc/sospriv.h index 764d1d424d0ba3..ef90037888260f 100644 --- a/src/coreclr/pal/prebuilt/inc/sospriv.h +++ b/src/coreclr/pal/prebuilt/inc/sospriv.h @@ -3766,6 +3766,545 @@ EXTERN_C const IID IID_ISOSDacInterface16; #endif /* __ISOSDacInterface16_INTERFACE_DEFINED__ */ +#ifndef _SOS_StressLogData +#define _SOS_StressLogData +typedef struct _SOSStressLogData + { + unsigned int LoggedFacilities; + unsigned int Level; + unsigned int MaxSizePerThread; + unsigned int MaxSizeTotal; + int TotalChunks; + UINT64 TickFrequency; + UINT64 StartTimestamp; + UINT64 StartTime; + } SOSStressLogData; + +#endif // _SOS_StressLogData + +#ifndef _SOS_ThreadStressLogData +#define _SOS_ThreadStressLogData +typedef struct _SOSThreadStressLogData + { + CLRDATA_ADDRESS ThreadLogAddress; + UINT64 ThreadId; + } SOSThreadStressLogData; + +#endif // _SOS_ThreadStressLogData + +#ifndef _SOS_StressMsgData +#define _SOS_StressMsgData +typedef struct _SOSStressMsgData + { + unsigned int Facility; + CLRDATA_ADDRESS FormatString; + UINT64 Timestamp; + unsigned int ArgumentCount; + } SOSStressMsgData; + +#endif // _SOS_StressMsgData + +#ifndef __ISOSStressLogThreadEnum_INTERFACE_DEFINED__ +#define __ISOSStressLogThreadEnum_INTERFACE_DEFINED__ + +/* interface ISOSStressLogThreadEnum */ +/* [uuid][local][object] */ + + +EXTERN_C const IID IID_ISOSStressLogThreadEnum; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("94a2bd3d-ab3d-43bf-81d8-3ae96b8e33cd") + ISOSStressLogThreadEnum : public ISOSEnum + { + public: + virtual HRESULT STDMETHODCALLTYPE Next( + unsigned int count, + SOSThreadStressLogData values[], + unsigned int *pFetched) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ISOSStressLogThreadEnumVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ISOSStressLogThreadEnum * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ISOSStressLogThreadEnum * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ISOSStressLogThreadEnum * This); + + HRESULT ( STDMETHODCALLTYPE *Skip )( + ISOSStressLogThreadEnum * This, + uint32_t count); + + HRESULT ( STDMETHODCALLTYPE *Reset )( + ISOSStressLogThreadEnum * This); + + HRESULT ( STDMETHODCALLTYPE *GetCount )( + ISOSStressLogThreadEnum * This, + uint32_t *pCount); + + HRESULT ( STDMETHODCALLTYPE *Next )( + ISOSStressLogThreadEnum * This, + unsigned int count, + SOSThreadStressLogData values[], + unsigned int *pFetched); + + END_INTERFACE + } ISOSStressLogThreadEnumVtbl; + + interface ISOSStressLogThreadEnum + { + CONST_VTBL struct ISOSStressLogThreadEnumVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ISOSStressLogThreadEnum_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ISOSStressLogThreadEnum_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ISOSStressLogThreadEnum_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ISOSStressLogThreadEnum_Skip(This,count) \ + ( (This)->lpVtbl -> Skip(This,count) ) + +#define ISOSStressLogThreadEnum_Reset(This) \ + ( (This)->lpVtbl -> Reset(This) ) + +#define ISOSStressLogThreadEnum_GetCount(This,pCount) \ + ( (This)->lpVtbl -> GetCount(This,pCount) ) + + +#define ISOSStressLogThreadEnum_Next(This,count,values,pFetched) \ + ( (This)->lpVtbl -> Next(This,count,values,pFetched) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ISOSStressLogThreadEnum_INTERFACE_DEFINED__ */ + + +#ifndef __ISOSStressLogMsgEnum_INTERFACE_DEFINED__ +#define __ISOSStressLogMsgEnum_INTERFACE_DEFINED__ + +/* interface ISOSStressLogMsgEnum */ +/* [uuid][local][object] */ + + +EXTERN_C const IID IID_ISOSStressLogMsgEnum; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("437cb033-afe7-4c0f-a4a7-82c891bc049e") + ISOSStressLogMsgEnum : public ISOSEnum + { + public: + virtual HRESULT STDMETHODCALLTYPE Next( + unsigned int count, + SOSStressMsgData values[], + unsigned int *pFetched) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetArguments( + unsigned int messageIndex, + unsigned int argCount, + CLRDATA_ADDRESS args[], + unsigned int *pFetched) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ISOSStressLogMsgEnumVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ISOSStressLogMsgEnum * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ISOSStressLogMsgEnum * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ISOSStressLogMsgEnum * This); + + HRESULT ( STDMETHODCALLTYPE *Skip )( + ISOSStressLogMsgEnum * This, + uint32_t count); + + HRESULT ( STDMETHODCALLTYPE *Reset )( + ISOSStressLogMsgEnum * This); + + HRESULT ( STDMETHODCALLTYPE *GetCount )( + ISOSStressLogMsgEnum * This, + uint32_t *pCount); + + HRESULT ( STDMETHODCALLTYPE *Next )( + ISOSStressLogMsgEnum * This, + unsigned int count, + SOSStressMsgData values[], + unsigned int *pFetched); + + HRESULT ( STDMETHODCALLTYPE *GetArguments )( + ISOSStressLogMsgEnum * This, + unsigned int messageIndex, + unsigned int argCount, + CLRDATA_ADDRESS args[], + unsigned int *pFetched); + + END_INTERFACE + } ISOSStressLogMsgEnumVtbl; + + interface ISOSStressLogMsgEnum + { + CONST_VTBL struct ISOSStressLogMsgEnumVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ISOSStressLogMsgEnum_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ISOSStressLogMsgEnum_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ISOSStressLogMsgEnum_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ISOSStressLogMsgEnum_Skip(This,count) \ + ( (This)->lpVtbl -> Skip(This,count) ) + +#define ISOSStressLogMsgEnum_Reset(This) \ + ( (This)->lpVtbl -> Reset(This) ) + +#define ISOSStressLogMsgEnum_GetCount(This,pCount) \ + ( (This)->lpVtbl -> GetCount(This,pCount) ) + + +#define ISOSStressLogMsgEnum_Next(This,count,values,pFetched) \ + ( (This)->lpVtbl -> Next(This,count,values,pFetched) ) + +#define ISOSStressLogMsgEnum_GetArguments(This,messageIndex,argCount,args,pFetched) \ + ( (This)->lpVtbl -> GetArguments(This,messageIndex,argCount,args,pFetched) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ISOSStressLogMsgEnum_INTERFACE_DEFINED__ */ + + +#ifndef __ISOSDacInterface17_INTERFACE_DEFINED__ +#define __ISOSDacInterface17_INTERFACE_DEFINED__ + +/* interface ISOSDacInterface17 */ +/* [uuid][local][object] */ + + +EXTERN_C const IID IID_ISOSDacInterface17; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("2f4bb585-ed50-479e-bbe0-10a95a5da3bb") + ISOSDacInterface17 : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetStressLogData( + SOSStressLogData *data) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetStressLogThreadEnumerator( + ISOSStressLogThreadEnum **ppEnum) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetStressLogMessageEnumerator( + CLRDATA_ADDRESS threadStressLogAddress, + ISOSStressLogMsgEnum **ppEnum) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ISOSDacInterface17Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ISOSDacInterface17 * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ISOSDacInterface17 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ISOSDacInterface17 * This); + + HRESULT ( STDMETHODCALLTYPE *GetStressLogData )( + ISOSDacInterface17 * This, + SOSStressLogData *data); + + HRESULT ( STDMETHODCALLTYPE *GetStressLogThreadEnumerator )( + ISOSDacInterface17 * This, + ISOSStressLogThreadEnum **ppEnum); + + HRESULT ( STDMETHODCALLTYPE *GetStressLogMessageEnumerator )( + ISOSDacInterface17 * This, + CLRDATA_ADDRESS threadStressLogAddress, + ISOSStressLogMsgEnum **ppEnum); + + END_INTERFACE + } ISOSDacInterface17Vtbl; + + interface ISOSDacInterface17 + { + CONST_VTBL struct ISOSDacInterface17Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ISOSDacInterface17_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ISOSDacInterface17_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ISOSDacInterface17_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ISOSDacInterface17_GetStressLogData(This,data) \ + ( (This)->lpVtbl -> GetStressLogData(This,data) ) + +#define ISOSDacInterface17_GetStressLogThreadEnumerator(This,ppEnum) \ + ( (This)->lpVtbl -> GetStressLogThreadEnumerator(This,ppEnum) ) + +#define ISOSDacInterface17_GetStressLogMessageEnumerator(This,threadStressLogAddress,ppEnum) \ + ( (This)->lpVtbl -> GetStressLogMessageEnumerator(This,threadStressLogAddress,ppEnum) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ISOSDacInterface17_INTERFACE_DEFINED__ */ + + +#ifndef _SOS_GCInfoData +#define _SOS_GCInfoData +typedef struct _SOSCodeRange + { + unsigned int BeginOffset; + unsigned int EndOffset; + } SOSCodeRange; + +typedef struct _SOSGCInfoHeader + { + ULONG SizeOf; + unsigned int GcInfoVersion; + unsigned int CodeSize; + unsigned int PrologSize; + unsigned int StackBaseRegister; + unsigned int SizeOfStackParameterArea; + BOOL IsVarArg; + BOOL WantsReportOnlyLeaf; + BOOL HasTailCalls; + BOOL GSCookieIsPresent; + int GSCookieStackSlot; + unsigned int GSCookieValidRangeStart; + unsigned int GSCookieValidRangeEnd; + BOOL PSPSymIsPresent; + int PSPSymStackSlot; + BOOL GenericsInstContextIsPresent; + int GenericsInstContextStackSlot; + unsigned int GenericsInstContextKind; + } SOSGCInfoHeader; + +typedef struct _SOSGCSlotLifetime + { + unsigned int BeginOffset; + unsigned int EndOffset; + int IsRegister; + unsigned int RegisterNumber; + int SpOffset; + unsigned int BaseRegister; + unsigned int GcFlags; + } SOSGCSlotLifetime; + +#endif // _SOS_GCInfoData + +#ifndef __ISOSDacInterface18_INTERFACE_DEFINED__ +#define __ISOSDacInterface18_INTERFACE_DEFINED__ + +/* interface ISOSDacInterface18 */ +/* [uuid][local][object] */ + + +EXTERN_C const IID IID_ISOSDacInterface18; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("3dccf95b-bca2-40ee-8b83-d8d7574a1df0") + ISOSDacInterface18 : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetGCInfoHeader( + CLRDATA_ADDRESS ip, + SOSGCInfoHeader *header) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetGCInfoInterruptibleRanges( + CLRDATA_ADDRESS ip, + ULONG count, + SOSCodeRange *ranges, + ULONG *pNeeded) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetGCInfoSafePoints( + CLRDATA_ADDRESS ip, + ULONG count, + unsigned int *offsets, + ULONG *pNeeded) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetGCInfoSlotLifetimes( + CLRDATA_ADDRESS ip, + ULONG count, + SOSGCSlotLifetime *lifetimes, + ULONG *pNeeded) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ISOSDacInterface18Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ISOSDacInterface18 * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ISOSDacInterface18 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ISOSDacInterface18 * This); + + HRESULT ( STDMETHODCALLTYPE *GetGCInfoHeader )( + ISOSDacInterface18 * This, + CLRDATA_ADDRESS ip, + SOSGCInfoHeader *header); + + HRESULT ( STDMETHODCALLTYPE *GetGCInfoInterruptibleRanges )( + ISOSDacInterface18 * This, + CLRDATA_ADDRESS ip, + ULONG count, + SOSCodeRange *ranges, + ULONG *pNeeded); + + HRESULT ( STDMETHODCALLTYPE *GetGCInfoSafePoints )( + ISOSDacInterface18 * This, + CLRDATA_ADDRESS ip, + ULONG count, + unsigned int *offsets, + ULONG *pNeeded); + + HRESULT ( STDMETHODCALLTYPE *GetGCInfoSlotLifetimes )( + ISOSDacInterface18 * This, + CLRDATA_ADDRESS ip, + ULONG count, + SOSGCSlotLifetime *lifetimes, + ULONG *pNeeded); + + END_INTERFACE + } ISOSDacInterface18Vtbl; + + interface ISOSDacInterface18 + { + CONST_VTBL struct ISOSDacInterface18Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ISOSDacInterface18_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ISOSDacInterface18_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ISOSDacInterface18_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ISOSDacInterface18_GetGCInfoHeader(This,ip,header) \ + ( (This)->lpVtbl -> GetGCInfoHeader(This,ip,header) ) + +#define ISOSDacInterface18_GetGCInfoInterruptibleRanges(This,ip,count,ranges,pNeeded) \ + ( (This)->lpVtbl -> GetGCInfoInterruptibleRanges(This,ip,count,ranges,pNeeded) ) + +#define ISOSDacInterface18_GetGCInfoSafePoints(This,ip,count,offsets,pNeeded) \ + ( (This)->lpVtbl -> GetGCInfoSafePoints(This,ip,count,offsets,pNeeded) ) + +#define ISOSDacInterface18_GetGCInfoSlotLifetimes(This,ip,count,lifetimes,pNeeded) \ + ( (This)->lpVtbl -> GetGCInfoSlotLifetimes(This,ip,count,lifetimes,pNeeded) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ISOSDacInterface18_INTERFACE_DEFINED__ */ + + /* Additional Prototypes for ALL interfaces */ /* end of Additional Prototypes */ @@ -3776,4 +4315,3 @@ EXTERN_C const IID IID_ISOSDacInterface16; #endif - diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IGCInfo.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IGCInfo.cs index 74e085dc62508c..d9b1ebb327195b 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IGCInfo.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IGCInfo.cs @@ -42,6 +42,51 @@ public record struct GcSlotEnumerationOptions public bool ReportFPBasedSlotsOnly { get; set; } } +/// Header fields decoded from the GC info stream for a method. +public readonly record struct GCInfoHeader( + uint Version, + uint CodeSize, + uint PrologSize, + uint StackBaseRegister, + uint SizeOfStackParameterArea, + bool IsVarArg, + bool WantsReportOnlyLeaf, + bool HasTailCalls, + SpecialSlot? GSCookie, + uint GSCookieValidRangeStart, + uint GSCookieValidRangeEnd, + SpecialSlot? PSPSym, + SpecialSlot? GenericsInstContext, + GenericsContextKind GenericsInstContextKind); + +/// Stack offset of a special GC info slot (GS cookie, PSP sym, or generics inst context). +public readonly record struct SpecialSlot(int SpOffset); + +/// Kind of the generics instantiation context. +public enum GenericsContextKind +{ + MethodDesc = 0, + MethodHandle = 1, + This = 2, +} + +/// Unified lifetime of a GC slot (register or stack). +/// True if the slot is a CPU register; false if it is a stack location. +/// Register number (meaningful only when IsRegister is true). +/// Stack offset from the base (meaningful only when IsRegister is false). +/// Stack base register (meaningful only when IsRegister is false). +/// GC slot flags: 0x1 = interior pointer, 0x2 = pinned. +/// Code offset where the slot becomes live. +/// Code offset where the slot becomes dead (exclusive). +public readonly record struct GCSlotLifetime( + bool IsRegister, + uint RegisterNumber, + int SpOffset, + uint BaseRegister, + uint GcFlags, + uint BeginOffset, + uint EndOffset); + public interface IGCInfo : IContract { static string IContract.Name { get; } = nameof(GCInfo); @@ -54,7 +99,10 @@ public interface IGCInfo : IContract uint GetSizeOfStackParameterArea(IGCInfoHandle handle) => throw new NotImplementedException(); uint GetCalleePoppedArgumentsSize(IGCInfoHandle handle) => throw new NotImplementedException(); IReadOnlyList GetInterruptibleRanges(IGCInfoHandle handle) => throw new NotImplementedException(); + IReadOnlyList GetSafePoints(IGCInfoHandle handle) => throw new NotImplementedException(); IReadOnlyList EnumerateLiveSlots(IGCInfoHandle handle, uint instructionOffset, GcSlotEnumerationOptions options) => throw new NotImplementedException(); + GCInfoHeader GetHeader(IGCInfoHandle handle) => throw new NotImplementedException(); + IReadOnlyList GetSlotLifetimes(IGCInfoHandle handle) => throw new NotImplementedException(); } public readonly struct GCInfo : IGCInfo diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/GCInfoDecoder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/GCInfoDecoder.cs index 8dcded7b994d17..dadfe659566b91 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/GCInfoDecoder.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/GCInfoDecoder.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Numerics; using ILCompiler.Reflection.ReadyToRun; @@ -129,6 +130,7 @@ public static GcSlotDesc CreateStackSlot(int spOffset, GcStackSlotBase slotBase, private uint _numSafePoints; private uint _numInterruptibleRanges; private List _interruptibleRanges = []; + private List _safePoints = []; private int _safePointBitOffset; /* Slot Table Fields */ @@ -332,11 +334,19 @@ private IEnumerable DecodeInterruptibleRanges() private IEnumerable DecodeSafePoints() { - // Save the position of the safe point data for FindSafePoint _safePointBitOffset = _bitOffset; - // skip over safe point data uint numBitsPerOffset = CeilOfLog2(TTraits.NormalizeCodeOffset(_codeLength)); - _bitOffset += (int)(numBitsPerOffset * _numSafePoints); + + // Eagerly decode safe point offsets in normalized form (matching the on-disk encoding). + // FindSafePoint normalizes the query offset before comparing, so these stay normalized. + // GetSafePoints() denormalizes them on the way out for consumers that need real offsets. + _safePoints = new List((int)_numSafePoints); + for (uint i = 0; i < _numSafePoints; i++) + { + uint normalizedOffset = (uint)_reader.ReadBits((int)numBitsPerOffset, ref _bitOffset); + _safePoints.Add(normalizedOffset); + } + yield break; } @@ -530,6 +540,336 @@ public IReadOnlyList GetInterruptibleRanges() return _interruptibleRanges; } + public IReadOnlyList GetSafePoints() + { + EnsureDecodedTo(DecodePoints.InterruptibleRanges); + // _safePoints stores normalized offsets; denormalize for external consumers. + return _safePoints.Select(TTraits.DenormalizeCodeOffset).ToList(); + } + + public GCInfoHeader GetHeader() + { + EnsureDecodedTo(DecodePoints.ReversePInvoke); + + GcInfoHeaderFlags genericsInstContextFlags = _headerFlags & GcInfoHeaderFlags.GC_INFO_HAS_GENERICS_INST_CONTEXT_MASK; + SpecialSlot? gsCookie = _gsCookieStackSlot != TTraits.NO_GS_COOKIE ? new SpecialSlot(_gsCookieStackSlot) : null; + SpecialSlot? pspSym = _pspSymStackSlot != TTraits.NO_PSP_SYM ? new SpecialSlot(_pspSymStackSlot) : null; + SpecialSlot? genericsInstContext = _genericsInstContextStackSlot != TTraits.NO_GENERICS_INST_CONTEXT ? new SpecialSlot(_genericsInstContextStackSlot) : null; + + GenericsContextKind genericsContextKind = genericsInstContextFlags switch + { + GcInfoHeaderFlags.GC_INFO_HAS_GENERICS_INST_CONTEXT_MD => GenericsContextKind.MethodDesc, + GcInfoHeaderFlags.GC_INFO_HAS_GENERICS_INST_CONTEXT_MT => GenericsContextKind.MethodHandle, + GcInfoHeaderFlags.GC_INFO_HAS_GENERICS_INST_CONTEXT_THIS => GenericsContextKind.This, + _ => GenericsContextKind.MethodDesc, + }; + + bool flag80Set = _headerFlags.HasFlag(GcInfoHeaderFlags.GC_INFO_WANTS_REPORT_ONLY_LEAF); + bool wantsReportOnlyLeaf = _arch == RuntimeInfoArchitecture.X64 && _gcVersion < 4 ? flag80Set : true; + bool hasTailCalls = _arch is RuntimeInfoArchitecture.Arm or RuntimeInfoArchitecture.Arm64 or RuntimeInfoArchitecture.LoongArch64 or RuntimeInfoArchitecture.RiscV64 + && flag80Set; + + return new GCInfoHeader( + Version: _gcVersion, + CodeSize: _codeLength, + PrologSize: _validRangeStart, + StackBaseRegister: _stackBaseRegister, + SizeOfStackParameterArea: _fixedStackParameterScratchArea, + IsVarArg: _headerFlags.HasFlag(GcInfoHeaderFlags.GC_INFO_IS_VARARG), + WantsReportOnlyLeaf: wantsReportOnlyLeaf, + HasTailCalls: hasTailCalls, + GSCookie: gsCookie, + GSCookieValidRangeStart: _validRangeStart, + GSCookieValidRangeEnd: _validRangeEnd, + PSPSym: pspSym, + GenericsInstContext: genericsInstContext, + GenericsInstContextKind: genericsContextKind); + } + + public IReadOnlyList GetSlotLifetimes() + { + EnsureDecodedTo(DecodePoints.SlotTable); + + List lifetimes = []; + uint numTracked = NumTrackedSlots; + + // Untracked slots are always live for the entire method + for (uint slotIndex = numTracked; slotIndex < _numSlots; slotIndex++) + { + GcSlotDesc slot = _slots[(int)slotIndex]; + uint gcFlags = (uint)slot.Flags & ((uint)GcSlotFlags.GC_SLOT_INTERIOR | (uint)GcSlotFlags.GC_SLOT_PINNED | (uint)GcSlotFlags.GC_SLOT_UNTRACKED); + lifetimes.Add(new GCSlotLifetime(slot.IsRegister, slot.RegisterNumber, slot.SpOffset, (uint)slot.Base, gcFlags, 0, _codeLength)); + } + + if (numTracked == 0) + return lifetimes; + + // For methods with only safe points (not fully interruptible), + // decode tracked slot lifetimes from the per-safe-point bitmaps. + if (_numInterruptibleRanges == 0) + { + if (_numSafePoints == 0) + return lifetimes; + + int safePointBitPos = _liveStateBitOffset; + + // Read indirect live state table header + uint numBitsPerOffset = 0; + if (_reader.ReadBits(1, ref safePointBitPos) != 0) + { + numBitsPerOffset = (uint)_reader.DecodeVarLengthUnsigned(TTraits.POINTER_SIZE_ENCBASE, ref safePointBitPos) + 1; + } + + // For each safe point, read the live slot bitmap. + // Safe points are independent snapshots. We emit lifetimes spanning + // from the first safe point where a slot is live to the last consecutive + // safe point where it remains live. + IReadOnlyList safePoints = GetSafePoints(); + uint[] liveStart = new uint[numTracked]; + bool[] prevLive = new bool[numTracked]; + + for (uint sp = 0; sp < _numSafePoints; sp++) + { + bool[] curLive = new bool[numTracked]; + int spBitOffset; + + if (numBitsPerOffset != 0) + { + // Indirect table: read offset pointer for this safe point + int offsetTablePos = safePointBitPos; + int spOffsetBit = offsetTablePos + (int)(sp * numBitsPerOffset); + uint liveStatesOffset = (uint)_reader.ReadBits((int)numBitsPerOffset, ref spOffsetBit); + int liveStatesStart = (int)(((uint)offsetTablePos + _numSafePoints * numBitsPerOffset + 7) & (~7u)); + spBitOffset = (int)(liveStatesStart + liveStatesOffset); + + if (_reader.ReadBits(1, ref spBitOffset) != 0) + { + // RLE encoded + bool fSkip = _reader.ReadBits(1, ref spBitOffset) == 0; + bool fReport = true; + uint readSlots = (uint)_reader.DecodeVarLengthUnsigned( + fSkip ? TTraits.LIVESTATE_RLE_SKIP_ENCBASE : TTraits.LIVESTATE_RLE_RUN_ENCBASE, ref spBitOffset); + fSkip = !fSkip; + while (readSlots < numTracked) + { + uint cnt = (uint)_reader.DecodeVarLengthUnsigned( + fSkip ? TTraits.LIVESTATE_RLE_SKIP_ENCBASE : TTraits.LIVESTATE_RLE_RUN_ENCBASE, ref spBitOffset) + 1; + if (fReport) + { + for (uint si = readSlots; si < readSlots + cnt; si++) + curLive[si] = true; + } + readSlots += cnt; + fSkip = !fSkip; + fReport = !fReport; + } + } + else + { + for (uint si = 0; si < numTracked; si++) + curLive[si] = _reader.ReadBits(1, ref spBitOffset) != 0; + } + } + else + { + // Direct bitmap: numTracked bits per safe point + spBitOffset = safePointBitPos + (int)(sp * numTracked); + for (uint si = 0; si < numTracked; si++) + curLive[si] = _reader.ReadBits(1, ref spBitOffset) != 0; + } + + uint spOffset = safePoints[(int)sp]; + + for (uint si = 0; si < numTracked; si++) + { + if (curLive[si] && !prevLive[si]) + { + // Slot became live at this safe point + liveStart[si] = spOffset; + } + else if (!curLive[si] && prevLive[si]) + { + // Slot is dead -- emit lifetime ending after previous safe point + EmitSlotLifetime(si, liveStart[si], safePoints[(int)(sp - 1)] + 1, lifetimes); + } + } + + Array.Copy(curLive, prevLive, numTracked); + } + + // Close any slots still live at the last safe point + uint lastSpOffset = safePoints[(int)(_numSafePoints - 1)]; + for (uint si = 0; si < numTracked; si++) + { + if (prevLive[si]) + EmitSlotLifetime(si, liveStart[si], lastSpOffset + 1, lifetimes); + } + + return lifetimes; + } + + int bitOffset = _liveStateBitOffset; + + // Skip indirect live state table header and safe point data + if (_numSafePoints > 0 && _reader.ReadBits(1, ref bitOffset) != 0) + { + _reader.DecodeVarLengthUnsigned(TTraits.POINTER_SIZE_ENCBASE, ref bitOffset); + } + bitOffset += (int)(_numSafePoints * numTracked); + + // Compute total interruptible length + uint numInterruptibleLength = 0; + for (int i = 0; i < _interruptibleRanges.Count; i++) + { + uint normStart = TTraits.NormalizeCodeOffset(_interruptibleRanges[i].StartOffset); + uint normStop = TTraits.NormalizeCodeOffset(_interruptibleRanges[i].EndOffset); + numInterruptibleLength += normStop - normStart; + } + + uint numChunks = (numInterruptibleLength + TTraits.NUM_NORM_CODE_OFFSETS_PER_CHUNK - 1) / TTraits.NUM_NORM_CODE_OFFSETS_PER_CHUNK; + uint numBitsPerPointer = (uint)_reader.DecodeVarLengthUnsigned(TTraits.POINTER_SIZE_ENCBASE, ref bitOffset); + if (numBitsPerPointer == 0) + return lifetimes; + + int pointerTablePos = bitOffset; + int chunksStartPos = (int)(((uint)pointerTablePos + numChunks * numBitsPerPointer + 7) & (~7u)); + + // Track per-slot live state across chunks: slotIndex -> beginCodeOffset + Dictionary activeSlots = []; + + for (uint chunk = 0; chunk < numChunks; chunk++) + { + bitOffset = pointerTablePos + (int)(chunk * numBitsPerPointer); + uint chunkPointer = (uint)_reader.ReadBits((int)numBitsPerPointer, ref bitOffset); + if (chunkPointer == 0) + continue; + + int chunkPos = (int)(chunksStartPos + chunkPointer - 1); + bitOffset = chunkPos; + + // Read couldBeLive bitvector + List couldBeLiveSlots = ReadCouldBeLiveSlots(ref bitOffset, numTracked); + + int finalStateBitOffset = bitOffset; + int transitionBitOffset = bitOffset + couldBeLiveSlots.Count; + + uint chunkStartNormOffset = chunk * TTraits.NUM_NORM_CODE_OFFSETS_PER_CHUNK; + + for (int i = 0; i < couldBeLiveSlots.Count; i++) + { + uint slotIndex = couldBeLiveSlots[i]; + uint finalState = (uint)_reader.ReadBits(1, ref finalStateBitOffset); + + // Collect transitions within this chunk + List transitions = []; + while (_reader.ReadBits(1, ref transitionBitOffset) != 0) + { + uint transOffset = (uint)_reader.ReadBits(TTraits.NUM_NORM_CODE_OFFSETS_PER_CHUNK_LOG2, ref transitionBitOffset); + transitions.Add(transOffset); + } + + // Initial state = finalState XOR (transitions.Count % 2) + uint currentState = finalState ^ (uint)(transitions.Count % 2); + + // Walk transitions and track lifetime boundaries + uint prevNormOffset = chunkStartNormOffset; + foreach (uint transOffset in transitions) + { + uint absNormOffset = chunkStartNormOffset + transOffset; + if (currentState != 0 && !activeSlots.ContainsKey(slotIndex)) + { + // Slot is live and we haven't recorded its start yet + activeSlots[slotIndex] = DenormInterruptibleOffset(prevNormOffset); + } + else if (currentState == 0 && activeSlots.TryGetValue(slotIndex, out uint beginOffset)) + { + // Slot just died + EmitSlotLifetime(slotIndex, beginOffset, DenormInterruptibleOffset(absNormOffset), lifetimes); + activeSlots.Remove(slotIndex); + } + currentState ^= 1; + prevNormOffset = absNormOffset; + } + + // End of chunk state + if (currentState != 0 && !activeSlots.ContainsKey(slotIndex)) + activeSlots[slotIndex] = DenormInterruptibleOffset(prevNormOffset); + else if (currentState == 0 && activeSlots.TryGetValue(slotIndex, out uint begin)) + { + EmitSlotLifetime(slotIndex, begin, DenormInterruptibleOffset(prevNormOffset), lifetimes); + activeSlots.Remove(slotIndex); + } + } + } + + // Close any remaining open lifetimes + foreach ((uint slotIndex, uint beginOffset) in activeSlots) + EmitSlotLifetime(slotIndex, beginOffset, _codeLength, lifetimes); + + return lifetimes; + } + + private List ReadCouldBeLiveSlots(ref int bitOffset, uint numTracked) + { + List slots = []; + if (_reader.ReadBits(1, ref bitOffset) != 0) + { + // RLE encoded + bool fSkip = _reader.ReadBits(1, ref bitOffset) == 0; + bool fReport = true; + uint readSlots = (uint)_reader.DecodeVarLengthUnsigned( + fSkip ? TTraits.LIVESTATE_RLE_SKIP_ENCBASE : TTraits.LIVESTATE_RLE_RUN_ENCBASE, ref bitOffset); + fSkip = !fSkip; + while (readSlots < numTracked) + { + uint cnt = (uint)_reader.DecodeVarLengthUnsigned( + fSkip ? TTraits.LIVESTATE_RLE_SKIP_ENCBASE : TTraits.LIVESTATE_RLE_RUN_ENCBASE, ref bitOffset) + 1; + if (fReport) + { + for (uint j = 0; j < cnt; j++) + slots.Add(readSlots + j); + } + readSlots += cnt; + fSkip = !fSkip; + fReport = !fReport; + } + } + else + { + for (uint i = 0; i < numTracked; i++) + { + if (_reader.ReadBits(1, ref bitOffset) != 0) + slots.Add(i); + } + } + return slots; + } + + private uint DenormInterruptibleOffset(uint normOffset) + { + uint accumulated = 0; + for (int i = 0; i < _interruptibleRanges.Count; i++) + { + uint normStart = TTraits.NormalizeCodeOffset(_interruptibleRanges[i].StartOffset); + uint normStop = TTraits.NormalizeCodeOffset(_interruptibleRanges[i].EndOffset); + uint rangeLen = normStop - normStart; + if (normOffset < accumulated + rangeLen) + { + uint delta = normOffset - accumulated; + return TTraits.DenormalizeCodeOffset(normStart + delta); + } + accumulated += rangeLen; + } + return _codeLength; + } + + private void EmitSlotLifetime(uint slotIndex, uint beginOffset, uint endOffset, List lifetimes) + { + GcSlotDesc slot = _slots[(int)slotIndex]; + uint gcFlags = (uint)slot.Flags & ((uint)GcSlotFlags.GC_SLOT_INTERIOR | (uint)GcSlotFlags.GC_SLOT_PINNED); + lifetimes.Add(new GCSlotLifetime(slot.IsRegister, slot.RegisterNumber, slot.SpOffset, (uint)slot.Base, gcFlags, beginOffset, endOffset)); + } + public uint NumTrackedSlots => _numSlots - _numUntrackedSlots; IReadOnlyList IGCInfoDecoder.EnumerateLiveSlots( @@ -860,18 +1200,14 @@ private uint FindSafePoint(uint codeOffset) EnsureDecodedTo(DecodePoints.InterruptibleRanges); uint normBreakOffset = TTraits.NormalizeCodeOffset(codeOffset); - uint numBitsPerOffset = CeilOfLog2(TTraits.NormalizeCodeOffset(_codeLength)); // TODO(stackref): The native FindSafePoint uses binary search (NarrowSafePointSearch) // when numSafePoints > 32. This is a performance optimization only — no correctness impact. - // Linear scan through safe point offsets from the saved position - int scanOffset = _safePointBitOffset; - for (uint i = 0; i < _numSafePoints; i++) + for (uint i = 0; i < _safePoints.Count; i++) { - uint spOffset = (uint)_reader.ReadBits((int)numBitsPerOffset, ref scanOffset); - if (spOffset == normBreakOffset) + if (_safePoints[(int)i] == normBreakOffset) return i; - if (spOffset > normBreakOffset) + if (_safePoints[(int)i] > normBreakOffset) break; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/GCInfo_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/GCInfo_1.cs index d13e0f6051e98d..1ff33321130434 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/GCInfo_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/GCInfo_1.cs @@ -52,12 +52,30 @@ IReadOnlyList IGCInfo.GetInterruptibleRanges(IGCInfoHandle g return handle.GetInterruptibleRanges(); } + IReadOnlyList IGCInfo.GetSafePoints(IGCInfoHandle gcInfoHandle) + { + IGCInfoDecoder handle = AssertCorrectHandle(gcInfoHandle); + return handle.GetSafePoints(); + } + IReadOnlyList IGCInfo.EnumerateLiveSlots(IGCInfoHandle gcInfoHandle, uint instructionOffset, GcSlotEnumerationOptions options) { IGCInfoDecoder handle = AssertCorrectHandle(gcInfoHandle); return handle.EnumerateLiveSlots(instructionOffset, options); } + GCInfoHeader IGCInfo.GetHeader(IGCInfoHandle gcInfoHandle) + { + IGCInfoDecoder handle = AssertCorrectHandle(gcInfoHandle); + return handle.GetHeader(); + } + + IReadOnlyList IGCInfo.GetSlotLifetimes(IGCInfoHandle gcInfoHandle) + { + IGCInfoDecoder handle = AssertCorrectHandle(gcInfoHandle); + return handle.GetSlotLifetimes(); + } + private static IGCInfoDecoder AssertCorrectHandle(IGCInfoHandle gcInfoHandle) { if (gcInfoHandle is not IGCInfoDecoder handle) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/IGCInfoDecoder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/IGCInfoDecoder.cs index 279992fb4cd0dc..6a9c6d41d28467 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/IGCInfoDecoder.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/IGCInfoDecoder.cs @@ -18,5 +18,8 @@ internal interface IGCInfoDecoder : IGCInfoHandle uint GetCalleePoppedArgumentsSize() => 0; IReadOnlyList GetInterruptibleRanges(); + IReadOnlyList GetSafePoints(); IReadOnlyList EnumerateLiveSlots(uint instructionOffset, GcSlotEnumerationOptions options); + GCInfoHeader GetHeader(); + IReadOnlyList GetSlotLifetimes(); } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/X86/GCInfo.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/X86/GCInfo.cs index 568469664ce059..08181382f30e8d 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/X86/GCInfo.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/X86/GCInfo.cs @@ -821,6 +821,15 @@ private static IEnumerable EnumerateSingleRegs() yield return RegMask.EDI; // ESP is intentionally excluded -- it's never a live GC ref holder. } + + GCInfoHeader IGCInfoDecoder.GetHeader() + => throw new NotSupportedException("x86 GC info header projection for the expanded IGCInfo surface is not yet implemented."); + + IReadOnlyList IGCInfoDecoder.GetSafePoints() + => throw new NotSupportedException("x86 GC info safe-point enumeration for the expanded IGCInfo surface is not yet implemented."); + + IReadOnlyList IGCInfoDecoder.GetSlotLifetimes() + => throw new NotSupportedException("x86 GC info slot lifetime enumeration for the expanded IGCInfo surface is not yet implemented."); } /// diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ISOSDacInterface.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ISOSDacInterface.cs index 6cafe023802c0e..dd2ce0fbbacb3c 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ISOSDacInterface.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ISOSDacInterface.cs @@ -1271,3 +1271,76 @@ int GetStressLogMemoryRanges( DacComNullableByRef ppEnum); } + +// GCInfo data structures for ISOSDacInterface18 + +[StructLayout(LayoutKind.Sequential)] +public struct SOSCodeRange +{ + public uint BeginOffset; + public uint EndOffset; +} + +[StructLayout(LayoutKind.Sequential)] +public struct SOSGCInfoHeader +{ + public uint SizeOf; + public uint GcInfoVersion; + public uint CodeSize; + public uint PrologSize; + public uint StackBaseRegister; + public uint SizeOfStackParameterArea; + public int IsVarArg; + public int WantsReportOnlyLeaf; + public int HasTailCalls; + public int GSCookieIsPresent; + public int GSCookieStackSlot; + public uint GSCookieValidRangeStart; + public uint GSCookieValidRangeEnd; + public int PSPSymIsPresent; + public int PSPSymStackSlot; + public int GenericsInstContextIsPresent; + public int GenericsInstContextStackSlot; + public uint GenericsInstContextKind; +} + +[StructLayout(LayoutKind.Sequential)] +public struct SOSGCSlotLifetime +{ + public uint BeginOffset; + public uint EndOffset; + public int IsRegister; + public uint RegisterNumber; + public int SpOffset; + public uint BaseRegister; + public uint GcFlags; +} + +[GeneratedComInterface] +[Guid("3dccf95b-bca2-40ee-8b83-d8d7574a1df0")] +public unsafe partial interface ISOSDacInterface18 +{ + [PreserveSig] + int GetGCInfoHeader(ClrDataAddress ip, SOSGCInfoHeader* header); + + [PreserveSig] + int GetGCInfoInterruptibleRanges( + ClrDataAddress ip, + uint count, + [In, Out, MarshalUsing(CountElementName = nameof(count))] SOSCodeRange[]? ranges, + uint* pNeeded); + + [PreserveSig] + int GetGCInfoSafePoints( + ClrDataAddress ip, + uint count, + [In, Out, MarshalUsing(CountElementName = nameof(count))] uint[]? offsets, + uint* pNeeded); + + [PreserveSig] + int GetGCInfoSlotLifetimes( + ClrDataAddress ip, + uint count, + [In, Out, MarshalUsing(CountElementName = nameof(count))] SOSGCSlotLifetime[]? lifetimes, + uint* pNeeded); +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs index 68a99b6e5a268d..291b22fd55dbb9 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs @@ -33,7 +33,7 @@ public sealed unsafe partial class SOSDacImpl : ISOSDacInterface, ISOSDacInterface2, ISOSDacInterface3, ISOSDacInterface4, ISOSDacInterface5, ISOSDacInterface6, ISOSDacInterface7, ISOSDacInterface8, ISOSDacInterface9, ISOSDacInterface10, ISOSDacInterface11, ISOSDacInterface12, ISOSDacInterface13, ISOSDacInterface14, ISOSDacInterface15, - ISOSDacInterface16, ISOSDacInterface17 + ISOSDacInterface16, ISOSDacInterface17, ISOSDacInterface18 { private const uint DefaultAppDomainId = 1; @@ -7385,4 +7385,191 @@ int ISOSDacInterface17.GetStressLogMemoryRanges(DacComNullableByRefSizeOf = (uint)sizeof(SOSGCInfoHeader); + header->GcInfoVersion = h.Version; + header->CodeSize = h.CodeSize; + header->PrologSize = h.PrologSize; + header->StackBaseRegister = h.StackBaseRegister; + header->SizeOfStackParameterArea = h.SizeOfStackParameterArea; + header->IsVarArg = h.IsVarArg ? 1 : 0; + header->WantsReportOnlyLeaf = h.WantsReportOnlyLeaf ? 1 : 0; + header->HasTailCalls = h.HasTailCalls ? 1 : 0; + header->GSCookieIsPresent = h.GSCookie.HasValue ? 1 : 0; + header->GSCookieStackSlot = h.GSCookie?.SpOffset ?? 0; + header->GSCookieValidRangeStart = h.GSCookieValidRangeStart; + header->GSCookieValidRangeEnd = h.GSCookieValidRangeEnd; + header->PSPSymIsPresent = h.PSPSym.HasValue ? 1 : 0; + header->PSPSymStackSlot = h.PSPSym?.SpOffset ?? 0; + header->GenericsInstContextIsPresent = h.GenericsInstContext.HasValue ? 1 : 0; + header->GenericsInstContextStackSlot = h.GenericsInstContext?.SpOffset ?? 0; + header->GenericsInstContextKind = (uint)h.GenericsInstContextKind; + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + + return hr; + } + + int ISOSDacInterface18.GetGCInfoInterruptibleRanges( + ClrDataAddress ip, + uint count, + SOSCodeRange[]? ranges, + uint* pNeeded) + { + int hr = HResults.S_OK; + try + { + if (pNeeded is null) + return HResults.E_POINTER; + + *pNeeded = 0; + + (Contracts.IGCInfoHandle handle, Contracts.IGCInfo gcInfo) = ResolveGCInfo(ip); + IReadOnlyList interruptibleRanges = gcInfo.GetInterruptibleRanges(handle); + + *pNeeded = (uint)interruptibleRanges.Count; + + if (ranges is not null) + { + uint toWrite = Math.Min(count, (uint)interruptibleRanges.Count); + for (uint i = 0; i < toWrite; i++) + { + ranges[i] = new SOSCodeRange + { + BeginOffset = interruptibleRanges[(int)i].StartOffset, + EndOffset = interruptibleRanges[(int)i].EndOffset, + }; + } + hr = toWrite < (uint)interruptibleRanges.Count ? HResults.S_FALSE : HResults.S_OK; + } + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + + return hr; + } + + int ISOSDacInterface18.GetGCInfoSafePoints( + ClrDataAddress ip, + uint count, + uint[]? offsets, + uint* pNeeded) + { + int hr = HResults.S_OK; + try + { + if (pNeeded is null) + return HResults.E_POINTER; + + *pNeeded = 0; + + (Contracts.IGCInfoHandle handle, Contracts.IGCInfo gcInfo) = ResolveGCInfo(ip); + IReadOnlyList safePoints = gcInfo.GetSafePoints(handle); + + *pNeeded = (uint)safePoints.Count; + + if (offsets is not null) + { + uint toWrite = Math.Min(count, (uint)safePoints.Count); + for (uint i = 0; i < toWrite; i++) + { + offsets[i] = safePoints[(int)i]; + } + hr = toWrite < (uint)safePoints.Count ? HResults.S_FALSE : HResults.S_OK; + } + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + + return hr; + } + + int ISOSDacInterface18.GetGCInfoSlotLifetimes( + ClrDataAddress ip, + uint count, + SOSGCSlotLifetime[]? lifetimes, + uint* pNeeded) + { + int hr = HResults.S_OK; + try + { + if (pNeeded is null) + return HResults.E_POINTER; + + *pNeeded = 0; + + (Contracts.IGCInfoHandle handle, Contracts.IGCInfo gcInfo) = ResolveGCInfo(ip); + IReadOnlyList allLifetimes = gcInfo.GetSlotLifetimes(handle); + + *pNeeded = (uint)allLifetimes.Count; + + if (lifetimes is not null) + { + uint toWrite = Math.Min(count, (uint)allLifetimes.Count); + for (uint i = 0; i < toWrite; i++) + { + Contracts.GCSlotLifetime s = allLifetimes[(int)i]; + lifetimes[i] = new SOSGCSlotLifetime + { + BeginOffset = s.BeginOffset, + EndOffset = s.EndOffset, + IsRegister = s.IsRegister ? 1 : 0, + RegisterNumber = s.RegisterNumber, + SpOffset = s.SpOffset, + BaseRegister = s.BaseRegister, + GcFlags = s.GcFlags, + }; + } + hr = toWrite < (uint)allLifetimes.Count ? HResults.S_FALSE : HResults.S_OK; + } + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + + return hr; + } + + #endregion ISOSDacInterface18 }