Продолжить отладку после неудачного утверждения в Linux? - PullRequest
15 голосов
/ 12 ноября 2009

При сбое подтверждения в Visual C ++ в Windows отладчик останавливается, отображает сообщение и затем позволяет продолжить (или, если сеанс отладки не запущен, предлагает запустить Visual Studio для вас).

В Linux похоже, что поведение assert () по умолчанию состоит в отображении ошибки и выходе из программы. Поскольку все мои утверждения проходят через макросы, я пытался использовать сигналы, чтобы обойти эту проблему, например

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

Но хотя GDB (через KDevelop ) останавливается в правильной точке, я не могу продолжать проходить мимо сигнала, и отправка сигнала вручную в GDB просто заставляет меня висеть , с контролем ни GDB, ни отлаженного процесса.

Ответы [ 5 ]

18 голосов
/ 12 ноября 2009

Вы действительно хотите воссоздать поведение DebugBreak . Это останавливает программу в отладчике.

Мой поиск в "DebugBreak linux" обнаружил несколько ссылок на этот фрагмент встроенной сборки, который должен делать то же самое.

#define DEBUG_BREAK asm("int $3")

Тогда ваше утверждение может стать

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

Согласно Andomar int 3 заставляет процессор вызывать прерывание 3. Согласно drpepper более переносимый способ сделать это - вызвать:

 raise(SIGTRAP);
10 голосов
/ 12 ноября 2009

Вы можете настроить GDB для обработки определенных сигналов другим способом. Например, следующее приведет к тому, что SIGSTOP не будет обрабатываться как событие, которое можно остановить.

handle SIGSTOP nostop noprint pass

help handle в GDB даст вам больше информации.

2 голосов
/ 25 ноября 2011

Еще лучшее удобство использования достигается с помощью

/*!
 * \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
1 голос
/ 03 декабря 2012

Вы можете заменить 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 (на основе assert.c в glibc):

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();
}
1 голос
/ 12 ноября 2009

Вы пытались отправить сигнал SIGCONT процессу?

kill -s SIGCONT <pid>
...