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.
.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
I think there is no magic way in dbgeng to retain a register context and use it in every further API call, but if you get an exception context from IDebugControl4::GetStoredEventInformation() I believe you should prefer IDebugControl4::GetContextStackTrace() instead of tricking GetStackTrace().
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With