Little bit of ClrMD + WinDBG = Profit

Sometimes little bit of C# and ClrMD can save a lot of time when investigating a crash dump…

and the follwing is exactly the case in question.
While debugging a crash dump of a production issue, I saw the following line in the output of !dumpheap -stat command in WinDBG:

1
00007fff8638eb68 24357247    584573928 System.WeakReference`1[[Raven.Database.Config.ILowMemoryHandler, Raven.Database]]

Investigating ~24 million entries on what goes on there would obviously take ALOT of time, and randomly selecting couple of entries returned that WeakReference pointed to NULLs.
I suspected that most of the WeakReferences point to null, but what about non-null ones? Manually, its too hard to find-out… So, ClrMD to the rescue! After some mucking around and some experimentation, I came up with the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using (var session = ClrMDSession.LoadCrashDump(@"c:\path\to\the\dump.dmp"))
{
var weakRefType = session.Heap.GetTypeByName("System.WeakReference");
var weakRefHandleField = weakRefType.GetFieldByName("m_handle");
var IntPtrType = session.Heap.GetTypeByName("System.IntPtr");
var IntPtrValueField = IntPtrType.GetFieldByName("m_value");

//walk the heap, on large dumps this can take some time
foreach (var objRef in session.Heap.EnumerateObjects())
{
var objType = session.Heap.GetObjectType(objRef);
if (objType.Name.Contains("System.WeakReference") &&
objType.Name.Contains("ILowMemoryHandler"))
{
var handleAddr = (long) weakRefHandleField.GetValue(objRef);
var weakRefTargetRef = (ulong) IntPtrValueField.GetValue((ulong) handleAddr, true);

//we are interested only in WeakReferences that point on actual objects
if (weakRefTargetRef != 0)
{
var objectType = session.Heap.GetObjectType(weakRefTargetRef);
if(objectType == null) //heap corruption? (according to ClrMd docs it is...)
continue;
Console.WriteLine("{0} (gen. {1})", objectType.Name, session.Heap.GetGeneration(weakRefTargetRef));
}
}
}
}

Most likely this code can be made more efficient and optimized yes. Yet, it did it’s job - a throwaway code to investigate specific issue, that otherwise would take too long to investigate manually.

Oh, and in case you are wondering, what the heck is ClrMDSession class in the snippet - I used in this code an awesome ClrMD.Extensions library that streamlines and simplifies the use of ClrMD, and also provides some pretty neat method extensions for common ClrMD related tasks.
You can take a look at it in it’s GitHub repository.

So… as I said - profit!
profit.jpg