Continue to debug after failed assertion on Linux?

12,034

Solution 1

You really want to recreate the behavior of DebugBreak. This stops the program in the debugger.

My googling of "DebugBreak linux" has turned up several references to this piece of inline assembly which is supposed to do the same.

#define DEBUG_BREAK asm("int $3")

Then your assert can become

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

According to Andomar int 3 causes the cpu to raise interrupt 3. According to drpepper a more portable way to do this would be to call:

 raise(SIGTRAP);

Solution 2

You can configure gdb to handle specific signals in a different way. For example, the following will cause SIGSTOP not to be treated as a stoppable event.

handle SIGSTOP nostop noprint pass

help handle within gdb will give you more information.

Solution 3

Even better usability is achieved with

/*!
 * \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

Solution 4

Have you tried to send a SIGCONT signal to the process?

kill -s SIGCONT <pid>
Share:
12,034
drpepper
Author by

drpepper

Updated on June 03, 2022

Comments

  • drpepper
    drpepper almost 2 years

    When an assertion fails with Visual C++ on Windows, the debugger stops, displays the message, and then lets you continue (or, if no debugging session is running, offers to launch visual studio for you).

    On Linux, it seems that the default behavior of assert() is to display the error and quit the program. Since all my asserts go through macros, I tried to use signals to get around this problem, like

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

    But although GDB (through KDevelop) stops at the correct point, I can't seem to continue past the signal, and sending the signal manually within GDB just leaves me hanging, with control of neither GDB nor the debugged process.

  • Andomar
    Andomar over 14 years
    It will cause the CPU to raise interrupt 3 (faydoc.tripod.com/cpu/int3.htm) The debugger has an interrupt handler registered for interrupt 3, and will break the program.
  • drpepper
    drpepper over 14 years
    perfect! it catches a SIGTRAP event, stops on a dime, and then lets me continue! thanks a lot.
  • drpepper
    drpepper over 14 years
    to make it a bit more portable, i replaced the assembly with the equivalent c code: raise(SIGTRAP); works great.
  • Lightness Races in Orbit
    Lightness Races in Orbit almost 10 years
    Your macro is broken in the presence of surrounding else
  • jheriko
    jheriko almost 9 years
    its worth noting that the int 3 is intel specific, where as raise(SIGTRAP) has worked for me in iOS and Android on ARM32/64 and MIPS, and i suspect works everywhere by virtue of being part of the standard library as well.