mirror of
https://github.com/Detanup01/gbe_fork.git
synced 2024-12-27 02:44:16 +08:00
170 lines
5.2 KiB
C++
170 lines
5.2 KiB
C++
|
// 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();
|
||
|
}
|