mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	Merge pull request #6638 from GPUCode/new-log
common: Backport yuzu log improvements
This commit is contained in:
		
						commit
						4ccd9f24fb
					
				
					 48 changed files with 1201 additions and 750 deletions
				
			
		
							
								
								
									
										2
									
								
								externals/boost
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								externals/boost
									
										
									
									
										vendored
									
									
								
							|  | @ -1 +1 @@ | ||||||
| Subproject commit 32f5cd8ebb35b29ccb3860861bd285f80804bc85 | Subproject commit 700ae2eff3134792f09cea2b051666688b1d5b97 | ||||||
|  | @ -7,8 +7,8 @@ | ||||||
| #include <sstream> | #include <sstream> | ||||||
| #include <unordered_map> | #include <unordered_map> | ||||||
| #include <inih/cpp/INIReader.h> | #include <inih/cpp/INIReader.h> | ||||||
| 
 |  | ||||||
| #include "common/file_util.h" | #include "common/file_util.h" | ||||||
|  | #include "common/logging/backend.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "common/param_package.h" | #include "common/param_package.h" | ||||||
| #include "common/settings.h" | #include "common/settings.h" | ||||||
|  | @ -260,6 +260,12 @@ void Config::ReadValues() { | ||||||
|     // Miscellaneous
 |     // Miscellaneous
 | ||||||
|     ReadSetting("Miscellaneous", Settings::values.log_filter); |     ReadSetting("Miscellaneous", Settings::values.log_filter); | ||||||
| 
 | 
 | ||||||
|  |     // Apply the log_filter setting as the logger has already been initialized
 | ||||||
|  |     // and doesn't pick up the filter on its own.
 | ||||||
|  |     Common::Log::Filter filter; | ||||||
|  |     filter.ParseFilterString(Settings::values.log_filter.GetValue()); | ||||||
|  |     Common::Log::SetGlobalFilter(filter); | ||||||
|  | 
 | ||||||
|     // Debugging
 |     // Debugging
 | ||||||
|     Settings::values.record_frame_times = |     Settings::values.record_frame_times = | ||||||
|         sdl2_config->GetBoolean("Debugging", "record_frame_times", false); |         sdl2_config->GetBoolean("Debugging", "record_frame_times", false); | ||||||
|  |  | ||||||
|  | @ -180,12 +180,6 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { | ||||||
|     if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION) != JNI_OK) |     if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION) != JNI_OK) | ||||||
|         return JNI_ERR; |         return JNI_ERR; | ||||||
| 
 | 
 | ||||||
|     // Initialize Logger
 |  | ||||||
|     Log::Filter log_filter; |  | ||||||
|     log_filter.ParseFilterString(Settings::values.log_filter.GetValue()); |  | ||||||
|     Log::SetGlobalFilter(log_filter); |  | ||||||
|     Log::AddBackend(std::make_unique<Log::LogcatBackend>()); |  | ||||||
| 
 |  | ||||||
|     // Initialize misc classes
 |     // Initialize misc classes
 | ||||||
|     s_savestate_info_class = reinterpret_cast<jclass>( |     s_savestate_info_class = reinterpret_cast<jclass>( | ||||||
|         env->NewGlobalRef(env->FindClass("org/citra/citra_emu/NativeLibrary$SavestateInfo"))); |         env->NewGlobalRef(env->FindClass("org/citra/citra_emu/NativeLibrary$SavestateInfo"))); | ||||||
|  |  | ||||||
|  | @ -141,7 +141,7 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) { | ||||||
|         app_loader->ReadProgramId(program_id); |         app_loader->ReadProgramId(program_id); | ||||||
|         GameSettings::LoadOverrides(program_id); |         GameSettings::LoadOverrides(program_id); | ||||||
|     } |     } | ||||||
|     Settings::Apply(); |     system.ApplySettings(); | ||||||
|     Settings::LogSettings(); |     Settings::LogSettings(); | ||||||
| 
 | 
 | ||||||
