16

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

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

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

但是,尽管GDB(通过KDevelop)在正确的点停止,但我似乎无法继续越过信号,并且在 GDB 中手动发送信号只会让我悬而未决,既无法控制 GDB,也无法控制被调试的进程。

4

5 回答 5

19

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

我对“DebugBreak linux”的谷歌搜索已经找到了对这个内联程序集的几个 引用,它应该做同样的事情。

#define DEBUG_BREAK asm("int $3")

然后你的断言可以变成

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

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

 raise(SIGTRAP);
于 2009-11-12T11:28:50.450 回答
10

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

handle SIGSTOP nostop noprint pass

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

于 2009-11-12T11:26:43.637 回答
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
于 2011-11-25T15:34:24.107 回答
1

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

头文件(基于 /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):

#include <stdio.h>   /* for stderr, fprintf() */
#include <stdlib.h>  /* for abort() */
#include <unistd.h>  /* for pause() */

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();
}
于 2012-12-03T15:23:22.037 回答
1

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

kill -s SIGCONT <pid>
于 2009-11-12T11:28:23.227 回答