Files
XCEngine/editor/src/Platform/WindowsProcessDiagnostics.h

153 lines
4.5 KiB
C
Raw Normal View History

#pragma once
#include "Platform/Win32Utf8.h"
#ifdef _MSC_VER
#include <crtdbg.h>
#endif
#include <dbghelp.h>
#include <stdio.h>
#include <string>
#include <windows.h>
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<SYMBOL_INFO*>(symbolStorage);
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
symbol->MaxNameLen = MAX_SYM_NAME;
for (USHORT frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
const DWORD64 address = reinterpret_cast<DWORD64>(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<unsigned long long>(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