in src/Microsoft.Diagnostics.Runtime/DacImplementation/DacHeap.cs [238:357]
private bool TryCreateSegment(SubHeapInfo subHeap, ulong address, int generation, out SegmentInfo segInfo)
{
if (!_sos.GetSegmentData(address, out SegmentData data))
{
segInfo = default;
return false;
}
var flags = (ClrSegmentFlags)data.Flags;
GCSegmentKind kind = GCSegmentKind.Generation2;
if ((flags & ClrSegmentFlags.ReadOnly) == ClrSegmentFlags.ReadOnly)
{
kind = GCSegmentKind.Frozen;
}
else if (generation == 3)
{
kind = GCSegmentKind.Large;
}
else if (generation == 4)
{
kind = GCSegmentKind.Pinned;
}
else
{
// We are not a Frozen, Large, or Pinned segment/region:
if (subHeap.HasRegions)
{
if (generation == 0)
kind = GCSegmentKind.Generation0;
else if (generation == 1)
kind = GCSegmentKind.Generation1;
else if (generation == 2)
kind = GCSegmentKind.Generation2;
}
else
{
if (subHeap.EphemeralHeapSegment == address)
kind = GCSegmentKind.Ephemeral;
else
kind = GCSegmentKind.Generation2;
}
}
// The range of memory occupied by allocated objects
MemoryRange allocated = new(data.Start, subHeap.EphemeralHeapSegment == address ? subHeap.Allocated : (ulong)data.Allocated);
// There's a bit of calculation involved with finding the committed start.
// For regions, it's "allocated.Start - sizeof(aligned_plug_and_gap)".
// For segments, it's adjusted by segment_info_size which can be different based
// on whether background GC is enabled. Since we don't have that information, we'll
// use a heuristic here and hope for the best.
ulong committedStart;
if (kind == GCSegmentKind.Frozen)
committedStart = allocated.Start - (uint)IntPtr.Size;
else if ((allocated.Start & 0x1ffful) == 0x1000)
committedStart = allocated.Start - 0x1000;
else
committedStart = allocated.Start & ~0xffful;
MemoryRange committed, gen0, gen1, gen2;
if (subHeap.HasRegions)
{
committed = new(committedStart, data.Committed);
gen0 = default;
gen1 = default;
gen2 = default;
switch (generation)
{
case 0:
gen0 = new(allocated.Start, allocated.End);
break;
case 1:
gen1 = new(allocated.Start, allocated.End);
break;
default:
gen2 = new(allocated.Start, allocated.End);
break;
}
}
else
{
committed = new(committedStart, data.Committed);
if (kind == GCSegmentKind.Ephemeral)
{
gen0 = new(subHeap.Generations[0].AllocationStart, allocated.End);
gen1 = new(subHeap.Generations[1].AllocationStart, gen0.Start);
gen2 = new(allocated.Start, gen1.Start);
}
else
{
gen0 = default;
gen1 = default;
gen2 = allocated;
}
}
// The range of memory reserved
MemoryRange reserved = new(committed.End, data.Reserved);
segInfo = new()
{
Address = data.Address,
Kind = kind,
ObjectRange = allocated,
CommittedMemory = committed,
ReservedMemory = reserved,
Generation0 = gen0,
Generation1 = gen1,
Generation2 = gen2,
Flags = flags,
Next = data.Next,
BackgroundAllocated = data.BackgroundAllocated,
};
return true;
}