Debugging 从小型转储中存储的异常上下文检索堆栈跟踪(类似于.ecxr;k)

Debugging 从小型转储中存储的异常上下文检索堆栈跟踪(类似于.ecxr;k),debugging,windbg,dump,windows-error-reporting,Debugging,Windbg,Dump,Windows Error Reporting,从Windows错误报告中获得的转储通常在出错线程上设置了一个无用的当前上下文,堆栈位于werpreprirtfault的深处。异常发生时的实际上下文可以通过.ecxr检索——它还可以设置上下文,使同一线程上的后续命令(如k)返回“正确”信息 我正在构建一个用于自动转储分析的工具,它使用IDebugControl::GetStackTrace来获取出错线程的堆栈。我可以使用IDebugControl4::GetStoredEventInformation检索存储的异常上下文。如果将存储上下文中的

从Windows错误报告中获得的转储通常在出错线程上设置了一个无用的当前上下文,堆栈位于
werpreprirtfault
的深处。异常发生时的实际上下文可以通过
.ecxr
检索——它还可以设置上下文,使同一线程上的后续命令(如
k
)返回“正确”信息

我正在构建一个用于自动转储分析的工具,它使用
IDebugControl::GetStackTrace
来获取出错线程的堆栈。我可以使用
IDebugControl4::GetStoredEventInformation
检索存储的异常上下文。如果将存储上下文中的EBP/RBP、ESP/RSP、EIP/RIP值与
GetStackTrace
一起使用,则会得到正确的堆栈。但是,我更愿意复制
.ecxr
命令的功能,设置“正确”状态,直到切换线程。我尝试使用
IDebugAdvanced::SetThreadContext
,但它似乎对转储目标是非法的操作,并且在使用0x8000FFFF时失败

我试图通过调试WinDbg实例来了解
.ecxr
的功能,它看起来像
.ecxr
是在
dbgeng中实现的!DotEcxr
。然而,通过跟踪它(使用
wt
),我无法理解它是如何重置当前线程的上下文的。无论如何,它似乎没有调用任何COM调试客户端接口方法,也没有使用
IDebugAdvanced::SetThreadContext


任何关于如何在转储文件中设置线程上下文的建议都将不胜感激。作为最后的手段,我可以始终使用
IDebugControl::Execute
并简单地调用
.ecxr
命令,但我更喜欢一种编程方式。

.ecxr memcopies上下文记录

要设置范围,可以使用以下命令

EXT_COMMAND( setscope, "setscope", "{;e,d=@$ip;!setscope;}" )
{
    m_Symbols3->SetScopeFromStoredEvent();
}
在此调用之后,如果您执行k等操作,它将用于最后一组上下文

:\>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>
完整的扩展代码,包括getstacktrace和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);
}

我认为dbgeng中没有神奇的方法来保留寄存器上下文并在每次进一步的API调用中使用它,但是如果您从IDebugControl4::GetStoredEventInformation()获得异常上下文,我认为您应该更喜欢IDebugControl4::GetContextStackTrace(),而不是欺骗GetStackTrace()。听起来正是我需要的。
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