mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-07-17 22:39:17 +02:00
New Crash Handler
This commit is contained in:
187
EU07.cpp
187
EU07.cpp
@@ -21,12 +21,13 @@ Stele, firleju, szociu, hunter, ZiomalCl, OLI_EU and others
|
||||
#include "application/application.h"
|
||||
#include "utilities/Logs.h"
|
||||
#include <cstdlib>
|
||||
#ifdef WITHDUMPGEN
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <exception>
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#include <DbgHelp.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
|
||||
@@ -43,20 +44,144 @@ void export_e3d_standalone(std::string in, std::string out, int flags, bool dyna
|
||||
#ifdef _WIN32
|
||||
#pragma comment(lib, "Dbghelp.lib")
|
||||
|
||||
LONG WINAPI CrashHandler(EXCEPTION_POINTERS *ExceptionInfo)
|
||||
// Builds a "crash_YYYY-MM-DD_HH-MM-SS" timestamp shared by the .txt and .dmp.
|
||||
static std::string crash_timestamp()
|
||||
{
|
||||
// Get current local time
|
||||
SYSTEMTIME st;
|
||||
GetLocalTime(&st);
|
||||
|
||||
// Format: crash_YYYY-MM-DD_HH-MM-SS.dmp
|
||||
std::ostringstream oss;
|
||||
oss << "crash_" << std::setw(4) << std::setfill('0') << st.wYear << "-" << std::setw(2) << std::setfill('0') << st.wMonth << "-" << std::setw(2) << std::setfill('0') << st.wDay << "_"
|
||||
<< std::setw(2) << std::setfill('0') << st.wHour << "-" << std::setw(2) << std::setfill('0') << st.wMinute << "-" << std::setw(2) << std::setfill('0') << st.wSecond << ".dmp";
|
||||
<< std::setw(2) << std::setfill('0') << st.wHour << "-" << std::setw(2) << std::setfill('0') << st.wMinute << "-" << std::setw(2) << std::setfill('0') << st.wSecond;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string filename = oss.str();
|
||||
// Writes a human-readable crash report with a symbolized call stack straight to
|
||||
// a file. Deliberately bypasses the engine's async logger (WriteLog/ErrorLog
|
||||
// push to a worker thread behind a mutex that may be dead or held at crash
|
||||
// time), writing synchronously instead so the report survives the crash.
|
||||
// ExceptionInfo may be null (e.g. from std::terminate), in which case the
|
||||
// current thread context is captured on the spot.
|
||||
static void WriteCrashReport(const std::string &basename, const char *reason, EXCEPTION_POINTERS *ExceptionInfo)
|
||||
{
|
||||
std::ofstream out(basename + ".txt", std::ios::trunc);
|
||||
if (!out.is_open())
|
||||
return;
|
||||
|
||||
HANDLE hFile = CreateFileA(filename.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
out << "MaSzyna EU07 crash report\n";
|
||||
out << "version: " << Global.asVersion << "\n";
|
||||
out << "reason: " << (reason ? reason : "unknown") << "\n";
|
||||
|
||||
HANDLE process = GetCurrentProcess();
|
||||
CONTEXT localContext;
|
||||
CONTEXT *ctx;
|
||||
|
||||
if (ExceptionInfo && ExceptionInfo->ContextRecord)
|
||||
{
|
||||
ctx = ExceptionInfo->ContextRecord;
|
||||
if (const EXCEPTION_RECORD *er = ExceptionInfo->ExceptionRecord)
|
||||
{
|
||||
const char *name = nullptr;
|
||||
switch (er->ExceptionCode)
|
||||
{
|
||||
case EXCEPTION_ACCESS_VIOLATION: name = "ACCESS_VIOLATION"; break;
|
||||
case EXCEPTION_STACK_OVERFLOW: name = "STACK_OVERFLOW"; break;
|
||||
case EXCEPTION_ILLEGAL_INSTRUCTION: name = "ILLEGAL_INSTRUCTION"; break;
|
||||
case EXCEPTION_INT_DIVIDE_BY_ZERO: name = "INT_DIVIDE_BY_ZERO"; break;
|
||||
case EXCEPTION_FLT_DIVIDE_BY_ZERO: name = "FLT_DIVIDE_BY_ZERO"; break;
|
||||
case EXCEPTION_PRIV_INSTRUCTION: name = "PRIV_INSTRUCTION"; break;
|
||||
case EXCEPTION_IN_PAGE_ERROR: name = "IN_PAGE_ERROR"; break;
|
||||
case 0xE06D7363: name = "C++ exception (MSVC)"; break;
|
||||
default: break;
|
||||
}
|
||||
out << "code: 0x" << std::hex << er->ExceptionCode << std::dec;
|
||||
if (name) out << " (" << name << ")";
|
||||
out << "\n";
|
||||
out << "address: 0x" << std::hex << static_cast<DWORD64>(reinterpret_cast<uintptr_t>(er->ExceptionAddress)) << std::dec << "\n";
|
||||
if (er->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && er->NumberParameters >= 2)
|
||||
{
|
||||
const ULONG_PTR op = er->ExceptionInformation[0];
|
||||
out << "access: " << (op == 1 ? "write" : op == 8 ? "execute" : "read")
|
||||
<< " at 0x" << std::hex << er->ExceptionInformation[1] << std::dec << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RtlCaptureContext(&localContext);
|
||||
ctx = &localContext;
|
||||
}
|
||||
|
||||
out << "\ncall stack:\n";
|
||||
|
||||
SymSetOptions(SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_DEFERRED_LOADS);
|
||||
SymInitialize(process, nullptr, TRUE);
|
||||
|
||||
STACKFRAME64 frame = {};
|
||||
DWORD machine;
|
||||
#if defined(_M_X64)
|
||||
machine = IMAGE_FILE_MACHINE_AMD64;
|
||||
frame.AddrPC.Offset = ctx->Rip;
|
||||
frame.AddrFrame.Offset = ctx->Rbp;
|
||||
frame.AddrStack.Offset = ctx->Rsp;
|
||||
#elif defined(_M_IX86)
|
||||
machine = IMAGE_FILE_MACHINE_I386;
|
||||
frame.AddrPC.Offset = ctx->Eip;
|
||||
frame.AddrFrame.Offset = ctx->Ebp;
|
||||
frame.AddrStack.Offset = ctx->Esp;
|
||||
#endif
|
||||
frame.AddrPC.Mode = AddrModeFlat;
|
||||
frame.AddrFrame.Mode = AddrModeFlat;
|
||||
frame.AddrStack.Mode = AddrModeFlat;
|
||||
|
||||
char symbolMem[sizeof(SYMBOL_INFO) + 256];
|
||||
SYMBOL_INFO *symbol = reinterpret_cast<SYMBOL_INFO *>(symbolMem);
|
||||
|
||||
for (int i = 0; i < 128; ++i)
|
||||
{
|
||||
if (!StackWalk64(machine, process, GetCurrentThread(), &frame, ctx, nullptr, SymFunctionTableAccess64, SymGetModuleBase64, nullptr))
|
||||
break;
|
||||
const DWORD64 pc = frame.AddrPC.Offset;
|
||||
if (pc == 0)
|
||||
break;
|
||||
|
||||
out << " [" << std::setw(2) << std::setfill('0') << i << "] ";
|
||||
|
||||
IMAGEHLP_MODULE64 mod = {};
|
||||
mod.SizeOfStruct = sizeof(mod);
|
||||
if (SymGetModuleInfo64(process, pc, &mod))
|
||||
out << mod.ModuleName << "!";
|
||||
|
||||
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
symbol->MaxNameLen = 255;
|
||||
DWORD64 disp = 0;
|
||||
if (SymFromAddr(process, pc, &disp, symbol))
|
||||
out << symbol->Name << " + 0x" << std::hex << disp << std::dec;
|
||||
else
|
||||
out << "0x" << std::hex << pc << std::dec;
|
||||
|
||||
IMAGEHLP_LINE64 lineInfo = {};
|
||||
lineInfo.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
||||
DWORD lineDisp = 0;
|
||||
if (SymGetLineFromAddr64(process, pc, &lineDisp, &lineInfo))
|
||||
out << " (" << lineInfo.FileName << ":" << lineInfo.LineNumber << ")";
|
||||
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
SymCleanup(process);
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
|
||||
LONG WINAPI CrashHandler(EXCEPTION_POINTERS *ExceptionInfo)
|
||||
{
|
||||
const std::string basename = crash_timestamp();
|
||||
|
||||
// Readable stack trace first -- it's the most useful artifact and needs no
|
||||
// debugger to inspect.
|
||||
WriteCrashReport(basename, "unhandled SEH exception", ExceptionInfo);
|
||||
|
||||
HANDLE hFile = CreateFileA((basename + ".dmp").c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (hFile != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
MINIDUMP_EXCEPTION_INFORMATION dumpInfo;
|
||||
@@ -68,23 +193,44 @@ LONG WINAPI CrashHandler(EXCEPTION_POINTERS *ExceptionInfo)
|
||||
MINIDUMP_TYPE dumpType = MINIDUMP_TYPE(MiniDumpWithFullMemory | MiniDumpWithHandleData | MiniDumpWithThreadInfo | MiniDumpWithUnloadedModules | MiniDumpWithIndirectlyReferencedMemory |
|
||||
MiniDumpWithFullMemoryInfo | MiniDumpWithTokenInformation);
|
||||
|
||||
MessageBoxA(nullptr, "Simulator crash occured :(\n", "Simulator crashed :(", MB_ICONERROR);
|
||||
MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, dumpType, &dumpInfo, nullptr, nullptr);
|
||||
|
||||
CloseHandle(hFile);
|
||||
}
|
||||
|
||||
MessageBoxA(nullptr,
|
||||
("Simulator crash occured :(\n\nA crash report was written next to the executable:\n" + basename + ".txt (readable call stack)\n" + basename + ".dmp (full minidump)").c_str(),
|
||||
"Simulator crashed :(", MB_ICONERROR);
|
||||
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
|
||||
// Catches uncaught C++ exceptions (and other std::terminate triggers such as
|
||||
// pure-virtual calls), which the SEH filter above does NOT see. Extracts the
|
||||
// in-flight exception's message when possible.
|
||||
static void CrashTerminateHandler()
|
||||
{
|
||||
std::string reason = "std::terminate";
|
||||
if (std::exception_ptr ep = std::current_exception())
|
||||
{
|
||||
try { std::rethrow_exception(ep); }
|
||||
catch (const std::exception &e) { reason = std::string("uncaught exception: ") + e.what(); }
|
||||
catch (...) { reason = "uncaught non-standard exception"; }
|
||||
}
|
||||
|
||||
const std::string basename = crash_timestamp();
|
||||
WriteCrashReport(basename, reason.c_str(), nullptr);
|
||||
MessageBoxA(nullptr, ("Simulator terminated :(\n\n" + reason + "\n\nSee " + basename + ".txt for the call stack.").c_str(), "Simulator crashed :(", MB_ICONERROR);
|
||||
std::_Exit(3);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
#ifdef WITHDUMPGEN
|
||||
#ifdef _WIN32
|
||||
SetUnhandledExceptionFilter(CrashHandler);
|
||||
#endif
|
||||
std::set_terminate(CrashTerminateHandler);
|
||||
#endif
|
||||
// init start timestamp
|
||||
Global.startTimestamp = std::chrono::steady_clock::now();
|
||||
@@ -112,13 +258,22 @@ int main(int argc, char *argv[])
|
||||
catch (std::bad_alloc const &Error)
|
||||
{
|
||||
ErrorLog("Critical error, memory allocation failure: " + std::string(Error.what()));
|
||||
#ifdef _WIN32
|
||||
WriteCrashReport(crash_timestamp(), (std::string("memory allocation failure: ") + Error.what()).c_str(), nullptr);
|
||||
#endif
|
||||
}
|
||||
#ifdef _WIN32
|
||||
catch (std::runtime_error const &Error)
|
||||
catch (std::exception const &Error)
|
||||
{
|
||||
std::string msg = "Simulator crash occured :(\n";
|
||||
msg += Error.what();
|
||||
MessageBoxA(nullptr, msg.c_str(), "Simulator crashed :(", MB_ICONERROR);
|
||||
const std::string basename = crash_timestamp();
|
||||
WriteCrashReport(basename, (std::string("unhandled exception: ") + Error.what()).c_str(), nullptr);
|
||||
MessageBoxA(nullptr, ("Simulator crash occured :(\n\n" + std::string(Error.what()) + "\n\nSee " + basename + ".txt for details.").c_str(), "Simulator crashed :(", MB_ICONERROR);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
const std::string basename = crash_timestamp();
|
||||
WriteCrashReport(basename, "unhandled non-standard exception", nullptr);
|
||||
MessageBoxA(nullptr, ("Simulator crash occured :(\n\nSee " + basename + ".txt for details.").c_str(), "Simulator crashed :(", MB_ICONERROR);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user