feat(logger): use AsyncLogger instead of g3log (#882)

* feat(exception_handler): skip certain exception codes
* feat(zydis): Disable BUILD_DOXYGEN and FEATURE_ENCODER
* feat(cmake): link dbghelp
* feat(exception_handler): implement stack dumper
* feat(logger): differentiate between wine and non-wine
* feat(logger): add NO_COLOR env var support
* fix(logger): fix wine logging (#960)
* feat(exception_handler): added string logging of exception
* fix(logger): exception access violation NO_COLOR

--------

Co-authored-by: Aure7138 <100095051+Aure7138@users.noreply.github.com>
Co-authored-by: tupoy-ya <72797377+tupoy-ya@users.noreply.github.com>
This commit is contained in:
Yimura 2023-02-08 23:36:55 +01:00 committed by GitHub
parent 4249fd7f09
commit c4a85050a6
25 changed files with 669 additions and 261 deletions

View File

@ -10,10 +10,11 @@ include(scripts/git.cmake)
# Fetch modules
message("\nFetching modules")
include(scripts/minhook.cmake)
include(scripts/g3log.cmake)
include(scripts/async-logger.cmake)
include(scripts/pugixml.cmake)
include(scripts/json.cmake)
include(scripts/cpr.cmake)
include(scripts/zydis.cmake)
message("\nFetching custom modules")
include(scripts/imgui.cmake)
@ -45,7 +46,7 @@ target_include_directories(YimMenu PRIVATE
)
target_precompile_headers(YimMenu PRIVATE "${SRC_DIR}/common.hpp")
target_link_libraries(YimMenu PRIVATE pugixml minhook g3log imgui cpr)
target_link_libraries(YimMenu PRIVATE pugixml minhook AsyncLogger dbghelp imgui cpr Zydis)
# Warnings as errors
set_property(TARGET YimMenu PROPERTY COMPILE_WARNING_AS_ERROR ON)

View File

@ -0,0 +1,12 @@
include(FetchContent)
message("AsyncLogger")
FetchContent_Declare(
AsyncLogger
GIT_REPOSITORY https://github.com/Yimura/AsyncLogger.git
GIT_TAG v0.0.5
GIT_PROGRESS TRUE
)
FetchContent_MakeAvailable(AsyncLogger)
set_property(TARGET AsyncLogger PROPERTY CXX_STANDARD 23)

View File

@ -1,16 +0,0 @@
include(FetchContent)
set(ADD_FATAL_EXAMPLE OFF CACHE INTERNAL "")
set(INSTALL_G3LOG OFF CACHE INTERNAL "")
set(G3_SHARED_LIB OFF CACHE INTERNAL "")
set(G3_SHARED_RUNTIME OFF CACHE INTERNAL "")
FetchContent_Declare(
g3log
GIT_REPOSITORY https://github.com/YimMenu/g3log.git
GIT_TAG 6ccf93c2e966e9ae13df75496a88e7b45214f7f8
GIT_PROGRESS TRUE
)
message("g3log")
FetchContent_MakeAvailable(g3log)
set_property(TARGET g3log PROPERTY CXX_STANDARD 23)

15
scripts/zydis.cmake Normal file
View File

@ -0,0 +1,15 @@
include(FetchContent)
message("zydis")
set(ZYDIS_BUILD_DOXYGEN OFF CACHE BOOL "Zydis Build Doxygen")
set(ZYDIS_FEATURE_ENCODER OFF CACHE BOOL "Zydis Feature Encoder")
FetchContent_Declare(
zydis
GIT_REPOSITORY https://github.com/zyantific/zydis.git
GIT_TAG 8948d9a8f493330d27a0e7bbebf40f0391e45f1b
GIT_PROGRESS TRUE
)
FetchContent_MakeAvailable(zydis)
set_property(TARGET Zydis PROPERTY CXX_STANDARD 23)

View File

@ -49,7 +49,7 @@
#include <format>
#include <nlohmann/json.hpp>
#include "logger.hpp"
#include "logger/logger.hpp"
#include "core/globals.hpp"
#include "gta/natives.hpp"

View File

@ -1,6 +1,5 @@
#include "common.hpp"
#include "detour_hook.hpp"
#include "logger.hpp"
#include "memory/handle.hpp"
#include <MinHook.h>

View File

@ -1,6 +1,5 @@
#include "common.hpp"
#include "function_types.hpp"
#include "logger.hpp"
#include "gta/script_thread.hpp"
#include "gui.hpp"
#include "hooking.hpp"

View File

@ -438,7 +438,7 @@ namespace big
}
}
LOG(G3LOG_DEBUG) << "RECEIVED PACKET | Type: " << packet_type << " | Length: " << frame->m_length << " | Sender: " <<
LOG(VERBOSE) << "RECEIVED PACKET | Type: " << packet_type << " | Length: " << frame->m_length << " | Sender: " <<
(player ? player->get_name() : std::format("<M:{}>, <C:{:X}>, <P:{}>", (int)frame->m_msg_id, frame->m_connection_identifier, frame->m_peer_id).c_str()) << " | " << HEX_TO_UPPER((int)msgType);
}

