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

问题描述:

从Windows错误报告中获取的转储通常在故障线程上设置了无用的当前上下文,堆栈深度为WerpReportFault。异常时的实际上下文可以用.ecxr来检索 - 它也以这样的方式设置上下文,使得同一线程上的后续命令(例如k)返回“正确”信息。在小型转储中从存储的异常上下文中检索堆栈跟踪(类似于.ecxr; k)

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

我试图找出.ecxr通过调试WinDbg实例来做什么,它看起来像.ecxrdbgeng!DotEcxr中实现。但是,从跟踪它(与wt),我无法理解它是如何重置当前线程的上下文。它似乎没有调用任何COM调试客户端接口方法,并且不使用IDebugAdvanced::SetThreadContext

有关如何设置转储文件中的线程上下文的任何建议将不胜感激。作为最后的手段,我总是可以使用IDebugControl::Execute并简单地调用.ecxr命令,但我更喜欢更具编程性的方法。

.ecxr memcopies背景记录

设定的范围,你可以使用这个

EXT_COMMAND(setscope, "setscope", "{;e,[email protected]$ip;!setscope;}") 
{ 
    m_Symbols3->SetScopeFromStoredEvent(); 
} 

此调用后,如果您还ķ等这将是最后一组背景

:\>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,[email protected]$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); 
} 

执行KVF和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 
+0

太棒了。听起来正是我需要的。 –

我认为在dbgeng什么神奇的方法,以保持寄存器上下文,并在每个进一步的API调用使用它,但如果你从IDebugControl4 :: GetStoredEventInformation异常情况下( )我相信你应该更喜欢IDebugControl4 :: GetContextStackTrace()而不是欺骗GetStackTrace()。