summaryrefslogtreecommitdiffstats
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/assert.h7
-rw-r--r--src/common/common_funcs.h4
-rw-r--r--src/common/common_paths.h3
-rw-r--r--src/common/file_util.cpp113
-rw-r--r--src/common/file_util.h11
-rw-r--r--src/common/logging/backend.cpp157
-rw-r--r--src/common/logging/backend.h87
-rw-r--r--src/common/logging/filter.cpp8
-rw-r--r--src/common/logging/log.h14
-rw-r--r--src/common/memory_util.cpp24
-rw-r--r--src/common/param_package.cpp12
-rw-r--r--src/common/string_util.cpp8
-rw-r--r--src/common/swap.h2
13 files changed, 343 insertions, 107 deletions
diff --git a/src/common/assert.h b/src/common/assert.h
index 3ee07f6a2..0d4eddc19 100644
--- a/src/common/assert.h
+++ b/src/common/assert.h
@@ -30,15 +30,14 @@ __declspec(noinline, noreturn)
#define ASSERT(_a_) \
do \
if (!(_a_)) { \
- assert_noinline_call([] { NGLOG_CRITICAL(Debug, "Assertion Failed!"); }); \
+ assert_noinline_call([] { LOG_CRITICAL(Debug, "Assertion Failed!"); }); \
} \
while (0)
#define ASSERT_MSG(_a_, ...) \
do \
if (!(_a_)) { \
- assert_noinline_call( \
- [&] { NGLOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \
+ assert_noinline_call([&] { LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \
} \
while (0)
@@ -53,5 +52,5 @@ __declspec(noinline, noreturn)
#define DEBUG_ASSERT_MSG(_a_, _desc_, ...)
#endif
-#define UNIMPLEMENTED() DEBUG_ASSERT_MSG(false, "Unimplemented code!")
+#define UNIMPLEMENTED() LOG_CRITICAL(Debug, "Unimplemented code!")
#define UNIMPLEMENTED_MSG(...) ASSERT_MSG(false, __VA_ARGS__)
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index 7cf7b7997..995938d0b 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -4,7 +4,7 @@
#pragma once
-#if !defined(ARCHITECTURE_x86_64) && !defined(_M_ARM)
+#if !defined(ARCHITECTURE_x86_64) && !defined(ARCHITECTURE_ARM)
#include <cstdlib> // for exit
#endif
#include "common/common_types.h"
@@ -30,7 +30,7 @@
#ifdef ARCHITECTURE_x86_64
#define Crash() __asm__ __volatile__("int $3")
-#elif defined(_M_ARM)
+#elif defined(ARCHITECTURE_ARM)
#define Crash() __asm__ __volatile__("trap")
#else
#define Crash() exit(1)
diff --git a/src/common/common_paths.h b/src/common/common_paths.h
index 0a6132dab..9bf3efaf2 100644
--- a/src/common/common_paths.h
+++ b/src/common/common_paths.h
@@ -32,12 +32,15 @@
#define SDMC_DIR "sdmc"
#define NAND_DIR "nand"
#define SYSDATA_DIR "sysdata"
+#define LOG_DIR "log"
// Filenames
// Files in the directory returned by GetUserPath(D_CONFIG_IDX)
#define EMU_CONFIG "emu.ini"
#define DEBUGGER_CONFIG "debugger.ini"
#define LOGGER_CONFIG "logger.ini"
+// Files in the directory returned by GetUserPath(D_LOGS_IDX)
+#define LOG_FILE "yuzu_log.txt"
// Sys files
#define SHARED_FONT "shared_font.bin"
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 2d0b81c6e..7213abe18 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -118,7 +118,7 @@ bool IsDirectory(const std::string& filename) {
#endif
if (result < 0) {
- NGLOG_DEBUG(Common_Filesystem, "stat failed on {}: {}", filename, GetLastErrorMsg());
+ LOG_DEBUG(Common_Filesystem, "stat failed on {}: {}", filename, GetLastErrorMsg());
return false;
}
@@ -128,29 +128,29 @@ bool IsDirectory(const std::string& filename) {
// Deletes a given filename, return true on success
// Doesn't supports deleting a directory
bool Delete(const std::string& filename) {
- NGLOG_TRACE(Common_Filesystem, "file {}", filename);
+ LOG_TRACE(Common_Filesystem, "file {}", filename);
// Return true because we care about the file no
// being there, not the actual delete.
if (!Exists(filename)) {
- NGLOG_DEBUG(Common_Filesystem, "{} does not exist", filename);
+ LOG_DEBUG(Common_Filesystem, "{} does not exist", filename);
return true;
}
// We can't delete a directory
if (IsDirectory(filename)) {
- NGLOG_ERROR(Common_Filesystem, "Failed: {} is a directory", filename);
+ LOG_ERROR(Common_Filesystem, "Failed: {} is a directory", filename);
return false;
}
#ifdef _WIN32
if (!DeleteFileW(Common::UTF8ToUTF16W(filename).c_str())) {
- NGLOG_ERROR(Common_Filesystem, "DeleteFile failed on {}: {}", filename, GetLastErrorMsg());
+ LOG_ERROR(Common_Filesystem, "DeleteFile failed on {}: {}", filename, GetLastErrorMsg());
return false;
}
#else
if (unlink(filename.c_str()) == -1) {
- NGLOG_ERROR(Common_Filesystem, "unlink failed on {}: {}", filename, GetLastErrorMsg());
+ LOG_ERROR(Common_Filesystem, "unlink failed on {}: {}", filename, GetLastErrorMsg());
return false;
}
#endif
@@ -160,16 +160,16 @@ bool Delete(const std::string& filename) {
// Returns true if successful, or path already exists.
bool CreateDir(const std::string& path) {
- NGLOG_TRACE(Common_Filesystem, "directory {}", path);
+ LOG_TRACE(Common_Filesystem, "directory {}", path);
#ifdef _WIN32
if (::CreateDirectoryW(Common::UTF8ToUTF16W(path).c_str(), nullptr))
return true;
DWORD error = GetLastError();
if (error == ERROR_ALREADY_EXISTS) {
- NGLOG_DEBUG(Common_Filesystem, "CreateDirectory failed on {}: already exists", path);
+ LOG_DEBUG(Common_Filesystem, "CreateDirectory failed on {}: already exists", path);
return true;
}
- NGLOG_ERROR(Common_Filesystem, "CreateDirectory failed on {}: {}", path, error);
+ LOG_ERROR(Common_Filesystem, "CreateDirectory failed on {}: {}", path, error);
return false;
#else
if (mkdir(path.c_str(), 0755) == 0)
@@ -178,11 +178,11 @@ bool CreateDir(const std::string& path) {
int err = errno;
if (err == EEXIST) {
- NGLOG_DEBUG(Common_Filesystem, "mkdir failed on {}: already exists", path);
+ LOG_DEBUG(Common_Filesystem, "mkdir failed on {}: already exists", path);
return true;
}
- NGLOG_ERROR(Common_Filesystem, "mkdir failed on {}: {}", path, strerror(err));
+ LOG_ERROR(Common_Filesystem, "mkdir failed on {}: {}", path, strerror(err));
return false;
#endif
}
@@ -190,10 +190,10 @@ bool CreateDir(const std::string& path) {
// Creates the full path of fullPath returns true on success
bool CreateFullPath(const std::string& fullPath) {
int panicCounter = 100;
- NGLOG_TRACE(Common_Filesystem, "path {}", fullPath);
+ LOG_TRACE(Common_Filesystem, "path {}", fullPath);
if (FileUtil::Exists(fullPath)) {
- NGLOG_DEBUG(Common_Filesystem, "path exists {}", fullPath);
+ LOG_DEBUG(Common_Filesystem, "path exists {}", fullPath);
return true;
}
@@ -209,14 +209,14 @@ bool CreateFullPath(const std::string& fullPath) {
// Include the '/' so the first call is CreateDir("/") rather than CreateDir("")
std::string const subPath(fullPath.substr(0, position + 1));
if (!FileUtil::IsDirectory(subPath) && !FileUtil::CreateDir(subPath)) {
- NGLOG_ERROR(Common, "CreateFullPath: directory creation failed");
+ LOG_ERROR(Common, "CreateFullPath: directory creation failed");
return false;
}
// A safety check
panicCounter--;
if (panicCounter <= 0) {
- NGLOG_ERROR(Common, "CreateFullPath: directory structure is too deep");
+ LOG_ERROR(Common, "CreateFullPath: directory structure is too deep");
return false;
}
position++;
@@ -225,11 +225,11 @@ bool CreateFullPath(const std::string& fullPath) {
// Deletes a directory filename, returns true on success
bool DeleteDir(const std::string& filename) {
- NGLOG_TRACE(Common_Filesystem, "directory {}", filename);
+ LOG_TRACE(Common_Filesystem, "directory {}", filename);
// check if a directory
if (!FileUtil::IsDirectory(filename)) {
- NGLOG_ERROR(Common_Filesystem, "Not a directory {}", filename);
+ LOG_ERROR(Common_Filesystem, "Not a directory {}", filename);
return false;
}
@@ -240,14 +240,14 @@ bool DeleteDir(const std::string& filename) {
if (rmdir(filename.c_str()) == 0)
return true;
#endif
- NGLOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg());
+ LOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg());
return false;
}
// renames file srcFilename to destFilename, returns true on success
bool Rename(const std::string& srcFilename, const std::string& destFilename) {
- NGLOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename);
+ LOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename);
#ifdef _WIN32
if (_wrename(Common::UTF8ToUTF16W(srcFilename).c_str(),
Common::UTF8ToUTF16W(destFilename).c_str()) == 0)
@@ -256,21 +256,21 @@ bool Rename(const std::string& srcFilename, const std::string& destFilename) {
if (rename(srcFilename.c_str(), destFilename.c_str()) == 0)
return true;
#endif
- NGLOG_ERROR(Common_Filesystem, "failed {} --> {}: {}", srcFilename, destFilename,
- GetLastErrorMsg());
+ LOG_ERROR(Common_Filesystem, "failed {} --> {}: {}", srcFilename, destFilename,
+ GetLastErrorMsg());
return false;
}
// copies file srcFilename to destFilename, returns true on success
bool Copy(const std::string& srcFilename, const std::string& destFilename) {
- NGLOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename);
+ LOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename);
#ifdef _WIN32
if (CopyFileW(Common::UTF8ToUTF16W(srcFilename).c_str(),
Common::UTF8ToUTF16W(destFilename).c_str(), FALSE))
return true;
- NGLOG_ERROR(Common_Filesystem, "failed {} --> {}: {}", srcFilename, destFilename,
- GetLastErrorMsg());
+ LOG_ERROR(Common_Filesystem, "failed {} --> {}: {}", srcFilename, destFilename,
+ GetLastErrorMsg());
return false;
#else
@@ -282,8 +282,8 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) {
// Open input file
FILE* input = fopen(srcFilename.c_str(), "rb");
if (!input) {
- NGLOG_ERROR(Common_Filesystem, "opening input failed {} --> {}: {}", srcFilename,
- destFilename, GetLastErrorMsg());
+ LOG_ERROR(Common_Filesystem, "opening input failed {} --> {}: {}", srcFilename,
+ destFilename, GetLastErrorMsg());
return false;
}
@@ -291,8 +291,8 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) {
FILE* output = fopen(destFilename.c_str(), "wb");
if (!output) {
fclose(input);
- NGLOG_ERROR(Common_Filesystem, "opening output failed {} --> {}: {}", srcFilename,
- destFilename, GetLastErrorMsg());
+ LOG_ERROR(Common_Filesystem, "opening output failed {} --> {}: {}", srcFilename,
+ destFilename, GetLastErrorMsg());
return false;
}
@@ -302,8 +302,8 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) {
size_t rnum = fread(buffer, sizeof(char), BSIZE, input);
if (rnum != BSIZE) {
if (ferror(input) != 0) {
- NGLOG_ERROR(Common_Filesystem, "failed reading from source, {} --> {}: {}",
- srcFilename, destFilename, GetLastErrorMsg());
+ LOG_ERROR(Common_Filesystem, "failed reading from source, {} --> {}: {}",
+ srcFilename, destFilename, GetLastErrorMsg());
goto bail;
}
}
@@ -311,8 +311,8 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) {
// write output
size_t wnum = fwrite(buffer, sizeof(char), rnum, output);
if (wnum != rnum) {
- NGLOG_ERROR(Common_Filesystem, "failed writing to output, {} --> {}: {}", srcFilename,
- destFilename, GetLastErrorMsg());
+ LOG_ERROR(Common_Filesystem, "failed writing to output, {} --> {}: {}", srcFilename,
+ destFilename, GetLastErrorMsg());
goto bail;
}
}
@@ -332,12 +332,12 @@ bail:
// Returns the size of filename (64bit)
u64 GetSize(const std::string& filename) {
if (!Exists(filename)) {
- NGLOG_ERROR(Common_Filesystem, "failed {}: No such file", filename);
+ LOG_ERROR(Common_Filesystem, "failed {}: No such file", filename);
return 0;
}
if (IsDirectory(filename)) {
- NGLOG_ERROR(Common_Filesystem, "failed {}: is a directory", filename);
+ LOG_ERROR(Common_Filesystem, "failed {}: is a directory", filename);
return 0;
}
@@ -348,11 +348,11 @@ u64 GetSize(const std::string& filename) {
if (stat(filename.c_str(), &buf) == 0)
#endif
{
- NGLOG_TRACE(Common_Filesystem, "{}: {}", filename, buf.st_size);
+ LOG_TRACE(Common_Filesystem, "{}: {}", filename, buf.st_size);
return buf.st_size;
}
- NGLOG_ERROR(Common_Filesystem, "Stat failed {}: {}", filename, GetLastErrorMsg());
+ LOG_ERROR(Common_Filesystem, "Stat failed {}: {}", filename, GetLastErrorMsg());
return 0;
}
@@ -360,7 +360,7 @@ u64 GetSize(const std::string& filename) {
u64 GetSize(const int fd) {
struct stat buf;
if (fstat(fd, &buf) != 0) {
- NGLOG_ERROR(Common_Filesystem, "GetSize: stat failed {}: {}", fd, GetLastErrorMsg());
+ LOG_ERROR(Common_Filesystem, "GetSize: stat failed {}: {}", fd, GetLastErrorMsg());
return 0;
}
return buf.st_size;
@@ -371,14 +371,12 @@ u64 GetSize(FILE* f) {
// can't use off_t here because it can be 32-bit
u64 pos = ftello(f);
if (fseeko(f, 0, SEEK_END) != 0) {
- NGLOG_ERROR(Common_Filesystem, "GetSize: seek failed {}: {}", fmt::ptr(f),
- GetLastErrorMsg());
+ LOG_ERROR(Common_Filesystem, "GetSize: seek failed {}: {}", fmt::ptr(f), GetLastErrorMsg());
return 0;
}
u64 size = ftello(f);
if ((size != pos) && (fseeko(f, pos, SEEK_SET) != 0)) {
- NGLOG_ERROR(Common_Filesystem, "GetSize: seek failed {}: {}", fmt::ptr(f),
- GetLastErrorMsg());
+ LOG_ERROR(Common_Filesystem, "GetSize: seek failed {}: {}", fmt::ptr(f), GetLastErrorMsg());
return 0;
}
return size;
@@ -386,10 +384,10 @@ u64 GetSize(FILE* f) {
// creates an empty file filename, returns true on success
bool CreateEmptyFile(const std::string& filename) {
- NGLOG_TRACE(Common_Filesystem, "{}", filename);
+ LOG_TRACE(Common_Filesystem, "{}", filename);
if (!FileUtil::IOFile(filename, "wb")) {
- NGLOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg());
+ LOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg());
return false;
}
@@ -398,7 +396,7 @@ bool CreateEmptyFile(const std::string& filename) {
bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string& directory,
DirectoryEntryCallable callback) {
- NGLOG_TRACE(Common_Filesystem, "directory {}", directory);
+ LOG_TRACE(Common_Filesystem, "directory {}", directory);
// How many files + directories we found
unsigned found_entries = 0;
@@ -556,7 +554,7 @@ std::string GetCurrentDir() {
char* dir;
if (!(dir = getcwd(nullptr, 0))) {
#endif
- NGLOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg());
+ LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg());
return nullptr;
}
#ifdef _WIN32
@@ -676,11 +674,11 @@ std::string GetSysDirectory() {
#endif
sysDir += DIR_SEP;
- NGLOG_DEBUG(Common_Filesystem, "Setting to {}:", sysDir);
+ LOG_DEBUG(Common_Filesystem, "Setting to {}:", sysDir);
return sysDir;
}
-// Returns a string with a Citra data dir or file in the user's home
+// Returns a string with a yuzu data dir or file in the user's home
// directory. To be used in "multi-user" mode (that is, installed).
const std::string& GetUserPath(const unsigned int DirIDX, const std::string& newPath) {
static std::string paths[NUM_PATH_INDICES];
@@ -692,7 +690,7 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string& new
if (!FileUtil::IsDirectory(paths[D_USER_IDX])) {
paths[D_USER_IDX] = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP;
} else {
- NGLOG_INFO(Common_Filesystem, "Using the local user directory");
+ LOG_INFO(Common_Filesystem, "Using the local user directory");
}
paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
@@ -715,11 +713,13 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string& new
paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP;
paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP;
+ // TODO: Put the logs in a better location for each OS
+ paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOG_DIR DIR_SEP;
}
if (!newPath.empty()) {
if (!FileUtil::IsDirectory(newPath)) {
- NGLOG_ERROR(Common_Filesystem, "Invalid path specified {}", newPath);
+ LOG_ERROR(Common_Filesystem, "Invalid path specified {}", newPath);
return paths[DirIDX];
} else {
paths[DirIDX] = newPath;
@@ -801,8 +801,8 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
IOFile::IOFile() {}
-IOFile::IOFile(const std::string& filename, const char openmode[]) {
- Open(filename, openmode);
+IOFile::IOFile(const std::string& filename, const char openmode[], int flags) {
+ Open(filename, openmode, flags);
}
IOFile::~IOFile() {
@@ -823,11 +823,16 @@ void IOFile::Swap(IOFile& other) noexcept {
std::swap(m_good, other.m_good);
}
-bool IOFile::Open(const std::string& filename, const char openmode[]) {
+bool IOFile::Open(const std::string& filename, const char openmode[], int flags) {
Close();
#ifdef _WIN32
- _wfopen_s(&m_file, Common::UTF8ToUTF16W(filename).c_str(),
- Common::UTF8ToUTF16W(openmode).c_str());
+ if (flags != 0) {
+ m_file = _wfsopen(Common::UTF8ToUTF16W(filename).c_str(),
+ Common::UTF8ToUTF16W(openmode).c_str(), flags);
+ } else {
+ _wfopen_s(&m_file, Common::UTF8ToUTF16W(filename).c_str(),
+ Common::UTF8ToUTF16W(openmode).c_str());
+ }
#else
m_file = fopen(filename.c_str(), openmode);
#endif
diff --git a/src/common/file_util.h b/src/common/file_util.h
index fc6b3ea46..5bc7fbf7c 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -156,7 +156,10 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
class IOFile : public NonCopyable {
public:
IOFile();
- IOFile(const std::string& filename, const char openmode[]);
+ // flags is used for windows specific file open mode flags, which
+ // allows yuzu to open the logs in shared write mode, so that the file
+ // isn't considered "locked" while yuzu is open and people can open the log file and view it
+ IOFile(const std::string& filename, const char openmode[], int flags = 0);
~IOFile();
@@ -165,7 +168,7 @@ public:
void Swap(IOFile& other) noexcept;
- bool Open(const std::string& filename, const char openmode[]);
+ bool Open(const std::string& filename, const char openmode[], int flags = 0);
bool Close();
template <typename T>
@@ -220,6 +223,10 @@ public:
return WriteArray(&object, 1);
}
+ size_t WriteString(const std::string& str) {
+ return WriteArray(str.c_str(), str.length());
+ }
+
bool IsOpen() const {
return nullptr != m_file;
}
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index c26b20062..242914c6a 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -2,16 +2,145 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <utility>
+#include <algorithm>
+#include <array>
+#include <chrono>
+#include <condition_variable>
+#include <memory>
+#include <thread>
+#ifdef _WIN32
+#include <share.h> // For _SH_DENYWR
+#else
+#define _SH_DENYWR 0
+#endif
#include "common/assert.h"
+#include "common/common_funcs.h" // snprintf compatibility define
#include "common/logging/backend.h"
-#include "common/logging/filter.h"
#include "common/logging/log.h"
#include "common/logging/text_formatter.h"
#include "common/string_util.h"
+#include "common/threadsafe_queue.h"
namespace Log {
+/**
+ * Static state as a singleton.
+ */
+class Impl {
+public:
+ static Impl& Instance() {
+ static Impl backend;
+ return backend;
+ }
+
+ Impl(Impl const&) = delete;
+ const Impl& operator=(Impl const&) = delete;
+
+ void PushEntry(Entry e) {
+ std::lock_guard<std::mutex> lock(message_mutex);
+ message_queue.Push(std::move(e));
+ message_cv.notify_one();
+ }
+
+ void AddBackend(std::unique_ptr<Backend> backend) {
+ std::lock_guard<std::mutex> lock(writing_mutex);
+ backends.push_back(std::move(backend));
+ }
+
+ void RemoveBackend(const std::string& backend_name) {
+ std::lock_guard<std::mutex> lock(writing_mutex);
+ auto it = std::remove_if(backends.begin(), backends.end(), [&backend_name](const auto& i) {
+ return !strcmp(i->GetName(), backend_name.c_str());
+ });
+ backends.erase(it, backends.end());
+ }
+
+ const Filter& GetGlobalFilter() const {
+ return filter;
+ }
+
+ void SetGlobalFilter(const Filter& f) {
+ filter = f;
+ }
+
+ Backend* GetBackend(const std::string& backend_name) {
+ auto it = std::find_if(backends.begin(), backends.end(), [&backend_name](const auto& i) {
+ return !strcmp(i->GetName(), backend_name.c_str());
+ });
+ if (it == backends.end())
+ return nullptr;
+ return it->get();
+ }
+
+private:
+ Impl() {
+ backend_thread = std::thread([&] {
+ Entry entry;
+ auto write_logs = [&](Entry& e) {
+ std::lock_guard<std::mutex> lock(writing_mutex);
+ for (const auto& backend : backends) {
+ backend->Write(e);
+ }
+ };
+ while (true) {
+ std::unique_lock<std::mutex> lock(message_mutex);
+ message_cv.wait(lock, [&] { return !running || message_queue.Pop(entry); });
+ if (!running) {
+ break;
+ }
+ write_logs(entry);
+ }
+ // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a case
+ // where a system is repeatedly spamming logs even on close.
+ constexpr int MAX_LOGS_TO_WRITE = 100;
+ int logs_written = 0;
+ while (logs_written++ < MAX_LOGS_TO_WRITE && message_queue.Pop(entry)) {
+ write_logs(entry);
+ }
+ });
+ }
+
+ ~Impl() {
+ running = false;
+ message_cv.notify_one();
+ backend_thread.join();
+ }
+
+ std::atomic_bool running{true};
+ std::mutex message_mutex, writing_mutex;
+ std::condition_variable message_cv;
+ std::thread backend_thread;
+ std::vector<std::unique_ptr<Backend>> backends;
+ Common::MPSCQueue<Log::Entry> message_queue;
+ Filter filter;
+};
+
+void ConsoleBackend::Write(const Entry& entry) {
+ PrintMessage(entry);
+}
+
+void ColorConsoleBackend::Write(const Entry& entry) {
+ PrintColoredMessage(entry);
+}
+
+// _SH_DENYWR allows read only access to the file for other programs.
+// It is #defined to 0 on other platforms
+FileBackend::FileBackend(const std::string& filename)
+ : file(filename, "w", _SH_DENYWR), bytes_written(0) {}
+
+void FileBackend::Write(const Entry& entry) {
+ // prevent logs from going over the maximum size (in case its spamming and the user doesn't
+ // know)
+ constexpr size_t MAX_BYTES_WRITTEN = 50 * 1024L * 1024L;
+ if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) {
+ return;
+ }
+ bytes_written += file.WriteString(FormatLogMessage(entry) + '\n');
+ if (entry.log_level >= Level::Error) {
+ file.Flush();
+ }
+}
+
/// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
#define ALL_LOG_CLASSES() \
CLS(Log) \
@@ -125,20 +254,32 @@ Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsign
return entry;
}
-static Filter* filter = nullptr;
+void SetGlobalFilter(const Filter& filter) {
+ Impl::Instance().SetGlobalFilter(filter);
+}
+
+void AddBackend(std::unique_ptr<Backend> backend) {
+ Impl::Instance().AddBackend(std::move(backend));
+}
-void SetFilter(Filter* new_filter) {
- filter = new_filter;
+void RemoveBackend(const std::string& backend_name) {
+ Impl::Instance().RemoveBackend(backend_name);
+}
+
+Backend* GetBackend(const std::string& backend_name) {
+ return Impl::Instance().GetBackend(backend_name);
}
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
unsigned int line_num, const char* function, const char* format,
const fmt::format_args& args) {
- if (filter && !filter->CheckMessage(log_class, log_level))
+ auto filter = Impl::Instance().GetGlobalFilter();
+ if (!filter.CheckMessage(log_class, log_level))
return;
+
Entry entry =
CreateEntry(log_class, log_level, filename, line_num, function, fmt::vformat(format, args));
- PrintColoredMessage(entry);
+ Impl::Instance().PushEntry(std::move(entry));
}
-} // namespace Log
+} // namespace Log \ No newline at end of file
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index 7e81efb23..57cdf6b2d 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -1,13 +1,15 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-
#pragma once
#include <chrono>
#include <cstdarg>
+#include <memory>
#include <string>
#include <utility>
+#include "common/file_util.h"
+#include "common/logging/filter.h"
#include "common/logging/log.h"
namespace Log {
@@ -35,6 +37,80 @@ struct Entry {
};
/**
+ * Interface for logging backends. As loggers can be created and removed at runtime, this can be
+ * used by a frontend for adding a custom logging backend as needed
+ */
+class Backend {
+public:
+ virtual ~Backend() = default;
+ virtual void SetFilter(const Filter& new_filter) {
+ filter = new_filter;
+ }
+ virtual const char* GetName() const = 0;
+ virtual void Write(const Entry& entry) = 0;
+
+private:
+ Filter filter;
+};
+
+/**
+ * Backend that writes to stderr without any color commands
+ */
+class ConsoleBackend : public Backend {
+public:
+ static const char* Name() {
+ return "console";
+ }
+ const char* GetName() const override {
+ return Name();
+ }
+ void Write(const Entry& entry) override;
+};
+
+/**
+ * Backend that writes to stderr and with color
+ */
+class ColorConsoleBackend : public Backend {
+public:
+ static const char* Name() {
+ return "color_console";
+ }
+
+ const char* GetName() const override {
+ return Name();
+ }
+ void Write(const Entry& entry) override;
+};
+
+/**
+ * Backend that writes to a file passed into the constructor
+ */
+class FileBackend : public Backend {
+public:
+ explicit FileBackend(const std::string& filename);
+
+ static const char* Name() {
+ return "file";
+ }
+
+ const char* GetName() const override {
+ return Name();
+ }
+
+ void Write(const Entry& entry) override;
+
+private:
+ FileUtil::IOFile file;
+ size_t bytes_written;
+};
+
+void AddBackend(std::unique_ptr<Backend> backend);
+
+void RemoveBackend(const std::string& backend_name);
+
+Backend* GetBackend(const std::string& backend_name);
+
+/**
* Returns the name of the passed log class as a C-string. Subclasses are separated by periods
* instead of underscores as in the enumeration.
*/
@@ -49,5 +125,10 @@ const char* GetLevelName(Level log_level);
Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
const char* function, std::string message);
-void SetFilter(Filter* filter);
-} // namespace Log
+/**
+ * The global filter will prevent any messages from even being processed if they are filtered. Each
+ * backend can have a filter, but if the level is lower than the global filter, the backend will
+ * never get the message
+ */
+void SetGlobalFilter(const Filter& filter);
+} // namespace Log \ No newline at end of file
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
index 428723dce..4e783a577 100644
--- a/src/common/logging/filter.cpp
+++ b/src/common/logging/filter.cpp
@@ -65,14 +65,14 @@ bool Filter::ParseFilterRule(const std::string::const_iterator begin,
const std::string::const_iterator end) {
auto level_separator = std::find(begin, end, ':');
if (level_separator == end) {
- NGLOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: %s",
- std::string(begin, end).c_str());
+ LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: {}",
+ std::string(begin, end));
return false;
}
const Level level = GetLevelByName(level_separator + 1, end);
if (level == Level::Count) {
- NGLOG_ERROR(Log, "Unknown log level in filter: %s", std::string(begin, end).c_str());
+ LOG_ERROR(Log, "Unknown log level in filter: {}", std::string(begin, end));
return false;
}
@@ -83,7 +83,7 @@ bool Filter::ParseFilterRule(const std::string::const_iterator begin,
const Class log_class = GetClassByName(begin, level_separator);
if (log_class == Class::Count) {
- NGLOG_ERROR(Log, "Unknown log class in filter: %s", std::string(begin, end).c_str());
+ LOG_ERROR(Log, "Unknown log class in filter: {}", std::string(begin, end));
return false;
}
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index c5015531c..e96c90e16 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -109,25 +109,25 @@ void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsig
} // namespace Log
#ifdef _DEBUG
-#define NGLOG_TRACE(log_class, ...) \
+#define LOG_TRACE(log_class, ...) \
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Trace, __FILE__, __LINE__, \
__func__, __VA_ARGS__)
#else
-#define NGLOG_TRACE(log_class, fmt, ...) (void(0))
+#define LOG_TRACE(log_class, fmt, ...) (void(0))
#endif
-#define NGLOG_DEBUG(log_class, ...) \
+#define LOG_DEBUG(log_class, ...) \
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Debug, __FILE__, __LINE__, \
__func__, __VA_ARGS__)
-#define NGLOG_INFO(log_class, ...) \
+#define LOG_INFO(log_class, ...) \
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Info, __FILE__, __LINE__, \
__func__, __VA_ARGS__)
-#define NGLOG_WARNING(log_class, ...) \
+#define LOG_WARNING(log_class, ...) \
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Warning, __FILE__, __LINE__, \
__func__, __VA_ARGS__)
-#define NGLOG_ERROR(log_class, ...) \
+#define LOG_ERROR(log_class, ...) \
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Error, __FILE__, __LINE__, \
__func__, __VA_ARGS__)
-#define NGLOG_CRITICAL(log_class, ...) \
+#define LOG_CRITICAL(log_class, ...) \
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Critical, __FILE__, __LINE__, \
__func__, __VA_ARGS__)
diff --git a/src/common/memory_util.cpp b/src/common/memory_util.cpp
index 4d1ec8fb9..09462ccee 100644
--- a/src/common/memory_util.cpp
+++ b/src/common/memory_util.cpp
@@ -16,7 +16,7 @@
#include <sys/mman.h>
#endif
-#if !defined(_WIN32) && defined(ARCHITECTURE_X64) && !defined(MAP_32BIT)
+#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
#include <unistd.h>
#define PAGE_MASK (getpagesize() - 1)
#define round_page(x) ((((unsigned long)(x)) + PAGE_MASK) & ~(PAGE_MASK))
@@ -30,7 +30,7 @@ void* AllocateExecutableMemory(size_t size, bool low) {
void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
#else
static char* map_hint = nullptr;
-#if defined(ARCHITECTURE_X64) && !defined(MAP_32BIT)
+#if defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
// This OS has no flag to enforce allocation below the 4 GB boundary,
// but if we hint that we want a low address it is very likely we will
// get one.
@@ -42,7 +42,7 @@ void* AllocateExecutableMemory(size_t size, bool low) {
#endif
void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_ANON | MAP_PRIVATE
-#if defined(ARCHITECTURE_X64) && defined(MAP_32BIT)
+#if defined(ARCHITECTURE_x86_64) && defined(MAP_32BIT)
| (low ? MAP_32BIT : 0)
#endif
,
@@ -55,9 +55,9 @@ void* AllocateExecutableMemory(size_t size, bool low) {
if (ptr == MAP_FAILED) {
ptr = nullptr;
#endif
- NGLOG_ERROR(Common_Memory, "Failed to allocate executable memory");
+ LOG_ERROR(Common_Memory, "Failed to allocate executable memory");
}
-#if !defined(_WIN32) && defined(ARCHITECTURE_X64) && !defined(MAP_32BIT)
+#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
else {
if (low) {
map_hint += size;
@@ -68,7 +68,7 @@ void* AllocateExecutableMemory(size_t size, bool low) {
#if EMU_ARCH_BITS == 64
if ((u64)ptr >= 0x80000000 && low == true)
- NGLOG_ERROR(Common_Memory, "Executable memory ended up above 2GB!");
+ LOG_ERROR(Common_Memory, "Executable memory ended up above 2GB!");
#endif
return ptr;
@@ -85,7 +85,7 @@ void* AllocateMemoryPages(size_t size) {
#endif
if (ptr == nullptr)
- NGLOG_ERROR(Common_Memory, "Failed to allocate raw memory");
+ LOG_ERROR(Common_Memory, "Failed to allocate raw memory");
return ptr;
}
@@ -99,12 +99,12 @@ void* AllocateAlignedMemory(size_t size, size_t alignment) {
ptr = memalign(alignment, size);
#else
if (posix_memalign(&ptr, alignment, size) != 0)
- NGLOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
+ LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
#endif
#endif
if (ptr == nullptr)
- NGLOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
+ LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
return ptr;
}
@@ -113,7 +113,7 @@ void FreeMemoryPages(void* ptr, size_t size) {
if (ptr) {
#ifdef _WIN32
if (!VirtualFree(ptr, 0, MEM_RELEASE))
- NGLOG_ERROR(Common_Memory, "FreeMemoryPages failed!\n{}", GetLastErrorMsg());
+ LOG_ERROR(Common_Memory, "FreeMemoryPages failed!\n{}", GetLastErrorMsg());
#else
munmap(ptr, size);
#endif
@@ -134,7 +134,7 @@ void WriteProtectMemory(void* ptr, size_t size, bool allowExecute) {
#ifdef _WIN32
DWORD oldValue;
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue))
- NGLOG_ERROR(Common_Memory, "WriteProtectMemory failed!\n{}", GetLastErrorMsg());
+ LOG_ERROR(Common_Memory, "WriteProtectMemory failed!\n{}", GetLastErrorMsg());
#else
mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ);
#endif
@@ -145,7 +145,7 @@ void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute) {
DWORD oldValue;
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE,
&oldValue))
- NGLOG_ERROR(Common_Memory, "UnWriteProtectMemory failed!\n{}", GetLastErrorMsg());
+ LOG_ERROR(Common_Memory, "UnWriteProtectMemory failed!\n{}", GetLastErrorMsg());
#else
mprotect(ptr, size,
allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ);
diff --git a/src/common/param_package.cpp b/src/common/param_package.cpp
index ab0154133..e0df430ab 100644
--- a/src/common/param_package.cpp
+++ b/src/common/param_package.cpp
@@ -25,7 +25,7 @@ ParamPackage::ParamPackage(const std::string& serialized) {
std::vector<std::string> key_value;
Common::SplitString(pair, KEY_VALUE_SEPARATOR, key_value);
if (key_value.size() != 2) {
- NGLOG_ERROR(Common, "invalid key pair {}", pair);
+ LOG_ERROR(Common, "invalid key pair {}", pair);
continue;
}
@@ -64,7 +64,7 @@ std::string ParamPackage::Serialize() const {
std::string ParamPackage::Get(const std::string& key, const std::string& default_value) const {
auto pair = data.find(key);
if (pair == data.end()) {
- NGLOG_DEBUG(Common, "key '{}' not found", key);
+ LOG_DEBUG(Common, "key '{}' not found", key);
return default_value;
}
@@ -74,14 +74,14 @@ std::string ParamPackage::Get(const std::string& key, const std::string& default
int ParamPackage::Get(const std::string& key, int default_value) const {
auto pair = data.find(key);
if (pair == data.end()) {
- NGLOG_DEBUG(Common, "key '{}' not found", key);
+ LOG_DEBUG(Common, "key '{}' not found", key);
return default_value;
}
try {
return std::stoi(pair->second);
} catch (const std::logic_error&) {
- NGLOG_ERROR(Common, "failed to convert {} to int", pair->second);
+ LOG_ERROR(Common, "failed to convert {} to int", pair->second);
return default_value;
}
}
@@ -89,14 +89,14 @@ int ParamPackage::Get(const std::string& key, int default_value) const {
float ParamPackage::Get(const std::string& key, float default_value) const {
auto pair = data.find(key);
if (pair == data.end()) {
- NGLOG_DEBUG(Common, "key {} not found", key);
+ LOG_DEBUG(Common, "key {} not found", key);
return default_value;
}
try {
return std::stof(pair->second);
} catch (const std::logic_error&) {
- NGLOG_ERROR(Common, "failed to convert {} to float", pair->second);
+ LOG_ERROR(Common, "failed to convert {} to float", pair->second);
return default_value;
}
}
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp
index 646400db0..ea9d8f77c 100644
--- a/src/common/string_util.cpp
+++ b/src/common/string_util.cpp
@@ -281,7 +281,7 @@ static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>&
iconv_t const conv_desc = iconv_open("UTF-8", fromcode);
if ((iconv_t)(-1) == conv_desc) {
- NGLOG_ERROR(Common, "Iconv initialization failure [{}]: {}", fromcode, strerror(errno));
+ LOG_ERROR(Common, "Iconv initialization failure [{}]: {}", fromcode, strerror(errno));
iconv_close(conv_desc);
return {};
}
@@ -310,7 +310,7 @@ static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>&
++src_buffer;
}
} else {
- NGLOG_ERROR(Common, "iconv failure [{}]: {}", fromcode, strerror(errno));
+ LOG_ERROR(Common, "iconv failure [{}]: {}", fromcode, strerror(errno));
break;
}
}
@@ -329,7 +329,7 @@ std::u16string UTF8ToUTF16(const std::string& input) {
iconv_t const conv_desc = iconv_open("UTF-16LE", "UTF-8");
if ((iconv_t)(-1) == conv_desc) {
- NGLOG_ERROR(Common, "Iconv initialization failure [UTF-8]: {}", strerror(errno));
+ LOG_ERROR(Common, "Iconv initialization failure [UTF-8]: {}", strerror(errno));
iconv_close(conv_desc);
return {};
}
@@ -358,7 +358,7 @@ std::u16string UTF8ToUTF16(const std::string& input) {
++src_buffer;
}
} else {
- NGLOG_ERROR(Common, "iconv failure [UTF-8]: {}", strerror(errno));
+ LOG_ERROR(Common, "iconv failure [UTF-8]: {}", strerror(errno));
break;
}
}
diff --git a/src/common/swap.h b/src/common/swap.h
index 4a4012d1a..f025f7450 100644
--- a/src/common/swap.h
+++ b/src/common/swap.h
@@ -69,7 +69,7 @@ inline u32 swap32(u32 _data) {
inline u64 swap64(u64 _data) {
return _byteswap_uint64(_data);
}
-#elif _M_ARM
+#elif ARCHITECTURE_ARM
inline u16 swap16(u16 _data) {
u32 data = _data;
__asm__("rev16 %0, %1\n" : "=l"(data) : "l"(data));