Berikut beberapa info yang mungkin berguna untuk men-debug masalah Anda
Jika pengecualian tidak tertangkap, fungsi perpustakaan khusus std::terminate()
dipanggil secara otomatis. Hentikan sebenarnya adalah penunjuk ke fungsi dan nilai defaultnya adalah fungsi pustaka C Standar std::abort()
. Jika tidak ada pembersihan terjadi karena eksepsi tidak tertangkap † , itu mungkin benar-benar membantu dalam debugging masalah ini karena tidak ada destructors disebut.
† Ini adalah implementasi-ditentukan apakah tumpukan dibatalkan sebelum std::terminate()
dipanggil.
Panggilan ke abort()
sering kali berguna dalam menghasilkan dump inti yang dapat dianalisis untuk menentukan penyebab pengecualian. Pastikan Anda mengaktifkan core dump melalui ulimit -c unlimited
(Linux).
Anda dapat menginstal terminate()
fungsi Anda sendiri dengan menggunakan std::set_terminate()
. Anda harus bisa mengatur breakpoint pada fungsi terminate Anda di gdb. Anda mungkin dapat membuat pelacakan mundur tumpukan dari terminate()
fungsi Anda dan pelacakan mundur ini dapat membantu dalam mengidentifikasi lokasi pengecualian.
Ada diskusi singkat tentang pengecualian yang tidak tertangkap dalam Bruce Eckel's Thinking in C ++, 2nd Ed yang mungkin bisa membantu juga.
Karena terminate()
panggilan abort()
secara default (yang akan menyebabkan SIGABRT
sinyal secara default), Anda mungkin dapat mengatur SIGABRT
penangan dan kemudian mencetak pelacakan mundur tumpukan dari dalam penangan sinyal . Pelacakan balik ini dapat membantu dalam mengidentifikasi lokasi pengecualian.
Catatan: Saya katakan mungkin karena C ++ mendukung penanganan kesalahan non-lokal melalui penggunaan konstruksi bahasa untuk memisahkan penanganan kesalahan dan kode pelaporan dari kode biasa. Blok tangkapan dapat, dan sering kali, terletak di fungsi / metode yang berbeda dari titik lemparan. Itu juga telah ditunjukkan kepada saya di komentar (terima kasih Dan ) bahwa itu adalah implementasi-ditentukan apakah tumpukan dibatalkan sebelum terminate()
dipanggil.
Pembaruan: Saya mengumpulkan program uji Linux yang disebut yang menghasilkan pelacakan balik dalam terminate()
fungsi yang ditetapkan melalui set_terminate()
dan lainnya dalam penangan sinyal untuk SIGABRT
. Kedua jejak belakang dengan benar menunjukkan lokasi pengecualian tidak tertangani.
Pembaruan 2: Berkat posting blog tentang Menangkap pengecualian yang tidak tertangkap dalam penghentian , saya belajar beberapa trik baru; termasuk membuang kembali pengecualian yang tidak tertangkap dalam penangan terminate. Penting untuk diperhatikan bahwa throw
pernyataan kosong dalam penangan penghentian khusus berfungsi dengan GCC dan bukan solusi portabel.
Kode:
#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 {
static const bool SET_TERMINATE = std::set_terminate(my_terminate);
}
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;
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";
array[1] = caller_address;
char ** messages = backtrace_symbols(array, size);
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 {
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 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);
}
Keluaran:
my_terminate menangkap pengecualian tanpa tangan. apa (): ERROR RUNTIME!
my_terminate backtrace mengembalikan 10 frame
[bt]: (0) ./test(terminasi_saya__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]
sinyal 6 (Dibatalkan), alamatnya adalah 0x1239 dari 0x42029331
crit_err_hdlr backtrace menghasilkan 13 frame
[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]