从小型转储中存储的异常上下文中检索堆栈跟踪(类似于 .ecxr;k)
Retrieving the stack trace from the stored exception context in a minidump (similar to .ecxr; k)
从 Windows 错误报告中获得的转储通常在错误线程上设置了无用的当前上下文,堆栈位于 WerpReportFault
深处。可以使用 .ecxr
检索异常发生时的实际上下文——它还以相同线程上的后续命令(例如 k
)return 的方式设置上下文"correct" 信息。
我正在构建一个自动转储分析工具,它使用 IDebugControl::GetStackTrace
获取故障线程的堆栈。我可以使用 IDebugControl4::GetStoredEventInformation
检索存储的异常上下文。如果我将存储上下文中的 EBP/RBP、ESP/RSP、EIP/RIP 值与 GetStackTrace
一起使用,我会得到正确的堆栈。但是,我更愿意复制 .ecxr
命令的作用,设置 "correct" 状态直到线程被切换。我尝试使用 IDebugAdvanced::SetThreadContext
,但它似乎是对转储目标的非法操作,并且失败并返回 0x8000FFFF。
我试图通过调试 WinDbg 实例来弄清楚 .ecxr
做了什么,看起来 .ecxr
是在 dbgeng!DotEcxr
中实现的。但是,通过跟踪它(使用 wt
)我无法理解它如何重置当前线程的上下文。无论如何,它似乎没有调用任何 COM 调试客户端接口方法,也没有使用 IDebugAdvanced::SetThreadContext
.
如有任何关于如何在转储文件中设置线程上下文的建议,我们将不胜感激。作为最后的手段,我总是可以使用 IDebugControl::Execute
并简单地调用 .ecxr
命令,但我更喜欢更编程的方法。
.ecxr memcopy 上下文记录
设置范围你可以使用这个
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);
}
执行了 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
我认为在 dbgeng 中没有神奇的方法可以保留寄存器上下文并在以后的每个 API 调用中使用它,但是如果您从 IDebugControl4::GetStoredEventInformation() 获得异常上下文,我相信您应该更喜欢 IDebugControl4::GetContextStackTrace() 而不是欺骗 GetStackTrace().
从 Windows 错误报告中获得的转储通常在错误线程上设置了无用的当前上下文,堆栈位于 WerpReportFault
深处。可以使用 .ecxr
检索异常发生时的实际上下文——它还以相同线程上的后续命令(例如 k
)return 的方式设置上下文"correct" 信息。
我正在构建一个自动转储分析工具,它使用 IDebugControl::GetStackTrace
获取故障线程的堆栈。我可以使用 IDebugControl4::GetStoredEventInformation
检索存储的异常上下文。如果我将存储上下文中的 EBP/RBP、ESP/RSP、EIP/RIP 值与 GetStackTrace
一起使用,我会得到正确的堆栈。但是,我更愿意复制 .ecxr
命令的作用,设置 "correct" 状态直到线程被切换。我尝试使用 IDebugAdvanced::SetThreadContext
,但它似乎是对转储目标的非法操作,并且失败并返回 0x8000FFFF。
我试图通过调试 WinDbg 实例来弄清楚 .ecxr
做了什么,看起来 .ecxr
是在 dbgeng!DotEcxr
中实现的。但是,通过跟踪它(使用 wt
)我无法理解它如何重置当前线程的上下文。无论如何,它似乎没有调用任何 COM 调试客户端接口方法,也没有使用 IDebugAdvanced::SetThreadContext
.
如有任何关于如何在转储文件中设置线程上下文的建议,我们将不胜感激。作为最后的手段,我总是可以使用 IDebugControl::Execute
并简单地调用 .ecxr
命令,但我更喜欢更编程的方法。
.ecxr memcopy 上下文记录
设置范围你可以使用这个
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);
}
执行了 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
我认为在 dbgeng 中没有神奇的方法可以保留寄存器上下文并在以后的每个 API 调用中使用它,但是如果您从 IDebugControl4::GetStoredEventInformation() 获得异常上下文,我相信您应该更喜欢 IDebugControl4::GetContextStackTrace() 而不是欺骗 GetStackTrace().