【问题标题】:What actions do I need to take to get a crash dump in ALL error scenarios?在所有错误情况下,我需要采取哪些操作来获取故障转储?
【发布时间】:2012-11-15 11:50:11
【问题描述】:

我们在 Windows 上,我们希望为应用程序意外退出的所有场景获取故障转储(可能使用MiniDumpWriteDump)。

到目前为止,我们已经确定并设置了以下内容:

  • SetUnhandledExceptionFilter 用于未处理的异常(Win32 以及“普通”C++ 异常。)
  • _set_invalid_parameter_handler 用于 CRT 无效参数处理
  • _set_abort_behavior加上一个 SIGABRT 处理程序来说明对 abort() 的调用

我们错过了什么吗? (取模一些非法调用ExitProcessTerminateProcessexit 变体之一的代码。)


我会注意到,这里的这个问题与如何然后获得故障转储是正交的。例如,如果您想在abort 的情况下进行故障转储,则必须始终使用_set_abort_behaviour,否则只会中止exits。

我还要注意,在 Windows7+ 上,设置 SetUHEF 而只设置 "correct" WER dump settings in the registry 通常是一种可行的方法。

【问题讨论】:

  • 您是否考虑过使用 crashrpt 之类的东西:不过,它的目的是生成堆栈回溯(文本)而不是小型转储。 code.google.com/p/crashrpt
  • 调用堆栈文件(文本)也可以在全局异常处理程序中使用StackWalk64 生成。
  • 我记得微软声称尝试在进程内进行操作是危险的。如果应用程序遇到异常,您将无法信任应用程序的状态。虽然在很多很多情况下,您都会侥幸逃脱(就像许多流行的商业软件一样),但要抓住每个案例的唯一方法是在单独的进程中设置一个看门狗。
  • @AdrianMcCarthy - 是的,in-process 不是最理想的,但大部分时间都有效。不过我会添加一个注释。

标签: c++ windows visual-c++ crash-dumps


【解决方案1】:

我使用的正是你列出的那些,加上_set_purecall_handler,加上这个方便的sn-p代码:

void EnableCrashingOnCrashes()
{
    typedef BOOL (WINAPI *tGetPolicy)(LPDWORD lpFlags);
    typedef BOOL (WINAPI *tSetPolicy)(DWORD dwFlags);
    static const DWORD EXCEPTION_SWALLOWING = 0x1;

    const HMODULE kernel32 = LoadLibraryA("kernel32.dll");
    const tGetPolicy pGetPolicy = (tGetPolicy)GetProcAddress(kernel32, "GetProcessUserModeExceptionPolicy");
    const tSetPolicy pSetPolicy = (tSetPolicy)GetProcAddress(kernel32, "SetProcessUserModeExceptionPolicy");
    if(pGetPolicy && pSetPolicy)
    {
        DWORD dwFlags;
        if(pGetPolicy(&dwFlags))
        {
            // Turn off the filter
            pSetPolicy(dwFlags & ~EXCEPTION_SWALLOWING);
        }
    }
}

来源: http://randomascii.wordpress.com/2012/07/05/when-even-crashing-doesnt-work/

他网站上的这些其他文章也帮助我理解了这一点: http://randomascii.wordpress.com/2011/12/07/increased-reliability-through-more-crashes/ http://randomascii.wordpress.com/2012/07/22/more-adventures-in-failing-to-crash-properly/

【讨论】:

  • 没问题 :) 可惜没有办法处理exit 和朋友们;我能想到的最好办法就是在源代码中找到它们的实例并删除它们!
  • 好吧,如果可以 API 挂钩 SetUnhandledExceptionFilter 就像其他地方建议的那样,我怀疑 ExitProcess 和/或 TerminateProcess 也是可能的。不过,这是否是一个好主意超出了我的想象。
  • 哦,我想钩住ExitProcessexit 完全没有帮助,因为我认为在调用ExitProcess 之前需要做更多的事情。
  • 2014 年后您将不再需要 EnableCrashingOnCrashes
【解决方案2】:

SetUnhandledExceptionFilter 显然不足以捕获所有意外退出。如果应用程序不小心调用了纯虚函数,则会弹出一个对话框。应用程序将挂起,但不会崩溃。由于没有例外,SetUnhandledExceptionFilter 和 WER 都无济于事。这个主题有一些变化。

