Try out CrashCatch for signal reporting.

This commit is contained in:
Ernie Pasveer
2025-04-05 10:35:18 -05:00
parent 17efc5d60f
commit 0b53706a9d
3 changed files with 206 additions and 0 deletions
+1
View File
@@ -2,3 +2,4 @@ hellosegv
*.seer
core.*
rr/*
crash_dumps/*
+201
View File
@@ -0,0 +1,201 @@
#pragma once
/*
CrashCatch - A simple cross-platform crash handler
Version 1.2.0
Created by Keith Pottratz
Email: keithpotz@gmail.com
License: MIT
*/
#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <chrono>
#include <filesystem>
#include <functional>
#if defined(_WIN32)
#define CRASHCATCH_PLATFORM_WINDOWS
#include <Windows.h>
#include <DbgHelp.h>
#pragma comment(lib, "DbgHelp.lib") //Auto-link debugging support library
#elif defined(__linux__)
#define CRASHCATCH_PLATFORM_LINUX
#include <signal.h>
#include <execinfo.h>
#include <unistd.h>
#include <limits.h>
#include <cxxabi.h>
#include <string.h>
#endif
namespace CrashCatch {
// Config structure to customize CrashCatch behaviour
struct Config {
std::string dumpFolder = "./crash_dumps/"; // Default folder for crash dumps
std::string dumpFileName = "crash"; // Default file name for crash dumps
bool enableTextLog = true; // Enable text log for crash dumps
bool autoTimestamp = true; // Automatically append timestamp to dump file name
bool showCrashDialog = false; // Windows only
std::function<void()> onCrash = nullptr; // Callback function to execute on crash
std::string appVersion = "unknown"; // Application version
std::string buildConfig =
#ifdef _DEBUG
"Debug";
#else
"Release";
#endif
std::string additionalNotes = ""; // Additional notes to include in the crash log
};
inline Config globalConfig; // Global configuration object used internally
// Utility: Generate a timestamp string
inline std::string getTimestamp() {
auto now = std::chrono::system_clock::now();
auto time = std::chrono::system_clock::to_time_t(now);
std::stringstream ss;
ss << std::put_time(std::localtime(&time), "%Y-%m-%d_%H-%M-%S");
return ss.str();
}
inline std::string getExecutablePath() {
#ifdef CRASHCATCH_PLATFORM_WINDOWS
char buffer[MAX_PATH];
GetModuleFileNameA(nullptr, buffer, MAX_PATH);
return std::string(buffer);
#elif defined(CRASHCATCH_PLATFORM_LINUX)
char path[PATH_MAX];
ssize_t len = readlink("/proc/self/exe", path, sizeof(path) - 1);
if (len != -1) {
path[len] = '\0';
return std::string(path);
}
return "(unknown)";
#endif
}
#ifdef CRASHCATCH_PLATFORM_LINUX
inline std::string demangle(const char* symbol) {
size_t size;
int status;
char* demangled = abi::__cxa_demangle(symbol, nullptr, &size, &status);
std::string result = (status == 0) ? demangled : symbol;
free(demangled);
return result;
}
#endif
// Collect Diagnositcs for crash log (version, build config, platform, executable path)
inline std::string getDiagnosticsInfo() {
std::stringstream ss;
ss << "App Version: " << globalConfig.appVersion << "\n";
ss << "Build Config: " << globalConfig.buildConfig << "\n";
#ifdef CRASHCATCH_PLATFORM_WINDOWS
ss << "Platform: Windows\n";
#elif defined(CRASHCATCH_PLATFORM_LINUX)
ss << "Platform: Linux\n";
#endif
ss << "Executable: " << getExecutablePath() << "\n";
if (!globalConfig.additionalNotes.empty()) {
ss << "Notes: " << globalConfig.additionalNotes << "\n";
}
return ss.str();
}
// Write Crash summary .txt file
inline void writeCrashLog(const std::string& logPath, const std::string& timestamp, int signal = 0) {
std::ofstream log(logPath);
if (!log.is_open()) return;
log << "Crash Report\n============\n";
#ifdef CRASHCATCH_PLATFORM_LINUX
// Stack trace for Linux
log << "Signal: " << strsignal(signal) << " (" << signal << ")\n";
#endif
log << "Timestamp: " << (timestamp.empty() ? "N/A" : timestamp) << "\n\n";
log << "Environment Info:\n" << getDiagnosticsInfo() << "\n";
#ifdef CRASHCATCH_PLATFORM_LINUX
void* callstack[128];
int frames = backtrace(callstack, 128);
char** symbols = backtrace_symbols(callstack, frames);
log << "\nStack Trace:\n";
for (int i = 0; i < frames; ++i) {
std::string raw = symbols[i];
std::string demangled = demangle(raw.c_str());
log << " [" << i << "]: " << demangled << "\n";
}
free(symbols);
#endif
log.close();
}
#ifdef CRASHCATCH_PLATFORM_WINDOWS
// Windows-specific crash handler
inline LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* ep) {
if (globalConfig.onCrash) globalConfig.onCrash();
std::filesystem::create_directories(globalConfig.dumpFolder);
std::string timestamp = globalConfig.autoTimestamp ? getTimestamp() : "";
std::string base = globalConfig.dumpFileName + (timestamp.empty() ? "" : ("_" + timestamp));
std::string dumpPath = globalConfig.dumpFolder + base + ".dmp";
HANDLE hFile = CreateFileA(dumpPath.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hFile != INVALID_HANDLE_VALUE) {
MINIDUMP_EXCEPTION_INFORMATION dumpInfo = { GetCurrentThreadId(), ep, FALSE };
MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpWithDataSegs, &dumpInfo, nullptr, nullptr);
CloseHandle(hFile);
if (globalConfig.enableTextLog) {
std::string logPath = globalConfig.dumpFolder + base + ".txt";
writeCrashLog(logPath, timestamp);
}
if (globalConfig.showCrashDialog) {
std::string msg = "Crash occurred. Dump written to:\n" + dumpPath;
MessageBoxA(nullptr, msg.c_str(), "Crash Detected", MB_OK | MB_ICONERROR);
}
}
return EXCEPTION_EXECUTE_HANDLER;
}
#endif
#ifdef CRASHCATCH_PLATFORM_LINUX
//Linux signal handler: creates .txt crash log with stack trace
inline void linuxSignalHandler(int signum) {
if (globalConfig.onCrash) globalConfig.onCrash();
std::filesystem::create_directories(globalConfig.dumpFolder);
std::string timestamp = globalConfig.autoTimestamp ? getTimestamp() : "";
std::string base = globalConfig.dumpFileName + (timestamp.empty() ? "" : ("_" + timestamp));
std::string logPath = globalConfig.dumpFolder + base + ".txt";
writeCrashLog(logPath, timestamp, signum);
_exit(1); // Exit immediately after handling signal
}
#endif
//Main initialization function: sets up the crash handler
inline bool initialize(const Config& config = Config()) {
globalConfig = config;
#ifdef CRASHCATCH_PLATFORM_WINDOWS
SetUnhandledExceptionFilter(UnhandledExceptionHandler);
#elif defined(CRASHCATCH_PLATFORM_LINUX)
signal(SIGSEGV, linuxSignalHandler); // Segmentation fault
signal(SIGABRT, linuxSignalHandler); // Abort signal
signal(SIGFPE, linuxSignalHandler); // Floating point exception
signal(SIGILL, linuxSignalHandler); // Illegal instruction
signal(SIGBUS, linuxSignalHandler); // Misaligned memory address
#endif
return true;
}
//One liner to enable crash handling with default config
inline bool enable() { return initialize(Config{}); }
//Optional Auto enable crash handler
#ifdef CRASHCATCH_AUTO_INIT
namespace {
const bool _autoInit = CrashCatch::enable();
}
#endif
} // namespace CrashCatch
+4
View File
@@ -1,9 +1,13 @@
#include "CrashCatch.hpp"
#include "function1.h"
#include <string>
#include <iostream>
int main (int argc, char** argv) {
CrashCatch::enable();
for (int i=0; i<argc; i++) {
std::cout << "XX: " << i << " " << argv[i] << std::endl;
}