2024-05-23 09:37:41 +08:00
|
|
|
#include "ExceptionHelper.hpp"
|
|
|
|
#if YYCC_OS == YYCC_OS_WINDOWS
|
|
|
|
|
2024-06-13 15:24:12 +08:00
|
|
|
#include "WinFctHelper.hpp"
|
2024-06-15 16:44:11 +08:00
|
|
|
#include "ConsoleHelper.hpp"
|
|
|
|
#include "StringHelper.hpp"
|
|
|
|
#include "IOHelper.hpp"
|
|
|
|
#include "EncodingHelper.hpp"
|
2024-06-17 15:10:45 +08:00
|
|
|
#include "FsPathPatch.hpp"
|
2024-05-23 09:37:41 +08:00
|
|
|
#include <filesystem>
|
|
|
|
#include <cstdarg>
|
|
|
|
#include <cstdio>
|
|
|
|
#include <cinttypes>
|
|
|
|
|
|
|
|
#include "WinImportPrefix.hpp"
|
|
|
|
#include <Windows.h>
|
|
|
|
#include <DbgHelp.h>
|
|
|
|
#include "WinImportSuffix.hpp"
|
|
|
|
|
|
|
|
namespace YYCC::ExceptionHelper {
|
2024-06-15 16:44:11 +08:00
|
|
|
|
2024-05-23 09:37:41 +08:00
|
|
|
/**
|
2024-06-13 11:18:25 +08:00
|
|
|
* @brief True if the exception handler already registered, otherwise false.
|
|
|
|
* @details
|
|
|
|
* This variable is designed to prevent multiple register operation
|
|
|
|
* because unhandled exception handler should only be registered once.
|
|
|
|
* \n
|
|
|
|
* Register function should check whether this variable is false before registering,
|
|
|
|
* and set this variable to true after registing.
|
|
|
|
* Unregister as well as should do the same check.
|
2024-05-23 09:37:41 +08:00
|
|
|
*/
|
|
|
|
static bool g_IsRegistered = false;
|
|
|
|
/**
|
2024-06-13 11:18:25 +08:00
|
|
|
* @brief True if a exception handler is running, otherwise false.
|
2024-06-15 16:44:11 +08:00
|
|
|
* @details
|
2024-05-23 09:37:41 +08:00
|
|
|
* This variable is served for blocking possible infinity recursive exception handling.
|
2024-06-13 11:18:25 +08:00
|
|
|
* \n
|
|
|
|
* When entering unhandled exception handler, we must check whether this variable is true.
|
|
|
|
* If it is true, it mean that there is another unhandled exception handler running.
|
|
|
|
* Then we should exit immediately.
|
|
|
|
* Otherwise, this variable should be set to true indicating we are processing unhandled exception.
|
|
|
|
* After processing exception, at the end of unhandled exception handler,
|
|
|
|
* we should restore this value to false.
|
2024-06-15 16:44:11 +08:00
|
|
|
*
|
2024-05-23 09:37:41 +08:00
|
|
|
*/
|
|
|
|
static bool g_IsProcessing = false;
|
|
|
|
/**
|
|
|
|
* @brief The backup of original exception handler.
|
2024-06-13 11:18:25 +08:00
|
|
|
* @details
|
|
|
|
* This variable was set when registering unhandled exception handler.
|
|
|
|
* And will be used when unregistering for restoring.
|
2024-05-23 09:37:41 +08:00
|
|
|
*/
|
2024-06-13 11:18:25 +08:00
|
|
|
static LPTOP_LEVEL_EXCEPTION_FILTER g_ProcBackup;
|
2024-05-23 09:37:41 +08:00
|
|
|
|
2024-05-28 19:59:41 +08:00
|
|
|
#pragma region Exception Handler Implementation
|
2024-05-23 09:37:41 +08:00
|
|
|
|
2024-06-15 16:44:11 +08:00
|
|
|
/**
|
|
|
|
* @brief Get human-readable exception string from given exception code.
|
|
|
|
* @param[in] code Exception code
|
|
|
|
* @return The const string pointer to corresponding exception explanation string.
|
|
|
|
*/
|
2024-06-28 15:46:58 +08:00
|
|
|
static const yycc_char8_t* UExceptionGetCodeName(DWORD code) {
|
2024-05-23 09:37:41 +08:00
|
|
|
switch (code) {
|
|
|
|
case EXCEPTION_ACCESS_VIOLATION:
|
2024-06-28 15:46:58 +08:00
|
|
|
return YYCC_U8("access violation");
|
2024-05-23 09:37:41 +08:00
|
|
|
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
|
2024-06-28 15:46:58 +08:00
|
|
|
return YYCC_U8("array index out of bound");
|
2024-05-23 09:37:41 +08:00
|
|
|
case EXCEPTION_BREAKPOINT:
|
2024-06-28 15:46:58 +08:00
|
|
|
return YYCC_U8("breakpoint reached");
|
2024-05-23 09:37:41 +08:00
|
|
|
case EXCEPTION_DATATYPE_MISALIGNMENT:
|
2024-06-28 15:46:58 +08:00
|
|
|
return YYCC_U8("misaligned data access");
|
2024-05-23 09:37:41 +08:00
|
|
|
case EXCEPTION_FLT_DENORMAL_OPERAND:
|
2024-06-28 15:46:58 +08:00
|
|
|
return YYCC_U8("operand had denormal value");
|
2024-05-23 09:37:41 +08:00
|
|
|
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
2024-06-28 15:46:58 +08:00
|
|
|
return YYCC_U8("floating-point division by zero");
|
2024-05-23 09:37:41 +08:00
|
|
|
case EXCEPTION_FLT_INEXACT_RESULT:
|
2024-06-28 15:46:58 +08:00
|
|
|
return YYCC_U8("no decimal fraction representation for value");
|
2024-05-23 09:37:41 +08:00
|
|
|
case EXCEPTION_FLT_INVALID_OPERATION:
|
2024-06-28 15:46:58 +08:00
|
|
|
return YYCC_U8("invalid floating-point operation");
|
2024-05-23 09:37:41 +08:00
|
|
|
case EXCEPTION_FLT_OVERFLOW:
|
2024-06-28 15:46:58 +08:00
|
|
|
return YYCC_U8("floating-point overflow");
|
2024-05-23 09:37:41 +08:00
|
|
|
case EXCEPTION_FLT_STACK_CHECK:
|
2024-06-28 15:46:58 +08:00
|
|
|
return YYCC_U8("floating-point stack corruption");
|
2024-05-23 09:37:41 +08:00
|
|
|
case EXCEPTION_FLT_UNDERFLOW:
|
2024-06-28 15:46:58 +08:00
|
|
|
return YYCC_U8("floating-point underflow");
|
2024-05-23 09:37:41 +08:00
|
|
|
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
2024-06-28 15:46:58 +08:00
|
|
|
return YYCC_U8("illegal instruction");
|
2024-05-23 09:37:41 +08:00
|
|
|
case EXCEPTION_IN_PAGE_ERROR:
|
2024-06-28 15:46:58 +08:00
|
|
|
return YYCC_U8("inaccessible page");
|
2024-05-23 09:37:41 +08:00
|
|
|
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
2024-06-28 15:46:58 +08:00
|
|
|
return YYCC_U8("integer division by zero");
|
2024-05-23 09:37:41 +08:00
|
|
|
case EXCEPTION_INT_OVERFLOW:
|
2024-06-28 15:46:58 +08:00
|
|
|
return YYCC_U8("integer overflow");
|
2024-05-23 09:37:41 +08:00
|
|
|
case EXCEPTION_INVALID_DISPOSITION:
|
2024-06-28 15:46:58 +08:00
|
|
|
return YYCC_U8("documentation says this should never happen");
|
2024-05-23 09:37:41 +08:00
|
|
|
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
|
2024-06-28 15:46:58 +08:00
|
|
|
return YYCC_U8("can't continue after a noncontinuable exception");
|
2024-05-23 09:37:41 +08:00
|
|
|
case EXCEPTION_PRIV_INSTRUCTION:
|
2024-06-28 15:46:58 +08:00
|
|
|
return YYCC_U8("attempted to execute a privileged instruction");
|
2024-05-23 09:37:41 +08:00
|
|
|
case EXCEPTION_SINGLE_STEP:
|
2024-06-28 15:46:58 +08:00
|
|
|
return YYCC_U8("one instruction has been executed");
|
2024-05-23 09:37:41 +08:00
|
|
|
case EXCEPTION_STACK_OVERFLOW:
|
2024-06-28 15:46:58 +08:00
|
|
|
return YYCC_U8("stack overflow");
|
2024-05-23 09:37:41 +08:00
|
|
|
default:
|
2024-06-28 15:46:58 +08:00
|
|
|
return YYCC_U8("unknown exception");
|
2024-05-23 09:37:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-15 16:44:11 +08:00
|
|
|
/**
|
2024-06-17 15:10:45 +08:00
|
|
|
* @brief Error log (including backtrace) used output function with format feature
|
2024-06-15 16:44:11 +08:00
|
|
|
* @details
|
|
|
|
* This function will format message first.
|
|
|
|
* And write them into given file stream and stderr.
|
2024-06-17 15:10:45 +08:00
|
|
|
* @param[in] fs
|
2024-06-15 16:44:11 +08:00
|
|
|
* The file stream where we write.
|
|
|
|
* If it is nullptr, function will skip writing for file stream.
|
|
|
|
* @param[in] fmt The format string.
|
|
|
|
* @param[in] ... The argument to be formatted.
|
|
|
|
*/
|
2024-06-28 15:46:58 +08:00
|
|
|
static void UExceptionErrLogFormatLine(std::FILE* fs, const yycc_char8_t* fmt, ...) {
|
2024-05-23 09:37:41 +08:00
|
|
|
// write to file
|
2024-06-15 16:44:11 +08:00
|
|
|
if (fs != nullptr) {
|
|
|
|
va_list arg1;
|
|
|
|
va_start(arg1, fmt);
|
2024-07-05 10:36:24 +08:00
|
|
|
std::vfprintf(fs, EncodingHelper::ToOrdinary(fmt), arg1);
|
2024-06-15 16:44:11 +08:00
|
|
|
std::fputs("\n", fs);
|
|
|
|
va_end(arg1);
|
|
|
|
}
|
|
|
|
// write to stderr
|
2024-05-23 09:37:41 +08:00
|
|
|
va_list arg2;
|
|
|
|
va_start(arg2, fmt);
|
2024-06-15 16:44:11 +08:00
|
|
|
ConsoleHelper::ErrWriteLine(YYCC::StringHelper::VPrintf(fmt, arg2).c_str());
|
2024-05-23 09:37:41 +08:00
|
|
|
va_end(arg2);
|
|
|
|
}
|
|
|
|
|
2024-06-15 16:44:11 +08:00
|
|
|
/**
|
2024-06-17 15:10:45 +08:00
|
|
|
* @brief Error log (including backtrace) used output function
|
2024-06-15 16:44:11 +08:00
|
|
|
* @details
|
|
|
|
* This function will write given string into given file stream and stderr.
|
2024-06-17 15:10:45 +08:00
|
|
|
* @param[in] fs
|
2024-06-15 16:44:11 +08:00
|
|
|
* The file stream where we write.
|
|
|
|
* If it is nullptr, function will skip writing for file stream.
|
|
|
|
* @param[in] strl The string to be written.
|
|
|
|
*/
|
2024-06-28 15:46:58 +08:00
|
|
|
static void UExceptionErrLogWriteLine(std::FILE* fs, const yycc_char8_t* strl) {
|
2024-05-23 09:37:41 +08:00
|
|
|
// write to file
|
2024-06-15 16:44:11 +08:00
|
|
|
if (fs != nullptr) {
|
2024-07-05 10:36:24 +08:00
|
|
|
std::fputs(EncodingHelper::ToOrdinary(strl), fs);
|
2024-06-15 16:44:11 +08:00
|
|
|
std::fputs("\n", fs);
|
|
|
|
}
|
|
|
|
// write to stderr
|
|
|
|
ConsoleHelper::ErrWriteLine(strl);
|
2024-05-23 09:37:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void UExceptionBacktrace(FILE* fs, LPCONTEXT context, int maxdepth) {
|
|
|
|
// setup loading symbol options
|
|
|
|
SymSetOptions(SymGetOptions() | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES); // lazy load symbol, and load line number.
|
|
|
|
|
|
|
|
// setup handle
|
|
|
|
HANDLE process = GetCurrentProcess();
|
|
|
|
HANDLE thread = GetCurrentThread();
|
|
|
|
|
|
|
|
// init symbol
|
|
|
|
if (!SymInitialize(process, 0, TRUE)) {
|
2024-06-15 16:44:11 +08:00
|
|
|
// fail to init. return
|
2024-06-28 15:46:58 +08:00
|
|
|
UExceptionErrLogWriteLine(fs, YYCC_U8("Fail to initialize symbol handle for process!"));
|
2024-05-23 09:37:41 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ========== CORE DUMP ==========
|
|
|
|
// prepare frame. setup correct fields
|
|
|
|
// references:
|
|
|
|
// https://github.com/rust-lang/backtrace-rs/blob/9ed25b581cfd2ee60e5a3b9054fd023bf6dced90/src/backtrace/dbghelp.rs
|
|
|
|
// https://sourceforge.net/p/predef/wiki/Architectures/
|
|
|
|
DWORD machine_type = 0;
|
|
|
|
STACKFRAME64 frame;
|
|
|
|
memset(&frame, 0, sizeof(frame));
|
|
|
|
#if defined(_M_IX86) || defined(__i386__)
|
|
|
|
// x86
|
|
|
|
machine_type = IMAGE_FILE_MACHINE_I386;
|
|
|
|
frame.AddrPC.Offset = context->Eip;
|
|
|
|
frame.AddrStack.Offset = context->Esp;
|
|
|
|
frame.AddrFrame.Offset = context->Ebp;
|
|
|
|
#elif defined(_M_AMD64) || defined(__amd64__)
|
|
|
|
// amd64
|
|
|
|
machine_type = IMAGE_FILE_MACHINE_AMD64;
|
|
|
|
frame.AddrPC.Offset = context->Rip;
|
|
|
|
frame.AddrStack.Offset = context->Rsp;
|
|
|
|
frame.AddrFrame.Offset = context->Rbp;
|
|
|
|
#elif defined(_M_ARM) || defined(__arm__)
|
|
|
|
// arm (32bit)
|
|
|
|
machine_type = IMAGE_FILE_MACHINE_ARMNT;
|
|
|
|
frame.AddrPC.Offset = context->Pc;
|
|
|
|
frame.AddrStack.Offset = context->Sp;
|
|
|
|
frame.AddrFrame.Offset = context->R11;
|
|
|
|
#elif defined(_M_ARM64) || defined(__aarch64__)
|
|
|
|
// arm64
|
|
|
|
machine_type = IMAGE_FILE_MACHINE_ARM64;
|
|
|
|
frame.AddrPC.Offset = context->Pc;
|
|
|
|
frame.AddrStack.Offset = context->Sp;
|
|
|
|
frame.AddrFrame.Offset = context->DUMMYUNIONNAME.DUMMYSTRUCTNAME.Fp;
|
|
|
|
#else
|
|
|
|
#error "Unsupported platform"
|
|
|
|
//IA-64 anybody?
|
|
|
|
|
|
|
|
#endif
|
|
|
|
frame.AddrPC.Mode = AddrModeFlat;
|
|
|
|
frame.AddrStack.Mode = AddrModeFlat;
|
|
|
|
frame.AddrFrame.Mode = AddrModeFlat;
|
|
|
|
|
|
|
|
// stack walker
|
|
|
|
while (StackWalk64(machine_type, process, thread, &frame, context,
|
|
|
|
0, SymFunctionTableAccess64, SymGetModuleBase64, 0)) {
|
|
|
|
|
|
|
|
// depth breaker
|
|
|
|
--maxdepth;
|
|
|
|
if (maxdepth < 0) {
|
2024-06-28 15:46:58 +08:00
|
|
|
UExceptionErrLogWriteLine(fs, YYCC_U8("...")); // indicate there are some frames not listed
|
2024-05-23 09:37:41 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// get module name
|
2024-06-28 15:46:58 +08:00
|
|
|
const yycc_char8_t* module_name = YYCC_U8("<unknown module>");
|
|
|
|
yycc_u8string module_name_raw;
|
2024-06-15 16:44:11 +08:00
|
|
|
DWORD64 module_base;
|
|
|
|
if (module_base = SymGetModuleBase64(process, frame.AddrPC.Offset)) {
|
2024-06-15 16:59:54 +08:00
|
|
|
if (WinFctHelper::GetModuleFileName((HINSTANCE)module_base, module_name_raw)) {
|
2024-06-15 16:44:11 +08:00
|
|
|
module_name = module_name_raw.c_str();
|
|
|
|
}
|
2024-05-23 09:37:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// get source file and line
|
2024-06-28 15:46:58 +08:00
|
|
|
const yycc_char8_t* source_file = YYCC_U8("<unknown source>");
|
2024-05-23 09:37:41 +08:00
|
|
|
DWORD64 source_file_line = 0;
|
|
|
|
DWORD dwDisplacement;
|
|
|
|
IMAGEHLP_LINE64 winline;
|
|
|
|
winline.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
|
|
|
if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &dwDisplacement, &winline)) {
|
2024-06-28 15:46:58 +08:00
|
|
|
source_file = EncodingHelper::ToUTF8(winline.FileName); // TODO: check whether there is UNICODE file name.
|
2024-05-23 09:37:41 +08:00
|
|
|
source_file_line = winline.LineNumber;
|
|
|
|
}
|
|
|
|
|
|
|
|
// write to file
|
2024-06-28 15:46:58 +08:00
|
|
|
UExceptionErrLogFormatLine(fs, YYCC_U8("0x%" PRI_XPTR_LEFT_PADDING PRIXPTR "[%s+0x%" PRI_XPTR_LEFT_PADDING PRIXPTR "]\t%s#L%" PRIu64),
|
2024-06-15 16:44:11 +08:00
|
|
|
frame.AddrPC.Offset, // memory adress
|
|
|
|
module_name, frame.AddrPC.Offset - module_base, // module name + relative address
|
|
|
|
source_file, source_file_line // source file + source line
|
2024-05-23 09:37:41 +08:00
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// ========== END CORE DUMP ==========
|
|
|
|
|
|
|
|
// free symbol
|
|
|
|
SymCleanup(process);
|
|
|
|
}
|
|
|
|
|
2024-06-28 15:46:58 +08:00
|
|
|
static void UExceptionErrorLog(const yycc_u8string& u8_filename, LPEXCEPTION_POINTERS info) {
|
2024-06-17 15:10:45 +08:00
|
|
|
// open file stream if we have file name
|
|
|
|
std::FILE* fs = nullptr;
|
|
|
|
if (!u8_filename.empty()) {
|
2024-06-28 15:46:58 +08:00
|
|
|
fs = IOHelper::UTF8FOpen(u8_filename.c_str(), YYCC_U8("wb"));
|
2024-06-17 15:10:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// record exception type first
|
|
|
|
PEXCEPTION_RECORD rec = info->ExceptionRecord;
|
2024-06-28 15:46:58 +08:00
|
|
|
UExceptionErrLogFormatLine(fs, YYCC_U8("Unhandled exception occured at 0x%" PRI_XPTR_LEFT_PADDING PRIXPTR ": %s (%" PRIu32 ")."),
|
2024-06-17 15:10:45 +08:00
|
|
|
rec->ExceptionAddress,
|
|
|
|
UExceptionGetCodeName(rec->ExceptionCode),
|
|
|
|
rec->ExceptionCode
|
|
|
|
);
|
|
|
|
|
|
|
|
// special proc for 2 exceptions
|
|
|
|
if (rec->ExceptionCode == EXCEPTION_ACCESS_VIOLATION || rec->ExceptionCode == EXCEPTION_IN_PAGE_ERROR) {
|
|
|
|
if (rec->NumberParameters >= 2) {
|
2024-06-28 16:30:21 +08:00
|
|
|
const yycc_char8_t* op =
|
|
|
|
rec->ExceptionInformation[0] == 0 ? YYCC_U8("read") :
|
|
|
|
rec->ExceptionInformation[0] == 1 ? YYCC_U8("written") : YYCC_U8("executed");
|
2024-06-28 15:46:58 +08:00
|
|
|
UExceptionErrLogFormatLine(fs, YYCC_U8("The data at memory address 0x%" PRI_XPTR_LEFT_PADDING PRIxPTR " could not be %s."),
|
2024-06-17 15:10:45 +08:00
|
|
|
rec->ExceptionInformation[1], op);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// output stacktrace
|
|
|
|
UExceptionBacktrace(fs, info->ContextRecord, 1024);
|
2024-06-13 15:24:12 +08:00
|
|
|
|
2024-06-17 15:10:45 +08:00
|
|
|
// close file if necessary
|
|
|
|
if (fs != nullptr) {
|
|
|
|
std::fclose(fs);
|
|
|
|
}
|
2024-06-13 15:24:12 +08:00
|
|
|
}
|
|
|
|
|
2024-06-28 15:46:58 +08:00
|
|
|
static void UExceptionCoreDump(const yycc_u8string& u8_filename, LPEXCEPTION_POINTERS info) {
|
2024-06-15 16:44:11 +08:00
|
|
|
// convert file encoding
|
|
|
|
std::wstring filename;
|
2024-06-17 15:10:45 +08:00
|
|
|
if (u8_filename.empty())
|
2024-06-15 16:44:11 +08:00
|
|
|
return; // if no given file name, return
|
2024-06-28 15:46:58 +08:00
|
|
|
if (!YYCC::EncodingHelper::UTF8ToWchar(u8_filename, filename))
|
2024-06-15 16:44:11 +08:00
|
|
|
return; // if convertion failed, return
|
|
|
|
|
2024-05-23 09:37:41 +08:00
|
|
|
// open file and write
|
2024-06-15 16:44:11 +08:00
|
|
|
HANDLE hFile = CreateFileW(filename.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
2024-05-23 09:37:41 +08:00
|
|
|
if (hFile != INVALID_HANDLE_VALUE) {
|
|
|
|
MINIDUMP_EXCEPTION_INFORMATION exception_info;
|
|
|
|
exception_info.ThreadId = GetCurrentThreadId();
|
|
|
|
exception_info.ExceptionPointers = info;
|
|
|
|
exception_info.ClientPointers = TRUE;
|
|
|
|
MiniDumpWriteDump(
|
|
|
|
GetCurrentProcess(), GetCurrentProcessId(), hFile,
|
|
|
|
MiniDumpNormal,
|
|
|
|
&exception_info,
|
|
|
|
NULL, NULL
|
|
|
|
);
|
|
|
|
CloseHandle(hFile);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-28 15:46:58 +08:00
|
|
|
static bool UExceptionFetchRecordPath(yycc_u8string& log_path, yycc_u8string& coredump_path) {
|
2024-06-17 15:10:45 +08:00
|
|
|
// build two file names like: "module.dll.1234.log" and "module.dll.1234.dmp".
|
|
|
|
// "module.dll" is the name of current module. "1234" is current process id.
|
|
|
|
// get self module name
|
2024-06-28 15:46:58 +08:00
|
|
|
yycc_u8string u8_self_module_name;
|
2024-06-17 15:10:45 +08:00
|
|
|
{
|
|
|
|
// get module handle
|
|
|
|
HMODULE hSelfModule = YYCC::WinFctHelper::GetCurrentModule();
|
|
|
|
if (hSelfModule == nullptr)
|
|
|
|
return false;
|
|
|
|
// get full path of self module
|
2024-06-28 15:46:58 +08:00
|
|
|
yycc_u8string u8_self_module_path;
|
2024-06-17 15:10:45 +08:00
|
|
|
if (!YYCC::WinFctHelper::GetModuleFileName(hSelfModule, u8_self_module_path))
|
|
|
|
return false;
|
|
|
|
// extract file name from full path by std::filesystem::path
|
|
|
|
std::filesystem::path self_module_path(FsPathPatch::FromUTF8Path(u8_self_module_path.c_str()));
|
|
|
|
u8_self_module_name = FsPathPatch::ToUTF8Path(self_module_path.filename());
|
2024-06-15 16:44:11 +08:00
|
|
|
}
|
2024-06-17 15:10:45 +08:00
|
|
|
// then get process id
|
|
|
|
DWORD process_id = GetCurrentProcessId();
|
|
|
|
// conbine them as a file name prefix
|
2024-06-28 15:46:58 +08:00
|
|
|
yycc_u8string u8_filename_prefix;
|
|
|
|
if (!YYCC::StringHelper::Printf(u8_filename_prefix, YYCC_U8("%s.%" PRIu32), u8_self_module_name.c_str(), process_id))
|
2024-06-17 15:10:45 +08:00
|
|
|
return false;
|
|
|
|
// then get file name for log and minidump
|
2024-06-28 15:46:58 +08:00
|
|
|
yycc_u8string u8_log_filename = u8_filename_prefix + YYCC_U8(".log");
|
|
|
|
yycc_u8string u8_coredump_filename = u8_filename_prefix + YYCC_U8(".dmp");
|
2024-06-17 15:10:45 +08:00
|
|
|
|
|
|
|
// fetch crash report path
|
|
|
|
// get local appdata folder
|
2024-06-28 15:46:58 +08:00
|
|
|
yycc_u8string u8_localappdata_path;
|
2024-06-17 15:10:45 +08:00
|
|
|
if (!WinFctHelper::GetLocalAppData(u8_localappdata_path))
|
|
|
|
return false;
|
|
|
|
// convert to std::filesystem::path
|
|
|
|
std::filesystem::path crash_report_path(FsPathPatch::FromUTF8Path(u8_localappdata_path.c_str()));
|
|
|
|
// slash into crash report folder
|
2024-06-28 15:46:58 +08:00
|
|
|
crash_report_path /= FsPathPatch::FromUTF8Path(YYCC_U8("CrashDumps"));
|
2024-06-17 15:10:45 +08:00
|
|
|
// use create function to make sure it is existing
|
|
|
|
std::filesystem::create_directories(crash_report_path);
|
|
|
|
|
|
|
|
// build log path and coredump path
|
|
|
|
// build std::filesystem::path first
|
|
|
|
std::filesystem::path log_filepath = crash_report_path / FsPathPatch::FromUTF8Path(u8_log_filename.c_str());
|
|
|
|
std::filesystem::path coredump_filepath = crash_report_path / FsPathPatch::FromUTF8Path(u8_coredump_filename.c_str());
|
|
|
|
// output to result
|
|
|
|
log_path = FsPathPatch::ToUTF8Path(log_filepath);
|
|
|
|
coredump_path = FsPathPatch::ToUTF8Path(coredump_filepath);
|
|
|
|
|
|
|
|
return true;
|
2024-06-13 15:24:12 +08:00
|
|
|
}
|
|
|
|
|
2024-05-23 09:37:41 +08:00
|
|
|
static LONG WINAPI UExceptionImpl(LPEXCEPTION_POINTERS info) {
|
|
|
|
// detect loop calling
|
2024-06-13 15:24:12 +08:00
|
|
|
if (g_IsProcessing) goto end_proc;
|
2024-05-23 09:37:41 +08:00
|
|
|
// start process
|
|
|
|
g_IsProcessing = true;
|
|
|
|
|
2024-06-17 15:10:45 +08:00
|
|
|
// core implementation
|
2024-05-23 09:37:41 +08:00
|
|
|
{
|
2024-06-17 15:10:45 +08:00
|
|
|
// fetch error report path first
|
2024-06-28 15:46:58 +08:00
|
|
|
yycc_u8string log_path, coredump_path;
|
2024-06-17 15:10:45 +08:00
|
|
|
if (!UExceptionFetchRecordPath(log_path, coredump_path)) {
|
|
|
|
// fail to fetch path, clear them.
|
|
|
|
// we still can handle crash without them
|
|
|
|
log_path.clear();
|
|
|
|
coredump_path.clear();
|
|
|
|
// and tell user we can not output file
|
2024-06-28 15:46:58 +08:00
|
|
|
ConsoleHelper::ErrWriteLine(YYCC_U8("Crash occurs, but we can not create crash log and coredump!"));
|
2024-06-17 15:10:45 +08:00
|
|
|
} else {
|
|
|
|
// okey. output file path to tell user the path where you can find.
|
2024-06-28 15:46:58 +08:00
|
|
|
ConsoleHelper::ErrFormatLine(YYCC_U8("Crash Log: %s"), log_path.c_str());
|
|
|
|
ConsoleHelper::ErrFormatLine(YYCC_U8("Crash Coredump: %s"), coredump_path.c_str());
|
2024-05-23 09:37:41 +08:00
|
|
|
}
|
|
|
|
|
2024-06-17 15:10:45 +08:00
|
|
|
// write crash log
|
|
|
|
UExceptionErrorLog(log_path, info);
|
|
|
|
// write crash coredump
|
|
|
|
UExceptionCoreDump(coredump_path, info);
|
2024-05-23 09:37:41 +08:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// end process
|
|
|
|
failed:
|
|
|
|
g_IsProcessing = false;
|
|
|
|
// if backup proc can be run, run it
|
|
|
|
// otherwise directly return.
|
|
|
|
end_proc:
|
|
|
|
if (g_ProcBackup != nullptr) {
|
|
|
|
return g_ProcBackup(info);
|
|
|
|
} else {
|
|
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#pragma endregion
|
|
|
|
|
|
|
|
void Register() {
|
|
|
|
if (g_IsRegistered) return;
|
|
|
|
g_ProcBackup = SetUnhandledExceptionFilter(UExceptionImpl);
|
|
|
|
g_IsRegistered = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Unregister() {
|
|
|
|
if (!g_IsRegistered) return;
|
|
|
|
SetUnhandledExceptionFilter(g_ProcBackup);
|
|
|
|
g_IsRegistered = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|