Aqui estão algumas informações que podem ser úteis para depurar seu problema
Se uma exceção não for detectada, a função de biblioteca especial std::terminate()
é chamada automaticamente. Terminate é, na verdade, um ponteiro para uma função e o valor padrão é a função da biblioteca C padrão std::abort()
. Se nenhuma limpeza ocorrer para uma exceção não detectada † , pode ser útil na depuração desse problema, pois nenhum destruidor é chamado.
† É definido pela implementação se a pilha é ou não desfeita antes de std::terminate()
ser chamada.
Uma chamada para abort()
geralmente é útil para gerar um dump de memória que pode ser analisado para determinar a causa da exceção. Certifique-se de habilitar core dumps via ulimit -c unlimited
(Linux).
Você pode instalar sua própria terminate()
função usando std::set_terminate()
. Você deve ser capaz de definir um ponto de interrupção em sua função encerrar no gdb. Você pode ser capaz de gerar um backtrace da pilha a partir de sua terminate()
função e esse backtrace pode ajudar a identificar a localização da exceção.
Há uma breve discussão sobre exceções não detectadas em Thinking in C ++, 2ª edição de Bruce Eckel que também pode ser útil.
Como as terminate()
chamadas são abort()
por padrão (o que irá causar um SIGABRT
sinal por padrão), você pode definir um SIGABRT
manipulador e, em seguida, imprimir um rastreamento de pilha de dentro do manipulador de sinal . Este backtrace pode ajudar a identificar a localização da exceção.
Observação: posso dizer que sim porque C ++ oferece suporte ao tratamento de erros não locais por meio do uso de construções de linguagem para separar o tratamento de erros e o código de relatório do código comum. O bloco de captura pode estar, e freqüentemente está, localizado em uma função / método diferente do ponto de lançamento. Também foi apontado para mim nos comentários (obrigado Dan ) que é definido pela implementação quer a pilha seja desfeita ou não antes de terminate()
ser chamada.
Atualização: Eu juntei um programa de teste Linux chamado que gera um backtrace em um terminate()
conjunto de funções via set_terminate()
e outro em um manipulador de sinal para SIGABRT
. Ambos os backtraces mostram corretamente a localização da exceção não tratada.
Atualização 2: graças a uma postagem do blog sobre Captura de exceções não capturadas no terminate , aprendi alguns truques novos; incluindo o relançamento da exceção não capturada dentro do manipulador de terminação. É importante observar que a throw
instrução vazia dentro do manipulador de terminação personalizado funciona com o GCC e não é uma solução 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);
}
Resultado:
my_terminate detectou uma exceção espontânea. what (): RUNTIME ERROR!
my_terminate backtrace retornou 10 frames
[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]
sinal 6 (abortado), o endereço é 0x1239 de 0x42029331
crit_err_hdlr backtrace retornou 13 frames
[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]