Đây là một số thông tin có thể được sử dụng để gỡ lỗi sự cố của bạn
Nếu không có ngoại lệ, hàm thư viện đặc biệt std::terminate()
sẽ tự động được gọi. Chấm dứt thực sự là một con trỏ đến một chức năng và giá trị mặc định là thư viện chức năng tiêu chuẩn C std::abort()
. Nếu không dọn dẹp xảy ra đối với một ngoại lệ uncaught † , nó có thể thực sự là hữu ích trong việc gỡ rối vấn đề này vì không hủy được gọi.
† Nó được xác định thực thi cho dù ngăn xếp chưa được gắn kết trước đó std::terminate()
có được gọi hay không.
Lệnh gọi tới abort()
thường hữu ích trong việc tạo kết xuất lõi có thể được phân tích để xác định nguyên nhân của ngoại lệ. Đảm bảo rằng bạn bật kết xuất lõi qua ulimit -c unlimited
(Linux).
Bạn có thể cài đặt terminate()
chức năng của riêng mình bằng cách sử dụng std::set_terminate()
. Bạn sẽ có thể đặt một điểm ngắt trên hàm kết thúc của mình trong gdb. Bạn có thể tạo một dấu lùi ngăn xếp từ terminate()
chức năng của mình và dấu vết này có thể giúp xác định vị trí của ngoại lệ.
Có một cuộc thảo luận ngắn gọn về các trường hợp ngoại lệ không cần thiết trong Tư duy của Bruce Eckel trong C ++, Phiên bản thứ hai cũng có thể hữu ích.
Vì terminate()
các cuộc gọi abort()
theo mặc định (sẽ gây ra SIGABRT
tín hiệu theo mặc định), bạn có thể đặt một SIGABRT
trình xử lý và sau đó in một dấu vết ngăn xếp từ bên trong trình xử lý tín hiệu . Dấu vết này có thể giúp xác định vị trí của ngoại lệ.
Lưu ý: Tôi nói có thể vì C ++ hỗ trợ xử lý lỗi không cục bộ thông qua việc sử dụng các cấu trúc ngôn ngữ để tách mã báo cáo và xử lý lỗi khỏi mã thông thường. Khối bắt có thể, và thường là, nằm ở một chức năng / phương pháp khác với điểm ném. Nó cũng đã được chỉ ra cho tôi trong các nhận xét (cảm ơn Dan ) rằng nó được xác định thực thi cho dù ngăn xếp chưa được gắn kết trước đó terminate()
được gọi hay không.
Cập nhật: Tôi đã tập hợp một chương trình thử nghiệm Linux có tên là tạo ra một dấu hiệu tồn đọng trong một terminate()
hàm được đặt qua set_terminate()
và một chương trình khác trong bộ xử lý tín hiệu cho SIGABRT
. Cả hai dấu lùi đều hiển thị chính xác vị trí của ngoại lệ chưa được xử lý.
Cập nhật 2: Nhờ một bài đăng trên blog về Bắt các ngoại lệ không cần thiết trong vòng chấm dứt , tôi đã học được một vài thủ thuật mới; bao gồm việc ném lại ngoại lệ chưa được suy nghĩ trong trình xử lý kết thúc. Điều quan trọng cần lưu ý là throw
câu lệnh trống trong trình xử lý kết thúc tùy chỉnh hoạt động với GCC và không phải là một giải pháp di động.
Mã:
#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);
}
Đầu ra:
my_termina đã bắt gặp ngoại lệ không được xử lý. what (): RUNTIME ERROR!
my_termina backtrace trả lại 10 khung hình
[bt]: (0) ./test(my_termina__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]
tín hiệu 6 (Đã hủy bỏ), địa chỉ là 0x1239 từ 0x42029331
crit_err_hdlr backtrace trả về 13 khung hình
[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]