Ecco alcune informazioni che potrebbero essere utili per il debug del tuo problema
Se un'eccezione non viene rilevata, std::terminate()
viene chiamata automaticamente la funzione di libreria speciale . Terminate è in realtà un puntatore a una funzione e il valore predefinito è la funzione della libreria C standard std::abort()
. Se non si verificano pulizie per un'eccezione non rilevata † , può effettivamente essere utile per il debug di questo problema poiché non viene chiamato alcun distruttore.
† È definito dall'implementazione se lo stack viene svolto o meno prima di std::terminate()
essere chiamato.
Una chiamata a abort()
è spesso utile per generare un core dump che può essere analizzato per determinare la causa dell'eccezione. Assicurati di abilitare i core dump tramite ulimit -c unlimited
(Linux).
È possibile installare la propria terminate()
funzione utilizzando std::set_terminate()
. Dovresti essere in grado di impostare un punto di interruzione sulla funzione di terminazione in gdb. Si può essere in grado di generare un backtrace dello stack dal vostro terminate()
funzione e questo backtrace può aiutare a identificare la posizione del l'eccezione.
C'è una breve discussione sulle eccezioni non gestite fanno in pensiero di Bruce Eckel in C ++, 2nd Ed che possono essere utili pure.
Dal momento che terminate()
le chiamate abort()
di default (che causerà un SIGABRT
segnale di default), si potrebbe essere in grado di impostare un SIGABRT
gestore e poi stampare un backtrace dello stack all'interno del gestore di segnale . Questo backtrace può aiutare a identificare la posizione dell'eccezione.
Nota: dico maggio perché C ++ supporta la gestione degli errori non locali attraverso l'uso di costrutti di linguaggio per separare la gestione degli errori e il codice di reporting dal codice ordinario. Il blocco di cattura può essere, e spesso lo è, situato in una funzione / metodo diverso dal punto di lancio. Mi è stato anche fatto notare nei commenti (grazie Dan ) che è definito dall'implementazione indipendentemente dal fatto che lo stack venga svolto o meno prima di terminate()
essere chiamato.
Aggiornamento: ho messo insieme un programma di test Linux chiamato che genera un backtrace in una terminate()
funzione impostata tramite set_terminate()
e un altro in un gestore di segnali per SIGABRT
. Entrambi i backtrace mostrano correttamente la posizione dell'eccezione non gestita.
Aggiornamento 2: grazie a un post sul blog sulla cattura delle eccezioni non rilevate all'interno di terminate , ho imparato alcuni nuovi trucchi; incluso il rilancio dell'eccezione non rilevata all'interno del gestore di terminazione. È importante notare che l' throw
istruzione vuota all'interno del gestore di terminazione personalizzato funziona con GCC e non è una soluzione portatile.
Codice:
#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);
}
Produzione:
my_terminate ha rilevato un'eccezione non gestita. what (): RUNTIME ERROR!
my_terminate backtrace ha restituito 10 frame
[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]
segnale 6 (interrotto), l'indirizzo è 0x1239 da 0x42029331
crit_err_hdlr backtrace ha restituito 13 frame
[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]