feat(ExceptionHandler): Don't log repeating exceptions (#1244)
This commit is contained in:
parent
60e1c2e3c6
commit
fc52860e02
193
src/common.hpp
193
src/common.hpp
@ -1,96 +1,97 @@
|
||||
#ifndef COMMON_INC
|
||||
#define COMMON_INC
|
||||
|
||||
// clang-format off
|
||||
|
||||
#include <sdkddkver.h>
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <d3d11.h>
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include <memory>
|
||||
#include <new>
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
#include <stack>
|
||||
#include <vector>
|
||||
|
||||
#include <typeinfo>
|
||||
#include <type_traits>
|
||||
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <any>
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
|
||||
#include <format>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "logger/logger.hpp"
|
||||
|
||||
#include "core/globals.hpp"
|
||||
#include "gta/natives.hpp"
|
||||
#include "ped/CPed.hpp"
|
||||
|
||||
#include "services/notifications/notification_service.hpp"
|
||||
#include "services/translation_service/translation_service.hpp"
|
||||
|
||||
// clang-format on
|
||||
|
||||
namespace big
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
inline HMODULE g_hmodule{};
|
||||
inline HANDLE g_main_thread{};
|
||||
inline DWORD g_main_thread_id{};
|
||||
inline std::atomic_bool g_running{false};
|
||||
|
||||
inline CPed* g_local_player;
|
||||
}
|
||||
|
||||
namespace self
|
||||
{
|
||||
inline Ped ped;
|
||||
inline Player id;
|
||||
inline Vector3 pos;
|
||||
inline Vehicle veh;
|
||||
}
|
||||
|
||||
template<size_t N>
|
||||
struct template_str
|
||||
{
|
||||
constexpr template_str(const char (&str)[N])
|
||||
{
|
||||
std::copy_n(str, N, value);
|
||||
}
|
||||
|
||||
char value[N];
|
||||
};
|
||||
|
||||
#endif
|
||||
#ifndef COMMON_INC
|
||||
#define COMMON_INC
|
||||
|
||||
// clang-format off
|
||||
|
||||
#include <sdkddkver.h>
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <d3d11.h>
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include <memory>
|
||||
#include <new>
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
#include <set>
|
||||
#include <stack>
|
||||
#include <vector>
|
||||
|
||||
#include <typeinfo>
|
||||
#include <type_traits>
|
||||
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <any>
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
|
||||
#include <format>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "logger/logger.hpp"
|
||||
|
||||
#include "core/globals.hpp"
|
||||
#include "gta/natives.hpp"
|
||||
#include "ped/CPed.hpp"
|
||||
|
||||
#include "services/notifications/notification_service.hpp"
|
||||
#include "services/translation_service/translation_service.hpp"
|
||||
|
||||
// clang-format on
|
||||
|
||||
namespace big
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
inline HMODULE g_hmodule{};
|
||||
inline HANDLE g_main_thread{};
|
||||
inline DWORD g_main_thread_id{};
|
||||
inline std::atomic_bool g_running{false};
|
||||
|
||||
inline CPed* g_local_player;
|
||||
}
|
||||
|
||||
namespace self
|
||||
{
|
||||
inline Ped ped;
|
||||
inline Player id;
|
||||
inline Vector3 pos;
|
||||
inline Vehicle veh;
|
||||
}
|
||||
|
||||
template<size_t N>
|
||||
struct template_str
|
||||
{
|
||||
constexpr template_str(const char (&str)[N])
|
||||
{
|
||||
std::copy_n(str, N, value);
|
||||
}
|
||||
|
||||
char value[N];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -6,6 +6,14 @@
|
||||
|
||||
namespace big
|
||||
{
|
||||
inline auto hash_stack_trace(std::vector<uint64_t> stack_trace)
|
||||
{
|
||||
auto data = reinterpret_cast<const char*>(stack_trace.data());
|
||||
std::size_t size = stack_trace.size() * sizeof(uint64_t);
|
||||
|
||||
return std::hash<std::string_view>()({ data, size });
|
||||
}
|
||||
|
||||
exception_handler::exception_handler()
|
||||
{
|
||||
SetErrorMode(0);
|
||||
@ -14,8 +22,8 @@ namespace big
|
||||
|
||||
exception_handler::~exception_handler()
|
||||
{
|
||||
// passing NULL / 0 will make it go back to normal exception handling
|
||||
SetUnhandledExceptionFilter(0);
|
||||
// passing nullptr will make it go back to normal exception handling
|
||||
SetUnhandledExceptionFilter(nullptr);
|
||||
}
|
||||
|
||||
inline static stack_trace trace;
|
||||
@ -25,8 +33,16 @@ namespace big
|
||||
if (exception_code == EXCEPTION_BREAKPOINT || exception_code == DBG_PRINTEXCEPTION_C || exception_code == DBG_PRINTEXCEPTION_WIDE_C)
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
|
||||
static std::set<std::size_t> logged_exceptions;
|
||||
|
||||
trace.new_stack_trace(exception_info);
|
||||
LOG(FATAL) << trace;
|
||||
const auto trace_hash = hash_stack_trace(trace.frame_pointers());
|
||||
if (const auto it = logged_exceptions.find(trace_hash); it == logged_exceptions.end())
|
||||
{
|
||||
LOG(FATAL) << trace;
|
||||
|
||||
logged_exceptions.insert(trace_hash);
|
||||
}
|
||||
|
||||
ZyanU64 opcode_address = exception_info->ContextRecord->Rip;
|
||||
ZydisDisassembledInstruction instruction;
|
||||
|
@ -1,200 +1,205 @@
|
||||
#include "stack_trace.hpp"
|
||||
|
||||
#include "gta/script_thread.hpp"
|
||||
#include "memory/module.hpp"
|
||||
|
||||
#include <DbgHelp.h>
|
||||
#include <winternl.h>
|
||||
|
||||
namespace big
|
||||
{
|
||||
stack_trace::stack_trace() :
|
||||
m_frame_pointers(32)
|
||||
{
|
||||
SymInitialize(GetCurrentProcess(), nullptr, true);
|
||||
}
|
||||
|
||||
stack_trace::~stack_trace()
|
||||
{
|
||||
SymCleanup(GetCurrentProcess());
|
||||
}
|
||||
|
||||
void stack_trace::new_stack_trace(EXCEPTION_POINTERS* exception_info)
|
||||
{
|
||||
static std::mutex m;
|
||||
std::lock_guard lock(m);
|
||||
|
||||
m_exception_info = exception_info;
|
||||
|
||||
m_dump << exception_code_to_string(exception_info->ExceptionRecord->ExceptionCode) << '\n';
|
||||
|
||||
if (g.in_script_vm)
|
||||
dump_script_info();
|
||||
dump_module_info();
|
||||
dump_registers();
|
||||
dump_stacktrace();
|
||||
|
||||
m_dump << "\n--------End of exception--------\n";
|
||||
}
|
||||
|
||||
std::string stack_trace::str() const
|
||||
{
|
||||
return m_dump.str();
|
||||
}
|
||||
|
||||
// I'd prefer to make some sort of global instance that cache all modules once instead of doing this every time
|
||||
void stack_trace::dump_module_info()
|
||||
{
|
||||
// modules cached already
|
||||
if (m_modules.size())
|
||||
return;
|
||||
|
||||
m_dump << "Dumping modules:\n";
|
||||
|
||||
const auto peb = reinterpret_cast<PPEB>(NtCurrentTeb()->ProcessEnvironmentBlock);
|
||||
if (!peb)
|
||||
return;
|
||||
|
||||
const auto ldr_data = peb->Ldr;
|
||||
if (!ldr_data)
|
||||
return;
|
||||
|
||||
const auto module_list = &ldr_data->InMemoryOrderModuleList;
|
||||
auto module_entry = module_list->Flink;
|
||||
for (; module_list != module_entry; module_entry = module_entry->Flink)
|
||||
{
|
||||
const auto table_entry = CONTAINING_RECORD(module_entry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
|
||||
if (!table_entry)
|
||||
continue;
|
||||
|
||||
if (table_entry->FullDllName.Buffer)
|
||||
{
|
||||
auto mod_info = module_info(table_entry->FullDllName.Buffer, table_entry->DllBase);
|
||||
|
||||
m_dump << mod_info.m_path.filename().string() << " Base Address: " << HEX_TO_UPPER(mod_info.m_base)
|
||||
<< " Size: " << mod_info.m_size << '\n';
|
||||
|
||||
m_modules.emplace_back(std::move(mod_info));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void stack_trace::dump_registers()
|
||||
{
|
||||
const auto context = m_exception_info->ContextRecord;
|
||||
|
||||
m_dump << "Dumping registers:\n"
|
||||
<< "RAX: " << HEX_TO_UPPER(context->Rax) << '\n'
|
||||
<< "RCX: " << HEX_TO_UPPER(context->Rcx) << '\n'
|
||||
<< "RDX: " << HEX_TO_UPPER(context->Rdx) << '\n'
|
||||
<< "RBX: " << HEX_TO_UPPER(context->Rbx) << '\n'
|
||||
<< "RSI: " << HEX_TO_UPPER(context->Rsi) << '\n'
|
||||
<< "RDI: " << HEX_TO_UPPER(context->Rdi) << '\n'
|
||||
<< "RSP: " << HEX_TO_UPPER(context->Rsp) << '\n'
|
||||
<< "RBP: " << HEX_TO_UPPER(context->Rbp) << '\n'
|
||||
<< "R8: " << HEX_TO_UPPER(context->R8) << '\n'
|
||||
<< "R9: " << HEX_TO_UPPER(context->R9) << '\n'
|
||||
<< "R10: " << HEX_TO_UPPER(context->R10) << '\n'
|
||||
<< "R11: " << HEX_TO_UPPER(context->R11) << '\n'
|
||||
<< "R12: " << HEX_TO_UPPER(context->R12) << '\n'
|
||||
<< "R13: " << HEX_TO_UPPER(context->R13) << '\n'
|
||||
<< "R14: " << HEX_TO_UPPER(context->R14) << '\n'
|
||||
<< "R15: " << HEX_TO_UPPER(context->R15) << '\n';
|
||||
}
|
||||
|
||||
void stack_trace::dump_stacktrace()
|
||||
{
|
||||
m_dump << "Dumping stacktrace:";
|
||||
grab_stacktrace();
|
||||
|
||||
// alloc once
|
||||
char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME];
|
||||
auto symbol = reinterpret_cast<SYMBOL_INFO*>(buffer);
|
||||
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
symbol->MaxNameLen = MAX_SYM_NAME;
|
||||
|
||||
DWORD64 displacement64;
|
||||
DWORD displacement;
|
||||
|
||||
|
||||
IMAGEHLP_LINE64 line;
|
||||
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
||||
|
||||
for (size_t i = 0; i < m_frame_pointers.size() && m_frame_pointers[i]; ++i)
|
||||
{
|
||||
const auto addr = m_frame_pointers[i];
|
||||
|
||||
m_dump << "\n[" << i << "]\t";
|
||||
if (SymFromAddr(GetCurrentProcess(), addr, &displacement64, symbol))
|
||||
{
|
||||
if (SymGetLineFromAddr64(GetCurrentProcess(), addr, &displacement, &line))
|
||||
{
|
||||
m_dump << line.FileName << " L: " << line.LineNumber << " " << std::string_view(symbol->Name, symbol->NameLen);
|
||||
|
||||
continue;
|
||||
}
|
||||
const auto module_info = get_module_by_address(addr);
|
||||
m_dump << module_info->m_path.filename().string() << " " << std::string_view(symbol->Name, symbol->NameLen);
|
||||
|
||||
continue;
|
||||
}
|
||||
const auto module_info = get_module_by_address(addr);
|
||||
m_dump << module_info->m_path.filename().string() << "+" << HEX_TO_UPPER(addr - module_info->m_base) << " " << HEX_TO_UPPER(addr);
|
||||
}
|
||||
}
|
||||
|
||||
void stack_trace::dump_script_info()
|
||||
{
|
||||
m_dump << "Currently executing script: " << rage::scrThread::get()->m_name << '\n';
|
||||
m_dump << "Thread program counter (could be inaccurate): "
|
||||
<< m_exception_info->ContextRecord->Rdi - m_exception_info->ContextRecord->Rsi << '\n';
|
||||
}
|
||||
|
||||
void stack_trace::grab_stacktrace()
|
||||
{
|
||||
CONTEXT context = *m_exception_info->ContextRecord;
|
||||
|
||||
STACKFRAME64 frame{};
|
||||
frame.AddrPC.Mode = AddrModeFlat;
|
||||
frame.AddrFrame.Mode = AddrModeFlat;
|
||||
frame.AddrStack.Mode = AddrModeFlat;
|
||||
frame.AddrPC.Offset = context.Rip;
|
||||
frame.AddrFrame.Offset = context.Rbp;
|
||||
frame.AddrStack.Offset = context.Rsp;
|
||||
|
||||
for (size_t i = 0; i < m_frame_pointers.size(); ++i)
|
||||
{
|
||||
if (!StackWalk64(IMAGE_FILE_MACHINE_AMD64, GetCurrentProcess(), GetCurrentThread(), &frame, &context, nullptr, SymFunctionTableAccess64, SymGetModuleBase64, nullptr))
|
||||
{
|
||||
break;
|
||||
}
|
||||
m_frame_pointers[i] = frame.AddrPC.Offset;
|
||||
}
|
||||
}
|
||||
|
||||
const stack_trace::module_info* stack_trace::get_module_by_address(uint64_t addr) const
|
||||
{
|
||||
for (auto& mod_info : m_modules)
|
||||
{
|
||||
if (mod_info.m_base < addr && addr < mod_info.m_base + mod_info.m_size)
|
||||
{
|
||||
return &mod_info;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string stack_trace::exception_code_to_string(const DWORD code)
|
||||
{
|
||||
#define MAP_PAIR_STRINGIFY(x) \
|
||||
{ \
|
||||
x, #x \
|
||||
}
|
||||
static const std::map<DWORD, std::string> exceptions = {MAP_PAIR_STRINGIFY(EXCEPTION_ACCESS_VIOLATION), MAP_PAIR_STRINGIFY(EXCEPTION_ARRAY_BOUNDS_EXCEEDED), MAP_PAIR_STRINGIFY(EXCEPTION_DATATYPE_MISALIGNMENT), MAP_PAIR_STRINGIFY(EXCEPTION_FLT_DENORMAL_OPERAND), MAP_PAIR_STRINGIFY(EXCEPTION_FLT_DIVIDE_BY_ZERO), MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INEXACT_RESULT), MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INEXACT_RESULT), MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INVALID_OPERATION), MAP_PAIR_STRINGIFY(EXCEPTION_FLT_OVERFLOW), MAP_PAIR_STRINGIFY(EXCEPTION_FLT_STACK_CHECK), MAP_PAIR_STRINGIFY(EXCEPTION_FLT_UNDERFLOW), MAP_PAIR_STRINGIFY(EXCEPTION_ILLEGAL_INSTRUCTION), MAP_PAIR_STRINGIFY(EXCEPTION_IN_PAGE_ERROR), MAP_PAIR_STRINGIFY(EXCEPTION_INT_DIVIDE_BY_ZERO), MAP_PAIR_STRINGIFY(EXCEPTION_INT_OVERFLOW), MAP_PAIR_STRINGIFY(EXCEPTION_INVALID_DISPOSITION), MAP_PAIR_STRINGIFY(EXCEPTION_NONCONTINUABLE_EXCEPTION), MAP_PAIR_STRINGIFY(EXCEPTION_PRIV_INSTRUCTION), MAP_PAIR_STRINGIFY(EXCEPTION_STACK_OVERFLOW), MAP_PAIR_STRINGIFY(EXCEPTION_BREAKPOINT), MAP_PAIR_STRINGIFY(EXCEPTION_SINGLE_STEP)};
|
||||
|
||||
if (const auto& it = exceptions.find(code); it != exceptions.end())
|
||||
return it->second;
|
||||
|
||||
return "UNKNOWN_EXCEPTION";
|
||||
}
|
||||
}
|
||||
#include "stack_trace.hpp"
|
||||
|
||||
#include "gta/script_thread.hpp"
|
||||
#include "memory/module.hpp"
|
||||
|
||||
#include <DbgHelp.h>
|
||||
#include <winternl.h>
|
||||
|
||||
namespace big
|
||||
{
|
||||
stack_trace::stack_trace() :
|
||||
m_frame_pointers(32)
|
||||
{
|
||||
SymInitialize(GetCurrentProcess(), nullptr, true);
|
||||
}
|
||||
|
||||
stack_trace::~stack_trace()
|
||||
{
|
||||
SymCleanup(GetCurrentProcess());
|
||||
}
|
||||
|
||||
const std::vector<uint64_t>& stack_trace::frame_pointers()
|
||||
{
|
||||
return m_frame_pointers;
|
||||
}
|
||||
|
||||
void stack_trace::new_stack_trace(EXCEPTION_POINTERS* exception_info)
|
||||
{
|
||||
static std::mutex m;
|
||||
std::lock_guard lock(m);
|
||||
|
||||
m_exception_info = exception_info;
|
||||
|
||||
m_dump << exception_code_to_string(exception_info->ExceptionRecord->ExceptionCode) << '\n';
|
||||
|
||||
if (g.in_script_vm)
|
||||
dump_script_info();
|
||||
dump_module_info();
|
||||
dump_registers();
|
||||
dump_stacktrace();
|
||||
|
||||
m_dump << "\n--------End of exception--------\n";
|
||||
}
|
||||
|
||||
std::string stack_trace::str() const
|
||||
{
|
||||
return m_dump.str();
|
||||
}
|
||||
|
||||
// I'd prefer to make some sort of global instance that cache all modules once instead of doing this every time
|
||||
void stack_trace::dump_module_info()
|
||||
{
|
||||
// modules cached already
|
||||
if (m_modules.size())
|
||||
return;
|
||||
|
||||
m_dump << "Dumping modules:\n";
|
||||
|
||||
const auto peb = reinterpret_cast<PPEB>(NtCurrentTeb()->ProcessEnvironmentBlock);
|
||||
if (!peb)
|
||||
return;
|
||||
|
||||
const auto ldr_data = peb->Ldr;
|
||||
if (!ldr_data)
|
||||
return;
|
||||
|
||||
const auto module_list = &ldr_data->InMemoryOrderModuleList;
|
||||
auto module_entry = module_list->Flink;
|
||||
for (; module_list != module_entry; module_entry = module_entry->Flink)
|
||||
{
|
||||
const auto table_entry = CONTAINING_RECORD(module_entry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
|
||||
if (!table_entry)
|
||||
continue;
|
||||
|
||||
if (table_entry->FullDllName.Buffer)
|
||||
{
|
||||
auto mod_info = module_info(table_entry->FullDllName.Buffer, table_entry->DllBase);
|
||||
|
||||
m_dump << mod_info.m_path.filename().string() << " Base Address: " << HEX_TO_UPPER(mod_info.m_base)
|
||||
<< " Size: " << mod_info.m_size << '\n';
|
||||
|
||||
m_modules.emplace_back(std::move(mod_info));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void stack_trace::dump_registers()
|
||||
{
|
||||
const auto context = m_exception_info->ContextRecord;
|
||||
|
||||
m_dump << "Dumping registers:\n"
|
||||
<< "RAX: " << HEX_TO_UPPER(context->Rax) << '\n'
|
||||
<< "RCX: " << HEX_TO_UPPER(context->Rcx) << '\n'
|
||||
<< "RDX: " << HEX_TO_UPPER(context->Rdx) << '\n'
|
||||
<< "RBX: " << HEX_TO_UPPER(context->Rbx) << '\n'
|
||||
<< "RSI: " << HEX_TO_UPPER(context->Rsi) << '\n'
|
||||
<< "RDI: " << HEX_TO_UPPER(context->Rdi) << '\n'
|
||||
<< "RSP: " << HEX_TO_UPPER(context->Rsp) << '\n'
|
||||
<< "RBP: " << HEX_TO_UPPER(context->Rbp) << '\n'
|
||||
<< "R8: " << HEX_TO_UPPER(context->R8) << '\n'
|
||||
<< "R9: " << HEX_TO_UPPER(context->R9) << '\n'
|
||||
<< "R10: " << HEX_TO_UPPER(context->R10) << '\n'
|
||||
<< "R11: " << HEX_TO_UPPER(context->R11) << '\n'
|
||||
<< "R12: " << HEX_TO_UPPER(context->R12) << '\n'
|
||||
<< "R13: " << HEX_TO_UPPER(context->R13) << '\n'
|
||||
<< "R14: " << HEX_TO_UPPER(context->R14) << '\n'
|
||||
<< "R15: " << HEX_TO_UPPER(context->R15) << '\n';
|
||||
}
|
||||
|
||||
void stack_trace::dump_stacktrace()
|
||||
{
|
||||
m_dump << "Dumping stacktrace:";
|
||||
grab_stacktrace();
|
||||
|
||||
// alloc once
|
||||
char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME];
|
||||
auto symbol = reinterpret_cast<SYMBOL_INFO*>(buffer);
|
||||
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
symbol->MaxNameLen = MAX_SYM_NAME;
|
||||
|
||||
DWORD64 displacement64;
|
||||
DWORD displacement;
|
||||
|
||||
|
||||
IMAGEHLP_LINE64 line;
|
||||
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
||||
|
||||
for (size_t i = 0; i < m_frame_pointers.size() && m_frame_pointers[i]; ++i)
|
||||
{
|
||||
const auto addr = m_frame_pointers[i];
|
||||
|
||||
m_dump << "\n[" << i << "]\t";
|
||||
if (SymFromAddr(GetCurrentProcess(), addr, &displacement64, symbol))
|
||||
{
|
||||
if (SymGetLineFromAddr64(GetCurrentProcess(), addr, &displacement, &line))
|
||||
{
|
||||
m_dump << line.FileName << " L: " << line.LineNumber << " " << std::string_view(symbol->Name, symbol->NameLen);
|
||||
|
||||
continue;
|
||||
}
|
||||
const auto module_info = get_module_by_address(addr);
|
||||
m_dump << module_info->m_path.filename().string() << " " << std::string_view(symbol->Name, symbol->NameLen);
|
||||
|
||||
continue;
|
||||
}
|
||||
const auto module_info = get_module_by_address(addr);
|
||||
m_dump << module_info->m_path.filename().string() << "+" << HEX_TO_UPPER(addr - module_info->m_base) << " " << HEX_TO_UPPER(addr);
|
||||
}
|
||||
}
|
||||
|
||||
void stack_trace::dump_script_info()
|
||||
{
|
||||
m_dump << "Currently executing script: " << rage::scrThread::get()->m_name << '\n';
|
||||
m_dump << "Thread program counter (could be inaccurate): "
|
||||
<< m_exception_info->ContextRecord->Rdi - m_exception_info->ContextRecord->Rsi << '\n';
|
||||
}
|
||||
|
||||
void stack_trace::grab_stacktrace()
|
||||
{
|
||||
CONTEXT context = *m_exception_info->ContextRecord;
|
||||
|
||||
STACKFRAME64 frame{};
|
||||
frame.AddrPC.Mode = AddrModeFlat;
|
||||
frame.AddrFrame.Mode = AddrModeFlat;
|
||||
frame.AddrStack.Mode = AddrModeFlat;
|
||||
frame.AddrPC.Offset = context.Rip;
|
||||
frame.AddrFrame.Offset = context.Rbp;
|
||||
frame.AddrStack.Offset = context.Rsp;
|
||||
|
||||
for (size_t i = 0; i < m_frame_pointers.size(); ++i)
|
||||
{
|
||||
if (!StackWalk64(IMAGE_FILE_MACHINE_AMD64, GetCurrentProcess(), GetCurrentThread(), &frame, &context, nullptr, SymFunctionTableAccess64, SymGetModuleBase64, nullptr))
|
||||
{
|
||||
break;
|
||||
}
|
||||
m_frame_pointers[i] = frame.AddrPC.Offset;
|
||||
}
|
||||
}
|
||||
|
||||
const stack_trace::module_info* stack_trace::get_module_by_address(uint64_t addr) const
|
||||
{
|
||||
for (auto& mod_info : m_modules)
|
||||
{
|
||||
if (mod_info.m_base < addr && addr < mod_info.m_base + mod_info.m_size)
|
||||
{
|
||||
return &mod_info;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string stack_trace::exception_code_to_string(const DWORD code)
|
||||
{
|
||||
#define MAP_PAIR_STRINGIFY(x) \
|
||||
{ \
|
||||
x, #x \
|
||||
}
|
||||
static const std::map<DWORD, std::string> exceptions = {MAP_PAIR_STRINGIFY(EXCEPTION_ACCESS_VIOLATION), MAP_PAIR_STRINGIFY(EXCEPTION_ARRAY_BOUNDS_EXCEEDED), MAP_PAIR_STRINGIFY(EXCEPTION_DATATYPE_MISALIGNMENT), MAP_PAIR_STRINGIFY(EXCEPTION_FLT_DENORMAL_OPERAND), MAP_PAIR_STRINGIFY(EXCEPTION_FLT_DIVIDE_BY_ZERO), MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INEXACT_RESULT), MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INEXACT_RESULT), MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INVALID_OPERATION), MAP_PAIR_STRINGIFY(EXCEPTION_FLT_OVERFLOW), MAP_PAIR_STRINGIFY(EXCEPTION_FLT_STACK_CHECK), MAP_PAIR_STRINGIFY(EXCEPTION_FLT_UNDERFLOW), MAP_PAIR_STRINGIFY(EXCEPTION_ILLEGAL_INSTRUCTION), MAP_PAIR_STRINGIFY(EXCEPTION_IN_PAGE_ERROR), MAP_PAIR_STRINGIFY(EXCEPTION_INT_DIVIDE_BY_ZERO), MAP_PAIR_STRINGIFY(EXCEPTION_INT_OVERFLOW), MAP_PAIR_STRINGIFY(EXCEPTION_INVALID_DISPOSITION), MAP_PAIR_STRINGIFY(EXCEPTION_NONCONTINUABLE_EXCEPTION), MAP_PAIR_STRINGIFY(EXCEPTION_PRIV_INSTRUCTION), MAP_PAIR_STRINGIFY(EXCEPTION_STACK_OVERFLOW), MAP_PAIR_STRINGIFY(EXCEPTION_BREAKPOINT), MAP_PAIR_STRINGIFY(EXCEPTION_SINGLE_STEP)};
|
||||
|
||||
if (const auto& it = exceptions.find(code); it != exceptions.end())
|
||||
return it->second;
|
||||
|
||||
return "UNKNOWN_EXCEPTION";
|
||||
}
|
||||
}
|
||||
|
@ -1,68 +1,69 @@
|
||||
#pragma once
|
||||
#include "common.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
class stack_trace
|
||||
{
|
||||
public:
|
||||
stack_trace();
|
||||
virtual ~stack_trace();
|
||||
|
||||
void new_stack_trace(EXCEPTION_POINTERS* exception_info);
|
||||
std::string str() const;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const stack_trace& st);
|
||||
friend std::ostream& operator<<(std::ostream& os, const stack_trace* st);
|
||||
|
||||
private:
|
||||
struct module_info
|
||||
{
|
||||
module_info(std::filesystem::path path, void* base) :
|
||||
m_path(path),
|
||||
m_base(reinterpret_cast<uintptr_t>(base))
|
||||
{
|
||||
const auto dos_header = reinterpret_cast<IMAGE_DOS_HEADER*>(base);
|
||||
const auto nt_header = reinterpret_cast<IMAGE_NT_HEADERS*>(m_base + dos_header->e_lfanew);
|
||||
|
||||
m_size = nt_header->OptionalHeader.SizeOfCode;
|
||||
}
|
||||
|
||||
std::filesystem::path m_path;
|
||||
uintptr_t m_base;
|
||||
size_t m_size;
|
||||
};
|
||||
|
||||
private:
|
||||
void dump_module_info();
|
||||
void dump_registers();
|
||||
void dump_stacktrace();
|
||||
void dump_script_info();
|
||||
void grab_stacktrace();
|
||||
const module_info* get_module_by_address(uint64_t addr) const;
|
||||
|
||||
static std::string exception_code_to_string(const DWORD code);
|
||||
|
||||
private:
|
||||
EXCEPTION_POINTERS* m_exception_info;
|
||||
|
||||
std::stringstream m_dump;
|
||||
std::vector<uint64_t> m_frame_pointers;
|
||||
|
||||
inline static std::vector<module_info> m_modules;
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const stack_trace& st)
|
||||
{
|
||||
os << st.str();
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const stack_trace* st)
|
||||
{
|
||||
os << st->str();
|
||||
|
||||
return os;
|
||||
}
|
||||
#pragma once
|
||||
#include "common.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
class stack_trace
|
||||
{
|
||||
public:
|
||||
stack_trace();
|
||||
virtual ~stack_trace();
|
||||
|
||||
const std::vector<uint64_t>& frame_pointers();
|
||||
void new_stack_trace(EXCEPTION_POINTERS* exception_info);
|
||||
std::string str() const;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const stack_trace& st);
|
||||
friend std::ostream& operator<<(std::ostream& os, const stack_trace* st);
|
||||
|
||||
private:
|
||||
struct module_info
|
||||
{
|
||||
module_info(std::filesystem::path path, void* base) :
|
||||
m_path(path),
|
||||
m_base(reinterpret_cast<uintptr_t>(base))
|
||||
{
|
||||
const auto dos_header = reinterpret_cast<IMAGE_DOS_HEADER*>(base);
|
||||
const auto nt_header = reinterpret_cast<IMAGE_NT_HEADERS*>(m_base + dos_header->e_lfanew);
|
||||
|
||||
m_size = nt_header->OptionalHeader.SizeOfCode;
|
||||
}
|
||||
|
||||
std::filesystem::path m_path;
|
||||
uintptr_t m_base;
|
||||
size_t m_size;
|
||||
};
|
||||
|
||||
private:
|
||||
void dump_module_info();
|
||||
void dump_registers();
|
||||
void dump_stacktrace();
|
||||
void dump_script_info();
|
||||
void grab_stacktrace();
|
||||
const module_info* get_module_by_address(uint64_t addr) const;
|
||||
|
||||
static std::string exception_code_to_string(const DWORD code);
|
||||
|
||||
private:
|
||||
EXCEPTION_POINTERS* m_exception_info;
|
||||
|
||||
std::stringstream m_dump;
|
||||
std::vector<uint64_t> m_frame_pointers;
|
||||
|
||||
inline static std::vector<module_info> m_modules;
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const stack_trace& st)
|
||||
{
|
||||
os << st.str();
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const stack_trace* st)
|
||||
{
|
||||
os << st->str();
|
||||
|
||||
return os;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user