Seregon/ShadPKG

A tool for deriving PKG packet encryption keys for ps4 written in c++

C++/47.3 KB/No license
common/logging/backend.cpp
ShadPKG / common / logging / backend.cpp
1// SPDX-FileCopyrightText: Copyright 2014 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3 
4#include <chrono>
5#include <filesystem>
6#include <thread>
7 
8#include <fmt/format.h>
9 
10#ifdef _WIN32
11#include <windows.h> // For OutputDebugStringW
12#endif
13 
14#include "common/bounded_threadsafe_queue.h"
15#include "common/config.h"
16#include "common/debug.h"
17#include "common/io_file.h"
18#include "common/logging/backend.h"
19#include "common/logging/log.h"
20#include "common/logging/log_entry.h"
21#include "common/logging/text_formatter.h"
22#include "common/path_util.h"
23#include "common/string_util.h"
24#include "common/thread.h"
25 
26namespace Common::Log {
27 
28using namespace Common::FS;
29 
30namespace {
31 
32/**
33 * Backend that writes to stderr and with color
34 */
35class ColorConsoleBackend {
36public:
37 explicit ColorConsoleBackend() = default;
38 
39 ~ColorConsoleBackend() = default;
40 
41 void Write(const Entry& entry) {
42 if (enabled.load(std::memory_order_relaxed)) {
43 PrintColoredMessage(entry);
44 }
45 }
46 
47 void Flush() {
48 // stderr shouldn't be buffered
49 }
50 
51 void SetEnabled(bool enabled_) {
52 enabled = enabled_;
53 }
54 
55private:
56 std::atomic_bool enabled{true};
57};
58 
59/**
60 * Backend that writes to a file passed into the constructor
61 */
62class FileBackend {
63public:
64 explicit FileBackend(const std::filesystem::path& filename)
65 : file{filename, FS::FileAccessMode::Write, FS::FileType::TextFile} {}
66 
67 ~FileBackend() = default;
68 
69 void Write(const Entry& entry) {
70 if (!enabled) {
71 return;
72 }
73 
74 bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n'));
75 
76 // Prevent logs from exceeding a set maximum size in the event that log entries are spammed.
77 const auto write_limit = 100_MB;
78 const bool write_limit_exceeded = bytes_written > write_limit;
79 if (entry.log_level >= Level::Error || write_limit_exceeded) {
80 if (write_limit_exceeded) {
81 // Stop writing after the write limit is exceeded.
82 // Don't close the file so we can print a stacktrace if necessary
83 enabled = false;
84 }
85 file.Flush();
86 }
87 }
88 
89 void Flush() {
90 file.Flush();
91 }
92 
93private:
94 Common::FS::IOFile file;
95 bool enabled = true;
96 std::size_t bytes_written = 0;
97};
98 
99/**
100 * Backend that writes to Visual Studio's output window
101 */
102class DebuggerBackend {
103public:
104 explicit DebuggerBackend() = default;
105 
106 ~DebuggerBackend() = default;
107 
108 void Write(const Entry& entry) {
109#ifdef _WIN32
110 ::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str());
111#endif
112 }
113 
114 void Flush() {}
115 
116 void EnableForStacktrace() {}
117};
118 
119bool initialization_in_progress_suppress_logging = true;
120 
121/**
122 * Static state as a singleton.
123 */
124class Impl {
125public:
126 static Impl& Instance() {
127 if (!instance) {
128 throw std::runtime_error("Using Logging instance before its initialization");
129 }
130 return *instance;
131 }
132 
133 static void Initialize(std::string_view log_file) {
134 if (instance) {
135 LOG_WARNING(Log, "Reinitializing logging backend");
136 return;
137 }
138 // Non creare directory utente: rispetta il nome file passato e usa la CWD se vuoto
139 Filter filter;
140 filter.ParseFilterString(Config::getLogFilter());
141 std::filesystem::path file_backend_path;
142 if (!log_file.empty()) {
143 file_backend_path = std::filesystem::path(std::string(log_file));
144 } else {
145 file_backend_path = LOG_FILE; // Crea il file nella directory corrente
146 }
147 instance = std::unique_ptr<Impl, decltype(&Deleter)>(new Impl(file_backend_path, filter),
148 Deleter);
149 initialization_in_progress_suppress_logging = false;
150 }
151 
152 static bool IsActive() {
153 return instance != nullptr;
154 }
155 
156 static void Start() {
157 instance->StartBackendThread();
158 }
159 
160 static void Stop() {
161 instance->StopBackendThread();
162 }
163 
164 Impl(const Impl&) = delete;
165 Impl& operator=(const Impl&) = delete;
166 
167 Impl(Impl&&) = delete;
168 Impl& operator=(Impl&&) = delete;
169 
170 void SetGlobalFilter(const Filter& f) {
171 filter = f;
172 }
173 
174 void SetColorConsoleBackendEnabled(bool enabled) {
175 color_console_backend.SetEnabled(enabled);
176 }
177 
178 void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num,
179 const char* function, std::string message) {
180 // Propagate important log messages to the profiler (DISABILITATO: Tracy non disponibile)
181 
182 if (!filter.CheckMessage(log_class, log_level)) {
183 return;
184 }
185 
186 using std::chrono::duration_cast;
187 using std::chrono::microseconds;
188 using std::chrono::steady_clock;
189 
190 const Entry entry = {
191 .timestamp = duration_cast<microseconds>(steady_clock::now() - time_origin),
192 .log_class = log_class,
193 .log_level = log_level,
194 .filename = filename,
195 .line_num = line_num,
196 .function = function,
197 .message = std::move(message),
198 };
199 if (Config::getLogType() == "async") {
200 message_queue.EmplaceWait(entry);
201 } else {
202 ForEachBackend([&entry](auto& backend) { backend.Write(entry); });
203 std::fflush(stdout);
204 }
205 }
206 
207private:
208 Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_)
209 : filter{filter_}, file_backend{file_backend_filename} {}
210 
211 ~Impl() = default;
212 
213 void StartBackendThread() {
214 backend_thread = std::jthread([this](std::stop_token stop_token) {
215 Common::SetCurrentThreadName("shadPS4:Log");
216 Entry entry;
217 const auto write_logs = [this, &entry]() {
218 ForEachBackend([&entry](auto& backend) { backend.Write(entry); });
219 };
220 while (!stop_token.stop_requested()) {
221 message_queue.PopWait(entry, stop_token);
222 if (entry.filename != nullptr) {
223 write_logs();
224 }
225 }
226 // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a
227 // case where a system is repeatedly spamming logs even on close.
228 int max_logs_to_write = filter.IsDebug() ? std::numeric_limits<s32>::max() : 100;
229 while (max_logs_to_write-- && message_queue.TryPop(entry)) {
230 write_logs();
231 }
232 });
233 }
234 
235 void StopBackendThread() {
236 backend_thread.request_stop();
237 if (backend_thread.joinable()) {
238 backend_thread.join();
239 }
240 
241 ForEachBackend([](auto& backend) { backend.Flush(); });
242 }
243 
244 void ForEachBackend(auto lambda) {
245 // lambda(debugger_backend);
246 lambda(color_console_backend);
247 lambda(file_backend);
248 }
249 
250 static void Deleter(Impl* ptr) {
251 delete ptr;
252 }
253 
254 static inline std::unique_ptr<Impl, decltype(&Deleter)> instance{nullptr, Deleter};
255 
256 Filter filter;
257 DebuggerBackend debugger_backend{};
258 ColorConsoleBackend color_console_backend{};
259 FileBackend file_backend;
260 
261 MPSCQueue<Entry> message_queue{};
262 std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
263 std::jthread backend_thread;
264};
265} // namespace
266 
267void Initialize(std::string_view log_file) {
268 Impl::Initialize(log_file.empty() ? LOG_FILE : log_file);
269}
270 
271bool IsActive() {
272 return Impl::IsActive();
273}
274 
275void Start() {
276 Impl::Start();
277}
278 
279void Stop() {
280 Impl::Stop();
281}
282 
283void SetGlobalFilter(const Filter& filter) {
284 Impl::Instance().SetGlobalFilter(filter);
285}
286 
287void SetColorConsoleBackendEnabled(bool enabled) {
288 Impl::Instance().SetColorConsoleBackendEnabled(enabled);
289}
290 
291void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
292 unsigned int line_num, const char* function, const char* format,
293 const fmt::format_args& args) {
294 if (!initialization_in_progress_suppress_logging) [[likely]] {
295 Impl::Instance().PushEntry(log_class, log_level, filename, line_num, function,
296 fmt::vformat(format, args));
297 }
298}
299} // namespace Common::Log
300