Eu tenho um programa que lança uma exceção não detectada em algum lugar. Tudo que recebo é um relatório de uma exceção sendo lançada, e nenhuma informação sobre onde ela foi lançada. Parece ilógico que um programa compilado para conter símbolos de depuração não me notifique sobre onde uma exceção foi gerada em meu código.
Existe alguma maneira de saber de onde estão vindo minhas exceções antes de definir 'catch throw' no gdb e chamar um backtrace para cada exceção lançada?
Respostas:
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ãostd::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 viaulimit -c unlimited
(Linux).Você pode instalar sua própria
terminate()
função usandostd::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 suaterminate()
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ãoabort()
por padrão (o que irá causar umSIGABRT
sinal por padrão), você pode definir umSIGABRT
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 viaset_terminate()
e outro em um manipulador de sinal paraSIGABRT
. 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:
Resultado:
fonte
main
) e então chamarterminate()
. Mas seu exemplo mostra que nenhum desenrolamento é feito, o que é muito legal.throw(int)
especificação é desnecessária. 2)uc->uc_mcontext.eip
Provavelmente depende muito da plataforma (por exemplo, use...rip
em uma plataforma de 64 bits). 3) Compile com-rdynamic
para obter símbolos de backtrace. 4) Corra./a.out 2>&1 | c++filt
para obter símbolos de backtrace bonitos.((sig_ucontext_t *)userContext)->uc_mcontext.fault_address;
trabalhou para meu alvo ARMComo você disse, podemos usar 'catch throw' no gdb e chamar 'backtrace' para cada exceção lançada. Embora isso geralmente seja muito tedioso para fazer manualmente, o gdb permite a automação do processo. Isso permite ver o backtrace de todas as exceções que são lançadas, incluindo a última não capturada:
gdb>
Sem outra intervenção manual, isso gera muitos rastros de retorno, incluindo um para a última exceção não detectada:
Aqui está uma ótima postagem do blog resumindo isso: http://741mhz.com/throw-stacktrace [no archive.org]
fonte
Você pode criar uma macro como:
... e ele lhe dará o local onde a exceção é lançada (reconhecidamente não o rastreamento de pilha). É necessário que você derive suas exceções de alguma classe base que usa o construtor acima.
fonte
throw new excation(...)
masthrow exception(...)
C ++ não é Java,Você não passou informações sobre qual SO / Compilador você usa.
No Visual Studio C ++, as exceções podem ser instrumentadas.
Consulte "Instrumentação para tratamento de exceções do Visual C ++" em ddj.com
Meu artigo "Postmortem Debugging" , também no ddj.com inclui código para usar o tratamento de exceções estruturadas do Win32 (usado pela instrumentação) para registro, etc.
fonte
Você pode marcar os principais locais restritos em seu código
noexcept
para localizar uma exceção e, em seguida, usar o libunwind (basta adicionar-lunwind
aos parâmetros do vinculador) (testado comclang++ 3.6
):demagle.hpp:
demangle.cpp:
backtrace.hpp:
backtrace.cpp:
backtrace_on_terminate.hpp:
Existe um bom artigo sobre o assunto.
fonte
Eu tenho um código para fazer isso no Windows / Visual Studio, deixe-me saber se você quiser um esboço. Não sei como fazer isso para o código dwarf2, porém, um google rápido sugere que há uma função _Unwind_Backtrace em libgcc que provavelmente é parte do que você precisa.
fonte
Verifique este tópico, talvez ajude:
Capturando todas as exceções não tratadas de C ++?
Tive boas experiências com esse software:
http://www.codeproject.com/KB/applications/blackbox.aspx
Ele pode imprimir um rastreamento de pilha em um arquivo para qualquer exceção não tratada.
fonte
exception thrown foo.c@54, ..., re-thrown bar.c@54, ....
sem ter que fazer isso manualmente.