Skip to content

Commit 4f0c957

Browse files
committed
Merge remote-tracking branch 'origin/main' into juhoyosa/release-stable
2 parents 92d87f2 + e0a90b7 commit 4f0c957

12 files changed

+602
-49
lines changed

eng/SourceBuild.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<PropertyGroup>
44
<GitHubRepositoryName>diagnostics</GitHubRepositoryName>
55
<SourceBuildManagedOnly>true</SourceBuildManagedOnly>
6+
<SourceBuildTrimNetFrameworkTargets>true</SourceBuildTrimNetFrameworkTargets>
67
</PropertyGroup>
78

89
<ItemGroup>

eng/Version.Details.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,13 @@
3131
<Uri>https://github.com/dotnet/installer</Uri>
3232
<Sha>51e06f6931e859f56564556fa6ba519761fa7141</Sha>
3333
</Dependency>
34-
<Dependency Name="Microsoft.AspNetCore.App.Ref.Internal" Version="8.0.0-preview.4.23178.4">
34+
<Dependency Name="Microsoft.AspNetCore.App.Ref.Internal" Version="8.0.0-preview.4.23179.5">
3535
<Uri>https://github.com/dotnet/aspnetcore</Uri>
36-
<Sha>3a6acd95c769bbbd2e5288d5844c81dee45fc958</Sha>
36+
<Sha>c0acf059eddd7e70498804dcc99a7c7b33732417</Sha>
3737
</Dependency>
38-
<Dependency Name="Microsoft.AspNetCore.App.Ref" Version="8.0.0-preview.4.23178.4">
38+
<Dependency Name="Microsoft.AspNetCore.App.Ref" Version="8.0.0-preview.4.23179.5">
3939
<Uri>https://github.com/dotnet/aspnetcore</Uri>
40-
<Sha>3a6acd95c769bbbd2e5288d5844c81dee45fc958</Sha>
40+
<Sha>c0acf059eddd7e70498804dcc99a7c7b33732417</Sha>
4141
</Dependency>
4242
<Dependency Name="Microsoft.NETCore.App.Runtime.win-x64" Version="8.0.0-preview.3.23155.1">
4343
<Uri>https://github.com/dotnet/runtime</Uri>

eng/Versions.props

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
<VSRedistCommonNetCoreSharedFrameworkx6480Version>8.0.0-preview.3.23155.1</VSRedistCommonNetCoreSharedFrameworkx6480Version>
2222
<MicrosoftNETCoreAppRuntimewinx64Version>8.0.0-preview.3.23155.1</MicrosoftNETCoreAppRuntimewinx64Version>
2323
<!-- Latest shared aspnetcore version updated by darc -->
24-
<MicrosoftAspNetCoreAppRefInternalVersion>8.0.0-preview.4.23178.4</MicrosoftAspNetCoreAppRefInternalVersion>
25-
<MicrosoftAspNetCoreAppRefVersion>8.0.0-preview.4.23178.4</MicrosoftAspNetCoreAppRefVersion>
24+
<MicrosoftAspNetCoreAppRefInternalVersion>8.0.0-preview.4.23179.5</MicrosoftAspNetCoreAppRefInternalVersion>
25+
<MicrosoftAspNetCoreAppRefVersion>8.0.0-preview.4.23179.5</MicrosoftAspNetCoreAppRefVersion>
2626
<!-- dotnet/installer: Testing version of the SDK. Needed for the signed & entitled host. -->
2727
<MicrosoftDotnetSdkInternalVersion>8.0.100-preview.3.23156.1</MicrosoftDotnetSdkInternalVersion>
2828
</PropertyGroup>

src/Microsoft.Diagnostics.ExtensionCommands/ExtensionMethodHelpers.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Collections.Generic;
55
using System.Linq;
6+
using Microsoft.Diagnostics.Runtime;
67

