Skip to content

Implement cDAC Memory Region APIs (GetHandleTableMemoryRegions, GetGCBookkeepingMemoryRegions, GetGCFreeRegions)#124926

Draft
Copilot wants to merge 19 commits intomainfrom
copilot/implement-memory-region-apis
Draft

Implement cDAC Memory Region APIs (GetHandleTableMemoryRegions, GetGCBookkeepingMemoryRegions, GetGCFreeRegions)#124926
Copilot wants to merge 19 commits intomainfrom
copilot/implement-memory-region-apis

Conversation

Copy link
Contributor

Copilot AI commented Feb 26, 2026

Description

Three ISOSDacInterface13 APIs (GetHandleTableMemoryRegions, GetGCBookkeepingMemoryRegions, GetGCFreeRegions) previously delegated to the legacy DAC or returned E_NOTIMPL. This PR implements them natively in the cDAC by extending the IGC contract and following the existing SOSMethodEnum pattern. Re-implemented on top of latest main which includes PR #124875 infrastructure (handle table data types, TotalCpuCount global, etc.).

Managed interface definitions (ISOSDacInterface.cs)

  • Added SOSMemoryRegion struct and ISOSMemoryEnum COM interface
  • Changed ISOSDacInterface13 method signatures from void** to typed out ISOSMemoryEnum?

GC data descriptors (datadescriptor.inc, datadescriptor.h)

  • Added CDAC_TYPE blocks for CardTableInfo and RegionFreeList
  • Added GCHeap fields (with conditional compilation guards): FreeableSohSegment, FreeableUohSegment (#ifdef BACKGROUND_GC), FreeRegions (#ifdef USE_REGIONS)
  • Added BookkeepingStart as a static pointer to the PER_HEAP_ISOLATED field (unconditional global pointer, not a per-heap field)
  • Added global pointers: BookkeepingStart (unconditional), GlobalFreeHugeRegions, GlobalRegionsToDecommit (#ifdef USE_REGIONS), workstation free-region globals (GCHeapFreeableSohSegment under BACKGROUND_GC, GCHeapFreeRegions under USE_REGIONS)
  • Added global constants: HandleSegmentSize, CardTableInfoSize, CountFreeRegionKinds (#ifdef USE_REGIONS, using FREE_REGION_KINDS macro)

Managed Data classes and type registration

  • New Data classes: CardTableInfo.cs, RegionFreeList.cs
  • DataType enum entries for CardTableInfo, RegionFreeList
  • Constants.Globals entries for all new globals
  • IGCHeap/GCHeapSVR/GCHeapWKS extended with optional fields: FreeableSohSegment, FreeableUohSegment, FreeRegions

IGC contract extension (IGC.cs, GC_1.cs)

  • Added GCMemoryRegionData, FreeRegionKind types
  • GetHandleTableMemoryRegions(): walks handle_table_map → buckets → tables → segments (cap: 8192), using TotalCpuCount (processor count) for the number of table slots per bucket in server GC (per PR Fix GC handle dac bug #124875)
  • GetGCBookkeepingMemoryRegions(): reads bookkeeping_start from BookkeepingStart global pointer (works for both WKS and server GC since it's PER_HEAP_ISOLATED), walks card_table_info linked list (cap: 32)
  • GetGCFreeRegions(): enumerates global free huge regions, global regions to decommit, per-heap free regions + freeable SOH/UOH segments for both workstation and server GC (cap: 2048)

SOSMemoryEnum and SOSDacImpl integration

  • SOSMemoryEnum: [GeneratedComClass] implementing ISOSMemoryEnum with Next/Skip/Reset/GetCount
  • SOSMemoryEnum constructor accepts Target and uses ToClrDataAddress(target) for proper sign extension on 32-bit targets
  • SOSMemoryEnum.Next handles null memRegion parameter by returning E_POINTER
  • Three SOSDacImpl methods now call IGC contract methods, wrap results in SOSMemoryEnum, and include #if DEBUG cross-validation blocks against _legacyImpl13

cDAC documentation (GC.md)

  • Added new API types (FreeRegionKind, GCMemoryRegionData) and method signatures to the contract API section
  • Added data descriptor entries for CardTableInfo, RegionFreeList, and new GCHeap fields (FreeableSohSegment, FreeableUohSegment, FreeRegions)
  • Added new globals (HandleSegmentSize, CardTableInfoSize, CountFreeRegionKinds, BookkeepingStart, GlobalFreeHugeRegions, GlobalRegionsToDecommit, workstation free-region globals) and safety-cap constants
  • BookkeepingStart correctly listed only in the globals table (not as a GCHeap field, since it's PER_HEAP_ISOLATED)
  • Added pseudocode for GetHandleTableMemoryRegions, GetGCBookkeepingMemoryRegions, GetGCFreeRegions and helper methods (AddFreeList, AddSegmentList)

Native build fixes

  • Added #ifdef BACKGROUND_GC guards for FreeableSohSegment/FreeableUohSegment fields and WKS global pointers
  • Added #ifdef USE_REGIONS guards for FreeRegions, GlobalFreeHugeRegions, GlobalRegionsToDecommit, CountFreeRegionKinds
  • Changed BookkeepingStart from GC_HEAP_FIELD to static constexpr decltype(...) pointer (it's PER_HEAP_ISOLATED, not a per-heap instance field)
  • Moved BookkeepingStart to unconditional CDAC_GLOBAL_POINTER (accessible in both WKS and SERVER_GC builds)
  • Used FREE_REGION_KINDS macro (defined at global scope) instead of count_free_region_kinds enum (which is inside GC_NAMESPACE and not accessible in the datadescriptor context)

Unit tests (GCMemoryRegionTests.cs)

  • 8 test methods × 4 architectures = 32 tests, all passing (928 total tests in suite)
  • GetHandleTableMemoryRegions: single segment, all-null buckets (empty result), multiple linked segments
  • GetGCBookkeepingMemoryRegions: single card table entry, missing global (empty result)
  • GetGCFreeRegions: single free region, no free regions (empty result), multiple free regions across kinds with linked segments

Dump tests (ServerGCDumpTests.cs, WorkstationGCDumpTests.cs)

  • Added 3 dump tests to ServerGCDumpTests and 3 to WorkstationGCDumpTests covering all three new APIs
  • GetHandleTableMemoryRegions: asserts at least one region with non-null start and non-zero size
  • GetGCBookkeepingMemoryRegions: asserts at least one region with non-null start and non-zero size
  • GetGCFreeRegions: validates any returned regions have non-null start and non-zero size (count may be zero since free regions depend on GC state)

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag
See info in area-owners.md if you want to be subscribed.

Copilot AI and others added 4 commits February 26, 2026 22:41
… for memory region types

Co-authored-by: max-charlamb <44248479+max-charlamb@users.noreply.github.com>
…p free region fields, and IGC contract methods

Co-authored-by: max-charlamb <44248479+max-charlamb@users.noreply.github.com>
…Impl integration

Co-authored-by: max-charlamb <44248479+max-charlamb@users.noreply.github.com>
Add GCMemoryRegionTests with three test methods covering:
- GetHandleTableMemoryRegions: single map, bucket, and segment
- GetGCBookkeepingMemoryRegions: single card table entry
- GetGCFreeRegions: workstation GC with one free region

All tests use [Theory] with [ClassData(typeof(MockTarget.StdArch))]
to run across all four architecture combinations.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI changed the title [WIP] Implement native cDAC Memory Region APIs Implement cDAC Memory Region APIs (GetHandleTableMemoryRegions, GetGCBookkeepingMemoryRegions, GetGCFreeRegions) Feb 26, 2026
@max-charlamb
Copy link
Member

@copilot , we need to update the cDAC docs and write tests for these new APIs

Co-authored-by: max-charlamb <44248479+max-charlamb@users.noreply.github.com>
@rcj1
Copy link
Contributor

rcj1 commented Feb 27, 2026

@copilot try again

…eeping entries, empty/multiple free regions

Co-authored-by: rcj1 <77995559+rcj1@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 27, 2026

@copilot try again

Updated the cDAC docs (GC.md) with new API types, data descriptors, globals, constants, and pseudocode for all three methods. Added 6 additional test methods covering edge cases (empty buckets, multiple linked segments, multiple bookkeeping entries, empty/multiple free regions). Total: 9 test methods × 4 architectures = 36 tests, all passing. See commit 50041df6.


int maxSlots = 1;
if (IsServerGC())
maxSlots = (int)GetGCHeapCount();
Copy link
Contributor

Choose a reason for hiding this comment

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

@copilot this is incorrect. We need to ensure that we limit by the processor count in this case. See #124875

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 6320ebd1. Changed maxSlots to read from TotalCpuCount (the processor count) instead of using GetGCHeapCount() for server GC, matching the fix in PR #124875. Added the TotalCpuCount global pointer to datadescriptor.inc (under SERVER_GC), Constants.Globals, and updated the GC.md pseudocode accordingly.

@rcj1
Copy link
Contributor

rcj1 commented Feb 27, 2026

@copilot fix the build break

@rcj1
Copy link
Contributor

rcj1 commented Feb 27, 2026

Ah I will merge my changes into main and then this should work better

…it in server GC

Co-authored-by: rcj1 <77995559+rcj1@users.noreply.github.com>
@rcj1 rcj1 marked this pull request as ready for review February 27, 2026 02:44
Copilot AI review requested due to automatic review settings February 27, 2026 02:44
Copilot AI and others added 5 commits February 27, 2026 02:54
Co-authored-by: rcj1 <77995559+rcj1@users.noreply.github.com>
…24875 infrastructure

Co-authored-by: rcj1 <77995559+rcj1@users.noreply.github.com>
Rewrite test infrastructure usage to follow the proper pattern:
- Use builder.CreateAllocator() instead of non-existent builder.BumpAllocator
- Use builder.AddHeapFragment() to register allocated fragments
- Use builder.BorrowAddressRange() + helpers to write data via Span<byte>
- Use 32-bit compatible address ranges (0x0010_0000-0x0100_0000)
- Wrap fragment addresses in TargetPointer for Assert.Equal comparisons

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…als, and pseudocode

Co-authored-by: rcj1 <77995559+rcj1@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 27, 2026

@copilot please start over using the current main as a base. I added some important infrastructure and bug fixes that have already been reviewed and approved.

Started over from the latest main which includes PR #124875 infrastructure. All previous changes were reverted and re-implemented on top of the new base, reusing the HandleTableMap, HandleTableBucket, HandleTable, TableSegment data classes and TotalCpuCount global that were already merged. See commits starting from 8c95b439.

@rcj1
Copy link
Contributor

rcj1 commented Feb 27, 2026

@copilot fix the build break

…d globals

- Wrap FreeableSohSegment/FreeableUohSegment in #ifdef BACKGROUND_GC
- Wrap FreeRegions/GlobalFreeHugeRegions/GlobalRegionsToDecommit/CountFreeRegionKinds in #ifdef USE_REGIONS
- Change BookkeepingStart from GC_HEAP_FIELD to static pointer (it's PER_HEAP_ISOLATED)
- Move BookkeepingStart to unconditional global pointer (not WKS-only)
- Remove BookkeepingStart from IGCHeap interface (it's a global, not per-heap)
- Simplify GetGCBookkeepingMemoryRegions to always use global pointer

Co-authored-by: rcj1 <77995559+rcj1@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 27, 2026

@copilot fix the build break

Fixed in 6e3291fc. The build broke because GC datadescriptor fields and globals were referenced unconditionally but only exist under specific build configurations:

  • FreeableSohSegment/FreeableUohSegment require #ifdef BACKGROUND_GC
  • FreeRegions, GlobalFreeHugeRegions, GlobalRegionsToDecommit, CountFreeRegionKinds require #ifdef USE_REGIONS
  • BookkeepingStart is PER_HEAP_ISOLATED (static), not a per-heap field — changed to a global pointer accessible in both WKS and SERVER_GC builds

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/graphql
    • Triggering command: /usr/bin/gh gh pr view --json number,headRefName (http block)
    • Triggering command: /usr/bin/gh gh pr list --head copilot/implement-memory-region-apis --json number (http block)
    • Triggering command: /usr/bin/gh gh pr list --head copilot/implement-memory-region-apis (http block)

If you need me to access, download, or install something from one of these locations, you can either:

@rcj1
Copy link
Contributor

rcj1 commented Feb 28, 2026

@copilot still broken
/__w/1/s/src/coreclr/gc/datadescriptor/datadescriptor.inc:150:57: error: use of undeclared identifier 'count_free_region_kinds'

…unt_free_region_kinds

Co-authored-by: rcj1 <77995559+rcj1@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 28, 2026

@copilot still broken
/__w/1/s/src/coreclr/gc/datadescriptor/datadescriptor.inc:150:57: error: use of undeclared identifier 'count_free_region_kinds'

Fixed in b3ddcf19. count_free_region_kinds is an enum defined inside the GC_NAMESPACE namespace (via gcpriv.h), so it's not visible at the datadescriptor.inc expansion scope. Replaced with FREE_REGION_KINDS, the equivalent macro defined at global scope in gcinterface.dac.h. There's a static_assert in gcpriv.h confirming they're always equal.

Copy link
Member

@max-charlamb max-charlamb left a comment

Choose a reason for hiding this comment

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

@copilot address feedback

}
}

int ISOSMemoryEnum.Next(uint count, SOSMemoryRegion[] memRegion, uint* pNeeded)
Copy link
Member

Choose a reason for hiding this comment

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

Can we handle a nullptr passed to memRegion like the DAC could?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 99dd6293. Added if (memRegion is null) return HResults.E_POINTER; check before accessing the array.

…egion, add #DEBUG blocks, fix GC.md docs

Co-authored-by: max-charlamb <44248479+max-charlamb@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 2, 2026

@copilot address feedback

All five feedback items addressed in 99dd6293:

  • ToClrDataAddress conversion helper in SOSMemoryEnum constructor
  • Null check for memRegion in Next()
  • #DEBUG validation blocks on all three API methods
  • Removed BookkeepingStart from GCHeap fields table (global pointer only)
  • Renamed GCHeapBookkeepingStartBookkeepingStart in globals table and pseudocode

@max-charlamb
Copy link
Member

@copilot, can you add cDAC dumptests for these three new APIs

…MemoryRegions, GetGCFreeRegions

Co-authored-by: max-charlamb <44248479+max-charlamb@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 2, 2026

@copilot, can you add cDAC dumptests for these three new APIs

Added in f9672e8e. Three dump tests added to each of ServerGCDumpTests.cs and WorkstationGCDumpTests.cs:

  • GetHandleTableMemoryRegions — asserts at least one region with non-null start and non-zero size
  • GetGCBookkeepingMemoryRegions — asserts at least one region with non-null start and non-zero size
  • GetGCFreeRegions — validates any returned regions have non-null start and non-zero size (count may be zero)

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.

4 participants