diff options
| author | 2021-08-13 18:39:45 +0000 | |
|---|---|---|
| committer | 2021-08-13 18:39:45 +0000 | |
| commit | 001675dced1b7b751d1db4f0d6490776c613df2f (patch) | |
| tree | 151923c90c42bdcede2581ddf6b56768bfcafc01 /src | |
| parent | Merge pull request #6862 from german77/badsdl (diff) | |
| download | yuzu-001675dced1b7b751d1db4f0d6490776c613df2f.tar.gz yuzu-001675dced1b7b751d1db4f0d6490776c613df2f.tar.xz yuzu-001675dced1b7b751d1db4f0d6490776c613df2f.zip | |
logging: Simplify and make thread-safe
This simplifies the logging system.
This also fixes some lost messages on startup.
The simplification is simple. I removed unused functions and moved most things in the .h to the .cpp. I replaced the unnecessary linked list with its contents laid out as three member variables. Anything that went through the linked list now directly accesses the backends. Generic functions are replaced with those for each specific use case and there aren't many. This change increases coupling but we gain back more KISS and encapsulation.
With those changes it was easy to make it thread-safe. I just removed the mutex and turned a boolean atomic. I was planning to use this thread-safety in my next PR about stacktraces. It was actually async-signal-safety at first but I ended up using a different approach. Anyway getting rid of the linked list is important for that because have the list of backends constantly changing complicates things.
Diffstat (limited to 'src')
| -rw-r--r-- | src/common/logging/backend.cpp | 350 | ||||
| -rw-r--r-- | src/common/logging/backend.h | 113 | ||||
| -rw-r--r-- | src/core/core.cpp | 9 | ||||
| -rw-r--r-- | src/core/core.h | 9 | ||||
| -rw-r--r-- | src/tests/common/param_package.cpp | 2 | ||||
| -rw-r--r-- | src/yuzu/debugger/console.cpp | 11 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 19 | ||||
| -rw-r--r-- | src/yuzu_cmd/yuzu.cpp | 22 |
8 files changed, 243 insertions, 292 deletions
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 61dddab3f..13edda9c9 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp | |||
| @@ -2,13 +2,9 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | ||
| 6 | #include <atomic> | 5 | #include <atomic> |
| 7 | #include <chrono> | 6 | #include <chrono> |
| 8 | #include <climits> | 7 | #include <climits> |
| 9 | #include <condition_variable> | ||
| 10 | #include <memory> | ||
| 11 | #include <mutex> | ||
| 12 | #include <thread> | 8 | #include <thread> |
| 13 | #include <vector> | 9 | #include <vector> |
| 14 | 10 | ||
| @@ -16,104 +12,229 @@ | |||
| 16 | #include <windows.h> // For OutputDebugStringW | 12 | #include <windows.h> // For OutputDebugStringW |
| 17 | #endif | 13 | #endif |
| 18 | 14 | ||
| 19 | #include "common/assert.h" | ||
| 20 | #include "common/fs/file.h" | 15 | #include "common/fs/file.h" |
| 21 | #include "common/fs/fs.h" | 16 | #include "common/fs/fs.h" |
| 17 | #include "common/fs/fs_paths.h" | ||
| 18 | #include "common/fs/path_util.h" | ||
| 22 | #include "common/literals.h" | 19 | #include "common/literals.h" |
| 23 | 20 | ||
| 24 | #include "common/logging/backend.h" | 21 | #include "common/logging/backend.h" |
| 25 | #include "common/logging/log.h" | 22 | #include "common/logging/log.h" |
| 26 | #include "common/logging/text_formatter.h" | 23 | #include "common/logging/text_formatter.h" |
| 27 | #include "common/settings.h" | 24 | #include "common/settings.h" |
| 25 | #ifdef _WIN32 | ||
| 28 | #include "common/string_util.h" | 26 | #include "common/string_util.h" |
| 27 | #endif | ||
| 29 | #include "common/threadsafe_queue.h" | 28 | #include "common/threadsafe_queue.h" |
| 30 | 29 | ||
| 31 | namespace Common::Log { | 30 | namespace Common::Log { |
| 32 | 31 | ||
| 32 | namespace { | ||
| 33 | |||
| 33 | /** | 34 | /** |
| 34 | * Static state as a singleton. | 35 | * Interface for logging backends. |
| 35 | */ | 36 | */ |
| 36 | class Impl { | 37 | class Backend { |
| 37 | public: | 38 | public: |
| 38 | static Impl& Instance() { | 39 | virtual ~Backend() = default; |
| 39 | static Impl backend; | 40 | |
| 40 | return backend; | 41 | virtual void Write(const Entry& entry) = 0; |
| 42 | |||
| 43 | virtual void EnableForStacktrace() = 0; | ||
| 44 | |||
| 45 | virtual void Flush() = 0; | ||
| 46 | }; | ||
| 47 | |||
| 48 | /** | ||
| 49 | * Backend that writes to stderr and with color | ||
| 50 | */ | ||
| 51 | class ColorConsoleBackend final : public Backend { | ||
| 52 | public: | ||
| 53 | explicit ColorConsoleBackend() = default; | ||
| 54 | |||
| 55 | ~ColorConsoleBackend() override = default; | ||
| 56 | |||
| 57 | void Write(const Entry& entry) override { | ||
| 58 | if (enabled.load(std::memory_order_relaxed)) { | ||
| 59 | PrintColoredMessage(entry); | ||
| 60 | } | ||
| 41 | } | 61 | } |
| 42 | 62 | ||
| 43 | Impl(const Impl&) = delete; | 63 | void Flush() override { |
| 44 | Impl& operator=(const Impl&) = delete; | 64 | // stderr shouldn't be buffered |
| 65 | } | ||
| 45 | 66 | ||
| 46 | Impl(Impl&&) = delete; | 67 | void EnableForStacktrace() override { |
| 47 | Impl& operator=(Impl&&) = delete; | 68 | enabled = true; |
| 69 | } | ||
| 48 | 70 | ||
| 49 | void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num, | 71 | void SetEnabled(bool enabled_) { |
| 50 | const char* function, std::string message) { | 72 | enabled = enabled_; |
| 51 | message_queue.Push( | 73 | } |
| 52 | CreateEntry(log_class, log_level, filename, line_num, function, std::move(message))); | 74 | |
| 75 | private: | ||
| 76 | std::atomic_bool enabled{false}; | ||
| 77 | }; | ||
| 78 | |||
| 79 | /** | ||
| 80 | * Backend that writes to a file passed into the constructor | ||
| 81 | */ | ||
| 82 | class FileBackend final : public Backend { | ||
| 83 | public: | ||
| 84 | explicit FileBackend(const std::filesystem::path& filename) { | ||
| 85 | auto old_filename = filename; | ||
| 86 | old_filename += ".old.txt"; | ||
| 87 | |||
| 88 | // Existence checks are done within the functions themselves. | ||
| 89 | // We don't particularly care if these succeed or not. | ||
| 90 | static_cast<void>(FS::RemoveFile(old_filename)); | ||
| 91 | static_cast<void>(FS::RenameFile(filename, old_filename)); | ||
| 92 | |||
| 93 | file = std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write, | ||
| 94 | FS::FileType::TextFile); | ||
| 95 | } | ||
| 96 | |||
| 97 | ~FileBackend() override = default; | ||
| 98 | |||
| 99 | void Write(const Entry& entry) override { | ||
| 100 | if (!enabled) { | ||
| 101 | return; | ||
| 102 | } | ||
| 103 | |||
| 104 | bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n')); | ||
| 105 | |||
| 106 | using namespace Common::Literals; | ||
| 107 | // Prevent logs from exceeding a set maximum size in the event that log entries are spammed. | ||
| 108 | const auto write_limit = Settings::values.extended_logging ? 1_GiB : 100_MiB; | ||
| 109 | const bool write_limit_exceeded = bytes_written > write_limit; | ||
| 110 | if (entry.log_level >= Level::Error || write_limit_exceeded) { | ||
| 111 | if (write_limit_exceeded) { | ||
| 112 | // Stop writing after the write limit is exceeded. | ||
| 113 | // Don't close the file so we can print a stacktrace if necessary | ||
| 114 | enabled = false; | ||
| 115 | } | ||
| 116 | file->Flush(); | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | void Flush() override { | ||
| 121 | file->Flush(); | ||
| 122 | } | ||
| 123 | |||
| 124 | void EnableForStacktrace() override { | ||
| 125 | enabled = true; | ||
| 126 | bytes_written = 0; | ||
| 53 | } | 127 | } |
| 54 | 128 | ||
| 55 | void AddBackend(std::unique_ptr<Backend> backend) { | 129 | private: |
| 56 | std::lock_guard lock{writing_mutex}; | 130 | std::unique_ptr<FS::IOFile> file; |
| 57 | backends.push_back(std::move(backend)); | 131 | bool enabled = true; |
| 132 | std::size_t bytes_written = 0; | ||
| 133 | }; | ||
| 134 | |||
| 135 | /** | ||
| 136 | * Backend that writes to Visual Studio's output window | ||
| 137 | */ | ||
| 138 | class DebuggerBackend final : public Backend { | ||
| 139 | public: | ||
| 140 | explicit DebuggerBackend() = default; | ||
| 141 | |||
| 142 | ~DebuggerBackend() override = default; | ||
| 143 | |||
| 144 | void Write(const Entry& entry) override { | ||
| 145 | #ifdef _WIN32 | ||
| 146 | ::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str()); | ||
| 147 | #endif | ||
| 58 | } | 148 | } |
| 59 | 149 | ||
| 60 | void RemoveBackend(std::string_view backend_name) { | 150 | void Flush() override {} |
| 61 | std::lock_guard lock{writing_mutex}; | 151 | |
| 152 | void EnableForStacktrace() override {} | ||
| 153 | }; | ||
| 154 | |||
| 155 | bool initialization_in_progress_suppress_logging = false; | ||
| 62 | 156 | ||
| 63 | std::erase_if(backends, [&backend_name](const auto& backend) { | 157 | /** |
| 64 | return backend_name == backend->GetName(); | 158 | * Static state as a singleton. |
| 65 | }); | 159 | */ |
| 160 | class Impl { | ||
| 161 | public: | ||
| 162 | static Impl& Instance() { | ||
| 163 | if (!instance) { | ||
| 164 | abort(); | ||
| 165 | } | ||
| 166 | return *instance; | ||
| 66 | } | 167 | } |
| 67 | 168 | ||
| 68 | const Filter& GetGlobalFilter() const { | 169 | static void Initialize() { |
| 69 | return filter; | 170 | if (instance) { |
| 171 | abort(); | ||
| 172 | } | ||
| 173 | using namespace Common::FS; | ||
| 174 | initialization_in_progress_suppress_logging = true; | ||
| 175 | const auto& log_dir = GetYuzuPath(YuzuPath::LogDir); | ||
| 176 | void(CreateDir(log_dir)); | ||
| 177 | Filter filter; | ||
| 178 | filter.ParseFilterString(Settings::values.log_filter.GetValue()); | ||
| 179 | instance = std::unique_ptr<Impl, decltype(&Deleter)>(new Impl(log_dir / LOG_FILE, filter), | ||
| 180 | Deleter); | ||
| 181 | initialization_in_progress_suppress_logging = false; | ||
| 70 | } | 182 | } |
| 71 | 183 | ||
| 184 | Impl(const Impl&) = delete; | ||
| 185 | Impl& operator=(const Impl&) = delete; | ||
| 186 | |||
| 187 | Impl(Impl&&) = delete; | ||
| 188 | Impl& operator=(Impl&&) = delete; | ||
| 189 | |||
| 72 | void SetGlobalFilter(const Filter& f) { | 190 | void SetGlobalFilter(const Filter& f) { |
| 73 | filter = f; | 191 | filter = f; |
| 74 | } | 192 | } |
| 75 | 193 | ||
| 76 | Backend* GetBackend(std::string_view backend_name) { | 194 | void SetColorConsoleBackendEnabled(bool enabled) { |
| 77 | const auto it = | 195 | color_console_backend.SetEnabled(enabled); |
| 78 | std::find_if(backends.begin(), backends.end(), | 196 | } |
| 79 | [&backend_name](const auto& i) { return backend_name == i->GetName(); }); | 197 | |
| 80 | if (it == backends.end()) | 198 | void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num, |
| 81 | return nullptr; | 199 | const char* function, std::string message) { |
| 82 | return it->get(); | 200 | if (!filter.CheckMessage(log_class, log_level)) |
| 201 | return; | ||
| 202 | const Entry& entry = | ||
| 203 | CreateEntry(log_class, log_level, filename, line_num, function, std::move(message)); | ||
| 204 | message_queue.Push(entry); | ||
| 83 | } | 205 | } |
| 84 | 206 | ||
| 85 | private: | 207 | private: |
| 86 | Impl() { | 208 | Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_) |
| 87 | backend_thread = std::thread([&] { | 209 | : filter{filter_}, file_backend{file_backend_filename}, backend_thread{std::thread([this] { |
| 88 | Entry entry; | 210 | Common::SetCurrentThreadName("yuzu:Log"); |
| 89 | auto write_logs = [&](Entry& e) { | 211 | Entry entry; |
| 90 | std::lock_guard lock{writing_mutex}; | 212 | const auto write_logs = [this, &entry]() { |
| 91 | for (const auto& backend : backends) { | 213 | ForEachBackend([&entry](Backend& backend) { backend.Write(entry); }); |
| 92 | backend->Write(e); | 214 | }; |
| 93 | } | 215 | while (true) { |
| 94 | }; | 216 | entry = message_queue.PopWait(); |
| 95 | while (true) { | 217 | if (entry.final_entry) { |
| 96 | entry = message_queue.PopWait(); | 218 | break; |
| 97 | if (entry.final_entry) { | 219 | } |
| 98 | break; | 220 | write_logs(); |
| 99 | } | 221 | } |
| 100 | write_logs(entry); | 222 | // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a |
| 101 | } | 223 | // case where a system is repeatedly spamming logs even on close. |
| 224 | int max_logs_to_write = filter.IsDebug() ? INT_MAX : 100; | ||
| 225 | while (max_logs_to_write-- && message_queue.Pop(entry)) { | ||
| 226 | write_logs(); | ||
| 227 | } | ||
| 228 | })} {} | ||
| 102 | 229 | ||
| 103 | // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a | 230 | ~Impl() { |
| 104 | // case where a system is repeatedly spamming logs even on close. | 231 | StopBackendThread(); |
| 105 | const int MAX_LOGS_TO_WRITE = filter.IsDebug() ? INT_MAX : 100; | ||
| 106 | int logs_written = 0; | ||
| 107 | while (logs_written++ < MAX_LOGS_TO_WRITE && message_queue.Pop(entry)) { | ||
| 108 | write_logs(entry); | ||
| 109 | } | ||
| 110 | }); | ||
| 111 | } | 232 | } |
| 112 | 233 | ||
| 113 | ~Impl() { | 234 | void StopBackendThread() { |
| 114 | Entry entry; | 235 | Entry stop_entry{}; |
| 115 | entry.final_entry = true; | 236 | stop_entry.final_entry = true; |
| 116 | message_queue.Push(entry); | 237 | message_queue.Push(stop_entry); |
| 117 | backend_thread.join(); | 238 | backend_thread.join(); |
| 118 | } | 239 | } |
| 119 | 240 | ||
| @@ -135,100 +256,51 @@ private: | |||
| 135 | }; | 256 | }; |
| 136 | } | 257 | } |
| 137 | 258 | ||
| 138 | std::mutex writing_mutex; | 259 | void ForEachBackend(auto lambda) { |
| 139 | std::thread backend_thread; | 260 | lambda(static_cast<Backend&>(debugger_backend)); |
| 140 | std::vector<std::unique_ptr<Backend>> backends; | 261 | lambda(static_cast<Backend&>(color_console_backend)); |
| 141 | MPSCQueue<Entry> message_queue; | 262 | lambda(static_cast<Backend&>(file_backend)); |
| 142 | Filter filter; | 263 | } |
| 143 | std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; | ||
| 144 | }; | ||
| 145 | |||
| 146 | ConsoleBackend::~ConsoleBackend() = default; | ||
| 147 | |||
| 148 | void ConsoleBackend::Write(const Entry& entry) { | ||
| 149 | PrintMessage(entry); | ||
| 150 | } | ||
| 151 | |||
| 152 | ColorConsoleBackend::~ColorConsoleBackend() = default; | ||
| 153 | |||
| 154 | void ColorConsoleBackend::Write(const Entry& entry) { | ||
| 155 | PrintColoredMessage(entry); | ||
| 156 | } | ||
| 157 | |||
| 158 | FileBackend::FileBackend(const std::filesystem::path& filename) { | ||
| 159 | auto old_filename = filename; | ||
| 160 | old_filename += ".old.txt"; | ||
| 161 | |||
| 162 | // Existence checks are done within the functions themselves. | ||
| 163 | // We don't particularly care if these succeed or not. | ||
| 164 | FS::RemoveFile(old_filename); | ||
| 165 | void(FS::RenameFile(filename, old_filename)); | ||
| 166 | |||
| 167 | file = | ||
| 168 | std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write, FS::FileType::TextFile); | ||
| 169 | } | ||
| 170 | |||
| 171 | FileBackend::~FileBackend() = default; | ||
| 172 | 264 | ||
| 173 | void FileBackend::Write(const Entry& entry) { | 265 | static void Deleter(Impl* ptr) { |
| 174 | if (!file->IsOpen()) { | 266 | delete ptr; |
| 175 | return; | ||
| 176 | } | 267 | } |
| 177 | 268 | ||
| 178 | using namespace Common::Literals; | 269 | static inline std::unique_ptr<Impl, decltype(&Deleter)> instance{nullptr, Deleter}; |
| 179 | // Prevent logs from exceeding a set maximum size in the event that log entries are spammed. | ||
| 180 | constexpr std::size_t MAX_BYTES_WRITTEN = 100_MiB; | ||
| 181 | constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1_GiB; | ||
| 182 | 270 | ||
| 183 | const bool write_limit_exceeded = | 271 | Filter filter; |
| 184 | bytes_written > MAX_BYTES_WRITTEN_EXTENDED || | 272 | DebuggerBackend debugger_backend{}; |
| 185 | (bytes_written > MAX_BYTES_WRITTEN && !Settings::values.extended_logging); | 273 | ColorConsoleBackend color_console_backend{}; |
| 274 | FileBackend file_backend; | ||
| 186 | 275 | ||
| 187 | // Close the file after the write limit is exceeded. | 276 | std::thread backend_thread; |
| 188 | if (write_limit_exceeded) { | 277 | MPSCQueue<Entry> message_queue{}; |
| 189 | file->Close(); | 278 | std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; |
| 190 | return; | 279 | }; |
| 191 | } | 280 | } // namespace |
| 192 | 281 | ||
| 193 | bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n')); | 282 | void Initialize() { |
| 194 | if (entry.log_level >= Level::Error) { | 283 | Impl::Initialize(); |
| 195 | file->Flush(); | ||
| 196 | } | ||
| 197 | } | 284 | } |
| 198 | 285 | ||
| 199 | DebuggerBackend::~DebuggerBackend() = default; | 286 | void DisableLoggingInTests() { |
| 200 | 287 | initialization_in_progress_suppress_logging = true; | |
| 201 | void DebuggerBackend::Write(const Entry& entry) { | ||
| 202 | #ifdef _WIN32 | ||
| 203 | ::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str()); | ||
| 204 | #endif | ||
| 205 | } | 288 | } |
| 206 | 289 | ||
| 207 | void SetGlobalFilter(const Filter& filter) { | 290 | void SetGlobalFilter(const Filter& filter) { |
| 208 | Impl::Instance().SetGlobalFilter(filter); | 291 | Impl::Instance().SetGlobalFilter(filter); |
| 209 | } | 292 | } |
| 210 | 293 | ||
| 211 | void AddBackend(std::unique_ptr<Backend> backend) { | 294 | void SetColorConsoleBackendEnabled(bool enabled) { |
| 212 | Impl::Instance().AddBackend(std::move(backend)); | 295 | Impl::Instance().SetColorConsoleBackendEnabled(enabled); |
| 213 | } | ||
| 214 | |||
| 215 | void RemoveBackend(std::string_view backend_name) { | ||
| 216 | Impl::Instance().RemoveBackend(backend_name); | ||
| 217 | } | ||
| 218 | |||
| 219 | Backend* GetBackend(std::string_view backend_name) { | ||
| 220 | return Impl::Instance().GetBackend(backend_name); | ||
| 221 | } | 296 | } |
| 222 | 297 | ||
| 223 | void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, | 298 | void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, |
| 224 | unsigned int line_num, const char* function, const char* format, | 299 | unsigned int line_num, const char* function, const char* format, |
| 225 | const fmt::format_args& args) { | 300 | const fmt::format_args& args) { |
| 226 | auto& instance = Impl::Instance(); | 301 | if (!initialization_in_progress_suppress_logging) { |
| 227 | const auto& filter = instance.GetGlobalFilter(); | 302 | Impl::Instance().PushEntry(log_class, log_level, filename, line_num, function, |
| 228 | if (!filter.CheckMessage(log_class, log_level)) | 303 | fmt::vformat(format, args)); |
| 229 | return; | 304 | } |
| 230 | |||
| 231 | instance.PushEntry(log_class, log_level, filename, line_num, function, | ||
| 232 | fmt::vformat(format, args)); | ||
| 233 | } | 305 | } |
| 234 | } // namespace Common::Log | 306 | } // namespace Common::Log |
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h index 4b9a910c1..cb7839ee9 100644 --- a/src/common/logging/backend.h +++ b/src/common/logging/backend.h | |||
| @@ -5,120 +5,21 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <filesystem> | 7 | #include <filesystem> |
| 8 | #include <memory> | ||
| 9 | #include <string> | ||
| 10 | #include <string_view> | ||
| 11 | #include "common/logging/filter.h" | 8 | #include "common/logging/filter.h" |
| 12 | #include "common/logging/log.h" | ||
| 13 | |||
| 14 | namespace Common::FS { | ||
| 15 | class IOFile; | ||
| 16 | } | ||
| 17 | 9 | ||
| 18 | namespace Common::Log { | 10 | namespace Common::Log { |
| 19 | 11 | ||
| 20 | class Filter; | 12 | class Filter; |
| 21 | 13 | ||
| 22 | /** | 14 | /// Initializes the logging system. This should be the first thing called in main. |
| 23 | * Interface for logging backends. As loggers can be created and removed at runtime, this can be | 15 | void Initialize(); |
| 24 | * used by a frontend for adding a custom logging backend as needed | ||
| 25 | */ | ||
| 26 | class Backend { | ||
| 27 | public: | ||
| 28 | virtual ~Backend() = default; | ||
| 29 | |||
| 30 | virtual void SetFilter(const Filter& new_filter) { | ||
| 31 | filter = new_filter; | ||
| 32 | } | ||
| 33 | virtual const char* GetName() const = 0; | ||
| 34 | virtual void Write(const Entry& entry) = 0; | ||
| 35 | |||
| 36 | private: | ||
| 37 | Filter filter; | ||
| 38 | }; | ||
| 39 | |||
| 40 | /** | ||
| 41 | * Backend that writes to stderr without any color commands | ||
| 42 | */ | ||
| 43 | class ConsoleBackend : public Backend { | ||
| 44 | public: | ||
| 45 | ~ConsoleBackend() override; | ||
| 46 | |||
| 47 | static const char* Name() { | ||
| 48 | return "console"; | ||
| 49 | } | ||
| 50 | const char* GetName() const override { | ||
| 51 | return Name(); | ||
| 52 | } | ||
| 53 | void Write(const Entry& entry) override; | ||
| 54 | }; | ||
| 55 | |||
| 56 | /** | ||
| 57 | * Backend that writes to stderr and with color | ||
| 58 | */ | ||
| 59 | class ColorConsoleBackend : public Backend { | ||
| 60 | public: | ||
| 61 | ~ColorConsoleBackend() override; | ||
| 62 | |||
| 63 | static const char* Name() { | ||
| 64 | return "color_console"; | ||
| 65 | } | ||
| 66 | |||
| 67 | const char* GetName() const override { | ||
| 68 | return Name(); | ||
| 69 | } | ||
| 70 | void Write(const Entry& entry) override; | ||
| 71 | }; | ||
| 72 | 16 | ||
| 73 | /** | 17 | void DisableLoggingInTests(); |
| 74 | * Backend that writes to a file passed into the constructor | ||
| 75 | */ | ||
| 76 | class FileBackend : public Backend { | ||
| 77 | public: | ||
| 78 | explicit FileBackend(const std::filesystem::path& filename); | ||
| 79 | ~FileBackend() override; | ||
| 80 | |||
| 81 | static const char* Name() { | ||
| 82 | return "file"; | ||
| 83 | } | ||
| 84 | |||
| 85 | const char* GetName() const override { | ||
| 86 | return Name(); | ||
| 87 | } | ||
| 88 | |||
| 89 | void Write(const Entry& entry) override; | ||
| 90 | |||
| 91 | private: | ||
| 92 | std::unique_ptr<FS::IOFile> file; | ||
| 93 | std::size_t bytes_written = 0; | ||
| 94 | }; | ||
| 95 | |||
| 96 | /** | ||
| 97 | * Backend that writes to Visual Studio's output window | ||
| 98 | */ | ||
| 99 | class DebuggerBackend : public Backend { | ||
| 100 | public: | ||
| 101 | ~DebuggerBackend() override; | ||
| 102 | |||
| 103 | static const char* Name() { | ||
| 104 | return "debugger"; | ||
| 105 | } | ||
| 106 | const char* GetName() const override { | ||
| 107 | return Name(); | ||
| 108 | } | ||
| 109 | void Write(const Entry& entry) override; | ||
| 110 | }; | ||
| 111 | |||
| 112 | void AddBackend(std::unique_ptr<Backend> backend); | ||
| 113 | |||
| 114 | void RemoveBackend(std::string_view backend_name); | ||
| 115 | |||
| 116 | Backend* GetBackend(std::string_view backend_name); | ||
| 117 | 18 | ||
| 118 | /** | 19 | /** |
| 119 | * The global filter will prevent any messages from even being processed if they are filtered. Each | 20 | * The global filter will prevent any messages from even being processed if they are filtered. |
| 120 | * backend can have a filter, but if the level is lower than the global filter, the backend will | ||
| 121 | * never get the message | ||
| 122 | */ | 21 | */ |
| 123 | void SetGlobalFilter(const Filter& filter); | 22 | void SetGlobalFilter(const Filter& filter); |
| 124 | } // namespace Common::Log \ No newline at end of file | 23 | |
| 24 | void SetColorConsoleBackendEnabled(bool enabled); | ||
| 25 | } // namespace Common::Log | ||
diff --git a/src/core/core.cpp b/src/core/core.cpp index d3e84c4ef..0d3c4182a 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -84,8 +84,6 @@ FileSys::StorageId GetStorageIdForFrontendSlot( | |||
| 84 | 84 | ||
| 85 | } // Anonymous namespace | 85 | } // Anonymous namespace |
| 86 | 86 | ||
| 87 | /*static*/ System System::s_instance; | ||
| 88 | |||
| 89 | FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, | 87 | FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, |
| 90 | const std::string& path) { | 88 | const std::string& path) { |
| 91 | // To account for split 00+01+etc files. | 89 | // To account for split 00+01+etc files. |
| @@ -425,6 +423,13 @@ struct System::Impl { | |||
| 425 | System::System() : impl{std::make_unique<Impl>(*this)} {} | 423 | System::System() : impl{std::make_unique<Impl>(*this)} {} |
| 426 | System::~System() = default; | 424 | System::~System() = default; |
| 427 | 425 | ||
| 426 | void System::InitializeGlobalInstance() { | ||
| 427 | if (s_instance) { | ||
| 428 | abort(); | ||
| 429 | } | ||
| 430 | s_instance = std::unique_ptr<System>(new System); | ||
| 431 | } | ||
| 432 | |||
| 428 | CpuManager& System::GetCpuManager() { | 433 | CpuManager& System::GetCpuManager() { |
| 429 | return impl->cpu_manager; | 434 | return impl->cpu_manager; |
| 430 | } | 435 | } |
diff --git a/src/core/core.h b/src/core/core.h index ea143043c..85836f2f8 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -121,9 +121,14 @@ public: | |||
| 121 | * @returns Reference to the instance of the System singleton class. | 121 | * @returns Reference to the instance of the System singleton class. |
| 122 | */ | 122 | */ |
| 123 | [[deprecated("Use of the global system instance is deprecated")]] static System& GetInstance() { | 123 | [[deprecated("Use of the global system instance is deprecated")]] static System& GetInstance() { |
| 124 | return s_instance; | 124 | if (!s_instance) { |
| 125 | abort(); | ||
| 126 | } | ||
| 127 | return *s_instance; | ||
| 125 | } | 128 | } |
| 126 | 129 | ||
| 130 | static void InitializeGlobalInstance(); | ||
| 131 | |||
| 127 | /// Enumeration representing the return values of the System Initialize and Load process. | 132 | /// Enumeration representing the return values of the System Initialize and Load process. |
| 128 | enum class ResultStatus : u32 { | 133 | enum class ResultStatus : u32 { |
| 129 | Success, ///< Succeeded | 134 | Success, ///< Succeeded |
| @@ -396,7 +401,7 @@ private: | |||
| 396 | struct Impl; | 401 | struct Impl; |
| 397 | std::unique_ptr<Impl> impl; | 402 | std::unique_ptr<Impl> impl; |
| 398 | 403 | ||
| 399 | static System s_instance; | 404 | inline static std::unique_ptr<System> s_instance{}; |
| 400 | }; | 405 | }; |
| 401 | 406 | ||
| 402 | } // namespace Core | 407 | } // namespace Core |
diff --git a/src/tests/common/param_package.cpp b/src/tests/common/param_package.cpp index 4c0f9654f..e31ca3544 100644 --- a/src/tests/common/param_package.cpp +++ b/src/tests/common/param_package.cpp | |||
| @@ -4,11 +4,13 @@ | |||
| 4 | 4 | ||
| 5 | #include <catch2/catch.hpp> | 5 | #include <catch2/catch.hpp> |
| 6 | #include <math.h> | 6 | #include <math.h> |
| 7 | #include "common/logging/backend.h" | ||
| 7 | #include "common/param_package.h" | 8 | #include "common/param_package.h" |
| 8 | 9 | ||
| 9 | namespace Common { | 10 | namespace Common { |
| 10 | 11 | ||
| 11 | TEST_CASE("ParamPackage", "[common]") { | 12 | TEST_CASE("ParamPackage", "[common]") { |
| 13 | Common::Log::DisableLoggingInTests(); | ||
| 12 | ParamPackage original{ | 14 | ParamPackage original{ |
| 13 | {"abc", "xyz"}, | 15 | {"abc", "xyz"}, |
| 14 | {"def", "42"}, | 16 | {"def", "42"}, |
diff --git a/src/yuzu/debugger/console.cpp b/src/yuzu/debugger/console.cpp index 22ca1285d..f89ea8ea7 100644 --- a/src/yuzu/debugger/console.cpp +++ b/src/yuzu/debugger/console.cpp | |||
| @@ -21,6 +21,7 @@ void ToggleConsole() { | |||
| 21 | console_shown = UISettings::values.show_console.GetValue(); | 21 | console_shown = UISettings::values.show_console.GetValue(); |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | using namespace Common::Log; | ||
| 24 | #if defined(_WIN32) && !defined(_DEBUG) | 25 | #if defined(_WIN32) && !defined(_DEBUG) |
| 25 | FILE* temp; | 26 | FILE* temp; |
| 26 | if (UISettings::values.show_console) { | 27 | if (UISettings::values.show_console) { |
| @@ -29,24 +30,20 @@ void ToggleConsole() { | |||
| 29 | freopen_s(&temp, "CONIN$", "r", stdin); | 30 | freopen_s(&temp, "CONIN$", "r", stdin); |
| 30 | freopen_s(&temp, "CONOUT$", "w", stdout); | 31 | freopen_s(&temp, "CONOUT$", "w", stdout); |
| 31 | freopen_s(&temp, "CONOUT$", "w", stderr); | 32 | freopen_s(&temp, "CONOUT$", "w", stderr); |
| 32 | Common::Log::AddBackend(std::make_unique<Common::Log::ColorConsoleBackend>()); | 33 | SetColorConsoleBackendEnabled(true); |
| 33 | } | 34 | } |
| 34 | } else { | 35 | } else { |
| 35 | if (FreeConsole()) { | 36 | if (FreeConsole()) { |
| 36 | // In order to close the console, we have to also detach the streams on it. | 37 | // In order to close the console, we have to also detach the streams on it. |
| 37 | // Just redirect them to NUL if there is no console window | 38 | // Just redirect them to NUL if there is no console window |
| 38 | Common::Log::RemoveBackend(Common::Log::ColorConsoleBackend::Name()); | 39 | SetColorConsoleBackendEnabled(false); |
| 39 | freopen_s(&temp, "NUL", "r", stdin); | 40 | freopen_s(&temp, "NUL", "r", stdin); |
| 40 | freopen_s(&temp, "NUL", "w", stdout); | 41 | freopen_s(&temp, "NUL", "w", stdout); |
| 41 | freopen_s(&temp, "NUL", "w", stderr); | 42 | freopen_s(&temp, "NUL", "w", stderr); |
| 42 | } | 43 | } |
| 43 | } | 44 | } |
| 44 | #else | 45 | #else |
| 45 | if (UISettings::values.show_console) { | 46 | SetColorConsoleBackendEnabled(UISettings::values.show_console.GetValue()); |
| 46 | Common::Log::AddBackend(std::make_unique<Common::Log::ColorConsoleBackend>()); | ||
| 47 | } else { | ||
| 48 | Common::Log::RemoveBackend(Common::Log::ColorConsoleBackend::Name()); | ||
| 49 | } | ||
| 50 | #endif | 47 | #endif |
| 51 | } | 48 | } |
| 52 | } // namespace Debugger | 49 | } // namespace Debugger |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 5940e0cfd..1bae1489f 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -175,21 +175,6 @@ void GMainWindow::ShowTelemetryCallout() { | |||
| 175 | 175 | ||
| 176 | const int GMainWindow::max_recent_files_item; | 176 | const int GMainWindow::max_recent_files_item; |
| 177 | 177 | ||
| 178 | static void InitializeLogging() { | ||
| 179 | using namespace Common; | ||
| 180 | |||
| 181 | Log::Filter log_filter; | ||
| 182 | log_filter.ParseFilterString(Settings::values.log_filter.GetValue()); | ||
| 183 | Log::SetGlobalFilter(log_filter); | ||
| 184 | |||
| 185 | const auto log_dir = FS::GetYuzuPath(FS::YuzuPath::LogDir); | ||
| 186 | void(FS::CreateDir(log_dir)); | ||
| 187 | Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir / LOG_FILE)); | ||
| 188 | #ifdef _WIN32 | ||
| 189 | Log::AddBackend(std::make_unique<Log::DebuggerBackend>()); | ||
| 190 | #endif | ||
| 191 | } | ||
| 192 | |||
| 193 | static void RemoveCachedContents() { | 178 | static void RemoveCachedContents() { |
| 194 | const auto cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir); | 179 | const auto cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir); |
| 195 | const auto offline_fonts = cache_dir / "fonts"; | 180 | const auto offline_fonts = cache_dir / "fonts"; |
| @@ -207,8 +192,6 @@ GMainWindow::GMainWindow() | |||
| 207 | : input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, | 192 | : input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, |
| 208 | config{std::make_unique<Config>()}, vfs{std::make_shared<FileSys::RealVfsFilesystem>()}, | 193 | config{std::make_unique<Config>()}, vfs{std::make_shared<FileSys::RealVfsFilesystem>()}, |
| 209 | provider{std::make_unique<FileSys::ManualContentProvider>()} { | 194 | provider{std::make_unique<FileSys::ManualContentProvider>()} { |
| 210 | InitializeLogging(); | ||
| 211 | |||
| 212 | LoadTranslation(); | 195 | LoadTranslation(); |
| 213 | 196 | ||
| 214 | setAcceptDrops(true); | 197 | setAcceptDrops(true); |
| @@ -3398,6 +3381,7 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) { | |||
| 3398 | #endif | 3381 | #endif |
| 3399 | 3382 | ||
| 3400 | int main(int argc, char* argv[]) { | 3383 | int main(int argc, char* argv[]) { |
| 3384 | Common::Log::Initialize(); | ||
| 3401 | Common::DetachedTasks detached_tasks; | 3385 | Common::DetachedTasks detached_tasks; |
| 3402 | MicroProfileOnThreadCreate("Frontend"); | 3386 | MicroProfileOnThreadCreate("Frontend"); |
| 3403 | SCOPE_EXIT({ MicroProfileShutdown(); }); | 3387 | SCOPE_EXIT({ MicroProfileShutdown(); }); |
| @@ -3437,6 +3421,7 @@ int main(int argc, char* argv[]) { | |||
| 3437 | // generating shaders | 3421 | // generating shaders |
| 3438 | setlocale(LC_ALL, "C"); | 3422 | setlocale(LC_ALL, "C"); |
| 3439 | 3423 | ||
| 3424 | Core::System::InitializeGlobalInstance(); | ||
| 3440 | GMainWindow main_window; | 3425 | GMainWindow main_window; |
| 3441 | // After settings have been loaded by GMainWindow, apply the filter | 3426 | // After settings have been loaded by GMainWindow, apply the filter |
| 3442 | main_window.show(); | 3427 | main_window.show(); |
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index c10093820..ba2c993ba 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -74,31 +74,14 @@ static void PrintVersion() { | |||
| 74 | std::cout << "yuzu " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl; | 74 | std::cout << "yuzu " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl; |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | static void InitializeLogging() { | ||
| 78 | using namespace Common; | ||
| 79 | |||
| 80 | Log::Filter log_filter(Log::Level::Debug); | ||
| 81 | log_filter.ParseFilterString(static_cast<std::string>(Settings::values.log_filter)); | ||
| 82 | Log::SetGlobalFilter(log_filter); | ||
| 83 | |||
| 84 | Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); | ||
| 85 | |||
| 86 | const auto& log_dir = FS::GetYuzuPath(FS::YuzuPath::LogDir); | ||
| 87 | void(FS::CreateDir(log_dir)); | ||
| 88 | Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir / LOG_FILE)); | ||
| 89 | #ifdef _WIN32 | ||
| 90 | Log::AddBackend(std::make_unique<Log::DebuggerBackend>()); | ||
| 91 | #endif | ||
| 92 | } | ||
| 93 | |||
| 94 | /// Application entry point | 77 | /// Application entry point |
| 95 | int main(int argc, char** argv) { | 78 | int main(int argc, char** argv) { |
| 79 | Common::Log::Initialize(); | ||
| 80 | Common::Log::SetColorConsoleBackendEnabled(true); | ||
| 96 | Common::DetachedTasks detached_tasks; | 81 | Common::DetachedTasks detached_tasks; |
| 97 | Config config; | 82 | Config config; |
| 98 | 83 | ||
| 99 | int option_index = 0; | 84 | int option_index = 0; |
| 100 | |||
| 101 | InitializeLogging(); | ||
| 102 | #ifdef _WIN32 | 85 | #ifdef _WIN32 |
| 103 | int argc_w; | 86 | int argc_w; |
| 104 | auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w); | 87 | auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w); |
| @@ -163,6 +146,7 @@ int main(int argc, char** argv) { | |||
| 163 | return -1; | 146 | return -1; |
| 164 | } | 147 | } |
| 165 | 148 | ||
| 149 | Core::System::InitializeGlobalInstance(); | ||
| 166 | auto& system{Core::System::GetInstance()}; | 150 | auto& system{Core::System::GetInstance()}; |
| 167 | InputCommon::InputSubsystem input_subsystem; | 151 | InputCommon::InputSubsystem input_subsystem; |
| 168 | 152 | ||