Add cDAC no-fallback test leg to runtime-diagnostics pipeline#126752
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new “cDAC no-fallback” diagnostics test leg and a corresponding runtime switch so diagnostics tests can run with cDAC without delegating unimplemented APIs to the legacy DAC.
Changes:
Entrypoints.cs: honorsCDAC_NO_FALLBACK=1by passingnullas the legacy DAC implementation toSOSDacImpl.runtime-diagnostics.yml: adds acDAC_no_fallbackjob that runs diagnostics tests withuseCdac: trueandCDAC_NO_FALLBACK=1.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/native/managed/cdac/mscordaccore_universal/Entrypoints.cs |
Adds an env-var controlled path to disable legacy DAC fallback in CreateSosInterface. |
eng/pipelines/runtime-diagnostics.yml |
Introduces a third diagnostics test leg to run cDAC tests with fallback disabled. |
e9ae9e6 to
d2f2469
Compare
This comment has been minimized.
This comment has been minimized.
d2f2469 to
f6db3bc
Compare
f6db3bc to
c4cc40d
Compare
c4cc40d to
4dc6bf8
Compare
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Revert AuxiliarySymbolInfo.Address type change (unrelated to this PR) - Restore CanFallback() before _legacyModulePointer check so fallback attempts are always logged Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The managed cDAC reads this field with ReadCodePointerField() which asserts the declared type is CodePointer. Using T_POINTER causes Debug.Assert crashes in checked builds and incorrect address comparisons on ARM32 (missing THUMB_CODE tag bit). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This comment was marked as outdated.
This comment was marked as outdated.
|
/ba-g no logical changes since last clean test run |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataModule.cs:64
LegacyFallbackHelper.CanFallback()is called unconditionally at the start ofGetInterface(). In no-fallback mode this will log an “Allowed/Blocked fallback” even when_legacyModulePointer == 0, or when the requestediidis notIID_IMetaDataImport(i.e., no legacy QI/delegation is attempted). Consider moving theCanFallback()call so it only runs when_legacyModulePointer != 0andiid == IID_IMetaDataImport(right beforeMarshal.QueryInterface).
if (!LegacyFallbackHelper.CanFallback() || _legacyModulePointer == 0)
return CustomQueryInterfaceResult.NotHandled;
// Legacy DAC implementation of IXCLRDataModule handles QIs for IMetaDataImport by creating and
// passing out an implementation of IMetaDataImport. Note that it does not do COM aggregation.
// It simply returns a completely separate object. See ClrDataModule::QueryInterface in task.cpp
if (iid == IID_IMetaDataImport && Marshal.QueryInterface(_legacyModulePointer, iid, out ppv) >= 0)
return CustomQueryInterfaceResult.Handled;
| } | ||
| int ISOSDacInterface.GetAssemblyLocation(ClrDataAddress assembly, int count, char* location, uint* pNeeded) | ||
| => _legacyImpl is not null ? _legacyImpl.GetAssemblyLocation(assembly, count, location, pNeeded) : HResults.E_NOTIMPL; | ||
| => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetAssemblyLocation(assembly, count, location, pNeeded) : HResults.E_NOTIMPL; |
There was a problem hiding this comment.
LegacyFallbackHelper.CanFallback() is evaluated before checking _legacyImpl is not null, so in CDAC_NO_FALLBACK=1 mode this will log an “Allowed/Blocked fallback” even when there is no legacy implementation to actually delegate to (and adds overhead for the null case). Consider reordering the condition to check _legacyImpl first and only call CanFallback() when delegation is possible.
| => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetAssemblyLocation(assembly, count, location, pNeeded) : HResults.E_NOTIMPL; | |
| => _legacyImpl is not null && LegacyFallbackHelper.CanFallback() ? _legacyImpl.GetAssemblyLocation(assembly, count, location, pNeeded) : HResults.E_NOTIMPL; |
| internal static bool CanFallback( | ||
| [CallerMemberName] string name = "", | ||
| [CallerFilePath] string file = "", | ||
| [CallerLineNumber] int line = 0) | ||
| { | ||
| if (!s_noFallback) | ||
| return true; | ||
|
|
There was a problem hiding this comment.
The PR description mentions CanFallback() is [AggressiveInlining] in normal mode, but the method currently has no MethodImplOptions.AggressiveInlining attribute. If the intent is to keep the fast-path overhead minimal (this helper is called at many delegation sites), consider adding the attribute (or update the PR description if this is no longer intended).
> [!NOTE] > This PR was generated with the assistance of GitHub Copilot. ## Summary Implement `IXCLRDataModule.GetMethodDefinitionByToken` in the cDAC `ClrDataModule` wrapper instead of delegating entirely to the legacy DAC. Remove it from the no-fallback allowlist and add newly-exposed downstream methods. ## Changes **`ClrDataModule.cs`** — Replaced the one-line legacy delegation stub with a full cDAC implementation that: 1. Calls the legacy implementation first (if available) for DEBUG parity validation 2. Validates the token is an `mdtMethodDef` (throws `ArgumentException` → `E_INVALIDARG`) 3. Creates a `ClrDataMethodDefinition` with the token, module address, and legacy method definition 4. Includes `#if DEBUG` `ValidateHResult` check against the legacy result **`LegacyFallbackHelper.cs`** — Removed `GetMethodDefinitionByToken` from the no-fallback allowlist. Added four `IXCLRDataMethodDefinition` methods that are now exposed as blocked fallbacks because `GetMethodDefinitionByToken` returns a cDAC-managed wrapper instead of a legacy object: - `StartEnumInstances` - `GetName` - `SetCodeNotification` - `HasClassOrMethodInstantiation` These methods are not yet implemented in the cDAC and must remain on the allowlist until they are. ## Pattern Follows the same pattern used by `EnumMethodDefinitionByName` (line 302) which already creates `ClrDataMethodDefinition` objects in the same way. The C++ legacy implementation (`task.cpp:2215-2251`) validates the token type, then calls `ClrDataMethodDefinition::NewFromModule` which simply creates a `ClrDataMethodDefinition` — the cDAC version mirrors this behavior. ## Motivation This enables removing `GetMethodDefinitionByToken` from the cDAC no-fallback allowlist (#126752). --------- Co-authored-by: Max Charlamb <maxcharlamb@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Note
This PR description was generated with the assistance of GitHub Copilot.
Summary
Add a granular, per-method allowlist (
LegacyFallbackHelper) that controls which delegation-only APIs may fall back to the legacy DAC whenCDAC_NO_FALLBACK=1is set. This enables selective no-fallback testing — blocking fallback for most APIs while allowing specific APIs that are known to not yet be implemented in the cDAC.Wire this into the runtime-diagnostics CI pipeline using the
-noFallbackflag from dotnet/diagnostics#5806.All fallback attempts (both allowed and blocked) are logged to stderr with method name, file, and line number for capture by the diagnostics test infrastructure.
Changes
LegacyFallbackHelper.cs — Granular fallback control
New static helper that every delegation-only call site invokes via
CanFallback(). Uses[CallerMemberName],[CallerFilePath], and[CallerLineNumber]to identify the call site.CDAC_NO_FALLBACKunset): Always returnstrue(singleboolcheck,[AggressiveInlining])CDAC_NO_FALLBACK=1): Checks method name against aHashSet<string>allowlist and file name against a file-level allowlistPer-method allowlist:
EnumMemoryRegionsGetInterfaceGetMethodDefinitionByTokenIsTrackedTypeTraverseLoaderHeapFile-level allowlist:
DacDbiImpl.csEntrypoints.cs — Simplified creation
Both
CreateSosInterfaceandCreateDacDbiInterfacenow follow the same pattern: the legacy implementation is always passed through, andLegacyFallbackHelper.CanFallback()at each call site decides whether to delegate. Removedprevent_release,noFallbackenv var check, and null-legacy-ref logic.13 Legacy wrapper files — Instrumented delegation sites
All 296 delegation-only methods across all legacy wrapper files now call
LegacyFallbackHelper.CanFallback():SOSDacImpl.cs(12 methods)SOSDacImpl.IXCLRDataProcess.cs(38 methods,Flush()intentionally excluded — cache management)ClrDataModule.cs(29 methods + IMetaDataImport QI)DacDbiImpl.cs(122 methods)ClrDataTask.cs,ClrDataExceptionState.cs,ClrDataFrame.cs,ClrDataValue.cs,ClrDataTypeInstance.cs,ClrDataMethodInstance.cs,ClrDataStackWalk.cs,ClrDataProcess.csCI Pipeline —
-noFallbackflagUpdated
runtime-diag-job.ymlto accept anoFallbackparameter that passes-noFallbackto the diagnostics build script. ThecDAC_no_fallbackleg inruntime-diagnostics.ymlnow usesnoFallback: trueinstead of settingCDAC_NO_FALLBACKas a pipeline-level environment variable. The-noFallbackflag (from dotnet/diagnostics#5806) properly:DOTNET_ENABLE_CDAC=1andCDAC_NO_FALLBACK=1on the debugger processCDAC_NO_FALLBACK_TESTINGto skipClrStack -itests (ICorDebug not implemented in cDAC)Stderr logging
Every fallback attempt is logged to stderr in the format:
The diagnostics test infrastructure (
ProcessRunner) captures stderr and routes it to xunit test output withSTDERROR:prefix, making fallback usage visible in test results.Test Results
With
CDAC_NO_FALLBACK=1and the current allowlist, running the full SOS test suite against a private runtime build:Motivation
The existing cDAC test leg always has the legacy DAC as a fallback, so unimplemented APIs are silently handled. The granular no-fallback mode makes gaps visible per-method, helping track progress toward full cDAC coverage while keeping tests green for known-deferred APIs.