summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/CMakeLists.txt13
-rw-r--r--src/common/break_points.cpp2
-rw-r--r--src/common/chunk_file.h35
-rw-r--r--src/common/common_funcs.h3
-rw-r--r--src/common/common_types.h2
-rw-r--r--src/common/concurrent_ring_buffer.h164
-rw-r--r--src/common/console_listener.cpp319
-rw-r--r--src/common/console_listener.h38
-rw-r--r--src/common/file_util.cpp84
-rw-r--r--src/common/log.h125
-rw-r--r--src/common/log_manager.cpp198
-rw-r--r--src/common/log_manager.h166
-rw-r--r--src/common/logging/backend.cpp151
-rw-r--r--src/common/logging/backend.h134
-rw-r--r--src/common/logging/filter.cpp132
-rw-r--r--src/common/logging/filter.h63
-rw-r--r--src/common/logging/log.h115
-rw-r--r--src/common/logging/text_formatter.cpp126
-rw-r--r--src/common/logging/text_formatter.h39
-rw-r--r--src/common/mem_arena.cpp14
-rw-r--r--src/common/memory_util.cpp2
-rw-r--r--src/common/msg_handler.cpp2
-rw-r--r--src/common/scope_exit.h37
-rw-r--r--src/common/string_util.cpp10
-rw-r--r--src/common/string_util.h15
-rw-r--r--src/common/thread.h1
26 files changed, 1078 insertions, 912 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 9d5a90762..15989708d 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -3,14 +3,15 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOU
3 3
4set(SRCS 4set(SRCS
5 break_points.cpp 5 break_points.cpp
6 console_listener.cpp
7 emu_window.cpp 6 emu_window.cpp
8 extended_trace.cpp 7 extended_trace.cpp
9 file_search.cpp 8 file_search.cpp
10 file_util.cpp 9 file_util.cpp
11 hash.cpp 10 hash.cpp
12 key_map.cpp 11 key_map.cpp
13 log_manager.cpp 12 logging/filter.cpp
13 logging/text_formatter.cpp
14 logging/backend.cpp
14 math_util.cpp 15 math_util.cpp
15 mem_arena.cpp 16 mem_arena.cpp
16 memory_util.cpp 17 memory_util.cpp
@@ -32,7 +33,7 @@ set(HEADERS
32 common_funcs.h 33 common_funcs.h
33 common_paths.h 34 common_paths.h
34 common_types.h 35 common_types.h
35 console_listener.h 36 concurrent_ring_buffer.h
36 cpu_detect.h 37 cpu_detect.h
37 debug_interface.h 38 debug_interface.h
38 emu_window.h 39 emu_window.h
@@ -44,13 +45,17 @@ set(HEADERS
44 key_map.h 45 key_map.h
45 linear_disk_cache.h 46 linear_disk_cache.h
46 log.h 47 log.h
47 log_manager.h 48 logging/text_formatter.h
49 logging/filter.h
50 logging/log.h
51 logging/backend.h
48 math_util.h 52 math_util.h
49 mem_arena.h 53 mem_arena.h
50 memory_util.h 54 memory_util.h
51 msg_handler.h 55 msg_handler.h
52 platform.h 56 platform.h
53 scm_rev.h 57 scm_rev.h
58 scope_exit.h
54 string_util.h 59 string_util.h
55 swap.h 60 swap.h
56 symbols.h 61 symbols.h
diff --git a/src/common/break_points.cpp b/src/common/break_points.cpp
index 25528b864..587dbf40a 100644
--- a/src/common/break_points.cpp
+++ b/src/common/break_points.cpp
@@ -180,7 +180,7 @@ void TMemCheck::Action(DebugInterface *debug_interface, u32 iValue, u32 addr,
180 { 180 {
181 if (Log) 181 if (Log)
182 { 182 {
183 INFO_LOG(MEMMAP, "CHK %08x (%s) %s%i %0*x at %08x (%s)", 183 LOG_DEBUG(Debug_Breakpoint, "CHK %08x (%s) %s%i %0*x at %08x (%s)",
184 pc, debug_interface->getDescription(pc).c_str(), 184 pc, debug_interface->getDescription(pc).c_str(),
185 write ? "Write" : "Read", size*8, size*2, iValue, addr, 185 write ? "Write" : "Read", size*8, size*2, iValue, addr,
186 debug_interface->getDescription(addr).c_str() 186 debug_interface->getDescription(addr).c_str()
diff --git a/src/common/chunk_file.h b/src/common/chunk_file.h
index 32af74594..39a14dc81 100644
--- a/src/common/chunk_file.h
+++ b/src/common/chunk_file.h
@@ -154,7 +154,7 @@ public:
154 Do(foundVersion); 154 Do(foundVersion);
155 155
156 if (error == ERROR_FAILURE || foundVersion < minVer || foundVersion > ver) { 156 if (error == ERROR_FAILURE || foundVersion < minVer || foundVersion > ver) {
157 WARN_LOG(COMMON, "Savestate failure: wrong version %d found for %s", foundVersion, title); 157 LOG_ERROR(Common, "Savestate failure: wrong version %d found for %s", foundVersion, title);
158 SetError(ERROR_FAILURE); 158 SetError(ERROR_FAILURE);
159 return PointerWrapSection(*this, -1, title); 159 return PointerWrapSection(*this, -1, title);
160 } 160 }
@@ -178,7 +178,14 @@ public:
178 case MODE_READ: if (memcmp(data, *ptr, size) != 0) return false; break; 178 case MODE_READ: if (memcmp(data, *ptr, size) != 0) return false; break;
179 case MODE_WRITE: memcpy(*ptr, data, size); break; 179 case MODE_WRITE: memcpy(*ptr, data, size); break;
180 case MODE_MEASURE: break; // MODE_MEASURE - don't need to do anything 180 case MODE_MEASURE: break; // MODE_MEASURE - don't need to do anything
181 case MODE_VERIFY: for(int i = 0; i < size; i++) _dbg_assert_msg_(COMMON, ((u8*)data)[i] == (*ptr)[i], "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], (*ptr)[i], (*ptr)[i], &(*ptr)[i]); break; 181 case MODE_VERIFY:
182 for (int i = 0; i < size; i++) {
183 _dbg_assert_msg_(Common, ((u8*)data)[i] == (*ptr)[i],
184 "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n",
185 ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i],
186 (*ptr)[i], (*ptr)[i], &(*ptr)[i]);
187 }
188 break;
182 default: break; // throw an error? 189 default: break; // throw an error?
183 } 190 }
184 (*ptr) += size; 191 (*ptr) += size;
@@ -191,7 +198,14 @@ public:
191 case MODE_READ: memcpy(data, *ptr, size); break; 198 case MODE_READ: memcpy(data, *ptr, size); break;
192 case MODE_WRITE: memcpy(*ptr, data, size); break; 199 case MODE_WRITE: memcpy(*ptr, data, size); break;
193 case MODE_MEASURE: break; // MODE_MEASURE - don't need to do anything 200 case MODE_MEASURE: break; // MODE_MEASURE - don't need to do anything
194 case MODE_VERIFY: for(int i = 0; i < size; i++) _dbg_assert_msg_(COMMON, ((u8*)data)[i] == (*ptr)[i], "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], (*ptr)[i], (*ptr)[i], &(*ptr)[i]); break; 201 case MODE_VERIFY:
202 for (int i = 0; i < size; i++) {
203 _dbg_assert_msg_(Common, ((u8*)data)[i] == (*ptr)[i],
204 "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n",
205 ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i],
206 (*ptr)[i], (*ptr)[i], &(*ptr)[i]);
207 }
208 break;
195 default: break; // throw an error? 209 default: break; // throw an error?
196 } 210 }
197 (*ptr) += size; 211 (*ptr) += size;
@@ -476,7 +490,7 @@ public:
476 break; 490 break;
477 491
478 default: 492 default:
479 ERROR_LOG(COMMON, "Savestate error: invalid mode %d.", mode); 493 LOG_ERROR(Common, "Savestate error: invalid mode %d.", mode);
480 } 494 }
481 } 495 }
482 496
@@ -490,7 +504,12 @@ public:
490 case MODE_READ: x = (char*)*ptr; break; 504 case MODE_READ: x = (char*)*ptr; break;
491 case MODE_WRITE: memcpy(*ptr, x.c_str(), stringLen); break; 505 case MODE_WRITE: memcpy(*ptr, x.c_str(), stringLen); break;
492 case MODE_MEASURE: break; 506 case MODE_MEASURE: break;
493 case MODE_VERIFY: _dbg_assert_msg_(COMMON, !strcmp(x.c_str(), (char*)*ptr), "Savestate verification failure: \"%s\" != \"%s\" (at %p).\n", x.c_str(), (char*)*ptr, ptr); break; 507 case MODE_VERIFY:
508 _dbg_assert_msg_(Common,
509 !strcmp(x.c_str(), (char*)*ptr),
510 "Savestate verification failure: \"%s\" != \"%s\" (at %p).\n",
511 x.c_str(), (char*)*ptr, ptr);
512 break;
494 } 513 }
495 (*ptr) += stringLen; 514 (*ptr) += stringLen;
496 } 515 }
@@ -504,7 +523,11 @@ public:
504 case MODE_READ: x = (wchar_t*)*ptr; break; 523 case MODE_READ: x = (wchar_t*)*ptr; break;
505 case MODE_WRITE: memcpy(*ptr, x.c_str(), stringLen); break; 524 case MODE_WRITE: memcpy(*ptr, x.c_str(), stringLen); break;
506 case MODE_MEASURE: break; 525 case MODE_MEASURE: break;
507 case MODE_VERIFY: _dbg_assert_msg_(COMMON, x == (wchar_t*)*ptr, "Savestate verification failure: \"%ls\" != \"%ls\" (at %p).\n", x.c_str(), (wchar_t*)*ptr, ptr); break; 526 case MODE_VERIFY:
527 _dbg_assert_msg_(Common, x == (wchar_t*)*ptr,
528 "Savestate verification failure: \"%ls\" != \"%ls\" (at %p).\n",
529 x.c_str(), (wchar_t*)*ptr, ptr);
530 break;
508 } 531 }
509 (*ptr) += stringLen; 532 (*ptr) += stringLen;
510 } 533 }
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index db041780a..67b3679b0 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include "common_types.h" 7#include "common_types.h"
8#include <cstdlib>
8 9
9#ifdef _WIN32 10#ifdef _WIN32
10#define SLEEP(x) Sleep(x) 11#define SLEEP(x) Sleep(x)
@@ -39,8 +40,6 @@ template<> struct CompileTimeAssert<true> {};
39#include <sys/endian.h> 40#include <sys/endian.h>
40#endif 41#endif
41 42
42#include "common_types.h"
43
44// go to debugger mode 43// go to debugger mode
45 #ifdef GEKKO 44 #ifdef GEKKO
46 #define Crash() 45 #define Crash()
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/console_listener.cpp b/src/common/console_listener.cpp
deleted file mode 100644
index b6042796d..000000000
--- a/src/common/console_listener.cpp
+++ /dev/null
@@ -1,319 +0,0 @@
1// Copyright 2013 Dolphin Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include <algorithm>
6
7#ifdef _WIN32
8#include <windows.h>
9#include <array>
10#endif
11
12#include "common/common.h"
13#include "common/log_manager.h" // Common
14#include "common/console_listener.h" // Common
15
16ConsoleListener::ConsoleListener()
17{
18#ifdef _WIN32
19 hConsole = nullptr;
20 bUseColor = true;
21#else
22 bUseColor = isatty(fileno(stdout));
23#endif
24}
25
26ConsoleListener::~ConsoleListener()
27{
28 Close();
29}
30
31// 100, 100, "Dolphin Log Console"
32// Open console window - width and height is the size of console window
33// Name is the window title
34void ConsoleListener::Open(bool Hidden, int Width, int Height, const char *Title)
35{
36#ifdef _WIN32
37 if (!GetConsoleWindow())
38 {
39 // Open the console window and create the window handle for GetStdHandle()
40 AllocConsole();
41 // Hide
42 if (Hidden) ShowWindow(GetConsoleWindow(), SW_HIDE);
43 // Save the window handle that AllocConsole() created
44 hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
45 // Set the console window title
46 SetConsoleTitle(Common::UTF8ToTStr(Title).c_str());
47 // Set letter space
48 LetterSpace(80, 4000);
49 //MoveWindow(GetConsoleWindow(), 200,200, 800,800, true);
50 }
51 else
52 {
53 hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
54 }
55#endif
56}
57
58void ConsoleListener::UpdateHandle()
59{
60#ifdef _WIN32
61 hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
62#endif
63}
64
65// Close the console window and close the eventual file handle
66void ConsoleListener::Close()
67{
68#ifdef _WIN32
69 if (hConsole == nullptr)
70 return;
71 FreeConsole();
72 hConsole = nullptr;
73#else
74 fflush(nullptr);
75#endif
76}
77
78bool ConsoleListener::IsOpen()
79{
80#ifdef _WIN32
81 return (hConsole != nullptr);
82#else
83 return true;
84#endif
85}
86
87/*
88 LetterSpace: SetConsoleScreenBufferSize and SetConsoleWindowInfo are
89 dependent on each other, that's the reason for the additional checks.
90*/
91void ConsoleListener::BufferWidthHeight(int BufferWidth, int BufferHeight, int ScreenWidth, int ScreenHeight, bool BufferFirst)
92{
93#ifdef _WIN32
94 BOOL SB, SW;
95 if (BufferFirst)
96 {
97 // Change screen buffer size
98 COORD Co = {BufferWidth, BufferHeight};
99 SB = SetConsoleScreenBufferSize(hConsole, Co);
100 // Change the screen buffer window size
101 SMALL_RECT coo = {0,0,ScreenWidth, ScreenHeight}; // top, left, right, bottom
102 SW = SetConsoleWindowInfo(hConsole, TRUE, &coo);
103 }
104 else
105 {
106 // Change the screen buffer window size
107 SMALL_RECT coo = {0,0, ScreenWidth, ScreenHeight}; // top, left, right, bottom
108 SW = SetConsoleWindowInfo(hConsole, TRUE, &coo);
109 // Change screen buffer size
110 COORD Co = {BufferWidth, BufferHeight};
111 SB = SetConsoleScreenBufferSize(hConsole, Co);
112 }
113#endif
114}
115void ConsoleListener::LetterSpace(int Width, int Height)
116{
117#ifdef _WIN32
118 // Get console info
119 CONSOLE_SCREEN_BUFFER_INFO ConInfo;
120 GetConsoleScreenBufferInfo(hConsole, &ConInfo);
121
122 //
123 int OldBufferWidth = ConInfo.dwSize.X;
124 int OldBufferHeight = ConInfo.dwSize.Y;
125 int OldScreenWidth = (ConInfo.srWindow.Right - ConInfo.srWindow.Left);
126 int OldScreenHeight = (ConInfo.srWindow.Bottom - ConInfo.srWindow.Top);
127 //
128 int NewBufferWidth = Width;
129 int NewBufferHeight = Height;
130 int NewScreenWidth = NewBufferWidth - 1;
131 int NewScreenHeight = OldScreenHeight;
132
133 // Width
134 BufferWidthHeight(NewBufferWidth, OldBufferHeight, NewScreenWidth, OldScreenHeight, (NewBufferWidth > OldScreenWidth-1));
135 // Height
136 BufferWidthHeight(NewBufferWidth, NewBufferHeight, NewScreenWidth, NewScreenHeight, (NewBufferHeight > OldScreenHeight-1));
137
138 // Resize the window too
139 //MoveWindow(GetConsoleWindow(), 200,200, (Width*8 + 50),(NewScreenHeight*12 + 200), true);
140#endif
141}
142#ifdef _WIN32
143COORD ConsoleListener::GetCoordinates(int BytesRead, int BufferWidth)
144{
145 COORD Ret = {0, 0};
146 // Full rows
147 int Step = (int)floor((float)BytesRead / (float)BufferWidth);
148 Ret.Y += Step;
149 // Partial row
150 Ret.X = BytesRead - (BufferWidth * Step);
151 return Ret;
152}
153#endif
154void ConsoleListener::PixelSpace(int Left, int Top, int Width, int Height, bool Resize)
155{
156#ifdef _WIN32
157 // Check size
158 if (Width < 8 || Height < 12) return;
159
160 bool DBef = true;
161 bool DAft = true;
162 std::string SLog = "";
163
164 const HWND hWnd = GetConsoleWindow();
165 const HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
166
167 // Get console info
168 CONSOLE_SCREEN_BUFFER_INFO ConInfo;
169 GetConsoleScreenBufferInfo(hConsole, &ConInfo);
170 DWORD BufferSize = ConInfo.dwSize.X * ConInfo.dwSize.Y;
171
172 // ---------------------------------------------------------------------
173 // Save the current text
174 // ------------------------
175 DWORD cCharsRead = 0;
176 COORD coordScreen = { 0, 0 };
177
178 static const int MAX_BYTES = 1024 * 16;
179
180 std::vector<std::array<TCHAR, MAX_BYTES>> Str;
181 std::vector<std::array<WORD, MAX_BYTES>> Attr;
182
183 // ReadConsoleOutputAttribute seems to have a limit at this level
184 static const int ReadBufferSize = MAX_BYTES - 32;
185
186 DWORD cAttrRead = ReadBufferSize;
187 DWORD BytesRead = 0;
188 while (BytesRead < BufferSize)
189 {
190 Str.resize(Str.size() + 1);
191 if (!ReadConsoleOutputCharacter(hConsole, Str.back().data(), ReadBufferSize, coordScreen, &cCharsRead))
192 SLog += Common::StringFromFormat("WriteConsoleOutputCharacter error");
193
194 Attr.resize(Attr.size() + 1);
195 if (!ReadConsoleOutputAttribute(hConsole, Attr.back().data(), ReadBufferSize, coordScreen, &cAttrRead))
196 SLog += Common::StringFromFormat("WriteConsoleOutputAttribute error");
197
198 // Break on error
199 if (cAttrRead == 0) break;
200 BytesRead += cAttrRead;
201 coordScreen = GetCoordinates(BytesRead, ConInfo.dwSize.X);
202 }
203 // Letter space
204 int LWidth = (int)(floor((float)Width / 8.0f) - 1.0f);
205 int LHeight = (int)(floor((float)Height / 12.0f) - 1.0f);
206 int LBufWidth = LWidth + 1;
207 int LBufHeight = (int)floor((float)BufferSize / (float)LBufWidth);
208 // Change screen buffer size
209 LetterSpace(LBufWidth, LBufHeight);
210
211
212 ClearScreen(true);
213 coordScreen.Y = 0;
214 coordScreen.X = 0;
215 DWORD cCharsWritten = 0;
216
217 int BytesWritten = 0;
218 DWORD cAttrWritten = 0;
219 for (size_t i = 0; i < Attr.size(); i++)
220 {
221 if (!WriteConsoleOutputCharacter(hConsole, Str[i].data(), ReadBufferSize, coordScreen, &cCharsWritten))
222 SLog += Common::StringFromFormat("WriteConsoleOutputCharacter error");
223 if (!WriteConsoleOutputAttribute(hConsole, Attr[i].data(), ReadBufferSize, coordScreen, &cAttrWritten))
224 SLog += Common::StringFromFormat("WriteConsoleOutputAttribute error");
225
226 BytesWritten += cAttrWritten;
227 coordScreen = GetCoordinates(BytesWritten, LBufWidth);
228 }
229
230 const int OldCursor = ConInfo.dwCursorPosition.Y * ConInfo.dwSize.X + ConInfo.dwCursorPosition.X;
231 COORD Coo = GetCoordinates(OldCursor, LBufWidth);
232 SetConsoleCursorPosition(hConsole, Coo);
233
234 if (SLog.length() > 0) Log(LogTypes::LNOTICE, SLog.c_str());
235
236 // Resize the window too
237 if (Resize) MoveWindow(GetConsoleWindow(), Left,Top, (Width + 100),Height, true);
238#endif
239}
240
241void ConsoleListener::Log(LogTypes::LOG_LEVELS Level, const char *Text)
242{
243#if defined(_WIN32)
244 WORD Color;
245
246 switch (Level)
247 {
248 case OS_LEVEL: // light yellow
249 Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
250 break;
251 case NOTICE_LEVEL: // light green
252 Color = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
253 break;
254 case ERROR_LEVEL: // light red
255 Color = FOREGROUND_RED | FOREGROUND_INTENSITY;
256 break;
257 case WARNING_LEVEL: // light purple
258 Color = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
259 break;
260 case INFO_LEVEL: // cyan
261 Color = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
262 break;
263 case DEBUG_LEVEL: // gray
264 Color = FOREGROUND_INTENSITY;
265 break;
266 default: // off-white
267 Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
268 break;
269 }
270 SetConsoleTextAttribute(hConsole, Color);
271 printf(Text);
272#else
273 char ColorAttr[16] = "";
274 char ResetAttr[16] = "";
275
276 if (bUseColor)
277 {
278 strcpy(ResetAttr, "\033[0m");
279 switch (Level)
280 {
281 case NOTICE_LEVEL: // light green
282 strcpy(ColorAttr, "\033[92m");
283 break;
284 case ERROR_LEVEL: // light red
285 strcpy(ColorAttr, "\033[91m");
286 break;
287 case WARNING_LEVEL: // light yellow
288 strcpy(ColorAttr, "\033[93m");
289 break;
290 default:
291 break;
292 }
293 }
294 fprintf(stderr, "%s%s%s", ColorAttr, Text, ResetAttr);
295#endif
296}
297// Clear console screen
298void ConsoleListener::ClearScreen(bool Cursor)
299{
300#if defined(_WIN32)
301 COORD coordScreen = { 0, 0 };
302 DWORD cCharsWritten;
303 CONSOLE_SCREEN_BUFFER_INFO csbi;
304 DWORD dwConSize;
305
306 HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
307
308 GetConsoleScreenBufferInfo(hConsole, &csbi);
309 dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
310 // Write space to the entire console
311 FillConsoleOutputCharacter(hConsole, TEXT(' '), dwConSize, coordScreen, &cCharsWritten);
312 GetConsoleScreenBufferInfo(hConsole, &csbi);
313 FillConsoleOutputAttribute(hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten);
314 // Reset cursor
315 if (Cursor) SetConsoleCursorPosition(hConsole, coordScreen);
316#endif
317}
318
319
diff --git a/src/common/console_listener.h b/src/common/console_listener.h
deleted file mode 100644
index ebd90a105..000000000
--- a/src/common/console_listener.h
+++ /dev/null
@@ -1,38 +0,0 @@
1// Copyright 2013 Dolphin Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/log_manager.h"
8
9#ifdef _WIN32
10#include <windows.h>
11#endif
12
13class ConsoleListener : public LogListener
14{
15public:
16 ConsoleListener();
17 ~ConsoleListener();
18
19 void Open(bool Hidden = false, int Width = 100, int Height = 100, const char * Name = "Console");
20 void UpdateHandle();
21 void Close();
22 bool IsOpen();
23 void LetterSpace(int Width, int Height);
24 void BufferWidthHeight(int BufferWidth, int BufferHeight, int ScreenWidth, int ScreenHeight, bool BufferFirst);
25 void PixelSpace(int Left, int Top, int Width, int Height, bool);
26#ifdef _WIN32
27 COORD GetCoordinates(int BytesRead, int BufferWidth);
28#endif
29 void Log(LogTypes::LOG_LEVELS, const char *Text) override;
30 void ClearScreen(bool Cursor = true);
31
32private:
33#ifdef _WIN32
34 HWND GetHwnd(void);
35 HANDLE hConsole;
36#endif
37 bool bUseColor;
38};
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 7579d8c0f..88c46c117 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -88,7 +88,7 @@ bool IsDirectory(const std::string &filename)
88#endif 88#endif
89 89
90 if (result < 0) { 90 if (result < 0) {
91 WARN_LOG(COMMON, "IsDirectory: stat failed on %s: %s", 91 LOG_WARNING(Common_Filesystem, "stat failed on %s: %s",
92 filename.c_str(), GetLastErrorMsg()); 92 filename.c_str(), GetLastErrorMsg());
93 return false; 93 return false;
94 } 94 }
@@ -100,33 +100,33 @@ bool IsDirectory(const std::string &filename)
100// Doesn't supports deleting a directory 100// Doesn't supports deleting a directory
101bool Delete(const std::string &filename) 101bool Delete(const std::string &filename)
102{ 102{
103 INFO_LOG(COMMON, "Delete: file %s", filename.c_str()); 103 LOG_INFO(Common_Filesystem, "file %s", filename.c_str());
104 104
105 // Return true because we care about the file no 105 // Return true because we care about the file no
106 // being there, not the actual delete. 106 // being there, not the actual delete.
107 if (!Exists(filename)) 107 if (!Exists(filename))
108 { 108 {
109 WARN_LOG(COMMON, "Delete: %s does not exist", filename.c_str()); 109 LOG_WARNING(Common_Filesystem, "%s does not exist", filename.c_str());
110 return true; 110 return true;
111 } 111 }
112 112
113 // We can't delete a directory 113 // We can't delete a directory
114 if (IsDirectory(filename)) 114 if (IsDirectory(filename))
115 { 115 {
116 WARN_LOG(COMMON, "Delete failed: %s is a directory", filename.c_str()); 116 LOG_ERROR(Common_Filesystem, "Failed: %s is a directory", filename.c_str());
117 return false; 117 return false;
118 } 118 }
119 119
120#ifdef _WIN32 120#ifdef _WIN32
121 if (!DeleteFile(Common::UTF8ToTStr(filename).c_str())) 121 if (!DeleteFile(Common::UTF8ToTStr(filename).c_str()))
122 { 122 {
123 WARN_LOG(COMMON, "Delete: DeleteFile failed on %s: %s", 123 LOG_ERROR(Common_Filesystem, "DeleteFile failed on %s: %s",
124 filename.c_str(), GetLastErrorMsg()); 124 filename.c_str(), GetLastErrorMsg());
125 return false; 125 return false;
126 } 126 }
127#else 127#else
128 if (unlink(filename.c_str()) == -1) { 128 if (unlink(filename.c_str()) == -1) {
129 WARN_LOG(COMMON, "Delete: unlink failed on %s: %s", 129 LOG_ERROR(Common_Filesystem, "unlink failed on %s: %s",
130 filename.c_str(), GetLastErrorMsg()); 130 filename.c_str(), GetLastErrorMsg());
131 return false; 131 return false;
132 } 132 }
@@ -138,17 +138,17 @@ bool Delete(const std::string &filename)
138// Returns true if successful, or path already exists. 138// Returns true if successful, or path already exists.
139bool CreateDir(const std::string &path) 139bool CreateDir(const std::string &path)
140{ 140{
141 INFO_LOG(COMMON, "CreateDir: directory %s", path.c_str()); 141 LOG_TRACE(Common_Filesystem, "directory %s", path.c_str());
142#ifdef _WIN32 142#ifdef _WIN32
143 if (::CreateDirectory(Common::UTF8ToTStr(path).c_str(), nullptr)) 143 if (::CreateDirectory(Common::UTF8ToTStr(path).c_str(), nullptr))
144 return true; 144 return true;
145 DWORD error = GetLastError(); 145 DWORD error = GetLastError();
146 if (error == ERROR_ALREADY_EXISTS) 146 if (error == ERROR_ALREADY_EXISTS)
147 { 147 {
148 WARN_LOG(COMMON, "CreateDir: CreateDirectory failed on %s: already exists", path.c_str()); 148 LOG_WARNING(Common_Filesystem, "CreateDirectory failed on %s: already exists", path.c_str());
149 return true; 149 return true;
150 } 150 }
151 ERROR_LOG(COMMON, "CreateDir: CreateDirectory failed on %s: %i", path.c_str(), error); 151 LOG_ERROR(Common_Filesystem, "CreateDirectory failed on %s: %i", path.c_str(), error);
152 return false; 152 return false;
153#else 153#else
154 if (mkdir(path.c_str(), 0755) == 0) 154 if (mkdir(path.c_str(), 0755) == 0)
@@ -158,11 +158,11 @@ bool CreateDir(const std::string &path)
158 158
159 if (err == EEXIST) 159 if (err == EEXIST)
160 { 160 {
161 WARN_LOG(COMMON, "CreateDir: mkdir failed on %s: already exists", path.c_str()); 161 LOG_WARNING(Common_Filesystem, "mkdir failed on %s: already exists", path.c_str());
162 return true; 162 return true;
163 } 163 }
164 164
165 ERROR_LOG(COMMON, "CreateDir: mkdir failed on %s: %s", path.c_str(), strerror(err)); 165 LOG_ERROR(Common_Filesystem, "mkdir failed on %s: %s", path.c_str(), strerror(err));
166 return false; 166 return false;
167#endif 167#endif
168} 168}
@@ -171,11 +171,11 @@ bool CreateDir(const std::string &path)
171bool CreateFullPath(const std::string &fullPath) 171bool CreateFullPath(const std::string &fullPath)
172{ 172{
173 int panicCounter = 100; 173 int panicCounter = 100;
174 INFO_LOG(COMMON, "CreateFullPath: path %s", fullPath.c_str()); 174 LOG_TRACE(Common_Filesystem, "path %s", fullPath.c_str());
175 175
176 if (FileUtil::Exists(fullPath)) 176 if (FileUtil::Exists(fullPath))
177 { 177 {
178 INFO_LOG(COMMON, "CreateFullPath: path exists %s", fullPath.c_str()); 178 LOG_WARNING(Common_Filesystem, "path exists %s", fullPath.c_str());
179 return true; 179 return true;
180 } 180 }
181 181
@@ -192,7 +192,7 @@ bool CreateFullPath(const std::string &fullPath)
192 // Include the '/' so the first call is CreateDir("/") rather than CreateDir("") 192 // Include the '/' so the first call is CreateDir("/") rather than CreateDir("")
193 std::string const subPath(fullPath.substr(0, position + 1)); 193 std::string const subPath(fullPath.substr(0, position + 1));
194 if (!FileUtil::IsDirectory(subPath) && !FileUtil::CreateDir(subPath)) { 194 if (!FileUtil::IsDirectory(subPath) && !FileUtil::CreateDir(subPath)) {
195 ERROR_LOG(COMMON, "CreateFullPath: directory creation failed"); 195 LOG_ERROR(Common, "CreateFullPath: directory creation failed");
196 return false; 196 return false;
197 } 197 }
198 198
@@ -200,7 +200,7 @@ bool CreateFullPath(const std::string &fullPath)
200 panicCounter--; 200 panicCounter--;
201 if (panicCounter <= 0) 201 if (panicCounter <= 0)
202 { 202 {
203 ERROR_LOG(COMMON, "CreateFullPath: directory structure is too deep"); 203 LOG_ERROR(Common, "CreateFullPath: directory structure is too deep");
204 return false; 204 return false;
205 } 205 }
206 position++; 206 position++;
@@ -211,12 +211,12 @@ bool CreateFullPath(const std::string &fullPath)
211// Deletes a directory filename, returns true on success 211// Deletes a directory filename, returns true on success
212bool DeleteDir(const std::string &filename) 212bool DeleteDir(const std::string &filename)
213{ 213{
214 INFO_LOG(COMMON, "DeleteDir: directory %s", filename.c_str()); 214 LOG_INFO(Common_Filesystem, "directory %s", filename.c_str());
215 215
216 // check if a directory 216 // check if a directory
217 if (!FileUtil::IsDirectory(filename)) 217 if (!FileUtil::IsDirectory(filename))
218 { 218 {
219 ERROR_LOG(COMMON, "DeleteDir: Not a directory %s", filename.c_str()); 219 LOG_ERROR(Common_Filesystem, "Not a directory %s", filename.c_str());
220 return false; 220 return false;
221 } 221 }
222 222
@@ -227,7 +227,7 @@ bool DeleteDir(const std::string &filename)
227 if (rmdir(filename.c_str()) == 0) 227 if (rmdir(filename.c_str()) == 0)
228 return true; 228 return true;
229#endif 229#endif
230 ERROR_LOG(COMMON, "DeleteDir: %s: %s", filename.c_str(), GetLastErrorMsg()); 230 LOG_ERROR(Common_Filesystem, "failed %s: %s", filename.c_str(), GetLastErrorMsg());
231 231
232 return false; 232 return false;
233} 233}
@@ -235,11 +235,11 @@ bool DeleteDir(const std::string &filename)
235// renames file srcFilename to destFilename, returns true on success 235// renames file srcFilename to destFilename, returns true on success
236bool Rename(const std::string &srcFilename, const std::string &destFilename) 236bool Rename(const std::string &srcFilename, const std::string &destFilename)
237{ 237{
238 INFO_LOG(COMMON, "Rename: %s --> %s", 238 LOG_TRACE(Common_Filesystem, "%s --> %s",
239 srcFilename.c_str(), destFilename.c_str()); 239 srcFilename.c_str(), destFilename.c_str());
240 if (rename(srcFilename.c_str(), destFilename.c_str()) == 0) 240 if (rename(srcFilename.c_str(), destFilename.c_str()) == 0)
241 return true; 241 return true;
242 ERROR_LOG(COMMON, "Rename: failed %s --> %s: %s", 242 LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s",
243 srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); 243 srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
244 return false; 244 return false;
245} 245}
@@ -247,13 +247,13 @@ bool Rename(const std::string &srcFilename, const std::string &destFilename)
247// copies file srcFilename to destFilename, returns true on success 247// copies file srcFilename to destFilename, returns true on success
248bool Copy(const std::string &srcFilename, const std::string &destFilename) 248bool Copy(const std::string &srcFilename, const std::string &destFilename)
249{ 249{
250 INFO_LOG(COMMON, "Copy: %s --> %s", 250 LOG_TRACE(Common_Filesystem, "%s --> %s",
251 srcFilename.c_str(), destFilename.c_str()); 251 srcFilename.c_str(), destFilename.c_str());
252#ifdef _WIN32 252#ifdef _WIN32
253 if (CopyFile(Common::UTF8ToTStr(srcFilename).c_str(), Common::UTF8ToTStr(destFilename).c_str(), FALSE)) 253 if (CopyFile(Common::UTF8ToTStr(srcFilename).c_str(), Common::UTF8ToTStr(destFilename).c_str(), FALSE))
254 return true; 254 return true;
255 255
256 ERROR_LOG(COMMON, "Copy: failed %s --> %s: %s", 256 LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s",
257 srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); 257 srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
258 return false; 258 return false;
259#else 259#else
@@ -267,7 +267,7 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename)
267 FILE *input = fopen(srcFilename.c_str(), "rb"); 267 FILE *input = fopen(srcFilename.c_str(), "rb");
268 if (!input) 268 if (!input)
269 { 269 {
270 ERROR_LOG(COMMON, "Copy: input failed %s --> %s: %s", 270 LOG_ERROR(Common_Filesystem, "opening input failed %s --> %s: %s",
271 srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); 271 srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
272 return false; 272 return false;
273 } 273 }
@@ -277,7 +277,7 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename)
277 if (!output) 277 if (!output)
278 { 278 {
279 fclose(input); 279 fclose(input);
280 ERROR_LOG(COMMON, "Copy: output failed %s --> %s: %s", 280 LOG_ERROR(Common_Filesystem, "opening output failed %s --> %s: %s",
281 srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); 281 srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
282 return false; 282 return false;
283 } 283 }
@@ -291,8 +291,8 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename)
291 { 291 {
292 if (ferror(input) != 0) 292 if (ferror(input) != 0)
293 { 293 {
294 ERROR_LOG(COMMON, 294 LOG_ERROR(Common_Filesystem,
295 "Copy: failed reading from source, %s --> %s: %s", 295 "failed reading from source, %s --> %s: %s",
296 srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); 296 srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
297 goto bail; 297 goto bail;
298 } 298 }
@@ -302,8 +302,8 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename)
302 int wnum = fwrite(buffer, sizeof(char), rnum, output); 302 int wnum = fwrite(buffer, sizeof(char), rnum, output);
303 if (wnum != rnum) 303 if (wnum != rnum)
304 { 304 {
305 ERROR_LOG(COMMON, 305 LOG_ERROR(Common_Filesystem,
306 "Copy: failed writing to output, %s --> %s: %s", 306 "failed writing to output, %s --> %s: %s",
307 srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); 307 srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
308 goto bail; 308 goto bail;
309 } 309 }
@@ -326,13 +326,13 @@ u64 GetSize(const std::string &filename)
326{ 326{
327 if (!Exists(filename)) 327 if (!Exists(filename))
328 { 328 {
329 WARN_LOG(COMMON, "GetSize: failed %s: No such file", filename.c_str()); 329 LOG_ERROR(Common_Filesystem, "failed %s: No such file", filename.c_str());
330 return 0; 330 return 0;
331 } 331 }
332 332
333 if (IsDirectory(filename)) 333 if (IsDirectory(filename))
334 { 334 {
335 WARN_LOG(COMMON, "GetSize: failed %s: is a directory", filename.c_str()); 335 LOG_ERROR(Common_Filesystem, "failed %s: is a directory", filename.c_str());
336 return 0; 336 return 0;
337 } 337 }
338 338
@@ -343,12 +343,12 @@ u64 GetSize(const std::string &filename)
343 if (stat64(filename.c_str(), &buf) == 0) 343 if (stat64(filename.c_str(), &buf) == 0)
344#endif 344#endif
345 { 345 {
346 DEBUG_LOG(COMMON, "GetSize: %s: %lld", 346 LOG_TRACE(Common_Filesystem, "%s: %lld",
347 filename.c_str(), (long long)buf.st_size); 347 filename.c_str(), (long long)buf.st_size);
348 return buf.st_size; 348 return buf.st_size;
349 } 349 }
350 350
351 ERROR_LOG(COMMON, "GetSize: Stat failed %s: %s", 351 LOG_ERROR(Common_Filesystem, "Stat failed %s: %s",
352 filename.c_str(), GetLastErrorMsg()); 352 filename.c_str(), GetLastErrorMsg());
353 return 0; 353 return 0;
354} 354}
@@ -358,7 +358,7 @@ u64 GetSize(const int fd)
358{ 358{
359 struct stat64 buf; 359 struct stat64 buf;
360 if (fstat64(fd, &buf) != 0) { 360 if (fstat64(fd, &buf) != 0) {
361 ERROR_LOG(COMMON, "GetSize: stat failed %i: %s", 361 LOG_ERROR(Common_Filesystem, "GetSize: stat failed %i: %s",
362 fd, GetLastErrorMsg()); 362 fd, GetLastErrorMsg());
363 return 0; 363 return 0;
364 } 364 }
@@ -371,13 +371,13 @@ u64 GetSize(FILE *f)
371 // can't use off_t here because it can be 32-bit 371 // can't use off_t here because it can be 32-bit
372 u64 pos = ftello(f); 372 u64 pos = ftello(f);
373 if (fseeko(f, 0, SEEK_END) != 0) { 373 if (fseeko(f, 0, SEEK_END) != 0) {
374 ERROR_LOG(COMMON, "GetSize: seek failed %p: %s", 374 LOG_ERROR(Common_Filesystem, "GetSize: seek failed %p: %s",
375 f, GetLastErrorMsg()); 375 f, GetLastErrorMsg());
376 return 0; 376 return 0;
377 } 377 }
378 u64 size = ftello(f); 378 u64 size = ftello(f);
379 if ((size != pos) && (fseeko(f, pos, SEEK_SET) != 0)) { 379 if ((size != pos) && (fseeko(f, pos, SEEK_SET) != 0)) {
380 ERROR_LOG(COMMON, "GetSize: seek failed %p: %s", 380 LOG_ERROR(Common_Filesystem, "GetSize: seek failed %p: %s",
381 f, GetLastErrorMsg()); 381 f, GetLastErrorMsg());
382 return 0; 382 return 0;
383 } 383 }
@@ -387,11 +387,11 @@ u64 GetSize(FILE *f)
387// creates an empty file filename, returns true on success 387// creates an empty file filename, returns true on success
388bool CreateEmptyFile(const std::string &filename) 388bool CreateEmptyFile(const std::string &filename)
389{ 389{
390 INFO_LOG(COMMON, "CreateEmptyFile: %s", filename.c_str()); 390 LOG_TRACE(Common_Filesystem, "%s", filename.c_str());
391 391
392 if (!FileUtil::IOFile(filename, "wb")) 392 if (!FileUtil::IOFile(filename, "wb"))
393 { 393 {
394 ERROR_LOG(COMMON, "CreateEmptyFile: failed %s: %s", 394 LOG_ERROR(Common_Filesystem, "failed %s: %s",
395 filename.c_str(), GetLastErrorMsg()); 395 filename.c_str(), GetLastErrorMsg());
396 return false; 396 return false;
397 } 397 }
@@ -404,7 +404,7 @@ bool CreateEmptyFile(const std::string &filename)
404// results into parentEntry. Returns the number of files+directories found 404// results into parentEntry. Returns the number of files+directories found
405u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry) 405u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry)
406{ 406{
407 INFO_LOG(COMMON, "ScanDirectoryTree: directory %s", directory.c_str()); 407 LOG_TRACE(Common_Filesystem, "directory %s", directory.c_str());
408 // How many files + directories we found 408 // How many files + directories we found
409 u32 foundEntries = 0; 409 u32 foundEntries = 0;
410#ifdef _WIN32 410#ifdef _WIN32
@@ -474,7 +474,7 @@ u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry)
474// Deletes the given directory and anything under it. Returns true on success. 474// Deletes the given directory and anything under it. Returns true on success.
475bool DeleteDirRecursively(const std::string &directory) 475bool DeleteDirRecursively(const std::string &directory)
476{ 476{
477 INFO_LOG(COMMON, "DeleteDirRecursively: %s", directory.c_str()); 477 LOG_TRACE(Common_Filesystem, "%s", directory.c_str());
478#ifdef _WIN32 478#ifdef _WIN32
479 // Find the first file in the directory. 479 // Find the first file in the directory.
480 WIN32_FIND_DATA ffd; 480 WIN32_FIND_DATA ffd;
@@ -588,7 +588,7 @@ std::string GetCurrentDir()
588 // Get the current working directory (getcwd uses malloc) 588 // Get the current working directory (getcwd uses malloc)
589 if (!(dir = __getcwd(nullptr, 0))) { 589 if (!(dir = __getcwd(nullptr, 0))) {
590 590
591 ERROR_LOG(COMMON, "GetCurrentDirectory failed: %s", 591 LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: %s",
592 GetLastErrorMsg()); 592 GetLastErrorMsg());
593 return nullptr; 593 return nullptr;
594 } 594 }
@@ -647,7 +647,7 @@ std::string GetSysDirectory()
647#endif 647#endif
648 sysDir += DIR_SEP; 648 sysDir += DIR_SEP;
649 649
650 INFO_LOG(COMMON, "GetSysDirectory: Setting to %s:", sysDir.c_str()); 650 LOG_DEBUG(Common_Filesystem, "Setting to %s:", sysDir.c_str());
651 return sysDir; 651 return sysDir;
652} 652}
653 653
@@ -695,7 +695,7 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new
695 { 695 {
696 if (!FileUtil::IsDirectory(newPath)) 696 if (!FileUtil::IsDirectory(newPath))
697 { 697 {
698 WARN_LOG(COMMON, "Invalid path specified %s", newPath.c_str()); 698 LOG_ERROR(Common_Filesystem, "Invalid path specified %s", newPath.c_str());
699 return paths[DirIDX]; 699 return paths[DirIDX];
700 } 700 }
701 else 701 else
diff --git a/src/common/log.h b/src/common/log.h
index 78f0dae4d..663eda9ad 100644
--- a/src/common/log.h
+++ b/src/common/log.h
@@ -6,105 +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 9#include "common/logging/log.h"
10#ifndef LOGGING
11#define LOGGING
12#endif
13
14enum {
15 OS_LEVEL, // Printed by the emulated operating system
16 NOTICE_LEVEL, // VERY important information that is NOT errors. Like startup and OSReports.
17 ERROR_LEVEL, // Critical errors
18 WARNING_LEVEL, // Something is suspicious.
19 INFO_LEVEL, // General information.
20 DEBUG_LEVEL, // Detailed debugging - might make things slow.
21};
22
23namespace LogTypes
24{
25
26enum LOG_TYPE {
27 ACTIONREPLAY,
28 AUDIO,
29 AUDIO_INTERFACE,
30 BOOT,
31 COMMANDPROCESSOR,
32 COMMON,
33 CONSOLE,
34 CONFIG,
35 DISCIO,
36 FILEMON,
37 DSPHLE,
38 DSPLLE,
39 DSP_MAIL,
40 DSPINTERFACE,
41 DVDINTERFACE,
42 DYNA_REC,
43 EXPANSIONINTERFACE,
44 GDB_STUB,
45 ARM11,
46 GSP,
47 OSHLE,
48 MASTER_LOG,
49 MEMMAP,
50 MEMCARD_MANAGER,
51 OSREPORT,
52 PAD,
53 PROCESSORINTERFACE,
54 PIXELENGINE,
55 SERIALINTERFACE,
56 SP1,
57 STREAMINGINTERFACE,
58 VIDEO,
59 VIDEOINTERFACE,
60 LOADER,
61 FILESYS,
62 WII_IPC_DVD,
63 WII_IPC_ES,
64 WII_IPC_FILEIO,
65 WII_IPC_HID,
66 KERNEL,
67 SVC,
68 HLE,
69 RENDER,
70 GPU,
71 HW,
72 TIME,
73 NETPLAY,
74 GUI,
75
76 NUMBER_OF_LOGS // Must be last
77};
78
79// FIXME: should this be removed?
80enum LOG_LEVELS {
81 LOS = OS_LEVEL,
82 LNOTICE = NOTICE_LEVEL,
83 LERROR = ERROR_LEVEL,
84 LWARNING = WARNING_LEVEL,
85 LINFO = INFO_LEVEL,
86 LDEBUG = DEBUG_LEVEL,
87};
88
89#define LOGTYPES_LEVELS LogTypes::LOG_LEVELS
90#define LOGTYPES_TYPE LogTypes::LOG_TYPE
91
92} // namespace
93
94void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, const char*file, int line,
95 const char* function, const char* fmt, ...)
96#ifdef __GNUC__
97 __attribute__((format(printf, 6, 7)))
98#endif
99 ;
100
101#if defined LOGGING || defined _DEBUG || defined DEBUGFAST
102#define MAX_LOGLEVEL LDEBUG
103#else
104#ifndef MAX_LOGLEVEL
105#define MAX_LOGLEVEL LWARNING
106#endif // loglevel
107#endif // logging
108 10
109#ifdef _WIN32 11#ifdef _WIN32
110#ifndef __func__ 12#ifndef __func__
@@ -112,29 +14,16 @@ void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, const char*file, int
112#endif 14#endif
113#endif 15#endif
114 16
115// Let the compiler optimize this out 17#if _DEBUG
116#define GENERIC_LOG(t, v, ...) { \
117 if (v <= LogTypes::MAX_LOGLEVEL) \
118 GenericLog(v, t, __FILE__, __LINE__, __func__, __VA_ARGS__); \
119 }
120
121#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 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 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
128#if MAX_LOGLEVEL >= DEBUG_LEVEL
129#define _dbg_assert_(_t_, _a_) \ 18#define _dbg_assert_(_t_, _a_) \
130 if (!(_a_)) {\ 19 if (!(_a_)) {\
131 ERROR_LOG(_t_, "Error...\n\n Line: %d\n File: %s\n Time: %s\n\nIgnore and continue?", \ 20 LOG_CRITICAL(_t_, "Error...\n\n Line: %d\n File: %s\n Time: %s\n\nIgnore and continue?", \
132 __LINE__, __FILE__, __TIME__); \ 21 __LINE__, __FILE__, __TIME__); \
133 if (!PanicYesNo("*** Assertion (see log)***\n")) {Crash();} \ 22 if (!PanicYesNo("*** Assertion (see log)***\n")) {Crash();} \
134 } 23 }
135#define _dbg_assert_msg_(_t_, _a_, ...)\ 24#define _dbg_assert_msg_(_t_, _a_, ...)\
136 if (!(_a_)) {\ 25 if (!(_a_)) {\
137 ERROR_LOG(_t_, __VA_ARGS__); \ 26 LOG_CRITICAL(_t_, __VA_ARGS__); \
138 if (!PanicYesNo(__VA_ARGS__)) {Crash();} \ 27 if (!PanicYesNo(__VA_ARGS__)) {Crash();} \
139 } 28 }
140#define _dbg_update_() Host_UpdateLogDisplay(); 29#define _dbg_update_() Host_UpdateLogDisplay();
@@ -146,11 +35,10 @@ void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, const char*file, int
146#define _dbg_assert_(_t_, _a_) {} 35#define _dbg_assert_(_t_, _a_) {}
147#define _dbg_assert_msg_(_t_, _a_, _desc_, ...) {} 36#define _dbg_assert_msg_(_t_, _a_, _desc_, ...) {}
148#endif // dbg_assert 37#endif // dbg_assert
149#endif // MAX_LOGLEVEL DEBUG 38#endif
150 39
151#define _assert_(_a_) _dbg_assert_(MASTER_LOG, _a_) 40#define _assert_(_a_) _dbg_assert_(MASTER_LOG, _a_)
152 41
153#ifndef GEKKO
154#ifdef _WIN32 42#ifdef _WIN32
155#define _assert_msg_(_t_, _a_, _fmt_, ...) \ 43#define _assert_msg_(_t_, _a_, _fmt_, ...) \
156 if (!(_a_)) {\ 44 if (!(_a_)) {\
@@ -162,6 +50,3 @@ void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, const char*file, int
162 if (!PanicYesNo(_fmt_, ##__VA_ARGS__)) {Crash();} \ 50 if (!PanicYesNo(_fmt_, ##__VA_ARGS__)) {Crash();} \
163 } 51 }
164#endif // WIN32 52#endif // WIN32
165#else // GEKKO
166#define _assert_msg_(_t_, _a_, _fmt_, ...)
167#endif
diff --git a/src/common/log_manager.cpp b/src/common/log_manager.cpp
deleted file mode 100644
index 128c15388..000000000
--- a/src/common/log_manager.cpp
+++ /dev/null
@@ -1,198 +0,0 @@
1// Copyright 2013 Dolphin Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include <algorithm>
6
7#include "common/log_manager.h"
8#include "common/console_listener.h"
9#include "common/timer.h"
10
11void GenericLog(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file, int line,
12 const char* function, const char* fmt, ...)
13{
14 va_list args;
15 va_start(args, fmt);
16
17 if (LogManager::GetInstance()) {
18 LogManager::GetInstance()->Log(level, type,
19 file, line, function, fmt, args);
20 }
21 va_end(args);
22}
23
24LogManager *LogManager::m_logManager = nullptr;
25
26LogManager::LogManager()
27{
28 // create log files
29 m_Log[LogTypes::MASTER_LOG] = new LogContainer("*", "Master Log");
30 m_Log[LogTypes::BOOT] = new LogContainer("BOOT", "Boot");
31 m_Log[LogTypes::COMMON] = new LogContainer("COMMON", "Common");
32 m_Log[LogTypes::CONFIG] = new LogContainer("CONFIG", "Configuration");
33 m_Log[LogTypes::DISCIO] = new LogContainer("DIO", "Disc IO");
34 m_Log[LogTypes::FILEMON] = new LogContainer("FileMon", "File Monitor");
35 m_Log[LogTypes::PAD] = new LogContainer("PAD", "Pad");
36 m_Log[LogTypes::PIXELENGINE] = new LogContainer("PE", "PixelEngine");
37 m_Log[LogTypes::COMMANDPROCESSOR] = new LogContainer("CP", "CommandProc");
38 m_Log[LogTypes::VIDEOINTERFACE] = new LogContainer("VI", "VideoInt");
39 m_Log[LogTypes::SERIALINTERFACE] = new LogContainer("SI", "SerialInt");
40 m_Log[LogTypes::PROCESSORINTERFACE] = new LogContainer("PI", "ProcessorInt");
41 m_Log[LogTypes::MEMMAP] = new LogContainer("MI", "MI & memmap");
42 m_Log[LogTypes::SP1] = new LogContainer("SP1", "Serial Port 1");
43 m_Log[LogTypes::STREAMINGINTERFACE] = new LogContainer("Stream", "StreamingInt");
44 m_Log[LogTypes::DSPINTERFACE] = new LogContainer("DSP", "DSPInterface");
45 m_Log[LogTypes::DVDINTERFACE] = new LogContainer("DVD", "DVDInterface");
46 m_Log[LogTypes::GSP] = new LogContainer("GSP", "GSP");
47 m_Log[LogTypes::EXPANSIONINTERFACE] = new LogContainer("EXI", "ExpansionInt");
48 m_Log[LogTypes::GDB_STUB] = new LogContainer("GDB_STUB", "GDB Stub");
49 m_Log[LogTypes::AUDIO_INTERFACE] = new LogContainer("AI", "AudioInt");
50 m_Log[LogTypes::ARM11] = new LogContainer("ARM11", "ARM11");
51 m_Log[LogTypes::OSHLE] = new LogContainer("HLE", "HLE");
52 m_Log[LogTypes::DSPHLE] = new LogContainer("DSPHLE", "DSP HLE");
53 m_Log[LogTypes::DSPLLE] = new LogContainer("DSPLLE", "DSP LLE");
54 m_Log[LogTypes::DSP_MAIL] = new LogContainer("DSPMails", "DSP Mails");
55 m_Log[LogTypes::VIDEO] = new LogContainer("Video", "Video Backend");
56 m_Log[LogTypes::AUDIO] = new LogContainer("Audio", "Audio Emulator");
57 m_Log[LogTypes::DYNA_REC] = new LogContainer("JIT", "JIT");
58 m_Log[LogTypes::CONSOLE] = new LogContainer("CONSOLE", "Dolphin Console");
59 m_Log[LogTypes::OSREPORT] = new LogContainer("OSREPORT", "OSReport");
60 m_Log[LogTypes::TIME] = new LogContainer("Time", "Core Timing");
61 m_Log[LogTypes::LOADER] = new LogContainer("Loader", "Loader");
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");
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");
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");
68 m_Log[LogTypes::RENDER] = new LogContainer("RENDER", "RENDER");
69 m_Log[LogTypes::GPU] = new LogContainer("GPU", "GPU");
70 m_Log[LogTypes::SVC] = new LogContainer("SVC", "Supervisor Call HLE");
71 m_Log[LogTypes::HLE] = new LogContainer("HLE", "High Level Emulation");
72 m_Log[LogTypes::HW] = new LogContainer("HW", "Hardware");
73 m_Log[LogTypes::ACTIONREPLAY] = new LogContainer("ActionReplay", "ActionReplay");
74 m_Log[LogTypes::MEMCARD_MANAGER] = new LogContainer("MemCard Manager", "MemCard Manager");
75 m_Log[LogTypes::NETPLAY] = new LogContainer("NETPLAY", "Netplay");
76 m_Log[LogTypes::GUI] = new LogContainer("GUI", "GUI");
77
78 m_fileLog = new FileLogListener(FileUtil::GetUserPath(F_MAINLOG_IDX).c_str());
79 m_consoleLog = new ConsoleListener();
80 m_debuggerLog = new DebuggerLogListener();
81
82 for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i)
83 {
84 m_Log[i]->SetEnable(true);
85 m_Log[i]->AddListener(m_fileLog);
86 m_Log[i]->AddListener(m_consoleLog);
87#ifdef _MSC_VER
88 if (IsDebuggerPresent())
89 m_Log[i]->AddListener(m_debuggerLog);
90#endif
91 }
92
93 m_consoleLog->Open();
94}
95
96LogManager::~LogManager()
97{
98 for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i)
99 {
100 m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_fileLog);
101 m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_consoleLog);
102 m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_debuggerLog);
103 }
104
105 for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i)
106 delete m_Log[i];
107
108 delete m_fileLog;
109 delete m_consoleLog;
110 delete m_debuggerLog;
111}
112
113void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file,
114 int line, const char* function, const char *fmt, va_list args)
115{
116 char temp[MAX_MSGLEN];
117 char msg[MAX_MSGLEN * 2];
118 LogContainer *log = m_Log[type];
119
120 if (!log->IsEnabled() || level > log->GetLevel() || ! log->HasListeners())
121 return;
122
123 Common::CharArrayFromFormatV(temp, MAX_MSGLEN, fmt, args);
124
125 static const char level_to_char[7] = "ONEWID";
126 sprintf(msg, "%s %s:%u %c[%s] %s: %s\n", Common::Timer::GetTimeFormatted().c_str(), file, line,
127 level_to_char[(int)level], log->GetShortName(), function, temp);
128
129#ifdef ANDROID
130 Host_SysMessage(msg);
131#endif
132 log->Trigger(level, msg);
133}
134
135void LogManager::Init()
136{
137 m_logManager = new LogManager();
138}
139
140void LogManager::Shutdown()
141{
142 delete m_logManager;
143 m_logManager = nullptr;
144}
145
146LogContainer::LogContainer(const char* shortName, const char* fullName, bool enable)
147 : m_enable(enable)
148{
149 strncpy(m_fullName, fullName, 128);
150 strncpy(m_shortName, shortName, 32);
151 m_level = LogTypes::MAX_LOGLEVEL;
152}
153
154// LogContainer
155void LogContainer::AddListener(LogListener *listener)
156{
157 std::lock_guard<std::mutex> lk(m_listeners_lock);
158 m_listeners.insert(listener);
159}
160
161void LogContainer::RemoveListener(LogListener *listener)
162{
163 std::lock_guard<std::mutex> lk(m_listeners_lock);
164 m_listeners.erase(listener);
165}
166
167void LogContainer::Trigger(LogTypes::LOG_LEVELS level, const char *msg)
168{
169 std::lock_guard<std::mutex> lk(m_listeners_lock);
170
171 std::set<LogListener*>::const_iterator i;
172 for (i = m_listeners.begin(); i != m_listeners.end(); ++i)
173 {
174 (*i)->Log(level, msg);
175 }
176}
177
178FileLogListener::FileLogListener(const char *filename)
179{
180 OpenFStream(m_logfile, filename, std::ios::app);
181 SetEnable(true);
182}
183
184void FileLogListener::Log(LogTypes::LOG_LEVELS, const char *msg)
185{
186 if (!IsEnabled() || !IsValid())
187 return;
188
189 std::lock_guard<std::mutex> lk(m_log_lock);
190 m_logfile << msg << std::flush;
191}
192
193void DebuggerLogListener::Log(LogTypes::LOG_LEVELS, const char *msg)
194{
195#if _MSC_VER
196 ::OutputDebugStringA(msg);
197#endif
198}
diff --git a/src/common/log_manager.h b/src/common/log_manager.h
deleted file mode 100644
index baefc4ba8..000000000
--- a/src/common/log_manager.h
+++ /dev/null
@@ -1,166 +0,0 @@
1// Copyright 2013 Dolphin Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/log.h"
8#include "common/string_util.h"
9#include "common/file_util.h"
10
11#include <cstring>
12#include <set>
13#include <mutex>
14
15#define MAX_MESSAGES 8000
16#define MAX_MSGLEN 1024
17
18
19// pure virtual interface
20class LogListener
21{
22public:
23 virtual ~LogListener() {}
24
25 virtual void Log(LogTypes::LOG_LEVELS, const char *msg) = 0;
26};
27
28class FileLogListener : public LogListener
29{
30public:
31 FileLogListener(const char *filename);
32
33 void Log(LogTypes::LOG_LEVELS, const char *msg) override;
34
35 bool IsValid() { return !m_logfile.fail(); }
36 bool IsEnabled() const { return m_enable; }
37 void SetEnable(bool enable) { m_enable = enable; }
38
39 const char* GetName() const { return "file"; }
40
41private:
42 std::mutex m_log_lock;
43 std::ofstream m_logfile;
44 bool m_enable;
45};
46
47class DebuggerLogListener : public LogListener
48{
49public:
50 void Log(LogTypes::LOG_LEVELS, const char *msg) override;
51};
52
53class LogContainer
54{
55public:
56 LogContainer(const char* shortName, const char* fullName, bool enable = false);
57
58 const char* GetShortName() const { return m_shortName; }
59 const char* GetFullName() const { return m_fullName; }
60
61 void AddListener(LogListener* listener);
62 void RemoveListener(LogListener* listener);
63
64 void Trigger(LogTypes::LOG_LEVELS, const char *msg);
65
66 bool IsEnabled() const { return m_enable; }
67 void SetEnable(bool enable) { m_enable = enable; }
68
69 LogTypes::LOG_LEVELS GetLevel() const { return m_level; }
70
71 void SetLevel(LogTypes::LOG_LEVELS level) { m_level = level; }
72
73 bool HasListeners() const { return !m_listeners.empty(); }
74
75private:
76 char m_fullName[128];
77 char m_shortName[32];
78 bool m_enable;
79 LogTypes::LOG_LEVELS m_level;
80 std::mutex m_listeners_lock;
81 std::set<LogListener*> m_listeners;
82};
83
84class ConsoleListener;
85
86class LogManager : NonCopyable
87{
88private:
89 LogContainer* m_Log[LogTypes::NUMBER_OF_LOGS];
90 FileLogListener *m_fileLog;
91 ConsoleListener *m_consoleLog;
92 DebuggerLogListener *m_debuggerLog;
93 static LogManager *m_logManager; // Singleton. Ugh.
94
95 LogManager();
96 ~LogManager();
97public:
98
99 static u32 GetMaxLevel() { return LogTypes::MAX_LOGLEVEL; }
100
101 void Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file, int line,
102 const char* function, const char *fmt, va_list args);
103
104 void SetLogLevel(LogTypes::LOG_TYPE type, LogTypes::LOG_LEVELS level)
105 {
106 m_Log[type]->SetLevel(level);
107 }
108
109 void SetEnable(LogTypes::LOG_TYPE type, bool enable)
110 {
111 m_Log[type]->SetEnable(enable);
112 }
113
114 bool IsEnabled(LogTypes::LOG_TYPE type) const
115 {
116 return m_Log[type]->IsEnabled();
117 }
118
119 const char* GetShortName(LogTypes::LOG_TYPE type) const
120 {
121 return m_Log[type]->GetShortName();
122 }
123
124 const char* GetFullName(LogTypes::LOG_TYPE type) const
125 {
126 return m_Log[type]->GetFullName();
127 }
128
129 void AddListener(LogTypes::LOG_TYPE type, LogListener *listener)
130 {
131 m_Log[type]->AddListener(listener);
132 }
133
134 void RemoveListener(LogTypes::LOG_TYPE type, LogListener *listener)
135 {
136 m_Log[type]->RemoveListener(listener);
137 }
138
139 FileLogListener *GetFileListener() const
140 {
141 return m_fileLog;
142 }
143
144 ConsoleListener *GetConsoleListener() const
145 {
146 return m_consoleLog;
147 }
148
149 DebuggerLogListener *GetDebuggerListener() const
150 {
151 return m_debuggerLog;
152 }
153
154 static LogManager* GetInstance()
155 {
156 return m_logManager;
157 }
158
159 static void SetInstance(LogManager *logManager)
160 {
161 m_logManager = logManager;
162 }
163
164 static void Init();
165 static void Shutdown();
166};
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/filter.cpp b/src/common/logging/filter.cpp
new file mode 100644
index 000000000..0cf9b05e7
--- /dev/null
+++ b/src/common/logging/filter.cpp
@@ -0,0 +1,132 @@
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/logging/filter.h"
8#include "common/logging/backend.h"
9#include "common/string_util.h"
10
11namespace Log {
12
13Filter::Filter(Level default_level) {
14 ResetAll(default_level);
15}
16
17void Filter::ResetAll(Level level) {
18 class_levels.fill(level);
19}
20
21void Filter::SetClassLevel(Class log_class, Level level) {
22 class_levels[static_cast<size_t>(log_class)] = level;
23}
24
25void Filter::SetSubclassesLevel(const ClassInfo& log_class, Level level) {
26 const size_t log_class_i = static_cast<size_t>(log_class.log_class);
27
28 const size_t begin = log_class_i + 1;
29 const size_t end = begin + log_class.num_children;
30 for (size_t i = begin; begin < end; ++i) {
31 class_levels[i] = level;
32 }
33}
34
35void Filter::ParseFilterString(const std::string& filter_str) {
36 auto clause_begin = filter_str.cbegin();
37 while (clause_begin != filter_str.cend()) {
38 auto clause_end = std::find(clause_begin, filter_str.cend(), ' ');
39
40 // If clause isn't empty
41 if (clause_end != clause_begin) {
42 ParseFilterRule(clause_begin, clause_end);
43 }
44
45 if (clause_end != filter_str.cend()) {
46 // Skip over the whitespace
47 ++clause_end;
48 }
49 clause_begin = clause_end;
50 }
51}
52
53template <typename It>
54static Level GetLevelByName(const It begin, const It end) {
55 for (u8 i = 0; i < static_cast<u8>(Level::Count); ++i) {
56 const char* level_name = Logger::GetLevelName(static_cast<Level>(i));
57 if (Common::ComparePartialString(begin, end, level_name)) {
58 return static_cast<Level>(i);
59 }
60 }
61 return Level::Count;
62}
63
64template <typename It>
65static Class GetClassByName(const It begin, const It end) {
66 for (ClassType i = 0; i < static_cast<ClassType>(Class::Count); ++i) {
67 const char* level_name = Logger::GetLogClassName(static_cast<Class>(i));
68 if (Common::ComparePartialString(begin, end, level_name)) {
69 return static_cast<Class>(i);
70 }
71 }
72 return Class::Count;
73}
74
75template <typename InputIt, typename T>
76static InputIt find_last(InputIt begin, const InputIt end, const T& value) {
77 auto match = end;
78 while (begin != end) {
79 auto new_match = std::find(begin, end, value);
80 if (new_match != end) {
81 match = new_match;
82 ++new_match;
83 }
84 begin = new_match;
85 }
86 return match;
87}
88
89bool Filter::ParseFilterRule(const std::string::const_iterator begin,
90 const std::string::const_iterator end) {
91 auto level_separator = std::find(begin, end, ':');
92 if (level_separator == end) {
93 LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: %s",
94 std::string(begin, end).c_str());
95 return false;
96 }
97
98 const Level level = GetLevelByName(level_separator + 1, end);
99 if (level == Level::Count) {
100 LOG_ERROR(Log, "Unknown log level in filter: %s", std::string(begin, end).c_str());
101 return false;
102 }
103
104 if (Common::ComparePartialString(begin, level_separator, "*")) {
105 ResetAll(level);
106 return true;
107 }
108
109 auto class_name_end = find_last(begin, level_separator, '.');
110 if (class_name_end != level_separator &&
111 !Common::ComparePartialString(class_name_end + 1, level_separator, "*")) {
112 class_name_end = level_separator;
113 }
114
115 const Class log_class = GetClassByName(begin, class_name_end);
116 if (log_class == Class::Count) {
117 LOG_ERROR(Log, "Unknown log class in filter: %s", std::string(begin, end).c_str());
118 return false;
119 }
120
121 if (class_name_end == level_separator) {
122 SetClassLevel(log_class, level);
123 }
124 SetSubclassesLevel(log_class, level);
125 return true;
126}
127
128bool Filter::CheckMessage(Class log_class, Level level) const {
129 return static_cast<u8>(level) >= static_cast<u8>(class_levels[static_cast<size_t>(log_class)]);
130}
131
132}
diff --git a/src/common/logging/filter.h b/src/common/logging/filter.h
new file mode 100644
index 000000000..32b14b159
--- /dev/null
+++ b/src/common/logging/filter.h
@@ -0,0 +1,63 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#include <array>
6#include <string>
7
8#include "common/logging/log.h"
9
10namespace Log {
11
12struct ClassInfo;
13
14/**
15 * Implements a log message filter which allows different log classes to have different minimum
16 * severity levels. The filter can be changed at runtime and can be parsed from a string to allow
17 * editing via the interface or loading from a configuration file.
18 */
19class Filter {
20public:
21 /// Initializes the filter with all classes having `default_level` as the minimum level.
22 Filter(Level default_level);
23
24 /// Resets the filter so that all classes have `level` as the minimum displayed level.
25 void ResetAll(Level level);
26 /// Sets the minimum level of `log_class` (and not of its subclasses) to `level`.
27 void SetClassLevel(Class log_class, Level level);
28 /**
29 * Sets the minimum level of all of `log_class` subclasses to `level`. The level of `log_class`
30 * itself is not changed.
31 */
32 void SetSubclassesLevel(const ClassInfo& log_class, Level level);
33
34 /**
35 * Parses a filter string and applies it to this filter.
36 *
37 * A filter string consists of a space-separated list of filter rules, each of the format
38 * `<class>:<level>`. `<class>` is a log class name, with subclasses separated using periods.
39 * A rule for a given class also affects all of its subclasses. `*` wildcards are allowed and
40 * can be used to apply a rule to all classes or to all subclasses of a class without affecting
41 * the parent class. `<level>` a severity level name which will be set as the minimum logging
42 * level of the matched classes. Rules are applied left to right, with each rule overriding
43 * previous ones in the sequence.
44 *
45 * A few examples of filter rules:
46 * - `*:Info` -- Resets the level of all classes to Info.
47 * - `Service:Info` -- Sets the level of Service and all subclasses (Service.FS, Service.APT,
48 * etc.) to Info.
49 * - `Service.*:Debug` -- Sets the level of all Service subclasses to Debug, while leaving the
50 * level of Service unchanged.
51 * - `Service.FS:Trace` -- Sets the level of the Service.FS class to Trace.
52 */
53 void ParseFilterString(const std::string& filter_str);
54 bool ParseFilterRule(const std::string::const_iterator start, const std::string::const_iterator end);
55
56 /// Matches class/level combination against the filter, returning true if it passed.
57 bool CheckMessage(Class log_class, Level level) const;
58
59private:
60 std::array<Level, (size_t)Class::Count> class_levels;
61};
62
63}
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..3fe435346
--- /dev/null
+++ b/src/common/logging/text_formatter.cpp
@@ -0,0 +1,126 @@
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#ifdef _WIN32
9# define WIN32_LEAN_AND_MEAN
10# include <Windows.h>
11#endif
12
13#include "common/logging/backend.h"
14#include "common/logging/filter.h"
15#include "common/logging/log.h"
16#include "common/logging/text_formatter.h"
17
18#include "common/string_util.h"
19
20namespace Log {
21
22// TODO(bunnei): This should be moved to a generic path manipulation library
23const char* TrimSourcePath(const char* path, const char* root) {
24 const char* p = path;
25
26 while (*p != '\0') {
27 const char* next_slash = p;
28 while (*next_slash != '\0' && *next_slash != '/' && *next_slash != '\\') {
29 ++next_slash;
30 }
31
32 bool is_src = Common::ComparePartialString(p, next_slash, root);
33 p = next_slash;
34
35 if (*p != '\0') {
36 ++p;
37 }
38 if (is_src) {
39 path = p;
40 }
41 }
42 return path;
43}
44
45void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len) {
46 unsigned int time_seconds = static_cast<unsigned int>(entry.timestamp.count() / 1000000);
47 unsigned int time_fractional = static_cast<unsigned int>(entry.timestamp.count() % 1000000);
48
49 const char* class_name = Logger::GetLogClassName(entry.log_class);
50 const char* level_name = Logger::GetLevelName(entry.log_level);
51
52 snprintf(out_text, text_len, "[%4u.%06u] %s <%s> %s: %s",
53 time_seconds, time_fractional, class_name, level_name,
54 TrimSourcePath(entry.location.c_str()), entry.message.c_str());
55}
56
57static void ChangeConsoleColor(Level level) {
58#ifdef _WIN32
59 static HANDLE console_handle = GetStdHandle(STD_ERROR_HANDLE);
60
61 WORD color = 0;
62 switch (level) {
63 case Level::Trace: // Grey
64 color = FOREGROUND_INTENSITY; break;
65 case Level::Debug: // Cyan
66 color = FOREGROUND_GREEN | FOREGROUND_BLUE; break;
67 case Level::Info: // Bright gray
68 color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; break;
69 case Level::Warning: // Bright yellow
70 color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; break;
71 case Level::Error: // Bright red
72 color = FOREGROUND_RED | FOREGROUND_INTENSITY; break;
73 case Level::Critical: // Bright magenta
74 color = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY; break;
75 }
76
77 SetConsoleTextAttribute(console_handle, color);
78#else
79#define ESC "\x1b"
80 const char* color = "";
81 switch (level) {
82 case Level::Trace: // Grey
83 color = ESC "[1;30m"; break;
84 case Level::Debug: // Cyan
85 color = ESC "[0;36m"; break;
86 case Level::Info: // Bright gray
87 color = ESC "[0;37m"; break;
88 case Level::Warning: // Bright yellow
89 color = ESC "[1;33m"; break;
90 case Level::Error: // Bright red
91 color = ESC "[1;31m"; break;
92 case Level::Critical: // Bright magenta
93 color = ESC "[1;35m"; break;
94 }
95#undef ESC
96
97 fputs(color, stderr);
98#endif
99}
100
101void PrintMessage(const Entry& entry) {
102 ChangeConsoleColor(entry.log_level);
103 std::array<char, 4 * 1024> format_buffer;
104 FormatLogMessage(entry, format_buffer.data(), format_buffer.size());
105 fputs(format_buffer.data(), stderr);
106 fputc('\n', stderr);
107}
108
109void TextLoggingLoop(std::shared_ptr<Logger> logger, const Filter* filter) {
110 std::array<Entry, 256> entry_buffer;
111
112 while (true) {
113 size_t num_entries = logger->GetEntries(entry_buffer.data(), entry_buffer.size());
114 if (num_entries == Logger::QUEUE_CLOSED) {
115 break;
116 }
117 for (size_t i = 0; i < num_entries; ++i) {
118 const Entry& entry = entry_buffer[i];
119 if (filter->CheckMessage(entry.log_class, entry.log_level)) {
120 PrintMessage(entry);
121 }
122 }
123 }
124}
125
126}
diff --git a/src/common/logging/text_formatter.h b/src/common/logging/text_formatter.h
new file mode 100644
index 000000000..d7e298e28
--- /dev/null
+++ b/src/common/logging/text_formatter.h
@@ -0,0 +1,39 @@
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;
14class Filter;
15
16/**
17 * Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's
18 * intended to be used to strip a system-specific build directory from the `__FILE__` macro,
19 * leaving only the path relative to the sources root.
20 *
21 * @param path The input file path as a null-terminated string
22 * @param root The name of the root source directory as a null-terminated string. Path up to and
23 * including the last occurence of this name will be stripped
24 * @return A pointer to the same string passed as `path`, but starting at the trimmed portion
25 */
26const char* TrimSourcePath(const char* path, const char* root = "src");
27
28/// Formats a log entry into the provided text buffer.
29void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len);
30/// Formats and prints a log entry to stderr.
31void PrintMessage(const Entry& entry);
32
33/**
34 * Logging loop that repeatedly reads messages from the provided logger and prints them to the
35 * console. It is the baseline barebones log outputter.
36 */
37void TextLoggingLoop(std::shared_ptr<Logger> logger, const Filter* filter);
38
39}
diff --git a/src/common/mem_arena.cpp b/src/common/mem_arena.cpp
index 7d4fda0e2..9904d2472 100644
--- a/src/common/mem_arena.cpp
+++ b/src/common/mem_arena.cpp
@@ -71,7 +71,7 @@ int ashmem_create_region(const char *name, size_t size)
71 return fd; 71 return fd;
72 72
73error: 73error:
74 ERROR_LOG(MEMMAP, "NASTY ASHMEM ERROR: ret = %08x", ret); 74 LOG_ERROR(Common_Memory, "NASTY ASHMEM ERROR: ret = %08x", ret);
75 close(fd); 75 close(fd);
76 return ret; 76 return ret;
77} 77}
@@ -130,7 +130,7 @@ void MemArena::GrabLowMemSpace(size_t size)
130 // Note that it appears that ashmem is pinned by default, so no need to pin. 130 // Note that it appears that ashmem is pinned by default, so no need to pin.
131 if (fd < 0) 131 if (fd < 0)
132 { 132 {
133 ERROR_LOG(MEMMAP, "Failed to grab ashmem space of size: %08x errno: %d", (int)size, (int)(errno)); 133 LOG_ERROR(Common_Memory, "Failed to grab ashmem space of size: %08x errno: %d", (int)size, (int)(errno));
134 return; 134 return;
135 } 135 }
136#else 136#else
@@ -148,12 +148,12 @@ void MemArena::GrabLowMemSpace(size_t size)
148 } 148 }
149 else if (errno != EEXIST) 149 else if (errno != EEXIST)
150 { 150 {
151 ERROR_LOG(MEMMAP, "shm_open failed: %s", strerror(errno)); 151 LOG_ERROR(Common_Memory, "shm_open failed: %s", strerror(errno));
152 return; 152 return;
153 } 153 }
154 } 154 }
155 if (ftruncate(fd, size) < 0) 155 if (ftruncate(fd, size) < 0)
156 ERROR_LOG(MEMMAP, "Failed to allocate low memory space"); 156 LOG_ERROR(Common_Memory, "Failed to allocate low memory space");
157#endif 157#endif
158} 158}
159 159
@@ -197,7 +197,7 @@ void *MemArena::CreateView(s64 offset, size_t size, void *base)
197 197
198 if (retval == MAP_FAILED) 198 if (retval == MAP_FAILED)
199 { 199 {
200 NOTICE_LOG(MEMMAP, "mmap failed"); 200 LOG_ERROR(Common_Memory, "mmap failed");
201 return nullptr; 201 return nullptr;
202 } 202 }
203 return retval; 203 return retval;
@@ -423,7 +423,7 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena
423 base = (u8 *)base_addr; 423 base = (u8 *)base_addr;
424 if (Memory_TryBase(base, views, num_views, flags, arena)) 424 if (Memory_TryBase(base, views, num_views, flags, arena))
425 { 425 {
426 INFO_LOG(MEMMAP, "Found valid memory base at %p after %i tries.", base, base_attempts); 426 LOG_DEBUG(Common_Memory, "Found valid memory base at %p after %i tries.", base, base_attempts);
427 base_attempts = 0; 427 base_attempts = 0;
428 break; 428 break;
429 } 429 }
@@ -442,7 +442,7 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena
442 u8 *base = MemArena::Find4GBBase(); 442 u8 *base = MemArena::Find4GBBase();
443 if (!Memory_TryBase(base, views, num_views, flags, arena)) 443 if (!Memory_TryBase(base, views, num_views, flags, arena))
444 { 444 {
445 ERROR_LOG(MEMMAP, "MemoryMap_Setup: Failed finding a memory base."); 445 LOG_ERROR(Common_Memory, "MemoryMap_Setup: Failed finding a memory base.");
446 PanicAlert("MemoryMap_Setup: Failed finding a memory base."); 446 PanicAlert("MemoryMap_Setup: Failed finding a memory base.");
447 return 0; 447 return 0;
448 } 448 }
diff --git a/src/common/memory_util.cpp b/src/common/memory_util.cpp
index 93da5500b..ca8a2a9e4 100644
--- a/src/common/memory_util.cpp
+++ b/src/common/memory_util.cpp
@@ -109,7 +109,7 @@ void* AllocateAlignedMemory(size_t size,size_t alignment)
109 ptr = memalign(alignment, size); 109 ptr = memalign(alignment, size);
110#else 110#else
111 if (posix_memalign(&ptr, alignment, size) != 0) 111 if (posix_memalign(&ptr, alignment, size) != 0)
112 ERROR_LOG(MEMMAP, "Failed to allocate aligned memory"); 112 LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
113#endif 113#endif
114#endif 114#endif
115 115
diff --git a/src/common/msg_handler.cpp b/src/common/msg_handler.cpp
index b3556aaa8..7ffedc45a 100644
--- a/src/common/msg_handler.cpp
+++ b/src/common/msg_handler.cpp
@@ -75,7 +75,7 @@ bool MsgAlert(bool yes_no, int Style, const char* format, ...)
75 Common::CharArrayFromFormatV(buffer, sizeof(buffer)-1, str_translator(format).c_str(), args); 75 Common::CharArrayFromFormatV(buffer, sizeof(buffer)-1, str_translator(format).c_str(), args);
76 va_end(args); 76 va_end(args);
77 77
78 ERROR_LOG(MASTER_LOG, "%s: %s", caption.c_str(), buffer); 78 LOG_INFO(Common, "%s: %s", caption.c_str(), buffer);
79 79
80 // Don't ignore questions, especially AskYesNo, PanicYesNo could be ignored 80 // Don't ignore questions, especially AskYesNo, PanicYesNo could be ignored
81 if (msg_handler && (AlertEnabled || Style == QUESTION || Style == CRITICAL)) 81 if (msg_handler && (AlertEnabled || Style == QUESTION || Style == CRITICAL))
diff --git a/src/common/scope_exit.h b/src/common/scope_exit.h
new file mode 100644
index 000000000..1d3e59319
--- /dev/null
+++ b/src/common/scope_exit.h
@@ -0,0 +1,37 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace detail {
8 template <typename Func>
9 struct ScopeExitHelper {
10 explicit ScopeExitHelper(Func&& func) : func(std::move(func)) {}
11 ~ScopeExitHelper() { func(); }
12
13 Func func;
14 };
15
16 template <typename Func>
17 ScopeExitHelper<Func> ScopeExit(Func&& func) { return ScopeExitHelper<Func>(std::move(func)); }
18}
19
20/**
21 * This macro allows you to conveniently specify a block of code that will run on scope exit. Handy
22 * for doing ad-hoc clean-up tasks in a function with multiple returns.
23 *
24 * Example usage:
25 * \code
26 * const int saved_val = g_foo;
27 * g_foo = 55;
28 * SCOPE_EXIT({ g_foo = saved_val; });
29 *
30 * if (Bar()) {
31 * return 0;
32 * } else {
33 * return 20;
34 * }
35 * \endcode
36 */
37#define SCOPE_EXIT(body) auto scope_exit_helper_##__LINE__ = detail::ScopeExit([&]() body)
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp
index 7a8274a91..6d9612fb5 100644
--- a/src/common/string_util.cpp
+++ b/src/common/string_util.cpp
@@ -107,7 +107,7 @@ std::string StringFromFormat(const char* format, ...)
107#else 107#else
108 va_start(args, format); 108 va_start(args, format);
109 if (vasprintf(&buf, format, args) < 0) 109 if (vasprintf(&buf, format, args) < 0)
110 ERROR_LOG(COMMON, "Unable to allocate memory for string"); 110 LOG_ERROR(Common, "Unable to allocate memory for string");
111 va_end(args); 111 va_end(args);
112 112
113 std::string temp = buf; 113 std::string temp = buf;
@@ -475,7 +475,7 @@ static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>&
475 iconv_t const conv_desc = iconv_open("UTF-8", fromcode); 475 iconv_t const conv_desc = iconv_open("UTF-8", fromcode);
476 if ((iconv_t)(-1) == conv_desc) 476 if ((iconv_t)(-1) == conv_desc)
477 { 477 {
478 ERROR_LOG(COMMON, "Iconv initialization failure [%s]: %s", fromcode, strerror(errno)); 478 LOG_ERROR(Common, "Iconv initialization failure [%s]: %s", fromcode, strerror(errno));
479 iconv_close(conv_desc); 479 iconv_close(conv_desc);
480 return {}; 480 return {};
481 } 481 }
@@ -510,7 +510,7 @@ static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>&
510 } 510 }
511 else 511 else
512 { 512 {
513 ERROR_LOG(COMMON, "iconv failure [%s]: %s", fromcode, strerror(errno)); 513 LOG_ERROR(Common, "iconv failure [%s]: %s", fromcode, strerror(errno));
514 break; 514 break;
515 } 515 }
516 } 516 }
@@ -531,7 +531,7 @@ std::u16string UTF8ToUTF16(const std::string& input)
531 iconv_t const conv_desc = iconv_open("UTF-16LE", "UTF-8"); 531 iconv_t const conv_desc = iconv_open("UTF-16LE", "UTF-8");
532 if ((iconv_t)(-1) == conv_desc) 532 if ((iconv_t)(-1) == conv_desc)
533 { 533 {
534 ERROR_LOG(COMMON, "Iconv initialization failure [UTF-8]: %s", strerror(errno)); 534 LOG_ERROR(Common, "Iconv initialization failure [UTF-8]: %s", strerror(errno));
535 iconv_close(conv_desc); 535 iconv_close(conv_desc);
536 return {}; 536 return {};
537 } 537 }
@@ -566,7 +566,7 @@ std::u16string UTF8ToUTF16(const std::string& input)
566 } 566 }
567 else 567 else
568 { 568 {
569 ERROR_LOG(COMMON, "iconv failure [UTF-8]: %s", strerror(errno)); 569 LOG_ERROR(Common, "iconv failure [UTF-8]: %s", strerror(errno));
570 break; 570 break;
571 } 571 }
572 } 572 }
diff --git a/src/common/string_util.h b/src/common/string_util.h
index ae5bbadad..7d75691b1 100644
--- a/src/common/string_util.h
+++ b/src/common/string_util.h
@@ -115,4 +115,19 @@ inline std::string UTF8ToTStr(const std::string& str)
115 115
116#endif 116#endif
117 117
118/**
119 * Compares the string defined by the range [`begin`, `end`) to the null-terminated C-string
120 * `other` for equality.
121 */
122template <typename InIt>
123bool ComparePartialString(InIt begin, InIt end, const char* other) {
124 for (; begin != end && *other != '\0'; ++begin, ++other) {
125 if (*begin != *other) {
126 return false;
127 }
128 }
129 // Only return true if both strings finished at the same point
130 return (begin == end) == (*other == '\0');
131}
132
118} 133}
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