Created
November 22, 2017 02:21
-
-
Save piers7/8a79454dc778713799a9537336530eed to your computer and use it in GitHub Desktop.
Memory dump explorer in Linqpad using the Microsoft.Diagnostics.Runtime engine
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Query Kind="Program"> | |
<Reference><RuntimeDirectory>\System.Windows.Forms.dll</Reference> | |
<NuGetReference>Rx-Main</NuGetReference> | |
<NuGetReference Prerelease="true">Microsoft.Diagnostics.Runtime</NuGetReference> | |
<Namespace>Microsoft.Diagnostics.Runtime</Namespace> | |
<Namespace>System.Reactive</Namespace> | |
<Namespace>System.Reactive.Linq</Namespace> | |
</Query> | |
//string sourceDump = @"C:\MyLocalData\piers.williams\Downloads\temp\xunit.dmp"; | |
string sourceDump = null; | |
[STAThread] | |
void Main() | |
{ | |
Environment.Version.Dump(); | |
Environment.Is64BitProcess.Dump(); | |
if(sourceDump == null){ | |
using(var d = new System.Windows.Forms.OpenFileDialog()){ | |
var result = d.ShowDialog(); | |
if(result==System.Windows.Forms.DialogResult.OK){ | |
sourceDump = d.FileName; | |
}else{ | |
return; | |
} | |
} | |
} | |
using(var target = DataTarget.LoadCrashDump(sourceDump)){ | |
target.SymbolLocator.SymbolCache = @"C:\Symbols"; | |
var clrVersion = target.ClrVersions.First(); | |
//var runtime = target.CreateRuntime(@"C:\Symbols\SOS_4.0.30319.1026\mscordacwks.dll"); | |
var runtime = clrVersion.CreateRuntime(); | |
var heap = runtime.GetHeap(); | |
runtime.Threads | |
.Select (t => new { | |
t.OSThreadId, | |
t.ManagedThreadId, | |
t.GcMode, | |
t.IsFinalizer, | |
t.IsAlive, | |
t.CurrentException, | |
t.StackTrace, | |
// Objects = t.EnumerateStackObjects(), | |
}) | |
.Dump("Threads",3); | |
GetHeapStats(heap).Dump("Heap"); | |
// var traceEventType = heap.GetTypeByName("ORDW.Diagnostics.AnalysisServices.Trace.TraceEvent"); | |
// var instances = heap.EnumerateObjects() | |
// .Select (o => new ClrObject(o, heap.GetObjectType(o))) | |
// .Where (o => o.Type == traceEventType) | |
// //.Dump(3) | |
// ; | |
//traceEventType.Dump(); | |
//WalkReferences(instances.First().Address).Dump(); | |
// heap.EnumerateObjects() | |
// .Take(10) | |
// .Dump() | |
// ; | |
// instances | |
// .Take(100) | |
// | |
// .Select (o => o.ToObject()) | |
// .Dump(1); | |
// var find = instances.First().Address; | |
// DisplayRefChainsToObject(find, heap); | |
} | |
} | |
// Define other methods and classes here | |
public static IEnumerable<object> GetHeapStats(ClrHeap heap){ | |
var heapByType = ( | |
from address in heap.EnumerateObjectAddresses() | |
let type = heap.GetObjectType(address) | |
let size = type.GetSize(address) | |
group address by type into g | |
select new { | |
Type = g.Key, | |
TotalSize = g.Sum (o => (long)g.Key.GetSize(o)), | |
Count = g.Count (), | |
Items = g.GetEnumerator(), | |
} | |
) | |
; | |
return heapByType | |
.OrderByDescending (g => g.TotalSize) | |
.Take(20) | |
.Select (t => new { | |
t.TotalSize, | |
t.Count, | |
t.Type.Name, | |
Items = Util.OnDemand("Items", () => t.Items) | |
}) | |
; | |
} | |
public static ILookup<ulong,ClrRoot> BuildRootsLookup(ClrHeap heap){ | |
var roots = heap.EnumerateRoots(); // aledgedly cached within the API anyway | |
return BuildRootsLookup(roots); | |
} | |
public static ILookup<ulong,ClrRoot> BuildRootsLookup(IEnumerable<ClrRoot> roots){ | |
// See https://github.com/Microsoft/dotnetsamples/blob/master/Microsoft.Diagnostics.Runtime/CLRMD/GCRoot/Program.cs | |
// That implementation populated a static dictionary and returned a list in one pass | |
// Bit of a dirty-side-effecting method IMHO | |
var lookup = roots | |
.ToLookup (r => r.Object) | |
; | |
return lookup; | |
} | |
private static void DisplayRefChainsToObject(ulong targetAddress, ClrHeap heap){ | |
var roots = heap.EnumerateRoots().ToList(); | |
var rootDictionary = BuildRootsLookup(roots); | |
foreach(var root in roots){ | |
var visited = new HashSet<ulong>(); | |
var chain = new Stack<ulong>(); | |
chain.Push(root.Object); | |
//Console.WriteLine("Scanning roots for {0}", root.Type.Name); | |
DisplayRefChainsToObject(targetAddress, heap, chain, visited); | |
} | |
} | |
private static void DisplayRefChainsToObject(ulong targetAddress, ClrHeap heap, Stack<ulong> refs, HashSet<ulong> visited) | |
{ | |
// See http://blogs.microsoft.co.il/sasha/2013/05/20/traversing-the-gc-heap-with-clrmd/ | |
// Also https://github.com/Microsoft/dotnetsamples/blob/master/Microsoft.Diagnostics.Runtime/CLRMD/docs/WalkingTheHeap.md | |
// Basic idea is that we have to start with all the GC roots, and recursively walk all their references | |
// Any time we hit the reference we are looking for we return the path | |
var currentObj = refs.Peek(); | |
if (visited.Contains(currentObj)) return; | |
visited.Add(currentObj); | |
if (currentObj == targetAddress) | |
{ | |
// Display the chain | |
Console.WriteLine("Found path to {0}", targetAddress); | |
refs | |
.Select (addr => new ClrObject(addr, heap.GetObjectType(addr))) | |
.Dump() | |
; | |
} | |
var type = heap.GetObjectType(currentObj); | |
if(type==null) | |
return; | |
type.EnumerateRefsOfObject(currentObj, (innerObj, fieldOffset) => | |
{ | |
refs.Push(innerObj); | |
DisplayRefChainsToObject(targetAddress, heap, refs, visited); | |
refs.Pop(); | |
}); | |
} | |
public class ClrObject{ | |
public ulong Address {get;private set;} | |
public ClrType Type {get;private set;} | |
public ClrObject(ulong address, ClrType type) | |
{ | |
Address = address; | |
Type = type; | |
} | |
public dynamic ToObject(){ | |
var expando = (IDictionary<string,object>)new System.Dynamic.ExpandoObject(); | |
expando["__this"] = this; | |
foreach(var field in Type.Fields){ | |
var name = field.Name; | |
if(name.StartsWith("<") && name.EndsWith(">k__BackingField")) | |
name = name.Substring(1,name.IndexOf("__")-3); | |
expando[name] = field.GetValue(Address); | |
} | |
return expando; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment