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
}