View File

@ -403,7 +403,7 @@ namespace big
}
script_args += " };";
LOG(G3LOG_DEBUG) << "Script Event:\n"
LOG(VERBOSE) << "Script Event:\n"
<< "\tPlayer: " << player->get_name() << "\n"
<< "\tArgs: " << script_args;
}

View File

@ -1,7 +1,6 @@
#include "common.hpp"
#include "crossmap.hpp"
#include "invoker.hpp"
#include "logger.hpp"
#include "pointers.hpp"
extern "C" void _call_asm(void* context, void* function, void* ret);

View File

@ -1,224 +0,0 @@
#pragma once
#include "common.hpp"
#include "file_manager.hpp"
#include <g3log/g3log.hpp>
#include <g3log/loglevels.hpp>
#include <g3log/logworker.hpp>
namespace big
{
template <typename TP>
std::time_t to_time_t(TP tp)
{
using namespace std::chrono;
auto sctp = time_point_cast<system_clock::duration>(tp - TP::clock::now()
+ system_clock::now());
return system_clock::to_time_t(sctp);
}
class logger;
inline logger* g_log{};
enum class LogColor
{
RESET,
WHITE = 97,
CYAN = 36,
MAGENTA = 35,
BLUE = 34,
GREEN = 32,
YELLOW = 33,
RED = 31,
BLACK = 30
};
#define ADD_COLOR_TO_STREAM(color) "\x1b[" << int(color) << "m"
#define RESET_STREAM_COLOR "\x1b[" << int(LogColor::RESET) << "m"
#define HEX_TO_UPPER(value) "0x" << std::hex << std::uppercase << (DWORD64)value << std::dec << std::nouppercase
class logger final
{
public:
logger(std::string_view console_title, file file, bool attach_console = true)
: m_attach_console(attach_console), m_did_console_exist(false),
m_console_title(console_title), m_original_console_mode(0),
m_console_handle(nullptr), m_file(file),
m_worker(g3::LogWorker::createLogWorker())
{
initialize();
g_log = this;
}
~logger()
{
g_log = nullptr;
}
void initialize()
{
if (m_attach_console)
{
if (m_did_console_exist = ::AttachConsole(GetCurrentProcessId()); !m_did_console_exist)
AllocConsole();
if (m_console_handle = GetStdHandle(STD_OUTPUT_HANDLE); m_console_handle != nullptr)
{
SetConsoleTitleA(m_console_title.data());
SetConsoleOutputCP(CP_UTF8);
DWORD console_mode;
GetConsoleMode(m_console_handle, &console_mode);
m_original_console_mode = console_mode;
// terminal like behaviour enable full color support
console_mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN;
// prevent clicking in terminal from suspending our main thread
console_mode &= ~(ENABLE_QUICK_EDIT_MODE);
SetConsoleMode(m_console_handle, console_mode);
}
}
create_backup();
open_outstreams();
initialize_g3log();
}
void destroy()
{
destroy_g3log();
close_outstreams();
if (m_did_console_exist)
SetConsoleMode(m_console_handle, m_original_console_mode);
if (!m_did_console_exist && m_attach_console)
FreeConsole();
}
void file_out(std::stringstream& str)
{
m_file_out << str.str() << '\n';
}
private:
void create_backup()
{
if (m_file.exists())
{
auto file_time = std::filesystem::last_write_time(m_file.get_path());
auto time_t = to_time_t(file_time);
auto local_time = std::localtime(&time_t);
m_file.move(
std::format(
"./backup/{:0>2}-{:0>2}-{}-{:0>2}-{:0>2}-{:0>2}_{}",
local_time->tm_mon + 1,
local_time->tm_mday,
local_time->tm_year + 1900,
local_time->tm_hour,
local_time->tm_min,
local_time->tm_sec,
m_file.get_path().filename().string().c_str()
)
);
}
}
void initialize_g3log()
{
m_worker->addSink(std::make_unique<log_sink>(), &log_sink::callback);
g3::initializeLogging(m_worker.get());
}
void destroy_g3log()
{
m_worker->removeAllSinks();
m_worker.reset();
}
void open_outstreams()
{
if (m_attach_console)
m_console_out.open("CONOUT$", std::ios_base::out | std::ios_base::app);
m_file_out.open(m_file.get_path(), std::ios_base::out | std::ios_base::trunc);
}
void close_outstreams()
{
if (m_attach_console)
m_console_out.close();
m_file_out.close();
}
private:
struct log_sink
{
void callback(g3::LogMessageMover log)
{
if (g_log->m_console_out.is_open())
g_log->m_console_out << log.get().toString(log_sink::format_console) << std::flush;
g_log->m_file_out << log.get().toString(log_sink::format_file) << std::flush;
}
static LogColor get_color(const LEVELS level)
{
switch (level.value)
{
case g3::kDebugValue:
return LogColor::BLUE;
case g3::kInfoValue:
return LogColor::GREEN;
case g3::kWarningValue:
return LogColor::YELLOW;
}
return g3::internal::wasFatal(level) ? LogColor::RED : LogColor::WHITE;
}
static std::string format_console(const g3::LogMessage& msg)
{
LogColor color = log_sink::get_color(msg._level);
std::stringstream out;
out
<< "[" << msg.timestamp("%H:%M:%S") << "]"
<< ADD_COLOR_TO_STREAM(color)
<< "[" << msg.level() << "/"
<< msg.file() << ":" << msg.line() << "]"
<< RESET_STREAM_COLOR
<< ": ";
return out.str();
}
static std::string format_file(const g3::LogMessage& msg)
{
std::stringstream out;
out
<< "[" << msg.timestamp("%H:%M:%S") << "]"
<< "[" << msg.level() << "/"
<< msg.file() << ":" << msg.line() << "]"
<< ": ";
return out.str();
}
};
private:
friend struct log_sink;
bool m_attach_console;
bool m_did_console_exist;
std::string_view m_console_title;
DWORD m_original_console_mode;
HANDLE m_console_handle;
std::ofstream m_console_out;
std::ofstream m_file_out;
file m_file;
std::unique_ptr<g3::LogWorker> m_worker;
};
}

