MiniDumps and "Bad" Stacks

A fellow reader sent a comment outlining the following problem:

 

I'm using the DMPSTK example from the Debugging Tools SDK. If I generate a crash dump from within Visual Studio 2005, I can see the call stack perfectly. However, if I create my own crash dump (the same as you do in your blog), I get several LoadLibrary(ext), LoadLibrary(exts), ... failures. What's more important to notice is that the callstacks are different.

 

The LoadLibrary messages are benign and just an artifact of the debugger attempting to load additional plug-ins. But with regard to the stack traces, things get more interesting.

 

It turns out that the crash dump files created with Visual Studio are slightly different from the ones created via the API MiniDumpWriteDump. On top of that, WinDBG and the debugging engine exposed by WinDBG handle things a little bit differently with regard these dumps. The main difference stems from the use of embedded "contexts". A context contains the basic state of the system (CPU registers for example), and crash dumps can contain an embedded context which gets added in if you pass down the appropriate exception details to MiniDumpWriteDump.

 

If you were to load up one of these API generated crash dump files in WinDBG itself, the program will kindly tell you that there is an additional context and in order to use it, you need to issue the ".ecxr" command. Until you do this, the stack trace you will see possibly is not the stack you are interested in. This is where things are going wrong for our reader, who was displaying the default stack via the code in DMPSTK sample.

 

Luckily for us, we can do the equivalent to .ecxr in our code allowing us to get the "correct" stack.

 

To start with we replace IDebugControl for IDebugControl4. This then gives us access to the GetStoredEventInformation and GetContextStackTrace methods.

 

If we were to modify the initial code from this blog entry, we would end up with something like this which lets us try to get the embedded context, otherwise just fall back on the old standby (GetStackTrace).

 

HRESULT DumpStack(IDebugControl4 *control, IDebugSymbols *symbols)

    {

    #define MAX_FRAMES 1024

    HRESULT hr = S_OK;

    DEBUG_STACK_FRAME stackFrames[MAX_FRAMES] = {0};

    ULONG numFrames = 0;

 

    char context[1024] = {0};

    ULONG type = 0;

    ULONG procID = 0;

    ULONG threadID = 0;

    ULONG contextSize = 0;

    char *contextData = NULL;

 

    // look for an embedded event

    hr = control->GetStoredEventInformation(&type, &procID, &threadID,

        context, sizeof(context), &contextSize, NULL, 0, 0);

 

    // get the stack trace

    if(SUCCEEDED(hr))

        {

        contextData = new char[MAX_FRAMES*contextSize];

        hr = control->GetContextStackTrace(context, contextSize,

            stackFrames, ARRAYSIZE(stackFrames), contextData,

            MAX_FRAMES*contextSize, contextSize, &numFrames);

        }

    else

        {

        hr = control->GetStackTrace(0, 0, 0, stackFrames,

            ARRAYSIZE(stackFrames), &numFrames);

        }

 

    ...

 

Just don't forget to delete the newly allocated "contextData" buffer at the end of the function.