Kelsidavis-WoWee/src/main.cpp
Kelsi 82267320b0 fix(diagnostics): add render-phase crash markers and improve signal handling
Add signal-safe render-phase markers throughout GameScreen::render() and
Application::render() so the crash handler can report which render call was
active when a SIGSEGV occurs. The AMD RADV crash backtrace only shows 2
frames due to missing frame pointers, making it impossible to identify the
actual crash site.

Changes:
- Add volatile g_crashRenderPhase marker updated before each major render call
- Upgrade Linux signal handler to sigaction with SA_SIGINFO for faulting address
- Set ImGui CheckVkResultFn to log silent Vulkan errors in ImGui backend
- Enable -fno-omit-frame-pointer in all build configs (not just Debug/RelWithDebInfo)
2026-04-03 20:19:33 -07:00

136 lines
4.5 KiB
C++

#include "core/application.hpp"
#include "core/logger.hpp"
#include <exception>
#include <csignal>
#include <cstdlib>
#include <cctype>
#include <string>
#include <SDL2/SDL.h>
#ifdef __linux__
#include <X11/Xlib.h>
#include <execinfo.h>
#include <unistd.h>
#include <cstring>
// Keep a persistent X11 connection for emergency mouse release in signal handlers.
// XOpenDisplay inside a signal handler is unreliable, so we open it once at startup.
static Display* g_emergencyDisplay = nullptr;
static void releaseMouseGrab() {
if (g_emergencyDisplay) {
XUngrabPointer(g_emergencyDisplay, CurrentTime);
XUngrabKeyboard(g_emergencyDisplay, CurrentTime);
XFlush(g_emergencyDisplay);
}
}
#else
static void releaseMouseGrab() {}
#endif
// Render-phase marker set by GameScreen::render() — lets crash handler
// identify which render call was active when backtrace is incomplete.
extern volatile const char* g_crashRenderPhase;
#ifdef __linux__
static void crashHandlerSigaction(int sig, siginfo_t* info, void* /*ucontext*/) {
releaseMouseGrab();
void* frames[64];
int n = backtrace(frames, 64);
const char* sigName = (sig == SIGSEGV) ? "SIGSEGV" :
(sig == SIGABRT) ? "SIGABRT" :
(sig == SIGFPE) ? "SIGFPE" : "UNKNOWN";
const char* phase = (const char*)g_crashRenderPhase;
void* faultAddr = info ? info->si_addr : nullptr;
fprintf(stderr, "\n=== CRASH: signal %s (%d) renderPhase=%s faultAddr=%p ===\n",
sigName, sig, phase ? phase : "?", faultAddr);
backtrace_symbols_fd(frames, n, STDERR_FILENO);
FILE* f = fopen("/tmp/wowee_debug.log", "a");
if (f) {
fprintf(f, "\n=== CRASH: signal %s (%d) renderPhase=%s faultAddr=%p ===\n",
sigName, sig, phase ? phase : "?", faultAddr);
fflush(f);
backtrace_symbols_fd(frames, n, fileno(f));
fclose(f);
}
// Re-raise with default handler
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = SIG_DFL;
sigaction(sig, &sa, nullptr);
raise(sig);
}
#else
static void crashHandler(int sig) {
releaseMouseGrab();
std::signal(sig, SIG_DFL);
std::raise(sig);
}
#endif
static wowee::core::LogLevel readLogLevelFromEnv() {
const char* raw = std::getenv("WOWEE_LOG_LEVEL");
if (!raw || !*raw) return wowee::core::LogLevel::WARNING;
std::string level(raw);
for (char& c : level) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
if (level == "debug") return wowee::core::LogLevel::DEBUG;
if (level == "info") return wowee::core::LogLevel::INFO;
if (level == "warn" || level == "warning") return wowee::core::LogLevel::WARNING;
if (level == "error") return wowee::core::kLogLevelError;
if (level == "fatal") return wowee::core::LogLevel::FATAL;
return wowee::core::LogLevel::WARNING;
}
int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) {
#ifdef __linux__
g_emergencyDisplay = XOpenDisplay(nullptr);
// Use sigaction for SIGSEGV/SIGABRT/SIGFPE to get si_addr (faulting address)
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = crashHandlerSigaction;
sa.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &sa, nullptr);
sigaction(SIGABRT, &sa, nullptr);
sigaction(SIGFPE, &sa, nullptr);
}
std::signal(SIGTERM, [](int) { std::_Exit(1); });
std::signal(SIGINT, [](int) { std::_Exit(1); });
#else
std::signal(SIGSEGV, crashHandler);
std::signal(SIGABRT, crashHandler);
std::signal(SIGFPE, crashHandler);
std::signal(SIGTERM, crashHandler);
std::signal(SIGINT, crashHandler);
#endif
try {
wowee::core::Logger::getInstance().setLogLevel(readLogLevelFromEnv());
LOG_INFO("=== Wowee Native Client ===");
LOG_INFO("Starting application...");
wowee::core::Application app;
if (!app.initialize()) {
LOG_FATAL("Failed to initialize application");
return 1;
}
app.run();
app.shutdown();
LOG_INFO("Application exited successfully");
#ifdef __linux__
if (g_emergencyDisplay) { XCloseDisplay(g_emergencyDisplay); g_emergencyDisplay = nullptr; }
#endif
return 0;
}
catch (const std::exception& e) {
releaseMouseGrab();
LOG_FATAL("Unhandled exception: ", e.what());
return 1;
}
catch (...) {
releaseMouseGrab();
LOG_FATAL("Unknown exception occurred");
return 1;
}
}