View File

@ -0,0 +1,35 @@
#include "exception_handler.hpp"
#include "stack_trace.hpp"
#include <Zydis/Zydis.h>
namespace big
{
exception_handler::exception_handler()
{
m_exception_handler = AddVectoredExceptionHandler(1, &vectored_exception_handler);
}
exception_handler::~exception_handler()
{
RemoveVectoredExceptionHandler(m_exception_handler);
}
LONG vectored_exception_handler(EXCEPTION_POINTERS* exception_info)
{
const auto exception_code = exception_info->ExceptionRecord->ExceptionCode;
if (exception_code == EXCEPTION_BREAKPOINT ||
exception_code == DBG_PRINTEXCEPTION_C ||
exception_code == DBG_PRINTEXCEPTION_WIDE_C)
return EXCEPTION_CONTINUE_SEARCH;
LOG(FATAL) << stack_trace(exception_info);
ZyanU64 opcode_address = exception_info->ContextRecord->Rip;
ZydisDisassembledInstruction instruction;
ZydisDisassembleIntel(ZYDIS_MACHINE_MODE_LONG_64, opcode_address, reinterpret_cast<void*>(opcode_address), 32, &instruction);
exception_info->ContextRecord->Rip += instruction.info.length;
return EXCEPTION_CONTINUE_EXECUTION;
}
}

View File

@ -0,0 +1,18 @@
#pragma once
#include "common.hpp"
namespace big
{
class exception_handler final
{
public:
exception_handler();
virtual ~exception_handler();
private:
void* m_exception_handler;
};
extern LONG vectored_exception_handler(EXCEPTION_POINTERS* exception_info);
}

212
src/logger/logger.cpp Normal file
View File