78
namespace Microsoft.Diagnostics.ExtensionCommands
89
{
@@ -36,5 +37,39 @@ internal static ulong FindMostCommonPointer(this IEnumerable<ulong> enumerable)
3637
group ptr by ptr into g
3738
orderby g.Count() descending
3839
select g.First()).First();
40+
41+
internal static Generation GetGeneration(this ClrObject obj, ClrSegment knownSegment)
42+
{
43+
if (knownSegment is null)
44+
{
45+
knownSegment = obj.Type.Heap.GetSegmentByAddress(obj);
46+
if (knownSegment is null)
47+
{
48+
return Generation.Error;
49+
}
50+
}
51+
52+
if (knownSegment.Kind == GCSegmentKind.Ephemeral)
53+
{
54+
return knownSegment.GetGeneration(obj) switch
55+
{
56+
0 => Generation.Gen0,
57+
1 => Generation.Gen1,
58+
2 => Generation.Gen2,
59+
_ => Generation.Error
60+
};
61+
}
62+
63+
return knownSegment.Kind switch
64+
{
65+
GCSegmentKind.Generation0 => Generation.Gen0,
66+
GCSegmentKind.Generation1 => Generation.Gen1,
67+
GCSegmentKind.Generation2 => Generation.Gen2,
68+
GCSegmentKind.Large => Generation.Large,
69+
GCSegmentKind.Pinned => Generation.Pinned,
70+
GCSegmentKind.Frozen => Generation.Frozen,
71+
_ => Generation.Error
72+
};
73+
}
3974
}
4075
}
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Collections.Generic;
5+
using System.Diagnostics;
6+
using System.Linq;
7+
using Microsoft.Diagnostics.DebugServices;
8+
using Microsoft.Diagnostics.Runtime;
9+
using static Microsoft.Diagnostics.ExtensionCommands.TableOutput;
10+
11+
namespace Microsoft.Diagnostics.ExtensionCommands
12+
{
13+
[Command(Name = "ephtoloh", Help = "Finds ephemeral objects which reference the large object heap.")]
14+
public class FindEphemeralReferencesToLOHCommand : CommandBase
15+
{
16+
// IComparer for binary search
17+
private readonly IComparer<(ClrObject, ClrObject)> _firstObjectComparer = Comparer<(ClrObject, ClrObject)>.Create((x, y) => x.Item1.Address.CompareTo(y.Item1.Address));
18+
19+
[ServiceImport]
20+
public ClrRuntime Runtime { get; set; }
21+
22+
public override void Invoke()
23+
{
24+
int segments = Runtime.Heap.Segments.Count(seg => seg.Kind is not GCSegmentKind.Frozen or GCSegmentKind.Pinned);
25+
if (segments > 50)
26+
{
27+
string gcSegKind = Runtime.Heap.SubHeaps[0].HasRegions ? "regions" : "segments";
28+
Console.WriteLineWarning($"Walking {segments:n0} {gcSegKind}, this may take a moment...");
29+
}
30+
31+
TableOutput output = new(Console, (16, "x12"), (64, ""), (16, "x12"));
32+
33+
// Ephemeral -> Large
34+
List<(ClrObject From, ClrObject To)> ephToLoh = FindEphemeralToLOH().OrderBy(i => i.From.Address).ThenBy(i => i.To.Address).ToList();
35+
if (ephToLoh.Count == 0)
36+
{
37+
Console.WriteLine("No Ephemeral objects pointing to the Large objects.");
38+
}
39+
else
40+
{
41+
Console.WriteLine("Ephemeral objects pointing to the Large objects:");
42+
Console.WriteLine();
43+
output.WriteRow("Ephemeral", "Ephemeral Type", "Large Object", "Large Object Type");
44+
45+
foreach ((ClrObject from, ClrObject to) in ephToLoh)
46+
{
47+
output.WriteRow(new DmlDumpObj(from), from.Type?.Name, new DmlDumpObj(to), to.Type?.Name);
48+
}
49+
50+
Console.WriteLine();
51+
}
52+
53+
// Large -> Ephemeral
54+
List<(ClrObject From, ClrObject To)> lohToEph = FindLOHToEphemeral().OrderBy(i => i.From.Address).ThenBy(i => i.To.Address).ToList();
55+
if (lohToEph.Count == 0)
56+
{
57+
Console.WriteLine("No Large objects pointing to Ephemeral objects.");
58+
}
59+
else
60+
{
61+
Console.WriteLine("Large objects pointing to Ephemeral objects:");
62+
Console.WriteLine();
63+
output.WriteRow("Ephemeral", "Ephemeral Type", "Large Object", "Large Object Type");
64+
65+
foreach ((ClrObject from, ClrObject to) in lohToEph)
66+
{
67+
output.WriteRow(new DmlDumpObj(from), from.Type?.Name, new DmlDumpObj(to), to.Type?.Name);
68+
}
69+
70+
Console.WriteLine();
71+
}
72+
73+
// Ephemeral -> Large -> Ephemeral
74+
if (ephToLoh.Count != 0 && lohToEph.Count != 0)
75+
{
76+
// We'll use output to signify if we need to print a header or not.
77+
output = null;
78+
foreach ((ClrObject from, ClrObject to) in ephToLoh)
79+
{
80+
int index = lohToEph.BinarySearch((to, to), _firstObjectComparer);
81+
if (index < 0)
82+
{
83+
continue;
84+
}
85+
86+
Debug.Assert(to == lohToEph[index].From);
87+
ClrObject ephEnd = lohToEph[index].To;
88+
89+
if (output is null)
90+
{
91+
Console.WriteLine($"Ephemeral objects which point to Large objects which point to Ephemeral objects:");
92+
Console.WriteLine();
93+
output = new(Console, (16, "x12"), (64, ""), (16, "x12"), (64, ""), (16, "x12"));
94+
output.WriteRow(new DmlDumpObj(from), from.Type?.Name, new DmlDumpObj(to), to.Type?.Name, new DmlDumpObj(ephEnd), ephEnd.Type?.Name);
95+
}
96+
}
97+
98+
if (output is null)
99+
{
100+
Console.WriteLine("No Ephemeral objects which point to Large objects which point to Ephemeral objects.");
101+
}
102+
else
103+
{
104+
Console.WriteLine();
105+
}
106+
}
107+
108+
foreach ((ClrObject From, ClrObject To) item in ephToLoh)
109+
{
110+
if (lohToEph.Any(r => item.To.Address == r.From.Address))
111+
{
112+
Console.WriteLine("error!");
113+
}
114+
}
115+
}
116+
117+
private IEnumerable<(ClrObject From, ClrObject To)> FindEphemeralToLOH()
118+
{
119+
foreach (ClrSegment seg in Runtime.Heap.Segments)
120+
{
121+
Console.CancellationToken.ThrowIfCancellationRequested();
122+
123+
if (seg.Kind is GCSegmentKind.Frozen or GCSegmentKind.Large or GCSegmentKind.Generation2 or GCSegmentKind.Pinned)
124+
{
125+
continue;
126+
}
127+
128+
foreach (ClrObject obj in seg.EnumerateObjects().Where(obj => obj.IsValid && obj.ContainsPointers))
129+
{
130+
Console.CancellationToken.ThrowIfCancellationRequested();
131+
132+
// This handles both regions and segments
133+
Generation gen = obj.GetGeneration(seg);
134+
if (gen is not Generation.Gen0 or Generation.Gen1)
135+
{
136+
continue;
137+
}
138+
139+
foreach (ClrObject objRef in obj.EnumerateReferences(carefully: true, considerDependantHandles: false))
140+
{
141+
Console.CancellationToken.ThrowIfCancellationRequested();
142+
143+
if (!objRef.IsValid || objRef.IsFree) // heap corruption
144+
{
145+
continue;
146+
}
147+
148+
Generation refGen = objRef.GetGeneration(null);
149+
if (refGen == Generation.Large)
150+
{
151+
yield return (obj, objRef);
152+
}
153+
}
154+
}
155+
}
156+
}
157+
158+
private IEnumerable<(ClrObject From, ClrObject To)> FindLOHToEphemeral()
159+
{
160+
foreach (ClrSegment seg in Runtime.Heap.Segments.Where(seg => seg.Kind == GCSegmentKind.Large))
161+
{
162+
Console.CancellationToken.ThrowIfCancellationRequested();
163+
164+
foreach (ClrObject obj in seg.EnumerateObjects().Where(obj => obj.IsValid && obj.ContainsPointers))
165+
{
166+
Console.CancellationToken.ThrowIfCancellationRequested();
167+
168+
foreach (ClrObject objRef in obj.EnumerateReferences(carefully: true, considerDependantHandles: false))
169+
{
170+
Console.CancellationToken.ThrowIfCancellationRequested();
171+
172+
if (!objRef.IsValid || objRef.IsFree) // heap corruption
173+
{
174+
continue;
175+
}
176+
177+
Generation refGen = objRef.GetGeneration(null);
178+
if (refGen is Generation.Gen0 or Generation.Gen1)
179+
{
180+
yield return (obj, objRef);
181+
}
182+
}
183+
}
184+
}
185+
}
186+
}
187+
}

src/Microsoft.Diagnostics.ExtensionCommands/FindPointersInCommand.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public override void Invoke()
6363

6464
private void PrintPointers(bool pinnedOnly, params string[] memTypes)
6565
{
66-
DescribedRegion[] allRegions = AddressHelper.EnumerateAddressSpace(tagClrMemoryRanges: true, includeReserveMemory: false, tagReserveMemoryHeuristically: false).ToArray();
66+
DescribedRegion[] allRegions = AddressHelper.EnumerateAddressSpace(tagClrMemoryRanges: true, includeReserveMemory: false, tagReserveMemoryHeuristically: false, includeHandleTableIfSlow: false).ToArray();
6767

6868
WriteLine("Scanning for pinned objects...");
6969
MemoryWalkContext ctx = CreateMemoryWalkContext();

0 commit comments

Comments
 (0)