diff options
Diffstat (limited to 'src/common/logging/backend.cpp')
| -rw-r--r-- | src/common/logging/backend.cpp | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp new file mode 100644 index 000000000..e79b84604 --- /dev/null +++ b/src/common/logging/backend.cpp | |||
| @@ -0,0 +1,151 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | |||
| 7 | #include "common/log.h" // For _dbg_assert_ | ||
| 8 | |||
| 9 | #include "common/logging/backend.h" | ||
| 10 | #include "common/logging/log.h" | ||
| 11 | #include "common/logging/text_formatter.h" | ||
| 12 | |||
| 13 | namespace Log { | ||
| 14 | |||
| 15 | static std::shared_ptr<Logger> global_logger; | ||
| 16 | |||
| 17 | /// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this. | ||
| 18 | #define ALL_LOG_CLASSES() \ | ||
| 19 | CLS(Log) \ | ||
| 20 | CLS(Common) \ | ||
| 21 | SUB(Common, Filesystem) \ | ||
| 22 | SUB(Common, Memory) \ | ||
| 23 | CLS(Core) \ | ||
| 24 | SUB(Core, ARM11) \ | ||
| 25 | CLS(Config) \ | ||
| 26 | CLS(Debug) \ | ||
| 27 | SUB(Debug, Emulated) \ | ||
| 28 | SUB(Debug, GPU) \ | ||
| 29 | SUB(Debug, Breakpoint) \ | ||
| 30 | CLS(Kernel) \ | ||
| 31 | SUB(Kernel, SVC) \ | ||
| 32 | CLS(Service) \ | ||
| 33 | SUB(Service, SRV) \ | ||
| 34 | SUB(Service, FS) \ | ||
| 35 | SUB(Service, APT) \ | ||
| 36 | SUB(Service, GSP) \ | ||
| 37 | SUB(Service, AC) \ | ||
| 38 | SUB(Service, PTM) \ | ||
| 39 | SUB(Service, CFG) \ | ||
| 40 | SUB(Service, DSP) \ | ||
| 41 | SUB(Service, HID) \ | ||
| 42 | CLS(HW) \ | ||
| 43 | SUB(HW, Memory) \ | ||
| 44 | SUB(HW, GPU) \ | ||
| 45 | CLS(Frontend) \ | ||
| 46 | CLS(Render) \ | ||
| 47 | SUB(Render, Software) \ | ||
| 48 | SUB(Render, OpenGL) \ | ||
| 49 | CLS(Loader) | ||
| 50 | |||
| 51 | Logger::Logger() { | ||
| 52 | // Register logging classes so that they can be queried at runtime | ||
| 53 | size_t parent_class; | ||
| 54 | all_classes.reserve((size_t)Class::Count); | ||
| 55 | |||
| 56 | #define CLS(x) \ | ||
| 57 | all_classes.push_back(Class::x); \ | ||
| 58 | parent_class = all_classes.size() - 1; | ||
| 59 | #define SUB(x, y) \ | ||
| 60 | all_classes.push_back(Class::x##_##y); \ | ||
| 61 | all_classes[parent_class].num_children += 1; | ||
| 62 | |||
| 63 | ALL_LOG_CLASSES() | ||
| 64 | #undef CLS | ||
| 65 | #undef SUB | ||
| 66 | |||
| 67 | // Ensures that ALL_LOG_CLASSES isn't missing any entries. | ||
| 68 | _dbg_assert_(Log, all_classes.size() == (size_t)Class::Count); | ||
| 69 | } | ||
| 70 | |||
| 71 | // GetClassName is a macro defined by Windows.h, grrr... | ||
| 72 | const char* Logger::GetLogClassName(Class log_class) { | ||
| 73 | switch (log_class) { | ||
| 74 | #define CLS(x) case Class::x: return #x; | ||
| 75 | #define SUB(x, y) case Class::x##_##y: return #x "." #y; | ||
| 76 | ALL_LOG_CLASSES() | ||
| 77 | #undef CLS | ||
| 78 | #undef SUB | ||
| 79 | } | ||
| 80 | return "Unknown"; | ||
| 81 | } | ||
| 82 | |||
| 83 | const char* Logger::GetLevelName(Level log_level) { | ||
| 84 | #define LVL(x) case Level::x: return #x | ||
| 85 | switch (log_level) { | ||
| 86 | LVL(Trace); | ||
| 87 | LVL(Debug); | ||
| 88 | LVL(Info); | ||
| 89 | LVL(Warning); | ||
| 90 | LVL(Error); | ||
| 91 | LVL(Critical); | ||
| 92 | } | ||
| 93 | return "Unknown"; | ||
| 94 | #undef LVL | ||
| 95 | } | ||
| 96 | |||
| 97 | void Logger::LogMessage(Entry entry) { | ||
| 98 | ring_buffer.Push(std::move(entry)); | ||
| 99 | } | ||
| 100 | |||
| 101 | size_t Logger::GetEntries(Entry* out_buffer, size_t buffer_len) { | ||
| 102 | return ring_buffer.BlockingPop(out_buffer, buffer_len); | ||
| 103 | } | ||
| 104 | |||
| 105 | std::shared_ptr<Logger> InitGlobalLogger() { | ||
| 106 | global_logger = std::make_shared<Logger>(); | ||
| 107 | return global_logger; | ||
| 108 | } | ||
| 109 | |||
| 110 | Entry CreateEntry(Class log_class, Level log_level, | ||
| 111 | const char* filename, unsigned int line_nr, const char* function, | ||
| 112 | const char* format, va_list args) { | ||
| 113 | using std::chrono::steady_clock; | ||
| 114 | using std::chrono::duration_cast; | ||
| 115 | |||
| 116 | static steady_clock::time_point time_origin = steady_clock::now(); | ||
| 117 | |||
| 118 | std::array<char, 4 * 1024> formatting_buffer; | ||
| 119 | |||
| 120 | Entry entry; | ||
| 121 | entry.timestamp = duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin); | ||
| 122 | entry.log_class = log_class; | ||
| 123 | entry.log_level = log_level; | ||
| 124 | |||
| 125 | snprintf(formatting_buffer.data(), formatting_buffer.size(), "%s:%s:%u", filename, function, line_nr); | ||
| 126 | entry.location = std::string(formatting_buffer.data()); | ||
| 127 | |||
| 128 | vsnprintf(formatting_buffer.data(), formatting_buffer.size(), format, args); | ||
| 129 | entry.message = std::string(formatting_buffer.data()); | ||
| 130 | |||
| 131 | return std::move(entry); | ||
| 132 | } | ||
| 133 | |||
| 134 | void LogMessage(Class log_class, Level log_level, | ||
| 135 | const char* filename, unsigned int line_nr, const char* function, | ||
| 136 | const char* format, ...) { | ||
| 137 | va_list args; | ||
| 138 | va_start(args, format); | ||
| 139 | Entry entry = CreateEntry(log_class, log_level, | ||
| 140 | filename, line_nr, function, format, args); | ||
| 141 | va_end(args); | ||
| 142 | |||
| 143 | if (global_logger != nullptr && !global_logger->IsClosed()) { | ||
| 144 | global_logger->LogMessage(std::move(entry)); | ||
| 145 | } else { | ||
| 146 | // Fall back to directly printing to stderr | ||
| 147 | PrintMessage(entry); | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | } | ||