Skip to content

Support loading cDAC from SOS installations#5874

Open
hoyosjs wants to merge 6 commits into
dotnet:mainfrom
hoyosjs:juhoyosa/bundle-cdac
Open

Support loading cDAC from SOS installations#5874
hoyosjs wants to merge 6 commits into
dotnet:mainfrom
hoyosjs:juhoyosa/bundle-cdac

Conversation

@hoyosjs

@hoyosjs hoyosjs commented Jun 11, 2026

Copy link
Copy Markdown
Member

Bundle the cDAC into diagnostics tool packages and implement interim loading policy for DAC/cDAC:

  • .NET 11+ defaults to cDAC unless DOTNET_ENABLE_CDAC is set to allow for runtime-diagnostics pipeline to still do dac/cdac diffing.
  • Bundle the cDAC into diagnostics tool packages and implement interim loading policy for DAC/cDAC as specified above/
  • Runtimes command now has a tri-state UseCDac (null= default policy described above, true=force, false=never) with --usecdac true|false|policy**
  • DataTarget implements ICLRContractLocator and stores resolved contract descriptor address for cDAC initialization
  • sos-packaging.props cleanup to allow any arch packaging; PackageWithCDac=true by default on main branch so all platforms (win-x64/x86/arm64, linux-*, osx-*) bundle cDAC alongside sos.

max-charlamb added a commit to dotnet/runtime that referenced this pull request Jun 17, 2026
…ntract (#129456)

> [!NOTE]
> This PR was authored with assistance from GitHub Copilot.

## Summary

Fixes a cDAC `GetCodeHeaderData` failure that surfaced as `Unable to get
codeHeader information` when SOS ran `!clru` against an IL stub
MethodDesc on **Windows x86** with cDAC enabled (the new default
behavior on .NET 11 introduced by
[dotnet/diagnostics#5874](dotnet/diagnostics#5874)).

The CI failure that motivated this is
`SOSMethodTests.VarargPInvokeInteropMD` on x86 in dotnet/diagnostics:
`!IP2MD` returned the IL stub MethodDesc correctly, but the immediate
follow-up `!clru <MD>` printed only `Unable to get codeHeader
information`. x64 / arm64 / .NET 8/9/10 were unaffected.

## Root cause

x86 uses a fundamentally different GC info encoding from every other
architecture: the legacy bit-packed `InfoHdr` byte-stream format from
[`src/coreclr/vm/gc_unwind_x86.inl`](https://github.com/dotnet/runtime/blob/main/src/coreclr/vm/gc_unwind_x86.inl)
and
[`src/coreclr/inc/gcdecoder.cpp`](https://github.com/dotnet/runtime/blob/main/src/coreclr/inc/gcdecoder.cpp)
(`USE_GC_INFO_DECODER` is defined for every target except x86, see
`eetwain.h:34`).

The cDAC `GCInfo` contract registered IGCInfo implementations for
X64/Arm64/Arm/LoongArch64/RiscV64 but **not** X86 -- so on x86 it fell
through to `default(GCInfo)` and threw `NotImplementedException` from
the interface's default `DecodePlatformSpecificGCInfo`. Any SOS path
that needed method size on x86 (`!clru`, `GetCodeHeaderData`,
`GetMethodRegionInfo`) failed.

## Approach

The cDAC already had a substantial x86 `InfoHdr` decoder under
`Contracts/StackWalk/Context/X86/GCInfoDecoding/`, used by the x86 stack
walker. Rather than write a parallel decoder, this PR **relocates** that
existing decoder under the `GCInfo` contract so there is **one canonical
x86 GC info implementation** shared between SOS callers and the stack
walker -- mirroring how the other architectures' decoders are
structured.

## Changes

* **Move** `Contracts/StackWalk/Context/X86/GCInfoDecoding/*` →
`Contracts/GCInfo/X86/*` (6 files, tracked as renames). Rename namespace
`StackWalkHelpers.X86` → `GCInfoHelpers.X86`.
* **Rename** the moved class `GCInfo` → `X86GCInfo` to avoid collision
with the empty `Contracts.GCInfo` IGCInfo fallback struct.
* Make `relativeOffset` ctor arg optional. Implement `IGCInfoDecoder`
directly on `X86GCInfo`: `GetCodeLength` / `GetStackBaseRegister` /
`GetSizeOfStackParameterArea` are wired up. `GetInterruptibleRanges` and
`EnumerateLiveSlots` throw `NotSupportedException` (future work, needed
for `!gcroot` / `!clrstack -l` etc.).
* Add `GCInfoX86_1` IGCInfo for x86; register it in
`CoreCLRContracts.cs` for `RuntimeInfoArchitecture.X86`.
* Update `ExecutionManagerCore.GetStackParameterSize` to delegate to
`IGCInfo.GetSizeOfStackParameterArea` (one source of truth).
* `X86Unwinder` continues to construct `X86GCInfo` directly because it
needs offset-bound state (`IsInProlog` / `IsInEpilog` / `PushedArgSize`)
not exposed through `IGCInfoDecoder`.

## Tests

* New `VarargPInvoke_GetCodeHeaderDataForILStub_ReturnsMethodSize`
regression test in `cdac/tests/DumpTests/StackWalkDumpTests.cs` --
asserts the IL stub path returns S_OK with non-zero `MethodSize`. Runs
against the existing cdac-dump-helix `windows_x86` matrix on every PR
(no pipeline changes needed).
* Existing 2509 cDAC unit tests still pass.
* Validated end-to-end against `SOSMethodTests.VarargPInvokeInteropMD`
x86 .NET 11prev6 cDAC: 4/4 pass after this fix; failed before.

## Docs

* `docs/design/datacontracts/GCInfo.md` -- intro now reflects partial
x86 support; `GetSizeOfStackParameterArea` API documented; per-method
status notes for x86.
* `docs/design/datacontracts/StackWalk.md` -- x86 section points at the
consolidated decoder location and explains how it's shared.

## Out of scope (future work)

* `GetInterruptibleRanges` and `EnumerateLiveSlots` for x86. The
underlying transition data is decoded but the adapter to the cDAC
`IGCInfoDecoder` shape is not wired up yet. This is what's needed to
unblock `!gcroot`, `!clrstack -l`, `!pe` on x86 cDAC. The two
pre-existing `[SkipOnArch("x86", "GCInfo decoder does not support
x86")]` markers in `StackReferenceDumpTests.cs` should become removable
once that lands.

---------

Co-authored-by: Max Charlamb <maxcharlamb@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@hoyosjs hoyosjs marked this pull request as ready for review June 17, 2026 18:20
@hoyosjs hoyosjs requested a review from a team as a code owner June 17, 2026 18:20
Copilot AI review requested due to automatic review settings June 17, 2026 18:20

Copilot AI 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.

Pull request overview

This PR updates SOS/tool packaging and runtime-loading behavior to bundle and preferentially load the cDAC (mscordaccore_universal) from diagnostics tool installations, with a default policy for .NET 11+ and a new tri-state user control surface.

Changes:

  • Add cDAC discovery/loading paths across native SOS, SOS.Hosting, and ClrMD runtime creation (including contract descriptor plumbing via ICLRContractLocator).
  • Introduce an asset/layout resolver (IHostAssetResolver + SOSPackageLayout) so tools consistently locate native binaries (including bundled cDAC) regardless of host.
  • Update packaging/build logic to optionally bundle cDAC into tool packages and adjust tests/scripts for current SOS output differences.

Reviewed changes

Copilot reviewed 34 out of 34 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/tests/SOS.UnitTests/SOS.cs Updates test skip conditions (notably Win-x86) for tracked regression coverage.
src/tests/SOS.UnitTests/Scripts/StackAndOtherTests.script Makes stack verification tolerant to shifted source line numbers.
src/SOS/Strike/util.cpp Requests cDAC-capable CLR data process in the main SOS load path; whitespace cleanup.
src/SOS/Strike/platform/runtimeimpl.h Adds cDAC path/process members and policy/helpers to native Runtime.
src/SOS/Strike/platform/runtimeimpl.cpp Implements cDAC path resolution (next to SOS), policy, IXCLRDataProcess creation, and contract descriptor lookup.
src/SOS/Strike/platform/datatarget.h Extends DataTarget to implement ICLRContractLocator.
src/SOS/Strike/platform/datatarget.cpp Implements ICLRContractLocator::GetContractDescriptor and QueryInterface wiring.
src/SOS/Strike/clrma/managedanalysis.cpp Improves tracing when runtime-based DAC retrieval fails and falls back.
src/SOS/SOS.Package/SOS.Symbol.Package.csproj Adds single-RID packing support via conditional symbol inclusion.
src/SOS/SOS.Package/SOS.Package.csproj Ensures dotnet run for manifest generation uses the active configuration.
src/SOS/SOS.Package/GenerateManifest/Directory.Build.targets Shields file-based app build by preventing repo targets import (intentionally empty).
src/SOS/SOS.Package/GenerateManifest/Directory.Build.props Shields file-based app build by preventing repo props import (intentionally empty).
src/SOS/SOS.InstallHelper/SOS.InstallHelper.csproj Links shared SOSPackageLayout implementation into InstallHelper.
src/SOS/SOS.InstallHelper/InstallHelper.cs Switches native/managed source path resolution to SOSPackageLayout.
src/SOS/SOS.Hosting/SOSPackageLayout.cs Defines package-relative layout rules for native vs managed SOS assets.
src/SOS/SOS.Hosting/SOSLibrary.cs Uses IHostAssetResolver for native binary location; updates ISOSModule semantics/comments.
src/SOS/SOS.Hosting/SOS.Hosting.csproj Removes InstallHelper project reference (no longer needed).
src/SOS/SOS.Hosting/RuntimeWrapper.cs Prefers cDAC for IXCLRDataProcess path; refactors DAC/cDAC load handling.
src/SOS/SOS.Hosting/HostAssetResolver.cs New global service that resolves native binaries directory and cDAC path.
src/SOS/SOS.Extensions/HostServices.cs Removes unnecessary unsafe modifier; continues to expose ISOSModule data.
src/sos-packaging.props Reworks packaging inputs/conditions, adds SingleTargetRidPackage/PackageWithCDac semantics.
src/Microsoft.Diagnostics.ExtensionCommands/Host/RuntimesCommand.cs Replaces previous flags with tri-state `--usecdac true
src/Microsoft.Diagnostics.ExtensionCommands/Host/CommandFormatHelpers.cs Updates settings output to reflect tri-state cDAC policy.
src/Microsoft.Diagnostics.DebugServices/ISettingsService.cs Replaces prior cDAC flags with bool? UseCDac and documents policy semantics.
src/Microsoft.Diagnostics.DebugServices/IRuntime.cs Clarifies DAC vs cDAC path responsibilities and nullability for cDAC.
src/Microsoft.Diagnostics.DebugServices/IHostAssetResolver.cs New contract for resolving host-native asset locations (including cDAC).
src/Microsoft.Diagnostics.DebugServices.Implementation/ServiceManager.cs Removes SOS.InstallHelper from default assemblies list for extension loading.
src/Microsoft.Diagnostics.DebugServices.Implementation/RuntimeProvider.cs Adds signature-verification override for the trusted bundled cDAC path.
src/Microsoft.Diagnostics.DebugServices.Implementation/Runtime.cs Implements cDAC policy, uses host resolver for cDAC path, and prefers cDAC for ClrMD runtime creation.
src/Microsoft.Diagnostics.DebugServices.Implementation/Host.cs Updates settings storage to bool? UseCDac.
src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.props Optionally packages cDAC next to dbgshim when enabled.
eng/Versions.props Defaults PackageWithCDac=true on preview branches unless overridden.
eng/InstallNativePackages.targets Downloads/stages cDAC transport pack content when PackageWithCDac=true.
eng/CdacPackageItems.props Removes prior per-RID cDAC transport pack itemization (now in targets).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

ExtDbgOut("GetCDacFilePath: dladdr failed to locate the sos module\n");
return nullptr;
}
strcpy_s(szSOSModulePath.GetPtr(), MAX_LONGPATH, info.dli_fname);
Comment on lines +24 to +28
<ItemGroup Condition="'$(PackageWithCDac)' == 'true'">
<PackageDownload Include="runtime.$(TargetRid).Microsoft.DotNet.Cdac.Transport"
Version="[$(runtimewinx64MicrosoftDotNetCdacTransportVersion)]" />
<PackageSourceFiles Include="$(NuGetPackageRoot)runtime.$(TargetRid).microsoft.dotnet.cdac.transport\$(runtimewinx64MicrosoftDotNetCdacTransportVersion)\runtimes\$(TargetRid)\native\*" />
</ItemGroup>
Comment on lines +241 to +245
// Prefer the cDAC for the data-access (IXCLRDataProcess) path when the runtime policy
// selects it (GetCDacFilePath returns non-null); fall back to the in-box DAC otherwise.
// The ICorDebug/DBI path (CreateCorDebugProcess) always uses the in-box DAC. The flags
// parameter is retained for the native IRuntime contract but no longer consulted here.
if (_cdacDataProcess == IntPtr.Zero)
public bool UseContractReader { get; set; }

public bool ForceUseContractReader { get; set; }
public bool? UseCDac { get; set; }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: I'd prefer an enum here, something like:

enum CDacLoadPolicy
{
    Default = 0,
    UseCDac,
    UseLegacyDac
}

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.

I don't mind in this layer. Do you think this should be surfaced to the command?

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.

4 participants