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 <climits>
9 #include <condition_variable>
10 #include <memory>
11 #include <mutex>
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 #include "core/settings.h"
27 
28 namespace Log {
29 
30 /**
31  * Static state as a singleton.
32  */
33 class Impl {
34 public:
Instance()35     static Impl& Instance() {
36         static Impl backend;
37         return backend;
38     }
39 
40     Impl(Impl const&) = delete;
41     const Impl& operator=(Impl const&) = delete;
42 
PushEntry(Class log_class,Level log_level,const char * filename,unsigned int line_num,const char * function,std::string message)43     void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num,
44                    const char* function, std::string message) {
45         message_queue.Push(
46             CreateEntry(log_class, log_level, filename, line_num, function, std::move(message)));
47     }
48 
AddBackend(std::unique_ptr<Backend> backend)49     void AddBackend(std::unique_ptr<Backend> backend) {
50         std::lock_guard lock{writing_mutex};
51         backends.push_back(std::move(backend));
52     }
53 
RemoveBackend(std::string_view backend_name)54     void RemoveBackend(std::string_view backend_name) {
55         std::lock_guard lock{writing_mutex};
56         const auto it =
57             std::remove_if(backends.begin(), backends.end(),
58                            [&backend_name](const auto& i) { return backend_name == i->GetName(); });
59         backends.erase(it, backends.end());
60     }
61 
GetGlobalFilter() const62     const Filter& GetGlobalFilter() const {
63         return filter;
64     }
65 
SetGlobalFilter(const Filter & f)66     void SetGlobalFilter(const Filter& f) {
67         filter = f;
68     }
69 
GetBackend(std::string_view backend_name)70     Backend* GetBackend(std::string_view backend_name) {
71         const auto it =
72             std::find_if(backends.begin(), backends.end(),
73                          [&backend_name](const auto& i) { return backend_name == i->GetName(); });
74         if (it == backends.end())
75             return nullptr;
76         return it->get();
77     }
78 
79 private:
Impl()80     Impl() {
81         backend_thread = std::thread([&] {
82             Entry entry;
83             auto write_logs = [&](Entry& e) {
84                 std::lock_guard lock{writing_mutex};
85                 for (const auto& backend : backends) {
86                     backend->Write(e);
87                 }
88             };
89             while (true) {
90                 entry = message_queue.PopWait();
91                 if (entry.final_entry) {
92                     break;
93                 }
94                 write_logs(entry);
95             }
96 
97             // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a case
98             // where a system is repeatedly spamming logs even on close.
99             const int MAX_LOGS_TO_WRITE = filter.IsDebug() ? INT_MAX : 100;
100             int logs_written = 0;
101             while (logs_written++ < MAX_LOGS_TO_WRITE && message_queue.Pop(entry)) {
102                 write_logs(entry);
103             }
104         });
105     }
106 
~Impl()107     ~Impl() {
108         Entry entry;
109         entry.final_entry = true;
110         message_queue.Push(entry);
111         backend_thread.join();
112     }
113 
CreateEntry(Class log_class,Level log_level,const char * filename,unsigned int line_nr,const char * function,std::string message) const114     Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
115                       const char* function, std::string message) const {
116         using std::chrono::duration_cast;
117         using std::chrono::microseconds;
118         using std::chrono::steady_clock;
119 
120         return {
121             .timestamp = duration_cast<microseconds>(steady_clock::now() - time_origin),
122             .log_class = log_class,
123             .log_level = log_level,
124             .filename = filename,
125             .line_num = line_nr,
126             .function = function,
127             .message = std::move(message),
128             .final_entry = false,
129         };
130     }
131 
132     std::mutex writing_mutex;
133     std::thread backend_thread;
134     std::vector<std::unique_ptr<Backend>> backends;
135     Common::MPSCQueue<Log::Entry> message_queue;
136     Filter filter;
137     std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
138 };
139 
Write(const Entry & entry)140 void ConsoleBackend::Write(const Entry& entry) {
141     PrintMessage(entry);
142 }
143 
Write(const Entry & entry)144 void ColorConsoleBackend::Write(const Entry& entry) {
145     PrintColoredMessage(entry);
146 }
147 
148 // _SH_DENYWR allows read only access to the file for other programs.
149 // It is #defined to 0 on other platforms
FileBackend(const std::string & filename)150 FileBackend::FileBackend(const std::string& filename)
151     : file(filename, "w", _SH_DENYWR), bytes_written(0) {}
152 
Write(const Entry & entry)153 void FileBackend::Write(const Entry& entry) {
154     // prevent logs from going over the maximum size (in case its spamming and the user doesn't
155     // know)
156     constexpr std::size_t MAX_BYTES_WRITTEN = 100 * 1024 * 1024;
157     constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1024 * 1024 * 1024;
158 
159     if (!file.IsOpen()) {
160         return;
161     }
162 
163     if (Settings::values.extended_logging && bytes_written > MAX_BYTES_WRITTEN_EXTENDED) {
164         return;
165     } else if (!Settings::values.extended_logging && bytes_written > MAX_BYTES_WRITTEN) {
166         return;
167     }
168 
169     bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n'));
170     if (entry.log_level >= Level::Error) {
171         file.Flush();
172     }
173 }
174 
Write(const Entry & entry)175 void DebuggerBackend::Write(const Entry& entry) {
176 #ifdef _WIN32
177     ::OutputDebugStringW(Common::UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str());
178 #endif
179 }
180 
181 /// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
182 #define ALL_LOG_CLASSES()                                                                          \
183     CLS(Log)                                                                                       \
184     CLS(Common)                                                                                    \
185     SUB(Common, Filesystem)                                                                        \
186     SUB(Common, Memory)                                                                            \
187     CLS(Core)                                                                                      \
188     SUB(Core, ARM)                                                                                 \
189     SUB(Core, Timing)                                                                              \
190     CLS(Config)                                                                                    \
191     CLS(Debug)                                                                                     \
192     SUB(Debug, Emulated)                                                                           \
193     SUB(Debug, GPU)                                                                                \
194     SUB(Debug, Breakpoint)                                                                         \
195     SUB(Debug, GDBStub)                                                                            \
196     CLS(Kernel)                                                                                    \
197     SUB(Kernel, SVC)                                                                               \
198     CLS(Service)                                                                                   \
199     SUB(Service, ACC)                                                                              \
200     SUB(Service, Audio)                                                                            \
201     SUB(Service, AM)                                                                               \
202     SUB(Service, AOC)                                                                              \
203     SUB(Service, APM)                                                                              \
204     SUB(Service, ARP)                                                                              \
205     SUB(Service, BCAT)                                                                             \
206     SUB(Service, BPC)                                                                              \
207     SUB(Service, BTDRV)                                                                            \
208     SUB(Service, BTM)                                                                              \
209     SUB(Service, Capture)                                                                          \
210     SUB(Service, ERPT)                                                                             \
211     SUB(Service, ETicket)                                                                          \
212     SUB(Service, EUPLD)                                                                            \
213     SUB(Service, Fatal)                                                                            \
214     SUB(Service, FGM)                                                                              \
215     SUB(Service, Friend)                                                                           \
216     SUB(Service, FS)                                                                               \
217     SUB(Service, GRC)                                                                              \
218     SUB(Service, HID)                                                                              \
219     SUB(Service, IRS)                                                                              \
220     SUB(Service, LBL)                                                                              \
221     SUB(Service, LDN)                                                                              \
222     SUB(Service, LDR)                                                                              \
223     SUB(Service, LM)                                                                               \
224     SUB(Service, Migration)                                                                        \
225     SUB(Service, Mii)                                                                              \
226     SUB(Service, MM)                                                                               \
227     SUB(Service, NCM)                                                                              \
228     SUB(Service, NFC)                                                                              \
229     SUB(Service, NFP)                                                                              \
230     SUB(Service, NIFM)                                                                             \
231     SUB(Service, NIM)                                                                              \
232     SUB(Service, NPNS)                                                                             \
233     SUB(Service, NS)                                                                               \
234     SUB(Service, NVDRV)                                                                            \
235     SUB(Service, OLSC)                                                                             \
236     SUB(Service, PCIE)                                                                             \
237     SUB(Service, PCTL)                                                                             \
238     SUB(Service, PCV)                                                                              \
239     SUB(Service, PM)                                                                               \
240     SUB(Service, PREPO)                                                                            \
241     SUB(Service, PSC)                                                                              \
242     SUB(Service, PSM)                                                                              \
243     SUB(Service, SET)                                                                              \
244     SUB(Service, SM)                                                                               \
245     SUB(Service, SPL)                                                                              \
246     SUB(Service, SSL)                                                                              \
247     SUB(Service, TCAP)                                                                             \
248     SUB(Service, Time)                                                                             \
249     SUB(Service, USB)                                                                              \
250     SUB(Service, VI)                                                                               \
251     SUB(Service, WLAN)                                                                             \
252     CLS(HW)                                                                                        \
253     SUB(HW, Memory)                                                                                \
254     SUB(HW, LCD)                                                                                   \
255     SUB(HW, GPU)                                                                                   \
256     SUB(HW, AES)                                                                                   \
257     CLS(IPC)                                                                                       \
258     CLS(Frontend)                                                                                  \
259     CLS(Render)                                                                                    \
260     SUB(Render, Software)                                                                          \
261     SUB(Render, OpenGL)                                                                            \
262     SUB(Render, Vulkan)                                                                            \
263     CLS(Audio)                                                                                     \
264     SUB(Audio, DSP)                                                                                \
265     SUB(Audio, Sink)                                                                               \
266     CLS(Input)                                                                                     \
267     CLS(Network)                                                                                   \
268     CLS(Loader)                                                                                    \
269     CLS(CheatEngine)                                                                               \
270     CLS(Crypto)                                                                                    \
271     CLS(WebService)
272 
273 // GetClassName is a macro defined by Windows.h, grrr...
GetLogClassName(Class log_class)274 const char* GetLogClassName(Class log_class) {
275     switch (log_class) {
276 #define CLS(x)                                                                                     \
277     case Class::x:                                                                                 \
278         return #x;
279 #define SUB(x, y)                                                                                  \
280     case Class::x##_##y:                                                                           \
281         return #x "." #y;
282         ALL_LOG_CLASSES()
283 #undef CLS
284 #undef SUB
285     case Class::Count:
286         break;
287     }
288     return "Invalid";
289 }
290 
GetLevelName(Level log_level)291 const char* GetLevelName(Level log_level) {
292 #define LVL(x)                                                                                     \
293     case Level::x:                                                                                 \
294         return #x
295     switch (log_level) {
296         LVL(Trace);
297         LVL(Debug);
298         LVL(Info);
299         LVL(Warning);
300         LVL(Error);
301         LVL(Critical);
302     case Level::Count:
303         break;
304     }
305 #undef LVL
306     return "Invalid";
307 }
308 
SetGlobalFilter(const Filter & filter)309 void SetGlobalFilter(const Filter& filter) {
310     Impl::Instance().SetGlobalFilter(filter);
311 }
312 
AddBackend(std::unique_ptr<Backend> backend)313 void AddBackend(std::unique_ptr<Backend> backend) {
314     Impl::Instance().AddBackend(std::move(backend));
315 }
316 
RemoveBackend(std::string_view backend_name)317 void RemoveBackend(std::string_view backend_name) {
318     Impl::Instance().RemoveBackend(backend_name);
319 }
320 
GetBackend(std::string_view backend_name)321 Backend* GetBackend(std::string_view backend_name) {
322     return Impl::Instance().GetBackend(backend_name);
323 }
324 
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)325 void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
326                        unsigned int line_num, const char* function, const char* format,
327                        const fmt::format_args& args) {
328     auto& instance = Impl::Instance();
329     const auto& filter = instance.GetGlobalFilter();
330     if (!filter.CheckMessage(log_class, log_level))
331         return;
332 
333     instance.PushEntry(log_class, log_level, filename, line_num, function,
334                        fmt::vformat(format, args));
335 }
336 } // namespace Log
337