@ -0,0 +1,212 @@
#pragma once
#include "logger.hpp"
#include "memory/module.hpp"
namespace big
{
template <typename TP>
std::time_t to_time_t(TP tp)
{
using namespace std::chrono;
auto sctp = time_point_cast<system_clock::duration>(tp - TP::clock::now()
+ system_clock::now());
return system_clock::to_time_t(sctp);
}
logger::logger(std::string_view console_title, file file, bool attach_console) :
m_attach_console(attach_console),
m_did_console_exist(false),
m_console_logger(&logger::format_console),
m_console_title(console_title),
m_original_console_mode(0),
m_console_handle(nullptr),
m_file(file)
{
auto module = memory::module("ntdll.dll");
if (const auto env_no_color = std::getenv("NO_COLOR"); module.get_export("wine_get_version") || (env_no_color && strlen(env_no_color)))
{
LOG(VERBOSE) << "Using simple logger.";
m_console_logger = &logger::format_console_simple;
}
initialize();
g_log = this;
}
logger::~logger()
{
g_log = nullptr;
}
void logger::initialize()
{
if (m_attach_console)
{
if (m_did_console_exist = ::AttachConsole(GetCurrentProcessId()); !m_did_console_exist)
AllocConsole();
if (m_console_handle = GetStdHandle(STD_OUTPUT_HANDLE); m_console_handle != nullptr)
{
SetConsoleTitleA(m_console_title.data());
SetConsoleOutputCP(CP_UTF8);
DWORD console_mode;
GetConsoleMode(m_console_handle, &console_mode);
m_original_console_mode = console_mode;
// terminal like behaviour enable full color support
console_mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN;
// prevent clicking in terminal from suspending our main thread
console_mode &= ~(ENABLE_QUICK_EDIT_MODE);
SetConsoleMode(m_console_handle, console_mode);
}
}
create_backup();
open_outstreams();
Logger::Init();
Logger::AddSink([this](LogMessagePtr msg)
{
(this->*m_console_logger)(std::move(msg));
});
Logger::AddSink([this](LogMessagePtr msg)
{
format_file(std::move(msg));
});
}
void logger::destroy()
{
Logger::Destroy();
close_outstreams();
if (m_did_console_exist)
SetConsoleMode(m_console_handle, m_original_console_mode);
if (!m_did_console_exist && m_attach_console)
FreeConsole();
}
void logger::create_backup()
{
if (m_file.exists())
{
auto file_time = std::filesystem::last_write_time(m_file.get_path());
auto time_t = to_time_t(file_time);
auto local_time = std::localtime(&time_t);
m_file.move(
std::format(
"./backup/{:0>2}-{:0>2}-{}-{:0>2}-{:0>2}-{:0>2}_{}",
local_time->tm_mon + 1,
local_time->tm_mday,
local_time->tm_year + 1900,
local_time->tm_hour,
local_time->tm_min,
local_time->tm_sec,
m_file.get_path().filename().string().c_str()
)
);
}
}
void logger::open_outstreams()
{
if (m_attach_console)
m_console_out.open("CONOUT$", std::ios_base::out | std::ios_base::app);
m_file_out.open(m_file.get_path(), std::ios_base::out | std::ios_base::trunc);
}
void logger::close_outstreams()
{
if (m_attach_console)
m_console_out.close();
m_file_out.close();
}
const LogColor get_color(const eLogLevel level)
{
switch (level)
{
case VERBOSE:
return LogColor::BLUE;
case INFO:
return LogColor::GREEN;
case WARNING:
return LogColor::YELLOW;
case FATAL:
return LogColor::RED;
}
return LogColor::WHITE;
}
const char* get_level_string(const eLogLevel level)
{
constexpr std::array<const char*, 4> levelStrings = {{
{ "DEBUG" },
{ "INFO" },
{ "WARN" },
{ "ERROR" }
}};
return levelStrings[level];
}
void logger::format_console(const LogMessagePtr msg)
{
const auto color = get_color(msg->Level());
const auto timestamp = std::format("{0:%H:%M:%S}", msg->Timestamp());
const auto& location = msg->Location();
const auto level = msg->Level();
const auto file = std::filesystem::path(location.file_name()).filename().string();
m_console_out
<< "[" << timestamp << "]"
<< ADD_COLOR_TO_STREAM(color)
<< "[" << get_level_string(level) << "/" << file << ":" << location.line() << "] "
<< RESET_STREAM_COLOR
<< msg->Message()
<< std::flush;
}
void logger::format_console_simple(const LogMessagePtr msg)
{
const auto color = get_color(msg->Level());
const auto timestamp = std::format("{0:%H:%M:%S}", msg->Timestamp());
const auto& location = msg->Location();
const auto level = msg->Level();
const auto file = std::filesystem::path(location.file_name()).filename().string();
m_console_out
<< "[" << timestamp << "]"
<< "[" << get_level_string(level) << "/" << file << ":" << location.line() << "] "
<< msg->Message()
<< std::flush;
}
void logger::format_file(const LogMessagePtr msg)
{
if (!m_file_out.is_open())
return;
const auto timestamp = std::format("{0:%H:%M:%S}", msg->Timestamp());
const auto& location = msg->Location();
const auto level = msg->Level();
const auto file = std::filesystem::path(location.file_name()).filename().string();
m_file_out
<< "[" << timestamp << "]"
<< "[" << get_level_string(level) << "/" << file << ":" << location.line() << "] "
<< msg->Message()
<< std::flush;
}
}

