// https://stackoverflow.com/a/1925461 #include "crash_printer/common.hpp" #include "crash_printer/linux.hpp" #include #include #include #include #include // SIGBUS + SA_SIGINFO ... #include // strsignal #include // backtrace + backtrace_symbols constexpr static const int max_stack_frames = 50; static bool old_SIGILL = false; static struct sigaction oldact_SIGILL{}; static bool old_SIGSEGV = false; static struct sigaction oldact_SIGSEGV{}; static bool old_SIGBUS = false; static struct sigaction oldact_SIGBUS{}; static std::string logs_filepath{}; static void restore_handlers() { if (old_SIGILL) { old_SIGILL = false; sigaction(SIGILL, &oldact_SIGILL, nullptr); } if (old_SIGSEGV) { old_SIGSEGV = false; sigaction(SIGSEGV, &oldact_SIGSEGV, nullptr); } if (old_SIGBUS) { old_SIGBUS = false; sigaction(SIGBUS, &oldact_SIGBUS, nullptr); } } static void exception_handler(int signal, siginfo_t *info, void *context, struct sigaction *oldact) { if (!crash_printer::create_dir(logs_filepath)) { return; } std::ofstream file(logs_filepath, std::ios::app); auto now = std::chrono::system_clock::now(); auto t_now = std::chrono::system_clock::to_time_t(now); auto gm_time = std::gmtime(&t_now); auto time = std::string(std::asctime(gm_time)); time.pop_back(); // remove the trailing '\n' added by asctime crash_printer::write(file, "[" + time + "]"); { std::stringstream ss{}; ss << "Unhandled exception:" << std::endl << " code: " << std::dec << signal << " (" << strsignal(signal) << ")" << std::endl; crash_printer::write(file, ss.str()); } void* stack_frames[max_stack_frames]; int stack_size = backtrace(stack_frames, max_stack_frames); char** stack_symbols = backtrace_symbols(stack_frames, stack_size); if (stack_symbols != nullptr) { // fprintf(stderr, "Stack trace:\n"); crash_printer::write(file, "*********** Stack trace ***********"); for (int i = 1; i < stack_size; ++i) { char *symbol = stack_symbols[i]; std::stringstream ss{}; ss << "[frame " << std::dec << (stack_size - i - 1) << "]: " << std::hex << stack_frames[i] << " | " << symbol; crash_printer::write(file, ss.str()); } free(stack_symbols); } crash_printer::write(file, "**********************************\n"); file.close(); } // Register the signal handler for illegal instruction (SIGILL) static void exception_handler_SIGILL(int signal, siginfo_t *info, void *context) { exception_handler(signal, info, context, &oldact_SIGILL); sigaction(SIGILL, &oldact_SIGILL, nullptr); } static void exception_handler_SIGSEGV(int signal, siginfo_t *info, void *context) { exception_handler(signal, info, context, &oldact_SIGSEGV); sigaction(SIGSEGV, &oldact_SIGSEGV, nullptr); } static void exception_handler_SIGBUS(int signal, siginfo_t *info, void *context) { exception_handler(signal, info, context, &oldact_SIGBUS); sigaction(SIGBUS, &oldact_SIGBUS, nullptr); } bool crash_printer::init(const std::string &log_file) { logs_filepath = log_file; // save old handlers // https://linux.die.net/man/2/sigaction if ( sigaction(SIGILL, nullptr, &oldact_SIGILL) != 0 || sigaction(SIGSEGV, nullptr, &oldact_SIGSEGV) != 0 || sigaction(SIGBUS, nullptr, &oldact_SIGBUS) != 0) { return false; } constexpr int sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK | SA_RESETHAND | SA_NOCLDSTOP; // https://linux.die.net/man/2/sigaction // register handler for illegal instruction (SIGILL) struct sigaction sa_SIGILL{}; sa_SIGILL.sa_sigaction = exception_handler_SIGILL; sa_SIGILL.sa_flags = sa_flags; sigemptyset(&sa_SIGILL.sa_mask); // all signals unblocked if (sigaction(SIGILL, &sa_SIGILL, nullptr) != 0) { restore_handlers(); return false; } old_SIGILL = true; // register handler for segmentation fault (SIGSEGV) struct sigaction sa_SIGSEGV{}; sa_SIGSEGV.sa_sigaction = exception_handler_SIGSEGV; sa_SIGSEGV.sa_flags = sa_flags; sigemptyset(&sa_SIGSEGV.sa_mask); // all signals unblocked if (sigaction(SIGSEGV, &sa_SIGSEGV, nullptr) != 0) { restore_handlers(); return false; } old_SIGSEGV = true; // register handler for bus error (SIGBUS) struct sigaction sa_SIGBUS{}; sa_SIGBUS.sa_sigaction = exception_handler_SIGBUS; sa_SIGBUS.sa_flags = sa_flags; sigemptyset(&sa_SIGBUS.sa_mask); // all signals unblocked if (sigaction(SIGBUS, &sa_SIGBUS, nullptr) != 0) { restore_handlers(); return false; } old_SIGBUS = true; // // register handler for floating-point exception (SIGFPE) // if (sigaction(SIGFPE, &sa, nullptr) != 0) { // perror("Error setting up signal handler"); // return EXIT_FAILURE; // } return true; } void crash_printer::deinit() { restore_handlers(); }