Skip to content

Commit

Permalink
Merge pull request #3812 from hoyosjs/juhoyosa/check-ports
Browse files Browse the repository at this point in the history
Port targetted fixes to release
  • Loading branch information
hoyosjs authored Apr 12, 2023
2 parents 0100b6a + 1815f49 commit e01ddda
Show file tree
Hide file tree
Showing 15 changed files with 172 additions and 123 deletions.
8 changes: 4 additions & 4 deletions eng/Version.Details.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
<Uri>https://github.com/dotnet/symstore</Uri>
<Sha>e09f81a0b38786cb20f66b589a8b88b6997a62da</Sha>
</Dependency>
<Dependency Name="Microsoft.Diagnostics.Runtime" Version="3.0.0-beta.23177.1">
<Dependency Name="Microsoft.Diagnostics.Runtime" Version="3.0.0-beta.23205.1">
<Uri>https://github.com/microsoft/clrmd</Uri>
<Sha>e61e6bdb23739ad2c59616b6c8d6659f4558c88d</Sha>
<Sha>3368bf4451a9441076595022fdff0f2bbea57b1b</Sha>
</Dependency>
<Dependency Name="Microsoft.Diagnostics.Runtime.Utilities" Version="3.0.0-beta.23177.1">
<Dependency Name="Microsoft.Diagnostics.Runtime.Utilities" Version="3.0.0-beta.23205.1">
<Uri>https://github.com/microsoft/clrmd</Uri>
<Sha>e61e6bdb23739ad2c59616b6c8d6659f4558c88d</Sha>
<Sha>3368bf4451a9441076595022fdff0f2bbea57b1b</Sha>
</Dependency>
</ProductDependencies>
<ToolsetDependencies>
Expand Down
2 changes: 1 addition & 1 deletion eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
<SystemReflectionMetadataVersion>5.0.0</SystemReflectionMetadataVersion>
<!-- Other libs -->
<MicrosoftBclAsyncInterfacesVersion>1.1.0</MicrosoftBclAsyncInterfacesVersion>
<MicrosoftDiagnosticsRuntimeVersion>3.0.0-beta.23177.1</MicrosoftDiagnosticsRuntimeVersion>
<MicrosoftDiagnosticsRuntimeVersion>3.0.0-beta.23205.1</MicrosoftDiagnosticsRuntimeVersion>
<MicrosoftDiaSymReaderNativePackageVersion>16.9.0-beta1.21055.5</MicrosoftDiaSymReaderNativePackageVersion>
<MicrosoftDiagnosticsTracingTraceEventVersion>3.0.7</MicrosoftDiagnosticsTracingTraceEventVersion>
<!-- Use pinned version to avoid picking up latest (which doesn't support netcoreapp3.1) during source-build -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ public override void Invoke()
});
}

bool printFragmentation = false;
DumpHeapService.DisplayKind displayKind = DumpHeapService.DisplayKind.Normal;
if (ThinLock)
{
Expand All @@ -141,8 +142,12 @@ public override void Invoke()
{
displayKind = DumpHeapService.DisplayKind.Short;
}
else
{
printFragmentation = true;
}

DumpHeap.PrintHeap(objectsToPrint, displayKind, StatOnly);
DumpHeap.PrintHeap(objectsToPrint, displayKind, StatOnly, printFragmentation);
}

private void ParseArguments()
Expand Down
62 changes: 58 additions & 4 deletions src/Microsoft.Diagnostics.ExtensionCommands/DumpHeapService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands
[ServiceExport(Scope = ServiceScope.Runtime)]
public class DumpHeapService
{
private const ulong FragmentationBlockMinSize = 512 * 1024;
private const char StringReplacementCharacter = '.';

[ServiceImport]
Expand All @@ -34,18 +35,20 @@ public enum DisplayKind
Strings
}