|     Camera::RegisterFactory("image", std::make_unique<Camera::StillImage::Factory>()); |     Camera::RegisterFactory("image", std::make_unique<Camera::StillImage::Factory>()); | ||||||
|  | @ -438,10 +438,8 @@ void Java_org_citra_citra_1emu_NativeLibrary_CreateConfigFile(JNIEnv* env, | ||||||
| 
 | 
 | ||||||
| void Java_org_citra_citra_1emu_NativeLibrary_CreateLogFile(JNIEnv* env, | void Java_org_citra_citra_1emu_NativeLibrary_CreateLogFile(JNIEnv* env, | ||||||
|                                                            [[maybe_unused]] jclass clazz) { |                                                            [[maybe_unused]] jclass clazz) { | ||||||
|     Log::RemoveBackend(Log::FileBackend::Name()); |     Common::Log::Initialize(); | ||||||
|     FileUtil::CreateFullPath(FileUtil::GetUserPath(FileUtil::UserPath::LogDir)); |     Common::Log::Start(); | ||||||
|     Log::AddBackend(std::make_unique<Log::FileBackend>( |  | ||||||
|         FileUtil::GetUserPath(FileUtil::UserPath::LogDir) + LOG_FILE)); |  | ||||||
|     LOG_INFO(Frontend, "Logging backend initialised"); |     LOG_INFO(Frontend, "Logging backend initialised"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -474,7 +472,7 @@ void Java_org_citra_citra_1emu_NativeLibrary_ReloadSettings(JNIEnv* env, | ||||||
|         GameSettings::LoadOverrides(program_id); |         GameSettings::LoadOverrides(program_id); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     Settings::Apply(); |     system.ApplySettings(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| jstring Java_org_citra_citra_1emu_NativeLibrary_GetUserSetting(JNIEnv* env, | jstring Java_org_citra_citra_1emu_NativeLibrary_GetUserSetting(JNIEnv* env, | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #include <memory> | ||||||
| #include <optional> | #include <optional> | ||||||
| #include <string> | #include <string> | ||||||
| #include <tuple> | #include <tuple> | ||||||
|  |  | ||||||
|  | @ -174,23 +174,11 @@ static void OnStatusMessageReceived(const Network::StatusMessageEntry& msg) { | ||||||
|         std::cout << std::endl << "* " << message << std::endl << std::endl; |         std::cout << std::endl << "* " << message << std::endl << std::endl; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void InitializeLogging() { |  | ||||||
|     Log::Filter log_filter(Log::Level::Debug); |  | ||||||
|     log_filter.ParseFilterString(Settings::values.log_filter.GetValue()); |  | ||||||
|     Log::SetGlobalFilter(log_filter); |  | ||||||
| 
 |  | ||||||
|     Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); |  | ||||||
| 
 |  | ||||||
|     const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); |  | ||||||
|     FileUtil::CreateFullPath(log_dir); |  | ||||||
|     Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE)); |  | ||||||
| #ifdef _WIN32 |  | ||||||
|     Log::AddBackend(std::make_unique<Log::DebuggerBackend>()); |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Application entry point
 | /// Application entry point
 | ||||||
| int main(int argc, char** argv) { | int main(int argc, char** argv) { | ||||||
|  |     Common::Log::Initialize(); | ||||||
|  |     Common::Log::SetColorConsoleBackendEnabled(true); | ||||||
|  |     Common::Log::Start(); | ||||||
|     Common::DetachedTasks detached_tasks; |     Common::DetachedTasks detached_tasks; | ||||||
|     Config config; |     Config config; | ||||||
|     int option_index = 0; |     int option_index = 0; | ||||||
|  | @ -201,8 +189,6 @@ int main(int argc, char** argv) { | ||||||
|     std::string movie_play; |     std::string movie_play; | ||||||
|     std::string dump_video; |     std::string dump_video; | ||||||
| 
 | 
 | ||||||
|     InitializeLogging(); |  | ||||||
| 
 |  | ||||||
|     char* endarg; |     char* endarg; | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
|     int argc_w; |     int argc_w; | ||||||
|  | @ -357,7 +343,7 @@ int main(int argc, char** argv) { | ||||||
|     // Apply the command line arguments
 |     // Apply the command line arguments
 | ||||||
|     Settings::values.gdbstub_port = gdb_port; |     Settings::values.gdbstub_port = gdb_port; | ||||||
|     Settings::values.use_gdbstub = use_gdbstub; |     Settings::values.use_gdbstub = use_gdbstub; | ||||||
|     Settings::Apply(); |     system.ApplySettings(); | ||||||
| 
 | 
 | ||||||
|     // Register frontend applets
 |     // Register frontend applets
 | ||||||
|     Frontend::RegisterDefaultApplets(); |     Frontend::RegisterDefaultApplets(); | ||||||
|  |  | ||||||
|  | @ -11,8 +11,8 @@ | ||||||
| #include "citra/config.h" | #include "citra/config.h" | ||||||
| #include "citra/default_ini.h" | #include "citra/default_ini.h" | ||||||
| #include "common/file_util.h" | #include "common/file_util.h" | ||||||
|  | #include "common/logging/backend.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "common/param_package.h" |  | ||||||
| #include "common/settings.h" | #include "common/settings.h" | ||||||
| #include "core/hle/service/service.h" | #include "core/hle/service/service.h" | ||||||
| #include "input_common/main.h" | #include "input_common/main.h" | ||||||
|  | @ -299,6 +299,12 @@ void Config::ReadValues() { | ||||||
|     // Miscellaneous
 |     // Miscellaneous
 | ||||||
|     ReadSetting("Miscellaneous", Settings::values.log_filter); |     ReadSetting("Miscellaneous", Settings::values.log_filter); | ||||||
| 
 | 
 | ||||||
|  |     // Apply the log_filter setting as the logger has already been initialized
 | ||||||
|  |     // and doesn't pick up the filter on its own.
 | ||||||
|  |     Common::Log::Filter filter; | ||||||
|  |     filter.ParseFilterString(Settings::values.log_filter.GetValue()); | ||||||
|  |     Common::Log::SetGlobalFilter(filter); | ||||||
|  | 
 | ||||||
|     // Debugging
 |     // Debugging
 | ||||||
|     Settings::values.record_frame_times = |     Settings::values.record_frame_times = | ||||||
|         sdl2_config->GetBoolean("Debugging", "record_frame_times", false); |         sdl2_config->GetBoolean("Debugging", "record_frame_times", false); | ||||||
|  |  | ||||||
|  | @ -9,7 +9,7 @@ | ||||||
| #include "citra_qt/debugger/console.h" | #include "citra_qt/debugger/console.h" | ||||||
| #include "citra_qt/uisettings.h" | #include "citra_qt/uisettings.h" | ||||||
| #include "common/file_util.h" | #include "common/file_util.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/backend.h" | ||||||
| #include "common/settings.h" | #include "common/settings.h" | ||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
| #include "ui_configure_debug.h" | #include "ui_configure_debug.h" | ||||||
|  | @ -89,9 +89,9 @@ void ConfigureDebug::ApplyConfiguration() { | ||||||
|     UISettings::values.show_console = ui->toggle_console->isChecked(); |     UISettings::values.show_console = ui->toggle_console->isChecked(); | ||||||
|     Settings::values.log_filter = ui->log_filter_edit->text().toStdString(); |     Settings::values.log_filter = ui->log_filter_edit->text().toStdString(); | ||||||
|     Debugger::ToggleConsole(); |     Debugger::ToggleConsole(); | ||||||
|     Log::Filter filter; |     Common::Log::Filter filter; | ||||||
|     filter.ParseFilterString(Settings::values.log_filter.GetValue()); |     filter.ParseFilterString(Settings::values.log_filter.GetValue()); | ||||||
|     Log::SetGlobalFilter(filter); |     Common::Log::SetGlobalFilter(filter); | ||||||
|     Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked(); |     Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked(); | ||||||
|     Settings::values.renderer_debug = ui->toggle_renderer_debug->isChecked(); |     Settings::values.renderer_debug = ui->toggle_renderer_debug->isChecked(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -8,10 +8,13 @@ | ||||||
| #include "citra_qt/configuration/configure_dialog.h" | #include "citra_qt/configuration/configure_dialog.h" | ||||||
| #include "citra_qt/hotkeys.h" | #include "citra_qt/hotkeys.h" | ||||||
| #include "common/settings.h" | #include "common/settings.h" | ||||||
|  | #include "core/core.h" | ||||||
| #include "ui_configure.h" | #include "ui_configure.h" | ||||||
| 
 | 
 | ||||||
| ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, bool enable_web_config) | ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, Core::System& system_, | ||||||
|     : QDialog(parent), ui(std::make_unique<Ui::ConfigureDialog>()), registry(registry) { |                                  bool enable_web_config) | ||||||
|  |     : QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()}, registry{registry_}, | ||||||
|  |       system{system_} { | ||||||
|     Settings::SetConfiguringGlobal(true); |     Settings::SetConfiguringGlobal(true); | ||||||
| 
 | 
 | ||||||
|     ui->setupUi(this); |     ui->setupUi(this); | ||||||
|  | @ -68,7 +71,7 @@ void ConfigureDialog::ApplyConfiguration() { | ||||||
|     ui->webTab->ApplyConfiguration(); |     ui->webTab->ApplyConfiguration(); | ||||||
|     ui->uiTab->ApplyConfiguration(); |     ui->uiTab->ApplyConfiguration(); | ||||||
|     ui->storageTab->ApplyConfiguration(); |     ui->storageTab->ApplyConfiguration(); | ||||||
|     Settings::Apply(); |     system.ApplySettings(); | ||||||
|     Settings::LogSettings(); |     Settings::LogSettings(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -13,11 +13,15 @@ namespace Ui { | ||||||
| class ConfigureDialog; | class ConfigureDialog; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | namespace Core { | ||||||
|  | class System; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| class ConfigureDialog : public QDialog { | class ConfigureDialog : public QDialog { | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, |     explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, Core::System& system, | ||||||
|                              bool enable_web_config = true); |                              bool enable_web_config = true); | ||||||
|     ~ConfigureDialog() override; |     ~ConfigureDialog() override; | ||||||
| 
 | 
 | ||||||
|  | @ -37,4 +41,5 @@ private: | ||||||
| 
 | 
 | ||||||
|     std::unique_ptr<Ui::ConfigureDialog> ui; |     std::unique_ptr<Ui::ConfigureDialog> ui; | ||||||
|     HotkeyRegistry& registry; |     HotkeyRegistry& registry; | ||||||
|  |     Core::System& system; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -102,7 +102,7 @@ void ConfigurePerGame::ApplyConfiguration() { | ||||||
|     audio_tab->ApplyConfiguration(); |     audio_tab->ApplyConfiguration(); | ||||||
|     debug_tab->ApplyConfiguration(); |     debug_tab->ApplyConfiguration(); | ||||||
| 
 | 
 | ||||||
|     Settings::Apply(); |     system.ApplySettings(); | ||||||
|     Settings::LogSettings(); |     Settings::LogSettings(); | ||||||
| 
 | 
 | ||||||
|     game_config->Save(); |     game_config->Save(); | ||||||
|  |  | ||||||
|  | @ -21,6 +21,7 @@ void ToggleConsole() { | ||||||
|         console_shown = UISettings::values.show_console.GetValue(); |         console_shown = UISettings::values.show_console.GetValue(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     using namespace Common::Log; | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
|     FILE* temp; |     FILE* temp; | ||||||
|     if (UISettings::values.show_console) { |     if (UISettings::values.show_console) { | ||||||
|  | @ -29,24 +30,20 @@ void ToggleConsole() { | ||||||
|             freopen_s(&temp, "CONIN$", "r", stdin); |             freopen_s(&temp, "CONIN$", "r", stdin); | ||||||
|             freopen_s(&temp, "CONOUT$", "w", stdout); |             freopen_s(&temp, "CONOUT$", "w", stdout); | ||||||
|             freopen_s(&temp, "CONOUT$", "w", stderr); |             freopen_s(&temp, "CONOUT$", "w", stderr); | ||||||
|             Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); |             SetColorConsoleBackendEnabled(true); | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|         if (FreeConsole()) { |         if (FreeConsole()) { | ||||||
|             // In order to close the console, we have to also detach the streams on it.
 |             // In order to close the console, we have to also detach the streams on it.
 | ||||||
|             // Just redirect them to NUL if there is no console window
 |             // Just redirect them to NUL if there is no console window
 | ||||||
|             Log::RemoveBackend(Log::ColorConsoleBackend::Name()); |             SetColorConsoleBackendEnabled(false); | ||||||
|             freopen_s(&temp, "NUL", "r", stdin); |             freopen_s(&temp, "NUL", "r", stdin); | ||||||
|             freopen_s(&temp, "NUL", "w", stdout); |             freopen_s(&temp, "NUL", "w", stdout); | ||||||
|             freopen_s(&temp, "NUL", "w", stderr); |             freopen_s(&temp, "NUL", "w", stderr); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| #else | #else | ||||||
|     if (UISettings::values.show_console) { |     SetColorConsoleBackendEnabled(UISettings::values.show_console.GetValue()); | ||||||
|         Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); |  | ||||||
|     } else { |  | ||||||
|         Log::RemoveBackend(Log::ColorConsoleBackend::Name()); |  | ||||||
|     } |  | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
| } // namespace Debugger
 | } // namespace Debugger
 | ||||||
|  |  | ||||||
|  | @ -8,10 +8,11 @@ | ||||||
| #include "citra_qt/dumping/options_dialog.h" | #include "citra_qt/dumping/options_dialog.h" | ||||||
| #include "citra_qt/uisettings.h" | #include "citra_qt/uisettings.h" | ||||||
| #include "common/settings.h" | #include "common/settings.h" | ||||||
|  | #include "core/core.h" | ||||||
| #include "ui_dumping_dialog.h" | #include "ui_dumping_dialog.h" | ||||||
| 
 | 
 | ||||||
| DumpingDialog::DumpingDialog(QWidget* parent) | DumpingDialog::DumpingDialog(QWidget* parent, Core::System& system_) | ||||||
|     : QDialog(parent), ui(std::make_unique<Ui::DumpingDialog>()) { |     : QDialog(parent), ui{std::make_unique<Ui::DumpingDialog>()}, system{system_} { | ||||||
| 
 | 
 | ||||||
|     ui->setupUi(this); |     ui->setupUi(this); | ||||||
| 
 | 
 | ||||||
|  | @ -216,5 +217,5 @@ void DumpingDialog::ApplyConfiguration() { | ||||||
|     Settings::values.audio_encoder_options = ui->audioEncoderOptionsLineEdit->text().toStdString(); |     Settings::values.audio_encoder_options = ui->audioEncoderOptionsLineEdit->text().toStdString(); | ||||||
|     Settings::values.audio_bitrate = ui->audioBitrateSpinBox->value(); |     Settings::values.audio_bitrate = ui->audioBitrateSpinBox->value(); | ||||||
|     UISettings::values.video_dumping_path = last_path; |     UISettings::values.video_dumping_path = last_path; | ||||||
|     Settings::Apply(); |     system.ApplySettings(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -10,13 +10,17 @@ namespace Ui { | ||||||
| class DumpingDialog; | class DumpingDialog; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | namespace Core { | ||||||
|  | class System; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| class QLineEdit; | class QLineEdit; | ||||||
| 
 | 
 | ||||||
| class DumpingDialog : public QDialog { | class DumpingDialog : public QDialog { | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     explicit DumpingDialog(QWidget* parent); |     explicit DumpingDialog(QWidget* parent, Core::System& system); | ||||||
|     ~DumpingDialog() override; |     ~DumpingDialog() override; | ||||||
| 
 | 
 | ||||||
|     QString GetFilePath() const; |     QString GetFilePath() const; | ||||||
|  | @ -32,6 +36,7 @@ private: | ||||||
|                            QLineEdit* line_edit); |                            QLineEdit* line_edit); | ||||||
| 
 | 
 | ||||||
|     std::unique_ptr<Ui::DumpingDialog> ui; |     std::unique_ptr<Ui::DumpingDialog> ui; | ||||||
|  |     Core::System& system; | ||||||
| 
 | 
 | ||||||
|     QString last_path; |     QString last_path; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -144,25 +144,12 @@ void GMainWindow::ShowTelemetryCallout() { | ||||||
|            "<br/><br/>Would you like to share your usage data with us?"); |            "<br/><br/>Would you like to share your usage data with us?"); | ||||||
|     if (QMessageBox::question(this, tr("Telemetry"), telemetry_message) == QMessageBox::Yes) { |     if (QMessageBox::question(this, tr("Telemetry"), telemetry_message) == QMessageBox::Yes) { | ||||||
|         NetSettings::values.enable_telemetry = true; |         NetSettings::values.enable_telemetry = true; | ||||||
|         Settings::Apply(); |         system.ApplySettings(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const int GMainWindow::max_recent_files_item; | const int GMainWindow::max_recent_files_item; | ||||||
| 
 | 
 | ||||||
| static void InitializeLogging() { |  | ||||||
|     Log::Filter log_filter; |  | ||||||
|     log_filter.ParseFilterString(Settings::values.log_filter.GetValue()); |  | ||||||
|     Log::SetGlobalFilter(log_filter); |  | ||||||
| 
 |  | ||||||
|     const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); |  | ||||||
|     FileUtil::CreateFullPath(log_dir); |  | ||||||
|     Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE)); |  | ||||||
| #ifdef _WIN32 |  | ||||||
|     Log::AddBackend(std::make_unique<Log::DebuggerBackend>()); |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static QString PrettyProductName() { | static QString PrettyProductName() { | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
|     // After Windows 10 Version 2004, Microsoft decided to switch to a different notation: 20H2
 |     // After Windows 10 Version 2004, Microsoft decided to switch to a different notation: 20H2
 | ||||||
|  | @ -188,9 +175,10 @@ static QString PrettyProductName() { | ||||||
| GMainWindow::GMainWindow(Core::System& system_) | GMainWindow::GMainWindow(Core::System& system_) | ||||||
|     : ui{std::make_unique<Ui::MainWindow>()}, system{system_}, movie{Core::Movie::GetInstance()}, |     : ui{std::make_unique<Ui::MainWindow>()}, system{system_}, movie{Core::Movie::GetInstance()}, | ||||||
|       config{std::make_unique<Config>()}, emu_thread{nullptr} { |       config{std::make_unique<Config>()}, emu_thread{nullptr} { | ||||||
|     InitializeLogging(); |     Common::Log::Initialize(); | ||||||
|  |     Common::Log::Start(); | ||||||
|  | 
 | ||||||
|     Debugger::ToggleConsole(); |     Debugger::ToggleConsole(); | ||||||
|     Settings::LogSettings(); |  | ||||||
| 
 | 
 | ||||||
|     // register types to use in slots and signals
 |     // register types to use in slots and signals
 | ||||||
|     qRegisterMetaType<std::size_t>("std::size_t"); |     qRegisterMetaType<std::size_t>("std::size_t"); | ||||||
|  | @ -1193,12 +1181,13 @@ void GMainWindow::BootGame(const QString& filename) { | ||||||
|         const std::string config_file_name = |         const std::string config_file_name = | ||||||
|             title_id == 0 ? name : fmt::format("{:016X}", title_id); |             title_id == 0 ? name : fmt::format("{:016X}", title_id); | ||||||
|         Config per_game_config(config_file_name, Config::ConfigType::PerGameConfig); |         Config per_game_config(config_file_name, Config::ConfigType::PerGameConfig); | ||||||
|         Settings::Apply(); |         system.ApplySettings(); | ||||||
| 
 | 
 | ||||||
|         LOG_INFO(Frontend, "Using per game config file for title id {}", config_file_name); |         LOG_INFO(Frontend, "Using per game config file for title id {}", config_file_name); | ||||||
|         Settings::LogSettings(); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     Settings::LogSettings(); | ||||||
|  | 
 | ||||||
|     // Save configurations
 |     // Save configurations
 | ||||||
|     UpdateUISettings(); |     UpdateUISettings(); | ||||||
|     game_list->SaveInterfaceLayout(); |     game_list->SaveInterfaceLayout(); | ||||||
|  | @ -1936,7 +1925,7 @@ void GMainWindow::ChangeScreenLayout() { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     Settings::values.layout_option = new_layout; |     Settings::values.layout_option = new_layout; | ||||||
|     Settings::Apply(); |     system.ApplySettings(); | ||||||
|     UpdateSecondaryWindowVisibility(); |     UpdateSecondaryWindowVisibility(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -1964,18 +1953,18 @@ void GMainWindow::ToggleScreenLayout() { | ||||||
| 
 | 
 | ||||||
|     Settings::values.layout_option = new_layout; |     Settings::values.layout_option = new_layout; | ||||||
|     SyncMenuUISettings(); |     SyncMenuUISettings(); | ||||||
|     Settings::Apply(); |     system.ApplySettings(); | ||||||
|     UpdateSecondaryWindowVisibility(); |     UpdateSecondaryWindowVisibility(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GMainWindow::OnSwapScreens() { | void GMainWindow::OnSwapScreens() { | ||||||
|     Settings::values.swap_screen = ui->action_Screen_Layout_Swap_Screens->isChecked(); |     Settings::values.swap_screen = ui->action_Screen_Layout_Swap_Screens->isChecked(); | ||||||
|     Settings::Apply(); |     system.ApplySettings(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GMainWindow::OnRotateScreens() { | void GMainWindow::OnRotateScreens() { | ||||||
|     Settings::values.upright_screen = ui->action_Screen_Layout_Upright_Screens->isChecked(); |     Settings::values.upright_screen = ui->action_Screen_Layout_Upright_Screens->isChecked(); | ||||||
|     Settings::Apply(); |     system.ApplySettings(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GMainWindow::TriggerSwapScreens() { | void GMainWindow::TriggerSwapScreens() { | ||||||
|  | @ -2014,7 +2003,7 @@ void GMainWindow::OnLoadState() { | ||||||
| void GMainWindow::OnConfigure() { | void GMainWindow::OnConfigure() { | ||||||
|     game_list->SetDirectoryWatcherEnabled(false); |     game_list->SetDirectoryWatcherEnabled(false); | ||||||
|     Settings::SetConfiguringGlobal(true); |     Settings::SetConfiguringGlobal(true); | ||||||
|     ConfigureDialog configureDialog(this, hotkey_registry, |     ConfigureDialog configureDialog(this, hotkey_registry, system, | ||||||
|                                     !multiplayer_state->IsHostingPublicRoom()); |                                     !multiplayer_state->IsHostingPublicRoom()); | ||||||
|     connect(&configureDialog, &ConfigureDialog::LanguageChanged, this, |     connect(&configureDialog, &ConfigureDialog::LanguageChanged, this, | ||||||
|             &GMainWindow::OnLanguageChanged); |             &GMainWindow::OnLanguageChanged); | ||||||
|  | @ -2336,7 +2325,7 @@ void GMainWindow::OnOpenFFmpeg() { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| void GMainWindow::OnStartVideoDumping() { | void GMainWindow::OnStartVideoDumping() { | ||||||
|     DumpingDialog dialog(this); |     DumpingDialog dialog(this, system); | ||||||
|     if (dialog.exec() != QDialog::DialogCode::Accepted) { |     if (dialog.exec() != QDialog::DialogCode::Accepted) { | ||||||
|         ui->action_Dump_Video->setChecked(false); |         ui->action_Dump_Video->setChecked(false); | ||||||
|         return; |         return; | ||||||
|  | @ -2938,7 +2927,7 @@ int main(int argc, char* argv[]) { | ||||||
|     // generating shaders
 |     // generating shaders
 | ||||||
|     setlocale(LC_ALL, "C"); |     setlocale(LC_ALL, "C"); | ||||||
| 
 | 
 | ||||||
|     Core::System& system = Core::System::GetInstance(); |     auto& system{Core::System::GetInstance()}; | ||||||
|     GMainWindow main_window(system); |     GMainWindow main_window(system); | ||||||
| 
 | 
 | ||||||
|     // Register frontend applets
 |     // Register frontend applets
 | ||||||
|  |  | ||||||
|  | @ -57,6 +57,7 @@ add_library(citra_common STATIC | ||||||
|     detached_tasks.h |     detached_tasks.h | ||||||
|     bit_field.h |     bit_field.h | ||||||
|     bit_set.h |     bit_set.h | ||||||
|  |     bounded_threadsafe_queue.h | ||||||
|     cityhash.cpp |     cityhash.cpp | ||||||
|     cityhash.h |     cityhash.h | ||||||
|     color.h |     color.h | ||||||
|  | @ -85,8 +86,10 @@ add_library(citra_common STATIC | ||||||
|     logging/filter.h |     logging/filter.h | ||||||
|     logging/formatter.h |     logging/formatter.h | ||||||
|     logging/log.h |     logging/log.h | ||||||
|  |     logging/log_entry.h | ||||||
|     logging/text_formatter.cpp |     logging/text_formatter.cpp | ||||||
|     logging/text_formatter.h |     logging/text_formatter.h | ||||||
|  |     logging/types.h | ||||||
|     math_util.h |     math_util.h | ||||||
|     memory_detect.cpp |     memory_detect.cpp | ||||||
|     memory_detect.h |     memory_detect.h | ||||||
|  | @ -173,3 +176,6 @@ endif() | ||||||
| if (CITRA_USE_PRECOMPILED_HEADERS) | if (CITRA_USE_PRECOMPILED_HEADERS) | ||||||
|     target_precompile_headers(citra_common PRIVATE precompiled_headers.h) |     target_precompile_headers(citra_common PRIVATE precompiled_headers.h) | ||||||
| endif() | endif() | ||||||
|  | if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND CMAKE_CXX_COMPILER_ID STREQUAL GNU) | ||||||
|  |   target_link_libraries(citra_common PRIVATE backtrace dl) | ||||||
|  | endif() | ||||||
|  |  | ||||||
							
								
								
									
										251
									
								
								src/common/bounded_threadsafe_queue.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								src/common/bounded_threadsafe_queue.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,251 @@ | ||||||
|  | // Copyright 2023 yuzu Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <atomic> | ||||||
|  | #include <condition_variable> | ||||||
|  | #include <cstddef> | ||||||
|  | #include <memory> | ||||||
|  | #include <mutex> | ||||||
|  | #include <new> | ||||||
|  | 
 | ||||||
|  | #include "common/polyfill_thread.h" | ||||||
|  | 
 | ||||||
|  | namespace Common { | ||||||
|  | 
 | ||||||
|  | namespace detail { | ||||||
|  | constexpr size_t DefaultCapacity = 0x1000; | ||||||
|  | } // namespace detail
 | ||||||
|  | 
 | ||||||
|  | template <typename T, size_t Capacity = detail::DefaultCapacity> | ||||||
|  | class SPSCQueue { | ||||||
|  |     static_assert((Capacity & (Capacity - 1)) == 0, "Capacity must be a power of two."); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     template <typename... Args> | ||||||
|  |     bool TryEmplace(Args&&... args) { | ||||||
|  |         return Emplace<PushMode::Try>(std::forward<Args>(args)...); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template <typename... Args> | ||||||
|  |     void EmplaceWait(Args&&... args) { | ||||||
|  |         Emplace<PushMode::Wait>(std::forward<Args>(args)...); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool TryPop(T& t) { | ||||||
|  |         return Pop<PopMode::Try>(t); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void PopWait(T& t) { | ||||||
|  |         Pop<PopMode::Wait>(t); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void PopWait(T& t, std::stop_token stop_token) { | ||||||
|  |         Pop<PopMode::WaitWithStopToken>(t, stop_token); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     T PopWait() { | ||||||
|  |         T t; | ||||||
|  |         Pop<PopMode::Wait>(t); | ||||||
|  |         return t; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     T PopWait(std::stop_token stop_token) { | ||||||
|  |         T t; | ||||||
|  |         Pop<PopMode::WaitWithStopToken>(t, stop_token); | ||||||
|  |         return t; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     enum class PushMode { | ||||||
|  |         Try, | ||||||
|  |         Wait, | ||||||
|  |         Count, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     enum class PopMode { | ||||||
|  |         Try, | ||||||
|  |         Wait, | ||||||
|  |         WaitWithStopToken, | ||||||
|  |         Count, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     template <PushMode Mode, typename... Args> | ||||||
|  |     bool Emplace(Args&&... args) { | ||||||
|  |         const size_t write_index = m_write_index.load(std::memory_order::relaxed); | ||||||
|  | 
 | ||||||
|  |         if constexpr (Mode == PushMode::Try) { | ||||||
|  |             // Check if we have free slots to write to.
 | ||||||
|  |             if ((write_index - m_read_index.load(std::memory_order::acquire)) == Capacity) { | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } else if constexpr (Mode == PushMode::Wait) { | ||||||
|  |             // Wait until we have free slots to write to.
 | ||||||
|  |             std::unique_lock lock{producer_cv_mutex}; | ||||||
|  |             producer_cv.wait(lock, [this, write_index] { | ||||||
|  |                 return (write_index - m_read_index.load(std::memory_order::acquire)) < Capacity; | ||||||
|  |             }); | ||||||
|  |         } else { | ||||||
|  |             static_assert(Mode < PushMode::Count, "Invalid PushMode."); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Determine the position to write to.
 | ||||||
|  |         const size_t pos = write_index % Capacity; | ||||||
|  | 
 | ||||||
|  |         // Emplace into the queue.
 | ||||||
|  |         new (std::addressof(m_data[pos])) T(std::forward<Args>(args)...); | ||||||
|  | 
 | ||||||
|  |         // Increment the write index.
 | ||||||
|  |         ++m_write_index; | ||||||
|  | 
 | ||||||
|  |         // Notify the consumer that we have pushed into the queue.
 | ||||||
|  |         std::scoped_lock lock{consumer_cv_mutex}; | ||||||
|  |         consumer_cv.notify_one(); | ||||||
|  | 
 | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template <PopMode Mode> | ||||||
|  |     bool Pop(T& t, [[maybe_unused]] std::stop_token stop_token = {}) { | ||||||
|  |         const size_t read_index = m_read_index.load(std::memory_order::relaxed); | ||||||
|  | 
 | ||||||
|  |         if constexpr (Mode == PopMode::Try) { | ||||||
|  |             // Check if the queue is empty.
 | ||||||
|  |             if (read_index == m_write_index.load(std::memory_order::acquire)) { | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } else if constexpr (Mode == PopMode::Wait) { | ||||||
|  |             // Wait until the queue is not empty.
 | ||||||
|  |             std::unique_lock lock{consumer_cv_mutex}; | ||||||
|  |             consumer_cv.wait(lock, [this, read_index] { | ||||||
|  |                 return read_index != m_write_index.load(std::memory_order::acquire); | ||||||
|  |             }); | ||||||
|  |         } else if constexpr (Mode == PopMode::WaitWithStopToken) { | ||||||
|  |             // Wait until the queue is not empty.
 | ||||||
|  |             std::unique_lock lock{consumer_cv_mutex}; | ||||||
|  |             Common::CondvarWait(consumer_cv, lock, stop_token, [this, read_index] { | ||||||
|  |                 return read_index != m_write_index.load(std::memory_order::acquire); | ||||||
|  |             }); | ||||||
|  |             if (stop_token.stop_requested()) { | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             static_assert(Mode < PopMode::Count, "Invalid PopMode."); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Determine the position to read from.
 | ||||||
|  |         const size_t pos = read_index % Capacity; | ||||||
|  | 
 | ||||||
|  |         // Pop the data off the queue, moving it.
 | ||||||
|  |         t = std::move(m_data[pos]); | ||||||
|  | 
 | ||||||
|  |         // Increment the read index.
 | ||||||
|  |         ++m_read_index; | ||||||
|  | 
 | ||||||
|  |         // Notify the producer that we have popped off the queue.
 | ||||||
|  |         std::scoped_lock lock{producer_cv_mutex}; | ||||||
|  |         producer_cv.notify_one(); | ||||||
|  | 
 | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     alignas(128) std::atomic_size_t m_read_index{0}; | ||||||
|  |     alignas(128) std::atomic_size_t m_write_index{0}; | ||||||
|  | 
 | ||||||
|  |     std::array<T, Capacity> m_data; | ||||||
|  | 
 | ||||||
|  |     std::condition_variable_any producer_cv; | ||||||
|  |     std::mutex producer_cv_mutex; | ||||||
|  |     std::condition_variable_any consumer_cv; | ||||||
|  |     std::mutex consumer_cv_mutex; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | template <typename T, size_t Capacity = detail::DefaultCapacity> | ||||||
|  | class MPSCQueue { | ||||||
|  | public: | ||||||
|  |     template <typename... Args> | ||||||
|  |     bool TryEmplace(Args&&... args) { | ||||||
|  |         std::scoped_lock lock{write_mutex}; | ||||||
|  |         return spsc_queue.TryEmplace(std::forward<Args>(args)...); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template <typename... Args> | ||||||
|  |     void EmplaceWait(Args&&... args) { | ||||||
|  |         std::scoped_lock lock{write_mutex}; | ||||||
|  |         spsc_queue.EmplaceWait(std::forward<Args>(args)...); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool TryPop(T& t) { | ||||||
|  |         return spsc_queue.TryPop(t); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void PopWait(T& t) { | ||||||
|  |         spsc_queue.PopWait(t); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void PopWait(T& t, std::stop_token stop_token) { | ||||||
|  |         spsc_queue.PopWait(t, stop_token); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     T PopWait() { | ||||||
|  |         return spsc_queue.PopWait(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     T PopWait(std::stop_token stop_token) { | ||||||
|  |         return spsc_queue.PopWait(stop_token); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     SPSCQueue<T, Capacity> spsc_queue; | ||||||
|  |     std::mutex write_mutex; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | template <typename T, size_t Capacity = detail::DefaultCapacity> | ||||||
|  | class MPMCQueue { | ||||||
|  | public: | ||||||
|  |     template <typename... Args> | ||||||
|  |     bool TryEmplace(Args&&... args) { | ||||||
|  |         std::scoped_lock lock{write_mutex}; | ||||||
|  |         return spsc_queue.TryEmplace(std::forward<Args>(args)...); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template <typename... Args> | ||||||
|  |     void EmplaceWait(Args&&... args) { | ||||||
|  |         std::scoped_lock lock{write_mutex}; | ||||||
|  |         spsc_queue.EmplaceWait(std::forward<Args>(args)...); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool TryPop(T& t) { | ||||||
|  |         std::scoped_lock lock{read_mutex}; | ||||||
|  |         return spsc_queue.TryPop(t); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void PopWait(T& t) { | ||||||
|  |         std::scoped_lock lock{read_mutex}; | ||||||
|  |         spsc_queue.PopWait(t); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void PopWait(T& t, std::stop_token stop_token) { | ||||||
|  |         std::scoped_lock lock{read_mutex}; | ||||||
|  |         spsc_queue.PopWait(t, stop_token); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     T PopWait() { | ||||||
|  |         std::scoped_lock lock{read_mutex}; | ||||||
|  |         return spsc_queue.PopWait(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     T PopWait(std::stop_token stop_token) { | ||||||
|  |         std::scoped_lock lock{read_mutex}; | ||||||
|  |         return spsc_queue.PopWait(stop_token); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     SPSCQueue<T, Capacity> spsc_queue; | ||||||
|  |     std::mutex write_mutex; | ||||||
|  |     std::mutex read_mutex; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace Common
 | ||||||
|  | @ -2,6 +2,8 @@ | ||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
|  | #include <memory> | ||||||
|  | 
 | ||||||
| #include "common/dynamic_library/dynamic_library.h" | #include "common/dynamic_library/dynamic_library.h" | ||||||
| #include "common/dynamic_library/fdk-aac.h" | #include "common/dynamic_library/fdk-aac.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
|  |  | ||||||
|  | @ -2,6 +2,8 @@ | ||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
|  | #include <memory> | ||||||
|  | 
 | ||||||
| #include "common/dynamic_library/dynamic_library.h" | #include "common/dynamic_library/dynamic_library.h" | ||||||
| #include "common/dynamic_library/ffmpeg.h" | #include "common/dynamic_library/ffmpeg.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
|  |  | ||||||
|  | @ -10,6 +10,7 @@ | ||||||
| #include <unordered_map> | #include <unordered_map> | ||||||
| #include <boost/iostreams/device/file_descriptor.hpp> | #include <boost/iostreams/device/file_descriptor.hpp> | ||||||
| #include <boost/iostreams/stream.hpp> | #include <boost/iostreams/stream.hpp> | ||||||
|  | #include <fmt/format.h> | ||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| #include "common/common_funcs.h" | #include "common/common_funcs.h" | ||||||
| #include "common/common_paths.h" | #include "common/common_paths.h" | ||||||
|  |  | ||||||
|  | @ -2,304 +2,444 @@ | ||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include <algorithm> |  | ||||||
| #include <chrono> | #include <chrono> | ||||||
| #include <condition_variable> | 
 | ||||||
| #include <memory> | #include <fmt/format.h> | ||||||
| #include <mutex> | 
 | ||||||
| #include <thread> |  | ||||||
| #include <vector> |  | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
| #include <share.h>   // For _SH_DENYWR
 | #include <share.h>   // For _SH_DENYWR
 | ||||||
| #include <windows.h> // For OutputDebugStringW
 | #include <windows.h> // For OutputDebugStringW
 | ||||||
| #else | #else | ||||||
| #define _SH_DENYWR 0 | #define _SH_DENYWR 0 | ||||||
| #endif | #endif | ||||||
| #include "common/assert.h" | 
 | ||||||
|  | #if defined(__linux__) && defined(__GNUG__) && !defined(__clang__) | ||||||
|  | #define BOOST_STACKTRACE_USE_BACKTRACE | ||||||
|  | #include <boost/stacktrace.hpp> | ||||||
|  | #undef BOOST_STACKTRACE_USE_BACKTRACE | ||||||
|  | #include <signal.h> | ||||||
|  | #define CITRA_LINUX_GCC_BACKTRACE | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #include "common/bounded_threadsafe_queue.h" | ||||||
|  | #include "common/common_paths.h" | ||||||
|  | #include "common/file_util.h" | ||||||
|  | #include "common/literals.h" | ||||||
| #include "common/logging/backend.h" | #include "common/logging/backend.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
|  | #include "common/logging/log_entry.h" | ||||||
| #include "common/logging/text_formatter.h" | #include "common/logging/text_formatter.h" | ||||||
|  | #include "common/polyfill_thread.h" | ||||||
|  | #include "common/settings.h" | ||||||
| #include "common/string_util.h" | #include "common/string_util.h" | ||||||
| #include "common/threadsafe_queue.h" | #include "common/thread.h" | ||||||
| 
 | 
 | ||||||
| namespace Log { | namespace Common::Log { | ||||||
| 
 | 
 | ||||||
| Filter filter; | namespace { | ||||||
| void SetGlobalFilter(const Filter& f) { | 
 | ||||||
|     filter = f; | /**
 | ||||||
|  |  * Interface for logging backends. | ||||||
|  |  */ | ||||||
|  | class Backend { | ||||||
|  | public: | ||||||
|  |     virtual ~Backend() = default; | ||||||
|  | 
 | ||||||
|  |     virtual void Write(const Entry& entry) = 0; | ||||||
|  | 
 | ||||||
|  |     virtual void EnableForStacktrace() = 0; | ||||||
|  | 
 | ||||||
|  |     virtual void Flush() = 0; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Backend that writes to stderr and with color | ||||||
|  |  */ | ||||||
|  | class ColorConsoleBackend final : public Backend { | ||||||
|  | public: | ||||||
|  |     explicit ColorConsoleBackend() = default; | ||||||
|  | 
 | ||||||
|  |     ~ColorConsoleBackend() override = default; | ||||||
|  | 
 | ||||||
|  |     void Write(const Entry& entry) override { | ||||||
|  |         if (enabled.load(std::memory_order_relaxed)) { | ||||||
|  |             PrintColoredMessage(entry); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void Flush() override { | ||||||
|  |         // stderr shouldn't be buffered
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void EnableForStacktrace() override { | ||||||
|  |         enabled = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void SetEnabled(bool enabled_) { | ||||||
|  |         enabled = enabled_; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     std::atomic_bool enabled{false}; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Backend that writes to a file passed into the constructor | ||||||
|  |  */ | ||||||
|  | class FileBackend final : public Backend { | ||||||
|  | public: | ||||||
|  |     explicit FileBackend(const std::string& filename) { | ||||||
|  |         auto old_filename = filename; | ||||||
|  |         old_filename += ".old.txt"; | ||||||
|  | 
 | ||||||
|  |         // Existence checks are done within the functions themselves.
 | ||||||
|  |         // We don't particularly care if these succeed or not.
 | ||||||
|  |         static_cast<void>(FileUtil::Delete(old_filename)); | ||||||
|  |         static_cast<void>(FileUtil::Rename(filename, old_filename)); | ||||||
|  | 
 | ||||||
|  |         // _SH_DENYWR allows read only access to the file for other programs.
 | ||||||
|  |         // It is #defined to 0 on other platforms
 | ||||||
|  |         file = std::make_unique<FileUtil::IOFile>(filename, "w", _SH_DENYWR); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ~FileBackend() override = default; | ||||||
|  | 
 | ||||||
|  |     void Write(const Entry& entry) override { | ||||||
|  |         if (!enabled) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n')); | ||||||
|  | 
 | ||||||
|  |         using namespace Common::Literals; | ||||||
|  |         // Prevent logs from exceeding a set maximum size in the event that log entries are spammed.
 | ||||||
|  |         const auto write_limit = 100_MiB; | ||||||
|  |         const bool write_limit_exceeded = bytes_written > write_limit; | ||||||
|  |         if (entry.log_level >= Level::Error || write_limit_exceeded) { | ||||||
|  |             if (write_limit_exceeded) { | ||||||
|  |                 // Stop writing after the write limit is exceeded.
 | ||||||
|  |                 // Don't close the file so we can print a stacktrace if necessary
 | ||||||
|  |                 enabled = false; | ||||||
|  |             } | ||||||
|  |             file->Flush(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void Flush() override { | ||||||
|  |         file->Flush(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void EnableForStacktrace() override { | ||||||
|  |         enabled = true; | ||||||
|  |         bytes_written = 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     std::unique_ptr<FileUtil::IOFile> file; | ||||||
|  |     bool enabled = true; | ||||||
|  |     std::size_t bytes_written = 0; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Backend that writes to Visual Studio's output window | ||||||
|  |  */ | ||||||
|  | class DebuggerBackend final : public Backend { | ||||||
|  | public: | ||||||
|  |     explicit DebuggerBackend() = default; | ||||||
|  | 
 | ||||||
|  |     ~DebuggerBackend() override = default; | ||||||
|  | 
 | ||||||
|  |     void Write(const Entry& entry) override { | ||||||
|  | #ifdef _WIN32 | ||||||
|  |         ::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str()); | ||||||
|  | #endif | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void Flush() override {} | ||||||
|  | 
 | ||||||
|  |     void EnableForStacktrace() override {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #ifdef ANDROID | ||||||
|  | /**
 | ||||||
|  |  * Backend that writes to the Android logcat | ||||||
|  |  */ | ||||||
|  | class LogcatBackend : public Backend { | ||||||
|  | public: | ||||||
|  |     explicit LogcatBackend() = default; | ||||||
|  | 
 | ||||||
|  |     ~LogcatBackend() override = default; | ||||||
|  | 
 | ||||||
|  |     void Write(const Entry& entry) override { | ||||||
|  |         PrintMessageToLogcat(entry); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void Flush() override {} | ||||||
|  | 
 | ||||||
|  |     void EnableForStacktrace() override {} | ||||||
|  | }; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | bool initialization_in_progress_suppress_logging = true; | ||||||
|  | 
 | ||||||
|  | #ifdef CITRA_LINUX_GCC_BACKTRACE | ||||||
|  | [[noreturn]] void SleepForever() { | ||||||
|  |     while (true) { | ||||||
|  |         pause(); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Static state as a singleton. |  * Static state as a singleton. | ||||||
|  */ |  */ | ||||||
| class Impl { | class Impl { | ||||||
| public: | public: | ||||||
|     static Impl& Instance() { |     static Impl& Instance() { | ||||||
|         static Impl backend; |         if (!instance) { | ||||||
|         return backend; |             throw std::runtime_error("Using Logging instance before its initialization"); | ||||||
|  |         } | ||||||
|  |         return *instance; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     Impl(Impl const&) = delete; |     static void Initialize(std::string_view log_file) { | ||||||
|     const Impl& operator=(Impl const&) = delete; |         if (instance) { | ||||||
|  |             LOG_WARNING(Log, "Reinitializing logging backend"); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         initialization_in_progress_suppress_logging = true; | ||||||
|  |         const auto& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); | ||||||
|  |         void(FileUtil::CreateDir(log_dir)); | ||||||
|  |         Filter filter; | ||||||
|  |         filter.ParseFilterString(Settings::values.log_filter.GetValue()); | ||||||
|  |         instance = std::unique_ptr<Impl, decltype(&Deleter)>( | ||||||
|  |             new Impl(fmt::format("{}{}", log_dir, log_file), filter), Deleter); | ||||||
|  |         initialization_in_progress_suppress_logging = false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     static void Start() { | ||||||
|  |         instance->StartBackendThread(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     Impl(const Impl&) = delete; | ||||||
|  |     Impl& operator=(const Impl&) = delete; | ||||||
|  | 
 | ||||||
|  |     Impl(Impl&&) = delete; | ||||||
|  |     Impl& operator=(Impl&&) = delete; | ||||||
|  | 
 | ||||||
|  |     void SetGlobalFilter(const Filter& f) { | ||||||
|  |         filter = f; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void SetColorConsoleBackendEnabled(bool enabled) { | ||||||
|  |         color_console_backend.SetEnabled(enabled); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num, |     void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num, | ||||||
|                    const char* function, std::string message) { |                    const char* function, std::string message) { | ||||||
|         message_queue.Push( |         if (!filter.CheckMessage(log_class, log_level)) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         message_queue.EmplaceWait( | ||||||
|             CreateEntry(log_class, log_level, filename, line_num, function, std::move(message))); |             CreateEntry(log_class, log_level, filename, line_num, function, std::move(message))); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void AddBackend(std::unique_ptr<Backend> backend) { |  | ||||||
|         std::lock_guard lock{writing_mutex}; |  | ||||||
|         backends.push_back(std::move(backend)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void RemoveBackend(std::string_view backend_name) { |  | ||||||
|         std::lock_guard lock{writing_mutex}; |  | ||||||
|         const auto it = |  | ||||||
|             std::remove_if(backends.begin(), backends.end(), |  | ||||||
|                            [&backend_name](const auto& i) { return backend_name == i->GetName(); }); |  | ||||||
|         backends.erase(it, backends.end()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     Backend* GetBackend(std::string_view backend_name) { |  | ||||||
|         const auto it = |  | ||||||
|             std::find_if(backends.begin(), backends.end(), |  | ||||||
|                          [&backend_name](const auto& i) { return backend_name == i->GetName(); }); |  | ||||||
|         if (it == backends.end()) |  | ||||||
|             return nullptr; |  | ||||||
|         return it->get(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| private: | private: | ||||||
|     Impl() { |     Impl(const std::string& file_backend_filename, const Filter& filter_) | ||||||
|         backend_thread = std::thread([&] { |         : filter{filter_}, file_backend{file_backend_filename} { | ||||||
|             Entry entry; | #ifdef CITRA_LINUX_GCC_BACKTRACE | ||||||
|             auto write_logs = [&](Entry& e) { |         int waker_pipefd[2]; | ||||||
|                 std::lock_guard lock{writing_mutex}; |         int done_printing_pipefd[2]; | ||||||
|                 for (const auto& backend : backends) { |         if (pipe2(waker_pipefd, O_CLOEXEC) || pipe2(done_printing_pipefd, O_CLOEXEC)) { | ||||||
|                     backend->Write(e); |             abort(); | ||||||
|                 } |         } | ||||||
|             }; |         backtrace_thread_waker_fd = waker_pipefd[1]; | ||||||
|             while (true) { |         backtrace_done_printing_fd = done_printing_pipefd[0]; | ||||||
|                 entry = message_queue.PopWait(); |         std::thread([this, wait_fd = waker_pipefd[0], done_fd = done_printing_pipefd[1]] { | ||||||
|                 if (entry.final_entry) { |             Common::SetCurrentThreadName("citra:Crash"); | ||||||
|                     break; |             for (u8 ignore = 0; read(wait_fd, &ignore, 1) != 1;) | ||||||
|                 } |                 ; | ||||||
|                 write_logs(entry); |             const int sig = received_signal; | ||||||
|  |             if (sig <= 0) { | ||||||
|  |                 abort(); | ||||||
|             } |             } | ||||||
|  |             backend_thread.request_stop(); | ||||||
|  |             backend_thread.join(); | ||||||
|  |             const auto signal_entry = | ||||||
|  |                 CreateEntry(Class::Log, Level::Critical, "?", 0, "?", | ||||||
|  |                             fmt::vformat("Received signal {}", fmt::make_format_args(sig))); | ||||||
|  |             ForEachBackend([&signal_entry](Backend& backend) { | ||||||
|  |                 backend.EnableForStacktrace(); | ||||||
|  |                 backend.Write(signal_entry); | ||||||
|  |             }); | ||||||
|  |             const auto backtrace = | ||||||
|  |                 boost::stacktrace::stacktrace::from_dump(backtrace_storage.data(), 4096); | ||||||
|  |             for (const auto& frame : backtrace.as_vector()) { | ||||||
|  |                 auto line = boost::stacktrace::detail::to_string(&frame, 1); | ||||||
|  |                 if (line.empty()) { | ||||||
|  |                     abort(); | ||||||
|  |                 } | ||||||
|  |                 line.pop_back(); // Remove newline
 | ||||||
|  |                 const auto frame_entry = | ||||||
|  |                     CreateEntry(Class::Log, Level::Critical, "?", 0, "?", std::move(line)); | ||||||
|  |                 ForEachBackend([&frame_entry](Backend& backend) { backend.Write(frame_entry); }); | ||||||
|  |             } | ||||||
|  |             using namespace std::literals; | ||||||
|  |             const auto rip_entry = CreateEntry(Class::Log, Level::Critical, "?", 0, "?", "RIP"s); | ||||||
|  |             ForEachBackend([&rip_entry](Backend& backend) { | ||||||
|  |                 backend.Write(rip_entry); | ||||||
|  |                 backend.Flush(); | ||||||
|  |             }); | ||||||
|  |             for (const u8 anything = 0; write(done_fd, &anything, 1) != 1;) | ||||||
|  |                 ; | ||||||
|  |             // Abort on original thread to help debugging
 | ||||||
|  |             SleepForever(); | ||||||
|  |         }).detach(); | ||||||
|  |         signal(SIGSEGV, &HandleSignal); | ||||||
|  |         signal(SIGABRT, &HandleSignal); | ||||||
|  | #endif | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|             // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a case
 |     ~Impl() { | ||||||
|             // where a system is repeatedly spamming logs even on close.
 | #ifdef CITRA_LINUX_GCC_BACKTRACE | ||||||
|             constexpr int MAX_LOGS_TO_WRITE = 100; |         if (int zero_or_ignore = 0; | ||||||
|             int logs_written = 0; |             !received_signal.compare_exchange_strong(zero_or_ignore, SIGKILL)) { | ||||||
|             while (logs_written++ < MAX_LOGS_TO_WRITE && message_queue.Pop(entry)) { |             SleepForever(); | ||||||
|                 write_logs(entry); |         } | ||||||
|  | #endif | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void StartBackendThread() { | ||||||
|  |         backend_thread = std::jthread([this](std::stop_token stop_token) { | ||||||
|  |             Common::SetCurrentThreadName("citra:Log"); | ||||||
|  |             Entry entry; | ||||||
|  |             const auto write_logs = [this, &entry]() { | ||||||
|  |                 ForEachBackend([&entry](Backend& backend) { backend.Write(entry); }); | ||||||
|  |             }; | ||||||
|  |             while (!stop_token.stop_requested()) { | ||||||
|  |                 message_queue.PopWait(entry, stop_token); | ||||||
|  |                 if (entry.filename != nullptr) { | ||||||
|  |                     write_logs(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             // 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.
 | ||||||
|  |             int max_logs_to_write = filter.IsDebug() ? INT_MAX : 100; | ||||||
|  |             while (max_logs_to_write-- && message_queue.TryPop(entry)) { | ||||||
|  |                 write_logs(); | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     ~Impl() { |  | ||||||
|         Entry entry; |  | ||||||
|         entry.final_entry = true; |  | ||||||
|         message_queue.Push(entry); |  | ||||||
|         backend_thread.join(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr, |     Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr, | ||||||
|                       const char* function, std::string message) const { |                       const char* function, std::string&& message) const { | ||||||
|         using std::chrono::duration_cast; |         using std::chrono::duration_cast; | ||||||
|  |         using std::chrono::microseconds; | ||||||
|         using std::chrono::steady_clock; |         using std::chrono::steady_clock; | ||||||
| 
 | 
 | ||||||
|         Entry entry; |         return { | ||||||
|         entry.timestamp = |             .timestamp = duration_cast<microseconds>(steady_clock::now() - time_origin), | ||||||
|             duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin); |             .log_class = log_class, | ||||||
|         entry.log_class = log_class; |             .log_level = log_level, | ||||||
|         entry.log_level = log_level; |             .filename = filename, | ||||||
|         entry.filename = filename; |             .line_num = line_nr, | ||||||
|         entry.line_num = line_nr; |             .function = function, | ||||||
|         entry.function = function; |             .message = std::move(message), | ||||||
|         entry.message = std::move(message); |         }; | ||||||
| 
 |  | ||||||
|         return entry; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     std::mutex writing_mutex; |     void ForEachBackend(auto lambda) { | ||||||
|     std::thread backend_thread; |         lambda(static_cast<Backend&>(debugger_backend)); | ||||||
|     std::vector<std::unique_ptr<Backend>> backends; |         lambda(static_cast<Backend&>(color_console_backend)); | ||||||
|     Common::MPSCQueue<Log::Entry> message_queue; |         lambda(static_cast<Backend&>(file_backend)); | ||||||
|     Filter filter; | #ifdef ANDROID | ||||||
|     std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; |         lambda(static_cast<Backend&>(lc_backend)); | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| void ConsoleBackend::Write(const Entry& entry) { |  | ||||||
|     PrintMessage(entry); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void ColorConsoleBackend::Write(const Entry& entry) { |  | ||||||
|     PrintColoredMessage(entry); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void LogcatBackend::Write(const Entry& entry) { |  | ||||||
|     PrintMessageToLogcat(entry); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| FileBackend::FileBackend(const std::string& filename) : bytes_written(0) { |  | ||||||
|     if (FileUtil::Exists(filename + ".old.txt")) { |  | ||||||
|         FileUtil::Delete(filename + ".old.txt"); |  | ||||||
|     } |  | ||||||
|     if (FileUtil::Exists(filename)) { |  | ||||||
|         FileUtil::Rename(filename, filename + ".old.txt"); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // _SH_DENYWR allows read only access to the file for other programs.
 |  | ||||||
|     // It is #defined to 0 on other platforms
 |  | ||||||
|     file = FileUtil::IOFile(filename, "w", _SH_DENYWR); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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 std::size_t MAX_BYTES_WRITTEN = 50 * 1024L * 1024L; |  | ||||||
|     if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|     bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n')); |  | ||||||
|     if (entry.log_level >= Level::Error) { |  | ||||||
|         file.Flush(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void DebuggerBackend::Write(const Entry& entry) { |  | ||||||
| #ifdef _WIN32 |  | ||||||
|     ::OutputDebugStringW(Common::UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str()); |  | ||||||
| #endif | #endif | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
 |  | ||||||
| #define ALL_LOG_CLASSES()                                                                          \ |  | ||||||
|     CLS(Log)                                                                                       \ |  | ||||||
|     CLS(Common)                                                                                    \ |  | ||||||
|     SUB(Common, Filesystem)                                                                        \ |  | ||||||
|     SUB(Common, Memory)                                                                            \ |  | ||||||
|     CLS(Core)                                                                                      \ |  | ||||||
|     SUB(Core, ARM11)                                                                               \ |  | ||||||
|     SUB(Core, Timing)                                                                              \ |  | ||||||
|     SUB(Core, Cheats)                                                                              \ |  | ||||||
|     CLS(Config)                                                                                    \ |  | ||||||
|     CLS(Debug)                                                                                     \ |  | ||||||
|     SUB(Debug, Emulated)                                                                           \ |  | ||||||
|     SUB(Debug, GPU)                                                                                \ |  | ||||||
|     SUB(Debug, Breakpoint)                                                                         \ |  | ||||||
|     SUB(Debug, GDBStub)                                                                            \ |  | ||||||
|     CLS(Kernel)                                                                                    \ |  | ||||||
|     SUB(Kernel, SVC)                                                                               \ |  | ||||||
|     CLS(Applet)                                                                                    \ |  | ||||||
|     SUB(Applet, SWKBD)                                                                             \ |  | ||||||
|     CLS(Service)                                                                                   \ |  | ||||||
|     SUB(Service, SRV)                                                                              \ |  | ||||||
|     SUB(Service, FRD)                                                                              \ |  | ||||||
|     SUB(Service, FS)                                                                               \ |  | ||||||
|     SUB(Service, ERR)                                                                              \ |  | ||||||
|     SUB(Service, APT)                                                                              \ |  | ||||||
|     SUB(Service, BOSS)                                                                             \ |  | ||||||
|     SUB(Service, GSP)                                                                              \ |  | ||||||
|     SUB(Service, AC)                                                                               \ |  | ||||||
|     SUB(Service, AM)                                                                               \ |  | ||||||
|     SUB(Service, PTM)                                                                              \ |  | ||||||
|     SUB(Service, LDR)                                                                              \ |  | ||||||
|     SUB(Service, MIC)                                                                              \ |  | ||||||
|     SUB(Service, NDM)                                                                              \ |  | ||||||
|     SUB(Service, NFC)                                                                              \ |  | ||||||
|     SUB(Service, NIM)                                                                              \ |  | ||||||
|     SUB(Service, NS)                                                                               \ |  | ||||||
|     SUB(Service, NWM)                                                                              \ |  | ||||||
|     SUB(Service, CAM)                                                                              \ |  | ||||||
|     SUB(Service, CECD)                                                                             \ |  | ||||||
|     SUB(Service, CFG)                                                                              \ |  | ||||||
|     SUB(Service, CSND)                                                                             \ |  | ||||||
|     SUB(Service, DSP)                                                                              \ |  | ||||||
|     SUB(Service, DLP)                                                                              \ |  | ||||||
|     SUB(Service, HID)                                                                              \ |  | ||||||
|     SUB(Service, HTTP)                                                                             \ |  | ||||||
|     SUB(Service, SOC)                                                                              \ |  | ||||||
|     SUB(Service, IR)                                                                               \ |  | ||||||
|     SUB(Service, Y2R)                                                                              \ |  | ||||||
|     SUB(Service, PS)                                                                               \ |  | ||||||
|     SUB(Service, PLGLDR)                                                                           \ |  | ||||||
|     CLS(HW)                                                                                        \ |  | ||||||
|     SUB(HW, Memory)                                                                                \ |  | ||||||
|     SUB(HW, LCD)                                                                                   \ |  | ||||||
|     SUB(HW, GPU)                                                                                   \ |  | ||||||
|     SUB(HW, AES)                                                                                   \ |  | ||||||
|     CLS(Frontend)                                                                                  \ |  | ||||||
|     CLS(Render)                                                                                    \ |  | ||||||
|     SUB(Render, Software)                                                                          \ |  | ||||||
|     SUB(Render, OpenGL)                                                                            \ |  | ||||||
|     SUB(Render, Vulkan)                                                                            \ |  | ||||||
|     CLS(Audio)                                                                                     \ |  | ||||||
|     SUB(Audio, DSP)                                                                                \ |  | ||||||
|     SUB(Audio, Sink)                                                                               \ |  | ||||||
|     CLS(Input)                                                                                     \ |  | ||||||
|     CLS(Network)                                                                                   \ |  | ||||||
|     CLS(Movie)                                                                                     \ |  | ||||||
|     CLS(Loader)                                                                                    \ |  | ||||||
|     CLS(WebService)                                                                                \ |  | ||||||
|     CLS(RPC_Server) |  | ||||||
| 
 |  | ||||||
| // GetClassName is a macro defined by Windows.h, grrr...
 |  | ||||||
| const char* GetLogClassName(Class log_class) { |  | ||||||
|     switch (log_class) { |  | ||||||
| #define CLS(x)                                                                                     \ |  | ||||||
|     case Class::x:                                                                                 \ |  | ||||||
|         return #x; |  | ||||||
| #define SUB(x, y)                                                                                  \ |  | ||||||
|     case Class::x##_##y:                                                                           \ |  | ||||||
|         return #x "." #y; |  | ||||||
|         ALL_LOG_CLASSES() |  | ||||||
| #undef CLS |  | ||||||
| #undef SUB |  | ||||||
|     case Class::Count: |  | ||||||
|     default: |  | ||||||
|         break; |  | ||||||
|     } |     } | ||||||
|     UNREACHABLE(); |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| const char* GetLevelName(Level log_level) { |     static void Deleter(Impl* ptr) { | ||||||
| #define LVL(x)                                                                                     \ |         delete ptr; | ||||||
|     case Level::x:                                                                                 \ |  | ||||||
|         return #x |  | ||||||
|     switch (log_level) { |  | ||||||
|         LVL(Trace); |  | ||||||
|         LVL(Debug); |  | ||||||
|         LVL(Info); |  | ||||||
|         LVL(Warning); |  | ||||||
|         LVL(Error); |  | ||||||
|         LVL(Critical); |  | ||||||
|     case Level::Count: |  | ||||||
|     default: |  | ||||||
|         break; |  | ||||||
|     } |     } | ||||||
| #undef LVL | 
 | ||||||
|     UNREACHABLE(); | #ifdef CITRA_LINUX_GCC_BACKTRACE | ||||||
|  |     [[noreturn]] static void HandleSignal(int sig) { | ||||||
|  |         signal(SIGABRT, SIG_DFL); | ||||||
|  |         signal(SIGSEGV, SIG_DFL); | ||||||
|  |         if (sig <= 0) { | ||||||
|  |             abort(); | ||||||
|  |         } | ||||||
|  |         instance->InstanceHandleSignal(sig); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     [[noreturn]] void InstanceHandleSignal(int sig) { | ||||||
|  |         if (int zero_or_ignore = 0; !received_signal.compare_exchange_strong(zero_or_ignore, sig)) { | ||||||
|  |             if (received_signal == SIGKILL) { | ||||||
|  |                 abort(); | ||||||
|  |             } | ||||||
|  |             SleepForever(); | ||||||
|  |         } | ||||||
|  |         // Don't restart like boost suggests. We want to append to the log file and not lose dynamic
 | ||||||
|  |         // symbols. This may segfault if it unwinds outside C/C++ code but we'll just have to fall
 | ||||||
|  |         // back to core dumps.
 | ||||||
|  |         boost::stacktrace::safe_dump_to(backtrace_storage.data(), 4096); | ||||||
|  |         std::atomic_thread_fence(std::memory_order_seq_cst); | ||||||
|  |         for (const int anything = 0; write(backtrace_thread_waker_fd, &anything, 1) != 1;) | ||||||
|  |             ; | ||||||
|  |         for (u8 ignore = 0; read(backtrace_done_printing_fd, &ignore, 1) != 1;) | ||||||
|  |             ; | ||||||
|  |         abort(); | ||||||
|  |     } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |     static inline std::unique_ptr<Impl, decltype(&Deleter)> instance{nullptr, Deleter}; | ||||||
|  | 
 | ||||||
|  |     Filter filter; | ||||||
|  |     DebuggerBackend debugger_backend{}; | ||||||
|  |     ColorConsoleBackend color_console_backend{}; | ||||||
|  |     FileBackend file_backend; | ||||||
|  | #ifdef ANDROID | ||||||
|  |     LogcatBackend lc_backend{}; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |     MPSCQueue<Entry> message_queue{}; | ||||||
|  |     std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; | ||||||
|  |     std::jthread backend_thread; | ||||||
|  | 
 | ||||||
|  | #ifdef CITRA_LINUX_GCC_BACKTRACE | ||||||
|  |     std::atomic_int received_signal{0}; | ||||||
|  |     std::array<u8, 4096> backtrace_storage{}; | ||||||
|  |     int backtrace_thread_waker_fd; | ||||||
|  |     int backtrace_done_printing_fd; | ||||||
|  | #endif | ||||||
|  | }; | ||||||
|  | } // namespace
 | ||||||
|  | 
 | ||||||
|  | void Initialize(std::string_view log_file) { | ||||||
|  |     Impl::Initialize(log_file.empty() ? LOG_FILE : log_file); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void AddBackend(std::unique_ptr<Backend> backend) { | void Start() { | ||||||
|     Impl::Instance().AddBackend(std::move(backend)); |     Impl::Start(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RemoveBackend(std::string_view backend_name) { | void DisableLoggingInTests() { | ||||||
|     Impl::Instance().RemoveBackend(backend_name); |     initialization_in_progress_suppress_logging = true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Backend* GetBackend(std::string_view backend_name) { | void SetGlobalFilter(const Filter& filter) { | ||||||
|     return Impl::Instance().GetBackend(backend_name); |     Impl::Instance().SetGlobalFilter(filter); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void SetColorConsoleBackendEnabled(bool enabled) { | ||||||
|  |     Impl::Instance().SetColorConsoleBackendEnabled(enabled); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, | void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, | ||||||
|                        unsigned int line_num, const char* function, const char* format, |                        unsigned int line_num, const char* function, const char* format, | ||||||
|                        const fmt::format_args& args) { |                        const fmt::format_args& args) { | ||||||
|     auto& instance = Impl::Instance(); |     if (!initialization_in_progress_suppress_logging) { | ||||||
|     instance.PushEntry(log_class, log_level, filename, line_num, function, |         Impl::Instance().PushEntry(log_class, log_level, filename, line_num, function, | ||||||
|                        fmt::vformat(format, args)); |                                    fmt::vformat(format, args)); | ||||||
|  |     } | ||||||
| } | } | ||||||
| } // namespace Log
 | } // namespace Common::Log
 | ||||||
|  |  | ||||||
|  | @ -4,149 +4,24 @@ | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <chrono> |  | ||||||
| #include <memory> |  | ||||||
| #include <string> |  | ||||||
| #include <string_view> | #include <string_view> | ||||||
| #include "common/file_util.h" |  | ||||||
| #include "common/logging/filter.h" | #include "common/logging/filter.h" | ||||||
| #include "common/logging/log.h" |  | ||||||
| 
 | 
 | ||||||
| namespace Log { | namespace Common::Log { | ||||||
|  | 
 | ||||||
|  | class Filter; | ||||||
|  | 
 | ||||||
|  | /// Initializes the logging system. This should be the first thing called in main.
 | ||||||
|  | void Initialize(std::string_view log_file = ""); | ||||||
|  | 
 | ||||||
|  | void Start(); | ||||||
|  | 
 | ||||||
|  | void DisableLoggingInTests(); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * A log entry. Log entries are store in a structured format to permit more varied output |  * The global filter will prevent any messages from even being processed if they are filtered. | ||||||
|  * formatting on different frontends, as well as facilitating filtering and aggregation. |  | ||||||
|  */ |  */ | ||||||
| struct Entry { | void SetGlobalFilter(const Filter& filter); | ||||||
|     std::chrono::microseconds timestamp; |  | ||||||
|     Class log_class; |  | ||||||
|     Level log_level; |  | ||||||
|     const char* filename; |  | ||||||
|     unsigned int line_num; |  | ||||||
|     std::string function; |  | ||||||
|     std::string message; |  | ||||||
|     bool final_entry = false; |  | ||||||
| 
 | 
 | ||||||
|     Entry() = default; | void SetColorConsoleBackendEnabled(bool enabled); | ||||||
|     Entry(Entry&& o) = default; | } // namespace Common::Log
 | ||||||
| 
 |  | ||||||
|     Entry& operator=(Entry&& o) = default; |  | ||||||
|     Entry& operator=(const Entry& o) = default; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * 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 the Android logcat |  | ||||||
|  */ |  | ||||||
| class LogcatBackend : public Backend { |  | ||||||
| public: |  | ||||||
|     static const char* Name() { |  | ||||||
|         return "logcat"; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     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; |  | ||||||
|     std::size_t bytes_written; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * Backend that writes to Visual Studio's output window |  | ||||||
|  */ |  | ||||||
| class DebuggerBackend : public Backend { |  | ||||||
| public: |  | ||||||
|     static const char* Name() { |  | ||||||
|         return "debugger"; |  | ||||||
|     } |  | ||||||
|     const char* GetName() const override { |  | ||||||
|         return Name(); |  | ||||||
|     } |  | ||||||
|     void Write(const Entry& entry) override; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| void AddBackend(std::unique_ptr<Backend> backend); |  | ||||||
| 
 |  | ||||||
| void RemoveBackend(std::string_view backend_name); |  | ||||||
| 
 |  | ||||||
| Backend* GetBackend(std::string_view 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. |  | ||||||
|  */ |  | ||||||
| const char* GetLogClassName(Class log_class); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * Returns the name of the passed log level as a C-string. |  | ||||||
|  */ |  | ||||||
| const char* GetLevelName(Level log_level); |  | ||||||
| 
 |  | ||||||
| } // namespace Log
 |  | ||||||
|  |  | ||||||
|  | @ -3,11 +3,12 @@ | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include <algorithm> | #include <algorithm> | ||||||
| #include "common/logging/backend.h" | 
 | ||||||
|  | #include "common/assert.h" | ||||||
| #include "common/logging/filter.h" | #include "common/logging/filter.h" | ||||||
| #include "common/string_util.h" | #include "common/string_util.h" | ||||||
| 
 | 
 | ||||||
| namespace Log { | namespace Common::Log { | ||||||
| namespace { | namespace { | ||||||
| template <typename It> | template <typename It> | ||||||
| Level GetLevelByName(const It begin, const It end) { | Level GetLevelByName(const It begin, const It end) { | ||||||
|  | @ -22,7 +23,7 @@ Level GetLevelByName(const It begin, const It end) { | ||||||
| 
 | 
 | ||||||
| template <typename It> | template <typename It> | ||||||
| Class GetClassByName(const It begin, const It end) { | Class GetClassByName(const It begin, const It end) { | ||||||
|     for (ClassType i = 0; i < static_cast<ClassType>(Class::Count); ++i) { |     for (u8 i = 0; i < static_cast<u8>(Class::Count); ++i) { | ||||||
|         const char* level_name = GetLogClassName(static_cast<Class>(i)); |         const char* level_name = GetLogClassName(static_cast<Class>(i)); | ||||||
|         if (Common::ComparePartialString(begin, end, level_name)) { |         if (Common::ComparePartialString(begin, end, level_name)) { | ||||||
|             return static_cast<Class>(i); |             return static_cast<Class>(i); | ||||||
|  | @ -62,6 +63,115 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { | ||||||
| } | } | ||||||
| } // Anonymous namespace
 | } // Anonymous namespace
 | ||||||
| 
 | 
 | ||||||
|  | /// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
 | ||||||
|  | #define ALL_LOG_CLASSES()                                                                          \ | ||||||
|  |     CLS(Log)                                                                                       \ | ||||||
|  |     CLS(Common)                                                                                    \ | ||||||
|  |     SUB(Common, Filesystem)                                                                        \ | ||||||
|  |     SUB(Common, Memory)                                                                            \ | ||||||
|  |     CLS(Core)                                                                                      \ | ||||||
|  |     SUB(Core, ARM11)                                                                               \ | ||||||
|  |     SUB(Core, Timing)                                                                              \ | ||||||
|  |     SUB(Core, Cheats)                                                                              \ | ||||||
|  |     CLS(Config)                                                                                    \ | ||||||
|  |     CLS(Debug)                                                                                     \ | ||||||
|  |     SUB(Debug, Emulated)                                                                           \ | ||||||
|  |     SUB(Debug, GPU)                                                                                \ | ||||||
|  |     SUB(Debug, Breakpoint)                                                                         \ | ||||||
|  |     SUB(Debug, GDBStub)                                                                            \ | ||||||
|  |     CLS(Kernel)                                                                                    \ | ||||||
|  |     SUB(Kernel, SVC)                                                                               \ | ||||||
|  |     CLS(Applet)                                                                                    \ | ||||||
|  |     SUB(Applet, SWKBD)                                                                             \ | ||||||
|  |     CLS(Service)                                                                                   \ | ||||||
|  |     SUB(Service, SRV)                                                                              \ | ||||||
|  |     SUB(Service, FRD)                                                                              \ | ||||||
|  |     SUB(Service, FS)                                                                               \ | ||||||
|  |     SUB(Service, ERR)                                                                              \ | ||||||
|  |     SUB(Service, APT)                                                                              \ | ||||||
|  |     SUB(Service, BOSS)                                                                             \ | ||||||
|  |     SUB(Service, GSP)                                                                              \ | ||||||
|  |     SUB(Service, AC)                                                                               \ | ||||||
|  |     SUB(Service, AM)                                                                               \ | ||||||
|  |     SUB(Service, PTM)                                                                              \ | ||||||
|  |     SUB(Service, LDR)                                                                              \ | ||||||
|  |     SUB(Service, MIC)                                                                              \ | ||||||
|  |     SUB(Service, NDM)                                                                              \ | ||||||
|  |     SUB(Service, NFC)                                                                              \ | ||||||
|  |     SUB(Service, NIM)                                                                              \ | ||||||
|  |     SUB(Service, NS)                                                                               \ | ||||||
|  |     SUB(Service, NWM)                                                                              \ | ||||||
|  |     SUB(Service, CAM)                                                                              \ | ||||||
|  |     SUB(Service, CECD)                                                                             \ | ||||||
|  |     SUB(Service, CFG)                                                                              \ | ||||||
|  |     SUB(Service, CSND)                                                                             \ | ||||||
|  |     SUB(Service, DSP)                                                                              \ | ||||||
|  |     SUB(Service, DLP)                                                                              \ | ||||||
|  |     SUB(Service, HID)                                                                              \ | ||||||
|  |     SUB(Service, HTTP)                                                                             \ | ||||||
|  |     SUB(Service, SOC)                                                                              \ | ||||||
|  |     SUB(Service, IR)                                                                               \ | ||||||
|  |     SUB(Service, Y2R)                                                                              \ | ||||||
|  |     SUB(Service, PS)                                                                               \ | ||||||
|  |     SUB(Service, PLGLDR)                                                                           \ | ||||||
|  |     CLS(HW)                                                                                        \ | ||||||
|  |     SUB(HW, Memory)                                                                                \ | ||||||
|  |     SUB(HW, LCD)                                                                                   \ | ||||||
|  |     SUB(HW, GPU)                                                                                   \ | ||||||
|  |     SUB(HW, AES)                                                                                   \ | ||||||
|  |     CLS(Frontend)                                                                                  \ | ||||||
|  |     CLS(Render)                                                                                    \ | ||||||
|  |     SUB(Render, Software)                                                                          \ | ||||||
|  |     SUB(Render, OpenGL)                                                                            \ | ||||||
|  |     SUB(Render, Vulkan)                                                                            \ | ||||||
|  |     CLS(Audio)                                                                                     \ | ||||||
|  |     SUB(Audio, DSP)                                                                                \ | ||||||
|  |     SUB(Audio, Sink)                                                                               \ | ||||||
|  |     CLS(Input)                                                                                     \ | ||||||
|  |     CLS(Network)                                                                                   \ | ||||||
|  |     CLS(Movie)                                                                                     \ | ||||||
|  |     CLS(Loader)                                                                                    \ | ||||||
|  |     CLS(WebService)                                                                                \ | ||||||
|  |     CLS(RPC_Server) | ||||||
|  | 
 | ||||||
|  | // GetClassName is a macro defined by Windows.h, grrr...
 | ||||||
|  | const char* GetLogClassName(Class log_class) { | ||||||
|  |     switch (log_class) { | ||||||
|  | #define CLS(x)                                                                                     \ | ||||||
|  |     case Class::x:                                                                                 \ | ||||||
|  |         return #x; | ||||||
|  | #define SUB(x, y)                                                                                  \ | ||||||
|  |     case Class::x##_##y:                                                                           \ | ||||||
|  |         return #x "." #y; | ||||||
|  |         ALL_LOG_CLASSES() | ||||||
|  | #undef CLS | ||||||
|  | #undef SUB | ||||||
|  |     case Class::Count: | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     UNREACHABLE(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const char* GetLevelName(Level log_level) { | ||||||
|  | #define LVL(x)                                                                                     \ | ||||||
|  |     case Level::x:                                                                                 \ | ||||||
|  |         return #x | ||||||
|  |     switch (log_level) { | ||||||
|  |         LVL(Trace); | ||||||
|  |         LVL(Debug); | ||||||
|  |         LVL(Info); | ||||||
|  |         LVL(Warning); | ||||||
|  |         LVL(Error); | ||||||
|  |         LVL(Critical); | ||||||
|  |     case Level::Count: | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | #undef LVL | ||||||
|  |     UNREACHABLE(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| Filter::Filter(Level default_level) { | Filter::Filter(Level default_level) { | ||||||
|     ResetAll(default_level); |     ResetAll(default_level); | ||||||
| } | } | ||||||
|  | @ -96,4 +206,11 @@ bool Filter::CheckMessage(Class log_class, Level level) const { | ||||||
|     return static_cast<u8>(level) >= |     return static_cast<u8>(level) >= | ||||||
|            static_cast<u8>(class_levels[static_cast<std::size_t>(log_class)]); |            static_cast<u8>(class_levels[static_cast<std::size_t>(log_class)]); | ||||||
| } | } | ||||||
| } // namespace Log
 | 
 | ||||||
|  | bool Filter::IsDebug() const { | ||||||
|  |     return std::any_of(class_levels.begin(), class_levels.end(), [](const Level& l) { | ||||||
|  |         return static_cast<u8>(l) <= static_cast<u8>(Level::Debug); | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace Common::Log
 | ||||||
|  |  | ||||||
|  | @ -7,6 +7,60 @@ | ||||||
| #include <array> | #include <array> | ||||||
| #include <cstddef> | #include <cstddef> | ||||||
| #include <string_view> | #include <string_view> | ||||||
| #include "common/logging/log.h" | #include "common/logging/types.h" | ||||||
| 
 | 
 | ||||||
| namespace Log {} // namespace Log
 | namespace Common::Log { | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Returns the name of the passed log class as a C-string. Subclasses are separated by periods | ||||||
|  |  * instead of underscores as in the enumeration. | ||||||
|  |  */ | ||||||
|  | const char* GetLogClassName(Class log_class); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Returns the name of the passed log level as a C-string. | ||||||
|  |  */ | ||||||
|  | const char* GetLevelName(Level log_level); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Implements a log message filter which allows different log classes to have different minimum | ||||||
|  |  * severity levels. The filter can be changed at runtime and can be parsed from a string to allow | ||||||
|  |  * editing via the interface or loading from a configuration file. | ||||||
|  |  */ | ||||||
|  | class Filter { | ||||||
|  | public: | ||||||
|  |     /// Initializes the filter with all classes having `default_level` as the minimum level.
 | ||||||
|  |     explicit Filter(Level default_level = Level::Info); | ||||||
|  | 
 | ||||||
|  |     /// Resets the filter so that all classes have `level` as the minimum displayed level.
 | ||||||
|  |     void ResetAll(Level level); | ||||||
|  |     /// Sets the minimum level of `log_class` (and not of its subclasses) to `level`.
 | ||||||
|  |     void SetClassLevel(Class log_class, Level level); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Parses a filter string and applies it to this filter. | ||||||
|  |      * | ||||||
|  |      * A filter string consists of a space-separated list of filter rules, each of the format | ||||||
|  |      * `<class>:<level>`. `<class>` is a log class name, with subclasses separated using periods. | ||||||
|  |      * `*` is allowed as a class name and will reset all filters to the specified level. `<level>` | ||||||
|  |      * a severity level name which will be set as the minimum logging level of the matched classes. | ||||||
|  |      * Rules are applied left to right, with each rule overriding previous ones in the sequence. | ||||||
|  |      * | ||||||
|  |      * A few examples of filter rules: | ||||||
|  |      *  - `*:Info` -- Resets the level of all classes to Info. | ||||||
|  |      *  - `Service:Info` -- Sets the level of Service to Info. | ||||||
|  |      *  - `Service.FS:Trace` -- Sets the level of the Service.FS class to Trace. | ||||||
|  |      */ | ||||||
|  |     void ParseFilterString(std::string_view filter_view); | ||||||
|  | 
 | ||||||
|  |     /// Matches class/level combination against the filter, returning true if it passed.
 | ||||||
|  |     bool CheckMessage(Class log_class, Level level) const; | ||||||
|  | 
 | ||||||
|  |     /// Returns true if any logging classes are set to debug
 | ||||||
|  |     bool IsDebug() const; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     std::array<Level, static_cast<std::size_t>(Class::Count)> class_levels; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace Common::Log
 | ||||||
|  |  | ||||||
|  | @ -6,10 +6,12 @@ | ||||||
| 
 | 
 | ||||||
| #include <algorithm> | #include <algorithm> | ||||||
| #include <array> | #include <array> | ||||||
| #include "common/common_types.h" | #include <string_view> | ||||||
| #include "common/logging/formatter.h" |  | ||||||
| 
 | 
 | ||||||
| namespace Log { | #include "common/logging/formatter.h" | ||||||
|  | #include "common/logging/types.h" | ||||||
|  | 
 | ||||||
|  | namespace Common::Log { | ||||||
| 
 | 
 | ||||||
| // trims up to and including the last of ../, ..\, src/, src\ in a string
 | // trims up to and including the last of ../, ..\, src/, src\ in a string
 | ||||||
| constexpr const char* TrimSourcePath(std::string_view source) { | constexpr const char* TrimSourcePath(std::string_view source) { | ||||||
|  | @ -20,144 +22,6 @@ constexpr const char* TrimSourcePath(std::string_view source) { | ||||||
|     return source.data() + idx; |     return source.data() + idx; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Specifies the severity or level of detail of the log message.
 |  | ||||||
| enum class Level : u8 { |  | ||||||
|     Trace,    ///< Extremely detailed and repetitive debugging information that is likely to
 |  | ||||||
|               ///< pollute logs.
 |  | ||||||
|     Debug,    ///< Less detailed debugging information.
 |  | ||||||
|     Info,     ///< Status information from important points during execution.
 |  | ||||||
|     Warning,  ///< Minor or potential problems found during execution of a task.
 |  | ||||||
|     Error,    ///< Major problems found during execution of a task that prevent it from being
 |  | ||||||
|               ///< completed.
 |  | ||||||
|     Critical, ///< Major problems during execution that threaten the stability of the entire
 |  | ||||||
|               ///< application.
 |  | ||||||
| 
 |  | ||||||
|     Count ///< Total number of logging levels
 |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| typedef u8 ClassType; |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * Specifies the sub-system that generated the log message. |  | ||||||
|  * |  | ||||||
|  * @note If you add a new entry here, also add a corresponding one to `ALL_LOG_CLASSES` in |  | ||||||
|  * backend.cpp. |  | ||||||
|  */ |  | ||||||
| enum class Class : ClassType { |  | ||||||
|     Log,               ///< Messages about the log system itself
 |  | ||||||
|     Common,            ///< Library routines
 |  | ||||||
|     Common_Filesystem, ///< Filesystem interface library
 |  | ||||||
|     Common_Memory,     ///< Memory mapping and management functions
 |  | ||||||
|     Core,              ///< LLE emulation core
 |  | ||||||
|     Core_ARM11,        ///< ARM11 CPU core
 |  | ||||||
|     Core_Timing,       ///< CoreTiming functions
 |  | ||||||
|     Core_Cheats,       ///< Cheat functions
 |  | ||||||
|     Config,            ///< Emulator configuration (including commandline)
 |  | ||||||
|     Debug,             ///< Debugging tools
 |  | ||||||
|     Debug_Emulated,    ///< Debug messages from the emulated programs
 |  | ||||||
|     Debug_GPU,         ///< GPU debugging tools
 |  | ||||||
|     Debug_Breakpoint,  ///< Logging breakpoints and watchpoints
 |  | ||||||
|     Debug_GDBStub,     ///< GDB Stub
 |  | ||||||
|     Kernel,            ///< The HLE implementation of the CTR kernel
 |  | ||||||
|     Kernel_SVC,        ///< Kernel system calls
 |  | ||||||
|     Applet,            ///< HLE implementation of system applets. Each applet
 |  | ||||||
|                        ///< should have its own subclass.
 |  | ||||||
|     Applet_SWKBD,      ///< The Software Keyboard applet
 |  | ||||||
|     Service,           ///< HLE implementation of system services. Each major service
 |  | ||||||
|                        ///< should have its own subclass.
 |  | ||||||
|     Service_SRV,       ///< The SRV (Service Directory) implementation
 |  | ||||||
|     Service_FRD,       ///< The FRD (Friends) service
 |  | ||||||
|     Service_FS,        ///< The FS (Filesystem) service implementation
 |  | ||||||
|     Service_ERR,       ///< The ERR (Error) port implementation
 |  | ||||||
|     Service_APT,       ///< The APT (Applets) service
 |  | ||||||
|     Service_BOSS,      ///< The BOSS (SpotPass) service
 |  | ||||||
|     Service_GSP,       ///< The GSP (GPU control) service
 |  | ||||||
|     Service_AC,        ///< The AC (WiFi status) service
 |  | ||||||
|     Service_AM,        ///< The AM (Application manager) service
 |  | ||||||
|     Service_PTM,       ///< The PTM (Power status & misc.) service
 |  | ||||||
|     Service_LDR,       ///< The LDR (3ds dll loader) service
 |  | ||||||
|     Service_MIC,       ///< The MIC (Microphone) service
 |  | ||||||
|     Service_NDM,       ///< The NDM (Network daemon manager) service
 |  | ||||||
|     Service_NFC,       ///< The NFC service
 |  | ||||||
|     Service_NIM,       ///< The NIM (Network interface manager) service
 |  | ||||||
|     Service_NS,        ///< The NS (Nintendo User Interface Shell) service
 |  | ||||||
|     Service_NWM,       ///< The NWM (Network wlan manager) service
 |  | ||||||
|     Service_CAM,       ///< The CAM (Camera) service
 |  | ||||||
|     Service_CECD,      ///< The CECD (StreetPass) service
 |  | ||||||
|     Service_CFG,       ///< The CFG (Configuration) service
 |  | ||||||
|     Service_CSND,      ///< The CSND (CWAV format process) service
 |  | ||||||
|     Service_DSP,       ///< The DSP (DSP control) service
 |  | ||||||
|     Service_DLP,       ///< The DLP (Download Play) service
 |  | ||||||
|     Service_HID,       ///< The HID (Human interface device) service
 |  | ||||||
|     Service_HTTP,      ///< The HTTP service
 |  | ||||||
|     Service_SOC,       ///< The SOC (Socket) service
 |  | ||||||
|     Service_IR,        ///< The IR service
 |  | ||||||
|     Service_Y2R,       ///< The Y2R (YUV to RGB conversion) service
 |  | ||||||
|     Service_PS,        ///< The PS (Process) service
 |  | ||||||
|     Service_PLGLDR,    ///< The PLGLDR (plugin loader) service
 |  | ||||||
|     HW,                ///< Low-level hardware emulation
 |  | ||||||
|     HW_Memory,         ///< Memory-map and address translation
 |  | ||||||
|     HW_LCD,            ///< LCD register emulation
 |  | ||||||
|     HW_GPU,            ///< GPU control emulation
 |  | ||||||
|     HW_AES,            ///< AES engine emulation
 |  | ||||||
|     Frontend,          ///< Emulator UI
 |  | ||||||
|     Render,            ///< Emulator video output and hardware acceleration
 |  | ||||||
|     Render_Software,   ///< Software renderer backend
 |  | ||||||
|     Render_OpenGL,     ///< OpenGL backend
 |  | ||||||
|     Render_Vulkan,     ///< Vulkan backend
 |  | ||||||
|     Audio,             ///< Audio emulation
 |  | ||||||
|     Audio_DSP,         ///< The HLE and LLE implementations of the DSP
 |  | ||||||
|     Audio_Sink,        ///< Emulator audio output backend
 |  | ||||||
|     Loader,            ///< ROM loader
 |  | ||||||
|     Input,             ///< Input emulation
 |  | ||||||
|     Network,           ///< Network emulation
 |  | ||||||
|     Movie,             ///< Movie (Input Recording) Playback
 |  | ||||||
|     WebService,        ///< Interface to Citra Web Services
 |  | ||||||
|     RPC_Server,        ///< RPC server
 |  | ||||||
|     Count              ///< Total number of logging classes
 |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * Implements a log message filter which allows different log classes to have different minimum |  | ||||||
|  * severity levels. The filter can be changed at runtime and can be parsed from a string to allow |  | ||||||
|  * editing via the interface or loading from a configuration file. |  | ||||||
|  */ |  | ||||||
| class Filter { |  | ||||||
| public: |  | ||||||
|     /// Initializes the filter with all classes having `default_level` as the minimum level.
 |  | ||||||
|     explicit Filter(Level default_level = Level::Info); |  | ||||||
| 
 |  | ||||||
|     /// Resets the filter so that all classes have `level` as the minimum displayed level.
 |  | ||||||
|     void ResetAll(Level level); |  | ||||||
|     /// Sets the minimum level of `log_class` (and not of its subclasses) to `level`.
 |  | ||||||
|     void SetClassLevel(Class log_class, Level level); |  | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|      * Parses a filter string and applies it to this filter. |  | ||||||
|      * |  | ||||||
|      * A filter string consists of a space-separated list of filter rules, each of the format |  | ||||||
|      * `<class>:<level>`. `<class>` is a log class name, with subclasses separated using periods. |  | ||||||
|      * `*` is allowed as a class name and will reset all filters to the specified level. `<level>` |  | ||||||
|      * a severity level name which will be set as the minimum logging level of the matched classes. |  | ||||||
|      * Rules are applied left to right, with each rule overriding previous ones in the sequence. |  | ||||||
|      * |  | ||||||
|      * A few examples of filter rules: |  | ||||||
|      *  - `*:Info` -- Resets the level of all classes to Info. |  | ||||||
|      *  - `Service:Info` -- Sets the level of Service to Info. |  | ||||||
|      *  - `Service.FS:Trace` -- Sets the level of the Service.FS class to Trace. |  | ||||||
|      */ |  | ||||||
|     void ParseFilterString(std::string_view filter_view); |  | ||||||
| 
 |  | ||||||
|     /// Matches class/level combination against the filter, returning true if it passed.
 |  | ||||||
|     bool CheckMessage(Class log_class, Level level) const; |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     std::array<Level, static_cast<std::size_t>(Class::Count)> class_levels; |  | ||||||
| }; |  | ||||||
| extern Filter filter; |  | ||||||
| 
 |  | ||||||
| void SetGlobalFilter(const Filter& f); |  | ||||||
| 
 |  | ||||||
| /// Logs a message to the global logger, using fmt
 | /// Logs a message to the global logger, using fmt
 | ||||||
| void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, | void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, | ||||||
|                        unsigned int line_num, const char* function, const char* format, |                        unsigned int line_num, const char* function, const char* format, | ||||||
|  | @ -166,40 +30,43 @@ void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, | ||||||
| template <typename... Args> | template <typename... Args> | ||||||
| void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num, | void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num, | ||||||
|                    const char* function, const char* format, const Args&... args) { |                    const char* function, const char* format, const Args&... args) { | ||||||
|     if (!filter.CheckMessage(log_class, log_level)) |  | ||||||
|         return; |  | ||||||
| 
 |  | ||||||
|     FmtLogMessageImpl(log_class, log_level, filename, line_num, function, format, |     FmtLogMessageImpl(log_class, log_level, filename, line_num, function, format, | ||||||
|                       fmt::make_format_args(args...)); |                       fmt::make_format_args(args...)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } // namespace Log
 | } // namespace Common::Log
 | ||||||
| 
 | 
 | ||||||
| // Define the fmt lib macros
 | // Define the fmt lib macros
 | ||||||
| #define LOG_GENERIC(log_class, log_level, ...)                                                     \ | #define LOG_GENERIC(log_class, log_level, ...)                                                     \ | ||||||
|     ::Log::FmtLogMessage(log_class, log_level, ::Log::TrimSourcePath(__FILE__), __LINE__,          \ |     Common::Log::FmtLogMessage(log_class, log_level, Common::Log::TrimSourcePath(__FILE__),        \ | ||||||
|                          __func__, __VA_ARGS__) |                                __LINE__, __func__, __VA_ARGS__) | ||||||
| 
 | 
 | ||||||
| #ifdef _DEBUG | #ifdef _DEBUG | ||||||
| #define LOG_TRACE(log_class, ...)                                                                  \ | #define LOG_TRACE(log_class, ...)                                                                  \ | ||||||
|     ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Trace,                             \ |     Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Trace,           \ | ||||||
|                          ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) |                                Common::Log::TrimSourcePath(__FILE__), __LINE__, __func__,          \ | ||||||
|  |                                __VA_ARGS__) | ||||||
| #else | #else | ||||||
| #define LOG_TRACE(log_class, fmt, ...) (void(0)) | #define LOG_TRACE(log_class, fmt, ...) (void(0)) | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #define LOG_DEBUG(log_class, ...)                                                                  \ | #define LOG_DEBUG(log_class, ...)                                                                  \ | ||||||
|     ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Debug,                             \ |     Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Debug,           \ | ||||||
|                          ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) |                                Common::Log::TrimSourcePath(__FILE__), __LINE__, __func__,          \ | ||||||
|  |                                __VA_ARGS__) | ||||||
| #define LOG_INFO(log_class, ...)                                                                   \ | #define LOG_INFO(log_class, ...)                                                                   \ | ||||||
|     ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Info,                              \ |     Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Info,            \ | ||||||
|                          ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) |                                Common::Log::TrimSourcePath(__FILE__), __LINE__, __func__,          \ | ||||||
|  |                                __VA_ARGS__) | ||||||
| #define LOG_WARNING(log_class, ...)                                                                \ | #define LOG_WARNING(log_class, ...)                                                                \ | ||||||
|     ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Warning,                           \ |     Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Warning,         \ | ||||||
|                          ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) |                                Common::Log::TrimSourcePath(__FILE__), __LINE__, __func__,          \ | ||||||
|  |                                __VA_ARGS__) | ||||||
| #define LOG_ERROR(log_class, ...)                                                                  \ | #define LOG_ERROR(log_class, ...)                                                                  \ | ||||||
|     ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Error,                             \ |     Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Error,           \ | ||||||
|                          ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) |                                Common::Log::TrimSourcePath(__FILE__), __LINE__, __func__,          \ | ||||||
|  |                                __VA_ARGS__) | ||||||
| #define LOG_CRITICAL(log_class, ...)                                                               \ | #define LOG_CRITICAL(log_class, ...)                                                               \ | ||||||
|     ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Critical,                          \ |     Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Critical,        \ | ||||||
|                          ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) |                                Common::Log::TrimSourcePath(__FILE__), __LINE__, __func__,          \ | ||||||
|  |                                __VA_ARGS__) | ||||||
|  |  | ||||||
							
								
								
									
										27
									
								
								src/common/logging/log_entry.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/common/logging/log_entry.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | ||||||
|  | // Copyright 2021 yuzu Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <chrono> | ||||||
|  | 
 | ||||||
|  | #include "common/logging/types.h" | ||||||
|  | 
 | ||||||
|  | namespace Common::Log { | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * A log entry. Log entries are store in a structured format to permit more varied output | ||||||
|  |  * formatting on different frontends, as well as facilitating filtering and aggregation. | ||||||
|  |  */ | ||||||
|  | struct Entry { | ||||||
|  |     std::chrono::microseconds timestamp; | ||||||
|  |     Class log_class{}; | ||||||
|  |     Level log_level{}; | ||||||
|  |     const char* filename = nullptr; | ||||||
|  |     u32 line_num = 0; | ||||||
|  |     std::string function; | ||||||
|  |     std::string message; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace Common::Log
 | ||||||
|  | @ -12,13 +12,12 @@ | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| #include "common/common_funcs.h" | #include "common/logging/filter.h" | ||||||
| #include "common/logging/backend.h" |  | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
|  | #include "common/logging/log_entry.h" | ||||||
| #include "common/logging/text_formatter.h" | #include "common/logging/text_formatter.h" | ||||||
| #include "common/string_util.h" |  | ||||||
| 
 | 
 | ||||||
| namespace Log { | namespace Common::Log { | ||||||
| 
 | 
 | ||||||
| std::string FormatLogMessage(const Entry& entry) { | std::string FormatLogMessage(const Entry& entry) { | ||||||
|     unsigned int time_seconds = static_cast<unsigned int>(entry.timestamp.count() / 1000000); |     unsigned int time_seconds = static_cast<unsigned int>(entry.timestamp.count() / 1000000); | ||||||
|  | @ -141,4 +140,4 @@ void PrintMessageToLogcat([[maybe_unused]] const Entry& entry) { | ||||||
|     __android_log_print(android_log_priority, "CitraNative", "%s", str.c_str()); |     __android_log_print(android_log_priority, "CitraNative", "%s", str.c_str()); | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
| } // namespace Log
 | } // namespace Common::Log
 | ||||||
|  |  | ||||||
|  | @ -4,10 +4,9 @@ | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <cstddef> |  | ||||||
| #include <string> | #include <string> | ||||||
| 
 | 
 | ||||||
| namespace Log { | namespace Common::Log { | ||||||
| 
 | 
 | ||||||
| struct Entry; | struct Entry; | ||||||
| 
 | 
 | ||||||
|  | @ -19,4 +18,4 @@ void PrintMessage(const Entry& entry); | ||||||
| void PrintColoredMessage(const Entry& entry); | void PrintColoredMessage(const Entry& entry); | ||||||
| /// Formats and prints a log entry to the android logcat.
 | /// Formats and prints a log entry to the android logcat.
 | ||||||
| void PrintMessageToLogcat(const Entry& entry); | void PrintMessageToLogcat(const Entry& entry); | ||||||
| } // namespace Log
 | } // namespace Common::Log
 | ||||||
|  |  | ||||||
							
								
								
									
										106
									
								
								src/common/logging/types.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								src/common/logging/types.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,106 @@ | ||||||
|  | // Copyright 2023 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "common/common_types.h" | ||||||
|  | 
 | ||||||
|  | namespace Common::Log { | ||||||
|  | 
 | ||||||
|  | /// Specifies the severity or level of detail of the log message.
 | ||||||
|  | enum class Level : u8 { | ||||||
|  |     Trace, ///< Extremely detailed and repetitive debugging information that is likely to
 | ||||||
|  |     ///< pollute logs.
 | ||||||
|  |     Debug,   ///< Less detailed debugging information.
 | ||||||
|  |     Info,    ///< Status information from important points during execution.
 | ||||||
|  |     Warning, ///< Minor or potential problems found during execution of a task.
 | ||||||
|  |     Error,   ///< Major problems found during execution of a task that prevent it from being
 | ||||||
|  |     ///< completed.
 | ||||||
|  |     Critical, ///< Major problems during execution that threaten the stability of the entire
 | ||||||
|  |     ///< application.
 | ||||||
|  | 
 | ||||||
|  |     Count, ///< Total number of logging levels
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Specifies the sub-system that generated the log message. | ||||||
|  |  * | ||||||
|  |  * @note If you add a new entry here, also add a corresponding one to `ALL_LOG_CLASSES` in | ||||||
|  |  * backend.cpp. | ||||||
|  |  */ | ||||||
|  | enum class Class : u8 { | ||||||
|  |     Log,               ///< Messages about the log system itself
 | ||||||
|  |     Common,            ///< Library routines
 | ||||||
|  |     Common_Filesystem, ///< Filesystem interface library
 | ||||||
|  |     Common_Memory,     ///< Memory mapping and management functions
 | ||||||
|  |     Core,              ///< LLE emulation core
 | ||||||
|  |     Core_ARM11,        ///< ARM11 CPU core
 | ||||||
|  |     Core_Timing,       ///< CoreTiming functions
 | ||||||
|  |     Core_Cheats,       ///< Cheat functions
 | ||||||
|  |     Config,            ///< Emulator configuration (including commandline)
 | ||||||
|  |     Debug,             ///< Debugging tools
 | ||||||
|  |     Debug_Emulated,    ///< Debug messages from the emulated programs
 | ||||||
|  |     Debug_GPU,         ///< GPU debugging tools
 | ||||||
|  |     Debug_Breakpoint,  ///< Logging breakpoints and watchpoints
 | ||||||
|  |     Debug_GDBStub,     ///< GDB Stub
 | ||||||
|  |     Kernel,            ///< The HLE implementation of the CTR kernel
 | ||||||
|  |     Kernel_SVC,        ///< Kernel system calls
 | ||||||
|  |     Applet,            ///< HLE implementation of system applets. Each applet
 | ||||||
|  |     ///< should have its own subclass.
 | ||||||
|  |     Applet_SWKBD, ///< The Software Keyboard applet
 | ||||||
|  |     Service,      ///< HLE implementation of system services. Each major service
 | ||||||
|  |     ///< should have its own subclass.
 | ||||||
|  |     Service_SRV,     ///< The SRV (Service Directory) implementation
 | ||||||
|  |     Service_FRD,     ///< The FRD (Friends) service
 | ||||||
|  |     Service_FS,      ///< The FS (Filesystem) service implementation
 | ||||||
|  |     Service_ERR,     ///< The ERR (Error) port implementation
 | ||||||
|  |     Service_APT,     ///< The APT (Applets) service
 | ||||||
|  |     Service_BOSS,    ///< The BOSS (SpotPass) service
 | ||||||
|  |     Service_GSP,     ///< The GSP (GPU control) service
 | ||||||
|  |     Service_AC,      ///< The AC (WiFi status) service
 | ||||||
|  |     Service_AM,      ///< The AM (Application manager) service
 | ||||||
|  |     Service_PTM,     ///< The PTM (Power status & misc.) service
 | ||||||
|  |     Service_LDR,     ///< The LDR (3ds dll loader) service
 | ||||||
|  |     Service_MIC,     ///< The MIC (Microphone) service
 | ||||||
|  |     Service_NDM,     ///< The NDM (Network daemon manager) service
 | ||||||
|  |     Service_NFC,     ///< The NFC service
 | ||||||
|  |     Service_NIM,     ///< The NIM (Network interface manager) service
 | ||||||
|  |     Service_NS,      ///< The NS (Nintendo User Interface Shell) service
 | ||||||
|  |     Service_NWM,     ///< The NWM (Network wlan manager) service
 | ||||||
|  |     Service_CAM,     ///< The CAM (Camera) service
 | ||||||
|  |     Service_CECD,    ///< The CECD (StreetPass) service
 | ||||||
|  |     Service_CFG,     ///< The CFG (Configuration) service
 | ||||||
|  |     Service_CSND,    ///< The CSND (CWAV format process) service
 | ||||||
|  |     Service_DSP,     ///< The DSP (DSP control) service
 | ||||||
|  |     Service_DLP,     ///< The DLP (Download Play) service
 | ||||||
|  |     Service_HID,     ///< The HID (Human interface device) service
 | ||||||
|  |     Service_HTTP,    ///< The HTTP service
 | ||||||
|  |     Service_SOC,     ///< The SOC (Socket) service
 | ||||||
|  |     Service_IR,      ///< The IR service
 | ||||||
|  |     Service_Y2R,     ///< The Y2R (YUV to RGB conversion) service
 | ||||||
|  |     Service_PS,      ///< The PS (Process) service
 | ||||||
|  |     Service_PLGLDR,  ///< The PLGLDR (plugin loader) service
 | ||||||
|  |     HW,              ///< Low-level hardware emulation
 | ||||||
|  |     HW_Memory,       ///< Memory-map and address translation
 | ||||||
|  |     HW_LCD,          ///< LCD register emulation
 | ||||||
|  |     HW_GPU,          ///< GPU control emulation
 | ||||||
|  |     HW_AES,          ///< AES engine emulation
 | ||||||
|  |     Frontend,        ///< Emulator UI
 | ||||||
|  |     Render,          ///< Emulator video output and hardware acceleration
 | ||||||
|  |     Render_Software, ///< Software renderer backend
 | ||||||
|  |     Render_OpenGL,   ///< OpenGL backend
 | ||||||
|  |     Render_Vulkan,   ///< Vulkan backend
 | ||||||
|  |     Audio,           ///< Audio emulation
 | ||||||
|  |     Audio_DSP,       ///< The HLE and LLE implementations of the DSP
 | ||||||
|  |     Audio_Sink,      ///< Emulator audio output backend
 | ||||||
|  |     Loader,          ///< ROM loader
 | ||||||
|  |     Input,           ///< Input emulation
 | ||||||
|  |     Network,         ///< Network emulation
 | ||||||
|  |     Movie,           ///< Movie (Input Recording) Playback
 | ||||||
|  |     WebService,      ///< Interface to Citra Web Services
 | ||||||
|  |     RPC_Server,      ///< RPC server
 | ||||||
|  |     Count,           ///< Total number of logging classes
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace Common::Log
 | ||||||
|  | @ -3,6 +3,7 @@ | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include <array> | #include <array> | ||||||
|  | #include <stdexcept> | ||||||
| #include <utility> | #include <utility> | ||||||
| #include <vector> | #include <vector> | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
|  |  | ||||||
|  | @ -5,18 +5,8 @@ | ||||||
| #include <string_view> | #include <string_view> | ||||||
| #include <utility> | #include <utility> | ||||||
| #include "audio_core/dsp_interface.h" | #include "audio_core/dsp_interface.h" | ||||||
|  | #include "common/file_util.h" | ||||||
| #include "common/settings.h" | #include "common/settings.h" | ||||||
| #include "core/core.h" |  | ||||||
| #include "core/gdbstub/gdbstub.h" |  | ||||||
| #include "core/hle/kernel/shared_page.h" |  | ||||||
| #include "core/hle/service/cam/cam.h" |  | ||||||
| #include "core/hle/service/hid/hid.h" |  | ||||||
| #include "core/hle/service/ir/ir_rst.h" |  | ||||||
| #include "core/hle/service/ir/ir_user.h" |  | ||||||
| #include "core/hle/service/mic_u.h" |  | ||||||
| #include "core/hle/service/plgldr/plgldr.h" |  | ||||||
| #include "video_core/renderer_base.h" |  | ||||||
| #include "video_core/video_core.h" |  | ||||||
| 
 | 
 | ||||||
| namespace Settings { | namespace Settings { | ||||||
| 
 | 
 | ||||||
|  | @ -72,64 +62,6 @@ std::string_view GetTextureFilterName(TextureFilter filter) { | ||||||
| Values values = {}; | Values values = {}; | ||||||
| static bool configuring_global = true; | static bool configuring_global = true; | ||||||
| 
 | 
 | ||||||
| void Apply() { |  | ||||||
|     GDBStub::SetServerPort(values.gdbstub_port.GetValue()); |  | ||||||
|     GDBStub::ToggleServer(values.use_gdbstub.GetValue()); |  | ||||||
| 
 |  | ||||||
|     VideoCore::g_shader_jit_enabled = values.use_shader_jit.GetValue(); |  | ||||||
|     VideoCore::g_hw_shader_enabled = values.use_hw_shader.GetValue(); |  | ||||||
|     VideoCore::g_hw_shader_accurate_mul = values.shaders_accurate_mul.GetValue(); |  | ||||||
| 
 |  | ||||||
| #ifndef ANDROID |  | ||||||
|     if (VideoCore::g_renderer) { |  | ||||||
|         VideoCore::g_renderer->UpdateCurrentFramebufferLayout(); |  | ||||||
|     } |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
|     if (VideoCore::g_renderer) { |  | ||||||
|         auto& settings = VideoCore::g_renderer->Settings(); |  | ||||||
|         settings.bg_color_update_requested = true; |  | ||||||
|         settings.sampler_update_requested = true; |  | ||||||
|         settings.shader_update_requested = true; |  | ||||||
|         settings.texture_filter_update_requested = true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     auto& system = Core::System::GetInstance(); |  | ||||||
|     if (system.IsPoweredOn()) { |  | ||||||
|         system.CoreTiming().UpdateClockSpeed(values.cpu_clock_percentage.GetValue()); |  | ||||||
|         Core::DSP().SetSink(values.output_type.GetValue(), values.output_device.GetValue()); |  | ||||||
|         Core::DSP().EnableStretching(values.enable_audio_stretching.GetValue()); |  | ||||||
| 
 |  | ||||||
|         auto hid = Service::HID::GetModule(system); |  | ||||||
|         if (hid) { |  | ||||||
|             hid->ReloadInputDevices(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         auto apt = Service::APT::GetModule(system); |  | ||||||
|         if (apt) { |  | ||||||
|             apt->GetAppletManager()->ReloadInputDevices(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         auto sm = system.ServiceManager(); |  | ||||||
|         auto ir_user = sm.GetService<Service::IR::IR_USER>("ir:USER"); |  | ||||||
|         if (ir_user) |  | ||||||
|             ir_user->ReloadInputDevices(); |  | ||||||
|         auto ir_rst = sm.GetService<Service::IR::IR_RST>("ir:rst"); |  | ||||||
|         if (ir_rst) |  | ||||||
|             ir_rst->ReloadInputDevices(); |  | ||||||
| 
 |  | ||||||
|         auto cam = Service::CAM::GetModule(system); |  | ||||||
|         if (cam) { |  | ||||||
|             cam->ReloadCameraDevices(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         Service::MIC::ReloadMic(system); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     Service::PLGLDR::PLG_LDR::SetEnabled(values.plugin_loader_enabled.GetValue()); |  | ||||||
|     Service::PLGLDR::PLG_LDR::SetAllowGameChangeState(values.allow_plugin_loader.GetValue()); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void LogSettings() { | void LogSettings() { | ||||||
|     const auto log_setting = [](std::string_view name, const auto& value) { |     const auto log_setting = [](std::string_view name, const auto& value) { | ||||||
|         LOG_INFO(Config, "{}: {}", name, value); |         LOG_INFO(Config, "{}: {}", name, value); | ||||||
|  |  | ||||||
|  | @ -525,7 +525,6 @@ void SetConfiguringGlobal(bool is_global); | ||||||
| 
 | 
 | ||||||
| float Volume(); | float Volume(); | ||||||
| 
 | 
 | ||||||
| void Apply(); |  | ||||||
| void LogSettings(); | void LogSettings(); | ||||||
| 
 | 
 | ||||||
| // Restore the global state of all applicable settings in the Values struct
 | // Restore the global state of all applicable settings in the Values struct
 | ||||||
|  |  | ||||||
|  | @ -13,8 +13,10 @@ | ||||||
| #include <mutex> | #include <mutex> | ||||||
| #include <utility> | #include <utility> | ||||||
| 
 | 
 | ||||||
|  | #include "common/polyfill_thread.h" | ||||||
|  | 
 | ||||||
| namespace Common { | namespace Common { | ||||||
| template <typename T> | template <typename T, bool with_stop_token = false> | ||||||
| class SPSCQueue { | class SPSCQueue { | ||||||
| public: | public: | ||||||
|     SPSCQueue() { |     SPSCQueue() { | ||||||
|  | @ -46,15 +48,13 @@ public: | ||||||
|         ElementPtr* new_ptr = new ElementPtr(); |         ElementPtr* new_ptr = new ElementPtr(); | ||||||
|         write_ptr->next.store(new_ptr, std::memory_order_release); |         write_ptr->next.store(new_ptr, std::memory_order_release); | ||||||
|         write_ptr = new_ptr; |         write_ptr = new_ptr; | ||||||
|  |         ++size; | ||||||
| 
 | 
 | ||||||
|         const size_t previous_size{size++}; |         // cv_mutex must be held or else there will be a missed wakeup if the other thread is in the
 | ||||||
| 
 |         // line before cv.wait
 | ||||||
|         // Acquire the mutex and then immediately release it as a fence.
 |  | ||||||
|         // TODO(bunnei): This can be replaced with C++20 waitable atomics when properly supported.
 |         // TODO(bunnei): This can be replaced with C++20 waitable atomics when properly supported.
 | ||||||
|         // See discussion on https://github.com/yuzu-emu/yuzu/pull/3173 for details.
 |         // See discussion on https://github.com/yuzu-emu/yuzu/pull/3173 for details.
 | ||||||
|         if (previous_size == 0) { |         std::scoped_lock lock{cv_mutex}; | ||||||
|             std::lock_guard lock{cv_mutex}; |  | ||||||
|         } |  | ||||||
|         cv.notify_one(); |         cv.notify_one(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -93,6 +93,19 @@ public: | ||||||
|         return t; |         return t; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     T PopWait(std::stop_token stop_token) { | ||||||
|  |         if (Empty()) { | ||||||
|  |             std::unique_lock lock{cv_mutex}; | ||||||
|  |             CondvarWait(cv, lock, stop_token, [this] { return !Empty(); }); | ||||||
|  |         } | ||||||
|  |         if (stop_token.stop_requested()) { | ||||||
|  |             return T{}; | ||||||
|  |         } | ||||||
|  |         T t; | ||||||
|  |         Pop(t); | ||||||
|  |         return t; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     // not thread-safe
 |     // not thread-safe
 | ||||||
|     void Clear() { |     void Clear() { | ||||||
|         size.store(0); |         size.store(0); | ||||||
|  | @ -121,13 +134,13 @@ private: | ||||||
|     ElementPtr* read_ptr; |     ElementPtr* read_ptr; | ||||||
|     std::atomic_size_t size{0}; |     std::atomic_size_t size{0}; | ||||||
|     std::mutex cv_mutex; |     std::mutex cv_mutex; | ||||||
|     std::condition_variable cv; |     std::conditional_t<with_stop_token, std::condition_variable_any, std::condition_variable> cv; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // a simple thread-safe,
 | // a simple thread-safe,
 | ||||||
| // single reader, multiple writer queue
 | // single reader, multiple writer queue
 | ||||||
| 
 | 
 | ||||||
| template <typename T> | template <typename T, bool with_stop_token = false> | ||||||
| class MPSCQueue { | class MPSCQueue { | ||||||
| public: | public: | ||||||
|     [[nodiscard]] std::size_t Size() const { |     [[nodiscard]] std::size_t Size() const { | ||||||
|  | @ -144,7 +157,7 @@ public: | ||||||
| 
 | 
 | ||||||
|     template <typename Arg> |     template <typename Arg> | ||||||
|     void Push(Arg&& t) { |     void Push(Arg&& t) { | ||||||
|         std::lock_guard lock{write_lock}; |         std::scoped_lock lock{write_lock}; | ||||||
|         spsc_queue.Push(t); |         spsc_queue.Push(t); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -160,13 +173,17 @@ public: | ||||||
|         return spsc_queue.PopWait(); |         return spsc_queue.PopWait(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     T PopWait(std::stop_token stop_token) { | ||||||
|  |         return spsc_queue.PopWait(stop_token); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     // not thread-safe
 |     // not thread-safe
 | ||||||
|     void Clear() { |     void Clear() { | ||||||
|         spsc_queue.Clear(); |         spsc_queue.Clear(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     SPSCQueue<T> spsc_queue; |     SPSCQueue<T, with_stop_token> spsc_queue; | ||||||
|     std::mutex write_lock; |     std::mutex write_lock; | ||||||
| }; | }; | ||||||
| } // namespace Common
 | } // namespace Common
 | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ | ||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
|  | #include <exception> | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <stdexcept> | #include <stdexcept> | ||||||
| #include <utility> | #include <utility> | ||||||
|  | @ -12,9 +13,9 @@ | ||||||
| #include "common/arch.h" | #include "common/arch.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "common/settings.h" | #include "common/settings.h" | ||||||
| #include "common/texture.h" |  | ||||||
| #include "core/arm/arm_interface.h" | #include "core/arm/arm_interface.h" | ||||||
| #include "core/arm/exclusive_monitor.h" | #include "core/arm/exclusive_monitor.h" | ||||||
|  | #include "core/hle/service/cam/cam.h" | ||||||
| #if CITRA_ARCH(x86_64) || CITRA_ARCH(arm64) | #if CITRA_ARCH(x86_64) || CITRA_ARCH(arm64) | ||||||
| #include "core/arm/dynarmic/arm_dynarmic.h" | #include "core/arm/dynarmic/arm_dynarmic.h" | ||||||
| #endif | #endif | ||||||
|  | @ -23,19 +24,22 @@ | ||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
| #include "core/core_timing.h" | #include "core/core_timing.h" | ||||||
| #include "core/dumping/backend.h" | #include "core/dumping/backend.h" | ||||||
| #include "core/dumping/ffmpeg_backend.h" |  | ||||||
| #include "core/frontend/image_interface.h" | #include "core/frontend/image_interface.h" | ||||||
| #include "core/gdbstub/gdbstub.h" | #include "core/gdbstub/gdbstub.h" | ||||||
| #include "core/global.h" | #include "core/global.h" | ||||||
| #include "core/hle/kernel/client_port.h" |  | ||||||
| #include "core/hle/kernel/kernel.h" | #include "core/hle/kernel/kernel.h" | ||||||
| #include "core/hle/kernel/process.h" | #include "core/hle/kernel/process.h" | ||||||
| #include "core/hle/kernel/thread.h" | #include "core/hle/kernel/thread.h" | ||||||
| #include "core/hle/service/apt/applet_manager.h" | #include "core/hle/service/apt/applet_manager.h" | ||||||
| #include "core/hle/service/apt/apt.h" | #include "core/hle/service/apt/apt.h" | ||||||
|  | #include "core/hle/service/cam/cam.h" | ||||||
| #include "core/hle/service/fs/archive.h" | #include "core/hle/service/fs/archive.h" | ||||||
| #include "core/hle/service/gsp/gsp.h" | #include "core/hle/service/gsp/gsp.h" | ||||||
| #include "core/hle/service/pm/pm_app.h" | #include "core/hle/service/hid/hid.h" | ||||||
|  | #include "core/hle/service/ir/ir_rst.h" | ||||||
|  | #include "core/hle/service/ir/ir_user.h" | ||||||
|  | #include "core/hle/service/mic_u.h" | ||||||
|  | #include "core/hle/service/plgldr/plgldr.h" | ||||||
| #include "core/hle/service/service.h" | #include "core/hle/service/service.h" | ||||||
| #include "core/hle/service/sm/sm.h" | #include "core/hle/service/sm/sm.h" | ||||||
| #include "core/hw/gpu.h" | #include "core/hw/gpu.h" | ||||||
|  | @ -596,6 +600,64 @@ void System::Reset() { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void System::ApplySettings() { | ||||||
|  |     GDBStub::SetServerPort(Settings::values.gdbstub_port.GetValue()); | ||||||
|  |     GDBStub::ToggleServer(Settings::values.use_gdbstub.GetValue()); | ||||||
|  | 
 | ||||||
|  |     VideoCore::g_shader_jit_enabled = Settings::values.use_shader_jit.GetValue(); | ||||||
|  |     VideoCore::g_hw_shader_enabled = Settings::values.use_hw_shader.GetValue(); | ||||||
|  |     VideoCore::g_hw_shader_accurate_mul = Settings::values.shaders_accurate_mul.GetValue(); | ||||||
|  | 
 | ||||||
|  | #ifndef ANDROID | ||||||
|  |     if (VideoCore::g_renderer) { | ||||||
|  |         VideoCore::g_renderer->UpdateCurrentFramebufferLayout(); | ||||||
|  |     } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |     if (VideoCore::g_renderer) { | ||||||
|  |         auto& settings = VideoCore::g_renderer->Settings(); | ||||||
|  |         settings.bg_color_update_requested = true; | ||||||
|  |         settings.sampler_update_requested = true; | ||||||
|  |         settings.shader_update_requested = true; | ||||||
|  |         settings.texture_filter_update_requested = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (IsPoweredOn()) { | ||||||
|  |         CoreTiming().UpdateClockSpeed(Settings::values.cpu_clock_percentage.GetValue()); | ||||||
|  |         Core::DSP().SetSink(Settings::values.output_type.GetValue(), | ||||||
|  |                             Settings::values.output_device.GetValue()); | ||||||
|  |         Core::DSP().EnableStretching(Settings::values.enable_audio_stretching.GetValue()); | ||||||
|  | 
 | ||||||
|  |         auto hid = Service::HID::GetModule(*this); | ||||||
|  |         if (hid) { | ||||||
|  |             hid->ReloadInputDevices(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         auto apt = Service::APT::GetModule(*this); | ||||||
|  |         if (apt) { | ||||||
|  |             apt->GetAppletManager()->ReloadInputDevices(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         auto ir_user = service_manager->GetService<Service::IR::IR_USER>("ir:USER"); | ||||||
|  |         if (ir_user) | ||||||
|  |             ir_user->ReloadInputDevices(); | ||||||
|  |         auto ir_rst = service_manager->GetService<Service::IR::IR_RST>("ir:rst"); | ||||||
|  |         if (ir_rst) | ||||||
|  |             ir_rst->ReloadInputDevices(); | ||||||
|  | 
 | ||||||
|  |         auto cam = Service::CAM::GetModule(*this); | ||||||
|  |         if (cam) { | ||||||
|  |             cam->ReloadCameraDevices(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         Service::MIC::ReloadMic(*this); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     Service::PLGLDR::PLG_LDR::SetEnabled(Settings::values.plugin_loader_enabled.GetValue()); | ||||||
|  |     Service::PLGLDR::PLG_LDR::SetAllowGameChangeState( | ||||||
|  |         Settings::values.allow_plugin_loader.GetValue()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| template <class Archive> | template <class Archive> | ||||||
| void System::serialize(Archive& ar, const unsigned int file_version) { | void System::serialize(Archive& ar, const unsigned int file_version) { | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -330,6 +330,9 @@ public: | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /// Applies any changes to settings to this core instance.
 | ||||||
|  |     void ApplySettings(); | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     /**
 |     /**
 | ||||||
|      * Initialize the emulated system. |      * Initialize the emulated system. | ||||||
|  |  | ||||||
|  | @ -203,7 +203,7 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) { | ||||||
| 
 | 
 | ||||||
|     status = ProcessStatus::Running; |     status = ProcessStatus::Running; | ||||||
| 
 | 
 | ||||||
|     vm_manager.LogLayout(Log::Level::Debug); |     vm_manager.LogLayout(Common::Log::Level::Debug); | ||||||
|     Kernel::SetupMainThread(kernel, codeset->entrypoint, main_thread_priority, SharedFrom(this)); |     Kernel::SetupMainThread(kernel, codeset->entrypoint, main_thread_priority, SharedFrom(this)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -529,7 +529,7 @@ ResultCode SVC::ControlMemory(u32* out_addr, u32 addr0, u32 addr1, u32 size, u32 | ||||||
|         return ERR_INVALID_COMBINATION; |         return ERR_INVALID_COMBINATION; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     process.vm_manager.LogLayout(Log::Level::Trace); |     process.vm_manager.LogLayout(Common::Log::Level::Trace); | ||||||
| 
 | 
 | ||||||
|     return RESULT_SUCCESS; |     return RESULT_SUCCESS; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -239,10 +239,10 @@ ResultCode VMManager::ReprotectRange(VAddr target, u32 size, VMAPermission new_p | ||||||
|     return RESULT_SUCCESS; |     return RESULT_SUCCESS; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void VMManager::LogLayout(Log::Level log_level) const { | void VMManager::LogLayout(Common::Log::Level log_level) const { | ||||||
|     for (const auto& p : vma_map) { |     for (const auto& p : vma_map) { | ||||||
|         const VirtualMemoryArea& vma = p.second; |         const VirtualMemoryArea& vma = p.second; | ||||||
|         LOG_GENERIC(::Log::Class::Kernel, log_level, "{:08X} - {:08X}  size: {:8X} {}{}{} {}", |         LOG_GENERIC(Common::Log::Class::Kernel, log_level, "{:08X} - {:08X}  size: {:8X} {}{}{} {}", | ||||||
|                     vma.base, vma.base + vma.size, vma.size, |                     vma.base, vma.base + vma.size, vma.size, | ||||||
|                     (u8)vma.permissions & (u8)VMAPermission::Read ? 'R' : '-', |                     (u8)vma.permissions & (u8)VMAPermission::Read ? 'R' : '-', | ||||||
|                     (u8)vma.permissions & (u8)VMAPermission::Write ? 'W' : '-', |                     (u8)vma.permissions & (u8)VMAPermission::Write ? 'W' : '-', | ||||||
|  |  | ||||||
|  | @ -202,7 +202,7 @@ public: | ||||||
|     ResultCode ReprotectRange(VAddr target, u32 size, VMAPermission new_perms); |     ResultCode ReprotectRange(VAddr target, u32 size, VMAPermission new_perms); | ||||||
| 
 | 
 | ||||||
|     /// Dumps the address space layout to the log, for debugging
 |     /// Dumps the address space layout to the log, for debugging
 | ||||||
|     void LogLayout(Log::Level log_level) const; |     void LogLayout(Common::Log::Level log_level) const; | ||||||
| 
 | 
 | ||||||
|     /// Gets a list of backing memory blocks for the specified range
 |     /// Gets a list of backing memory blocks for the specified range
 | ||||||
|     ResultVal<std::vector<std::pair<MemoryRef, u32>>> GetBackingBlocksForRange(VAddr address, |     ResultVal<std::vector<std::pair<MemoryRef, u32>>> GetBackingBlocksForRange(VAddr address, | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ | ||||||
| #include <boost/serialization/unique_ptr.hpp> | #include <boost/serialization/unique_ptr.hpp> | ||||||
| #include <cryptopp/osrng.h> | #include <cryptopp/osrng.h> | ||||||
| #include <cryptopp/sha.h> | #include <cryptopp/sha.h> | ||||||
|  | #include <fmt/format.h> | ||||||
| #include "common/archives.h" | #include "common/archives.h" | ||||||
| #include "common/file_util.h" | #include "common/file_util.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ | ||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
|  | #include <fmt/format.h> | ||||||
| #include "common/alignment.h" | #include "common/alignment.h" | ||||||
| #include "common/settings.h" | #include "common/settings.h" | ||||||
| #include "common/string_util.h" | #include "common/string_util.h" | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ | ||||||
| #include <boost/serialization/base_object.hpp> | #include <boost/serialization/base_object.hpp> | ||||||
| #include <boost/serialization/shared_ptr.hpp> | #include <boost/serialization/shared_ptr.hpp> | ||||||
| #include <boost/serialization/unique_ptr.hpp> | #include <boost/serialization/unique_ptr.hpp> | ||||||
|  | #include <fmt/format.h> | ||||||
| #include "common/string_util.h" | #include "common/string_util.h" | ||||||
| #include "common/swap.h" | #include "common/swap.h" | ||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
|  |  | ||||||
|  | @ -10,6 +10,7 @@ | ||||||
| #include <boost/optional.hpp> | #include <boost/optional.hpp> | ||||||
| #include <cryptopp/hex.h> | #include <cryptopp/hex.h> | ||||||
| #include <cryptopp/osrng.h> | #include <cryptopp/osrng.h> | ||||||
|  | #include <fmt/format.h> | ||||||
| #include "common/bit_field.h" | #include "common/bit_field.h" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/file_util.h" | #include "common/file_util.h" | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ | ||||||
| 
 | 
 | ||||||
| #include <chrono> | #include <chrono> | ||||||
| #include <cryptopp/hex.h> | #include <cryptopp/hex.h> | ||||||
|  | #include <fmt/format.h> | ||||||
| #include "common/archives.h" | #include "common/archives.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "common/scm_rev.h" | #include "common/scm_rev.h" | ||||||
|  |  | ||||||
|  | @ -150,15 +150,8 @@ static void SaveBanList(const Network::Room::BanList& ban_list, const std::strin | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void InitializeLogging(const std::string& log_file) { | static void InitializeLogging(const std::string& log_file) { | ||||||
|     Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); |     Common::Log::Initialize(log_file); | ||||||
| 
 |     Common::Log::SetColorConsoleBackendEnabled(true); | ||||||
|     const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); |  | ||||||
|     FileUtil::CreateFullPath(log_dir); |  | ||||||
|     Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + log_file)); |  | ||||||
| 
 |  | ||||||
| #ifdef _WIN32 |  | ||||||
|     Log::AddBackend(std::make_unique<Log::DebuggerBackend>()); |  | ||||||
| #endif |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Application entry point
 | /// Application entry point
 | ||||||
|  |  | ||||||
|  | @ -4,11 +4,13 @@ | ||||||
| 
 | 
 | ||||||
| #include <cmath> | #include <cmath> | ||||||
| #include <catch2/catch_test_macros.hpp> | #include <catch2/catch_test_macros.hpp> | ||||||
|  | #include "common/logging/backend.h" | ||||||
| #include "common/param_package.h" | #include "common/param_package.h" | ||||||
| 
 | 
 | ||||||
| namespace Common { | namespace Common { | ||||||
| 
 | 
 | ||||||
| TEST_CASE("ParamPackage", "[common]") { | TEST_CASE("ParamPackage", "[common]") { | ||||||
|  |     Common::Log::DisableLoggingInTests(); | ||||||
|     ParamPackage original{ |     ParamPackage original{ | ||||||
|         {"abc", "xyz"}, |         {"abc", "xyz"}, | ||||||
|         {"def", "42"}, |         {"def", "42"}, | ||||||
|  |  | ||||||
|  | @ -56,22 +56,22 @@ inline std::string_view GetType(GLenum type) { | ||||||
| 
 | 
 | ||||||
| static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severity, | static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severity, | ||||||
|                                   GLsizei length, const GLchar* message, const void* user_param) { |                                   GLsizei length, const GLchar* message, const void* user_param) { | ||||||
|     Log::Level level = Log::Level::Info; |     auto level = Common::Log::Level::Info; | ||||||
|     switch (severity) { |     switch (severity) { | ||||||
|     case GL_DEBUG_SEVERITY_HIGH: |     case GL_DEBUG_SEVERITY_HIGH: | ||||||
|         level = Log::Level::Critical; |         level = Common::Log::Level::Critical; | ||||||
|         break; |         break; | ||||||
|     case GL_DEBUG_SEVERITY_MEDIUM: |     case GL_DEBUG_SEVERITY_MEDIUM: | ||||||
|         level = Log::Level::Warning; |         level = Common::Log::Level::Warning; | ||||||
|         break; |         break; | ||||||
|     case GL_DEBUG_SEVERITY_NOTIFICATION: |     case GL_DEBUG_SEVERITY_NOTIFICATION: | ||||||
|     case GL_DEBUG_SEVERITY_LOW: |     case GL_DEBUG_SEVERITY_LOW: | ||||||
|         level = Log::Level::Debug; |         level = Common::Log::Level::Debug; | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     LOG_GENERIC(Log::Class::Render_OpenGL, level, "{} {} {}: {}", GetSource(source), GetType(type), |     LOG_GENERIC(Common::Log::Class::Render_OpenGL, level, "{} {} {}: {}", GetSource(source), | ||||||
|                 id, message); |                 GetType(type), id, message); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Driver::Driver(Core::TelemetrySession& telemetry_session_) | Driver::Driver(Core::TelemetrySession& telemetry_session_) | ||||||
|  |  | ||||||
|  | @ -38,23 +38,23 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL DebugUtilsCallback( | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     Log::Level level{}; |     Common::Log::Level level{}; | ||||||
|     switch (severity) { |     switch (severity) { | ||||||
|     case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: |     case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: | ||||||
|         level = Log::Level::Error; |         level = Common::Log::Level::Error; | ||||||
|         break; |         break; | ||||||
|     case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: |     case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: | ||||||
|         level = Log::Level::Info; |         level = Common::Log::Level::Info; | ||||||
|         break; |         break; | ||||||
|     case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: |     case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: | ||||||
|     case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: |     case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: | ||||||
|         level = Log::Level::Debug; |         level = Common::Log::Level::Debug; | ||||||
|         break; |         break; | ||||||
|     default: |     default: | ||||||
|         level = Log::Level::Info; |         level = Common::Log::Level::Info; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     LOG_GENERIC(Log::Class::Render_Vulkan, level, "{}: {}", |     LOG_GENERIC(Common::Log::Class::Render_Vulkan, level, "{}: {}", | ||||||
|                 callback_data->pMessageIdName ? callback_data->pMessageIdName : "<null>", |                 callback_data->pMessageIdName ? callback_data->pMessageIdName : "<null>", | ||||||
|                 callback_data->pMessage ? callback_data->pMessage : "<null>"); |                 callback_data->pMessage ? callback_data->pMessage : "<null>"); | ||||||
| 
 | 
 | ||||||
|  | @ -69,25 +69,25 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL DebugReportCallback(VkDebugReportFlagsEXT | ||||||
|                                                           const char* pMessage, void* pUserData) { |                                                           const char* pMessage, void* pUserData) { | ||||||
| 
 | 
 | ||||||
|     const VkDebugReportFlagBitsEXT severity = static_cast<VkDebugReportFlagBitsEXT>(flags); |     const VkDebugReportFlagBitsEXT severity = static_cast<VkDebugReportFlagBitsEXT>(flags); | ||||||
|     Log::Level level{}; |     Common::Log::Level level{}; | ||||||
|     switch (severity) { |     switch (severity) { | ||||||
|     case VK_DEBUG_REPORT_ERROR_BIT_EXT: |     case VK_DEBUG_REPORT_ERROR_BIT_EXT: | ||||||
|         level = Log::Level::Error; |         level = Common::Log::Level::Error; | ||||||
|         break; |         break; | ||||||
|     case VK_DEBUG_REPORT_INFORMATION_BIT_EXT: |     case VK_DEBUG_REPORT_INFORMATION_BIT_EXT: | ||||||
|         level = Log::Level::Warning; |         level = Common::Log::Level::Warning; | ||||||
|         break; |         break; | ||||||
|     case VK_DEBUG_REPORT_DEBUG_BIT_EXT: |     case VK_DEBUG_REPORT_DEBUG_BIT_EXT: | ||||||
|     case VK_DEBUG_REPORT_WARNING_BIT_EXT: |     case VK_DEBUG_REPORT_WARNING_BIT_EXT: | ||||||
|     case VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT: |     case VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT: | ||||||
|         level = Log::Level::Debug; |         level = Common::Log::Level::Debug; | ||||||
|         break; |         break; | ||||||
|     default: |     default: | ||||||
|         level = Log::Level::Info; |         level = Common::Log::Level::Info; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const vk::DebugReportObjectTypeEXT type = static_cast<vk::DebugReportObjectTypeEXT>(objectType); |     const vk::DebugReportObjectTypeEXT type = static_cast<vk::DebugReportObjectTypeEXT>(objectType); | ||||||
|     LOG_GENERIC(Log::Class::Render_Vulkan, level, |     LOG_GENERIC(Common::Log::Class::Render_Vulkan, level, | ||||||
|                 "type = {}, object = {} | MessageCode = {:#x}, LayerPrefix = {} | {}", |                 "type = {}, object = {} | MessageCode = {:#x}, LayerPrefix = {} | {}", | ||||||
|                 vk::to_string(type), object, messageCode, pLayerPrefix, pMessage); |                 vk::to_string(type), object, messageCode, pLayerPrefix, pMessage); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue