#pragma once #include "Platform/Win32Utf8.h" #ifdef _MSC_VER #include #endif #include #include #include #include namespace XCEngine { namespace Editor { namespace Platform { inline std::string GetExecutableLogPath(const char* fileName) { return GetExecutableDirectoryUtf8() + "\\" + fileName; } #ifdef _MSC_VER inline void WriteInvalidParameterReport( const wchar_t* expression, const wchar_t* function, const wchar_t* file, unsigned int line) { const std::string logPath = GetExecutableLogPath("crash.log"); FILE* logFile = nullptr; fopen_s(&logFile, logPath.c_str(), "a"); if (logFile != nullptr) { fwprintf( logFile, L"[CRT] Invalid parameter. function=%s file=%s line=%u expression=%s\n", function != nullptr ? function : L"(null)", file != nullptr ? file : L"(null)", line, expression != nullptr ? expression : L"(null)"); fclose(logFile); } fwprintf( stderr, L"[CRT] Invalid parameter. function=%s file=%s line=%u expression=%s\n", function != nullptr ? function : L"(null)", file != nullptr ? file : L"(null)", line, expression != nullptr ? expression : L"(null)"); fflush(stderr); } inline void InvalidParameterHandler( const wchar_t* expression, const wchar_t* function, const wchar_t* file, unsigned int line, uintptr_t) { WriteInvalidParameterReport(expression, function, file, line); } #endif inline void WriteCrashStackTrace(FILE* file) { if (file == nullptr) { return; } HANDLE process = GetCurrentProcess(); SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); if (!SymInitialize(process, nullptr, TRUE)) { fprintf(file, "[CRASH] SymInitialize failed: %lu\n", GetLastError()); return; } void* frames[64] = {}; const USHORT frameCount = CaptureStackBackTrace(0, 64, frames, nullptr); char symbolStorage[sizeof(SYMBOL_INFO) + MAX_SYM_NAME] = {}; SYMBOL_INFO* symbol = reinterpret_cast(symbolStorage); symbol->SizeOfStruct = sizeof(SYMBOL_INFO); symbol->MaxNameLen = MAX_SYM_NAME; for (USHORT frameIndex = 0; frameIndex < frameCount; ++frameIndex) { const DWORD64 address = reinterpret_cast(frames[frameIndex]); DWORD64 displacement = 0; IMAGEHLP_LINE64 line = {}; line.SizeOfStruct = sizeof(line); DWORD lineDisplacement = 0; fprintf(file, "[CRASH] #%u 0x%p", frameIndex, frames[frameIndex]); if (SymFromAddr(process, address, &displacement, symbol)) { fprintf(file, " %s+0x%llX", symbol->Name, static_cast(displacement)); } if (SymGetLineFromAddr64(process, address, &lineDisplacement, &line)) { fprintf(file, " (%s:%lu)", line.FileName, line.LineNumber); } fprintf(file, "\n"); } SymCleanup(process); } inline LONG WINAPI CrashExceptionFilter(EXCEPTION_POINTERS* exceptionPointers) { const std::string logPath = GetExecutableLogPath("crash.log"); FILE* file = nullptr; fopen_s(&file, logPath.c_str(), "a"); if (file) { fprintf( file, "[CRASH] ExceptionCode=0x%08X, Address=0x%p\n", exceptionPointers->ExceptionRecord->ExceptionCode, exceptionPointers->ExceptionRecord->ExceptionAddress); WriteCrashStackTrace(file); fclose(file); } fprintf( stderr, "[CRASH] ExceptionCode=0x%08X, Address=0x%p\n", exceptionPointers->ExceptionRecord->ExceptionCode, exceptionPointers->ExceptionRecord->ExceptionAddress); return EXCEPTION_EXECUTE_HANDLER; } inline void InstallCrashExceptionFilter() { SetUnhandledExceptionFilter(CrashExceptionFilter); #ifdef _MSC_VER _set_invalid_parameter_handler(InvalidParameterHandler); #endif } inline void RedirectStderrToExecutableLog() { const std::string stderrPath = GetExecutableLogPath("stderr.log"); freopen(stderrPath.c_str(), "w", stderr); #ifdef _MSC_VER _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE); _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE); _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); #endif } } // namespace Platform } // namespace Editor } // namespace XCEngine