mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 10:17:22 +01:00
veh clean up and more log info
This commit is contained in:
parent
803b61a5a9
commit
ff5dc1fa04
3 changed files with 184 additions and 96 deletions
|
|
@ -6,60 +6,118 @@
|
|||
#include <DbgHelp.h>
|
||||
#include <format>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
std::vector g_exception_whitelist({
|
||||
STATUS_ACCESS_VIOLATION,
|
||||
STATUS_IN_PAGE_ERROR,
|
||||
STATUS_INVALID_HANDLE,
|
||||
STATUS_INVALID_PARAMETER,
|
||||
STATUS_NO_MEMORY,
|
||||
STATUS_ILLEGAL_INSTRUCTION,
|
||||
STATUS_NONCONTINUABLE_EXCEPTION,
|
||||
STATUS_INVALID_DISPOSITION,
|
||||
STATUS_ARRAY_BOUNDS_EXCEEDED,
|
||||
STATUS_FLOAT_DENORMAL_OPERAND,
|
||||
STATUS_FLOAT_DIVIDE_BY_ZERO,
|
||||
STATUS_FLOAT_INEXACT_RESULT,
|
||||
STATUS_FLOAT_INVALID_OPERATION,
|
||||
STATUS_FLOAT_OVERFLOW,
|
||||
STATUS_FLOAT_STACK_CHECK,
|
||||
STATUS_FLOAT_UNDERFLOW,
|
||||
STATUS_INTEGER_DIVIDE_BY_ZERO,
|
||||
STATUS_INTEGER_OVERFLOW,
|
||||
STATUS_PRIVILEGED_INSTRUCTION,
|
||||
STATUS_STACK_OVERFLOW,
|
||||
STATUS_DLL_NOT_FOUND,
|
||||
STATUS_ORDINAL_NOT_FOUND,
|
||||
STATUS_ENTRYPOINT_NOT_FOUND,
|
||||
STATUS_DLL_INIT_FAILED,
|
||||
STATUS_CONTROL_STACK_VIOLATION,
|
||||
STATUS_FLOAT_MULTIPLE_FAULTS,
|
||||
STATUS_FLOAT_MULTIPLE_TRAPS,
|
||||
STATUS_HEAP_CORRUPTION,
|
||||
STATUS_STACK_BUFFER_OVERRUN,
|
||||
STATUS_INVALID_CRUNTIME_PARAMETER,
|
||||
STATUS_THREAD_NOT_RUNNING,
|
||||
STATUS_ALREADY_REGISTERED
|
||||
});
|
||||
bool is_whitelist_exception(const DWORD code)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case STATUS_ACCESS_VIOLATION:
|
||||
case STATUS_IN_PAGE_ERROR:
|
||||
case STATUS_INVALID_HANDLE:
|
||||
case STATUS_INVALID_PARAMETER:
|
||||
case STATUS_NO_MEMORY:
|
||||
case STATUS_ILLEGAL_INSTRUCTION:
|
||||
case STATUS_NONCONTINUABLE_EXCEPTION:
|
||||
case STATUS_INVALID_DISPOSITION:
|
||||
case STATUS_ARRAY_BOUNDS_EXCEEDED:
|
||||
case STATUS_FLOAT_DENORMAL_OPERAND:
|
||||
case STATUS_FLOAT_DIVIDE_BY_ZERO:
|
||||
case STATUS_FLOAT_INEXACT_RESULT:
|
||||
case STATUS_FLOAT_INVALID_OPERATION:
|
||||
case STATUS_FLOAT_OVERFLOW:
|
||||
case STATUS_FLOAT_STACK_CHECK:
|
||||
case STATUS_FLOAT_UNDERFLOW:
|
||||
case STATUS_INTEGER_DIVIDE_BY_ZERO:
|
||||
case STATUS_INTEGER_OVERFLOW:
|
||||
case STATUS_PRIVILEGED_INSTRUCTION:
|
||||
case STATUS_STACK_OVERFLOW:
|
||||
case STATUS_DLL_NOT_FOUND:
|
||||
case STATUS_ORDINAL_NOT_FOUND:
|
||||
case STATUS_ENTRYPOINT_NOT_FOUND:
|
||||
case STATUS_DLL_INIT_FAILED:
|
||||
case STATUS_CONTROL_STACK_VIOLATION:
|
||||
case STATUS_FLOAT_MULTIPLE_FAULTS:
|
||||
case STATUS_FLOAT_MULTIPLE_TRAPS:
|
||||
case STATUS_HEAP_CORRUPTION:
|
||||
case STATUS_STACK_BUFFER_OVERRUN:
|
||||
case STATUS_INVALID_CRUNTIME_PARAMETER:
|
||||
case STATUS_THREAD_NOT_RUNNING:
|
||||
case STATUS_ALREADY_REGISTERED:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool GetModuleFileAndBase(DWORD64 address, PDWORD64 moduleBase, std::filesystem::path& moduleFile)
|
||||
|
||||
bool get_module_file_and_base(const DWORD64 address, DWORD64* module_base, std::filesystem::path& module_file)
|
||||
{
|
||||
HMODULE handle;
|
||||
if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, reinterpret_cast<LPCSTR>(address), &handle))
|
||||
{
|
||||
if (wchar_t path[1024]; GetModuleFileNameW(handle, path, sizeof path / 2) > 0)
|
||||
{
|
||||
*moduleBase = reinterpret_cast<DWORD64>(handle);
|
||||
moduleFile = path;
|
||||
*module_base = reinterpret_cast<DWORD64>(handle);
|
||||
module_file = path;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetCallStack(PEXCEPTION_POINTERS ex, std::vector<DWORD64>& addressList, DWORD frames = 10)
|
||||
|
||||
bool is_ffxiv_address(const DWORD64 address)
|
||||
{
|
||||
DWORD64 module_base;
|
||||
if (std::filesystem::path module_path; get_module_file_and_base(address, &module_base, module_path))
|
||||
return _wcsicmp(module_path.filename().c_str(), L"ffxiv_dx11.exe") == 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool get_sym_from_addr(const DWORD64 address, DWORD64* displacement, std::wstring& symbol_name)
|
||||
{
|
||||
char buffer[sizeof(SYMBOL_INFOW) + MAX_SYM_NAME * sizeof(wchar_t)];
|
||||
auto symbol = reinterpret_cast<PSYMBOL_INFOW>(buffer);
|
||||
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
symbol->MaxNameLen = MAX_SYM_NAME;
|
||||
|
||||
if (SymFromAddrW(GetCurrentProcess(), address, displacement, symbol))
|
||||
{
|
||||
symbol_name.assign(_wcsdup(symbol->Name));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
std::wstring to_address_string(const DWORD64 address, const bool try_ptrderef = true)
|
||||
{
|
||||
DWORD64 module_base;
|
||||
std::filesystem::path module_path;
|
||||
bool is_mod_addr = get_module_file_and_base(address, &module_base, module_path);
|
||||
|
||||
DWORD64 value = 0;
|
||||
if(try_ptrderef && address > 0x10000 && address < 0x7FFFFFFE0000)
|
||||
{
|
||||
ReadProcessMemory(GetCurrentProcess(), reinterpret_cast<void*>(address), &value, sizeof value, nullptr);
|
||||
}
|
||||
|
||||
std::wstring addr_str = is_mod_addr ?
|
||||
std::format(L"{}+{:X}", module_path.filename().c_str(), address - module_base) :
|
||||
std::format(L"{:X}", address);
|
||||
|
||||
DWORD64 displacement;
|
||||
if (std::wstring symbol; get_sym_from_addr(address, &displacement, symbol))
|
||||
return std::format(L"{}\t({})", addr_str, displacement != 0 ? std::format(L"{}+0x{:X}", symbol, displacement) : std::format(L"{}", symbol));
|
||||
return value != 0 ? std::format(L"{} [{}]", addr_str, to_address_string(value, false)) : addr_str;
|
||||
}
|
||||
|
||||
|
||||
void print_exception_info(const EXCEPTION_POINTERS* ex, std::wofstream& log)
|
||||
{
|
||||
log << L"\nCall Stack\n{";
|
||||
|
||||
STACKFRAME64 sf;
|
||||
sf.AddrPC.Offset = ex->ContextRecord->Rip;
|
||||
sf.AddrPC.Mode = AddrModeFlat;
|
||||
|
|
@ -67,31 +125,77 @@ bool GetCallStack(PEXCEPTION_POINTERS ex, std::vector<DWORD64>& addressList, DWO
|
|||
sf.AddrStack.Mode = AddrModeFlat;
|
||||
sf.AddrFrame.Offset = ex->ContextRecord->Rbp;
|
||||
sf.AddrFrame.Mode = AddrModeFlat;
|
||||
CONTEXT ctx = *ex->ContextRecord;
|
||||
addressList.clear();
|
||||
CONTEXT ctx = *ex->ContextRecord;
|
||||
int frame_index = 0;
|
||||
|
||||
log << std::format(L"\n [{}]\t{}", frame_index++, to_address_string(sf.AddrPC.Offset, false));
|
||||
|
||||
do
|
||||
{
|
||||
if (!StackWalk64(IMAGE_FILE_MACHINE_AMD64, GetCurrentProcess(), GetCurrentThread(), &sf, &ctx, nullptr, nullptr, nullptr, nullptr))
|
||||
return false;
|
||||
addressList.push_back(sf.AddrPC.Offset);
|
||||
} while (sf.AddrReturn.Offset != 0 && --frames);
|
||||
return true;
|
||||
if (!StackWalk64(IMAGE_FILE_MACHINE_AMD64, GetCurrentProcess(), GetCurrentThread(), &sf, &ctx, nullptr, SymFunctionTableAccess64, SymGetModuleBase64, nullptr))
|
||||
break;
|
||||
|
||||
log << std::format(L"\n [{}]\t{}", frame_index++, to_address_string(sf.AddrPC.Offset, false));
|
||||
|
||||
} while (sf.AddrReturn.Offset != 0 && sf.AddrPC.Offset != sf.AddrReturn.Offset);
|
||||
|
||||
log << "\n}" << std::endl;
|
||||
|
||||
ctx = *ex->ContextRecord;
|
||||
|
||||
log << "\nRegisters\n{";
|
||||
|
||||
log << std::format(L"\n RAX:\t{}", to_address_string(ctx.Rax));
|
||||
log << std::format(L"\n RBX:\t{}", to_address_string(ctx.Rbx));
|
||||
log << std::format(L"\n RCX:\t{}", to_address_string(ctx.Rcx));
|
||||
log << std::format(L"\n RDX:\t{}", to_address_string(ctx.Rdx));
|
||||
log << std::format(L"\n R8:\t{}", to_address_string(ctx.R8));
|
||||
log << std::format(L"\n R9:\t{}", to_address_string(ctx.R9));
|
||||
log << std::format(L"\n R10:\t{}", to_address_string(ctx.R10));
|
||||
log << std::format(L"\n R11:\t{}", to_address_string(ctx.R11));
|
||||
log << std::format(L"\n R12:\t{}", to_address_string(ctx.R12));
|
||||
log << std::format(L"\n R13:\t{}", to_address_string(ctx.R13));
|
||||
log << std::format(L"\n R14:\t{}", to_address_string(ctx.R14));
|
||||
log << std::format(L"\n R15:\t{}", to_address_string(ctx.R15));
|
||||
|
||||
log << std::format(L"\n RSI:\t{}", to_address_string(ctx.Rsi));
|
||||
log << std::format(L"\n RDI:\t{}", to_address_string(ctx.Rdi));
|
||||
log << std::format(L"\n RBP:\t{}", to_address_string(ctx.Rbp));
|
||||
log << std::format(L"\n RSP:\t{}", to_address_string(ctx.Rsp));
|
||||
log << std::format(L"\n RIP:\t{}", to_address_string(ctx.Rip));
|
||||
|
||||
log << "\n}" << std::endl;
|
||||
|
||||
if(ctx.Rsp <= 0x10000 || ctx.Rsp >= 0x7FFFFFFE0000)
|
||||
return;
|
||||
|
||||
log << "\nStack\n{";
|
||||
|
||||
for(DWORD64 i = 0; i < 16; i++)
|
||||
log << std::format(L"\n [RSP+{:X}]\t{}", i * 8, to_address_string(*reinterpret_cast<DWORD64*>(ctx.Rsp + i * 8ull)));
|
||||
|
||||
log << "\n}" << std::endl;
|
||||
}
|
||||
|
||||
LONG ExceptionHandler(PEXCEPTION_POINTERS ex)
|
||||
|
||||
bool g_veh_message_open;
|
||||
|
||||
LONG exception_handler(EXCEPTION_POINTERS* ex)
|
||||
{
|
||||
// return if the exception is not in the whitelist
|
||||
if (std::ranges::find(g_exception_whitelist, ex->ExceptionRecord->ExceptionCode) == g_exception_whitelist.end())
|
||||
//block any further exceptions while the message box is open
|
||||
if (g_veh_message_open)
|
||||
for (;;) Sleep(1);
|
||||
|
||||
if (!is_whitelist_exception(ex->ExceptionRecord->ExceptionCode))
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
|
||||
if (!is_ffxiv_address(ex->ContextRecord->Rip))
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
|
||||
DWORD64 module_base;
|
||||
std::filesystem::path module_path;
|
||||
|
||||
// return if the exception did not happen in ffxiv_dx11.exe
|
||||
if (!GetModuleFileAndBase(ex->ContextRecord->Rip, &module_base, module_path) || module_path.filename() != L"ffxiv_dx11.exe")
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
|
||||
GetModuleFileAndBase(reinterpret_cast<DWORD64>(&ExceptionHandler), &module_base, module_path);
|
||||
get_module_file_and_base(reinterpret_cast<DWORD64>(&exception_handler), &module_base, module_path);
|
||||
#ifndef NDEBUG
|
||||
std::wstring dmp_path = _wcsdup(module_path.replace_filename(L"dalamud_appcrashd.dmp").wstring().c_str());
|
||||
#else
|
||||
|
|
@ -100,42 +204,26 @@ LONG ExceptionHandler(PEXCEPTION_POINTERS ex)
|
|||
std::wstring log_path = _wcsdup(module_path.replace_filename(L"dalamud_appcrash.log").wstring().c_str());
|
||||
|
||||
std::wofstream log;
|
||||
log.open(log_path, std::ios::app);
|
||||
log.open(log_path, std::ios::trunc);
|
||||
|
||||
std::wstring time_stamp;
|
||||
log << std::format(L"Unhandled native exception occurred at {}", to_address_string(ex->ContextRecord->Rip, false)) << std::endl;
|
||||
log << std::format(L"Code: {:X}", ex->ExceptionRecord->ExceptionCode) << std::endl;
|
||||
log << L"Time: " << std::chrono::zoned_time{ std::chrono::current_zone(), std::chrono::system_clock::now() } << std::endl;
|
||||
|
||||
std::time_t t = std::time(nullptr);
|
||||
std::tm tm{};
|
||||
localtime_s(&tm, &t);
|
||||
|
||||
log << L"[" << std::put_time(&tm, L"%d/%m/%Y %H:%M:%S") << L"] Exception " << std::uppercase << std::hex << ex->ExceptionRecord->ExceptionCode << " at ";
|
||||
if (GetModuleFileAndBase(ex->ContextRecord->Rip, &module_base, module_path))
|
||||
log << module_path.filename() << "+" << std::uppercase << std::hex << ex->ContextRecord->Rip - module_base << std::endl;
|
||||
else log << std::uppercase << std::hex << ex->ContextRecord->Rip << std::endl;
|
||||
|
||||
if (std::vector<DWORD64> call_stack; GetCallStack(ex, call_stack, -1))
|
||||
{
|
||||
log << L"Call Stack:" << std::endl;
|
||||
for (auto& addr : call_stack)
|
||||
{
|
||||
if (GetModuleFileAndBase(addr, &module_base, module_path))
|
||||
log << L" " << module_path.filename().c_str() << "+" << std::uppercase << std::hex << addr - module_base << std::endl;
|
||||
else log << L" " << std::uppercase << std::hex << addr << std::endl;
|
||||
}
|
||||
}
|
||||
SymRefreshModuleList(GetCurrentProcess());
|
||||
print_exception_info(ex, log);
|
||||
|
||||
log.close();
|
||||
|
||||
MINIDUMP_EXCEPTION_INFORMATION ex_info;
|
||||
ex_info.ClientPointers = true;
|
||||
ex_info.ClientPointers = false;
|
||||
ex_info.ExceptionPointers = ex;
|
||||
ex_info.ThreadId = GetCurrentThreadId();
|
||||
|
||||
auto file = CreateFileW(dmp_path.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
|
||||
HANDLE file = CreateFileW(dmp_path.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), file, MiniDumpWithDataSegs, &ex_info, nullptr, nullptr);
|
||||
CloseHandle(file);
|
||||
|
||||
log << "Crash Dump: " << dmp_path << std::endl;
|
||||
|
||||
log.close();
|
||||
auto msg = L"An error within the game has occurred and Dalamud has caught it.\n\n"
|
||||
L"This could be caused by a faulty plugin.\n"
|
||||
L"Please report this issue on our Discord - more information has been recorded separately.\n\n"
|
||||
|
|
@ -147,32 +235,33 @@ LONG ExceptionHandler(PEXCEPTION_POINTERS ex)
|
|||
|
||||
auto formatted = std::format(msg, dmp_path, log_path);
|
||||
|
||||
// block any other exceptions hitting the veh while the messagebox is open
|
||||
g_veh_message_open = true;
|
||||
MessageBoxW(nullptr, formatted.c_str(), L"Dalamud Error", MB_OK | MB_ICONERROR | MB_TOPMOST);
|
||||
g_veh_message_open = false;
|
||||
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
PVOID g_hVEH; // VEH Handle to remove the handler later in DLL_PROCESS_DETACH
|
||||
|
||||
PVOID g_veh_handle;
|
||||
|
||||
bool veh::add_handler()
|
||||
{
|
||||
if (g_hVEH)
|
||||
if (g_veh_handle)
|
||||
return false;
|
||||
g_hVEH = AddVectoredExceptionHandler(0, ExceptionHandler);
|
||||
return g_hVEH != nullptr;
|
||||
g_veh_handle = AddVectoredExceptionHandler(0, exception_handler);
|
||||
// init the symbol handler, the game already does it in WinMain but just in case
|
||||
SymInitializeW(GetCurrentProcess(), nullptr, true);
|
||||
return g_veh_handle != nullptr;
|
||||
}
|
||||
|
||||
bool veh::remove_handler()
|
||||
{
|
||||
if (g_hVEH && RemoveVectoredExceptionHandler(g_hVEH) != 0)
|
||||
if (g_veh_handle && RemoveVectoredExceptionHandler(g_veh_handle) != 0)
|
||||
{
|
||||
g_hVEH = nullptr;
|
||||
g_veh_handle = nullptr;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void* veh::get_handle()
|
||||
{
|
||||
return g_hVEH;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue