Search code examples
debuggingwindbgdumpwindows-error-reporting

Retrieving the stack trace from the stored exception context in a minidump (similar to .ecxr; k)


Dumps obtained from Windows Error Reporting typically have a useless current context set on the faulting thread, with a stack deep in WerpReportFault. The actual context at the time of the exception can be retrieved with .ecxr -- which also sets the context in such a way that subsequent commands on the same thread (such as k) return the "correct" information.

I am building a tool for automatic dump analysis, which uses IDebugControl::GetStackTrace to obtain the faulting thread's stack. I can retrieve the stored exception context using IDebugControl4::GetStoredEventInformation. If I use the EBP/RBP, ESP/RSP, EIP/RIP values from the stored context with GetStackTrace, I get the correct stack. However, I would much rather replicate what the .ecxr command does, setting the "correct" state until the thread is switched. I tried using IDebugAdvanced::SetThreadContext, but it seems to be an illegal operation for dump targets and fails with 0x8000FFFF.

I tried to figure out what .ecxr does by debugging a WinDbg instance, and it looks like .ecxr is implemented in dbgeng!DotEcxr. However, from tracing it (with wt) I wasn't able to understand how it resets the current thread's context. It doesn't seem to call any of the COM debug-client interface methods, anyway, and doesn't use IDebugAdvanced::SetThreadContext.

Any suggestions on how to set the thread context in a dump file would be much appreciated. As a last resort, I can always use IDebugControl::Execute and simply invoke the .ecxr command, but I'd prefer a more programmatic approach.


Solution

  • .ecxr memcopies the context record

    to set the scope you can use this

    EXT_COMMAND( setscope, "setscope", "{;e,d=@$ip;!setscope;}" )
    {
        m_Symbols3->SetScopeFromStoredEvent();
    }
    

    after this call if you do k etc it will be for the last set context

    :\>cdb -z oktest.dmp
    Microsoft (R) Windows Debugger Version 10.0.10586.567 X86
    
    This dump file has a breakpoint exception stored in it.
    The stored exception information can be accessed via .ecxr.
    
    0:000> k
    ChildEBP RetAddr
    0007fb1c 7c940442 ntdll!DbgBreakPoint
    0007fc94 7c9210af ntdll!LdrpInitializeProcess+0xffa
    0007fd1c 7c90e457 ntdll!_LdrpInitialize+0x183
    00000000 00000000 ntdll!KiUserApcDispatcher+0x7
    
    
    0:000> .load setscope
    0:000> !setscope
    0:000> k
    
    
      *** Stack trace for last set context - .thread/.cxr resets it
    ChildEBP RetAddr
    0007fb1c 7c940442 ntdll!DbgBreakPoint
    0007fc94 7c9210af ntdll!LdrpInitializeProcess+0xffa
    0007fd1c 7c90e457 ntdll!_LdrpInitialize+0x183
    00000000 00000000 ntdll!KiUserApcDispatcher+0x7
    0:000>
    

    the full extension code including getstacktrace and outputstacktrace

    #include <codeanalysis\warnings.h>
    #pragma warning( push )
    #pragma warning ( disable : ALL_CODE_ANALYSIS_WARNINGS )
    #include <engextcpp.cpp>
    #pragma warning( pop )
    class EXT_CLASS : public ExtExtension 
    {
    public:
        EXT_COMMAND_METHOD(setscope);
    };
    EXT_DECLARE_GLOBALS();
    EXT_COMMAND( setscope, "setscope", "{;e,d=@$ip;!setscope;}" )
    {
        m_Symbols3->SetScopeFromStoredEvent();
        DEBUG_STACK_FRAME Frames[0x20] = {0};
        ULONG FramesFilled = NULL;
        m_Control->GetStackTrace(0,0,0,Frames,0x20,&FramesFilled);
        m_Control->OutputStackTrace(DEBUG_OUTCTL_THIS_CLIENT,Frames,FramesFilled,0x1fff);
    }
    

    executed kvf and setscope

    0:000> kVf
      *** Stack trace for last set context - .thread/.cxr resets it
     #   Memory  ChildEBP RetAddr  Args to Child              
    00           0007fb1c 7c940442 00000000 00000000 00000000 ntdll!DbgBreakPoint (FPO: [0,0,0])
    01       178 0007fc94 7c9210af 0007fd30 7c900000 0007fce0 ntdll!LdrpInitializeProcess+0xffa (FPO: [Non-Fpo])
    02        88 0007fd1c 7c90e457 0007fd30 7c900000 00000000 ntdll!_LdrpInitialize+0x183 (FPO: [Non-Fpo])
    03           00000000 00000000 00000000 00000000 00000000 ntdll!KiUserApcDispatcher+0x7
    0:000> !setscope
     #   Memory  ChildEBP RetAddr  Args to Child              
    00           0007fb1c 7c940442 00000000 00000000 00000000 ntdll!DbgBreakPoint (FPO: [0,0,0])
    01       178 0007fc94 7c9210af 0007fd30 7c900000 0007fce0 ntdll!LdrpInitializeProcess+0xffa (FPO: [Non-Fpo])
    02        88 0007fd1c 7c90e457 0007fd30 7c900000 00000000 ntdll!_LdrpInitialize+0x183 (FPO: [Non-Fpo])
    03           00000000 00000000 00000000 00000000 00000000 ntdll!KiUserApcDispatcher+0x7