Skip to content

Reduce raw global / typeinfo usage in cDAC Legacy interfaces#129260

Merged
max-charlamb merged 2 commits into
mainfrom
removeGlobalUsageInLegacyInterfaces
Jun 16, 2026
Merged

Reduce raw global / typeinfo usage in cDAC Legacy interfaces#129260
max-charlamb merged 2 commits into
mainfrom
removeGlobalUsageInLegacyInterfaces

Conversation

@max-charlamb

@max-charlamb max-charlamb commented Jun 10, 2026

Copy link
Copy Markdown
Member

Note

This PR description was AI-generated by GitHub Copilot.

Refactors the Microsoft.Diagnostics.DataContractReader.Legacy project to stop reading runtime globals and typeinfo by string key, routing them through typed contract accessors instead. Behavior-preserving: each refactor moves an existing read down one layer (from Legacy into the corresponding Contracts/*_1 impl) without changing what bytes get read.

Changes

1. ILoader.GetLookupTables owns the ModuleLookupMap.TableData offset

Adds a TableDataOffset field to the existing ModuleLookupTables record struct, populated by Loader_1.GetLookupTables. SOSDacImpl.GetModuleData no longer reads raw typeinfo via _target.GetTypeInfo(DataType.ModuleLookupMap) and instead consumes the offset from the struct, going through a small ReadMapBase helper that null-guards the table pointer.

This removes the only direct GetTypeInfo call in the Legacy project.

2. ILoader.GetAppDomain for the global AppDomain pointer

Adds a simple GetAppDomain() accessor on ILoader that reads and dereferences the AppDomain global pointer. All Legacy call sites that previously did ReadGlobalPointer + ReadPointer inline now call this single method.

RuntimeDomainInfo struct and GetDomainInfo() were removed per review feedback. DefaultADID was removed from the data contract -- Legacy call sites use a local const uint DefaultAppDomainId = 1 instead.

3. IRuntimeTypeSystem.GetWellKnownMethodTable for runtime singleton MTs

Introduces a WellKnownMethodTable enum (Object, String, Array, Exception, Free, Canon) and a GetWellKnownMethodTable(kind) accessor on IRuntimeTypeSystem. RuntimeTypeSystem_1 implements it with a safe TryReadGlobalPointer + TryReadPointer pair so callers (e.g. GetUsefulGlobals during early load notifications) get TargetPointer.Null when the runtime has not yet initialized a given global, matching the prior lazy-read behavior.

Refactors the Legacy project to use the new accessor:

  • SOSDacImpl: removes the _stringMethodTable / _objectMethodTable lazy fields; GetObjectData routes through the contract; GetUsefulGlobals reads all 5 well-known MTs via the contract.
  • DacDbiImpl: IsExceptionObject (Exception MT) and EnumerateTypeParameters (Canon MT).
  • Dbi/Helpers/HeapWalk: ctor (Free MT).

4. DAC: Remove SystemDomain special-casing

GetAppDomainStoreData now reports systemDomain = 0. The SystemDomain guard in GetAppDomainData, GetAssemblyList, and GetAppDomainName has been removed -- these methods now always treat the address as an AppDomain.

Important

Breaking behavior change: DacpAppDomainStoreData::systemDomain will now be NULL. SOS consumers that assume systemDomain is always valid need to be updated. The companion diagnostics PR handles this: dotnet/diagnostics#5878

Testing

All cDAC unit tests pass (2493 passed, 16 skipped). Test updates:

  • ClrDataTaskTests.GetCurrentAppDomain registers the ILoader contract.
  • DacDbiImplTests.IsExceptionObject now mocks IRuntimeTypeSystem.GetWellKnownMethodTable instead of setting up the raw ExceptionMethodTable global.

Out of scope

Several additional Legacy raw-global sites (e.g. DacNotificationFlags, StressLog, FeatureCOMInterop, SOSBreakingChangeVersion, TlsIndexBase/OffsetOfCurrentThreadInfo) remain and can be addressed in follow-up PRs where the contract-side surface is justified by callers beyond a single SOS API.

@github-actions

This comment has been minimized.

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

Refactors the cDAC Legacy implementation to reduce direct raw reads of runtime globals / typeinfo by routing those reads through typed contract APIs in the Abstractions/Contracts layer.

Changes:

  • Adds new contract accessors/types to centralize reading of domain globals (ILoader.GetDomainInfo) and well-known singleton MethodTables (IRuntimeTypeSystem.GetWellKnownMethodTable).
  • Moves ModuleLookupMap.TableData offset calculation into ILoader.GetLookupTables via a new TableDataOffset field on ModuleLookupTables.
  • Updates Legacy (SOS/IXCLRData*/DBI/heap walk) call sites and unit tests to consume the new contract-based APIs.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/native/managed/cdac/tests/UnitTests/DacDbiImplTests.cs Updates tests to mock GetWellKnownMethodTable instead of setting up raw globals.
