在Linux上断言失败后继续调试?

时间:2009-11-12 11:21:37

标签: c++ c linux gdb assert

当Windows上的Visual C ++断言失败时,调试器停止,显示消息,然后让您继续(或者,如果没有运行调试会话,则提供为您启动Visual Studio)。

在Linux上,似乎assert()的默认行为是显示错误并退出程序。由于我的所有断言都通过宏,我试图使用信号来解决这个问题,比如

#define ASSERT(TEST) if(!(TEST)) raise(SIGSTOP);

但是虽然GDB(通过KDevelop)在正确的位置停止,但我似乎无法继续通过信号,并且在GDB内手动发送信号只会让我停下来,可以控制既不是GDB也不是调试过程。

5 个答案:

答案 0 :(得分:18)

您真的想要重新创建DebugBreak的行为。这会在调试器中停止程序。

我在谷歌上搜索“DebugBreak linux”已经将several references发送到这段内联汇编程序,该程序集也应该这样做。

#define DEBUG_BREAK asm("int $3")

然后你的断言可以成为

#define ASSERT(TEST) if(!(TEST)) asm("int $3");

根据 Andomar int 3导致cpu引发中断3.根据 drpepper ,更便携的方法是调用:

 raise(SIGTRAP);

答案 1 :(得分:10)

您可以配置gdb以不同的方式处理特定信号。例如,以下内容将导致SIGSTOP不被视为可停止事件。

handle SIGSTOP nostop noprint pass

gdb中的

help handle将为您提供更多信息。

答案 2 :(得分:2)

使用

实现更好的可用性
/*!
 * \file: assert_x.h
 * \brief: Usability Improving Extensions to assert.h.
 * \author: Per Nordlöw
 */

#pragma once

#include <errno.h>
#include <signal.h>
#include <assert.h>

#ifdef __cplusplus
extern "C" {
#endif

#if !defined(NDEBUG)
#  define passert(expr)                                                 \
  if (!(expr)) {                                                        \
    fprintf(stderr, "%s:%d: %s: Assertion `%s' failed.",                \
            __FILE__, __LINE__, __ASSERT_FUNCTION, __STRING(expr)); raise(SIGTRAP); \
  }
#  define passert_with(expr, sig)                                       \
  if (!(expr)) {                                                        \
    fprintf(stderr, "%s:%d: %s: Assertion `%s' failed.",                \
            __FILE__, __LINE__, __ASSERT_FUNCTION, __STRING(expr)); raise(sig); \
  }
#  define passert_eq(expected, actual)                                  \
  if (!(expected == actual)) {                                          \
    fprintf(stderr, "%s:%d: %s: Assertion `%s' == `%s' failed.",        \
            __FILE__, __LINE__, __ASSERT_FUNCTION, __STRING(expected), __STRING(actual)); raise(SIGTRAP); \
  }
#  define passert_neq(expected, actual)                                 \
  if (!(expected != actual)) {                                          \
    fprintf(stderr, "%s:%d: %s: Assertion `%s' != `%s' failed.",        \
            __FILE__, __LINE__, __ASSERT_FUNCTION, __STRING(expected), __STRING(actual)); raise(SIGTRAP); \
  }
#  define passert_lt(lhs, rhs)                                          \
  if (!(lhs < rhs)) {                                                   \
    fprintf(stderr, "%s:%d: %s: Assertion `%s' < `%s' failed.",         \
            __FILE__, __LINE__, __ASSERT_FUNCTION, __STRING(lhs), __STRING(rhs)); raise(SIGTRAP); \
  }
#  define passert_gt(lhs, rhs)                                          \
  if (!(lhs > rhs)) {                                                   \
    fprintf(stderr, "%s:%d: %s: Assertion `%s' < `%s' failed.",         \
            __FILE__, __LINE__, __ASSERT_FUNCTION, __STRING(lhs), __STRING(rhs)); raise(SIGTRAP); \
  }
#  define passert_lte(lhs, rhs)                                         \
  if (!(lhs <= rhs)) {                                                  \
    fprintf(stderr, "%s:%d: %s: Assertion `%s' <= `%s' failed.",        \
            __FILE__, __LINE__, __ASSERT_FUNCTION, __STRING(lhs), __STRING(rhs)); raise(SIGTRAP); \
  }
#  define passert_gte(lhs, rhs)                                         \
  if (!(lhs >= rhs)) {                                                  \
    fprintf(stderr, "%s:%d: %s: Assertion `%s' >= `%s' failed.",        \
            __FILE__, __LINE__, __ASSERT_FUNCTION, __STRING(lhs), __STRING(rhs)); raise(SIGTRAP); \
  }
#  define passert_zero(expr)                                            \
  if (!(expr == 0)) {                                                   \
    fprintf(stderr, "%s:%d: %s: Assertion `%s' is zero failed.",        \
            __FILE__, __LINE__, __ASSERT_FUNCTION, __STRING(expr)); raise(SIGTRAP); \
  }
#else
#  define passert(expr)
#  define passert_with(expr, sig)
#  define passert_eq(expected, actual)
#  define passert_lt(lhs, rhs)
#  define passert_gt(lhs, rhs)
#  define passert_lte(lhs, rhs)
#  define passert_gte(lhs, rhs)
#  define passert_zero(expr)
#endif

#ifdef __cplusplus
}
#endif

答案 3 :(得分:1)

您是否尝试过向该过程发送SIGCONT信号?

kill -s SIGCONT <pid>

答案 4 :(得分:1)

您可以将assert替换为您自己的调用pause()而不是abort()的版本。当断言失败时,程序将暂停,您可以运行gdb --pid $(pidof program)来检查callstack和变量。这种方法的一个优点是不需要在GDB下启动program

头文件(基于/usr/include/assert.h):

#include <assert.h>

#ifndef NDEBUG
    void assert_fail(const char *assertion, const char *file, unsigned line, const char *function)
    __attribute__ ((noreturn));
    #undef assert
    #define assert(expr)            \
        ((expr)                     \
        ? __ASSERT_VOID_CAST (0)    \
        : assert_fail (__STRING(expr), __FILE__, __LINE__, __ASSERT_FUNCTION))
#endif /* NDEBUG */

assert_fail的实现(基于glibc中的assert.c):

void assert_fail(const char *assertion, const char *file, unsigned line, const char *function) {
    extern const char *__progname;
    fprintf(stderr, "%s%s%s:%u: %s%sAssertion `%s' failed.\n",
        __progname,
        __progname[0] ? ": " : "",
        file,
        line,
        function ? function : "",
        function ? ": " : "",
        assertion
    );
    pause();
    abort();
}