นี่คือข้อมูลบางส่วนที่อาจใช้ในการแก้ไขปัญหาของคุณ
หากไม่มีข้อยกเว้นฟังก์ชันไลบรารีพิเศษstd::terminate()
จะถูกเรียกโดยอัตโนมัติ std::abort()
ยุติเป็นจริงชี้ไปยังฟังก์ชั่นและการเริ่มต้นค่าฟังก์ชั่นมาตรฐานห้องสมุด C หากไม่มีการสะสางเกิดขึ้นสำหรับข้อยกเว้น uncaught †มันอาจจะจริงจะเป็นประโยชน์ในการแก้จุดบกพร่องปัญหานี้ไม่มี destructors จะเรียกว่า
†มีการกำหนดการใช้งานไม่ว่าจะคลายสแต็กก่อนที่จะstd::terminate()
ถูกเรียกหรือไม่
การเรียกหาabort()
มักมีประโยชน์ในการสร้างคอร์ดัมพ์ที่สามารถวิเคราะห์เพื่อหาสาเหตุของข้อยกเว้น ตรวจสอบให้แน่ใจว่าคุณเปิดใช้งานการถ่ายโอนข้อมูลหลักผ่านulimit -c unlimited
(Linux)
คุณสามารถติดตั้งterminate()
ฟังก์ชันของคุณเองโดยใช้std::set_terminate()
. คุณควรตั้งค่าเบรกพอยต์บนฟังก์ชันยุติใน gdb ได้ คุณอาจสร้าง stack backtrace จากterminate()
ฟังก์ชันของคุณได้และ backtrace นี้อาจช่วยในการระบุตำแหน่งของข้อยกเว้น
มีการอภิปรายสั้น ๆ เกี่ยวกับข้อยกเว้นที่ไม่ถูกจับได้ในThinking in C ++, 2nd Ed ของ Bruce Eckelที่อาจเป็นประโยชน์เช่นกัน
ตั้งแต่terminate()
สายabort()
โดยค่าเริ่มต้น (ซึ่งจะทำให้เกิดSIGABRT
สัญญาณโดยค่าเริ่มต้น), คุณอาจจะสามารถตั้งค่าการSIGABRT
จัดการและจากนั้นพิมพ์ backtrace สแต็คจากภายในจัดการสัญญาณ การติดตามย้อนกลับนี้อาจช่วยในการระบุตำแหน่งของข้อยกเว้น
หมายเหตุ:ฉันพูดว่าอาจเป็นเพราะ C ++ สนับสนุนการจัดการข้อผิดพลาดที่ไม่ใช่ในเครื่องผ่านการใช้โครงสร้างภาษาเพื่อแยกการจัดการข้อผิดพลาดและรหัสการรายงานออกจากรหัสธรรมดา บล็อกจับได้และมักจะอยู่ในฟังก์ชัน / วิธีการที่แตกต่างจากจุดที่ขว้างปา นอกจากนี้ยังมีการชี้ให้ฉันเห็นในความคิดเห็น (ขอบคุณDan ) ว่ามีการกำหนดการใช้งานไม่ว่าจะคลายสแต็กก่อนที่จะterminate()
ถูกเรียก
อัปเดต:ฉันรวบรวมโปรแกรมทดสอบ Linux ที่เรียกว่าสร้าง backtrace ในterminate()
ชุดฟังก์ชันผ่านset_terminate()
และอีกโปรแกรมหนึ่งในตัวจัดการสัญญาณสำหรับSIGABRT
. การย้อนกลับทั้งสองแสดงตำแหน่งของข้อยกเว้นที่ไม่สามารถจัดการได้อย่างถูกต้อง
อัปเดต 2:ขอบคุณบล็อกโพสต์เกี่ยวกับข้อยกเว้นการจับไม่ได้ภายในการยุติฉันได้เรียนรู้เทคนิคใหม่ ๆ รวมถึงการขว้างซ้ำข้อยกเว้นที่ไม่ถูกจับภายในตัวจัดการการยุติ สิ่งสำคัญคือต้องสังเกตว่าthrow
คำสั่งว่างภายในตัวจัดการการยุติที่กำหนดเองทำงานร่วมกับ GCC และไม่ใช่โซลูชันแบบพกพา
รหัส:
#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);
}
เอาท์พุต:
my_terminate พบข้อยกเว้นที่ไม่ได้จัดการ อะไร (): RUNTIME ERROR!
my_terminate backtrace ส่งคืน 10 เฟรม
[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]
สัญญาณ 6 (ยกเลิก) ที่อยู่คือ 0x1239 จาก 0x42029331
Crit_err_hdlr backtrace ส่งคืน 13 เฟรม
[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]