Reduce raw global / typeinfo usage in cDAC Legacy interfaces#129260
Conversation
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
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.TableDataoffset calculation intoILoader.GetLookupTablesvia a newTableDataOffsetfield onModuleLookupTables. - 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. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
4b91585 to
f8464a3
Compare
This comment has been minimized.
This comment has been minimized.
> [!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
f8464a3 to
d602046
Compare
This comment has been minimized.
This comment has been minimized.
|
/ba-g known CI infra issues. SOSTests same baseline across DAC and cDAC due to known issues |
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>
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>
d602046 to
8595521
Compare
This comment has been minimized.
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>
8595521 to
4a8e236
Compare
Copilot Code ReviewHolistic AssessmentMotivation: Well-justified refactoring that eliminates raw Approach: Sound approach. Each refactoring moves one existing raw read into the contract layer without changing the semantics. The Summary: ✅ LGTM. The refactoring is behavior-preserving (with the documented breaking change for Detailed FindingsDetailed Findings✅ Contract Accessor Design — Correct and defensive
✅
|
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>
|
/ba-g previous successful pipeline run: https://dev.azure.com/dnceng-public/public/_build/results?buildId=1466895&view=results |
Note
This PR description was AI-generated by GitHub Copilot.
Refactors the
Microsoft.Diagnostics.DataContractReader.Legacyproject 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 correspondingContracts/*_1impl) without changing what bytes get read.Changes
1.
ILoader.GetLookupTablesowns theModuleLookupMap.TableDataoffsetAdds a
TableDataOffsetfield to the existingModuleLookupTablesrecord struct, populated byLoader_1.GetLookupTables.SOSDacImpl.GetModuleDatano longer reads raw typeinfo via_target.GetTypeInfo(DataType.ModuleLookupMap)and instead consumes the offset from the struct, going through a smallReadMapBasehelper that null-guards the table pointer.This removes the only direct
GetTypeInfocall in the Legacy project.2.
ILoader.GetAppDomainfor the global AppDomain pointerAdds a simple
GetAppDomain()accessor onILoaderthat reads and dereferences theAppDomainglobal pointer. All Legacy call sites that previously didReadGlobalPointer+ReadPointerinline now call this single method.RuntimeDomainInfostruct andGetDomainInfo()were removed per review feedback.DefaultADIDwas removed from the data contract -- Legacy call sites use a localconst uint DefaultAppDomainId = 1instead.3.
IRuntimeTypeSystem.GetWellKnownMethodTablefor runtime singleton MTsIntroduces a
WellKnownMethodTableenum (Object,String,Array,Exception,Free,Canon) and aGetWellKnownMethodTable(kind)accessor onIRuntimeTypeSystem.RuntimeTypeSystem_1implements it with a safeTryReadGlobalPointer+TryReadPointerpair so callers (e.g.GetUsefulGlobalsduring early load notifications) getTargetPointer.Nullwhen 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/_objectMethodTablelazy fields;GetObjectDataroutes through the contract;GetUsefulGlobalsreads all 5 well-known MTs via the contract.DacDbiImpl:IsExceptionObject(Exception MT) andEnumerateTypeParameters(Canon MT).Dbi/Helpers/HeapWalk: ctor (Free MT).4. DAC: Remove SystemDomain special-casing
GetAppDomainStoreDatanow reportssystemDomain = 0. The SystemDomain guard inGetAppDomainData,GetAssemblyList, andGetAppDomainNamehas been removed -- these methods now always treat the address as an AppDomain.Important
Breaking behavior change:
DacpAppDomainStoreData::systemDomainwill now beNULL. SOS consumers that assumesystemDomainis always valid need to be updated. The companion diagnostics PR handles this: dotnet/diagnostics#5878Testing
All cDAC unit tests pass (2493 passed, 16 skipped). Test updates:
ClrDataTaskTests.GetCurrentAppDomainregisters theILoadercontract.DacDbiImplTests.IsExceptionObjectnow mocksIRuntimeTypeSystem.GetWellKnownMethodTableinstead of setting up the rawExceptionMethodTableglobal.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.