Skip to content

Instantly share code, notes, and snippets.

@mattwarren
Last active August 15, 2016 15:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mattwarren/3dce1aea76c50da850af53a2d453e3c0 to your computer and use it in GitHub Desktop.
Save mattwarren/3dce1aea76c50da850af53a2d453e3c0 to your computer and use it in GitHub Desktop.
using Microsoft.Diagnostics.Runtime;
using System;
using System.IO;
using System.Linq;
namespace MemoryDumpGcInfo
{
// See https://github.com/Microsoft/clrmd/blob/master/Documentation/GettingStarted.md
// and https://github.com/Microsoft/clrmd/blob/master/Documentation/ClrRuntime.md
// and https://github.com/Microsoft/clrmd/blob/master/Documentation/WalkingTheHeap.md
// A useful list of instructions for working with CLRMD,
// see http://blogs.msdn.com/b/kirillosenkov/archive/2014/07/05/get-most-duplicated-strings-from-a-heap-dump-using-clrmd.aspx
class Program
{
static void Main(string[] args)
{
Console.ForegroundColor = ConsoleColor.DarkCyan;
Console.WriteLine(".NET Memory Dump Heap Analyser - created by Matt Warren - github.com/mattwarren\n");
Console.ResetColor();
if (args.Length < 1)
{
Console.WriteLine("Usage:\n MemoryDumpGcInfo.exe <Dump File>\n");
return;
}
if (File.Exists(args[0]) == false)
{
Console.WriteLine("{0} - does not exist!", args[0]);
return;
}
using (DataTarget target = DataTarget.LoadCrashDump(args[0]))
{
string dacLocation = null;
foreach (ClrInfo version in target.ClrVersions)
{
Console.WriteLine("Found CLR Version: " + version.Version.ToString());
dacLocation = LoadCorrectDacForMemoryDump(version);
}
var runtimeInfo = target.ClrVersions[0]; // just using the first runtime
ClrRuntime runtime = null;
try
{
if (string.IsNullOrEmpty(dacLocation))
runtime = runtimeInfo.CreateRuntime();
else
runtime = runtimeInfo.CreateRuntime(dacLocation);
}
catch (Exception ex)
{
Console.WriteLine("\n" + ex);
Console.WriteLine("\nEnsure that this program is compliled for the same architecture as the memory dump (i.e. 32-bit or 64-bit)");
return;
}
var heap = runtime.GetHeap();
//PrintMemoryRegionInfo(runtime);
PrintGCHeapInfo(heap);
}
}
private static string LoadCorrectDacForMemoryDump(ClrInfo version)
{
// First try the main location, i.e.:
// C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscordacwks.dll
if (version.LocalMatchingDac != null && File.Exists(version.LocalMatchingDac))
{
Console.WriteLine("\nDac already exists on the local machine at:\n{0}", version.LocalMatchingDac);
return version.LocalMatchingDac;
}
// Location: <TEMP>\symbols\mscordacwks_amd64_amd64_4.0.30319.18444.dll\52717f9a96b000\mscordacwks_amd64_amd64_4.0.30319.18444.dll
ModuleInfo dacInfo = version.DacInfo;
var dacLocation = string.Format(@"{0}\symbols\{1}\{2:x}{3:x}\{4}",
Path.GetTempPath(),
dacInfo.FileName,
dacInfo.TimeStamp,
dacInfo.FileSize,
dacInfo.FileName);
if (File.Exists(dacLocation))
{
Console.WriteLine("\nDac {0} already exists in the local cache at:\n{1}", dacInfo.FileName, dacLocation);
return dacLocation;
}
else
{
Console.WriteLine("\nUnable to find copy of the dac on the local machine.");
Console.WriteLine("It will now be downloaded from the Microsoft Symbol Server.");
Console.WriteLine("Press <ENTER> if you are okay with this, if not you can just type Ctrl-C to exit");
Console.ReadLine();
string downloadLocation = version.TryDownloadDac(new SymbolNotification());
Console.WriteLine("Downloaded a copy of the dac to:\n" + downloadLocation);
return downloadLocation;
}
}
private static void PrintMemoryRegionInfo(ClrRuntime runtime)
{
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine("\nMemory Region Information");
Console.ResetColor();
Console.WriteLine("--------------------------------------------");
Console.WriteLine("{0,6} {1,15} {2}", "Count", "Total Size", "Type");
Console.WriteLine("--------------------------------------------");
foreach (var region in (from r in runtime.EnumerateMemoryRegions()
where r.Type != ClrMemoryRegionType.ReservedGCSegment
group r by r.Type into g
let total = g.Sum(p => (uint)p.Size)
orderby total descending
select new
{
TotalSize = total,
Count = g.Count(),
Type = g.Key
}))
{
Console.WriteLine("{0,6:n0} {1,15:n0} {2}", region.Count, region.TotalSize, region.Type.ToString());
}
Console.WriteLine("--------------------------------------------");
}
private static void PrintGCHeapInfo(ClrHeap heap)
{
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine("\nGC Heap Information (Segments)");
Console.ResetColor();
Console.WriteLine("----------------------------------------------------------------------------");
Console.WriteLine("{0,12} {1,12} {2,8} {3,9} {4,8} {5,4} {6}", "Start", "End", "Length", "Committed", "Reserved", "Heap", "Type");
Console.WriteLine("----------------------------------------------------------------------------");
foreach (ClrSegment segment in heap.Segments)
{
string type;
if (segment.IsEphemeral)
type = "Ephemeral";
else if (segment.IsLarge)
type = "Large";
else
type = "Gen2";
//Console.WriteLine("{0,12:X} {1,12:X} {2,8:N2} {3,9:N2} {4,8:N2} {5,4} {6}",
Console.WriteLine("{0,12:X} {1,12:X} {2,8:N0} {3,9:N0} {4,8:N0} {5,4} {6}",
segment.Start,
segment.End,
segment.Length / 1024.0, // / 1024.0,
(segment.CommittedEnd - segment.Start) / 1024.0, // / 1024.0,
(segment.ReservedEnd - segment.Start) / 1024.0, // / 1024.0,
segment.ProcessorAffinity,
type);
}
Console.WriteLine("----------------------------------------------------------------------------");
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine("\nGC Heap Information (Per CPU)");
Console.ResetColor();
Console.WriteLine("-----------------------------------------------------------");
foreach (var item in (from seg in heap.Segments
group seg by seg.ProcessorAffinity into g
orderby g.Key
select new
{
Heap = g.Key,
Size = g.Sum(p => (uint)p.Length)
}))
{
Console.WriteLine("Heap {0,2}: {1,12:n0} bytes ({2:N2} MB)", item.Heap, item.Size, item.Size / 1024.0 / 1024.0);
}
Console.WriteLine("-----------------------------------------------------------");
Console.WriteLine("Total : {0,12:N0} bytes ({1:N2} MB)",
heap.Segments.Sum(s => (long)s.Length), heap.Segments.Sum(s => (long)s.Length) / 1024.0 / 1024.0);
Console.WriteLine("-----------------------------------------------------------");
Console.WriteLine();
}
}
class SymbolNotification : ISymbolNotification
{
public void DecompressionComplete(string localPath)
{
Console.WriteLine("DecompressionComplete: " + (localPath ?? "<NULL>"));
}
public void DownloadComplete(string localPath, bool requiresDecompression)
{
Console.WriteLine("DecompressionComplete: " + (localPath ?? "<NULL>"));
}
public void DownloadProgress(int bytesDownloaded)
{
Console.WriteLine("DownloadProgress: bytesDownloaded = " + bytesDownloaded);
}
public void FoundSymbolInCache(string localPath)
{
Console.WriteLine("FoundSymbolInCache: " + (localPath ?? "<NULL>"));
}
public void FoundSymbolOnPath(string url)
{
Console.WriteLine("FoundSymbolOnPath: " + (url ?? "<NULL>"));
}
public void ProbeFailed(string url)
{
Console.WriteLine("ProbeFailed: " + (url ?? "<NULL>"));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment