Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ public GCHeapWKS(Target target)
BackgroundMaxSavedAddr = target.ReadPointer(bgMaxPtr.Value);
AllocAllocated = target.ReadPointer(target.ReadGlobalPointer(Constants.Globals.GCHeapAllocAllocated));
EphemeralHeapSegment = target.ReadPointer(target.ReadGlobalPointer(Constants.Globals.GCHeapEphemeralHeapSegment));
CardTable = target.ReadPointer(target.ReadGlobalPointer(Constants.Globals.GCHeapCardTable));
TargetPointer cardTablePtr = target.ReadGlobalPointer(Constants.Globals.GCHeapCardTable);
CardTable = target.TryReadPointer(cardTablePtr, out TargetPointer cardTable)
? cardTable
: TargetPointer.Null;
FinalizeQueue = target.ReadPointer(target.ReadGlobalPointer(Constants.Globals.GCHeapFinalizeQueue));
GenerationTable = target.ReadGlobalPointer(Constants.Globals.GCHeapGenerationTable);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1640,7 +1640,8 @@ int ISOSDacInterface.GetGCHeapStaticData(DacpGcHeapDetails* details)

Debug.Assert(details->lowest_address == detailsLocal.lowest_address, $"cDAC: {details->lowest_address:x}, DAC: {detailsLocal.lowest_address:x}");
Debug.Assert(details->highest_address == detailsLocal.highest_address, $"cDAC: {details->highest_address:x}, DAC: {detailsLocal.highest_address:x}");
Debug.Assert(details->card_table == detailsLocal.card_table, $"cDAC: {details->card_table:x}, DAC: {detailsLocal.card_table:x}");
// Reduced dumps may omit the cDAC card-table slot while retaining the legacy VM mirror.
Debug.Assert(details->card_table == 0 || details->card_table == detailsLocal.card_table, $"cDAC: {details->card_table:x}, DAC: {detailsLocal.card_table:x}");
}
}
#endif
Expand Down
72 changes: 72 additions & 0 deletions src/native/managed/cdac/tests/UnitTests/GCTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,62 @@
using System;
using System.Collections.Generic;
using Microsoft.Diagnostics.DataContractReader.Contracts;
using Microsoft.Diagnostics.DataContractReader.Legacy;
using Microsoft.Diagnostics.DataContractReader.TestInfrastructure;
using Xunit;

namespace Microsoft.Diagnostics.DataContractReader.Tests;

public class GCTests
{
private const ulong CardTableAddress = 0x1234_5000;

private static readonly MockGCBuilder.Generation[] s_cardTableGenerations =
[
new() { StartSegment = 0x1A00_0000, AllocationStart = 0x1A00_1000, AllocContextPointer = 0x1A00_2000, AllocContextLimit = 0x1A00_3000 },
new() { StartSegment = 0x1B00_0000, AllocationStart = 0x1B00_1000, AllocContextPointer = 0, AllocContextLimit = 0 },
new() { StartSegment = 0x1C00_0000, AllocationStart = 0x1C00_1000, AllocContextPointer = 0, AllocContextLimit = 0 },
new() { StartSegment = 0x1D00_0000, AllocationStart = 0x1D00_1000, AllocContextPointer = 0, AllocContextLimit = 0 },
];

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void GetHeapData_ReadableCardTableSlot_ReturnsCardTable(MockTarget.Architecture arch)
{
Target target = CreateWksTargetWithCardTable(arch, cardTableSlotReadable: true);

GCHeapData heapData = target.Contracts.GC.GetHeapData();

Assert.Equal(CardTableAddress, (ulong)heapData.CardTable);
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void GetHeapData_UnreadableCardTableSlot_ReturnsHeapDataWithNullCardTable(MockTarget.Architecture arch)
{
Target target = CreateWksTargetWithCardTable(arch, cardTableSlotReadable: false);

GCHeapData heapData = target.Contracts.GC.GetHeapData();

Assert.Equal(TargetPointer.Null, heapData.CardTable);
Assert.Equal(s_cardTableGenerations.Length, heapData.GenerationTable.Count);
Assert.Equal(s_cardTableGenerations[0].StartSegment, (ulong)heapData.GenerationTable[0].StartSegment);
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public unsafe void GetGCHeapStaticData_UnreadableCardTableSlot_ReturnsHeapDataWithNullCardTable(MockTarget.Architecture arch)
{
Target target = CreateWksTargetWithCardTable(arch, cardTableSlotReadable: false);

ISOSDacInterface sosDac = new SOSDacImpl(target, legacyObj: null, new());
DacpGcHeapDetails details = default;

Assert.Equal(0, sosDac.GetGCHeapStaticData(&details));
Assert.Equal(0UL, details.card_table.Value);
Assert.Equal(s_cardTableGenerations[0].StartSegment, details.generation_table[0].start_segment.Value);
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void GetHeapData_ReturnsCorrectGenerationTable(MockTarget.Architecture arch)
Expand Down Expand Up @@ -120,6 +169,29 @@ public void GetHeapData_WithFiveGenerations(MockTarget.Architecture arch)

private sealed record CapturedSegment(ulong Start, ulong End, GCSegmentClassification Generation);

private static Target CreateWksTargetWithCardTable(MockTarget.Architecture arch, bool cardTableSlotReadable)
{
ulong cardTableGlobalAddress = 0;
var builder = new TestPlaceholderTarget.Builder(arch);
builder.AddGCHeapWks(gc =>
{
gc.Generations = s_cardTableGenerations;
gc.ConfigureMemory = gcBuilder =>
{
cardTableGlobalAddress = gcBuilder.CardTableGlobalAddress;
gcBuilder.WritePointerGlobal(cardTableGlobalAddress, CardTableAddress);
};
});

if (!cardTableSlotReadable)
{
TestPlaceholderTarget.ReadFromTargetDelegate reader = builder.MemoryBuilder.GetMemoryContext().ReadFromTarget;
builder.UseReader((address, buffer) => address == cardTableGlobalAddress ? -1 : reader(address, buffer));
}

return builder.Build();
}

private static MockGCBuilder.Generation[] MakeGenerations(ulong gen0Seg, ulong gen0Start, ulong gen1Seg, ulong gen1Start, ulong gen2Seg, ulong lohSeg, ulong pohSeg)
=>
[
Expand Down