Last week, the first public beta of the Microsoft.Diagnostics.Runtime library was released. This is a very cool library that can be used to write automated dump analysis of processes hosting the CLR.

One of the first things you will need in order to use ClrMD is get a hold of the DAC library for the specific version of the CLR that your dump/live process is using. If this is a local dump/process, then you'll have the DAC handy, as it will be part of your .NET Framework installation (example: c:\windows\Microsoft.NET\Framework64\v2.0.50727\mscordacwks.dll).

If you're inspecting a dump from another machine, then you could also copy the mscordacwks.dll from the right folder in the remote machine. A more interesting option, however, is to dynamically fetch the right DAC library from the public Microsoft Symbol Server. ClrMD does not have built-in code to do this, and it can be a bit tricky, but it's possible to implement it relatively easy in many scenarios.

In order to do this, you first need find two native libraries and copy them over to the same directory your application executable is located:

  • dbghelp.dll
  • symsrv.dll

The right place to pick these up is as part of the Debugging Tools For Windows package. Remember that you need to pick these for the right bitness (x64 or x86) depending on your process architecture, which needs to match the architecture of the dump/process you're going to be inspecting. For this case, I picked the debugger tools package that comes with the Windows 8 SDK. When you install the tools from the SDK installer, these get installed to c:\Program Files (x86)\Windows Kits\8.0\Debuggers.

For this sample, you need to put the x64 and x86 versions of the libraries in the corresponding folder under .\dbglibs in the project folder. A custom build action will then copy the right version over to the output directory:

copy "$(ProjectDir)dbglibs\$(PlatformName)\*.dll" "$(TargetDir)"

The relevant code can be found in the DacLocator class. This class will load the dbghelp.dll library and initialize it. Using it is relatively simple:

dacloc = DacLocator.FromPublicSymbolServer(localCachePathTextBox.Text);
DataTarget dt = DataTarget.LoadCrashDump(dumpFileTextBox.Text);
String dac = dacloc.FindDac(dt.ClrVersions[0]);

Here we're just initializing the library to use the public Symbol Server and using the specified path as the local cache, and then attempting to locate the DAC library that is required. Finding the DAC itself is done using the SymFindFileInPath() function:

StringBuilder symbolFile = new StringBuilder(2048);
if ( SymFindFileInPath(ourProcess.Handle, searchPath, dacname,
timestamp, fileSize, 0, 0x02, symbolFile, IntPtr.Zero, IntPtr.Zero) ) {
return symbolFile.ToString();
} else {
throw new Win32Exception(String.Format("SymFindFileInPath() failed: {0}", Marshal.GetLastWin32Error()));
}

The rest of the sample is pretty straightforward: It just iterates through all objects in the heap, looking for HttpContext items and then
printing out some basic details of each one:

private IEnumerable FindHttpContexts(ClrRuntime clr) {
ClrHeap heap = clr.GetHeap();
foreach ( ulong addr in heap.EnumerateObjects() ) {
ClrType type = heap.GetObjectType(addr);
if ( type == null ) continue;
if ( type.Name != "System.Web.HttpContext" ) continue;

yield return GetHttpContextInfo(heap, addr, type);
}
}

private HttpCtxtInfo GetHttpContextInfo(ClrHeap heap, ulong addr, ClrType type) {
HttpCtxtInfo info = new HttpCtxtInfo {
Address = addr
};
ulong reqAddr = (ulong)type.GetFieldByName("_request").GetFieldValue(addr);
ClrType reqType = heap.GetObjectType(reqAddr);
info.Method = (string)reqType.GetFieldByName("_httpMethod").GetFieldValue(reqAddr);
info.Url = (string)reqType.GetFieldByName("_rawUrl").GetFieldValue(reqAddr);
return info;
}

Running the sample app will look something like this:

[caption id="attachment_1354" align="alignnone" width="670"] DacSample Screenshot[/caption]

The sample code can be downloaded here. Enjoy!


Tomas Restrepo

Software developer located in Colombia.