Skip to content

WIP: Update SOS for unified ISOSDacInterface18 GCInfo API#5898

Draft
max-charlamb wants to merge 7 commits into
mainfrom
maxcharlamb/isosdacinterface18-gcinfo
Draft

WIP: Update SOS for unified ISOSDacInterface18 GCInfo API#5898
max-charlamb wants to merge 7 commits into
mainfrom
maxcharlamb/isosdacinterface18-gcinfo

Conversation

@max-charlamb

Copy link
Copy Markdown
Member

Note

This content was generated with AI assistance.

Update SOS !GCInfo command to use unified ISOSDacInterface18 API with timeline output format.

Depends on: dotnet/runtime#129761

Add gcinfoprovider.h/.cpp with GCInfoData struct that queries
ISOSDacInterface18 for GC info header, interruptible ranges,
slot lifetimes, and safe points. Both !GCInfo and !U -gcinfo
try this path first, falling back to the legacy raw-blob +
GcInfoDecoder/GcInfoDumper path for older runtimes.

- Add ISOSDacInterface18 and supporting structs (SOSGCInfoHeader,
  SOSCodeRange, SOSGCSlotLifetime) to prebuilt sospriv headers
- Add -legacy flag to !GCInfo for A/B comparison
- DumpToOutput matches legacy format exactly: split display for
  interruptible (transitions) vs non-interruptible (safe points)
- Sync gcinfodecoder.cpp, gcinfodumper.cpp, gcinfotypes.h from
  runtime to support .NET 11 GC info format

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ISOSDacInterface18 * This,
CLRDATA_ADDRESS ip,
ULONG count,
unsigned int *offsets,

@leculver leculver Jun 26, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a long instead of unsigned int? Or raw addresses instead of offsets (ulong)? The offsets are from the start of the ip, but on some platforms functions have disconnected ranges, like funclets on arm64. I think currently (and probably for the forseeable future), these funclets always occur after the main function, and fairly close by (e.g. within 1 uint), but is this design future-proof?

While it may be overkill, I think if this were ulong *addresses instead of uint *offsets that would fully future-proof this no matter how to JIT is implemented in the future.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current GCInfo uses 32 bit offsets heavily, if this is an issue in the future, we can create a separate API.

Comment thread src/SOS/Strike/strike.cpp Outdated

@leculver leculver left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a great start. I know it's a draft, but I had a few comments to help steer. Thanks!

Comment thread src/SOS/Strike/strike.cpp
Comment thread src/SOS/Strike/strike.cpp
@max-charlamb
max-charlamb force-pushed the maxcharlamb/isosdacinterface18-gcinfo branch 6 times, most recently from 74a13ca to 863d5c4 Compare June 30, 2026 00:10
Add ISOSDacInterface18 consumption in !GCInfo command with fallback
to the existing raw-bytes decoding path when the interface is not
available (non-cDAC scenarios).

The new path queries the cDAC for:
- GC info header (code size, prolog, stack base, etc.)
- Interruptible ranges
- Safe points
- Register lifetimes
- Stack slot lifetimes

Also adds a basic GCInfo verification to OtherCommands.script.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@max-charlamb
max-charlamb force-pushed the maxcharlamb/isosdacinterface18-gcinfo branch from 863d5c4 to 491a060 Compare July 1, 2026 16:51
…output

- Replace SOSGCRegisterLifetime + SOSGCStackSlotLifetime with unified
  SOSGCSlotLifetime struct
- Replace GetGCInfoRegisterLifetimes + GetGCInfoStackSlotLifetimes with
  single GetGCInfoSlotLifetimes
- Remove ReturnKind from SOSGCInfoHeader (matches runtime change)
- TryDumpGCInfoViaInterface18 now outputs legacy-compatible timeline format:
  header fields, untracked slots, then chronological interruptible/slot events
- Update OtherCommands.script GCInfo verification to match timeline output

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@max-charlamb
max-charlamb force-pushed the maxcharlamb/isosdacinterface18-gcinfo branch from 491a060 to 26a0263 Compare July 1, 2026 19:06
max-charlamb added a commit to dotnet/runtime that referenced this pull request Jul 23, 2026
> [!NOTE]
> This content was generated with AI assistance.

## ISOSDacInterface18 for GCInfo inspection APIs

Add `ISOSDacInterface18` with 4 COM methods exposing GC info data
through the cDAC for the SOS `!GCInfo` command:

- `GetGCInfoHeader` -- header fields (version, code size, prolog, stack
base register, GS cookie, PSP sym, generics context)
- `GetGCInfoInterruptibleRanges` -- code regions where GC can interrupt
execution
- `GetGCInfoSafePoints` -- specific offsets with point-in-time GC slot
liveness
- `GetGCInfoSlotLifetimes` -- unified slot lifetime ranges (register +
stack combined via `SOSGCSlotLifetime` with `IsRegister` discriminator)

### IGCInfo contract changes

- Add `GetHeader`, `GetSafePoints`, `GetSlotLifetimes` to
`IGCInfo`/`IGCInfoDecoder`
- Consolidate `GetStackBaseRegister` and `GetSizeOfStackParameterArea`
into `GCInfoHeader` (accessed via `GetHeader()`). These fields require
the same decode depth (`DecodePoints.ReversePInvoke`) as the rest of the
header, so there's no performance benefit to keeping them separate.
- Keep `GetCodeLength` as a standalone method -- it only decodes to
`DecodePoints.CodeLength`, which is significantly cheaper than the full
header decode. Used on the hot path by
`EEJitManager`/`InterpreterJitManager`/`ReadyToRunJitManager` for method
size lookups.
- Keep `GetCalleePoppedArgumentsSize` as a standalone method -- it
returns 0 via a default interface method on non-x86 platforms without
any decoding. Only x86 reads the `ArgCount` header field.
- Eagerly decode safe points in `DecodeSafePoints`, stored denormalized
- Single-pass chunk decoder for interruptible slot lifetimes
(O(transitions), not O(codeLength))
- Safe-point-only methods emit `[safePointOffset, safePointOffset+1)`
per live slot per safe point
- Implement x86 GCInfo support
(`GetHeader`/`GetSafePoints`/`GetSlotLifetimes`)
- Add `GenericsContextKind.None` as default enum value
- Fix `GSCookieValidRange` -- only set when GSCookie is present
- Native DAC returns `E_NOINTERFACE` for `ISOSDacInterface18` (cDAC-only
interface)

### Bug fix: ARM thumb bit in relative offset computation

`EEJitManager.GetMethodInfo` and `InterpreterJitManager.GetMethodInfo`
computed `relativeOffset` as `jittedCodeAddress.Value -
codeStart.Value`. On ARM (Thumb-2), `TargetCodePointer.Value` includes
the thumb bit (bit 0 = 1), but `codeStart` (a `TargetPointer` from the
nibble map) does not. This produced a relative offset that was off by 1,
causing `FindSafePoint` and `EnumerateLiveSlots` to look up the wrong
offset.

Fixed by stripping the thumb bit via
`CodePointerUtils.AddressFromCodePointer` before computing the
difference. `ReadyToRunJitManager` was already doing this correctly.

### Companion PR

- dotnet/diagnostics#5898 -- SOS `!GCInfo` command updated to use
`ISOSDacInterface18` with legacy-compatible timeline output format

---------

Co-authored-by: Max Charlamb <maxcharlamb@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb and others added 4 commits July 23, 2026 13:37
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3ebf2a2d-da27-41b5-9f45-4558240207ec
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3ebf2a2d-da27-41b5-9f45-4558240207ec
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3ebf2a2d-da27-41b5-9f45-4558240207ec
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants