Winapi 为什么';使用SetUnhandledExceptionFilter时堆栈遍历是否正常工作?

Winapi 为什么';使用SetUnhandledExceptionFilter时堆栈遍历是否正常工作?,winapi,exception,visual-c++,stack-trace,dbghelp,Winapi,Exception,Visual C++,Stack Trace,Dbghelp,我使用以下代码在异常情况下遍历堆栈(注意:您必须在release中运行它,以便正确地将所需的堆栈跟踪输出接收到控制台,而不是在调试模式下,否则它将只显示弹出窗口): 编辑:在与OP的作者进行了广泛的讨论后,我们找到了当只有最顶层的函数function0()在调用堆栈跟踪中“注册”时“意外”行为的解释。事实上,正如评论部分很早就推测的那样,这是因为所有其他函数都内联在发布版本中。使用装饰所有函数可以确保它们不是内联的。在这种情况下,将获得预期的调用堆栈跟踪。。。下面描述的处理C++/SE异常的方案

我使用以下代码在异常情况下遍历堆栈(注意:您必须在release中运行它,以便正确地将所需的堆栈跟踪输出接收到控制台,而不是在调试模式下,否则它将只显示弹出窗口):


编辑:在与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++;
            }
        }
    }