Uh oh!
There was an error while loading. Please reload this page.
Add cDAC ObjectiveCMarshal contract and IsTrackedReferenceWithFinalizer API - #125895
Conversation
Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag |
Uh oh!
There was an error while loading. Please reload this page.
noahfalk
left a comment
There was a problem hiding this comment.
LGTM - one refactor suggestion inline
Uh oh!
There was an error while loading. Please reload this page.
> [!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 when `CDAC_NO_FALLBACK=1` is 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 `-noFallback` flag from [dotnet/diagnostics#5806](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. - **Normal mode** (`CDAC_NO_FALLBACK` unset): Always returns `true` (single `bool` check, `[AggressiveInlining]`) - **No-fallback mode** (`CDAC_NO_FALLBACK=1`): Checks method name against a `HashSet<string>` allowlist and file name against a file-level allowlist **Per-method allowlist:** | Method | Reason | |--------|--------| | `EnumMemoryRegions` | Dump creation — cDAC has no memory enumeration implementation | | `GetInterface` | IMetaDataImport QI ([PR #127028](#127028)) | | `GetMethodDefinitionByToken` | IXCLRDataModule — not yet implemented in cDAC | | `IsTrackedType` | GC heap analysis ([PR #125895](#125895)) | | `TraverseLoaderHeap` | Loader heap traversal ([PR #125129](#125129)) | **File-level allowlist:** | File | Reason | |------|--------| | `DacDbiImpl.cs` | Entire DBI/ICorDebug interface (122 methods) — deferred | ### Entrypoints.cs — Simplified creation Both `CreateSosInterface` and `CreateDacDbiInterface` now follow the same pattern: the legacy implementation is always passed through, and `LegacyFallbackHelper.CanFallback()` at each call site decides whether to delegate. Removed `prevent_release`, `noFallback` env 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) - Other wrappers: `ClrDataTask.cs`, `ClrDataExceptionState.cs`, `ClrDataFrame.cs`, `ClrDataValue.cs`, `ClrDataTypeInstance.cs`, `ClrDataMethodInstance.cs`, `ClrDataStackWalk.cs`, `ClrDataProcess.cs` ### CI Pipeline — `-noFallback` flag Updated `runtime-diag-job.yml` to accept a `noFallback` parameter that passes `-noFallback` to the diagnostics build script. The `cDAC_no_fallback` leg in `runtime-diagnostics.yml` now uses `noFallback: true` instead of setting `CDAC_NO_FALLBACK` as a pipeline-level environment variable. The `-noFallback` flag (from [dotnet/diagnostics#5806](dotnet/diagnostics#5806)) properly: - Sets `DOTNET_ENABLE_CDAC=1` and `CDAC_NO_FALLBACK=1` on the debugger process - Defines `CDAC_NO_FALLBACK_TESTING` to skip `ClrStack -i` tests (ICorDebug not implemented in cDAC) ### Stderr logging Every fallback attempt is logged to stderr in the format: ``` [cDAC] Allowed fallback: CreateStackWalk at DacDbiImpl.cs:590 [cDAC] Blocked fallback: SomeMethod at SOSDacImpl.cs:123 ``` The diagnostics test infrastructure (`ProcessRunner`) captures stderr and routes it to xunit test output with `STDERROR:` prefix, making fallback usage visible in test results. ## Test Results With `CDAC_NO_FALLBACK=1` and the current allowlist, running the full SOS test suite against a private runtime build: - **24 passed**, **2 failed** (flaky/pre-existing), **2 skipped** (Linux-only) - **0 blocked fallbacks** ## 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. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb
commented
Apr 21, 2026
We should make sure to remove IsTrackedType from the LegacyFallbackHelper in this PR. |
max-charlamb
commented
Apr 21, 2026
@copilot, please merge from main, fix any issues, then address feedback on this PR. |
rcj1
commented
Apr 21, 2026
@copilot@max-charlamb there is a reason this is not merged, and that is that we cannot have proper testing because the Mac tests are not set up. No action is needed at the moment. |
…dReferenceWithFinalizer APIs Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/fa8e5180-e07a-43c6-b618-58c6b60fd64e Co-authored-by: rcj1 <77995559+rcj1@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds Objective-C interop support to the cDAC reader and legacy SOS DAC bridge by introducing a new ObjectiveCMarshal contract, exposing tracked-reference metadata, and adding unit/dump coverage around tagged memory.
Changes:
- Add a new
IObjectiveCMarshalcontract and wire it through cDAC registration, runtime data descriptors, and legacyISOSDacInterface11implementations. - Extend object/sync-block data readers and test mocks to surface Objective-C tagged memory and tracked-reference-with-finalizer metadata.
- Add macOS-specific dump-test infrastructure, a new ObjectiveCMarshal debuggee, and unit/integration tests for tagged-memory scenarios.
Reviewed changes
Copilot reviewed 25 out of 25 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
src/native/managed/cdac/tests/ObjectiveCMarshalTests.cs | Adds unit tests for GetTaggedMemory contract behavior. |
src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.SyncBlock.cs | Extends mock sync-block/interop info with tagged-memory support. |
src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.Object.cs | Threads tagged-memory data through mock object creation helpers. |
src/native/managed/cdac/tests/DumpTests/ObjectiveCMarshalDumpTests.cs | Adds dump-based tests for tracked objects and tagged memory. |
src/native/managed/cdac/tests/DumpTests/DumpTests.targets | Filters macOS-only debuggees on non-macOS hosts. |
src/native/managed/cdac/tests/DumpTests/Debuggees/ObjectiveCMarshal/Program.cs | Adds a macOS-only debuggee that creates a tracked object and crashes. |
src/native/managed/cdac/tests/DumpTests/Debuggees/ObjectiveCMarshal/ObjectiveCMarshal.csproj | Declares the new debuggee as macOS-only and full-dump capable. |
src/native/managed/cdac/tests/DumpTests/Debuggees/Directory.Build.targets | Propagates MacOnly metadata from debuggee projects. |
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs | Implements ISOSDacInterface11 tagged-memory/tracked-type APIs via cDAC. |
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/MethodTableFlags_1.cs | Adds the tracked-reference-with-finalizer method-table flag. |
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/InteropSyncBlockInfo.cs | Reads tagged-memory pointer from interop sync-block info. |
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/CoreCLRContracts.cs | Registers the new IObjectiveCMarshal contract implementation. |
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs | Exposes IsTrackedReferenceWithFinalizer from method-table flags. |
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ObjectiveCMarshal_1.cs | Implements tagged-memory lookup from an object's sync block. |
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Object_1.cs | Factors sync-block address lookup into IObject. |
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs | Adds public contract API for tracked-reference-with-finalizer checks. |
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IObjectiveCMarshal.cs | Introduces the public Objective-C marshal contract API. |
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IObject.cs | Adds public sync-block address lookup API. |
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs | Exposes ObjectiveCMarshal from the contract registry. |
src/coreclr/vm/syncblk.h | Publishes tagged-memory field offset for cDAC data descriptors. |
src/coreclr/vm/methodtable.h | Marks tracked-reference-with-finalizer flag as cDAC-dependent. |
src/coreclr/vm/datadescriptor/datadescriptor.inc | Exposes tagged-memory field and ObjectiveCMarshal global contract. |
docs/design/datacontracts/RuntimeTypeSystem.md | Documents the new runtime-type-system API. |
docs/design/datacontracts/ObjectiveCMarshal.md | Adds design documentation for the new contract. |
docs/design/datacontracts/Object.md | Documents the new sync-block lookup helper in Object contract. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Copilot encountered an error: Your billing is not configured or you have Copilot licenses from multiple standalone organizations or enterprises. To use premium requests, select a billing entity via the GitHub site, under Settings > Copilot > Features.
🤖 Copilot Code Review — PR #125895Note This review was generated by GitHub Copilot. Holistic AssessmentMotivation: This PR adds a new Approach: The approach is sound and follows established cDAC patterns exactly. A new Summary: ✅ LGTM. The implementation correctly mirrors the legacy C++ DAC behavior ( Detailed Findings✅ Correctness — SOSDacImpl matches legacy DAC semanticsThe new
The ✅ Flag Value — IsTrackedReferenceWithFinalizer = 0x04000000The flag value in ✅ Data Descriptor — Conditional field exposure
✅ Refactoring — GetSyncBlockAddress extractionThe extracted ✅ Tagged Memory Size — Hardcoded 2 * PointerSize
✅ Test Coverage — Comprehensive
💡 Minor — Redundant object header read in TryGetHashCode (non-blocking, follow-up)In ✅ Contract Registration — Properly orderedRegistrations maintain alphabetical ordering in
|
rcj1
commented
May 6, 2026
/ba-g already ran CI |
1 similar comment
rcj1
commented
May 6, 2026
/ba-g already ran CI |
Summary
Implements cDAC APIs for Objective-C interop diagnostics as a new
ObjectiveCMarshalcontract, plus addsIsTrackedReferenceWithFinalizerto theRuntimeTypeSystemcontract. Based on draft PR #118250, adjusted per review feedback (contract renamed,TargetNUIntinstead ofnuint, APIs in dedicated contract).Note
This PR was generated with GitHub Copilot.
Changes
New
ObjectiveCMarshalcDAC ContractIObjectiveCMarshal(Abstractions/Contracts/IObjectiveCMarshal.cs): Interface with a single API:Returns the tagged memory pointer for an Objective-C tracked reference object (or
TargetPointer.Nullif none). Setssizeto2 * pointerSizebytes only on success.ObjectiveCMarshal_1(Contracts/ObjectiveCMarshal_1.cs): Version 1 implementation — reads the sync block from the object header, then extractsInteropSyncBlockInfo.TaggedMemory.ObjectiveCMarshalFactory(Contracts/ObjectiveCMarshalFactory.cs): Contract factory.Registered in
ContractRegistryandCachingContractRegistry.datadescriptor.inc: AddedTaggedMemoryfield toInteropSyncBlockInfotype (under#ifdef FEATURE_OBJCMARSHAL) andCDAC_GLOBAL_CONTRACT(ObjectiveCMarshal, 1)(also guarded).syncblk.h: AddedTaggedMemoryoffset tocdac_data<InteropSyncBlockInfo>under#ifdef FEATURE_OBJCMARSHAL.InteropSyncBlockInfo.cs(Data class): AddedTaggedMemoryproperty usingTryGetValueto handle the optionalFEATURE_OBJCMARSHALfield.IsTrackedReferenceWithFinalizeronRuntimeTypeSystemAdded
IsTrackedReferenceWithFinalizer = 0x04000000toWFLAGS_HIGHenum inMethodTableFlags_1.cs, plus a convenience propertybool IsTrackedReferenceWithFinalizer.Added
IsTrackedReferenceWithFinalizer(TypeHandle)toIRuntimeTypeSystem.csand implemented it inRuntimeTypeSystem_1.cs.SOSDacImpl.cs—ISOSDacInterface11Replaced the stub implementations of
IsTrackedTypeandGetTaggedMemorywith full cDAC implementations:IsTrackedType: usesIRuntimeTypeSystem.IsTrackedReferenceWithFinalizerandIObjectiveCMarshal.GetTaggedMemory; returnsS_OKif tracked,S_FALSEif not,E_INVALIDARGfor null inputs.GetTaggedMemory: usesIObjectiveCMarshal.GetTaggedMemory; returnsS_OKwith address and size if tagged memory exists,S_FALSEotherwise.Both methods include
#if DEBUGassertions against the legacy DAC implementation for validation.Documentation
docs/design/datacontracts/ObjectiveCMarshal.md: New contract documentation.docs/design/datacontracts/RuntimeTypeSystem.md: AddedIsTrackedReferenceWithFinalizerto API listing and pseudocode.Testing
All 1322 existing cDAC unit tests pass. The new contract will be exercised when running on an Apple platform with
FEATURE_OBJCMARSHALenabled.