public void PrintHeap(IEnumerable<ClrObject> objects, DisplayKind displayKind, bool statsOnly)
public void PrintHeap(IEnumerable<ClrObject> objects, DisplayKind displayKind, bool statsOnly, bool printFragmentation)
{
List<(ClrObject Free, ClrObject Next)> fragmentation = null;
Dictionary<(string String, ulong Size), uint> stringTable = null;
Dictionary<ulong, (int Count, ulong Size, string TypeName)> stats = new();

TableOutput thinLockOutput = null;
TableOutput objectTable = new(Console, (12, "x12"), (12, "x12"), (12, ""), (0, ""));
if (!statsOnly && (displayKind is DisplayKind.Normal or DisplayKind.Strings))
{
objectTable.WriteRow("Address", "MT", "Size");
}

Dictionary<ulong, (int Count, ulong Size, string TypeName)> stats = new();
Dictionary<(string String, ulong Size), uint> stringTable = null;

ClrObject lastFreeObject = default;
foreach (ClrObject obj in objects)
{
if (displayKind == DisplayKind.ThinLock)
Expand Down Expand Up @@ -77,6 +80,35 @@ public void PrintHeap(IEnumerable<ClrObject> objects, DisplayKind displayKind, b
objectTable.WriteRow(new DmlDumpObj(obj), new DmlDumpHeap(obj.Type?.MethodTable ?? 0), size, obj.IsFree ? "Free" : "");
}

if (printFragmentation)
{
if (lastFreeObject.IsFree && obj.IsValid && !obj.IsFree)
{
// Check to see if the previous object lands directly before this one. We don't want
// to print fragmentation after changing segments, or after an allocation context.
if (lastFreeObject.Address + lastFreeObject.Size == obj.Address)
{
// Also, don't report fragmentation for Large/Pinned/Frozen segments. This check
// is a little slow, so we do this last.
ClrSegment seg = obj.Type.Heap.GetSegmentByAddress(obj);
if (seg is not null && seg.Kind is not GCSegmentKind.Large or GCSegmentKind.Pinned or GCSegmentKind.Frozen)
{
fragmentation ??= new();
fragmentation.Add((lastFreeObject, obj));
}
}
}

if (obj.IsFree && size >= FragmentationBlockMinSize)
{
lastFreeObject = obj;
}
else
{
lastFreeObject = default;
}
}

if (displayKind == DisplayKind.Strings)
{
// We only read a maximum of 1024 characters for each string. This may lead to some collisions if strings are unique
Expand Down Expand Up @@ -198,6 +230,28 @@ orderby Size
Console.WriteLine($"Total {stats.Values.Sum(r => r.Count):n0} objects, {stats.Values.Sum(r => (long)r.Size):n0} bytes");
}
}

// Print fragmentation if we calculated it
PrintFragmentation(fragmentation);
}

private void PrintFragmentation(List<(ClrObject Free, ClrObject Next)> fragmentation)
{
if (fragmentation is null || fragmentation.Count == 0)
{
return;
}

TableOutput output = new(Console, (16, "x12"), (12, "n0"), (16, "x12"));

Console.WriteLine();
Console.WriteLine("Fragmented blocks larger than 0.5 MB:");
output.WriteRow("Address", "Size", "Followed By");

foreach ((ClrObject free, ClrObject next) in fragmentation)
{
output.WriteRow(free.Address, free.Size, new DmlDumpObj(next.Address), next.Type?.Name ?? "<unknown_type>");
}
}

private string Sanitize(string str, int maxLen)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,39 +37,5 @@ internal static ulong FindMostCommonPointer(this IEnumerable<ulong> enumerable)
group ptr by ptr into g
orderby g.Count() descending
select g.First()).First();

internal static Generation GetGeneration(this ClrObject obj, ClrSegment knownSegment)
{
if (knownSegment is null)
{
knownSegment = obj.Type.Heap.GetSegmentByAddress(obj);
if (knownSegment is null)
{
return Generation.Error;
}
}

if (knownSegment.Kind == GCSegmentKind.Ephemeral)
{
return knownSegment.GetGeneration(obj) switch
{
0 => Generation.Gen0,
1 => Generation.Gen1,
2 => Generation.Gen2,
_ => Generation.Error
};
}

return knownSegment.Kind switch
{
GCSegmentKind.Generation0 => Generation.Gen0,
GCSegmentKind.Generation1 => Generation.Gen1,
GCSegmentKind.Generation2 => Generation.Gen2,
GCSegmentKind.Large => Generation.Large,
GCSegmentKind.Pinned => Generation.Pinned,
GCSegmentKind.Frozen => Generation.Frozen,
_ => Generation.Error
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,8 @@ public override void Invoke()
Console.CancellationToken.ThrowIfCancellationRequested();

// This handles both regions and segments
Generation gen = obj.GetGeneration(seg);
if (gen is not Generation.Gen0 or Generation.Gen1)
Generation gen = seg.GetGeneration(obj);
if (gen is not Generation.Generation0 or Generation.Generation1)
{
continue;
}
Expand All @@ -145,8 +145,7 @@ public override void Invoke()
continue;
}

Generation refGen = objRef.GetGeneration(null);
if (refGen == Generation.Large)
if (GetGenerationWithoutSegment(objRef) == Generation.Large)
{
yield return (obj, objRef);
}
Expand Down Expand Up @@ -174,14 +173,24 @@ public override void Invoke()
continue;
}

Generation refGen = objRef.GetGeneration(null);
if (refGen is Generation.Gen0 or Generation.Gen1)
if (GetGenerationWithoutSegment(objRef) is Generation.Generation0 or Generation.Generation1)
{
yield return (obj, objRef);
}
}
}
}
}

