Conversation
|
Tagging subscribers to this area: @steveisok, @dotnet/dotnet-diag |
There was a problem hiding this comment.
Pull Request Overview
This PR implements the GetCodeHeaderData method and adds GC info decoder functionality to the contract DAC (cDAC) system. It introduces a new GCInfo contract with platform-specific implementations for decoding garbage collection metadata from native code.
Key Changes:
- Implements
GetCodeHeaderDatamethod with proper type safety and error handling - Adds a comprehensive GCInfo contract with decoder capabilities for AMD64, ARM64, and ARM architectures
- Extends ExecutionManager with new methods for method region info, JIT type detection, and method descriptor lookup
Reviewed Changes
Copilot reviewed 29 out of 29 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| SOSDacImpl.cs | Implements GetCodeHeaderData with GCInfo decoder integration and debug assertions |
| ISOSDacInterface.cs | Updates interface signature and adds supporting data structures |
| CachingContractRegistry.cs | Registers the new GCInfo contract factory |
| GCInfo contract files | Complete GCInfo decoder implementation with platform-specific traits |
| ExecutionManager files | Extends with method region info, JIT type detection, and entrypoint resolution |
| Data structure files | Adds UnwindInfo, PortableEntryPoint, and related helper classes |
Comments suppressed due to low confidence (1)
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/PlatformTraits/IGCInfoTraits.cs:1
- Corrected spelling of 'ENCBACE' to 'ENCBASE'.
// Licensed to the .NET Foundation under one or more agreements.
...ed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/GCInfoDecoder.cs
Outdated
Show resolved
Hide resolved
....Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/PlatformTraits/ARMGCInfoTraits.cs
Outdated
Show resolved
Hide resolved
...iagnostics.DataContractReader.Contracts/Contracts/GCInfo/PlatformTraits/ARM64GCInfoTraits.cs
Outdated
Show resolved
Hide resolved
...iagnostics.DataContractReader.Contracts/Contracts/GCInfo/PlatformTraits/AMD64GCInfoTraits.cs
Outdated
Show resolved
Hide resolved
....Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/FrameIterator.cs
Show resolved
Hide resolved
...ractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs
Outdated
Show resolved
Hide resolved
|
|
||
| ## Implementation | ||
|
|
||
| The GCInfo contract has platform specific implementations as GCInfo differs per architecture. With the exception of x86, all platforms have a common encoding scheme with different encoding lengths and normalization functions for data. x86 uses an entirely different scheme which is not currently supported by this contract. |
There was a problem hiding this comment.
How do we handle the interpreter. It also uses the GC info.
There was a problem hiding this comment.
Mirroring how the runtime handles this case, I believe it is acceptable to have a different entrypoint to decode the 'standard platform' code and the interpreter code.
IGCInfoHandle IGCInfo.DecodeGCInfo(TargetPointer gcInfoAddress, uint gcVersion)IGCInfoHandle IGCInfo.DecodeInterpreterGCInfo(TargetPointer gcInfoAddress, uint gcVersion)
I don't believe this can be encapsulated in the current format as the ExecutionManager calls into the GCInfo contract to find the method region info. If the GCInfo contract differentiated between interpreted and jitted code, then it would need to call into ExecutionManager creating a cyclic dependency.
There was a problem hiding this comment.
Shall we use similar arch-specific structure as: src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/{arch}/{arch}Unwinder.cs or bring that to this (single directory) plan?
There was a problem hiding this comment.
I don't have a strong feeling either way. I didn't make /{arch}/ subdirectories here because each platform only has a single file. In the future we may have versions of traits and multiple files. I think it would make sense to have specific directories.
| | Operation | Normalization (Encode) | Denormalization (Decode) | | ||
| | --- | --- | --- | | ||
| | **Stack Base Register** | `reg ^ 0x29` | `reg ^ 0x29` | | ||
| | **Code Length** | `length << 2` | `length >> 2` | | ||
| | **Code Offset** | `offset << 2` | `offset >> 2` | | ||
| | **Stack Slot** | `offset >> 3` | `offset << 3` | | ||
| | **Stack Area Size** | `size >> 3` | `size << 3` | |
There was a problem hiding this comment.
The ARM64 Normalization/Denormalization table has swapped column values for Code Length, Code Offset, Stack Slot, and Stack Area Size.
For Code Length and Code Offset: Normalization should be >> 2 (shift right), Denormalization should be << 2 (shift left).
For Stack Slot and Stack Area Size: Normalization should be >> 3 (shift right), Denormalization should be << 3 (shift left).
The current table shows the operations reversed. The implementation in ARM64GCInfoTraits.cs is correct and matches the native code in src/coreclr/inc/gcinfotypes.h (lines 689-690, 686-687).
| #region Helper Methods | ||
|
|
||
| private static uint CeilOfLog2(ulong value) | ||
| { |
There was a problem hiding this comment.
The CeilOfLog2 method doesn't handle the edge case where value is 0. When value is 0, Math.Log2(0) returns negative infinity, which causes Math.Ceiling to also return negative infinity, and casting to uint is undefined behavior.
This could occur if _codeLength is 0 when DecodeSafePoints is called. Consider adding a check: if (value == 0) return 0; at the beginning of the CeilOfLog2 method, or handle the zero case in DecodeSafePoints before calling CeilOfLog2.
| { | |
| { | |
| if (value == 0) | |
| return 0; |
| data->JITType = JitTypes.TYPE_UNKNOWN; | ||
| data->GCInfo = 0; | ||
| data->MethodStart = 0; | ||
| data->MethodSize = 0; |
There was a problem hiding this comment.
In the non-code-block path, data->HotRegionSize and data->ColdRegionSize are not initialized. Only ColdRegionStart is set to 0. For consistency with the else branch where all three region-related fields are set, these should also be explicitly initialized to 0 in this path.
| data->MethodSize = 0; | |
| data->MethodSize = 0; | |
| data->HotRegionSize = 0; | |
| data->ColdRegionSize = 0; |
| Debug.Assert(rangeStartOffset < rangeStopOffset); | ||
| Debug.Assert(rangeStartOffset >= prevEndOffset); | ||
|
|
||
| lastInterruptibleRangeStopOffsetNormalized = rangeStopOffsetNormalized; |
There was a problem hiding this comment.
The variable prevEndOffset is declared and used in a Debug.Assert (line 307) but is never updated in the loop. It remains 0 throughout. Based on the assertion logic checking that rangeStartOffset >= prevEndOffset, it appears this should be updated at the end of each iteration to track the previous range's end offset. Consider adding: prevEndOffset = rangeStopOffset; after line 309.
| lastInterruptibleRangeStopOffsetNormalized = rangeStopOffsetNormalized; | |
| lastInterruptibleRangeStopOffsetNormalized = rangeStopOffsetNormalized; | |
| prevEndOffset = rangeStopOffset; |
| /// Gets an instance of the GCInfo contract for the target. | ||
| /// </summary> | ||
| public virtual IGCInfo GCInfo => GetContract<IGCInfo>(); | ||
| /// </summary> |
There was a problem hiding this comment.
There's an extra closing XML doc comment tag /// </summary> on line 89 that should be removed. This creates a malformed XML documentation structure where the GCInfo property summary is followed by an orphaned closing tag.
| | `UnwindInfo` | `CountOfUnwindCodes` | Number of unwind codes in the unwind info. Only exists on some platforms | | ||
| | `UnwindInfo` | `UnwindCodeOffset` | Offset of UnwindCodeOffset in the UnwindInfo data struct. Only exists on some platforms | |
There was a problem hiding this comment.
| | `UnwindInfo` | `CountOfUnwindCodes` | Number of unwind codes in the unwind info. Only exists on some platforms | | |
| | `UnwindInfo` | `UnwindCodeOffset` | Offset of UnwindCodeOffset in the UnwindInfo data struct. Only exists on some platforms | |
This is UnwindInfo blob encoding. I do not think it makes sense to have abstracted offsets for parts of it.
There was a problem hiding this comment.
I think the markdown just needs to be re-synced - I don't see any of these new UnwindInfo fields used in the actual implementation:
https://github.com/dotnet/runtime/pull/120275/changes#diff-1bb05a509581b4e0bb5620c1db064b28263ee278ba395861ecab4f96130de85bR622-R627
fork of #119609 with GCInfo decoder
Fixes #124687