Implement tier0 library logging bettererer

This commit is contained in:
Lily Tsuru 2023-07-18 05:48:59 -04:00
parent 55eb398b1c
commit c59dcfc614
15 changed files with 253 additions and 17 deletions

View File

@ -30,6 +30,7 @@ endfunction()
add_library(lcpu_native SHARED add_library(lcpu_native SHARED
src/main.cpp src/main.cpp
src/SourceSink.cpp
) )
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")

View File

@ -0,0 +1,47 @@
#include "SourceSink.hpp"
#include <lucore/Library.hpp>
namespace tier0 {
// TODO: For now this'll leak..
lucore::Library* library = nullptr;
// TODO: Switch this to using the LoggingSystem. It is available on the x86-64 branch of
// gmod.
using Msg_t = void (*)(const char* format, ...);
Msg_t Msg = nullptr;
Msg_t Warning = nullptr;
} // namespace tier0
namespace lcpu {
SourceSink& SourceSink::The() {
static SourceSink sink;
return sink;
}
void SourceSink::Init() {
// Modern 64-bit GMod has one tier0 library.
// I am not supporting non-x86-64 branch servers, so this is OK and I don't need to
// do any library scanning nonsense to pick the correct one.
//
// (note that the x86-64 branch also includes 32-bit binaries, rather
// weirdly. You can build the module for this too, but not the non-x86-64 branch
// 32-bit GMod, and it also shares having only one tier0 library.)
tier0::library = lucore::Library::Open("tier0");
tier0::Msg = tier0::library->Symbol<tier0::Msg_t>("Msg");
tier0::Warning = tier0::library->Symbol<tier0::Msg_t>("Warning");
}
void SourceSink::OutputMessage(const lucore::Logger::MessageData& data) {
auto formatted =
std::format("[LCPU Native/{}] [{}] {}\n", lucore::Logger::SeverityToString(data.severity),
data.time, std::vformat(data.format, data.args));
if(data.severity < lucore::Logger::MessageSeverity::Warning) {
tier0::Msg("%s", formatted.c_str());
} else {
tier0::Warning("%s", formatted.c_str());
}
}
} // namespace lcpu

View File

@ -0,0 +1,15 @@
#include <lucore/Logger.hpp>
namespace lcpu {
/// A lucore logger sink which funnels to the Source engine's
/// Tier0 library's logging functionality.
struct SourceSink : public lucore::Logger::Sink {
static SourceSink& The();
static void Init();
void OutputMessage(const lucore::Logger::MessageData& data) override;
};
}

View File

@ -1,6 +1,17 @@
#include <GarrysMod/Lua/Interface.h> #include <GarrysMod/Lua/Interface.h>
#include "SourceSink.hpp"
#include <lucore/Assert.hpp>
GMOD_MODULE_OPEN() { GMOD_MODULE_OPEN() {
// Initalize the source logging sink and attach it to the lucore logger.
lcpu::SourceSink::Init();
lucore::Logger::The().AttachSink(lcpu::SourceSink::The());
lucore::LogInfo("Hello Source World :) {} {}", 123.456, "This should work");
lucore::LogWarning("test");
LUCORE_CHECK(false, "this should bring the process down");
return 0; return 0;
} }

View File

