(Intentionally) Generating a Dr. Watson MiniDump file from managed code
For many years, Dr. Watson ‘minidump’ files have been one of the mainstays of the Windows debugging arsenal. Usually generated automatically, they contain stack trace information, a list of loaded modules, and a chunk of memory at the time of the crash, and other related info to help track down errors.
Since I moved primarily to C#, I’ve had less call to dig in at that level. There are some circumstances, however, where getting a peek under the CLR at the underlying Win32 app comes in handy. One of those times came up a couple years back, and since I couldn’t find much love on the search engines, here’s a quick review of my findings. This was originally posted to an old blog at the time, but it's kind of fun and figured I'd move it over to my more permanent home. Enjoy!
Follow up:
Under the CLR:
A lot of readers might not realize the relationship between the .NET Common Language Runtime and Windows as a whole, in many cases it’s handled smoothly enough that you don’t even think about it. Each time you load up a .NET application, a Win32 process is launched, and it in turn load (via COM) the CLR from MsCorEE.dll (and then MsCorWrks.dll). Each instance of each .NET application then uses its own copy of the Common Language Runtime, and loads up the application code to be run from the assembly and starts running it.
Most .NET applications just rely on the default loader host within the .EXE file or the ASP.NET framework, but any Win32 application can host an instance of the CLR – it’s just a COM component after all, and with 6-7 lines of C++ you’ve got managed code from inside your unmanaged Win32 app.
P/Invoking the Doctor:
We just moved up a level in the technology stack, with the CLR existing as a COM component running in a Win32 process. You might already know about COM Inter-op as a mechanism for pushing back down that level to COM, but in this case we find ourselves looking at a non-COM, plane-jane Win32 dll.
The dbghelp.dll file is the key to Dr. Watson, containing the code that generates the minidump file. It made its first appearance (I think) in 1992 as part of Windows 3.1 and has been a cornerstone of Windows debugging ever since. In particular, the function MiniDumpWriteDump will be our focus, it will generate a minidump file and output it to a location of our choosing.
P/Invoke (Platform Invoke) is the .NET mechanism to communicate with the platform underneath, in our case, Win32. I won’t go into great depth here, there are fine explanations elsewhere, but here’s an example from the aforementioned link:
Besides providing the opportunity for rich extensibility and customization, it’s just pretty cool! And, since there’s a Win32 app lurking under the CLR, we can have the good Dr. tell us a few things about it.
DllImport("Kernel32.dll")]
static extern Boolean Beep(UInt32 frequency, UInt32 duration);
Fairly simple; C/C++ programmers will recognize the extern keyword, and .NET folks will note the use of the DllImport attribute. Between the two, we are defining a contract with the external DLL.
From there, a check of the MSDN documentation for MiniDumpWriteDump shows us the signature of the Win32 function:
BOOL MiniDumpWriteDump
(
HANDLE hProcess,
DWORD ProcessId,
HANDLE hFile,
MINIDUMP_TYPE DumpType,
PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, PMINIDUMP_CALLBACK_INFORMATION CallbackParam
);
The only real trick is matching that contract right in C#, or we’ll get a funky “unbalanced stack” error. It took a few tries, but here’s what I ended up with for the declaration:
internal enum MINIDUMP_TYPE
{
MiniDumpNormal = 0x00000000,
MiniDumpWithDataSegs = 0x00000001,
MiniDumpWithFullMemory = 0x00000002,
MiniDumpWithHandlData = 0x00000004,
// ... and it goes on
}
[DllImport("dbghelp.dll")]
static extern bool MiniDumpWriteDump(
IntPtr hProcess,
Int32 ProcessId,
IntPtr hFile,
MINIDUMP_TYPE DumpType,
IntPtr ExceptionParam,
IntPtr UserStreamParam,
IntPtr CallackParam
);
And then to call it:
string tempFile = Path.GetTempFileName();
FileStream fs = new FiileStream(tempFile, FileMode.Create);
Process thisProcess = Process.GetCurrentProcess();
MiniDumpWriteDump(
thisProcess.Handle,
thisProcess.Id,
fs.SafeFileHandle.DangerousGetHandle(), MINIDUMP_TYPE.MiniDumpNormal,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero
);
And there you have it. The Mini-dump file will be save to the location you specified, and it can then be analyzed by any tool that can analyze any other Dr. Watson minidump.
09/17/09 01:54:15 pm,