Voici quelques infos qui peuvent être utiles dans le débogage de votre problème
Si une exception est interceptée, la bibliothèque spéciale de la fonction std::terminate()
est automatiquement appelé. Mettre fin est en fait un pointeur à une fonction et la valeur par défaut est le Standard de la fonction de la bibliothèque C std::abort()
. Si aucun des nettoyages de se produire pour l'une exception non interceptée†, il peut effectivement être utile pour le débogage de ce problème car aucun les destructeurs sont appelés.
†Il est définis par l'implémentation de savoir si ou non la pile est déroulée avant d' std::terminate()
est appelé.
Un appel à l' abort()
est souvent utile dans la génération d'un core dump qui peuvent être analysés afin de déterminer la cause de l'exception. Assurez-vous d'activer core dumps via ulimit -c unlimited
(Linux).
Vous pouvez installer votre propre terminate()
fonction à l'aide de std::set_terminate()
. Vous devriez être en mesure de définir un point d'arrêt sur votre mettre fin à la fonction de gdb. Vous pouvez être en mesure de générer une trace de la pile de votre terminate()
fonction et cette trace peut aider à déterminer l'emplacement de l'exception.
Il y a une brève discussion sur les exceptions non traitées dans Bruce Eckel de Penser en C++, 2e Ed qui peut être utile.
Depuis terminate()
des appels abort()
par défaut (ce qui va provoquer un SIGABRT
du signal par défaut), vous pourriez être en mesure de définir un SIGABRT
gestionnaire, puis imprimer une trace de la pile à partir de dans le gestionnaire de signal. Cette trace peut aider à déterminer l'emplacement de l'exception.
Note: je dis peut parce que le C++ prend en charge non-locale de gestion des erreurs par le biais de l'utilisation de la langue des constructions de séparer la gestion d'erreur et de reporting du code de l'action ordinaire de code. Le bloc catch peut être, et est souvent, situé dans une autre fonction/méthode que le point de jeter. Il a également été souligné à moi dans les commentaires (merci Dan) qu'elle est mise en œuvre-définir si ou non la pile est déroulée avant d' terminate()
est appelé.
Mise à jour: j'ai jeté un Linux programme de test appelé qui génère une trace dans un terminate()
de la fonction définie par set_terminate()
et un autre dans un gestionnaire de signal pour SIGABRT
. Les deux backtraces afficher correctement l'emplacement de l'exception non gérée.
Mise à jour 2: Grâce à un billet de blog sur Attraper les exceptions à l'intérieur de résilier, j'ai appris quelques nouveaux trucs, y compris le re-lancement de l'exception non interceptée dans le résilier gestionnaire. Il est important de noter que le vide d' throw
énoncé dans la coutume de résilier le gestionnaire travaille 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);
}
Sortie:
my_terminate pris unhanded exception. quel (le): RUNTIME ERROR!
my_terminate backtrace retourné à 10 images
[bt]: (0) ./test(mon_résilier__Fv+0x1a) [0x8048e52]
[bt]: (1) /usr/lib/libstdc++-libc6.2-2..3 [0x40045baa]
[bt]: (2) /usr/lib/libstdc++-libc6.2-2..3 [0x400468e5]
[bt]: (3) /usr/lib/libstdc++-libc6.2-2..3(__rethrow+0xaf) [0x40046bdf]
[bt]: (4) ./test(throw_exception__Fv+0x68) [0x8049008]
[bt]: (5) ./test(foo2__Fv+0xb) [0x8049043]
[bt]: (6) ./test(toto1__Fv+0xb) [0x8049057]
[bt]: (7) ./test(principal+0xc1) [0x8049121]
[bt]: (8) ./test(__libc_start_+0x95) [0x42017589]
[bt]: (9) ./test(__hein_alloc+0x3d) [0x8048b21]
signal 6 (Avorté), l'adresse est 0x1239 de 0x42029331
crit_err_hdlr backtrace retournée 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..3 [0x40045baa]
[bt]: (5) /usr/lib/libstdc++-libc6.2-2..3 [0x400468e5]
[bt]: (6) /usr/lib/libstdc++-libc6.2-2..3(__rethrow+0xaf) [0x40046bdf]
[bt]: (7) ./test(throw_exception__Fv+0x68) [0x8049008]
[bt]: (8) ./test(foo2__Fv+0xb) [0x8049043]
[bt]: (9) ./test(toto1__Fv+0xb) [0x8049057]
[bt]: (10) ./test(principal+0xc1) [0x8049121]
[bt]: (11) ./test(__libc_start_+0x95) [0x42017589]
[bt]: (12) ./test(__hein_alloc+0x3d) [0x8048b21]