backend.cpp 17 KB

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