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:
parent
4a6d865edd
commit
335e9325b0
@ -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)
|
||||
|
12
scripts/async-logger.cmake
Normal file
12
scripts/async-logger.cmake
Normal 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)
|
@ -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
15
scripts/zydis.cmake
Normal 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)
|
@ -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"
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include "common.hpp"
|
||||
#include "detour_hook.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory/handle.hpp"
|
||||
#include <MinHook.h>
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
224
src/logger.hpp
224
src/logger.hpp
@ -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;
|
||||
|
||||
};
|
||||
}
|
35
src/logger/exception_handler.cpp
Normal file
35
src/logger/exception_handler.cpp
Normal 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;
|
||||
}
|
||||
}
|
18
src/logger/exception_handler.hpp
Normal file
18
src/logger/exception_handler.hpp
Normal 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
212
src/logger/logger.cpp
Normal 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
64
src/logger/logger.hpp
Normal 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
222
src/logger/stack_trace.cpp
Normal 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";
|
||||
}
|
||||
}
|
60
src/logger/stack_trace.hpp
Normal file
60
src/logger/stack_trace.hpp
Normal 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;
|
||||
}
|
||||
}
|
@ -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.";
|
||||
|
@ -1,5 +1,4 @@
|
||||
#include "../common.hpp"
|
||||
#include "../logger.hpp"
|
||||
#include "common.hpp"
|
||||
#include "batch.hpp"
|
||||
#include "range.hpp"
|
||||
|
||||
|
@ -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())
|
||||
|
@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
#include "common.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "script.hpp"
|
||||
|
||||
namespace big
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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...";
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
Reference in New Issue
Block a user