summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/citra/citra.cpp16
-rw-r--r--src/citra_qt/main.cpp18
-rw-r--r--src/common/CMakeLists.txt6
-rw-r--r--src/common/common_types.h2
-rw-r--r--src/common/concurrent_ring_buffer.h164
-rw-r--r--src/common/log.h78
-rw-r--r--src/common/log_manager.cpp58
-rw-r--r--src/common/logging/backend.cpp151
-rw-r--r--src/common/logging/backend.h134
-rw-r--r--src/common/logging/log.h115
-rw-r--r--src/common/logging/text_formatter.cpp47
-rw-r--r--src/common/logging/text_formatter.h26
-rw-r--r--src/common/thread.h1
-rw-r--r--src/core/hle/config_mem.cpp1
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
17int __cdecl main(int argc, char **argv) { 21int __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
37GMainWindow::GMainWindow() 43GMainWindow::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
272int __cdecl main(int argc, char* argv[]) 277int __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
41typedef float f32; ///< 32-bit floating point 41typedef float f32; ///< 32-bit floating point
42typedef double f64; ///< 64-bit floating point 42typedef 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
47union t16 { 45union 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
16namespace 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 */
22template <typename T, size_t ArraySize>
23class ConcurrentRingBuffer : private NonCopyable {
24public:
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
118private:
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
26enum LOG_TYPE { 27enum 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
13namespace Log {
14
15static 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
51Logger::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...
72const 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
83const 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
97void Logger::LogMessage(Entry entry) {
98 ring_buffer.Push(std::move(entry));
99}
100
101size_t Logger::GetEntries(Entry* out_buffer, size_t buffer_len) {
102 return ring_buffer.BlockingPop(out_buffer, buffer_len);
103}
104
105std::shared_ptr<Logger> InitGlobalLogger() {
106 global_logger = std::make_shared<Logger>();
107 return global_logger;
108}
109
110Entry 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
134void 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
15namespace 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 */
21struct 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
50struct 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 */
67class Logger {
68private:
69 using Buffer = Common::ConcurrentRingBuffer<Entry, 16 * 1024 / sizeof(Entry)>;
70
71public:
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
122private:
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.
128Entry 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.
132std::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
13namespace Log {
14
15/// Specifies the severity or level of detail of the log message.
16enum 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
30typedef 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 */
37enum 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 */
78const 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 */
90void 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
12namespace Log {
13
14void 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
26void 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
33void 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
10namespace Log {
11
12class Logger;
13struct Entry;
14
15/// Formats a log entry into the provided text buffer.
16void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len);
17/// Formats and prints a log entry to stderr.
18void 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 */
24void 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
26namespace Common 27namespace 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