diff --git a/crash_printer/crash_printer/linux.hpp b/crash_printer/crash_printer/linux.hpp new file mode 100644 index 00000000..950edd9f --- /dev/null +++ b/crash_printer/crash_printer/linux.hpp @@ -0,0 +1,15 @@ +#ifndef _CRASH_PRINTER_LINUX +#define _CRASH_PRINTER_LINUX + + +#include + +namespace crash_printer { + +bool init(const std::string &log_file); + +void deinit(); + +} + +#endif // _CRASH_PRINTER_LINUX diff --git a/crash_printer/linux.cpp b/crash_printer/linux.cpp new file mode 100644 index 00000000..5c9ca0ce --- /dev/null +++ b/crash_printer/linux.cpp @@ -0,0 +1,169 @@ +// 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(); +}