From 9dd91307b2d3fef4847205cc67456d40a75f449d Mon Sep 17 00:00:00 2001 From: lizzie Date: Wed, 28 Jan 2026 20:22:52 +0000 Subject: [PATCH] [common] remove uneeded allocations in logs Signed-off-by: lizzie --- src/common/logging/backend.cpp | 114 ++++++++++++++------------ src/common/logging/log_entry.h | 11 +-- src/common/logging/text_formatter.cpp | 28 ++++--- src/common/logging/text_formatter.h | 11 +-- 4 files changed, 89 insertions(+), 75 deletions(-) diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 5c93e179fb..788cf77ca2 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: 2014 Citra Emulator Project @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -41,7 +42,7 @@ namespace { /// @brief Trims up to and including the last of ../, ..\, src/, src\ in a string /// do not be fooled this isn't generating new strings on .rodata :) -constexpr const char* TrimSourcePath(std::string_view source) { +constexpr const char* TrimSourcePath(std::string_view source) noexcept { const auto rfind = [source](const std::string_view match) { return source.rfind(match) == source.npos ? 0 : (source.rfind(match) + match.size()); }; @@ -51,31 +52,31 @@ constexpr const char* TrimSourcePath(std::string_view source) { /// @brief Interface for logging backends. struct Backend { - virtual ~Backend() = default; - virtual void Write(const Entry& entry) = 0; - virtual void EnableForStacktrace() = 0; - virtual void Flush() = 0; + virtual ~Backend() noexcept = default; + virtual void Write(const Entry& entry) noexcept = 0; + virtual void EnableForStacktrace() noexcept= 0; + virtual void Flush() noexcept = 0; }; /// @brief Backend that writes to stderr and with color struct ColorConsoleBackend final : public Backend { - explicit ColorConsoleBackend() = default; - ~ColorConsoleBackend() override = default; + explicit ColorConsoleBackend() noexcept = default; + ~ColorConsoleBackend() noexcept override = default; - void Write(const Entry& entry) override { + void Write(const Entry& entry) noexcept override { if (enabled.load(std::memory_order_relaxed)) PrintColoredMessage(entry); } - void Flush() override { + void Flush() noexcept override { // stderr shouldn't be buffered } - void EnableForStacktrace() override { + void EnableForStacktrace() noexcept override { enabled = true; } - void SetEnabled(bool enabled_) { + void SetEnabled(bool enabled_) noexcept { enabled = enabled_; } @@ -83,9 +84,10 @@ private: std::atomic_bool enabled{false}; }; +#ifndef __OPENORBIS__ /// @brief Backend that writes to a file passed into the constructor struct FileBackend final : public Backend { - explicit FileBackend(const std::filesystem::path& filename) { + explicit FileBackend(const std::filesystem::path& filename) noexcept { auto old_filename = filename; old_filename += ".old.txt"; @@ -97,9 +99,9 @@ struct FileBackend final : public Backend { file = std::make_unique(filename, FS::FileAccessMode::Write, FS::FileType::TextFile); } - ~FileBackend() override = default; + ~FileBackend() noexcept override = default; - void Write(const Entry& entry) override { + void Write(const Entry& entry) noexcept override { if (!enabled) return; @@ -147,11 +149,11 @@ struct FileBackend final : public Backend { } } - void Flush() override { + void Flush() noexcept override { file->Flush(); } - void EnableForStacktrace() override { + void EnableForStacktrace() noexcept override { enabled = true; bytes_written = 0; } @@ -161,29 +163,30 @@ private: std::size_t bytes_written = 0; bool enabled = true; }; +#endif #ifdef _WIN32 /// @brief Backend that writes to Visual Studio's output window struct DebuggerBackend final : public Backend { - explicit DebuggerBackend() = default; - ~DebuggerBackend() override = default; - void Write(const Entry& entry) override { + explicit DebuggerBackend() noexcept = default; + ~DebuggerBackend() noexcept override = default; + void Write(const Entry& entry) noexcept override { ::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str()); } - void Flush() override {} - void EnableForStacktrace() override {} + void Flush() noexcept override {} + void EnableForStacktrace() noexcept override {} }; #endif #ifdef ANDROID /// @brief Backend that writes to the Android logcat struct LogcatBackend : public Backend { - explicit LogcatBackend() = default; - ~LogcatBackend() override = default; - void Write(const Entry& entry) override { + explicit LogcatBackend() noexcept = default; + ~LogcatBackend() noexcept override = default; + void Write(const Entry& entry) noexcept override { PrintMessageToLogcat(entry); } - void Flush() override {} - void EnableForStacktrace() override {} + void Flush() noexcept override {} + void EnableForStacktrace() noexcept override {} }; #endif @@ -192,13 +195,13 @@ bool initialization_in_progress_suppress_logging = true; /// @brief Static state as a singleton. class Impl { public: - static Impl& Instance() { + static Impl& Instance() noexcept { if (!instance) - throw std::runtime_error("Using Logging instance before its initialization"); + std::abort(); return *instance; } - static void Initialize() { + static void Initialize() noexcept { if (instance) { LOG_WARNING(Log, "Reinitializing logging backend"); return; @@ -212,25 +215,25 @@ public: initialization_in_progress_suppress_logging = false; } - static void Start() { + static void Start() noexcept { instance->StartBackendThread(); } - static void Stop() { + static void Stop() noexcept { instance->StopBackendThread(); } - Impl(const Impl&) = delete; - Impl& operator=(const Impl&) = delete; + Impl(const Impl&) noexcept = delete; + Impl& operator=(const Impl&) noexcept = delete; - Impl(Impl&&) = delete; - Impl& operator=(Impl&&) = delete; + Impl(Impl&&) noexcept = delete; + Impl& operator=(Impl&&) noexcept = delete; - void SetGlobalFilter(const Filter& f) { + void SetGlobalFilter(const Filter& f) noexcept { filter = f; } - void SetColorConsoleBackendEnabled(bool enabled) { + void SetColorConsoleBackendEnabled(bool enabled) noexcept { color_console_backend.SetEnabled(enabled); } @@ -238,19 +241,21 @@ public: return filter.CheckMessage(log_class, log_level); } - void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num, - const char* function, std::string&& message) noexcept { - message_queue.EmplaceWait( - CreateEntry(log_class, log_level, TrimSourcePath(filename), line_num, function, std::move(message))); + void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num, const char* function, std::string&& message) noexcept { + message_queue.EmplaceWait(CreateEntry(log_class, log_level, TrimSourcePath(filename), line_num, function, std::move(message))); } private: - Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_) - : filter{filter_}, file_backend{file_backend_filename} {} + Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_) noexcept : + filter{filter_} +#ifndef __OPENORBIS__ + , file_backend{file_backend_filename} +#endif + {} - ~Impl() = default; + ~Impl() noexcept = default; - void StartBackendThread() { + void StartBackendThread() noexcept { backend_thread = std::jthread([this](std::stop_token stop_token) { Common::SetCurrentThreadName("Logger"); Entry entry; @@ -271,15 +276,14 @@ private: }); } - void StopBackendThread() { + void StopBackendThread() noexcept { backend_thread.request_stop(); if (backend_thread.joinable()) backend_thread.join(); ForEachBackend([](Backend& backend) { backend.Flush(); }); } - Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr, - const char* function, std::string&& message) const { + Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr, const char* function, std::string&& message) const noexcept { using std::chrono::duration_cast; using std::chrono::microseconds; using std::chrono::steady_clock; @@ -294,9 +298,11 @@ private: }; } - void ForEachBackend(auto lambda) { + void ForEachBackend(auto lambda) noexcept { lambda(static_cast(color_console_backend)); +#ifndef __OPENORBIS__ lambda(static_cast(file_backend)); +#endif #ifdef _WIN32 lambda(static_cast(debugger_backend)); #endif @@ -305,7 +311,7 @@ private: #endif } - static void Deleter(Impl* ptr) { + static void Deleter(Impl* ptr) noexcept { delete ptr; } @@ -313,7 +319,9 @@ private: Filter filter; ColorConsoleBackend color_console_backend{}; +#ifndef __OPENORBIS__ FileBackend file_backend; +#endif #ifdef _WIN32 DebuggerBackend debugger_backend{}; #endif @@ -351,9 +359,7 @@ void SetColorConsoleBackendEnabled(bool enabled) { Impl::Instance().SetColorConsoleBackendEnabled(enabled); } -void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, - unsigned int line_num, const char* function, fmt::string_view format, - const fmt::format_args& args) { +void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, unsigned int line_num, const char* function, fmt::string_view format, const fmt::format_args& args) { if (!initialization_in_progress_suppress_logging) { auto& instance = Impl::Instance(); if (instance.CanPushEntry(log_class, log_level)) diff --git a/src/common/logging/log_entry.h b/src/common/logging/log_entry.h index d8d7daf762..23f1aaf625 100644 --- a/src/common/logging/log_entry.h +++ b/src/common/logging/log_entry.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -9,17 +12,15 @@ namespace Common::Log { -/** - * A log entry. Log entries are store in a structured format to permit more varied output - * formatting on different frontends, as well as facilitating filtering and aggregation. - */ +/// @brief A log entry. Log entries are store in a structured format to permit more varied output +/// formatting on different frontends, as well as facilitating filtering and aggregation. struct Entry { std::chrono::microseconds timestamp; Class log_class{}; Level log_level{}; const char* filename = nullptr; unsigned int line_num = 0; - std::string function; + const char* function = nullptr; std::string message; }; diff --git a/src/common/logging/text_formatter.cpp b/src/common/logging/text_formatter.cpp index 68b84d0e1b..dee1f770bc 100644 --- a/src/common/logging/text_formatter.cpp +++ b/src/common/logging/text_formatter.cpp @@ -24,24 +24,26 @@ namespace Common::Log { // Some IDEs prefer : instead, so let's just do that :) -std::string FormatLogMessage(const Entry& entry) { +std::string FormatLogMessage(const Entry& entry) noexcept { if (!entry.filename) return ""; auto const time_seconds = uint32_t(entry.timestamp.count() / 1000000); auto const time_fractional = uint32_t(entry.timestamp.count() % 1000000); - char const* class_name = GetLogClassName(entry.log_class); - char const* level_name = GetLevelName(entry.log_level); + auto const class_name = GetLogClassName(entry.log_class); + auto const level_name = GetLevelName(entry.log_level); return fmt::format("[{:4d}.{:06d}] {} <{}> {}:{}:{}: {}", time_seconds, time_fractional, class_name, level_name, entry.filename, entry.line_num, entry.function, entry.message); } -void PrintMessage(const Entry& entry) { +/// @brief Formats and prints a log entry to stderr. +static void PrintMessage(const Entry& entry) noexcept { #ifdef _WIN32 auto const str = FormatLogMessage(entry).append(1, '\n'); + fwrite(str.c_str(), 1, str.size(), stderr); #else #define ESC "\x1b" - auto str = std::string{[&entry]() -> const char* { + auto const color_str = [&entry]() -> const char* { switch (entry.log_level) { case Level::Debug: return ESC "[0;36m"; // Cyan case Level::Info: return ESC "[0;37m"; // Bright gray @@ -50,15 +52,19 @@ void PrintMessage(const Entry& entry) { case Level::Critical: return ESC "[1;35m"; // Bright magenta default: return ESC "[1;30m"; // Grey } - }()}; - str.append(FormatLogMessage(entry)); - str.append(ESC "[0m\n"); + }(); + auto const time_seconds = uint32_t(entry.timestamp.count() / 1000000); + auto const time_fractional = uint32_t(entry.timestamp.count() % 1000000); + auto const class_name = GetLogClassName(entry.log_class); + auto const level_name = GetLevelName(entry.log_level); + fprintf(stderr, "%s[%4d.%06d] %s <%s> %s:%u:%s: %s" ESC "[0m\n", color_str, + time_seconds, time_fractional, class_name, level_name, entry.filename, + entry.line_num, entry.function, entry.message.c_str()); #undef ESC #endif - fwrite(str.c_str(), 1, str.size(), stderr); } -void PrintColoredMessage(const Entry& entry) { +void PrintColoredMessage(const Entry& entry) noexcept { #ifdef _WIN32 HANDLE console_handle = GetStdHandle(STD_ERROR_HANDLE); if (console_handle == INVALID_HANDLE_VALUE) @@ -84,7 +90,7 @@ void PrintColoredMessage(const Entry& entry) { #endif } -void PrintMessageToLogcat(const Entry& entry) { +void PrintMessageToLogcat(const Entry& entry) noexcept { #ifdef ANDROID android_LogPriority android_log_priority = [&]() { switch (entry.log_level) { diff --git a/src/common/logging/text_formatter.h b/src/common/logging/text_formatter.h index 68417420bb..8ce11240f2 100644 --- a/src/common/logging/text_formatter.h +++ b/src/common/logging/text_formatter.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2014 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -10,11 +13,9 @@ namespace Common::Log { struct Entry; /// Formats a log entry into the provided text buffer. -std::string FormatLogMessage(const Entry& entry); -/// Formats and prints a log entry to stderr. -void PrintMessage(const Entry& entry); +std::string FormatLogMessage(const Entry& entry) noexcept; /// Prints the same message as `PrintMessage`, but colored according to the severity level. -void PrintColoredMessage(const Entry& entry); +void PrintColoredMessage(const Entry& entry) noexcept; /// Formats and prints a log entry to the android logcat. -void PrintMessageToLogcat(const Entry& entry); +void PrintMessageToLogcat(const Entry& entry) noexcept; } // namespace Common::Log