c++ - ¿Continuar depurando después de la afirmación fallida en Linux?
gdb assert (5)
¿Has intentado enviar una señal SIGCONT al proceso?
kill -s SIGCONT <pid>
Cuando una afirmación falla con Visual C ++ en Windows, el depurador se detiene, muestra el mensaje y luego le permite continuar (o, si no se está ejecutando ninguna sesión de depuración, le ofrece iniciar Visual Studio para usted).
En Linux, parece que el comportamiento predeterminado de assert () es mostrar el error y salir del programa. Como todas mis afirmaciones pasan por macros, traté de usar señales para evitar este problema, como
#define ASSERT(TEST) if(!(TEST)) raise(SIGSTOP);
Pero aunque GDB (a través de KDevelop ) se detiene en el punto correcto, parece que no puedo continuar más allá de la señal, y enviar la señal manualmente dentro de GDB simplemente me deja colgando, sin control ni GDB ni el proceso depurado.
Puede configurar gdb para manejar señales específicas de una manera diferente. Por ejemplo, lo siguiente hará que SIGSTOP no se trate como un evento que se puede detener.
handle SIGSTOP nostop noprint pass
help handle
dentro de gdb le dará más información.
Realmente desea recrear el comportamiento de DebugBreak . Esto detiene el programa en el depurador.
Mi búsqueda en Google de "DebugBreak linux" ha revelado varias referencias a esta pieza de montaje en línea que se supone que hace lo mismo.
#define DEBUG_BREAK asm("int $3")
Entonces tu afirmación puede convertirse
#define ASSERT(TEST) if(!(TEST)) asm("int $3");
Según Andomar int 3 hace que la CPU eleve la interrupción 3. Según drpepper, una forma más portátil de hacer esto sería llamar:
raise(SIGTRAP);
Se logra una mejor usabilidad incluso con
/*!
* /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
Puede reemplazar assert
con su propia versión que llama a pause()
lugar de abort()
. Cuando la afirmación falla, el programa se detendrá y podrá ejecutar gdb --pid $(pidof program)
para examinar la pila de llamadas y las variables. Una ventaja de este enfoque es que el program
no necesita iniciarse bajo GDB.
Archivo de encabezado (basado en /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 */
Implementación de assert_fail
(basado en assert.c en 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();
}