更改Debug的断言行为(SIGABRT - > SIGTRAP)

时间:2014-01-21 11:44:07

标签: c debugging signals assert abort

我正在尝试在使用gcc / g ++构建的Debian上调试程序(DEBUG=1NDEBUG未定义)。我正在使用第三方库,它也用于调试(DEBUG=1NDEBUG未定义,以及其他调试定义)。该库是150K行,充满了断言。这很好。

我的代码有一个错误,我正在尝试调试SQL查询。它导致断言在第三方库中触发。这是好的,也是课程的标准。

但是,当assert在库中触发时,脑死亡Posix指定的行为会导致程序崩溃。调试“debugging and diagnostic”功能时,这是无用的行为。这必须是该委员会最愚蠢的决定之一,难怪很多人很少在开发过程中使用它。

我想改变行为,以便在断言触发时,它会引发SIGTRAP而不是SIGABRT。我有点受限制,因为我没有写第三方库(我的代码使用MY_ASSERT,并且它调用SIGTRAP,所以我可以继续观看负代码路径。)

我知道我可以通过gdb安装或更改信号处理程序,但gdb已经在SIGABRT停止,然后再将其传递给程序并中止。我还尝试安装一个自定义SIGABRT信号处理程序,由于中止仍然存在,因此似乎没有帮助。

如何在调试时更改assert的行为以提升SIGTRAP

1 个答案:

答案 0 :(得分:2)

对于linux,您可以定义一个可以引发SIGTRAP的{​​{3}}函数,而不是默认实现。

void __assert_fail(const char * assertion, const char * file, unsigned int line, const char * function) {
    fprintf(stderr, "Assert: %s failed at %s:%d in function %s", assertion, file, line, function);
    raise(SIGTRAP);
}

规范说你实际上应该在此时终止程序,因为该函数在__attribute__((noreturn))中被声明为signal.h

您可以添加一些像这样的iffy代码(通过ptrace进行编辑以使用fork检测而不是信号):

#include <assert.h>
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/ptrace.h>

static int ptrace_failed;

static int
detect_ptrace(void)
{
    int status, waitrc;

    pid_t child = fork();
    if (child == -1) return -1;
    if (child == 0) {
        if (ptrace(PT_ATTACH, getppid(), 0, 0))
            exit(1);
        do {
            waitrc = waitpid(getppid(), &status, 0);
        } while (waitrc == -1 && errno == EINTR);
        ptrace(PT_DETACH, getppid(), (caddr_t)1, SIGCONT);
        exit(0);
    }
    do {
        waitrc = waitpid(child, &status, 0);
    } while (waitrc == -1 && errno == EINTR);
    return WEXITSTATUS(status);
}

__attribute__((constructor))
static void
detect_debugger(void)
{
    ptrace_failed = detect_ptrace();
}

void __assert_fail(const char * assertion, const char * file, unsigned int line, const char * function) {
    fprintf(stderr, "Assert: %s failed at %s:%d in function %s\n", assertion, file, line, function);
    if (ptrace_failed)
        raise(SIGTRAP);
    else
        abort();
}

在gdb下运行时触发SIGTRAP,否则会触发SIGABRT。我已经通过LD_PRELOAD测试了它,并且似乎按预期工作了。