64
src/logger/logger.hpp Normal file
View File

@ -0,0 +1,64 @@
#pragma once
#include "common.hpp"
#include "file_manager.hpp"
#include <AsyncLogger/Logger.hpp>
using namespace al;
namespace big
{
#define ADD_COLOR_TO_STREAM(color) "\x1b[" << int(color) << "m"
#define RESET_STREAM_COLOR "\x1b[" << int(LogColor::RESET) << "m"
#define HEX_TO_UPPER(value) "0x" << std::hex << std::uppercase << (DWORD64)value << std::dec << std::nouppercase
enum class LogColor
{
RESET,
WHITE = 97,
CYAN = 36,
MAGENTA = 35,
BLUE = 34,
GREEN = 32,
YELLOW = 33,
RED = 31,
BLACK = 30
};
class logger final
{
public:
logger(std::string_view console_title, file file, bool attach_console = true);
virtual ~logger();
void initialize();
void destroy();
private:
void create_backup();
void open_outstreams();
void close_outstreams();
void format_console(const LogMessagePtr msg);
void format_console_simple(const LogMessagePtr msg);
void format_file(const LogMessagePtr msg);
private:
bool m_attach_console;
bool m_did_console_exist;
void (logger::* m_console_logger)(const LogMessagePtr msg);
std::string_view m_console_title;
DWORD m_original_console_mode;
HANDLE m_console_handle;
std::ofstream m_console_out;
std::ofstream m_file_out;
file m_file;
};
inline logger* g_log = nullptr;
}

222
src/logger/stack_trace.cpp Normal file
View File

@ -0,0 +1,222 @@
#include "stack_trace.hpp"
#include "memory/module.hpp"
#include <DbgHelp.h>
#include <winternl.h>
namespace big
{
stack_trace::stack_trace(EXCEPTION_POINTERS* exception_info) :
m_exception_info(exception_info),
m_frame_pointers(32)
{
static std::mutex m;
std::lock_guard lock(m);
SymInitialize(GetCurrentProcess(), nullptr, true);
m_dump << exception_code_to_string(exception_info->ExceptionRecord->ExceptionCode) << '\n';
dump_module_info();
dump_registers();
dump_stacktrace();
m_dump << "\n--------End of exception--------\n";
}
stack_trace::~stack_trace()
{
SymCleanup(GetCurrentProcess());
}
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()
{
m_dump << "Dumping modules:\n";
// modules cached already
if (m_modules.size())
{
for (const auto& module : m_modules)
{
m_dump
<< module.m_path.filename().string()
<< " Base Address: " << HEX_TO_UPPER(module.m_base)
<< " Size: " << module.m_size << '\n';
}
return;
}
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::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";
}
}

View File

@ -0,0 +1,60 @@
#pragma once
#include "common.hpp"
namespace big
{
class stack_trace
{
public:
stack_trace(EXCEPTION_POINTERS* exception_info);
virtual ~stack_trace();
std::string str() const;
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 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;
}
}

View File