如果您在诸如 WindowProc 之类的内核回调中崩溃,那就更糟糕了。如果这种情况发生在 64 位 Windows 上的 32 位应用程序中,那么异常会被操作系统捕获并继续执行。是的,崩溃是无声无息的。我觉得这很可怕。

http://randomascii.wordpress.com/2012/07/05/when-even-crashing-doesnt-work/ 应该详细说明处理这些异常情况所需的所有技巧。

【讨论】:

    【解决方案3】:

    简单地说:

    • 您无需使用任何其他_set* 函数,SetUnhandledExceptionFilter 就足够了。
    • abort 这样的C 运行时函数将禁用您使用SetUnhandledExceptionFilter 设置的全局异常处理程序。如果 CRT 导致崩溃,CRT 将简单地调用相同的函数将 NULL 参数,并且您的异常处理程序被禁用(未调用)! 你能做什么? [X]
    • 调用 excption-handler 时禁用所有其他正在运行的线程。只需使用CreateToolhelp32Snapshot 和其他函数查找所有线程。查找此进程,并暂停所有其他正在运行的线程(当然当前线程除外)。
    • 使用 SEH 或无 SEH,除非 CRT 干扰,否则将调用全局异常处理程序。不用担心(在大多数情况下)。
    • 中间不要有任何 CLR,如果中间有任何 CLR/托管调用(是的来自 C/C++),它将不允许异常处理程序调用。
    • 您无法处理一个异常 - 堆栈内存溢出!思考!在调试器下运行是唯一的解决方案,见下文。

    还有更多,我没有尝试过(没有发现有用)——向量异常处理。

    另一种方法是将应用程序运行到调试器中,您可以自己制作!在调试器中,您可以捕获所有异常,就像 VS 调试器捕获一样。见我的article。但是,你知道,这不是正确的方法。

    编辑:只需阅读有关进程终止的最后内容。你不应该控制它。在任何情况下,您都可以只挂钩所需的 API,这将充当您的代码(例如显示消息框)。

    [X] 您需要使用 API 挂钩。我没有方便的链接和详细信息。您将挂钩其他相关 API,但主要是 SetUnhandledExceptionFilter(在您为您调用它之后)。您的虚拟(挂钩)函数将如下所示:

    xxx SetUnhandledExceptionFilter_DUMMY(xxx)
    {
      // Dont do any thing
      return NULL;
    }
    

    我没有方便的 API 挂钩的链接和详细信息。


    为什么不尝试让您的应用程序更安全呢?

    • 更正所有警告(是的,甚至是 4 级警告)。
    • 使用静态分析。 VS 本身有(虽然在更高版本中。除了 2012 - 所有变体都有)。还提供其他 SA 工具。
    • 仔细检查代码。值得!
    • 从调试器运行和调试您的 RELEASE 版本。使用所有功能。
    • 查看并纠正所有可能的内存泄漏。
    • 使用防御方法进行编程。与其检查是否为空,不如使用 ASSERT 或您自己的断言来保护它。将其与断言、日志、函数返回打包在一起。

    【讨论】:

    • "你不需要使用任何其他的 _set* 函数,SetUnhandledExceptionFilter 就足够了。" -- 我相当确定这是错误。我已经在 VS2005 中检查了调试器:SetUnhandledExceptionFilter(NULL) 只会被默认 CRT _invalid_parameter 函数调用的 _invoke_watson 函数调用,并且此默认版本将调用 @987654333 @iff 不存在用户定义的处理程序。所以,如果我使用 _set_invalid_parameter_handler,那么 CRT 将不会调用 SetUExF(NULL)
    • 对于abort(),它只会调用SetUnhExcF(NULL),如果没有用户定义的SIGABRT处理程序来做其他事情。
    • 你为什么在调试器中运行和测试同样的东西?我已经使用 VS2008 Release 版本测试了代码。 abort 和其他 CRT 函数调用 SetUExF 来禁用任何使用定义的处理程序。
    • 在调试器中,我可以查看invarg.c 中的代码,并在代码中准确看到SetUExF 仅在不存在用户定义的处理程序时才被调用。 (正如我所说,我用 VS2005 进行了测试,但我真的怀疑这在 VS2008 上有所改变。)
    • 嘿。之后我实际上确实附加了调试器。但这没关系。重复一遍:查看 VC 文件夹中 invarg.c 的源代码。你甚至不需要运行任何东西来验证当你注册一个处理程序时SetUExF(NULL) 不会被调用。
    【解决方案4】:

    为了扩展所有答案,我发现最适合 1 亿次以上的安装:

    std::set_terminatestd::set_unexpected 或许也应该被提及。

    最重要的部分是让一切顺利:

    • 所有这些处理程序都应调用在互斥体/临界区下执行的函数,以确保如果其他线程同时发生任何其他崩溃,它们都会停止并等待,而不是造成破坏。
    • SIGABRT 的信号处理程序必须将自身设置为 SIGABRT 处理程序!如果没有这个,如果您从其他线程同时发生崩溃,您处理的线程将立即退出,而不会给您任何时间来处理崩溃。
    • 理想情况下,错误的实际处理应该在另一个进程中进行,或者至少在进程开始时启动的另一个线程中进行,否则您将无法处理内存不足或堆栈溢出错误。

    请参阅下面的 setExceptionHandlers 以供参考。此外,您很可能不想在调试版本或IsDebuggerPresent 时连接所有处理程序。

    #include <signal.h>
    #include <windows.h>
    #include <boost/thread/mutex.hpp>
    
    void EnableCrashingOnCrashes();
    void PreventSetUnhandledExceptionFilter();
    
    static void exceptionHandler(EXCEPTION_POINTERS* excpInfo)
    {
        // your code to handle the exception. Ideally it should
        // marshal the exception for processing to some other
        // thread and waif for the thread to complete the job
    }
    
    static boost::mutex unhandledExceptionMx;
    static LONG WINAPI unhandledException(EXCEPTION_POINTERS* excpInfo = NULL)
    {
        boost::mutex::scoped_lock lock(unhandledExceptionMx);
        if (!excpInfo == NULL)
        {
            __try // Generate exception to get proper context in dump
            {
                RaiseException(EXCEPTION_BREAKPOINT, 0, 0, NULL);
            }
            __except (exceptionHandler(GetExceptionInformation()), EXCEPTION_EXECUTE_HANDLER)
            {
            }
        }
        else
        {
            exceptionHandler(excpInfo);
        }
    
        return 0;
    }
    
    static void invalidParameter(const wchar_t* expr, const wchar_t* func,
        const wchar_t* file, unsigned int line, uintptr_t reserved)
    {
        unhandledException();
    }
    
    static void pureVirtualCall()
    {
        unhandledException();
    }
    
    static void sigAbortHandler(int sig)
    {
        // this is required, otherwise if there is another thread
        // simultaneously tries to abort process will be terminated
        signal(SIGABRT, sigAbortHandler);
        unhandledException();
    }
    
    static void setExceptionHandlers()
    {
        SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
        SetUnhandledExceptionFilter(unhandledException);
        _set_invalid_parameter_handler(invalidParameter);
        _set_purecall_handler(pureVirtualCall);
        signal(SIGABRT, sigAbortHandler);
        _set_abort_behavior(0, 0);
        EnableCrashingOnCrashes();
        PreventSetUnhandledExceptionFilter();
    }
    

    【讨论】:

      【解决方案5】:

      我将添加一个在 Windows 7 上运行时可以在某些场景中使用的解决方法:

      Windows 错误报告 (WER) 提供write full memory dumps on app crash 的选项。

      因此,如果您对此感到满意,则“只需”确保您真正感兴趣的崩溃场景会触发 WER。 ...这确实使我们回到了这个问题,但仍然...

      【讨论】:

        【解决方案6】:

        您可以使用 WER 捕获任何异常。正如您所见,CRT 有时会强制调用 WER。

        如果您想始终在进程中捕获异常,则需要防止从 CRT 调用 SetUnhandledExceptionFilter(NULL)。有关更多信息,请参阅我的博客条目:Improved “PreventSetUnhandledExceptionFilter”

        【讨论】:

        • 该链接当然值得赞赏。正如我上面提到的,WER 的东西只适用于 Win7。而且我仍然必须支持 WinXP :-/
        猜你喜欢
        • 1970-01-01
        • 2019-09-07
        • 1970-01-01
        • 1970-01-01
        • 2012-02-10
        • 1970-01-01
        • 2020-03-15
        • 1970-01-01
        相关资源
        最近更新 更多