1 // Copyright 2014 Citra Emulator Project
2 // Licensed under GPLv2 or any later version
3 // Refer to the license.txt file included.
4
5 #include <algorithm>
6 #include <atomic>
7 #include <chrono>
8 #include <condition_variable>
9 #include <memory>
10 #include <mutex>
11 #include <regex>
12 #include <thread>
13 #include <vector>
14 #ifdef _WIN32
15 #include <share.h> // For _SH_DENYWR
16 #include <windows.h> // For OutputDebugStringW
17 #else
18 #define _SH_DENYWR 0
19 #endif
20 #include "common/assert.h"
21 #include "common/logging/backend.h"
22 #include "common/logging/log.h"
23 #include "common/logging/text_formatter.h"
24 #include "common/string_util.h"
25 #include "common/threadsafe_queue.h"
26
27 namespace Log {
28
29 Filter filter;
SetGlobalFilter(const Filter & f)30 void SetGlobalFilter(const Filter& f) {
31 filter = f;
32 }
33 /**
34 * Static state as a singleton.
35 */
36 class Impl {
37 public:
Instance()38 static Impl& Instance() {
39 static Impl backend;
40 return backend;
41 }
42
43 Impl(Impl const&) = delete;
44 const Impl& operator=(Impl const&) = delete;
45
PushEntry(Class log_class,Level log_level,const char * filename,unsigned int line_num,const char * function,std::string message)46 void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num,
47 const char* function, std::string message) {
48 message_queue.Push(
49 CreateEntry(log_class, log_level, filename, line_num, function, std::move(message)));
50 }
51
AddBackend(std::unique_ptr<Backend> backend)52 void AddBackend(std::unique_ptr<Backend> backend) {
53 std::lock_guard lock{writing_mutex};
54 backends.push_back(std::move(backend));
55 }
56
RemoveBackend(std::string_view backend_name)57 void RemoveBackend(std::string_view backend_name) {
58 std::lock_guard lock{writing_mutex};
59 const auto it =
60 std::remove_if(backends.begin(), backends.end(),
61 [&backend_name](const auto& i) { return backend_name == i->GetName(); });
62 backends.erase(it, backends.end());
63 }
64
GetBackend(std::string_view backend_name)65 Backend* GetBackend(std::string_view backend_name) {
66 const auto it =
67 std::find_if(backends.begin(), backends.end(),
68 [&backend_name](const auto& i) { return backend_name == i->GetName(); });
69 if (it == backends.end())
70 return nullptr;
71 return it->get();
72 }
73
74 private:
Impl()75 Impl() {
76 backend_thread = std::thread([&] {
77 Entry entry;
78 auto write_logs = [&](Entry& e) {
79 std::lock_guard lock{writing_mutex};
80 for (const auto& backend : backends) {
81 backend->Write(e);
82 }
83 };
84 while (true) {
85 entry = message_queue.PopWait();
86 if (entry.final_entry) {
87 break;
88 }
89 write_logs(entry);
90 }
91
92 // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a case
93 // where a system is repeatedly spamming logs even on close.
94 constexpr int MAX_LOGS_TO_WRITE = 100;
95 int logs_written = 0;
96 while (logs_written++ < MAX_LOGS_TO_WRITE && message_queue.Pop(entry)) {
97 write_logs(entry);
98 }
99 });
100 }
101
~Impl()102 ~Impl() {
103 Entry entry;
104 entry.final_entry = true;
105 message_queue.Push(entry);
106 backend_thread.join();
107 }
108
CreateEntry(Class log_class,Level log_level,const char * filename,unsigned int line_nr,const char * function,std::string message) const109 Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
110 const char* function, std::string message) const {
111 using std::chrono::duration_cast;
112 using std::chrono::steady_clock;
113
114 Entry entry;
115 entry.timestamp =
116 duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin);
117 entry.log_class = log_class;
118 entry.log_level = log_level;
119 entry.filename = filename;
120 entry.line_num = line_nr;
121 entry.function = function;
122 entry.message = std::move(message);
123
124 return entry;
125 }
126
127 std::mutex writing_mutex;
128 std::thread backend_thread;
129 std::vector<std::unique_ptr<Backend>> backends;
130 Common::MPSCQueue<Log::Entry> message_queue;
131 Filter filter;
132 std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
133 };
134
Write(const Entry & entry)135 void ConsoleBackend::Write(const Entry& entry) {
136 PrintMessage(entry);
137 }
138
Write(const Entry & entry)139 void ColorConsoleBackend::Write(const Entry& entry) {
140 PrintColoredMessage(entry);
141 }
142
Write(const Entry & entry)143 void LogcatBackend::Write(const Entry& entry) {
144 PrintMessageToLogcat(entry);
145 }
146
FileBackend(const std::string & filename)147 FileBackend::FileBackend(const std::string& filename) : bytes_written(0) {
148 if (FileUtil::Exists(filename + ".old.txt")) {
149 FileUtil::Delete(filename + ".old.txt");
150 }
151 if (FileUtil::Exists(filename)) {
152 FileUtil::Rename(filename, filename + ".old.txt");
153 }
154
155 // _SH_DENYWR allows read only access to the file for other programs.
156 // It is #defined to 0 on other platforms
157 file = FileUtil::IOFile(filename, "w", _SH_DENYWR);
158 }
159
Write(const Entry & entry)160 void FileBackend::Write(const Entry& entry) {
161 // prevent logs from going over the maximum size (in case its spamming and the user doesn't
162 // know)
163 constexpr std::size_t MAX_BYTES_WRITTEN = 50 * 1024L * 1024L;
164 if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) {
165 return;
166 }
167 bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n'));
168 if (entry.log_level >= Level::Error) {
169 file.Flush();
170 }
171 }
172
Write(const Entry & entry)173 void DebuggerBackend::Write(const Entry& entry) {
174 #ifdef _WIN32
175 ::OutputDebugStringW(Common::UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str());
176 #endif
177 }
178
179 /// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
180 #define ALL_LOG_CLASSES() \
181 CLS(Log) \
182 CLS(Common) \
183 SUB(Common, Filesystem) \
184 SUB(Common, Memory) \
185 CLS(Core) \
186 SUB(Core, ARM11) \
187 SUB(Core, Timing) \
188 SUB(Core, Cheats) \
189 CLS(Config) \
190 CLS(Debug) \
191 SUB(Debug, Emulated) \
192 SUB(Debug, GPU) \
193 SUB(Debug, Breakpoint) \
194 SUB(Debug, GDBStub) \
195 CLS(Kernel) \
196 SUB(Kernel, SVC) \
197 CLS(Applet) \
198 SUB(Applet, SWKBD) \
199 CLS(Service) \
200 SUB(Service, SRV) \
201 SUB(Service, FRD) \
202 SUB(Service, FS) \
203 SUB(Service, ERR) \
204 SUB(Service, APT) \
205 SUB(Service, BOSS) \
206 SUB(Service, GSP) \
207 SUB(Service, AC) \
208 SUB(Service, AM) \
209 SUB(Service, PTM) \
210 SUB(Service, LDR) \
211 SUB(Service, MIC) \
212 SUB(Service, NDM) \
213 SUB(Service, NFC) \
214 SUB(Service, NIM) \
215 SUB(Service, NS) \
216 SUB(Service, NWM) \
217 SUB(Service, CAM) \
218 SUB(Service, CECD) \
219 SUB(Service, CFG) \
220 SUB(Service, CSND) \
221 SUB(Service, DSP) \
222 SUB(Service, DLP) \
223 SUB(Service, HID) \
224 SUB(Service, HTTP) \
225 SUB(Service, SOC) \
226 SUB(Service, IR) \
227 SUB(Service, Y2R) \
228 SUB(Service, PS) \
229 CLS(HW) \
230 SUB(HW, Memory) \
231 SUB(HW, LCD) \
232 SUB(HW, GPU) \
233 SUB(HW, AES) \
234 CLS(Frontend) \
235 CLS(Render) \
236 SUB(Render, Software) \
237 SUB(Render, OpenGL) \
238 CLS(Audio) \
239 SUB(Audio, DSP) \
240 SUB(Audio, Sink) \
241 CLS(Input) \
242 CLS(Network) \
243 CLS(Movie) \
244 CLS(Loader) \
245 CLS(WebService) \
246 CLS(RPC_Server)
247
248 // GetClassName is a macro defined by Windows.h, grrr...
GetLogClassName(Class log_class)249 const char* GetLogClassName(Class log_class) {
250 switch (log_class) {
251 #define CLS(x) \
252 case Class::x: \
253 return #x;
254 #define SUB(x, y) \
255 case Class::x##_##y: \
256 return #x "." #y;
257 ALL_LOG_CLASSES()
258 #undef CLS
259 #undef SUB
260 case Class::Count:
261 break;
262 }
263 UNREACHABLE();
264 return "Invalid";
265 }
266
GetLevelName(Level log_level)267 const char* GetLevelName(Level log_level) {
268 #define LVL(x) \
269 case Level::x: \
270 return #x
271 switch (log_level) {
272 LVL(Trace);
273 LVL(Debug);
274 LVL(Info);
275 LVL(Warning);
276 LVL(Error);
277 LVL(Critical);
278 case Level::Count:
279 break;
280 }
281 #undef LVL
282 UNREACHABLE();
283 return "Invalid";
284 }
285
AddBackend(std::unique_ptr<Backend> backend)286 void AddBackend(std::unique_ptr<Backend> backend) {
287 Impl::Instance().AddBackend(std::move(backend));
288 }
289
RemoveBackend(std::string_view backend_name)290 void RemoveBackend(std::string_view backend_name) {
291 Impl::Instance().RemoveBackend(backend_name);
292 }
293
GetBackend(std::string_view backend_name)294 Backend* GetBackend(std::string_view backend_name) {
295 return Impl::Instance().GetBackend(backend_name);
296 }
297
FmtLogMessageImpl(Class log_class,Level log_level,const char * filename,unsigned int line_num,const char * function,const char * format,const fmt::format_args & args)298 void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
299 unsigned int line_num, const char* function, const char* format,
300 const fmt::format_args& args) {
301 auto& instance = Impl::Instance();
302 instance.PushEntry(log_class, log_level, filename, line_num, function,
303 fmt::vformat(format, args));
304 }
305 } // namespace Log
306