Voici quelques informations qui peuvent être utiles pour déboguer votre problème
Si une exception n'est pas interceptée, la fonction de bibliothèque spéciale std::terminate()
est automatiquement appelée. Terminate est en fait un pointeur vers une fonction et la valeur par défaut est la fonction de bibliothèque C standard std::abort()
. Si aucun nettoyage n'a lieu pour une exception non interceptée † , cela peut en fait être utile pour déboguer ce problème car aucun destructeur n'est appelé.
† Il est défini par l'implémentation, que la pile soit déroulée ou non avant d' std::terminate()
être appelée.
Un appel à abort()
est souvent utile pour générer un vidage de mémoire qui peut être analysé pour déterminer la cause de l'exception. Assurez-vous d'activer les vidages de mémoire via ulimit -c unlimited
(Linux).
Vous pouvez installer votre propre terminate()
fonction en utilisant std::set_terminate()
. Vous devriez pouvoir définir un point d'arrêt sur votre fonction de terminaison dans gdb. Vous pourrez peut- être générer une trace arrière de pile à partir de votre terminate()
fonction et cette trace arrière peut aider à identifier l'emplacement de l'exception.
Il y a une brève discussion sur les exceptions non détectées dans la pensée de Bruce Eckel en C ++, 2e édition qui peut également être utile.
Depuis les terminate()
appels abort()
par défaut (ce qui provoquera un SIGABRT
signal par défaut), vous pourrez peut- être définir un SIGABRT
gestionnaire, puis imprimer une trace de pile à partir du gestionnaire de signaux . Cette trace arrière peut aider à identifier l'emplacement de l'exception.
Remarque: je dis peut parce que C ++ prend en charge la gestion des erreurs non locales via l'utilisation de constructions de langage pour séparer la gestion des erreurs et le code de rapport du code ordinaire. Le bloc catch peut être, et est souvent, situé dans une fonction / méthode différente de celle du point de lancement. On m'a également fait remarquer dans les commentaires (merci Dan ) qu'il est défini par l'implémentation que la pile soit déroulée ou non avant d' terminate()
être appelée.
Mise à jour: J'ai lancé un programme de test Linux appelé qui génère une trace dans une terminate()
fonction définie via set_terminate()
et une autre dans un gestionnaire de signaux pour SIGABRT
. Les deux backtraces affichent correctement l'emplacement de l'exception non gérée.
Mise à jour 2: Grâce à un article de blog sur la capture d'exceptions non interceptées dans terminate , j'ai appris quelques nouvelles astuces; y compris la relance de l'exception non interceptée dans le gestionnaire de terminaison. Il est important de noter que l' throw
instruction vide dans le gestionnaire de terminaison personnalisé fonctionne avec GCC et n'est pas une solution portable.
Code:
#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);
}
Production:
my_terminate a intercepté une exception non gérée. quoi (): ERREUR DE RUNTIME!
my_terminate backtrace a renvoyé 10 images
[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]
signal 6 (abandonné), l'adresse est 0x1239 à partir de 0x42029331
crit_err_hdlr backtrace a renvoyé 13 images
[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]