13

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

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

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

有什么我们错过的吗?(对一些非法调用的代码ExitProcessTerminateProcess其中一种exit变体进行取模。)


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

我还要注意,在 Windows7+ 上,设置SetUHEF而只是在注册表中设置“正确的”WER 转储设置通常是一种可行的方法。

4

6 回答 6

6
于 2013-08-01T09:20:50.127 回答
2

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

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

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

于 2013-08-14T06:51:06.213 回答
1

高大上的命令,简而言之:

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

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

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

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

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

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

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


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

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

为了扩展所有答案,这是我发现最适合 100M+ 安装的答案:

或许还应该提到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();
}
于 2018-02-15T22:52:27.107 回答
0

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

Windows 错误报告 (WER) 提供了在应用程序崩溃时写入完整内存转储的选项。

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

于 2013-08-01T07:36:42.413 回答
0
于 2013-08-01T07:49:59.653 回答