@ -8,6 +8,9 @@ project(lucore
add_library(lucore add_library(lucore
src/Assert.cpp src/Assert.cpp
src/Logger.cpp src/Logger.cpp
src/OsLibrary.cpp
src/Library.cpp
) )
target_compile_features(lucore PUBLIC cxx_std_20) target_compile_features(lucore PUBLIC cxx_std_20)

View File

@ -5,18 +5,15 @@
#include <format> #include <format>
namespace lucore { namespace lucore {
[[noreturn]] void ExitMsg(const char* message);
// TODO: wrapper which uses source_location (we don't need no macros anymore!)
[[noreturn]] void ExitMsg(const char* fileName, int fileLine, const char* message);
} // namespace lucore } // namespace lucore
#ifndef NDEBUG #ifndef NDEBUG
#define LUCORE_ASSERT(expr, fmt, ...) \ #define LUCORE_ASSERT(expr, fmt, ...) \
if(!(expr)) [[unlikely]] { \ if(!(expr)) [[unlikely]] { \
auto msg = std::format("Assertion \"{}\" failed with message: {}", #expr, \ auto msg = std::format("Assertion \"{}\" @ {}:{} failed with message: {}", #expr, \
std::format(fmt, ##__VA_ARGS__)); \ __FILE__, __LINE__, std::format(fmt, ##__VA_ARGS__)); \
::lucore::ExitMsg(__FILE__, __LINE__, msg.c_str()); \ ::lucore::ExitMsg(msg.c_str()); \
} }
#else #else
#define LUCORE_ASSERT(expr, format, ...) #define LUCORE_ASSERT(expr, format, ...)
@ -25,7 +22,7 @@ namespace lucore {
// CHECK() is always active, even in release builds // CHECK() is always active, even in release builds
#define LUCORE_CHECK(expr, fmt, ...) \ #define LUCORE_CHECK(expr, fmt, ...) \
if(!(expr)) [[unlikely]] { \ if(!(expr)) [[unlikely]] { \
auto msg = std::format("Check \"{}\" failed with message: {}", #expr, \ auto msg = std::format("Check \"{}\" @ {}:{} failed with message: {}", #expr, __FILE__, \
std::format(fmt, ##__VA_ARGS__)); \ __LINE__, std::format(fmt, ##__VA_ARGS__)); \
::lucore::ExitMsg(__FILE__, __LINE__, msg.c_str()); \ ::lucore::ExitMsg(msg.c_str()); \
} }

View File

@ -0,0 +1,27 @@
#include <string_view>
namespace lucore {
struct Library {
using Handle = void*;
/// Create a new library instance.
static Library* Open(std::string_view dllname);
/// Query if [dllname] is loaded in the process.
static bool Loaded(std::string_view dllname);
~Library();
template <class T>
T Symbol(std::string_view symbolName) {
return reinterpret_cast<T>(SymbolImpl(symbolName.data()));
}
private:
void* SymbolImpl(const char* symbol);
constexpr explicit Library(Handle handle) : handle(handle) {}
Handle handle {};
};
} // namespace lucore

View File

@ -82,14 +82,36 @@ namespace lucore {
std::vector<Sink*> sinks; std::vector<Sink*> sinks;
}; };
#if 0
/// A logger sink implementation that prints to standard output. /// A logger sink implementation that prints to standard output.
struct StdoutSink : public Logger::Sink { struct StdoutSink : public Logger::Sink {
static StdoutSink& The(); static StdoutSink& The();
virtual void OutputMessage(const Logger::MessageData& data) override; virtual void OutputMessage(const Logger::MessageData& data) override;
}; };
#endif
/// Attach the stdout logger sink to the logger. /// Attach the stdout logger sink to the logger.
void LoggerAttachStdout(); void LoggerAttachStdout();
template <class... Args>
void LogInfo(std::string_view format, Args... args) {
Logger::The().Info(format, std::forward<Args>(args)...);
}
template <class... Args>
void LogWarning(std::string_view format, Args... args) {
Logger::The().Warning(format, std::forward<Args>(args)...);
}
template <class... Args>
void LogError(std::string_view format, Args... args) {
Logger::The().Error(format, std::forward<Args>(args)...);
}
template <class... Args>
void LogFatal(std::string_view format, Args... args) {
Logger::The().Fatal(format, std::forward<Args>(args)...);
}
} // namespace lucore } // namespace lucore

View File

@ -6,8 +6,8 @@
namespace lucore { namespace lucore {
[[noreturn]] void ExitMsg(const char* fileName, int fileLine, const char* message) { [[noreturn]] void ExitMsg(const char* message) {
Logger::The().Fatal(message); LogFatal("{}", message);
std::quick_exit(0xAF); std::quick_exit(0xAF);
} }

View File

@ -0,0 +1,37 @@
#include <format>
#include <lucore/Library.hpp>
#include <string>
#include "OsLibrary.hpp"
namespace lucore {
namespace {
std::string FormatLibraryName(std::string_view dllName) {
#ifdef _WIN32
return std::format("{}.dll", dllName);
#else
return std::format("lib{}.so", dllName);
#endif
}
} // namespace
Library* Library::Open(std::string_view dllname) {
return new Library(
static_cast<void*>(detail::OsOpenLibrary(FormatLibraryName(dllname).c_str())));
}
bool Library::Loaded(std::string_view dllname) {
return detail::OsLibraryLoaded(FormatLibraryName(dllname).c_str());
}
Library::~Library() {
if(handle) {
detail::OsFreeLibrary(static_cast<detail::OsLibraryHandle>(handle));
}
}
void* Library::SymbolImpl(const char* symbolName) {
return detail::OsLibrarySymbol(static_cast<detail::OsLibraryHandle>(handle), symbolName);
}
} // namespace lucore

View File

@ -31,7 +31,7 @@ namespace lucore {
for(auto sink : sinks) for(auto sink : sinks)
sink->OutputMessage(data); sink->OutputMessage(data);
} }
#if 0
StdoutSink& StdoutSink::The() { StdoutSink& StdoutSink::The() {
static StdoutSink sink; static StdoutSink sink;
return sink; return sink;
@ -69,5 +69,6 @@ namespace lucore {
void LoggerAttachStdout() { void LoggerAttachStdout() {
Logger::The().AttachSink(StdoutSink::The()); Logger::The().AttachSink(StdoutSink::The());
} }
#endif
} // namespace lucore } // namespace lucore

View File

@ -0,0 +1,9 @@
#include "OsLibrary.hpp"
#if defined(_WIN32)
#include "OsLibrary.win32.cpp"
#elif defined(__linux__)
#include "OsLibrary.linux.cpp"
#else
#error No OsLibrary implementation for this platform
#endif

View File

@ -0,0 +1,21 @@
//! Operating-system independent utilities for opening
//! shared libraries. This is currently a detail-only
//! Lucore API, and its stability is NOT guaranteed.
namespace lucore::detail {
/// Opaque handle type for libraries.
using OsLibraryHandle = void*;
/// Open a library.
OsLibraryHandle OsOpenLibrary(const char* filename);
/// Query if the library with the given [filename] is loaded.
bool OsLibraryLoaded(const char* filename);
/// Get a pointer to the symbol of a library.
void* OsLibrarySymbol(OsLibraryHandle dll, const char* symbolName);
/// Free the library.
void OsFreeLibrary(OsLibraryHandle handle);
} // namespace lucore::detail

View File

@ -0,0 +1,22 @@
#include <dlfcn.h>
namespace lucore::detail {
OsLibraryHandle OsOpenLibrary(const char* filename) {
return dlopen(filename, RTLD_LAZY);
}
bool OsLibraryLoaded(const char* filename) {
return dlopen(filename, RTLD_NOLOAD | RTLD_LAZY) != nullptr;
}
void* OsLibrarySymbol(OsLibraryHandle dll, const char* symbolName) {
return dlsym(dll, symbolName);
}
void OsFreeLibrary(OsLibraryHandle handle) {
// The reference count on *Nix will be incremented by the launcher
// process itself, therefore we do not risk accidentally pulling the
// library out of the rug of the engine in either case.
dlclose(handle);
}
} // namespace lucore::detail

View File

@ -0,0 +1,23 @@
#define _WIN32_LEAN_AND_MEAN
#include <windows.h>
namespace lucore::detail {
OsLibraryHandle OsOpenLibrary(const char* filename) {
return reinterpret_cast<OsLibraryHandle>(GetModuleHandleA(filename);
}
bool OsLibraryLoaded(const char* filename) {
return GetModuleHandleA(filename) != nullptr;
}
void* OsLibrarySymbol(OsLibraryHandle dll, const char* symbolName) {
return GetProcAddressA(reinterpret_cast<HMODULE*>(dll), symbolName);
}
void OsFreeLibrary(OsLibraryHandle handle) {
// GetModuleHandle*() does not increment the reference count;
// therefore, we have nothing to do here on Windows.
}
}