Продолжить отладку после неудачного утверждения в Linux?
Когда утверждение не выполняется с Visual С++ в Windows, отладчик останавливается, отображает сообщение, а затем позволяет продолжить (или, если сеанс отладки не работает, предлагает запустить визуальную студию для вас).
В Linux кажется, что поведение по умолчанию assert() заключается в отображении ошибки и выходе из программы. Поскольку все мои утверждения проходят макросы, я пытался использовать сигналы, чтобы обойти эту проблему, например
#define ASSERT(TEST) if(!(TEST)) raise(SIGSTOP);
Но хотя GDB (через KDevelop) останавливается в правильной точке, я не могу продолжать проходить мимо сигнала, и отправка сигнала вручную внутри GDB просто оставляет меня зависанием, не контролируя ни GDB, ни отлаживаемый процесс.
Ответы
Ответ 1
Вы действительно хотите воссоздать поведение DebugBreak. Это останавливает программу в отладчике.
Мой googling из "DebugBreak linux" появился несколько ссылки к этой части встроенной сборки, которая должна делать то же самое.
#define DEBUG_BREAK asm("int $3")
Тогда ваш assert может стать
#define ASSERT(TEST) if(!(TEST)) asm("int $3");
В соответствии с Andomar int 3 приводит к тому, что процессор поднимает прерывание 3. Согласно drpepper, более переносимым способом сделать это будет вызов:
raise(SIGTRAP);
Ответ 2
Вы можете настроить gdb для обработки определенных сигналов по-другому. Например, следующее приведет к тому, что SIGSTOP не будет рассматриваться как событие stoppable.
handle SIGSTOP nostop noprint pass
help handle
внутри gdb предоставит вам дополнительную информацию.
Ответ 3
Даже лучшее удобство использования достигается с помощью
/*!
* \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
Ответ 4
Вы пытались отправить сигнал SIGCONT процессу?
kill -s SIGCONT <pid>
Ответ 5
Вы можете заменить 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();
}