backend.cpp 17 KB

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