src/native/managed/cdac/tests/UnitTests/ClrDataTaskTests.cs Updates test target setup to provide globals required by ILoader.GetDomainInfo and registers the Loader contract.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.IXCLRDataProcess.cs Replaces raw AppDomain global reads with Loader.GetDomainInfo().DefaultAppDomain.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs Removes remaining Legacy GetTypeInfo(ModuleLookupMap) usage and routes domain/MT reads via contracts.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/Helpers/HeapWalk.cs Uses GetWellKnownMethodTable(Free) instead of reading FreeObjectMethodTable global directly.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs Routes domain id / appdomain / exception+canon MT reads through the new contract accessors.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataTask.cs Uses Loader.GetDomainInfo().DefaultAppDomain for GetCurrentAppDomain.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs Uses Loader.GetDomainInfo().DefaultAppDomain for frame AppDomain/method-instance construction.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataAppDomain.cs Uses Loader.GetDomainInfo().DefaultAppDomainId for AppDomain unique id.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs Implements GetWellKnownMethodTable with safe TryRead* global dereferencing.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Loader_1.cs Adds GetDomainInfo and populates ModuleLookupTables.TableDataOffset.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs Introduces WellKnownMethodTable enum + GetWellKnownMethodTable API on the abstraction.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ILoader.cs Extends ModuleLookupTables with TableDataOffset and adds RuntimeDomainInfo + GetDomainInfo.

Comment thread src/native/managed/cdac/tests/UnitTests/DacDbiImplTests.cs
@github-actions

This comment has been minimized.

Copilot AI review requested due to automatic review settings June 10, 2026 23:26
@github-actions

This comment has been minimized.

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

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

Comment thread docs/design/datacontracts/Loader.md Outdated
Comment thread docs/design/datacontracts/Loader.md Outdated
Copilot AI review requested due to automatic review settings June 12, 2026 17:39
@max-charlamb max-charlamb force-pushed the removeGlobalUsageInLegacyInterfaces branch from 4b91585 to f8464a3 Compare June 12, 2026 17:39
@github-actions

This comment has been minimized.

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

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

Comment thread src/coreclr/debug/daccess/request.cpp
max-charlamb added a commit to dotnet/diagnostics that referenced this pull request Jun 14, 2026
> [!NOTE]
> This PR was created with assistance from GitHub Copilot.

