Getting the Stack from a .DMP File (Automating Crash Dump Analysis Part 2)

So now that we have a memory dump file, and know how to open it, we will want to pull some useful data out. To start with, we will grab the stack trace (which is arguably one of the more important pieces). And in case you've stumbled across the "dumpstk" sample in the WinDBG install\sdk directory, we will do things a little bit differently and not call OutputStackTrace() for these reasons:

1. By enumerating the stack structures directly, we have more control over how to format the results.

2. We don't have to deal with supplying an IDebugOutputCallbacks implementation (see the out.cpp file which accompanies the "dumpstk" sample).

3. We have individual control over each frame (OutputStackTrack can "chunk" up frames as they are sent to the IDebugOutputCallback interface).

 

Here is a sample function that takes pointers to the IDebugControl and IDebugSymbols interfaces (which are opened in part 1) and will dump the stack trace with some good old fashioned printf statements.

 

HRESULT DumpStack(IDebugControl *control, IDebugSymbols *symbols)

    {

    HRESULT hr = S_OK;

    DEBUG_STACK_FRAME stackFrames[4096] = {0};

    ULONG numFrames = 0;

 

    // get the stack trace

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

        &numFrames);

 

    if(FAILED(hr))

        goto cleanup;

 

    printf("\nStack Trace\n");

 

    // dump out the details for each function call

    for(ULONG frame=0; frame<numFrames; frame++)

        {

        HRESULT symhr;

        char name[512];

        unsigned __int64 offset = 0;

        ULONG lineNo = 0;

 

        // get the module, function, and offset

        ZeroMemory(name, ARRAYSIZE(name));

        symhr = symbols->GetNameByOffset(stackFrames[frame].InstructionOffset,

            name, ARRAYSIZE(name)-1, NULL, &offset);

 

        if(SUCCEEDED(symhr))

            printf("%s+0x%I64X", name, offset);

        else

            printf("0x%08I64X", stackFrames[frame].InstructionOffset);

 

        // look for source file name and line number

        ZeroMemory(name, ARRAYSIZE(name));

        symhr = symbols->GetLineByOffset(stackFrames[frame].InstructionOffset,

            &lineNo, name, ARRAYSIZE(name)-1, NULL, NULL);

 

        if(SUCCEEDED(symhr))

            printf("\n %s(%u)", name, lineNo);

 

        printf("\n");

        }

 

    cleanup:

 

    return hr;

    }

 

Everything here is pretty straight forward: we retrieve the stack frame data, then just walk through each element of the populated array and pull out the module, the function name, and if we have full symbols, the filename and line number of the source code.

 

One thing you might want to add here is some intelligence about detecting recursive sequences. If a stack trace is extremely long and filled with repeating entries, it might be better to just write out the top and bottom of the stack and omit the majority of the redundant line items.

MSDN References

· DEBUG_STACK_FRAME

· GetStackTrace

· GetNameByOffset

· GetLineByOffset