Winapi 为什么';使用SetUnhandledExceptionFilter时堆栈遍历是否正常工作?
我使用以下代码在异常情况下遍历堆栈(注意:您必须在release中运行它,以便正确地将所需的堆栈跟踪输出接收到控制台,而不是在调试模式下,否则它将只显示弹出窗口):Winapi 为什么';使用SetUnhandledExceptionFilter时堆栈遍历是否正常工作?,winapi,exception,visual-c++,stack-trace,dbghelp,Winapi,Exception,Visual C++,Stack Trace,Dbghelp,我使用以下代码在异常情况下遍历堆栈(注意:您必须在release中运行它,以便正确地将所需的堆栈跟踪输出接收到控制台,而不是在调试模式下,否则它将只显示弹出窗口): 编辑:在与OP的作者进行了广泛的讨论后,我们找到了当只有最顶层的函数function0()在调用堆栈跟踪中“注册”时“意外”行为的解释。事实上,正如评论部分很早就推测的那样,这是因为所有其他函数都内联在发布版本中。使用装饰所有函数可以确保它们不是内联的。在这种情况下,将获得预期的调用堆栈跟踪。。。下面描述的处理C++/SE异常的方案
编辑:在与OP的作者进行了广泛的讨论后,我们找到了当只有最顶层的函数
function0()
在调用堆栈跟踪中“注册”时“意外”行为的解释。事实上,正如评论部分很早就推测的那样,这是因为所有其他函数都内联在发布版本中。使用装饰所有函数可以确保它们不是内联的。在这种情况下,将获得预期的调用堆栈跟踪。。。下面描述的处理C++/SE异常的方案仍然有效,尽管这无助于OP作者的问题,因为他无法更改生产代码,并且只能处理未处理的异常编辑结束。
只是在评论部分变得太长之前快速回答一下
.map
文件获取函数名DbgHelp.dll
中的函数是单线程的,应该在整个过程中调用一次。这意味着::SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS)代码>,::符号初始化(::GetCurrentProcess(),0,1)代码>和<代码>::符号清理(::GetCurrentProcess())应分别在main()
的开头和结尾调用code>
跟踪C++异常调用栈,将堆栈跟踪放入自定义C++异常类构造函数中。这样,当您
抛出MyException()时代码>和MyException
对象正在构造中,您可以跟踪调用堆栈
< > >同样,当SE被提升时(如除以零),则使用<代码>“StasySeSlTraclOrthor”< />代码,并生成一个翻译器函数,将C++异常类对象抛出“<代码>例外的指针*< /Cl> >传递给它的构造函数。然后使用EXCEPTION\u指针*
保存线程上下文以跟踪调用堆栈
基本上,您有一个自定义的C++异常类,它的构造函数看起来像这样(警告:未测试):
因此,要将它与您使用的throw
和\u set\u se\u translator
一起使用
throw MyException();
及
使用最近的dbghelp.dll(以及相应的最近的MSVC),您还可以找到内联帧的函数/位置
将此添加到StackWalk()之前。
:
将其添加到StackWalk()
循环中(但在SymFromAddr()
之前):
DWORD64 addr=frame.AddrPC.Offset;
//确保报告了调用函数的位置,而不是下一条语句的位置
如果(帧数!=0&&addr!=0)addr--;
frameCount++;
//内联帧数(如果有)
DWORD inlineTrace=SymAddrIncludeInlineTrace(进程,地址);
如果(inlineTrace!=0)
{
DWORD-inlineContext,frameIndex;
//需要内联上下文
if(SymQueryLineTrace(进程、地址、0、地址、地址和inlineContext及框架索引))
{
for(DWORD i=0;i名称,行->文件名,行->行号,符号->地址);
}
}
//提升以获取下一个内联帧的内联上下文
inlineContext++;
}
}
}
我无意中发现了这一页,因为我一直很难从异常中获取正确的文件名/行号,尽管手动创建了致命错误以生成正确的堆栈跟踪。结果很简单,包括PDB确保所有的调用栈都尽可能精确。< / P>很好的一件事,你正在混合SEH和C++异常…它们是非常不同的,并不是真正的可互操作的,或者被设计为一起使用。它可以是我,但是我不能在代码中找到<代码>函数0>(代码)的声明/定义……Alxand Rug。我说,Windows上的C++异常是在SEH的顶部构建的(根据我所看到的所有文档),这是一个实现定义的细节,可能会随时更改。即使在不同版本之间,微软也可以自由更改。这是许多原因之一,当您可以在内核模式中使用SEH时,您不能使用C++异常(另一个主要原因是RTTI的不足)。@ Alexandru不应该在<代码> unHealDeExpExtudiFielter()/<代码>中调用<代码> SyMiTyLalIZE()/Cuff>。您应该在此之前调用SymSetOptions()
以延迟加载。。。然后我会检查你的小函数是否没有内联。并尝试提高SE,而不是抛出C++异常,正如Mgetz指出的只是检查…这里是我知道的StActTrack,可以检查比较.s。通常,你把StActTrack放到C++异常的类构造函数中,并在抛出异常时进行跟踪。你也可以使用<代码> StasySeSUTROLATORE()/SCOD>将SE转换成C++并在那里进行跟踪,因为<代码> StasySeSIsTraceOrthor()/代码>给了你保存的线程上下文。嘿,那么你在什么时候调用StySysEx翻译者来设置翻译方法?它必须在每个线程中吗?是的,您为每个线程安装转换器-转换器是基于每个线程进行维护的。在每个线程中尽早设置它
Press any key to exit.
at threadFunction in c:\users\<youruseraccount>\documents\visual studio 2013\project
s\stacktracing\stacktracing\stacktracing.cpp: line: 135: address: 0x498B12D0
Wrote a dump.
#include "stdafx.h"
#include <process.h>
#include <iostream>
#include <Windows.h>
#include "dbghelp.h"
using namespace std;
#define TRACE_MAX_FUNCTION_NAME_LENGTH 1024
#define TRACE_LOG_ERRORS FALSE
#define TRACE_DUMP_NAME L"Exception.dmp"
void function2()
{
int a = 0;
int b = 0;
// The loop below should throw an unhandled exception.
for (int *i = 0; *i < 100; i++)
{
*i = 10000;
}
}
void function1()
{
int a = 0;
function2();
}
void function0()
{
function1();
}
void ShowStackTrace(EXCEPTION_POINTERS* exception)
{
CONTEXT context = *(exception->ContextRecord);
HANDLE thread = GetCurrentThread();
HANDLE process = GetCurrentProcess();
STACKFRAME64 frame;
memset(&frame, 0, sizeof(STACKFRAME64));
DWORD image;
#ifdef _M_IX86
image = IMAGE_FILE_MACHINE_I386;
frame.AddrPC.Offset = context.Eip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Ebp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Esp;
frame.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
image = IMAGE_FILE_MACHINE_AMD64;
frame.AddrPC.Offset = context.Rip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Rbp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Rsp;
frame.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
image = IMAGE_FILE_MACHINE_IA64;
frame.AddrPC.Offset = context.StIIP;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.IntSp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrBStore.Offset = context.RsBSP;
frame.AddrBStore.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.IntSp;
frame.AddrStack.Mode = AddrModeFlat;
#else
#error "This platform is not supported."
#endif
SYMBOL_INFO *symbol = (SYMBOL_INFO *)malloc(sizeof(SYMBOL_INFO)+(TRACE_MAX_FUNCTION_NAME_LENGTH - 1) * sizeof(TCHAR));
symbol->MaxNameLen = TRACE_MAX_FUNCTION_NAME_LENGTH;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
IMAGEHLP_LINE64 *line = (IMAGEHLP_LINE64 *)malloc(sizeof(IMAGEHLP_LINE64));
line->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
DWORD displacement;
SymInitialize(process, NULL, TRUE);
while (StackWalk(image, process, thread, &frame, &context, NULL, NULL, NULL, NULL))
{
if (SymFromAddr(process, frame.AddrPC.Offset, NULL, symbol))
{
if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &displacement, line))
{
printf("\tat %s in %s: line: %lu: address: 0x%0X\n", symbol->Name, line->FileName, line->LineNumber, symbol->Address);
}
else if (TRACE_LOG_ERRORS)
{
printf("Error from SymGetLineFromAddr64: %lu.\n", GetLastError());
}
}
else if (TRACE_LOG_ERRORS)
{
printf("Error from SymFromAddr: %lu.\n", GetLastError());
}
}
DWORD error = GetLastError();
if (error && TRACE_LOG_ERRORS)
{
printf("Error from StackWalk64: %lu.\n", error);
}
HANDLE dumpFile = CreateFile(TRACE_DUMP_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
MINIDUMP_EXCEPTION_INFORMATION exceptionInformation;
exceptionInformation.ThreadId = GetCurrentThreadId();
exceptionInformation.ExceptionPointers = exception;
exceptionInformation.ClientPointers = FALSE;
if (MiniDumpWriteDump(process, GetProcessId(process), dumpFile, MiniDumpNormal, exception ? &exceptionInformation : NULL, NULL, NULL))
{
printf("Wrote a dump.");
}
}
void ShowStackTrace(CONTEXT *aContext)
{
CONTEXT context = *aContext;
HANDLE thread = GetCurrentThread();
HANDLE process = GetCurrentProcess();
STACKFRAME64 frame;
memset(&frame, 0, sizeof(STACKFRAME64));
DWORD image;
#ifdef _M_IX86
image = IMAGE_FILE_MACHINE_I386;
frame.AddrPC.Offset = context.Eip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Ebp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Esp;
frame.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
image = IMAGE_FILE_MACHINE_AMD64;
frame.AddrPC.Offset = context.Rip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Rbp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Rsp;
frame.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
image = IMAGE_FILE_MACHINE_IA64;
frame.AddrPC.Offset = context.StIIP;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.IntSp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrBStore.Offset = context.RsBSP;
frame.AddrBStore.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.IntSp;
frame.AddrStack.Mode = AddrModeFlat;
#else
#error "This platform is not supported."
#endif
SYMBOL_INFO *symbol = (SYMBOL_INFO *)malloc(sizeof(SYMBOL_INFO)+(TRACE_MAX_FUNCTION_NAME_LENGTH - 1) * sizeof(TCHAR));
symbol->MaxNameLen = TRACE_MAX_FUNCTION_NAME_LENGTH;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
IMAGEHLP_LINE64 *line = (IMAGEHLP_LINE64 *)malloc(sizeof(IMAGEHLP_LINE64));
line->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
DWORD displacement;
SymInitialize(process, NULL, TRUE);
while (StackWalk(image, process, thread, &frame, &context, NULL, NULL, NULL, NULL))
{
if (SymFromAddr(process, frame.AddrPC.Offset, NULL, symbol))
{
if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &displacement, line))
{
printf("\tat %s in %s: line: %lu: address: 0x%0X\n", symbol->Name, line->FileName, line->LineNumber, symbol->Address);
}
else if (TRACE_LOG_ERRORS)
{
printf("Error from SymGetLineFromAddr64: %lu.\n", GetLastError());
}
}
else if (TRACE_LOG_ERRORS)
{
printf("Error from SymFromAddr: %lu.\n", GetLastError());
}
}
DWORD error = GetLastError();
if (error && TRACE_LOG_ERRORS)
{
printf("Error from StackWalk64: %lu.\n", error);
}
HANDLE dumpFile = CreateFile(TRACE_DUMP_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (MiniDumpWriteDump(process, GetProcessId(process), dumpFile, MiniDumpNormal, NULL, NULL, NULL))
{
printf("Wrote a dump.");
}
}
class CustomException {
public:
CustomException(EXCEPTION_POINTERS *exception = nullptr)
{
CONTEXT context;
ZeroMemory(&context, sizeof(CONTEXT));
if (exception)
{
// In case of an SEH exception.
ShowStackTrace(exception);
}
else
{
// In case of a C++ exception.
RtlCaptureContext(&context);
ShowStackTrace(&context);
}
}
};
void SEHExceptionTranslator(unsigned int, EXCEPTION_POINTERS *exception){
throw CustomException(exception);
}
static void threadFunction(void *param)
{
_set_se_translator(SEHExceptionTranslator);
function0();
}
LONG WINAPI UnhandledExceptionFilter(PEXCEPTION_POINTERS exception)
{
CONTEXT context = *(exception->ContextRecord);
HANDLE thread = GetCurrentThread();
HANDLE process = GetCurrentProcess();
STACKFRAME64 frame;
memset(&frame, 0, sizeof(STACKFRAME64));
DWORD image;
#ifdef _M_IX86
image = IMAGE_FILE_MACHINE_I386;
frame.AddrPC.Offset = context.Eip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Ebp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Esp;
frame.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
image = IMAGE_FILE_MACHINE_AMD64;
frame.AddrPC.Offset = context.Rip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Rbp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Rsp;
frame.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
image = IMAGE_FILE_MACHINE_IA64;
frame.AddrPC.Offset = context.StIIP;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.IntSp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrBStore.Offset = context.RsBSP;
frame.AddrBStore.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.IntSp;
frame.AddrStack.Mode = AddrModeFlat;
#else
#error "This platform is not supported."
#endif
SYMBOL_INFO *symbol = (SYMBOL_INFO *)malloc(sizeof(SYMBOL_INFO)+(TRACE_MAX_FUNCTION_NAME_LENGTH - 1) * sizeof(TCHAR));
symbol->MaxNameLen = TRACE_MAX_FUNCTION_NAME_LENGTH;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
IMAGEHLP_LINE64 *line = (IMAGEHLP_LINE64 *)malloc(sizeof(IMAGEHLP_LINE64));
line->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
DWORD displacement;
while (StackWalk(image, process, thread, &frame, &context, NULL, NULL, NULL, NULL))
{
if (SymFromAddr(process, frame.AddrPC.Offset, NULL, symbol))
{
if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &displacement, line))
{
printf("\tat %s in %s: line: %lu: address: 0x%0X\n", symbol->Name, line->FileName, line->LineNumber, symbol->Address);
}
else if (TRACE_LOG_ERRORS)
{
printf("Error from SymGetLineFromAddr64: %lu.\n", GetLastError());
}
}
else if (TRACE_LOG_ERRORS)
{
printf("Error from SymFromAddr: %lu.\n", GetLastError());
}
}
DWORD error = GetLastError();
if (error && TRACE_LOG_ERRORS)
{
printf("Error from StackWalk64: %lu.\n", error);
}
HANDLE dumpFile = CreateFile(TRACE_DUMP_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
MINIDUMP_EXCEPTION_INFORMATION exceptionInformation;
exceptionInformation.ThreadId = GetCurrentThreadId();
exceptionInformation.ExceptionPointers = exception;
exceptionInformation.ClientPointers = FALSE;
if (MiniDumpWriteDump(process, GetProcessId(process), dumpFile, MiniDumpNormal, exception ? &exceptionInformation : NULL, NULL, NULL))
{
printf("Wrote a dump.");
}
return EXCEPTION_CONTINUE_SEARCH;
}
int _tmain(int argc, _TCHAR* argv[])
{
SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS);
SymInitialize(GetCurrentProcess(), NULL, TRUE);
_set_se_translator(SEHExceptionTranslator);
SetUnhandledExceptionFilter(UnhandledExceptionFilter);
_beginthread(threadFunction, 0, NULL);
printf("Press any key to exit.\n");
cin.get();
SymCleanup(GetCurrentProcess());
return 0;
}
class MyException {
public:
MyException(EXCEPTION_POINTERS * _ptr = nullptr) {
::CONTEXT context_;
::ZeroMemory( &context_, sizeof(::CONTEXT));
CONTEXT * pcontext_ = &context_;
if(_ptr) pcontext_ = _ptr->ContextRecord; // in case of SE translator
else ::RtlCaptureContext(&context_); // in case of 'throw MyException();'
// Call-stack tracing using pcontext_ here...
}
// Other stuff for MyException class...
};
throw MyException();
void my_translator(unsigned int, EXCEPTION_POINTERS * _ptr){
throw MyException(_ptr);
}
DWORD frameCount = 0;
DWORD64 addr = frame.AddrPC.Offset;
// make sure the location of the calling function is reported, and not of the next statement
if (frameCount != 0 && addr != 0) addr--;
frameCount++;
// number of inlined frames, if any
DWORD inlineTrace = SymAddrIncludeInlineTrace (process, addr);
if (inlineTrace != 0)
{
DWORD inlineContext, frameIndex;
// the inline context is needed
if (SymQueryInlineTrace (process, addr, 0, addr, addr, &inlineContext, &frameIndex))
{
for (DWORD i = 0; i < inlineTrace; i++)
{
DWORD64 displacement64 = 0;
// similar to SymFromAddr()
if (SymFromInlineContext (process, addr, inlineContext, &displacement64, symbol))
{
DWORD displacement = 0;
// similar to SymGetLineFromAddr64()
if (SymGetLineFromInlineContext (process, addr, inlineContext, 0, &displacement, line))
{
printf("inline: at %s in %s: line: %lu: address: 0x%0X\n",
symbol->Name, line->FileName, line->LineNumber, symbol->Address);
}
}
// raise to get inline context of next inlined frame
inlineContext++;
}
}
}