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