diff options
| author | 2014-10-28 05:36:00 -0200 | |
|---|---|---|
| committer | 2014-12-13 01:59:52 -0200 | |
| commit | 616d87444313db865c60fbeee36ebe5250ef301e (patch) | |
| tree | fb99bf8bebfdf8c825c5d3e4f01fb4779ceaba68 /src | |
| parent | Add SCOPE_EXIT macro to conveniently execute cleanup actions (diff) | |
| download | yuzu-616d87444313db865c60fbeee36ebe5250ef301e.tar.gz yuzu-616d87444313db865c60fbeee36ebe5250ef301e.tar.xz yuzu-616d87444313db865c60fbeee36ebe5250ef301e.zip | |
New logging system
Diffstat (limited to 'src')
| -rw-r--r-- | src/citra/citra.cpp | 16 | ||||
| -rw-r--r-- | src/citra_qt/main.cpp | 18 | ||||
| -rw-r--r-- | src/common/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | src/common/common_types.h | 2 | ||||
| -rw-r--r-- | src/common/concurrent_ring_buffer.h | 164 | ||||
| -rw-r--r-- | src/common/log.h | 78 | ||||
| -rw-r--r-- | src/common/log_manager.cpp | 58 | ||||
| -rw-r--r-- | src/common/logging/backend.cpp | 151 | ||||
| -rw-r--r-- | src/common/logging/backend.h | 134 | ||||
| -rw-r--r-- | src/common/logging/log.h | 115 | ||||
| -rw-r--r-- | src/common/logging/text_formatter.cpp | 47 | ||||
| -rw-r--r-- | src/common/logging/text_formatter.h | 26 | ||||
| -rw-r--r-- | src/common/thread.h | 1 | ||||
| -rw-r--r-- | src/core/hle/config_mem.cpp | 1 |
14 files changed, 743 insertions, 74 deletions
diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index f2aeb510e..7c031ce8d 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp | |||
| @@ -2,8 +2,12 @@ | |||
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <thread> | ||
| 6 | |||
| 5 | #include "common/common.h" | 7 | #include "common/common.h" |
| 6 | #include "common/log_manager.h" | 8 | #include "common/logging/text_formatter.h" |
| 9 | #include "common/logging/backend.h" | ||
| 10 | #include "common/scope_exit.h" | ||
| 7 | 11 | ||
| 8 | #include "core/settings.h" | 12 | #include "core/settings.h" |
| 9 | #include "core/system.h" | 13 | #include "core/system.h" |
| @@ -15,7 +19,12 @@ | |||
| 15 | 19 | ||
| 16 | /// Application entry point | 20 | /// Application entry point |
| 17 | int __cdecl main(int argc, char **argv) { | 21 | int __cdecl main(int argc, char **argv) { |
| 18 | LogManager::Init(); | 22 | std::shared_ptr<Log::Logger> logger = Log::InitGlobalLogger(); |
| 23 | std::thread logging_thread(Log::TextLoggingLoop, logger); | ||
| 24 | SCOPE_EXIT({ | ||
| 25 | logger->Close(); | ||
| 26 | logging_thread.join(); | ||
| 27 | }); | ||
| 19 | 28 | ||
| 20 | if (argc < 2) { | 29 | if (argc < 2) { |
| 21 | ERROR_LOG(BOOT, "Failed to load ROM: No ROM specified"); | 30 | ERROR_LOG(BOOT, "Failed to load ROM: No ROM specified"); |
| @@ -24,9 +33,6 @@ int __cdecl main(int argc, char **argv) { | |||
| 24 | 33 | ||
| 25 | Config config; | 34 | Config config; |
| 26 | 35 | ||
| 27 | if (!Settings::values.enable_log) | ||
| 28 | LogManager::Shutdown(); | ||
| 29 | |||
| 30 | std::string boot_filename = argv[1]; | 36 | std::string boot_filename = argv[1]; |
| 31 | EmuWindow_GLFW* emu_window = new EmuWindow_GLFW; | 37 | EmuWindow_GLFW* emu_window = new EmuWindow_GLFW; |
| 32 | 38 | ||
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index b4e3ad964..2e3025295 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp | |||
| @@ -1,3 +1,5 @@ | |||
| 1 | #include <thread> | ||
| 2 | |||
| 1 | #include <QtGui> | 3 | #include <QtGui> |
| 2 | #include <QDesktopWidget> | 4 | #include <QDesktopWidget> |
| 3 | #include <QFileDialog> | 5 | #include <QFileDialog> |
| @@ -5,8 +7,13 @@ | |||
| 5 | #include "main.hxx" | 7 | #include "main.hxx" |
| 6 | 8 | ||
| 7 | #include "common/common.h" | 9 | #include "common/common.h" |
| 8 | #include "common/platform.h" | ||
| 9 | #include "common/log_manager.h" | 10 | #include "common/log_manager.h" |
| 11 | #include "common/logging/text_formatter.h" | ||
| 12 | #include "common/logging/log.h" | ||
| 13 | #include "common/logging/backend.h" | ||
| 14 | #include "common/platform.h" | ||
| 15 | #include "common/scope_exit.h" | ||
| 16 | |||
| 10 | #if EMU_PLATFORM == PLATFORM_LINUX | 17 | #if EMU_PLATFORM == PLATFORM_LINUX |
| 11 | #include <unistd.h> | 18 | #include <unistd.h> |
| 12 | #endif | 19 | #endif |
| @@ -33,10 +40,8 @@ | |||
| 33 | 40 | ||
| 34 | #include "version.h" | 41 | #include "version.h" |
| 35 | 42 | ||
| 36 | |||
| 37 | GMainWindow::GMainWindow() | 43 | GMainWindow::GMainWindow() |
| 38 | { | 44 | { |
| 39 | LogManager::Init(); | ||
| 40 | 45 | ||
| 41 | Pica::g_debug_context = Pica::DebugContext::Construct(); | 46 | Pica::g_debug_context = Pica::DebugContext::Construct(); |
| 42 | 47 | ||
| @@ -271,6 +276,13 @@ void GMainWindow::closeEvent(QCloseEvent* event) | |||
| 271 | 276 | ||
| 272 | int __cdecl main(int argc, char* argv[]) | 277 | int __cdecl main(int argc, char* argv[]) |
| 273 | { | 278 | { |
| 279 | std::shared_ptr<Log::Logger> logger = Log::InitGlobalLogger(); | ||
| 280 | std::thread logging_thread(Log::TextLoggingLoop, logger); | ||
| 281 | SCOPE_EXIT({ | ||
| 282 | logger->Close(); | ||
| 283 | logging_thread.join(); | ||
| 284 | }); | ||
| 285 | |||
| 274 | QApplication::setAttribute(Qt::AA_X11InitThreads); | 286 | QApplication::setAttribute(Qt::AA_X11InitThreads); |
| 275 | QApplication app(argc, argv); | 287 | QApplication app(argc, argv); |
| 276 | GMainWindow main_window; | 288 | GMainWindow main_window; |
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 1dbc5db21..7c1d3d1dc 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -11,6 +11,8 @@ set(SRCS | |||
| 11 | hash.cpp | 11 | hash.cpp |
| 12 | key_map.cpp | 12 | key_map.cpp |
| 13 | log_manager.cpp | 13 | log_manager.cpp |
| 14 | logging/text_formatter.cpp | ||
| 15 | logging/backend.cpp | ||
| 14 | math_util.cpp | 16 | math_util.cpp |
| 15 | mem_arena.cpp | 17 | mem_arena.cpp |
| 16 | memory_util.cpp | 18 | memory_util.cpp |
| @@ -32,6 +34,7 @@ set(HEADERS | |||
| 32 | common_funcs.h | 34 | common_funcs.h |
| 33 | common_paths.h | 35 | common_paths.h |
| 34 | common_types.h | 36 | common_types.h |
| 37 | concurrent_ring_buffer.h | ||
| 35 | console_listener.h | 38 | console_listener.h |
| 36 | cpu_detect.h | 39 | cpu_detect.h |
| 37 | debug_interface.h | 40 | debug_interface.h |
| @@ -45,6 +48,9 @@ set(HEADERS | |||
| 45 | linear_disk_cache.h | 48 | linear_disk_cache.h |
| 46 | log.h | 49 | log.h |
| 47 | log_manager.h | 50 | log_manager.h |
| 51 | logging/text_formatter.h | ||
| 52 | logging/log.h | ||
| 53 | logging/backend.h | ||
| 48 | math_util.h | 54 | math_util.h |
| 49 | mem_arena.h | 55 | mem_arena.h |
| 50 | memory_util.h | 56 | memory_util.h |
diff --git a/src/common/common_types.h b/src/common/common_types.h index fcc8b9a4e..c74c74f0f 100644 --- a/src/common/common_types.h +++ b/src/common/common_types.h | |||
| @@ -41,8 +41,6 @@ typedef std::int64_t s64; ///< 64-bit signed int | |||
| 41 | typedef float f32; ///< 32-bit floating point | 41 | typedef float f32; ///< 32-bit floating point |
| 42 | typedef double f64; ///< 64-bit floating point | 42 | typedef double f64; ///< 64-bit floating point |
| 43 | 43 | ||
| 44 | #include "common/common.h" | ||
| 45 | |||
| 46 | /// Union for fast 16-bit type casting | 44 | /// Union for fast 16-bit type casting |
| 47 | union t16 { | 45 | union t16 { |
| 48 | u8 _u8[2]; ///< 8-bit unsigned char(s) | 46 | u8 _u8[2]; ///< 8-bit unsigned char(s) |
diff --git a/src/common/concurrent_ring_buffer.h b/src/common/concurrent_ring_buffer.h new file mode 100644 index 000000000..2951d93db --- /dev/null +++ b/src/common/concurrent_ring_buffer.h | |||
| @@ -0,0 +1,164 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <condition_variable> | ||
| 9 | #include <cstdint> | ||
| 10 | #include <mutex> | ||
| 11 | #include <thread> | ||
| 12 | |||
| 13 | #include "common/common.h" // for NonCopyable | ||
| 14 | #include "common/log.h" // for _dbg_assert_ | ||
| 15 | |||
| 16 | namespace Common { | ||
| 17 | |||
| 18 | /** | ||
| 19 | * A MPMC (Multiple-Producer Multiple-Consumer) concurrent ring buffer. This data structure permits | ||
| 20 | * multiple threads to push and pop from a queue of bounded size. | ||
| 21 | */ | ||
| 22 | template <typename T, size_t ArraySize> | ||
| 23 | class ConcurrentRingBuffer : private NonCopyable { | ||
| 24 | public: | ||
| 25 | /// Value returned by the popping functions when the queue has been closed. | ||
| 26 | static const size_t QUEUE_CLOSED = -1; | ||
| 27 | |||
| 28 | ConcurrentRingBuffer() {} | ||
| 29 | |||
| 30 | ~ConcurrentRingBuffer() { | ||
| 31 | // If for whatever reason the queue wasn't completely drained, destroy the left over items. | ||
| 32 | for (size_t i = reader_index, end = writer_index; i != end; i = (i + 1) % ArraySize) { | ||
| 33 | Data()[i].~T(); | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | /** | ||
| 38 | * Pushes a value to the queue. If the queue is full, this method will block. Does nothing if | ||
| 39 | * the queue is closed. | ||
| 40 | */ | ||
| 41 | void Push(T val) { | ||
| 42 | std::unique_lock<std::mutex> lock(mutex); | ||
| 43 | if (closed) { | ||
| 44 | return; | ||
| 45 | } | ||
| 46 | |||
| 47 | // If the buffer is full, wait | ||
| 48 | writer.wait(lock, [&]{ | ||
| 49 | return (writer_index + 1) % ArraySize != reader_index; | ||
| 50 | }); | ||
| 51 | |||
| 52 | T* item = &Data()[writer_index]; | ||
| 53 | new (item) T(std::move(val)); | ||
| 54 | |||
| 55 | writer_index = (writer_index + 1) % ArraySize; | ||
| 56 | |||
| 57 | // Wake up waiting readers | ||
| 58 | lock.unlock(); | ||
| 59 | reader.notify_one(); | ||
| 60 | } | ||
| 61 | |||
| 62 | /** | ||
| 63 | * Pops up to `dest_len` items from the queue, storing them in `dest`. This function will not | ||
| 64 | * block, and might return 0 values if there are no elements in the queue when it is called. | ||
| 65 | * | ||
| 66 | * @return The number of elements stored in `dest`. If the queue has been closed, returns | ||
| 67 | * `QUEUE_CLOSED`. | ||
| 68 | */ | ||
| 69 | size_t Pop(T* dest, size_t dest_len) { | ||
| 70 | std::unique_lock<std::mutex> lock(mutex); | ||
| 71 | if (closed && !CanRead()) { | ||
| 72 | return QUEUE_CLOSED; | ||
| 73 | } | ||
| 74 | return PopInternal(dest, dest_len); | ||
| 75 | } | ||
| 76 | |||
| 77 | /** | ||
| 78 | * Pops up to `dest_len` items from the queue, storing them in `dest`. This function will block | ||
| 79 | * if there are no elements in the queue when it is called. | ||
| 80 | * | ||
| 81 | * @return The number of elements stored in `dest`. If the queue has been closed, returns | ||
| 82 | * `QUEUE_CLOSED`. | ||
| 83 | */ | ||
| 84 | size_t BlockingPop(T* dest, size_t dest_len) { | ||
| 85 | std::unique_lock<std::mutex> lock(mutex); | ||
| 86 | if (closed && !CanRead()) { | ||
| 87 | return QUEUE_CLOSED; | ||
| 88 | } | ||
| 89 | |||
| 90 | while (!CanRead()) { | ||
| 91 | reader.wait(lock); | ||
| 92 | if (closed && !CanRead()) { | ||
| 93 | return QUEUE_CLOSED; | ||
| 94 | } | ||
| 95 | } | ||
| 96 | _dbg_assert_(Common, CanRead()); | ||
| 97 | return PopInternal(dest, dest_len); | ||
| 98 | } | ||
| 99 | |||
| 100 | /** | ||
| 101 | * Closes the queue. After calling this method, `Push` operations won't have any effect, and | ||
| 102 | * `PopMany` and `PopManyBlock` will start returning `QUEUE_CLOSED`. This is intended to allow | ||
| 103 | * a graceful shutdown of all consumers. | ||
| 104 | */ | ||
| 105 | void Close() { | ||
| 106 | std::unique_lock<std::mutex> lock(mutex); | ||
| 107 | closed = true; | ||
| 108 | // We need to wake up any reader that are waiting for an item that will never come. | ||
| 109 | lock.unlock(); | ||
| 110 | reader.notify_all(); | ||
| 111 | } | ||
| 112 | |||
| 113 | /// Returns true if `Close()` has been called. | ||
| 114 | bool IsClosed() const { | ||
| 115 | return closed; | ||
| 116 | } | ||
| 117 | |||
| 118 | private: | ||
| 119 | size_t PopInternal(T* dest, size_t dest_len) { | ||
| 120 | size_t output_count = 0; | ||
| 121 | while (output_count < dest_len && CanRead()) { | ||
| 122 | _dbg_assert_(Common, CanRead()); | ||
| 123 | |||
| 124 | T* item = &Data()[reader_index]; | ||
| 125 | T out_val = std::move(*item); | ||
| 126 | item->~T(); | ||
| 127 | |||
| 128 | size_t prev_index = (reader_index + ArraySize - 1) % ArraySize; | ||
| 129 | reader_index = (reader_index + 1) % ArraySize; | ||
| 130 | if (writer_index == prev_index) { | ||
| 131 | writer.notify_one(); | ||
| 132 | } | ||
| 133 | dest[output_count++] = std::move(out_val); | ||
| 134 | } | ||
| 135 | return output_count; | ||
| 136 | } | ||
| 137 | |||
| 138 | bool CanRead() const { | ||
| 139 | return reader_index != writer_index; | ||
| 140 | } | ||
| 141 | |||
| 142 | T* Data() { | ||
| 143 | return static_cast<T*>(static_cast<void*>(&storage)); | ||
| 144 | } | ||
| 145 | |||
| 146 | /// Storage for entries | ||
| 147 | typename std::aligned_storage<ArraySize * sizeof(T), | ||
| 148 | std::alignment_of<T>::value>::type storage; | ||
| 149 | |||
| 150 | /// Data is valid in the half-open interval [reader, writer). If they are `QUEUE_CLOSED` then the | ||
| 151 | /// queue has been closed. | ||
| 152 | size_t writer_index = 0, reader_index = 0; | ||
| 153 | // True if the queue has been closed. | ||
| 154 | bool closed = false; | ||
| 155 | |||
| 156 | /// Mutex that protects the entire data structure. | ||
| 157 | std::mutex mutex; | ||
| 158 | /// Signaling wakes up reader which is waiting for storage to be non-empty. | ||
| 159 | std::condition_variable reader; | ||
| 160 | /// Signaling wakes up writer which is waiting for storage to be non-full. | ||
| 161 | std::condition_variable writer; | ||
| 162 | }; | ||
| 163 | |||
| 164 | } // namespace | ||
diff --git a/src/common/log.h b/src/common/log.h index 78f0dae4d..105db9802 100644 --- a/src/common/log.h +++ b/src/common/log.h | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include "common/common_funcs.h" | 7 | #include "common/common_funcs.h" |
| 8 | #include "common/msg_handler.h" | 8 | #include "common/msg_handler.h" |
| 9 | #include "common/logging/log.h" | ||
| 9 | 10 | ||
| 10 | #ifndef LOGGING | 11 | #ifndef LOGGING |
| 11 | #define LOGGING | 12 | #define LOGGING |
| @@ -24,45 +25,45 @@ namespace LogTypes | |||
| 24 | { | 25 | { |
| 25 | 26 | ||
| 26 | enum LOG_TYPE { | 27 | enum LOG_TYPE { |
| 27 | ACTIONREPLAY, | 28 | //ACTIONREPLAY, |
| 28 | AUDIO, | 29 | //AUDIO, |
| 29 | AUDIO_INTERFACE, | 30 | //AUDIO_INTERFACE, |
| 30 | BOOT, | 31 | BOOT, |
| 31 | COMMANDPROCESSOR, | 32 | //COMMANDPROCESSOR, |
| 32 | COMMON, | 33 | COMMON, |
| 33 | CONSOLE, | 34 | //CONSOLE, |
| 34 | CONFIG, | 35 | CONFIG, |
| 35 | DISCIO, | 36 | //DISCIO, |
| 36 | FILEMON, | 37 | //FILEMON, |
| 37 | DSPHLE, | 38 | //DSPHLE, |
| 38 | DSPLLE, | 39 | //DSPLLE, |
| 39 | DSP_MAIL, | 40 | //DSP_MAIL, |
| 40 | DSPINTERFACE, | 41 | //DSPINTERFACE, |
| 41 | DVDINTERFACE, | 42 | //DVDINTERFACE, |
| 42 | DYNA_REC, | 43 | //DYNA_REC, |
| 43 | EXPANSIONINTERFACE, | 44 | //EXPANSIONINTERFACE, |
| 44 | GDB_STUB, | 45 | //GDB_STUB, |
| 45 | ARM11, | 46 | ARM11, |
| 46 | GSP, | 47 | GSP, |
| 47 | OSHLE, | 48 | OSHLE, |
| 48 | MASTER_LOG, | 49 | MASTER_LOG, |
| 49 | MEMMAP, | 50 | MEMMAP, |
| 50 | MEMCARD_MANAGER, | 51 | //MEMCARD_MANAGER, |
| 51 | OSREPORT, | 52 | //OSREPORT, |
| 52 | PAD, | 53 | //PAD, |
| 53 | PROCESSORINTERFACE, | 54 | //PROCESSORINTERFACE, |
| 54 | PIXELENGINE, | 55 | //PIXELENGINE, |
| 55 | SERIALINTERFACE, | 56 | //SERIALINTERFACE, |
| 56 | SP1, | 57 | //SP1, |
| 57 | STREAMINGINTERFACE, | 58 | //STREAMINGINTERFACE, |
| 58 | VIDEO, | 59 | VIDEO, |
| 59 | VIDEOINTERFACE, | 60 | //VIDEOINTERFACE, |
| 60 | LOADER, | 61 | LOADER, |
| 61 | FILESYS, | 62 | FILESYS, |
| 62 | WII_IPC_DVD, | 63 | //WII_IPC_DVD, |
| 63 | WII_IPC_ES, | 64 | //WII_IPC_ES, |
| 64 | WII_IPC_FILEIO, | 65 | //WII_IPC_FILEIO, |
| 65 | WII_IPC_HID, | 66 | //WII_IPC_HID, |
| 66 | KERNEL, | 67 | KERNEL, |
| 67 | SVC, | 68 | SVC, |
| 68 | HLE, | 69 | HLE, |
| @@ -70,7 +71,7 @@ enum LOG_TYPE { | |||
| 70 | GPU, | 71 | GPU, |
| 71 | HW, | 72 | HW, |
| 72 | TIME, | 73 | TIME, |
| 73 | NETPLAY, | 74 | //NETPLAY, |
| 74 | GUI, | 75 | GUI, |
| 75 | 76 | ||
| 76 | NUMBER_OF_LOGS // Must be last | 77 | NUMBER_OF_LOGS // Must be last |
| @@ -118,12 +119,19 @@ void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, const char*file, int | |||
| 118 | GenericLog(v, t, __FILE__, __LINE__, __func__, __VA_ARGS__); \ | 119 | GenericLog(v, t, __FILE__, __LINE__, __func__, __VA_ARGS__); \ |
| 119 | } | 120 | } |
| 120 | 121 | ||
| 121 | #define OS_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LOS, __VA_ARGS__) } while (0) | 122 | //#define OS_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LOS, __VA_ARGS__) } while (0) |
| 122 | #define ERROR_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LERROR, __VA_ARGS__) } while (0) | 123 | //#define ERROR_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LERROR, __VA_ARGS__) } while (0) |
| 123 | #define WARN_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LWARNING, __VA_ARGS__) } while (0) | 124 | //#define WARN_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LWARNING, __VA_ARGS__) } while (0) |
| 124 | #define NOTICE_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LNOTICE, __VA_ARGS__) } while (0) | 125 | //#define NOTICE_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LNOTICE, __VA_ARGS__) } while (0) |
| 125 | #define INFO_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LINFO, __VA_ARGS__) } while (0) | 126 | //#define INFO_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LINFO, __VA_ARGS__) } while (0) |
| 126 | #define DEBUG_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LDEBUG, __VA_ARGS__) } while (0) | 127 | //#define DEBUG_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LDEBUG, __VA_ARGS__) } while (0) |
| 128 | |||
| 129 | #define OS_LOG(t,...) LOG_INFO(Common, __VA_ARGS__) | ||
| 130 | #define ERROR_LOG(t,...) LOG_ERROR(Common_Filesystem, __VA_ARGS__) | ||
| 131 | #define WARN_LOG(t,...) LOG_WARNING(Kernel_SVC, __VA_ARGS__) | ||
| 132 | #define NOTICE_LOG(t,...) LOG_INFO(Service, __VA_ARGS__) | ||
| 133 | #define INFO_LOG(t,...) LOG_INFO(Service_FS, __VA_ARGS__) | ||
| 134 | #define DEBUG_LOG(t,...) LOG_DEBUG(Common, __VA_ARGS__) | ||
| 127 | 135 | ||
| 128 | #if MAX_LOGLEVEL >= DEBUG_LEVEL | 136 | #if MAX_LOGLEVEL >= DEBUG_LEVEL |
| 129 | #define _dbg_assert_(_t_, _a_) \ | 137 | #define _dbg_assert_(_t_, _a_) \ |
diff --git a/src/common/log_manager.cpp b/src/common/log_manager.cpp index 128c15388..adc17444c 100644 --- a/src/common/log_manager.cpp +++ b/src/common/log_manager.cpp | |||
| @@ -30,49 +30,49 @@ LogManager::LogManager() | |||
| 30 | m_Log[LogTypes::BOOT] = new LogContainer("BOOT", "Boot"); | 30 | m_Log[LogTypes::BOOT] = new LogContainer("BOOT", "Boot"); |
| 31 | m_Log[LogTypes::COMMON] = new LogContainer("COMMON", "Common"); | 31 | m_Log[LogTypes::COMMON] = new LogContainer("COMMON", "Common"); |
| 32 | m_Log[LogTypes::CONFIG] = new LogContainer("CONFIG", "Configuration"); | 32 | m_Log[LogTypes::CONFIG] = new LogContainer("CONFIG", "Configuration"); |
| 33 | m_Log[LogTypes::DISCIO] = new LogContainer("DIO", "Disc IO"); | 33 | //m_Log[LogTypes::DISCIO] = new LogContainer("DIO", "Disc IO"); |
| 34 | m_Log[LogTypes::FILEMON] = new LogContainer("FileMon", "File Monitor"); | 34 | //m_Log[LogTypes::FILEMON] = new LogContainer("FileMon", "File Monitor"); |
| 35 | m_Log[LogTypes::PAD] = new LogContainer("PAD", "Pad"); | 35 | //m_Log[LogTypes::PAD] = new LogContainer("PAD", "Pad"); |
| 36 | m_Log[LogTypes::PIXELENGINE] = new LogContainer("PE", "PixelEngine"); | 36 | //m_Log[LogTypes::PIXELENGINE] = new LogContainer("PE", "PixelEngine"); |
| 37 | m_Log[LogTypes::COMMANDPROCESSOR] = new LogContainer("CP", "CommandProc"); | 37 | //m_Log[LogTypes::COMMANDPROCESSOR] = new LogContainer("CP", "CommandProc"); |
| 38 | m_Log[LogTypes::VIDEOINTERFACE] = new LogContainer("VI", "VideoInt"); | 38 | //m_Log[LogTypes::VIDEOINTERFACE] = new LogContainer("VI", "VideoInt"); |
| 39 | m_Log[LogTypes::SERIALINTERFACE] = new LogContainer("SI", "SerialInt"); | 39 | //m_Log[LogTypes::SERIALINTERFACE] = new LogContainer("SI", "SerialInt"); |
| 40 | m_Log[LogTypes::PROCESSORINTERFACE] = new LogContainer("PI", "ProcessorInt"); | 40 | //m_Log[LogTypes::PROCESSORINTERFACE] = new LogContainer("PI", "ProcessorInt"); |
| 41 | m_Log[LogTypes::MEMMAP] = new LogContainer("MI", "MI & memmap"); | 41 | m_Log[LogTypes::MEMMAP] = new LogContainer("MI", "MI & memmap"); |
| 42 | m_Log[LogTypes::SP1] = new LogContainer("SP1", "Serial Port 1"); | 42 | //m_Log[LogTypes::SP1] = new LogContainer("SP1", "Serial Port 1"); |
| 43 | m_Log[LogTypes::STREAMINGINTERFACE] = new LogContainer("Stream", "StreamingInt"); | 43 | //m_Log[LogTypes::STREAMINGINTERFACE] = new LogContainer("Stream", "StreamingInt"); |
| 44 | m_Log[LogTypes::DSPINTERFACE] = new LogContainer("DSP", "DSPInterface"); | 44 | //m_Log[LogTypes::DSPINTERFACE] = new LogContainer("DSP", "DSPInterface"); |
| 45 | m_Log[LogTypes::DVDINTERFACE] = new LogContainer("DVD", "DVDInterface"); | 45 | //m_Log[LogTypes::DVDINTERFACE] = new LogContainer("DVD", "DVDInterface"); |
| 46 | m_Log[LogTypes::GSP] = new LogContainer("GSP", "GSP"); | 46 | m_Log[LogTypes::GSP] = new LogContainer("GSP", "GSP"); |
| 47 | m_Log[LogTypes::EXPANSIONINTERFACE] = new LogContainer("EXI", "ExpansionInt"); | 47 | //m_Log[LogTypes::EXPANSIONINTERFACE] = new LogContainer("EXI", "ExpansionInt"); |
| 48 | m_Log[LogTypes::GDB_STUB] = new LogContainer("GDB_STUB", "GDB Stub"); | 48 | //m_Log[LogTypes::GDB_STUB] = new LogContainer("GDB_STUB", "GDB Stub"); |
| 49 | m_Log[LogTypes::AUDIO_INTERFACE] = new LogContainer("AI", "AudioInt"); | 49 | //m_Log[LogTypes::AUDIO_INTERFACE] = new LogContainer("AI", "AudioInt"); |
| 50 | m_Log[LogTypes::ARM11] = new LogContainer("ARM11", "ARM11"); | 50 | m_Log[LogTypes::ARM11] = new LogContainer("ARM11", "ARM11"); |
| 51 | m_Log[LogTypes::OSHLE] = new LogContainer("HLE", "HLE"); | 51 | m_Log[LogTypes::OSHLE] = new LogContainer("HLE", "HLE"); |
| 52 | m_Log[LogTypes::DSPHLE] = new LogContainer("DSPHLE", "DSP HLE"); | 52 | //m_Log[LogTypes::DSPHLE] = new LogContainer("DSPHLE", "DSP HLE"); |
| 53 | m_Log[LogTypes::DSPLLE] = new LogContainer("DSPLLE", "DSP LLE"); | 53 | //m_Log[LogTypes::DSPLLE] = new LogContainer("DSPLLE", "DSP LLE"); |
| 54 | m_Log[LogTypes::DSP_MAIL] = new LogContainer("DSPMails", "DSP Mails"); | 54 | //m_Log[LogTypes::DSP_MAIL] = new LogContainer("DSPMails", "DSP Mails"); |
| 55 | m_Log[LogTypes::VIDEO] = new LogContainer("Video", "Video Backend"); | 55 | m_Log[LogTypes::VIDEO] = new LogContainer("Video", "Video Backend"); |
| 56 | m_Log[LogTypes::AUDIO] = new LogContainer("Audio", "Audio Emulator"); | 56 | //m_Log[LogTypes::AUDIO] = new LogContainer("Audio", "Audio Emulator"); |
| 57 | m_Log[LogTypes::DYNA_REC] = new LogContainer("JIT", "JIT"); | 57 | //m_Log[LogTypes::DYNA_REC] = new LogContainer("JIT", "JIT"); |
| 58 | m_Log[LogTypes::CONSOLE] = new LogContainer("CONSOLE", "Dolphin Console"); | 58 | //m_Log[LogTypes::CONSOLE] = new LogContainer("CONSOLE", "Dolphin Console"); |
| 59 | m_Log[LogTypes::OSREPORT] = new LogContainer("OSREPORT", "OSReport"); | 59 | //m_Log[LogTypes::OSREPORT] = new LogContainer("OSREPORT", "OSReport"); |
| 60 | m_Log[LogTypes::TIME] = new LogContainer("Time", "Core Timing"); | 60 | m_Log[LogTypes::TIME] = new LogContainer("Time", "Core Timing"); |
| 61 | m_Log[LogTypes::LOADER] = new LogContainer("Loader", "Loader"); | 61 | m_Log[LogTypes::LOADER] = new LogContainer("Loader", "Loader"); |
| 62 | m_Log[LogTypes::FILESYS] = new LogContainer("FileSys", "File System"); | 62 | m_Log[LogTypes::FILESYS] = new LogContainer("FileSys", "File System"); |
| 63 | m_Log[LogTypes::WII_IPC_HID] = new LogContainer("WII_IPC_HID", "WII IPC HID"); | 63 | //m_Log[LogTypes::WII_IPC_HID] = new LogContainer("WII_IPC_HID", "WII IPC HID"); |
| 64 | m_Log[LogTypes::KERNEL] = new LogContainer("KERNEL", "KERNEL HLE"); | 64 | m_Log[LogTypes::KERNEL] = new LogContainer("KERNEL", "KERNEL HLE"); |
| 65 | m_Log[LogTypes::WII_IPC_DVD] = new LogContainer("WII_IPC_DVD", "WII IPC DVD"); | 65 | //m_Log[LogTypes::WII_IPC_DVD] = new LogContainer("WII_IPC_DVD", "WII IPC DVD"); |
| 66 | m_Log[LogTypes::WII_IPC_ES] = new LogContainer("WII_IPC_ES", "WII IPC ES"); | 66 | //m_Log[LogTypes::WII_IPC_ES] = new LogContainer("WII_IPC_ES", "WII IPC ES"); |
| 67 | m_Log[LogTypes::WII_IPC_FILEIO] = new LogContainer("WII_IPC_FILEIO", "WII IPC FILEIO"); | 67 | //m_Log[LogTypes::WII_IPC_FILEIO] = new LogContainer("WII_IPC_FILEIO", "WII IPC FILEIO"); |
| 68 | m_Log[LogTypes::RENDER] = new LogContainer("RENDER", "RENDER"); | 68 | m_Log[LogTypes::RENDER] = new LogContainer("RENDER", "RENDER"); |
| 69 | m_Log[LogTypes::GPU] = new LogContainer("GPU", "GPU"); | 69 | m_Log[LogTypes::GPU] = new LogContainer("GPU", "GPU"); |
| 70 | m_Log[LogTypes::SVC] = new LogContainer("SVC", "Supervisor Call HLE"); | 70 | m_Log[LogTypes::SVC] = new LogContainer("SVC", "Supervisor Call HLE"); |
| 71 | m_Log[LogTypes::HLE] = new LogContainer("HLE", "High Level Emulation"); | 71 | m_Log[LogTypes::HLE] = new LogContainer("HLE", "High Level Emulation"); |
| 72 | m_Log[LogTypes::HW] = new LogContainer("HW", "Hardware"); | 72 | m_Log[LogTypes::HW] = new LogContainer("HW", "Hardware"); |
| 73 | m_Log[LogTypes::ACTIONREPLAY] = new LogContainer("ActionReplay", "ActionReplay"); | 73 | //m_Log[LogTypes::ACTIONREPLAY] = new LogContainer("ActionReplay", "ActionReplay"); |
| 74 | m_Log[LogTypes::MEMCARD_MANAGER] = new LogContainer("MemCard Manager", "MemCard Manager"); | 74 | //m_Log[LogTypes::MEMCARD_MANAGER] = new LogContainer("MemCard Manager", "MemCard Manager"); |
| 75 | m_Log[LogTypes::NETPLAY] = new LogContainer("NETPLAY", "Netplay"); | 75 | //m_Log[LogTypes::NETPLAY] = new LogContainer("NETPLAY", "Netplay"); |
| 76 | m_Log[LogTypes::GUI] = new LogContainer("GUI", "GUI"); | 76 | m_Log[LogTypes::GUI] = new LogContainer("GUI", "GUI"); |
| 77 | 77 | ||
| 78 | m_fileLog = new FileLogListener(FileUtil::GetUserPath(F_MAINLOG_IDX).c_str()); | 78 | m_fileLog = new FileLogListener(FileUtil::GetUserPath(F_MAINLOG_IDX).c_str()); |
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 | } | ||
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h new file mode 100644 index 000000000..ae270efe8 --- /dev/null +++ b/src/common/logging/backend.h | |||
| @@ -0,0 +1,134 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <cstdarg> | ||
| 8 | #include <memory> | ||
| 9 | #include <vector> | ||
| 10 | |||
| 11 | #include "common/concurrent_ring_buffer.h" | ||
| 12 | |||
| 13 | #include "common/logging/log.h" | ||
| 14 | |||
| 15 | namespace Log { | ||
| 16 | |||
| 17 | /** | ||
| 18 | * A log entry. Log entries are store in a structured format to permit more varied output | ||
| 19 | * formatting on different frontends, as well as facilitating filtering and aggregation. | ||
| 20 | */ | ||
| 21 | struct Entry { | ||
| 22 | std::chrono::microseconds timestamp; | ||
| 23 | Class log_class; | ||
| 24 | Level log_level; | ||
| 25 | std::string location; | ||
| 26 | std::string message; | ||
| 27 | |||
| 28 | Entry() = default; | ||
| 29 | |||
| 30 | // TODO(yuriks) Use defaulted move constructors once MSVC supports them | ||
| 31 | #define MOVE(member) member(std::move(o.member)) | ||
| 32 | Entry(Entry&& o) | ||
| 33 | : MOVE(timestamp), MOVE(log_class), MOVE(log_level), | ||
| 34 | MOVE(location), MOVE(message) | ||
| 35 | {} | ||
| 36 | #undef MOVE | ||
| 37 | |||
| 38 | Entry& operator=(const Entry&& o) { | ||
| 39 | #define MOVE(member) member = std::move(o.member) | ||
| 40 | MOVE(timestamp); | ||
| 41 | MOVE(log_class); | ||
| 42 | MOVE(log_level); | ||
| 43 | MOVE(location); | ||
| 44 | MOVE(message); | ||
| 45 | #undef MOVE | ||
| 46 | return *this; | ||
| 47 | } | ||
| 48 | }; | ||
| 49 | |||
| 50 | struct ClassInfo { | ||
| 51 | Class log_class; | ||
| 52 | |||
| 53 | /** | ||
| 54 | * Total number of (direct or indirect) sub classes this class has. If any, they follow in | ||
| 55 | * sequence after this class in the class list. | ||
| 56 | */ | ||
| 57 | unsigned int num_children = 0; | ||
| 58 | |||
| 59 | ClassInfo(Class log_class) : log_class(log_class) {} | ||
| 60 | }; | ||
| 61 | |||
| 62 | /** | ||
| 63 | * Logging management class. This class has the dual purpose of acting as an exchange point between | ||
| 64 | * the logging clients and the log outputter, as well as containing reflection info about available | ||
| 65 | * log classes. | ||
| 66 | */ | ||
| 67 | class Logger { | ||
| 68 | private: | ||
| 69 | using Buffer = Common::ConcurrentRingBuffer<Entry, 16 * 1024 / sizeof(Entry)>; | ||
| 70 | |||
| 71 | public: | ||
| 72 | static const size_t QUEUE_CLOSED = Buffer::QUEUE_CLOSED; | ||
| 73 | |||
| 74 | Logger(); | ||
| 75 | |||
| 76 | /** | ||
| 77 | * Returns a list of all vector classes and subclasses. The sequence returned is a pre-order of | ||
| 78 | * classes and subclasses, which together with the `num_children` field in ClassInfo, allows | ||
| 79 | * you to recover the hierarchy. | ||
| 80 | */ | ||
| 81 | const std::vector<ClassInfo>& GetClasses() const { return all_classes; } | ||
| 82 | |||
| 83 | /** | ||
| 84 | * Returns the name of the passed log class as a C-string. Subclasses are separated by periods | ||
| 85 | * instead of underscores as in the enumeration. | ||
| 86 | */ | ||
| 87 | static const char* GetLogClassName(Class log_class); | ||
| 88 | |||
| 89 | /** | ||
| 90 | * Returns the name of the passed log level as a C-string. | ||
| 91 | */ | ||
| 92 | static const char* GetLevelName(Level log_level); | ||
| 93 | |||
| 94 | /** | ||
| 95 | * Appends a messages to the log buffer. | ||
| 96 | * @note This function is thread safe. | ||
| 97 | */ | ||
| 98 | void LogMessage(Entry entry); | ||
| 99 | |||
| 100 | /** | ||
| 101 | * Retrieves a batch of messages from the log buffer, blocking until they are available. | ||
| 102 | * @note This function is thread safe. | ||
| 103 | * | ||
| 104 | * @param out_buffer Destination buffer that will receive the log entries. | ||
| 105 | * @param buffer_len The maximum size of `out_buffer`. | ||
| 106 | * @return The number of entries stored. In case the logger is shutting down, `QUEUE_CLOSED` is | ||
| 107 | * returned, no entries are stored and the logger should shutdown. | ||
| 108 | */ | ||
| 109 | size_t GetEntries(Entry* out_buffer, size_t buffer_len); | ||
| 110 | |||
| 111 | /** | ||
| 112 | * Initiates a shutdown of the logger. This will indicate to log output clients that they | ||
| 113 | * should shutdown. | ||
| 114 | */ | ||
| 115 | void Close() { ring_buffer.Close(); } | ||
| 116 | |||
| 117 | /** | ||
| 118 | * Returns true if Close() has already been called on the Logger. | ||
| 119 | */ | ||
| 120 | bool IsClosed() const { return ring_buffer.IsClosed(); } | ||
| 121 | |||
| 122 | private: | ||
| 123 | Buffer ring_buffer; | ||
| 124 | std::vector<ClassInfo> all_classes; | ||
| 125 | }; | ||
| 126 | |||
| 127 | /// Creates a log entry by formatting the given source location, and message. | ||
| 128 | Entry CreateEntry(Class log_class, Level log_level, | ||
| 129 | const char* filename, unsigned int line_nr, const char* function, | ||
| 130 | const char* format, va_list args); | ||
| 131 | /// Initializes the default Logger. | ||
| 132 | std::shared_ptr<Logger> InitGlobalLogger(); | ||
| 133 | |||
| 134 | } | ||
diff --git a/src/common/logging/log.h b/src/common/logging/log.h new file mode 100644 index 000000000..1eec34fbb --- /dev/null +++ b/src/common/logging/log.h | |||
| @@ -0,0 +1,115 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <cassert> | ||
| 8 | #include <chrono> | ||
| 9 | #include <string> | ||
| 10 | |||
| 11 | #include "common/common_types.h" | ||
| 12 | |||
| 13 | namespace Log { | ||
| 14 | |||
| 15 | /// Specifies the severity or level of detail of the log message. | ||
| 16 | enum class Level : u8 { | ||
| 17 | Trace, ///< Extremely detailed and repetitive debugging information that is likely to | ||
| 18 | /// pollute logs. | ||
| 19 | Debug, ///< Less detailed debugging information. | ||
| 20 | Info, ///< Status information from important points during execution. | ||
| 21 | Warning, ///< Minor or potential problems found during execution of a task. | ||
| 22 | Error, ///< Major problems found during execution of a task that prevent it from being | ||
| 23 | /// completed. | ||
| 24 | Critical, ///< Major problems during execution that threathen the stability of the entire | ||
| 25 | /// application. | ||
| 26 | |||
| 27 | Count ///< Total number of logging levels | ||
| 28 | }; | ||
| 29 | |||
| 30 | typedef u8 ClassType; | ||
| 31 | |||
| 32 | /** | ||
| 33 | * Specifies the sub-system that generated the log message. | ||
| 34 | * | ||
| 35 | * @note If you add a new entry here, also add a corresponding one to `ALL_LOG_CLASSES` in log.cpp. | ||
| 36 | */ | ||
| 37 | enum class Class : ClassType { | ||
| 38 | Log, ///< Messages about the log system itself | ||
| 39 | Common, ///< Library routines | ||
| 40 | Common_Filesystem, ///< Filesystem interface library | ||
| 41 | Common_Memory, ///< Memory mapping and management functions | ||
| 42 | Core, ///< LLE emulation core | ||
| 43 | Core_ARM11, ///< ARM11 CPU core | ||
| 44 | Config, ///< Emulator configuration (including commandline) | ||
| 45 | Debug, ///< Debugging tools | ||
| 46 | Debug_Emulated, ///< Debug messages from the emulated programs | ||
| 47 | Debug_GPU, ///< GPU debugging tools | ||
| 48 | Debug_Breakpoint, ///< Logging breakpoints and watchpoints | ||
| 49 | Kernel, ///< The HLE implementation of the CTR kernel | ||
| 50 | Kernel_SVC, ///< Kernel system calls | ||
| 51 | Service, ///< HLE implementation of system services. Each major service | ||
| 52 | /// should have its own subclass. | ||
| 53 | Service_SRV, ///< The SRV (Service Directory) implementation | ||
| 54 | Service_FS, ///< The FS (Filesystem) service implementation | ||
| 55 | Service_APT, ///< The APT (Applets) service | ||
| 56 | Service_GSP, ///< The GSP (GPU control) service | ||
| 57 | Service_AC, ///< The AC (WiFi status) service | ||
| 58 | Service_PTM, ///< The PTM (Power status & misc.) service | ||
| 59 | Service_CFG, ///< The CFG (Configuration) service | ||
| 60 | Service_DSP, ///< The DSP (DSP control) service | ||
| 61 | Service_HID, ///< The HID (User input) service | ||
| 62 | HW, ///< Low-level hardware emulation | ||
| 63 | HW_Memory, ///< Memory-map and address translation | ||
| 64 | HW_GPU, ///< GPU control emulation | ||
| 65 | Frontend, ///< Emulator UI | ||
| 66 | Render, ///< Emulator video output and hardware acceleration | ||
| 67 | Render_Software, ///< Software renderer backend | ||
| 68 | Render_OpenGL, ///< OpenGL backend | ||
| 69 | Loader, ///< ROM loader | ||
| 70 | |||
| 71 | Count ///< Total number of logging classes | ||
| 72 | }; | ||
| 73 | |||
| 74 | /** | ||
| 75 | * Level below which messages are simply discarded without buffering regardless of the display | ||
| 76 | * settings. | ||
| 77 | */ | ||
| 78 | const Level MINIMUM_LEVEL = | ||
| 79 | #ifdef _DEBUG | ||
| 80 | Level::Trace; | ||
| 81 | #else | ||
| 82 | Level::Debug; | ||
| 83 | #endif | ||
| 84 | |||
| 85 | /** | ||
| 86 | * Logs a message to the global logger. This proxy exists to avoid exposing the details of the | ||
| 87 | * Logger class, including the ConcurrentRingBuffer template, to all files that desire to log | ||
| 88 | * messages, reducing unecessary recompilations. | ||
| 89 | */ | ||
| 90 | void LogMessage(Class log_class, Level log_level, | ||
| 91 | const char* filename, unsigned int line_nr, const char* function, | ||
| 92 | #ifdef _MSC_VER | ||
| 93 | _Printf_format_string_ | ||
| 94 | #endif | ||
| 95 | const char* format, ...) | ||
| 96 | #ifdef __GNUC__ | ||
| 97 | __attribute__((format(printf, 6, 7))) | ||
| 98 | #endif | ||
| 99 | ; | ||
| 100 | |||
| 101 | } // namespace Log | ||
| 102 | |||
| 103 | #define LOG_GENERIC(log_class, log_level, ...) \ | ||
| 104 | do { \ | ||
| 105 | if (::Log::Level::log_level >= ::Log::MINIMUM_LEVEL) \ | ||
| 106 | ::Log::LogMessage(::Log::Class::log_class, ::Log::Level::log_level, \ | ||
| 107 | __FILE__, __LINE__, __func__, __VA_ARGS__); \ | ||
| 108 | } while (0) | ||
| 109 | |||
| 110 | #define LOG_TRACE( log_class, ...) LOG_GENERIC(log_class, Trace, __VA_ARGS__) | ||
| 111 | #define LOG_DEBUG( log_class, ...) LOG_GENERIC(log_class, Debug, __VA_ARGS__) | ||
| 112 | #define LOG_INFO( log_class, ...) LOG_GENERIC(log_class, Info, __VA_ARGS__) | ||
| 113 | #define LOG_WARNING( log_class, ...) LOG_GENERIC(log_class, Warning, __VA_ARGS__) | ||
| 114 | #define LOG_ERROR( log_class, ...) LOG_GENERIC(log_class, Error, __VA_ARGS__) | ||
| 115 | #define LOG_CRITICAL(log_class, ...) LOG_GENERIC(log_class, Critical, __VA_ARGS__) | ||
diff --git a/src/common/logging/text_formatter.cpp b/src/common/logging/text_formatter.cpp new file mode 100644 index 000000000..01c355bb6 --- /dev/null +++ b/src/common/logging/text_formatter.cpp | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <array> | ||
| 6 | #include <cstdio> | ||
| 7 | |||
| 8 | #include "common/logging/backend.h" | ||
| 9 | #include "common/logging/log.h" | ||
| 10 | #include "common/logging/text_formatter.h" | ||
| 11 | |||
| 12 | namespace Log { | ||
| 13 | |||
| 14 | void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len) { | ||
| 15 | unsigned int time_seconds = static_cast<unsigned int>(entry.timestamp.count() / 1000000); | ||
| 16 | unsigned int time_fractional = static_cast<unsigned int>(entry.timestamp.count() % 1000000); | ||
| 17 | |||
| 18 | const char* class_name = Logger::GetLogClassName(entry.log_class); | ||
| 19 | const char* level_name = Logger::GetLevelName(entry.log_level); | ||
| 20 | |||
| 21 | snprintf(out_text, text_len, "[%4u.%06u] %s <%s> %s: %s", | ||
| 22 | time_seconds, time_fractional, class_name, level_name, | ||
| 23 | entry.location.c_str(), entry.message.c_str()); | ||
| 24 | } | ||
| 25 | |||
| 26 | void PrintMessage(const Entry& entry) { | ||
| 27 | std::array<char, 4 * 1024> format_buffer; | ||
| 28 | FormatLogMessage(entry, format_buffer.data(), format_buffer.size()); | ||
| 29 | fputs(format_buffer.data(), stderr); | ||
| 30 | fputc('\n', stderr); | ||
| 31 | } | ||
| 32 | |||
| 33 | void TextLoggingLoop(std::shared_ptr<Logger> logger) { | ||
| 34 | std::array<Entry, 256> entry_buffer; | ||
| 35 | |||
| 36 | while (true) { | ||
| 37 | size_t num_entries = logger->GetEntries(entry_buffer.data(), entry_buffer.size()); | ||
| 38 | if (num_entries == Logger::QUEUE_CLOSED) { | ||
| 39 | break; | ||
| 40 | } | ||
| 41 | for (size_t i = 0; i < num_entries; ++i) { | ||
| 42 | PrintMessage(entry_buffer[i]); | ||
| 43 | } | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | } | ||
diff --git a/src/common/logging/text_formatter.h b/src/common/logging/text_formatter.h new file mode 100644 index 000000000..6c2a6f1ea --- /dev/null +++ b/src/common/logging/text_formatter.h | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <cstddef> | ||
| 8 | #include <memory> | ||
| 9 | |||
| 10 | namespace Log { | ||
| 11 | |||
| 12 | class Logger; | ||
| 13 | struct Entry; | ||
| 14 | |||
| 15 | /// Formats a log entry into the provided text buffer. | ||
| 16 | void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len); | ||
| 17 | /// Formats and prints a log entry to stderr. | ||
| 18 | void PrintMessage(const Entry& entry); | ||
| 19 | |||
| 20 | /** | ||
| 21 | * Logging loop that repeatedly reads messages from the provided logger and prints them to the | ||
| 22 | * console. It is the baseline barebones log outputter. | ||
| 23 | */ | ||
| 24 | void TextLoggingLoop(std::shared_ptr<Logger> logger); | ||
| 25 | |||
| 26 | } | ||
diff --git a/src/common/thread.h b/src/common/thread.h index be9b5cbe2..8c36d3f07 100644 --- a/src/common/thread.h +++ b/src/common/thread.h | |||
| @@ -21,6 +21,7 @@ | |||
| 21 | //for gettimeofday and struct time(spec|val) | 21 | //for gettimeofday and struct time(spec|val) |
| 22 | #include <time.h> | 22 | #include <time.h> |
| 23 | #include <sys/time.h> | 23 | #include <sys/time.h> |
| 24 | #include <unistd.h> | ||
| 24 | #endif | 25 | #endif |
| 25 | 26 | ||
| 26 | namespace Common | 27 | namespace Common |
diff --git a/src/core/hle/config_mem.cpp b/src/core/hle/config_mem.cpp index c7cf5b1d3..1c9b89227 100644 --- a/src/core/hle/config_mem.cpp +++ b/src/core/hle/config_mem.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/common_types.h" | 5 | #include "common/common_types.h" |
| 6 | #include "common/log.h" | ||
| 6 | 7 | ||
| 7 | #include "core/hle/config_mem.h" | 8 | #include "core/hle/config_mem.h" |
| 8 | 9 | ||