The runtime will begin reporting `systemDomain` as NULL (0) from
`GetAppDomainStoreData` (see dotnet/runtime#129260). This PR guards all
SOS code that previously assumed `systemDomain` was always valid,
matching the existing pattern used for `SharedDomain`.

**Affected call sites:**

- **`DumpDomain`** -- wraps SystemDomain info section in null guard;
guards the single-domain label path
- **`FindAppDomain`** -- guards the "System Domain" label comparison
- **`GCHandleStatsForDomains::Init`** -- conditionally counts and adds
SystemDomain to the domain array
- **`ModuleFromName` (util.cpp)** -- conditionally adds SystemDomain to
domain array
- **`GetDomainList` (util.cpp)** -- conditionally adds SystemDomain to
domain list

Two existing call sites were already safe: `SaveModules` (has `!= 0`
check) and `EEHeapCommand.cs` (`PrintAppDomain` already null-checks).

---------

Co-authored-by: Max Charlamb <maxcharlamb@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add ILoader.GetDomainInfo and route domain global reads through it
- Move ModuleLookupMap TableData offset into ILoader.GetLookupTables
- Add IRuntimeTypeSystem.GetWellKnownMethodTable for runtime singleton MTs
- Document RuntimeDomainInfo, WellKnownMethodTable, and TableDataOffset
- Address PR feedback on GetDomainInfo and naming
@max-charlamb max-charlamb force-pushed the removeGlobalUsageInLegacyInterfaces branch from f8464a3 to d602046 Compare June 15, 2026 20:47
@github-actions

This comment has been minimized.

@max-charlamb

Copy link
Copy Markdown
Member Author

/ba-g known CI infra issues. SOSTests same baseline across DAC and cDAC due to known issues

@max-charlamb max-charlamb added the NO-MERGE The PR is not ready for merge yet (see discussion for detailed reasons) label Jun 16, 2026
max-charlamb pushed a commit to max-charlamb/diagnostics that referenced this pull request Jun 16, 2026
PR dotnet#5878 added null guards in SOS for the SystemDomain (which dotnet/runtime#129260 makes nullable on non-framework runtimes), but did not update the corresponding test scripts. The 'System Domain:' / 'LowFrequencyHeap' / 'HighFrequencyHeap' / 'Total size:' lines from the SystemDomain section are no longer printed on non-framework, so wrap them in IFDEF:DESKTOP.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit to max-charlamb/diagnostics that referenced this pull request Jun 16, 2026
PR dotnet#5878 added null guards in SOS for the SystemDomain (which dotnet/runtime#129260 makes nullable on non-framework runtimes), but did not update the corresponding test scripts. The 'System Domain:' line from the SystemDomain section is no longer printed on non-framework, so:

- GCTests.script (EEHeap) and OtherCommands.script (DumpDomain): wrap the 'System Domain:' VERIFY in IFDEF:DESKTOP. Other heap lines (LowFrequencyHeap/HighFrequencyHeap/Total size) still match against Domain 1 so they stay outside.

- GCPOH.script (EEHeap): delete the 'System Domain:' VERIFY entirely; GCPOHTests is already skipped on Desktop (POH didn't exist before .NET 5).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 16, 2026 18:15
@max-charlamb max-charlamb force-pushed the removeGlobalUsageInLegacyInterfaces branch from d602046 to 8595521 Compare June 16, 2026 18:15

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

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

Comment thread src/coreclr/debug/daccess/request.cpp
Comment thread src/coreclr/debug/daccess/request.cpp
@github-actions

This comment has been minimized.

Address Jan's review feedback:
- Remove RuntimeDomainInfo struct and GetDomainInfo(), replace with
  simple ILoader.GetAppDomain() returning TargetPointer
- Remove DefaultADID from data contract globals, use local constant
  (value 1) in Legacy call sites
- Remove SystemDomain special-casing from DAC request.cpp
  (GetAppDomainStoreData, GetAppDomainData, GetAssemblyList,
  GetAppDomainName)
- Report systemDomain as NULL from GetAppDomainStoreData
- Fix unused arrayTypeInfo variable in GetObjectData
- Update Loader.md documentation to match

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@max-charlamb max-charlamb force-pushed the removeGlobalUsageInLegacyInterfaces branch from 8595521 to 4a8e236 Compare June 16, 2026 19:39
@github-actions

Copy link
Copy Markdown
Contributor

Copilot Code Review

Holistic Assessment

Motivation: Well-justified refactoring that eliminates raw ReadGlobalPointer/GetTypeInfo usage from the Legacy project, routing them through typed contract accessors (ILoader.GetAppDomain(), IRuntimeTypeSystem.GetWellKnownMethodTable(), ModuleLookupTables.TableDataOffset). This reduces code duplication and establishes a cleaner boundary between Legacy consumers and the data contract layer.

Approach: Sound approach. Each refactoring moves one existing raw read into the contract layer without changing the semantics. The WellKnownMethodTable enum + TryRead pattern is a good fit for the early-init scenarios where globals may not yet be populated. The SystemDomain removal is coordinated with a companion diagnostics PR.

Summary: ✅ LGTM. The refactoring is behavior-preserving (with the documented breaking change for systemDomain = 0 handled by dotnet/diagnostics#5878). Code is clean, tests are updated, and the remaining open review comments about GetAppDomainData ignoring addr are inaccurate — addr IS used for module enumeration at line 128.


Detailed Findings

Detailed Findings

✅ Contract Accessor Design — Correct and defensive

RuntimeTypeSystem_1.GetWellKnownMethodTable correctly uses TryReadGlobalPointer + TryReadPointer with TargetPointer.Null fallback, matching the prior lazy-read behavior where globals might not be initialized during early load notifications. The switch expression with ArgumentOutOfRangeException for unknown enum values is idiomatic.

ILoader.GetAppDomain() — Clean extraction

The extraction of the ReadGlobalPointer + ReadPointer pair into a single GetAppDomain() accessor eliminates repetition across 10+ call sites in the Legacy project. The internal reuse via ((ILoader)this).GetAppDomain() in GetRootAssembly and GetAppDomainFriendlyName keeps the contract consistent.

ReadMapBase Helper — Good null-guard pattern

The static local ReadMapBase helper in GetModuleData properly null-guards the table pointer before adding the offset, returning default (0) for null tables. This is correct and matches the C++ DAC behavior where null map pointers would similarly resolve to 0.

✅ SystemDomain Removal — Consistent across C++ and cDAC

The removal of SystemDomain guards in request.cpp (GetAppDomainData, GetAssemblyList, GetAppDomainName) matches the cDAC changes. GetAppDomainStoreData now reports systemDomain = 0 in both paths. The breaking change is properly documented and the companion diagnostics PR handles SOS consumers.

GetUsefulGlobals Behavior — Improved resilience

Previously, if any one global was missing, ALL values would be zeroed and an error HResult returned. Now, each GetWellKnownMethodTable call independently returns TargetPointer.Null (→ 0) for missing globals while returning S_OK for the overall call. This is actually better behavior and still consistent with the DEBUG validation logic (lines 4526–4533) which accepts either hr or hrLocal being S_OK.

✅ Test Updates — Adequate

ClrDataTaskTests.GetCurrentAppDomain correctly adds the ILoader contract registration. DacDbiImplTests.IsExceptionObject correctly switches to mocking IRuntimeTypeSystem.GetWellKnownMethodTable rather than setting up raw globals, with the now-unused exceptionMT parameter properly removed from CreateDacDbiWithExceptionMT.

💡 GetObjectData — Repeated uncached MT lookups

Lines 3333 and 3340 call GetWellKnownMethodTable(String) and GetWellKnownMethodTable(Object) on every GetObjectData invocation. Previously these were cached in Lazy<TargetPointer> fields. In a heap walk scenario calling GetObjectData for thousands of objects, this adds two TryReadGlobalPointer + TryReadPointer round-trips per object. In diagnostics context this is unlikely to be a meaningful regression, but noting it as a potential follow-up optimization if profiling shows overhead.

Note

This review was generated by GitHub Copilot.

Generated by Code Review for issue #129260 · ● 52.5M ·

max-charlamb added a commit to dotnet/diagnostics that referenced this pull request Jun 16, 2026
Follow-up to #5878.

dotnet/runtime#129260 makes `GetAppDomainStoreData::systemDomain`
nullable (NULL on non-framework runtimes). #5878 added the null guards
in SOS so the SystemDomain section is no longer emitted in that case,
but the test scripts were not updated to match.

This PR removes/guards the now-conditional `System Domain:` VERIFY in
the three affected scripts:

- `OtherCommands.script` (`DumpDomain`) and `GCTests.script` (`EEHeap`):
wrap the `System Domain:` VERIFY in `IFDEF:DESKTOP`. The other heap
lines (`LowFrequencyHeap`/`HighFrequencyHeap`/`Total size:`) still match
against `Domain 1`'s heaps so they stay outside.
- `GCPOH.script` (`EEHeap`): delete the `System Domain:` VERIFY
entirely. `GCPOHTests` is already skipped on Desktop (`config.IsDesktop`
short-circuit; POH was introduced in .NET 5), so an `IFDEF:DESKTOP`
block would be dead code.

Edit: See the following
[runtime-diagnostics](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1466895&view=results)
run showing success

Co-authored-by: Max Charlamb <maxcharlamb@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@max-charlamb max-charlamb removed the NO-MERGE The PR is not ready for merge yet (see discussion for detailed reasons) label Jun 16, 2026
@max-charlamb

Copy link
Copy Markdown
Member Author

@max-charlamb max-charlamb merged commit 45d015d into main Jun 16, 2026
28 of 108 checks passed
@max-charlamb max-charlamb deleted the removeGlobalUsageInLegacyInterfaces branch June 16, 2026 19:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants