Hier einige Informationen , die möglicherweise in Debuggen Problem von Nutzen sein
Wenn eine Ausnahme nicht erfasst wird, wird die spezielle Bibliotheksfunktion std::terminate()
automatisch aufgerufen. Beenden ist eigentlich ein Zeiger auf eine Funktion und der Standardwert ist die Standard-C-Bibliotheksfunktion std::abort()
. Wenn für eine nicht erfasste Ausnahme keine Bereinigungen auftreten † , kann dies beim Debuggen dieses Problems hilfreich sein, da keine Destruktoren aufgerufen werden.
† Es ist implementierungsdefiniert, ob der Stapel vor dem std::terminate()
Aufruf abgewickelt wird oder nicht .
Ein Aufruf von abort()
ist häufig hilfreich, um einen Core-Dump zu generieren, der analysiert werden kann, um die Ursache der Ausnahme zu ermitteln. Stellen Sie sicher, dass Sie Core Dumps über ulimit -c unlimited
(Linux) aktivieren .
Sie können Ihre eigene terminate()
Funktion mit installieren std::set_terminate()
. Sie sollten in der Lage sein, einen Haltepunkt für Ihre Beendigungsfunktion in gdb festzulegen. Möglicherweise können Sie aus Ihrer terminate()
Funktion einen Stack-Backtrace generieren. Dieser Backtrace kann dabei helfen, den Ort der Ausnahme zu ermitteln.
Es gibt eine kurze Diskussion über nicht erfasste Ausnahmen in Bruce Eckels Denken in C ++, 2. Ausgabe , die ebenfalls hilfreich sein kann.
Da standardmäßig terminate()
Aufrufe ausgeführt abort()
werden (die standardmäßig ein SIGABRT
Signal verursachen ), können Sie möglicherweise einen SIGABRT
Handler festlegen und dann eine Stapelrückverfolgung aus dem Signalhandler heraus drucken . Diese Rückverfolgung kann bei der Identifizierung des Speicherorts der Ausnahme hilfreich sein.
Hinweis: Ich sage möglicherweise, weil C ++ die nicht-lokale Fehlerbehandlung durch die Verwendung von Sprachkonstrukten unterstützt, um die Fehlerbehandlung und den Berichtscode vom normalen Code zu trennen. Der Fangblock kann und befindet sich häufig in einer anderen Funktion / Methode als der Wurfpunkt. In den Kommentaren wurde ich auch darauf hingewiesen (danke Dan ), dass die Implementierung definiert ist, ob der Stapel vor dem terminate()
Aufruf abgewickelt wird oder nicht .
Update: Ich habe ein Linux-Testprogramm namens aufgerufen, das eine Rückverfolgung in einem terminate()
Funktionssatz über set_terminate()
und eine andere in einem Signalhandler für generiert SIGABRT
. Beide Rückverfolgungen zeigen den Ort der nicht behandelten Ausnahme korrekt an.
Update 2: Dank eines Blogposts über das Abfangen nicht erfasster Ausnahmen innerhalb von terminate habe ich einige neue Tricks gelernt. einschließlich des erneuten Auslösens der nicht erfassten Ausnahme innerhalb des Terminate-Handlers. Es ist wichtig zu beachten, dass die leere throw
Anweisung im benutzerdefinierten Terminate-Handler mit GCC funktioniert und keine tragbare Lösung ist.
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);
}
Ausgabe:
my_terminate hat eine unbehandelte Ausnahme abgefangen. what (): RUNTIME ERROR!
my_terminate backtrace hat 10 Frames zurückgegeben
[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 (abgebrochen), Adresse ist 0x1239 von 0x42029331
krit_err_hdlr backtrace gab 13 Frames zurück
[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]