Aquí hay información que puede ser útil para depurar su problema
Si no se detecta una excepción, std::terminate()
se llama automáticamente a la función de biblioteca especial . Terminar es en realidad un puntero a una función y el valor predeterminado es la función de la biblioteca C estándar std::abort()
. Si no se realizan limpiezas para una excepción no detectada † , en realidad puede ser útil para depurar este problema, ya que no se llaman destructores.
† Está definido por la implementación si la pila se desenrolla o no antes de que std::terminate()
se llame.
Una llamada a abort()
suele ser útil para generar un volcado de memoria que se puede analizar para determinar la causa de la excepción. Asegúrese de habilitar los volcados de núcleo a través de ulimit -c unlimited
(Linux).
Puede instalar su propia terminate()
función usando std::set_terminate()
. Debería poder establecer un punto de interrupción en su función de terminación en gdb. Es posible que pueda generar un seguimiento de pila a partir de su terminate()
función y este seguimiento puede ayudar a identificar la ubicación de la excepción.
Hay una breve discusión sobre excepciones no detectadas en Thinking in C ++, 2nd Ed de Bruce Eckel que también puede ser útil.
Dado que las terminate()
llamadas abort()
de forma predeterminada (lo que provocará una SIGABRT
señal de forma predeterminada), es posible que pueda configurar un SIGABRT
controlador y luego imprimir un seguimiento de la pila desde dentro del controlador de señal . Este seguimiento puede ayudar a identificar la ubicación de la excepción.
Nota: Digo que puede porque C ++ admite el manejo de errores no locales mediante el uso de construcciones de lenguaje para separar el manejo de errores y el código de informes del código ordinario. El bloque de captura puede estar, y a menudo está, ubicado en una función / método diferente al punto de lanzamiento. También se me ha señalado en los comentarios (gracias Dan ) que está definido por la implementación si la pila se desenrolla o no antes de terminate()
llamar.
Actualización: reuní un programa de prueba de Linux llamado que genera un backtrace en un terminate()
conjunto de funciones via set_terminate()
y otro en un controlador de señal para SIGABRT
. Ambos backtraces muestran correctamente la ubicación de la excepción no controlada.
Actualización 2: Gracias a una publicación de blog sobre cómo detectar excepciones no detectadas dentro de terminar , aprendí algunos trucos nuevos; incluido el relanzamiento de la excepción no detectada dentro del controlador de terminación. Es importante tener en cuenta que la throw
declaración vacía dentro del controlador de terminación personalizado funciona con GCC y no es una solución portátil.
Código:
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#ifndef __USE_GNU
#define __USE_GNU
#endif
#include <execinfo.h>
#include <signal.h>
#include <string.h>
#include <iostream>
#include <cstdlib>
#include <stdexcept>
void my_terminate(void);
namespace {
// invoke set_terminate as part of global constant initialization
static const bool SET_TERMINATE = std::set_terminate(my_terminate);
}
// This structure mirrors the one found in /usr/include/asm/ucontext.h
typedef struct _sig_ucontext {
unsigned long uc_flags;
struct ucontext *uc_link;
stack_t uc_stack;
struct sigcontext uc_mcontext;
sigset_t uc_sigmask;
} sig_ucontext_t;
void crit_err_hdlr(int sig_num, siginfo_t * info, void * ucontext) {
sig_ucontext_t * uc = (sig_ucontext_t *)ucontext;
// Get the address at the time the signal was raised from the EIP (x86)
void * caller_address = (void *) uc->uc_mcontext.eip;
std::cerr << "signal " << sig_num
<< " (" << strsignal(sig_num) << "), address is "
<< info->si_addr << " from "
<< caller_address << std::endl;
void * array[50];
int size = backtrace(array, 50);
std::cerr << __FUNCTION__ << " backtrace returned "
<< size << " frames\n\n";
// overwrite sigaction with caller's address
array[1] = caller_address;
char ** messages = backtrace_symbols(array, size);
// skip first stack frame (points here)
for (int i = 1; i < size && messages != NULL; ++i) {
std::cerr << "[bt]: (" << i << ") " << messages[i] << std::endl;
}
std::cerr << std::endl;
free(messages);
exit(EXIT_FAILURE);
}
void my_terminate() {
static bool tried_throw = false;
try {
// try once to re-throw currently active exception
if (!tried_throw++) throw;
}
catch (const std::exception &e) {
std::cerr << __FUNCTION__ << " caught unhandled exception. what(): "
<< e.what() << std::endl;
}
catch (...) {
std::cerr << __FUNCTION__ << " caught unknown/unhandled exception."
<< std::endl;
}
void * array[50];
int size = backtrace(array, 50);
std::cerr << __FUNCTION__ << " backtrace returned "
<< size << " frames\n\n";
char ** messages = backtrace_symbols(array, size);
for (int i = 0; i < size && messages != NULL; ++i) {
std::cerr << "[bt]: (" << i << ") " << messages[i] << std::endl;
}
std::cerr << std::endl;
free(messages);
abort();
}
int throw_exception() {
// throw an unhandled runtime error
throw std::runtime_error("RUNTIME ERROR!");
return 0;
}
int foo2() {
throw_exception();
return 0;
}
int foo1() {
foo2();
return 0;
}
int main(int argc, char ** argv) {
struct sigaction sigact;
sigact.sa_sigaction = crit_err_hdlr;
sigact.sa_flags = SA_RESTART | SA_SIGINFO;
if (sigaction(SIGABRT, &sigact, (struct sigaction *)NULL) != 0) {
std::cerr << "error setting handler for signal " << SIGABRT
<< " (" << strsignal(SIGABRT) << ")\n";
exit(EXIT_FAILURE);
}
foo1();
exit(EXIT_SUCCESS);
}
Salida:
my_terminate detectó una excepción sin mano. what (): RUNTIME ERROR!
my_terminate backtrace devolvió 10 fotogramas
[bt]: (0) ./test(my_terminate__Fv+0x1a) [0x8048e52]
[bt]: (1) /usr/lib/libstdc++-libc6.2-2.so.3 [0x40045baa]
[bt]: (2) /usr/lib/libstdc++-libc6.2-2.so.3 [0x400468e5]
[bt]: (3) /usr/lib/libstdc++-libc6.2-2.so.3(__rethrow+0xaf) [0x40046bdf]
[bt]: (4) ./test(throw_exception__Fv+0x68) [0x8049008]
[bt]: (5) ./test(foo2__Fv+0xb) [0x8049043]
[bt]: (6) ./test(foo1__Fv+0xb) [0x8049057]
[bt]: (7) ./test(main+0xc1) [0x8049121]
[bt]: (8) ./test(__libc_start_main+0x95) [0x42017589]
[bt]: (9) ./test(__eh_alloc+0x3d) [0x8048b21]
señal 6 (cancelada), la dirección es 0x1239 de 0x42029331
crit_err_hdlr backtrace devolvió 13 cuadros
[bt]: (1) ./test(kill+0x11) [0x42029331]
[bt]: (2) ./test(abort+0x16e) [0x4202a8c2]
[bt]: (3) ./test [0x8048f9f]
[bt]: (4) /usr/lib/libstdc++-libc6.2-2.so.3 [0x40045baa]
[bt]: (5) /usr/lib/libstdc++-libc6.2-2.so.3 [0x400468e5]
[bt]: (6) /usr/lib/libstdc++-libc6.2-2.so.3(__rethrow+0xaf) [0x40046bdf]
[bt]: (7) ./test(throw_exception__Fv+0x68) [0x8049008]
[bt]: (8) ./test(foo2__Fv+0xb) [0x8049043]
[bt]: (9) ./test(foo1__Fv+0xb) [0x8049057]
[bt]: (10) ./test(main+0xc1) [0x8049121]
[bt]: (11) ./test(__libc_start_main+0x95) [0x42017589]
[bt]: (12) ./test(__eh_alloc+0x3d) [0x8048b21]