linux crash printer

This commit is contained in:
otavepto 2024-01-07 09:20:14 +02:00
parent d15441a30e
commit 57e7dbebde
2 changed files with 184 additions and 0 deletions

View File

@ -0,0 +1,15 @@
#ifndef _CRASH_PRINTER_LINUX
#define _CRASH_PRINTER_LINUX
#include <string>
namespace crash_printer {
bool init(const std::string &log_file);
void deinit();
}
#endif // _CRASH_PRINTER_LINUX

169
crash_printer/linux.cpp Normal file
View File

@ -0,0 +1,169 @@
// https://stackoverflow.com/a/1925461
#include "crash_printer/common.hpp"
#include "crash_printer/linux.hpp"
#include <sstream>
#include <chrono>
#include <ctime>
#include <ucontext.h>
#include <signal.h> // SIGBUS + SA_SIGINFO ...
#include <string.h> // strsignal
#include <execinfo.h> // 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();
}