WIP: Add ISOSDacInterface18 for GCInfo inspection APIs#129761
Draft
max-charlamb wants to merge 3 commits into
Draft
WIP: Add ISOSDacInterface18 for GCInfo inspection APIs#129761max-charlamb wants to merge 3 commits into
max-charlamb wants to merge 3 commits into
Conversation
Contributor
|
Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR introduces a new SOS-DAC COM interface (ISOSDacInterface18) intended to expose GCInfo inspection APIs (header, safe points, interruptible ranges, and slot lifetimes), alongside expanding the managed cDAC IGCInfo contract and decoder surface to project the same data.
Changes:
- Add
ISOSDacInterface18(IDL + prebuilt headers + C# GeneratedComInterface) and wire it up through DACQueryInterface. - Implement GCInfo inspection entrypoints in the native DAC (
ClrDataAccess) usingGcInfoDecoder/GcInfoDumper, and extend the managed GCInfo decoder/contracts with header/safe point/lifetime APIs. - Extend GCInfo platform traits to include PSP symbol encoding base across architectures; add placeholder (E_NOTIMPL) stubs in the managed legacy SOS DAC implementation.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs | Adds ISOSDacInterface18 exposure but currently returns E_NOTIMPL for all new methods. |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ISOSDacInterface.cs | Defines GCInfo structs and ISOSDacInterface18 managed COM declarations. |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/X86/GCInfo.cs | Implements newly added IGCInfoDecoder members on x86 by throwing NotSupportedException. |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/PlatformTraits/RISCV64GCInfoTraits.cs | Adds PSP symbol stack-slot encoding base. |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/PlatformTraits/LoongArch64GCInfoTraits.cs | Adds PSP symbol stack-slot encoding base. |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/PlatformTraits/InterpreterGCInfoTraits.cs | Adds PSP symbol stack-slot encoding base. |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/PlatformTraits/IGCInfoTraits.cs | Extends traits interface with PSP_SYM_STACK_SLOT_ENCBASE. |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/PlatformTraits/ARMGCInfoTraits.cs | Adds PSP symbol stack-slot encoding base. |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/PlatformTraits/ARM64GCInfoTraits.cs | Adds PSP symbol stack-slot encoding base. |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/PlatformTraits/AMD64GCInfoTraits.cs | Adds PSP symbol stack-slot encoding base. |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/IGCInfoDecoder.cs | Expands decoder interface to include header/safe points/lifetimes. |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/GCInfoDecoder.cs | Adds decoding/projection for header/safe points/lifetimes (includes a correctness issue in safe-point lookup and a potentially expensive lifetime algorithm). |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/GCInfo_1.cs | Plumbs new IGCInfo contract methods to the decoder implementation. |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IGCInfo.cs | Adds public contract types (header/lifetime records) and new IGCInfo methods. |
| src/coreclr/pal/prebuilt/inc/sospriv.h | Updates prebuilt SOS-DAC header with new interface and structs. |
| src/coreclr/pal/prebuilt/idl/sospriv_i.cpp | Adds GUID definitions for new SOS-DAC interfaces/enums. |
| src/coreclr/inc/sospriv.idl | Adds IDL definitions for GCInfo structs and ISOSDacInterface18. |
| src/coreclr/debug/daccess/request.cpp | Implements ClrDataAccess::GetGCInfo* methods and supporting helpers. |
| src/coreclr/debug/daccess/dacimpl.h | Extends ClrDataAccess inheritance and declares ISOSDacInterface17/18 methods. |
| src/coreclr/debug/daccess/daccess.cpp | Extends QueryInterface to support ISOSDacInterface17/18. |
Comment on lines
937
to
942
| EnsureDecodedTo(DecodePoints.InterruptibleRanges); | ||
|
|
||
| if (_gcVersion < 4) | ||
| codeOffset--; | ||
|
|
||
| uint normBreakOffset = TTraits.NormalizeCodeOffset(codeOffset); |
Comment on lines
+1008
to
+1033
| for (uint offset = 0; offset <= _codeLength; offset++) | ||
| { | ||
| while (interruptibleRangeIndex < _interruptibleRanges.Count && | ||
| _interruptibleRanges[interruptibleRangeIndex].EndOffset <= offset) | ||
| { | ||
| interruptibleRangeIndex++; | ||
| } | ||
|
|
||
| bool isInterruptible = | ||
| interruptibleRangeIndex < _interruptibleRanges.Count && | ||
| _interruptibleRanges[interruptibleRangeIndex].StartOffset <= offset && | ||
| offset < _interruptibleRanges[interruptibleRangeIndex].EndOffset; | ||
| bool isSafePoint = safePoints.Contains(offset); | ||
|
|
||
| HashSet<uint> currentLiveSlots = []; | ||
| EnumerateLiveSlots( | ||
| offset, | ||
| new GcSlotEnumerationOptions | ||
| { | ||
| IsActiveFrame = !isSafePoint, | ||
| IsExecutionAborted = !isSafePoint && !isInterruptible, | ||
| IsParentOfFuncletStackFrame = false, | ||
| SuppressUntrackedSlots = true, | ||
| ReportFPBasedSlotsOnly = false, | ||
| }, | ||
| (uint slotIndex, GcSlotDesc slot, uint gcFlags) => |
Comment on lines
+7354
to
+7358
| int ISOSDacInterface18.GetGCInfoHeader(ClrDataAddress ip, SOSGCInfoHeader* header) | ||
| { | ||
| // TODO(cdac): Implement ISOSDacInterface18 GCInfo support. | ||
| return HResults.E_NOTIMPL; | ||
| } |
Comment on lines
+539
to
+596
| 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, | ||
| ReturnKind: _returnKind); | ||
| } | ||
|
|
||
| public IReadOnlyList<uint> GetSafePoints() | ||
| { | ||
| EnsureDecodedTo(DecodePoints.InterruptibleRanges); | ||
|
|
||
| List<uint> safePoints = new((int)_numSafePoints); | ||
| int bitOffset = _safePointBitOffset; | ||
| uint numBitsPerOffset = CeilOfLog2(TTraits.NormalizeCodeOffset(_codeLength)); | ||
| for (uint i = 0; i < _numSafePoints; i++) | ||
| { | ||
| uint offset = TTraits.DenormalizeCodeOffset((uint)_reader.ReadBits((int)numBitsPerOffset, ref bitOffset)); | ||
| if (_gcVersion < 4) | ||
| offset++; | ||
|
|
||
| safePoints.Add(offset); | ||
| } | ||
|
|
||
| return safePoints; | ||
| } |
646c0fb to
5310bc9
Compare
5310bc9 to
2f24300
Compare
Comment on lines
+7422
to
+7444
| if (pNeeded is null) | ||
| return HResults.E_POINTER; | ||
|
|
||
| *pNeeded = 0; | ||
|
|
||
| (Contracts.IGCInfoHandle handle, Contracts.IGCInfo gcInfo) = ResolveGCInfo(ip); | ||
| IReadOnlyList<Contracts.InterruptibleRange> 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; | ||
| } |
Comment on lines
+7395
to
+7398
| header->GSCookieIsPresent = h.GSCookie.HasValue ? 1 : 0; | ||
| header->GSCookieStackSlot = h.GSCookie?.SpOffset ?? 0; | ||
| header->GSCookieValidRangeStart = h.GSCookieValidRangeStart; | ||
| header->GSCookieValidRangeEnd = h.GSCookieValidRangeEnd; |
Comment on lines
+45
to
+62
| /// <summary>Header fields decoded from the GC info stream for a method.</summary> | ||
| 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, | ||
| uint? ReturnKind); | ||
|
|
Comment on lines
+939
to
+941
| if (_gcVersion < 4) | ||
| codeOffset--; | ||
|
|
Comment on lines
+556
to
+576
| 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, | ||
| ReturnKind: _returnKind); |
Comment on lines
+1008
to
+1023
| for (uint offset = 0; offset <= _codeLength; offset++) | ||
| { | ||
| while (interruptibleRangeIndex < _interruptibleRanges.Count && | ||
| _interruptibleRanges[interruptibleRangeIndex].EndOffset <= offset) | ||
| { | ||
| interruptibleRangeIndex++; | ||
| } | ||
|
|
||
| bool isInterruptible = | ||
| interruptibleRangeIndex < _interruptibleRanges.Count && | ||
| _interruptibleRanges[interruptibleRangeIndex].StartOffset <= offset && | ||
| offset < _interruptibleRanges[interruptibleRangeIndex].EndOffset; | ||
| bool isSafePoint = safePoints.Contains(offset); | ||
|
|
||
| HashSet<uint> currentLiveSlots = []; | ||
| EnumerateLiveSlots( |
Comment on lines
+7372
to
+7379
| int ISOSDacInterface18.GetGCInfoHeader(ClrDataAddress ip, SOSGCInfoHeader* header) | ||
| { | ||
| int hr = HResults.S_OK; | ||
| try | ||
| { | ||
| if (header is null) | ||
| return HResults.E_POINTER; | ||
|
|
2f24300 to
36cbff5
Compare
36cbff5 to
487271d
Compare
Add ISOSDacInterface18 with GCInfo inspection APIs (cDAC-only): - GetGCInfoHeader - GetGCInfoInterruptibleRanges - GetGCInfoSafePoints - GetGCInfoRegisterLifetimes - GetGCInfoStackSlotLifetimes Add efficient single-pass GetSlotLifetimes to GCInfoDecoder that walks the chunk transition table once instead of calling EnumerateLiveSlots at every byte offset (O(numTransitions) vs O(codeLength * numSlots)). GetRegisterLifetimes/GetStackSlotLifetimes now delegate to GetSlotLifetimes with filtering. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
487271d to
f01e817
Compare
This was referenced Jun 24, 2026
- Replace GetGCInfoRegisterLifetimes + GetGCInfoStackSlotLifetimes with single GetGCInfoSlotLifetimes returning unified SOSGCSlotLifetime struct - Eagerly decode safe points in DecodeSafePoints (cached in _safePoints list) - FindSafePoint now searches cached list instead of re-reading bit stream - Reorder GetSafePoints next to GetInterruptibleRanges in all interfaces - Remove _gcVersion < 4 guard from safe point decoding - ISOSDacInterface18 now has 4 methods: Header, InterruptibleRanges, SafePoints, SlotLifetimes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment on lines
+686
to
+693
| // 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); | ||
| } |
Comment on lines
+7358
to
+7361
| Contracts.CodeBlockHandle? cbh = eman.GetCodeBlockHandle(ip.ToTargetCodePointer(_target)); | ||
| if (cbh is null) | ||
| throw new ArgumentException("No code block found for the given IP"); | ||
|
|
Comment on lines
+1337
to
+1341
| int GetGCInfoSlotLifetimes( | ||
| ClrDataAddress ip, | ||
| uint count, | ||
| [In, Out, MarshalUsing(CountElementName = nameof(count))] SOSGCSlotLifetime[]? lifetimes, | ||
| uint* pNeeded); |
Comment on lines
+45
to
+60
| /// <summary>Header fields decoded from the GC info stream for a method.</summary> | ||
| 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); |
This was referenced Jun 25, 2026
GetSlotLifetimes was returning only untracked slots for methods without interruptible ranges (safe-point-only methods). Now decodes the per-safe-point live slot bitmaps and builds lifetime ranges from consecutive safe points where slots transition between live/dead. Supports both direct bitmap and indirect (RLE) safe point encodings. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
bbd22fa to
7d34213
Compare
Comment on lines
+7357
to
+7360
| Contracts.IExecutionManager eman = _target.Contracts.ExecutionManager; | ||
| Contracts.CodeBlockHandle? cbh = eman.GetCodeBlockHandle(ip.ToTargetCodePointer(_target)); | ||
| if (cbh is null) | ||
| throw new ArgumentException("No code block found for the given IP"); |
Comment on lines
+7431
to
+7443
| 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; | ||
| } |
Comment on lines
+7472
to
+7480
| 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; | ||
| } |
Comment on lines
+7509
to
+7527
| 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; | ||
| } |
Comment on lines
4
to
9
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Diagnostics; | ||
| using System.Linq; | ||
| using System.Numerics; | ||
| using ILCompiler.Reflection.ReadyToRun; |
Comment on lines
130
to
135
| private uint _numSafePoints; | ||
| private uint _numInterruptibleRanges; | ||
| private List<InterruptibleRange> _interruptibleRanges = []; | ||
| private List<uint> _safePoints = []; | ||
| private int _safePointBitOffset; | ||
|
|
Comment on lines
337
to
338
| _safePointBitOffset = _bitOffset; | ||
| // skip over safe point data | ||
| uint numBitsPerOffset = CeilOfLog2(TTraits.NormalizeCodeOffset(_codeLength)); |
Comment on lines
45
to
+105
| @@ -54,7 +99,10 @@ public interface IGCInfo : IContract | |||
| uint GetSizeOfStackParameterArea(IGCInfoHandle handle) => throw new NotImplementedException(); | |||
| uint GetCalleePoppedArgumentsSize(IGCInfoHandle handle) => throw new NotImplementedException(); | |||
| IReadOnlyList<InterruptibleRange> GetInterruptibleRanges(IGCInfoHandle handle) => throw new NotImplementedException(); | |||
| IReadOnlyList<uint> GetSafePoints(IGCInfoHandle handle) => throw new NotImplementedException(); | |||
| IReadOnlyList<LiveSlot> EnumerateLiveSlots(IGCInfoHandle handle, uint instructionOffset, GcSlotEnumerationOptions options) => throw new NotImplementedException(); | |||
| GCInfoHeader GetHeader(IGCInfoHandle handle) => throw new NotImplementedException(); | |||
| IReadOnlyList<GCSlotLifetime> GetSlotLifetimes(IGCInfoHandle handle) => throw new NotImplementedException(); | |||
Comment on lines
+7372
to
+7495
| int ISOSDacInterface18.GetGCInfoHeader(ClrDataAddress ip, SOSGCInfoHeader* header) | ||
| { | ||
| int hr = HResults.S_OK; | ||
| try | ||
| { | ||
| if (header is null) | ||
| return HResults.E_POINTER; | ||
|
|
||
| *header = default; | ||
|
|
||
| (Contracts.IGCInfoHandle handle, Contracts.IGCInfo gcInfo) = ResolveGCInfo(ip); | ||
| Contracts.GCInfoHeader h = gcInfo.GetHeader(handle); | ||
|
|
||
| header->SizeOf = (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<Contracts.InterruptibleRange> 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<uint> 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) | ||
| { |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note
This PR was created with assistance from GitHub Copilot.
Summary
Add
ISOSDacInterface18COM interface exposing GCInfo inspection APIs through the cDAC, enabling SOS and diagnostic tools to query GC information for managed methods.APIs
GetGCInfoHeader- Get GC info header for a code rangeGetGCInfoInterruptibleRanges- Enumerate interruptible code rangesGetGCInfoSafePoints- Enumerate GC safe pointsGetGCInfoRegisterLifetimes- Enumerate register-based GC slot lifetimesGetGCInfoStackSlotLifetimes- Enumerate stack-based GC slot lifetimesStatus
Work in progress - needs build verification and e2e testing.