@ -2,7 +2,6 @@
#include "core/globals.hpp"
#include "fiber_pool.hpp"
#include "gui.hpp"
#include "logger.hpp"
#include "hooking.hpp"
#include "pointers.hpp"
#include "renderer.hpp"
@ -29,6 +28,8 @@
#include "services/matchmaking/matchmaking_service.hpp"
#include "services/api/api_service.hpp"
#include "logger/exception_handler.hpp"
BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID)
{
using namespace big;
@ -39,6 +40,8 @@ BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID)
g_hmodule = hmod;
g_main_thread = CreateThread(nullptr, 0, [](PVOID) -> DWORD
{
auto handler = exception_handler();
while (!FindWindow("grcWindow", nullptr))
std::this_thread::sleep_for(100ms);
@ -56,7 +59,7 @@ BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID)
try
{
LOG(INFO) << "Yim's Menu Initializing";
LOGF(INFO, "Git Info\n\tBranch:\t%s\n\tHash:\t%s\n\tDate:\t%s", version::GIT_BRANCH, version::GIT_SHA1, version::GIT_DATE);
LOGF(INFO, "Git Info\n\tBranch:\t{}\n\tHash:\t{}\n\tDate:\t{}", version::GIT_BRANCH, version::GIT_SHA1, version::GIT_DATE);
g_translation_service.init();
LOG(INFO) << "Translation Service initialized.";

View File

@ -1,5 +1,4 @@
#include "../common.hpp"
#include "../logger.hpp"
#include "common.hpp"
#include "batch.hpp"
#include "range.hpp"

View File

@ -46,7 +46,7 @@ namespace memory
const auto giveup_time = time.has_value()
? std::make_optional(std::chrono::high_resolution_clock::now() + time.value())
: std::nullopt;
LOG(G3LOG_DEBUG) << "Waiting for " << m_name << "...";
LOG(VERBOSE) << "Waiting for " << m_name << "...";
while (!try_get_module())
{
if (giveup_time.has_value() && giveup_time <= std::chrono::high_resolution_clock::now())

View File

@ -1,6 +1,5 @@
#pragma once
#include "common.hpp"
#include "logger.hpp"
#include "script.hpp"
namespace big

View File

@ -30,7 +30,7 @@ namespace big
if (!location)
LOG(FATAL) << "Failed to find pattern " << m_name << " in script " << program->m_name;
else
LOG(DEBUG) << "Found pattern " << m_name << " in script " << program->m_name;
LOG(VERBOSE) << "Found pattern " << m_name << " in script " << program->m_name;
m_ip = location.value() + m_offset;
}

View File

@ -131,13 +131,13 @@ namespace big
void gta_data_service::load_data()
{
LOG(G3LOG_DEBUG) << "Loading data from cache.";
LOG(VERBOSE) << "Loading data from cache.";
load_peds();
load_vehicles();
load_weapons();
LOG(G3LOG_DEBUG) << "Loaded all data from cache.";
LOG(VERBOSE) << "Loaded all data from cache.";
}
void gta_data_service::load_peds()
@ -256,7 +256,7 @@ namespace big
if (dlc_name == "mpG9EC")
{
LOG(G3LOG_DEBUG) << "Bad DLC, skipping...";
LOG(VERBOSE) << "Bad DLC, skipping...";
return std::size_t(0);
}
@ -410,7 +410,7 @@ skip:
m_update_state = eGtaDataUpdateState::IDLE;
LOG(INFO) << "Cache has been rebuilt.\n\tPeds: " << peds.size() << "\n\tVehicles: " << vehicles.size() << "\n\tWeapons: " << weapons.size();
LOG(G3LOG_DEBUG) << "Starting cache saving procedure...";
LOG(VERBOSE) << "Starting cache saving procedure...";
g_thread_pool->push([this, peds = std::move(peds), vehicles = std::move(vehicles), weapons = std::move(weapons)]
{
const auto game_version = std::strtoul(g_pointers->m_game_version, nullptr, 10);

View File

@ -18,7 +18,7 @@ namespace big
{
const std::uint32_t thread_count = std::thread::hardware_concurrency();
LOG(G3LOG_DEBUG) << "Allocating " << thread_count << " threads in thread pool.";
LOG(VERBOSE) << "Allocating " << thread_count << " threads in thread pool.";
this->m_thread_pool.reserve(thread_count);
for (std::uint32_t i = 0; i < thread_count; i++)
@ -80,6 +80,6 @@ namespace big
}
}
LOG(G3LOG_DEBUG) << "Thread " << std::this_thread::get_id() << " exiting...";
LOG(VERBOSE) << "Thread " << std::this_thread::get_id() << " exiting...";
}
}

View File

@ -15,6 +15,17 @@ namespace big
{
if (ImGui::BeginTabItem("DEBUG_TAB_MISC"_T.data()))
{
if (components::button("MOV QWORD"))
{
*static_cast<uint64_t*>(nullptr) = 0;
uint64_t i = *static_cast<uint64_t*>(nullptr);
}
if (components::button("MOV 0xdead"))
{
*((unsigned int*)0) = 0xDEAD;
}
if (components::button("Dump entrypoints"))
{
system::dump_entry_points();