【问题标题】:How to catch assertion failures in MinGW gcc?如何在 MinGW gcc 中捕获断言失败?
【发布时间】:2020-06-13 08:12:24
【问题描述】:

在 Unix 系统上,我可以使用 fork() 生成一个进程,并检查该线程的标志以判断它是否中止,通常是通过断言失败。这是示例代码,给定一个函数,检查函数调用是否中止。

bool test_assert_fail(void (*run)(void *aux), void *aux) {
    if (fork()) { // parent process
        int *status = malloc(sizeof(*status));
        assert(status != NULL);
        wait(status);
        // Check whether child process aborted
        bool aborted = WIFSIGNALED(*status) && WTERMSIG(*status) == SIGABRT;
        free(status);
        return aborted;
    }
    else { // child process
        freopen("/dev/null", "w", stderr); // suppress assertion message
        run(aux);
        exit(0); // should not be reached
    }
}

使用 MinGW gcc 编译器在 Windows 上执行类似操作的最简单方法是什么?线程不起作用,因为子线程中止会导致父线程也中止。我不知道如何为此使用进程,因为它是一个函数调用。

【问题讨论】:

  • 无法修复代码使其不会中止吗?
  • 不,这是出于测试目的 - 预计代码应该中止。该用例类似于列表结构,并且检查是否越界访问索引失败。我想我可以从某些情况下返回 NULL 并检查它?但我并不真正喜欢这种做法,因为它混淆了真正的错误。
  • 我明白了。除非您使用 cygwin,否则您也可以使用 fork(),否则我看不到类似的方法。
  • 我最终做了一个不安全的解决方法,避免了完全启动一个新进程,但对于比我的用例更复杂的任何事情都绝对是错误的。

标签: c mingw assert mingw-w64


【解决方案1】:

注意:使用 this 调用的函数非常简单且是单线程的,因此这在某种程度上比通常更安全。

我最终使用 signal 捕获 SIGABRT 并使用 longjmp 恢复执行:

#ifdef _WIN32
bool SIGABRT_RAISED = false;
jmp_buf env;

void signal_handler(int signum) {
    if (signum == SIGABRT) {
        SIGABRT_RAISED = true;
        // Reregister default, and jump out to avoid return
        signal(signum, SIG_DFL);
        longjmp(env, 1);
    }
} 
#endif

bool test_assert_fail(void (*run)(void *aux), void *aux) {
    // Windows can't use POSIX apis
    #ifndef _WIN32
    if (fork()) { // parent process
        int *status = malloc(sizeof(*status));
        assert(status != NULL);
        wait(status);
        // Check whether child process aborted
        bool aborted = WIFSIGNALED(*status) && WTERMSIG(*status) == SIGABRT;
        free(status);
        return aborted;
    }
    else { // child process
        freopen("/dev/null", "w", stderr); // suppress assertion message
        run(aux);
        exit(0); // should not be reached
    }
    #else

    signal(SIGABRT, signal_handler);
    SIGABRT_RAISED = false;
    int cstderr = _dup(_fileno(stderr));

    // Run expected failure, jumping back when SIGABRT is raised
    if (setjmp(env) == 0) {
        // suppress assertion message
        freopen("NUL", "w", stderr);
        run(aux);
    }

    // Undo suppression, since we are in the same process
    _dup2(cstderr, _fileno(stderr));
    return SIGABRT_RAISED;
    #endif
}

【讨论】:

    猜你喜欢
    • 2021-09-22
    • 2021-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多