private Generation GetGenerationWithoutSegment(ClrObject obj)
{
ClrSegment seg = Runtime.Heap.GetSegmentByAddress(obj);
if (seg is not null)
{
return seg.GetGeneration(obj);
}

return Generation.Unknown;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ private IEnumerable<EphemeralRefCount> FindObjectsWithEphemeralReferences()
Console.CancellationToken.ThrowIfCancellationRequested();

// Skip this object if it's gen0 or we hit an error
Generation objGen = obj.GetGeneration(seg);
if (objGen is Generation.Gen0 or Generation.Error)
Generation objGen = seg.GetGeneration(obj);
if (objGen is Generation.Generation0 or Generation.Unknown)
{
continue;
}
Expand All @@ -108,10 +108,11 @@ private IEnumerable<EphemeralRefCount> FindObjectsWithEphemeralReferences()

ulong refObjSize = objRef.Size;

Generation refGen = objRef.GetGeneration(null);
ClrSegment refSeg = Runtime.Heap.GetSegmentByAddress(objRef);
Generation refGen = refSeg?.GetGeneration(objRef) ?? Generation.Unknown;
switch (refGen)
{
case Generation.Gen0:
case Generation.Generation0:
gen0 ??= new EphemeralRefCount()
{
Object = obj,
Expand All @@ -128,8 +129,8 @@ private IEnumerable<EphemeralRefCount> FindObjectsWithEphemeralReferences()

break;

case Generation.Gen1:
if (objGen > Generation.Gen1)
case Generation.Generation1:
if (objGen > Generation.Generation1)
{
gen1 ??= new EphemeralRefCount()
{
Expand Down
45 changes: 28 additions & 17 deletions src/Microsoft.Diagnostics.ExtensionCommands/GCHeapStatCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,25 +217,22 @@ private HeapInfo GetHeapInfo(ClrSubHeap heap)
continue;
}

GenerationInfo gen = seg.GetGeneration(obj) switch
GenerationInfo genInfo = result.GetInfoByGeneration(seg.GetGeneration(obj));
if (genInfo is not null)
{
0 => result.Gen0,
1 => result.Gen1,
_ => result.Gen2,
};

if (obj.IsFree)
{
result.Ephemeral.Free += obj.Size;
gen.Free += obj.Size;
}
else
{
gen.Allocated += obj.Size;

if (IncludeUnreachable && !LiveObjects.IsLive(obj))
if (obj.IsFree)
{
result.Ephemeral.Free += obj.Size;
genInfo.Free += obj.Size;
}
else
{
gen.Unrooted += obj.Size;
genInfo.Allocated += obj.Size;

if (IncludeUnreachable && !LiveObjects.IsLive(obj))
{
genInfo.Unrooted += obj.Size;
}
}
}
}
Expand Down Expand Up @@ -307,6 +304,20 @@ private sealed class HeapInfo
Frozen = left.Frozen + right.Frozen,
};
}

public GenerationInfo GetInfoByGeneration(Generation gen)
{
return gen switch
{
Generation.Generation0 => Gen0,
Generation.Generation1 => Gen1,
Generation.Generation2 => Gen2,
Generation.Large => LoH,
Generation.Pinned => PoH,
Generation.Frozen => Frozen,
_ => null
};
}
}

private sealed class GenerationInfo
Expand Down
24 changes: 10 additions & 14 deletions src/Microsoft.Diagnostics.ExtensionCommands/GCWhereCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,22 +54,18 @@ public override void Invoke()
{
generation = "reserve";
}
else if (segment.Kind == GCSegmentKind.Frozen)
{
generation = "frozen";
}
else if (segment.Kind == GCSegmentKind.Pinned)
{
generation = "pinned";
}
else if (segment.Kind == GCSegmentKind.Large)
{
generation = "large";
}
else
{
int gen = segment.GetGeneration(address);
generation = gen != -1 ? gen.ToString() : "???";
generation = segment.GetGeneration(address) switch
{
Generation.Generation0 => "0",
Generation.Generation1 => "1",
Generation.Generation2 => "2",
Generation.Frozen => "frozen",
Generation.Pinned => "pinned",
Generation.Large => "large",
_ => "???",
};
}

object addressColumn = segment.ObjectRange.Contains(address) ? new DmlListNearObj(address) : address;
Expand Down
16 changes: 0 additions & 16 deletions src/Microsoft.Diagnostics.ExtensionCommands/Generation.cs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public override void Invoke()
ulong curr = start;

IEnumerable<ClrObject> liveObjs = EnumerateLiveObjectsInRange(end, curr);
DumpHeap.PrintHeap(liveObjs, Short ? DumpHeapService.DisplayKind.Short : DumpHeapService.DisplayKind.Normal, statsOnly: false);
DumpHeap.PrintHeap(liveObjs, Short ? DumpHeapService.DisplayKind.Short : DumpHeapService.DisplayKind.Normal, statsOnly: false, printFragmentation: false);
}

private IEnumerable<ClrObject> EnumerateLiveObjectsInRange(ulong end, ulong curr)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public override void Invoke()
Console.WriteLine();

DumpHeapService.DisplayKind displayKind = Strings ? DumpHeapService.DisplayKind.Strings : DumpHeapService.DisplayKind.Normal;
DumpHeap.PrintHeap(GetTransitiveClosure(obj), displayKind, Stat);
DumpHeap.PrintHeap(GetTransitiveClosure(obj), displayKind, Stat, printFragmentation: false);
}

private static IEnumerable<ClrObject> GetTransitiveClosure(ClrObject obj)
Expand Down
Loading

0 comments on commit e01ddda

Please sign in to comment.