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 <unordered_map> | ||||
| #include <inih/cpp/INIReader.h> | ||||
| 
 | ||||
| #include "common/file_util.h" | ||||
| #include "common/logging/backend.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "common/param_package.h" | ||||
| #include "common/settings.h" | ||||
|  | @ -260,6 +260,12 @@ void Config::ReadValues() { | |||
|     // Miscellaneous
 | ||||
|     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
 | ||||
|     Settings::values.record_frame_times = | ||||
|         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) | ||||
|         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
 | ||||
|     s_savestate_info_class = reinterpret_cast<jclass>( | ||||
|         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); | ||||
|         GameSettings::LoadOverrides(program_id); | ||||
|     } | ||||
|     Settings::Apply(); | ||||
|     system.ApplySettings(); | ||||
|     Settings::LogSettings(); | ||||
| 
 | ||||
|     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, | ||||
|                                                            [[maybe_unused]] jclass clazz) { | ||||
|     Log::RemoveBackend(Log::FileBackend::Name()); | ||||
|     FileUtil::CreateFullPath(FileUtil::GetUserPath(FileUtil::UserPath::LogDir)); | ||||
|     Log::AddBackend(std::make_unique<Log::FileBackend>( | ||||
|         FileUtil::GetUserPath(FileUtil::UserPath::LogDir) + LOG_FILE)); | ||||
|     Common::Log::Initialize(); | ||||
|     Common::Log::Start(); | ||||
|     LOG_INFO(Frontend, "Logging backend initialised"); | ||||
| } | ||||
| 
 | ||||
|  | @ -474,7 +472,7 @@ void Java_org_citra_citra_1emu_NativeLibrary_ReloadSettings(JNIEnv* env, | |||
|         GameSettings::LoadOverrides(program_id); | ||||
|     } | ||||
| 
 | ||||
|     Settings::Apply(); | ||||
|     system.ApplySettings(); | ||||
| } | ||||
| 
 | ||||
| jstring Java_org_citra_citra_1emu_NativeLibrary_GetUserSetting(JNIEnv* env, | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ | |||
| // Refer to the license.txt file included.
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <memory> | ||||
| #include <optional> | ||||
| #include <string> | ||||
| #include <tuple> | ||||
|  |  | |||
|  | @ -174,23 +174,11 @@ static void OnStatusMessageReceived(const Network::StatusMessageEntry& msg) { | |||
|         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
 | ||||
| int main(int argc, char** argv) { | ||||
|     Common::Log::Initialize(); | ||||
|     Common::Log::SetColorConsoleBackendEnabled(true); | ||||
|     Common::Log::Start(); | ||||
|     Common::DetachedTasks detached_tasks; | ||||
|     Config config; | ||||
|     int option_index = 0; | ||||
|  | @ -201,8 +189,6 @@ int main(int argc, char** argv) { | |||
|     std::string movie_play; | ||||
|     std::string dump_video; | ||||
| 
 | ||||
|     InitializeLogging(); | ||||
| 
 | ||||
|     char* endarg; | ||||
| #ifdef _WIN32 | ||||
|     int argc_w; | ||||
|  | @ -357,7 +343,7 @@ int main(int argc, char** argv) { | |||
|     // Apply the command line arguments
 | ||||
|     Settings::values.gdbstub_port = gdb_port; | ||||
|     Settings::values.use_gdbstub = use_gdbstub; | ||||
|     Settings::Apply(); | ||||
|     system.ApplySettings(); | ||||
| 
 | ||||
|     // Register frontend applets
 | ||||
|     Frontend::RegisterDefaultApplets(); | ||||
|  |  | |||
|  | @ -11,8 +11,8 @@ | |||
| #include "citra/config.h" | ||||
| #include "citra/default_ini.h" | ||||
| #include "common/file_util.h" | ||||
| #include "common/logging/backend.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "common/param_package.h" | ||||
| #include "common/settings.h" | ||||
| #include "core/hle/service/service.h" | ||||
| #include "input_common/main.h" | ||||
|  | @ -299,6 +299,12 @@ void Config::ReadValues() { | |||
|     // Miscellaneous
 | ||||
|     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
 | ||||
|     Settings::values.record_frame_times = | ||||
|         sdl2_config->GetBoolean("Debugging", "record_frame_times", false); | ||||
|  |  | |||
|  | @ -9,7 +9,7 @@ | |||
| #include "citra_qt/debugger/console.h" | ||||
| #include "citra_qt/uisettings.h" | ||||
| #include "common/file_util.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "common/logging/backend.h" | ||||
| #include "common/settings.h" | ||||
| #include "core/core.h" | ||||
| #include "ui_configure_debug.h" | ||||
|  | @ -89,9 +89,9 @@ void ConfigureDebug::ApplyConfiguration() { | |||
|     UISettings::values.show_console = ui->toggle_console->isChecked(); | ||||
|     Settings::values.log_filter = ui->log_filter_edit->text().toStdString(); | ||||
|     Debugger::ToggleConsole(); | ||||
|     Log::Filter filter; | ||||
|     Common::Log::Filter filter; | ||||
|     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.renderer_debug = ui->toggle_renderer_debug->isChecked(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -8,10 +8,13 @@ | |||
| #include "citra_qt/configuration/configure_dialog.h" | ||||
| #include "citra_qt/hotkeys.h" | ||||
| #include "common/settings.h" | ||||
| #include "core/core.h" | ||||
| #include "ui_configure.h" | ||||
| 
 | ||||
| ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, bool enable_web_config) | ||||
|     : QDialog(parent), ui(std::make_unique<Ui::ConfigureDialog>()), registry(registry) { | ||||
| ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, Core::System& system_, | ||||
|                                  bool enable_web_config) | ||||
|     : QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()}, registry{registry_}, | ||||
|       system{system_} { | ||||
|     Settings::SetConfiguringGlobal(true); | ||||
| 
 | ||||
|     ui->setupUi(this); | ||||
|  | @ -68,7 +71,7 @@ void ConfigureDialog::ApplyConfiguration() { | |||
|     ui->webTab->ApplyConfiguration(); | ||||
|     ui->uiTab->ApplyConfiguration(); | ||||
|     ui->storageTab->ApplyConfiguration(); | ||||
|     Settings::Apply(); | ||||
|     system.ApplySettings(); | ||||
|     Settings::LogSettings(); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -13,11 +13,15 @@ namespace Ui { | |||
| class ConfigureDialog; | ||||
| } | ||||
| 
 | ||||
| namespace Core { | ||||
| class System; | ||||
| } | ||||
| 
 | ||||
| class ConfigureDialog : public QDialog { | ||||
|     Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|     explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, | ||||
|     explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, Core::System& system, | ||||
|                              bool enable_web_config = true); | ||||
|     ~ConfigureDialog() override; | ||||
| 
 | ||||
|  | @ -37,4 +41,5 @@ private: | |||
| 
 | ||||
|     std::unique_ptr<Ui::ConfigureDialog> ui; | ||||
|     HotkeyRegistry& registry; | ||||
|     Core::System& system; | ||||
| }; | ||||
|  |  | |||
|  | @ -102,7 +102,7 @@ void ConfigurePerGame::ApplyConfiguration() { | |||
|     audio_tab->ApplyConfiguration(); | ||||
|     debug_tab->ApplyConfiguration(); | ||||
| 
 | ||||
|     Settings::Apply(); | ||||
|     system.ApplySettings(); | ||||
|     Settings::LogSettings(); | ||||
| 
 | ||||
|     game_config->Save(); | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ void ToggleConsole() { | |||
|         console_shown = UISettings::values.show_console.GetValue(); | ||||
|     } | ||||
| 
 | ||||
|     using namespace Common::Log; | ||||
| #ifdef _WIN32 | ||||
|     FILE* temp; | ||||
|     if (UISettings::values.show_console) { | ||||
|  | @ -29,24 +30,20 @@ void ToggleConsole() { | |||
|             freopen_s(&temp, "CONIN$", "r", stdin); | ||||
|             freopen_s(&temp, "CONOUT$", "w", stdout); | ||||
|             freopen_s(&temp, "CONOUT$", "w", stderr); | ||||
|             Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); | ||||
|             SetColorConsoleBackendEnabled(true); | ||||
|         } | ||||
|     } else { | ||||
|         if (FreeConsole()) { | ||||
|             // 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
 | ||||
|             Log::RemoveBackend(Log::ColorConsoleBackend::Name()); | ||||
|             SetColorConsoleBackendEnabled(false); | ||||
|             freopen_s(&temp, "NUL", "r", stdin); | ||||
|             freopen_s(&temp, "NUL", "w", stdout); | ||||
|             freopen_s(&temp, "NUL", "w", stderr); | ||||
|         } | ||||
|     } | ||||
| #else | ||||
|     if (UISettings::values.show_console) { | ||||
|         Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); | ||||
|     } else { | ||||
|         Log::RemoveBackend(Log::ColorConsoleBackend::Name()); | ||||
|     } | ||||
|     SetColorConsoleBackendEnabled(UISettings::values.show_console.GetValue()); | ||||
| #endif | ||||
| } | ||||
| } // namespace Debugger
 | ||||
|  |  | |||
|  | @ -8,10 +8,11 @@ | |||
| #include "citra_qt/dumping/options_dialog.h" | ||||
| #include "citra_qt/uisettings.h" | ||||
| #include "common/settings.h" | ||||
| #include "core/core.h" | ||||
| #include "ui_dumping_dialog.h" | ||||
| 
 | ||||
| DumpingDialog::DumpingDialog(QWidget* parent) | ||||
|     : QDialog(parent), ui(std::make_unique<Ui::DumpingDialog>()) { | ||||
| DumpingDialog::DumpingDialog(QWidget* parent, Core::System& system_) | ||||
|     : QDialog(parent), ui{std::make_unique<Ui::DumpingDialog>()}, system{system_} { | ||||
| 
 | ||||
|     ui->setupUi(this); | ||||
| 
 | ||||
|  | @ -216,5 +217,5 @@ void DumpingDialog::ApplyConfiguration() { | |||
|     Settings::values.audio_encoder_options = ui->audioEncoderOptionsLineEdit->text().toStdString(); | ||||
|     Settings::values.audio_bitrate = ui->audioBitrateSpinBox->value(); | ||||
|     UISettings::values.video_dumping_path = last_path; | ||||
|     Settings::Apply(); | ||||
|     system.ApplySettings(); | ||||
| } | ||||
|  |  | |||
|  | @ -10,13 +10,17 @@ namespace Ui { | |||
| class DumpingDialog; | ||||
| } | ||||
| 
 | ||||
| namespace Core { | ||||
| class System; | ||||
| } | ||||
| 
 | ||||
| class QLineEdit; | ||||
| 
 | ||||
| class DumpingDialog : public QDialog { | ||||
|     Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|     explicit DumpingDialog(QWidget* parent); | ||||
|     explicit DumpingDialog(QWidget* parent, Core::System& system); | ||||
|     ~DumpingDialog() override; | ||||
| 
 | ||||
|     QString GetFilePath() const; | ||||
|  | @ -32,6 +36,7 @@ private: | |||
|                            QLineEdit* line_edit); | ||||
| 
 | ||||
|     std::unique_ptr<Ui::DumpingDialog> ui; | ||||
|     Core::System& system; | ||||
| 
 | ||||
|     QString last_path; | ||||
| 
 | ||||
|  |  | |||
|  | @ -144,25 +144,12 @@ void GMainWindow::ShowTelemetryCallout() { | |||
|            "<br/><br/>Would you like to share your usage data with us?"); | ||||
|     if (QMessageBox::question(this, tr("Telemetry"), telemetry_message) == QMessageBox::Yes) { | ||||
|         NetSettings::values.enable_telemetry = true; | ||||
|         Settings::Apply(); | ||||
|         system.ApplySettings(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 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() { | ||||
| #ifdef _WIN32 | ||||
|     // 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_) | ||||
|     : ui{std::make_unique<Ui::MainWindow>()}, system{system_}, movie{Core::Movie::GetInstance()}, | ||||
|       config{std::make_unique<Config>()}, emu_thread{nullptr} { | ||||
|     InitializeLogging(); | ||||
|     Common::Log::Initialize(); | ||||
|     Common::Log::Start(); | ||||
| 
 | ||||
|     Debugger::ToggleConsole(); | ||||
|     Settings::LogSettings(); | ||||
| 
 | ||||
|     // register types to use in slots and signals
 | ||||
|     qRegisterMetaType<std::size_t>("std::size_t"); | ||||
|  | @ -1193,12 +1181,13 @@ void GMainWindow::BootGame(const QString& filename) { | |||
|         const std::string config_file_name = | ||||
|             title_id == 0 ? name : fmt::format("{:016X}", title_id); | ||||
|         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); | ||||
|         Settings::LogSettings(); | ||||
|     } | ||||
| 
 | ||||
|     Settings::LogSettings(); | ||||
| 
 | ||||
|     // Save configurations
 | ||||
|     UpdateUISettings(); | ||||
|     game_list->SaveInterfaceLayout(); | ||||
|  | @ -1936,7 +1925,7 @@ void GMainWindow::ChangeScreenLayout() { | |||
|     } | ||||
| 
 | ||||
|     Settings::values.layout_option = new_layout; | ||||
|     Settings::Apply(); | ||||
|     system.ApplySettings(); | ||||
|     UpdateSecondaryWindowVisibility(); | ||||
| } | ||||
| 
 | ||||
|  | @ -1964,18 +1953,18 @@ void GMainWindow::ToggleScreenLayout() { | |||
| 
 | ||||
|     Settings::values.layout_option = new_layout; | ||||
|     SyncMenuUISettings(); | ||||
|     Settings::Apply(); | ||||
|     system.ApplySettings(); | ||||
|     UpdateSecondaryWindowVisibility(); | ||||
| } | ||||
| 
 | ||||
| void GMainWindow::OnSwapScreens() { | ||||
|     Settings::values.swap_screen = ui->action_Screen_Layout_Swap_Screens->isChecked(); | ||||
|     Settings::Apply(); | ||||
|     system.ApplySettings(); | ||||
| } | ||||
| 
 | ||||
| void GMainWindow::OnRotateScreens() { | ||||
|     Settings::values.upright_screen = ui->action_Screen_Layout_Upright_Screens->isChecked(); | ||||
|     Settings::Apply(); | ||||
|     system.ApplySettings(); | ||||
| } | ||||
| 
 | ||||
| void GMainWindow::TriggerSwapScreens() { | ||||
|  | @ -2014,7 +2003,7 @@ void GMainWindow::OnLoadState() { | |||
| void GMainWindow::OnConfigure() { | ||||
|     game_list->SetDirectoryWatcherEnabled(false); | ||||
|     Settings::SetConfiguringGlobal(true); | ||||
|     ConfigureDialog configureDialog(this, hotkey_registry, | ||||
|     ConfigureDialog configureDialog(this, hotkey_registry, system, | ||||
|                                     !multiplayer_state->IsHostingPublicRoom()); | ||||
|     connect(&configureDialog, &ConfigureDialog::LanguageChanged, this, | ||||
|             &GMainWindow::OnLanguageChanged); | ||||
|  | @ -2336,7 +2325,7 @@ void GMainWindow::OnOpenFFmpeg() { | |||
| #endif | ||||
| 
 | ||||
| void GMainWindow::OnStartVideoDumping() { | ||||
|     DumpingDialog dialog(this); | ||||
|     DumpingDialog dialog(this, system); | ||||
|     if (dialog.exec() != QDialog::DialogCode::Accepted) { | ||||
|         ui->action_Dump_Video->setChecked(false); | ||||
|         return; | ||||
|  | @ -2938,7 +2927,7 @@ int main(int argc, char* argv[]) { | |||
|     // generating shaders
 | ||||
|     setlocale(LC_ALL, "C"); | ||||
| 
 | ||||
|     Core::System& system = Core::System::GetInstance(); | ||||
|     auto& system{Core::System::GetInstance()}; | ||||
|     GMainWindow main_window(system); | ||||
| 
 | ||||
|     // Register frontend applets
 | ||||
|  |  | |||
|  | @ -57,6 +57,7 @@ add_library(citra_common STATIC | |||
|     detached_tasks.h | ||||
|     bit_field.h | ||||
|     bit_set.h | ||||
|     bounded_threadsafe_queue.h | ||||
|     cityhash.cpp | ||||
|     cityhash.h | ||||
|     color.h | ||||
|  | @ -85,8 +86,10 @@ add_library(citra_common STATIC | |||
|     logging/filter.h | ||||
|     logging/formatter.h | ||||
|     logging/log.h | ||||
|     logging/log_entry.h | ||||
|     logging/text_formatter.cpp | ||||
|     logging/text_formatter.h | ||||
|     logging/types.h | ||||
|     math_util.h | ||||
|     memory_detect.cpp | ||||
|     memory_detect.h | ||||
|  | @ -173,3 +176,6 @@ endif() | |||
| if (CITRA_USE_PRECOMPILED_HEADERS) | ||||
|     target_precompile_headers(citra_common PRIVATE precompiled_headers.h) | ||||
| 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
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <memory> | ||||
| 
 | ||||
| #include "common/dynamic_library/dynamic_library.h" | ||||
| #include "common/dynamic_library/fdk-aac.h" | ||||
| #include "common/logging/log.h" | ||||
|  |  | |||
|  | @ -2,6 +2,8 @@ | |||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <memory> | ||||
| 
 | ||||
| #include "common/dynamic_library/dynamic_library.h" | ||||
| #include "common/dynamic_library/ffmpeg.h" | ||||
| #include "common/logging/log.h" | ||||
|  |  | |||
|  | @ -10,6 +10,7 @@ | |||
| #include <unordered_map> | ||||
| #include <boost/iostreams/device/file_descriptor.hpp> | ||||
| #include <boost/iostreams/stream.hpp> | ||||
| #include <fmt/format.h> | ||||
| #include "common/assert.h" | ||||
| #include "common/common_funcs.h" | ||||
| #include "common/common_paths.h" | ||||
|  |  | |||
|  | @ -2,304 +2,444 @@ | |||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <chrono> | ||||
| #include <condition_variable> | ||||
| #include <memory> | ||||
| #include <mutex> | ||||
| #include <thread> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include <fmt/format.h> | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
| #include <share.h>   // For _SH_DENYWR
 | ||||
| #include <windows.h> // For OutputDebugStringW
 | ||||
| #else | ||||
| #define _SH_DENYWR 0 | ||||
| #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/log.h" | ||||
| #include "common/logging/log_entry.h" | ||||
| #include "common/logging/text_formatter.h" | ||||
| #include "common/polyfill_thread.h" | ||||
| #include "common/settings.h" | ||||
| #include "common/string_util.h" | ||||
| #include "common/threadsafe_queue.h" | ||||
| #include "common/thread.h" | ||||
| 
 | ||||
| namespace Log { | ||||
| namespace Common::Log { | ||||
| 
 | ||||
| Filter filter; | ||||
| void SetGlobalFilter(const Filter& f) { | ||||
|     filter = f; | ||||
| namespace { | ||||
| 
 | ||||
| /**
 | ||||
|  * 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. | ||||
|  */ | ||||
| class Impl { | ||||
| public: | ||||
|     static Impl& Instance() { | ||||
|         static Impl backend; | ||||
|         return backend; | ||||
|         if (!instance) { | ||||
|             throw std::runtime_error("Using Logging instance before its initialization"); | ||||
|         } | ||||
|         return *instance; | ||||
|     } | ||||
| 
 | ||||
|     Impl(Impl const&) = delete; | ||||
|     const Impl& operator=(Impl const&) = delete; | ||||
|     static void Initialize(std::string_view log_file) { | ||||
|         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, | ||||
|                    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))); | ||||
|     } | ||||
| 
 | ||||
|     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: | ||||
|     Impl() { | ||||
|         backend_thread = std::thread([&] { | ||||
|             Entry entry; | ||||
|             auto write_logs = [&](Entry& e) { | ||||
|                 std::lock_guard lock{writing_mutex}; | ||||
|                 for (const auto& backend : backends) { | ||||
|                     backend->Write(e); | ||||
|                 } | ||||
|             }; | ||||
|             while (true) { | ||||
|                 entry = message_queue.PopWait(); | ||||
|                 if (entry.final_entry) { | ||||
|                     break; | ||||
|                 } | ||||
|                 write_logs(entry); | ||||
|     Impl(const std::string& file_backend_filename, const Filter& filter_) | ||||
|         : filter{filter_}, file_backend{file_backend_filename} { | ||||
| #ifdef CITRA_LINUX_GCC_BACKTRACE | ||||
|         int waker_pipefd[2]; | ||||
|         int done_printing_pipefd[2]; | ||||
|         if (pipe2(waker_pipefd, O_CLOEXEC) || pipe2(done_printing_pipefd, O_CLOEXEC)) { | ||||
|             abort(); | ||||
|         } | ||||
|         backtrace_thread_waker_fd = waker_pipefd[1]; | ||||
|         backtrace_done_printing_fd = done_printing_pipefd[0]; | ||||
|         std::thread([this, wait_fd = waker_pipefd[0], done_fd = done_printing_pipefd[1]] { | ||||
|             Common::SetCurrentThreadName("citra:Crash"); | ||||
|             for (u8 ignore = 0; read(wait_fd, &ignore, 1) != 1;) | ||||
|                 ; | ||||
|             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
 | ||||
|             // where a system is repeatedly spamming logs even on close.
 | ||||
|             constexpr int MAX_LOGS_TO_WRITE = 100; | ||||
|             int logs_written = 0; | ||||
|             while (logs_written++ < MAX_LOGS_TO_WRITE && message_queue.Pop(entry)) { | ||||
|                 write_logs(entry); | ||||
|     ~Impl() { | ||||
| #ifdef CITRA_LINUX_GCC_BACKTRACE | ||||
|         if (int zero_or_ignore = 0; | ||||
|             !received_signal.compare_exchange_strong(zero_or_ignore, SIGKILL)) { | ||||
|             SleepForever(); | ||||
|         } | ||||
| #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, | ||||
|                       const char* function, std::string message) const { | ||||
|                       const char* function, std::string&& message) const { | ||||
|         using std::chrono::duration_cast; | ||||
|         using std::chrono::microseconds; | ||||
|         using std::chrono::steady_clock; | ||||
| 
 | ||||
|         Entry entry; | ||||
|         entry.timestamp = | ||||
|             duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin); | ||||
|         entry.log_class = log_class; | ||||
|         entry.log_level = log_level; | ||||
|         entry.filename = filename; | ||||
|         entry.line_num = line_nr; | ||||
|         entry.function = function; | ||||
|         entry.message = std::move(message); | ||||
| 
 | ||||
|         return entry; | ||||
|         return { | ||||
|             .timestamp = duration_cast<microseconds>(steady_clock::now() - time_origin), | ||||
|             .log_class = log_class, | ||||
|             .log_level = log_level, | ||||
|             .filename = filename, | ||||
|             .line_num = line_nr, | ||||
|             .function = function, | ||||
|             .message = std::move(message), | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     std::mutex writing_mutex; | ||||
|     std::thread backend_thread; | ||||
|     std::vector<std::unique_ptr<Backend>> backends; | ||||
|     Common::MPSCQueue<Log::Entry> message_queue; | ||||
|     Filter filter; | ||||
|     std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; | ||||
| }; | ||||
| 
 | ||||
| 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()); | ||||
|     void ForEachBackend(auto lambda) { | ||||
|         lambda(static_cast<Backend&>(debugger_backend)); | ||||
|         lambda(static_cast<Backend&>(color_console_backend)); | ||||
|         lambda(static_cast<Backend&>(file_backend)); | ||||
| #ifdef ANDROID | ||||
|         lambda(static_cast<Backend&>(lc_backend)); | ||||
| #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) { | ||||
| #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; | ||||
|     static void Deleter(Impl* ptr) { | ||||
|         delete ptr; | ||||
|     } | ||||
| #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) { | ||||
|     Impl::Instance().AddBackend(std::move(backend)); | ||||
| void Start() { | ||||
|     Impl::Start(); | ||||
| } | ||||
| 
 | ||||
| void RemoveBackend(std::string_view backend_name) { | ||||
|     Impl::Instance().RemoveBackend(backend_name); | ||||
| void DisableLoggingInTests() { | ||||
|     initialization_in_progress_suppress_logging = true; | ||||
| } | ||||
| 
 | ||||
| Backend* GetBackend(std::string_view backend_name) { | ||||
|     return Impl::Instance().GetBackend(backend_name); | ||||
| void SetGlobalFilter(const Filter& filter) { | ||||
|     Impl::Instance().SetGlobalFilter(filter); | ||||
| } | ||||
| 
 | ||||
| void SetColorConsoleBackendEnabled(bool enabled) { | ||||
|     Impl::Instance().SetColorConsoleBackendEnabled(enabled); | ||||
| } | ||||
| 
 | ||||
| void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, | ||||
|                        unsigned int line_num, const char* function, const char* format, | ||||
|                        const fmt::format_args& args) { | ||||
|     auto& instance = Impl::Instance(); | ||||
|     instance.PushEntry(log_class, log_level, filename, line_num, function, | ||||
|                        fmt::vformat(format, args)); | ||||
|     if (!initialization_in_progress_suppress_logging) { | ||||
|         Impl::Instance().PushEntry(log_class, log_level, filename, line_num, function, | ||||
|                                    fmt::vformat(format, args)); | ||||
|     } | ||||
| } | ||||
| } // namespace Log
 | ||||
| } // namespace Common::Log
 | ||||
|  |  | |||
|  | @ -4,149 +4,24 @@ | |||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <chrono> | ||||
| #include <memory> | ||||
| #include <string> | ||||
| #include <string_view> | ||||
| #include "common/file_util.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 | ||||
|  * formatting on different frontends, as well as facilitating filtering and aggregation. | ||||
|  * The global filter will prevent any messages from even being processed if they are filtered. | ||||
|  */ | ||||
| struct Entry { | ||||
|     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; | ||||
| void SetGlobalFilter(const Filter& filter); | ||||
| 
 | ||||
|     Entry() = default; | ||||
|     Entry(Entry&& o) = default; | ||||
| 
 | ||||
|     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
 | ||||
| void SetColorConsoleBackendEnabled(bool enabled); | ||||
| } // namespace Common::Log
 | ||||
|  |  | |||
|  | @ -3,11 +3,12 @@ | |||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include "common/logging/backend.h" | ||||
| 
 | ||||
| #include "common/assert.h" | ||||
| #include "common/logging/filter.h" | ||||
| #include "common/string_util.h" | ||||
| 
 | ||||
| namespace Log { | ||||
| namespace Common::Log { | ||||
| namespace { | ||||
| template <typename It> | ||||
| Level GetLevelByName(const It begin, const It end) { | ||||
|  | @ -22,7 +23,7 @@ Level GetLevelByName(const It begin, const It end) { | |||
| 
 | ||||
| template <typename It> | ||||
| 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)); | ||||
|         if (Common::ComparePartialString(begin, end, level_name)) { | ||||
|             return static_cast<Class>(i); | ||||
|  | @ -62,6 +63,115 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { | |||
| } | ||||
| } // 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) { | ||||
|     ResetAll(default_level); | ||||
| } | ||||
|  | @ -96,4 +206,11 @@ bool Filter::CheckMessage(Class log_class, Level level) const { | |||
|     return static_cast<u8>(level) >= | ||||
|            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 <cstddef> | ||||
| #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 <array> | ||||
| #include "common/common_types.h" | ||||
| #include "common/logging/formatter.h" | ||||
| #include <string_view> | ||||
| 
 | ||||
| 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
 | ||||
| constexpr const char* TrimSourcePath(std::string_view source) { | ||||
|  | @ -20,144 +22,6 @@ constexpr const char* TrimSourcePath(std::string_view source) { | |||
|     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
 | ||||
| void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, | ||||
|                        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> | ||||
| void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num, | ||||
|                    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, | ||||
|                       fmt::make_format_args(args...)); | ||||
| } | ||||
| 
 | ||||
| } // namespace Log
 | ||||
| } // namespace Common::Log
 | ||||
| 
 | ||||
| // Define the fmt lib macros
 | ||||
| #define LOG_GENERIC(log_class, log_level, ...)                                                     \ | ||||
|     ::Log::FmtLogMessage(log_class, log_level, ::Log::TrimSourcePath(__FILE__), __LINE__,          \ | ||||
|                          __func__, __VA_ARGS__) | ||||
|     Common::Log::FmtLogMessage(log_class, log_level, Common::Log::TrimSourcePath(__FILE__),        \ | ||||
|                                __LINE__, __func__, __VA_ARGS__) | ||||
| 
 | ||||
| #ifdef _DEBUG | ||||
| #define LOG_TRACE(log_class, ...)                                                                  \ | ||||
|     ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Trace,                             \ | ||||
|                          ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) | ||||
|     Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Trace,           \ | ||||
|                                Common::Log::TrimSourcePath(__FILE__), __LINE__, __func__,          \ | ||||
|                                __VA_ARGS__) | ||||
| #else | ||||
| #define LOG_TRACE(log_class, fmt, ...) (void(0)) | ||||
| #endif | ||||
| 
 | ||||
| #define LOG_DEBUG(log_class, ...)                                                                  \ | ||||
|     ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Debug,                             \ | ||||
|                          ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) | ||||
|     Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Debug,           \ | ||||
|                                Common::Log::TrimSourcePath(__FILE__), __LINE__, __func__,          \ | ||||
|                                __VA_ARGS__) | ||||
| #define LOG_INFO(log_class, ...)                                                                   \ | ||||
|     ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Info,                              \ | ||||
|                          ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) | ||||
|     Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Info,            \ | ||||
|                                Common::Log::TrimSourcePath(__FILE__), __LINE__, __func__,          \ | ||||
|                                __VA_ARGS__) | ||||
| #define LOG_WARNING(log_class, ...)                                                                \ | ||||
|     ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Warning,                           \ | ||||
|                          ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) | ||||
|     Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Warning,         \ | ||||
|                                Common::Log::TrimSourcePath(__FILE__), __LINE__, __func__,          \ | ||||
|                                __VA_ARGS__) | ||||
| #define LOG_ERROR(log_class, ...)                                                                  \ | ||||
|     ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Error,                             \ | ||||
|                          ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) | ||||
|     Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Error,           \ | ||||
|                                Common::Log::TrimSourcePath(__FILE__), __LINE__, __func__,          \ | ||||
|                                __VA_ARGS__) | ||||
| #define LOG_CRITICAL(log_class, ...)                                                               \ | ||||
|     ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Critical,                          \ | ||||
|                          ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) | ||||
|     Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Critical,        \ | ||||
|                                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 | ||||
| 
 | ||||
| #include "common/assert.h" | ||||
| #include "common/common_funcs.h" | ||||
| #include "common/logging/backend.h" | ||||
| #include "common/logging/filter.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "common/logging/log_entry.h" | ||||
| #include "common/logging/text_formatter.h" | ||||
| #include "common/string_util.h" | ||||
| 
 | ||||
| namespace Log { | ||||
| namespace Common::Log { | ||||
| 
 | ||||
| std::string FormatLogMessage(const Entry& entry) { | ||||
|     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()); | ||||
| #endif | ||||
| } | ||||
| } // namespace Log
 | ||||
| } // namespace Common::Log
 | ||||
|  |  | |||
|  | @ -4,10 +4,9 @@ | |||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <cstddef> | ||||
| #include <string> | ||||
| 
 | ||||
| namespace Log { | ||||
| namespace Common::Log { | ||||
| 
 | ||||
| struct Entry; | ||||
| 
 | ||||
|  | @ -19,4 +18,4 @@ void PrintMessage(const Entry& entry); | |||
| void PrintColoredMessage(const Entry& entry); | ||||
| /// Formats and prints a log entry to the android logcat.
 | ||||
| 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.
 | ||||
| 
 | ||||
| #include <array> | ||||
| #include <stdexcept> | ||||
| #include <utility> | ||||
| #include <vector> | ||||
| #include "common/logging/log.h" | ||||
|  |  | |||
|  | @ -5,18 +5,8 @@ | |||
| #include <string_view> | ||||
| #include <utility> | ||||
| #include "audio_core/dsp_interface.h" | ||||
| #include "common/file_util.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 { | ||||
| 
 | ||||
|  | @ -72,64 +62,6 @@ std::string_view GetTextureFilterName(TextureFilter filter) { | |||
| Values values = {}; | ||||
| 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() { | ||||
|     const auto log_setting = [](std::string_view name, const auto& value) { | ||||
|         LOG_INFO(Config, "{}: {}", name, value); | ||||
|  |  | |||
|  | @ -525,7 +525,6 @@ void SetConfiguringGlobal(bool is_global); | |||
| 
 | ||||
| float Volume(); | ||||
| 
 | ||||
| void Apply(); | ||||
| void LogSettings(); | ||||
| 
 | ||||
| // Restore the global state of all applicable settings in the Values struct
 | ||||
|  |  | |||
|  | @ -13,8 +13,10 @@ | |||
| #include <mutex> | ||||
| #include <utility> | ||||
| 
 | ||||
| #include "common/polyfill_thread.h" | ||||
| 
 | ||||
| namespace Common { | ||||
| template <typename T> | ||||
| template <typename T, bool with_stop_token = false> | ||||
| class SPSCQueue { | ||||
| public: | ||||
|     SPSCQueue() { | ||||
|  | @ -46,15 +48,13 @@ public: | |||
|         ElementPtr* new_ptr = new ElementPtr(); | ||||
|         write_ptr->next.store(new_ptr, std::memory_order_release); | ||||
|         write_ptr = new_ptr; | ||||
|         ++size; | ||||
| 
 | ||||
|         const size_t previous_size{size++}; | ||||
| 
 | ||||
|         // Acquire the mutex and then immediately release it as a fence.
 | ||||
|         // cv_mutex must be held or else there will be a missed wakeup if the other thread is in the
 | ||||
|         // line before cv.wait
 | ||||
|         // 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.
 | ||||
|         if (previous_size == 0) { | ||||
|             std::lock_guard lock{cv_mutex}; | ||||
|         } | ||||
|         std::scoped_lock lock{cv_mutex}; | ||||
|         cv.notify_one(); | ||||
|     } | ||||
| 
 | ||||
|  | @ -93,6 +93,19 @@ public: | |||
|         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
 | ||||
|     void Clear() { | ||||
|         size.store(0); | ||||
|  | @ -121,13 +134,13 @@ private: | |||
|     ElementPtr* read_ptr; | ||||
|     std::atomic_size_t size{0}; | ||||
|     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,
 | ||||
| // single reader, multiple writer queue
 | ||||
| 
 | ||||
| template <typename T> | ||||
| template <typename T, bool with_stop_token = false> | ||||
| class MPSCQueue { | ||||
| public: | ||||
|     [[nodiscard]] std::size_t Size() const { | ||||
|  | @ -144,7 +157,7 @@ public: | |||
| 
 | ||||
|     template <typename Arg> | ||||
|     void Push(Arg&& t) { | ||||
|         std::lock_guard lock{write_lock}; | ||||
|         std::scoped_lock lock{write_lock}; | ||||
|         spsc_queue.Push(t); | ||||
|     } | ||||
| 
 | ||||
|  | @ -160,13 +173,17 @@ public: | |||
|         return spsc_queue.PopWait(); | ||||
|     } | ||||
| 
 | ||||
|     T PopWait(std::stop_token stop_token) { | ||||
|         return spsc_queue.PopWait(stop_token); | ||||
|     } | ||||
| 
 | ||||
|     // not thread-safe
 | ||||
|     void Clear() { | ||||
|         spsc_queue.Clear(); | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     SPSCQueue<T> spsc_queue; | ||||
|     SPSCQueue<T, with_stop_token> spsc_queue; | ||||
|     std::mutex write_lock; | ||||
| }; | ||||
| } // namespace Common
 | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ | |||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <exception> | ||||
| #include <memory> | ||||
| #include <stdexcept> | ||||
| #include <utility> | ||||
|  | @ -12,9 +13,9 @@ | |||
| #include "common/arch.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "common/settings.h" | ||||
| #include "common/texture.h" | ||||
| #include "core/arm/arm_interface.h" | ||||
| #include "core/arm/exclusive_monitor.h" | ||||
| #include "core/hle/service/cam/cam.h" | ||||
| #if CITRA_ARCH(x86_64) || CITRA_ARCH(arm64) | ||||
| #include "core/arm/dynarmic/arm_dynarmic.h" | ||||
| #endif | ||||
|  | @ -23,19 +24,22 @@ | |||
| #include "core/core.h" | ||||
| #include "core/core_timing.h" | ||||
| #include "core/dumping/backend.h" | ||||
| #include "core/dumping/ffmpeg_backend.h" | ||||
| #include "core/frontend/image_interface.h" | ||||
| #include "core/gdbstub/gdbstub.h" | ||||
| #include "core/global.h" | ||||
| #include "core/hle/kernel/client_port.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/kernel/thread.h" | ||||
| #include "core/hle/service/apt/applet_manager.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/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/sm/sm.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> | ||||
| void System::serialize(Archive& ar, const unsigned int file_version) { | ||||
| 
 | ||||
|  |  | |||
|  | @ -330,6 +330,9 @@ public: | |||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     /// Applies any changes to settings to this core instance.
 | ||||
|     void ApplySettings(); | ||||
| 
 | ||||
| private: | ||||
|     /**
 | ||||
|      * Initialize the emulated system. | ||||
|  |  | |||
|  | @ -203,7 +203,7 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) { | |||
| 
 | ||||
|     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)); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -529,7 +529,7 @@ ResultCode SVC::ControlMemory(u32* out_addr, u32 addr0, u32 addr1, u32 size, u32 | |||
|         return ERR_INVALID_COMBINATION; | ||||
|     } | ||||
| 
 | ||||
|     process.vm_manager.LogLayout(Log::Level::Trace); | ||||
|     process.vm_manager.LogLayout(Common::Log::Level::Trace); | ||||
| 
 | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
|  |  | |||
|  | @ -239,10 +239,10 @@ ResultCode VMManager::ReprotectRange(VAddr target, u32 size, VMAPermission new_p | |||
|     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) { | ||||
|         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, | ||||
|                     (u8)vma.permissions & (u8)VMAPermission::Read ? 'R' : '-', | ||||
|                     (u8)vma.permissions & (u8)VMAPermission::Write ? 'W' : '-', | ||||
|  |  | |||
|  | @ -202,7 +202,7 @@ public: | |||
|     ResultCode ReprotectRange(VAddr target, u32 size, VMAPermission new_perms); | ||||
| 
 | ||||
|     /// 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
 | ||||
|     ResultVal<std::vector<std::pair<MemoryRef, u32>>> GetBackingBlocksForRange(VAddr address, | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ | |||
| #include <boost/serialization/unique_ptr.hpp> | ||||
| #include <cryptopp/osrng.h> | ||||
| #include <cryptopp/sha.h> | ||||
| #include <fmt/format.h> | ||||
| #include "common/archives.h" | ||||
| #include "common/file_util.h" | ||||
| #include "common/logging/log.h" | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ | |||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <fmt/format.h> | ||||
| #include "common/alignment.h" | ||||
| #include "common/settings.h" | ||||
| #include "common/string_util.h" | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ | |||
| #include <boost/serialization/base_object.hpp> | ||||
| #include <boost/serialization/shared_ptr.hpp> | ||||
| #include <boost/serialization/unique_ptr.hpp> | ||||
| #include <fmt/format.h> | ||||
| #include "common/string_util.h" | ||||
| #include "common/swap.h" | ||||
| #include "core/core.h" | ||||
|  |  | |||
|  | @ -10,6 +10,7 @@ | |||
| #include <boost/optional.hpp> | ||||
| #include <cryptopp/hex.h> | ||||
| #include <cryptopp/osrng.h> | ||||
| #include <fmt/format.h> | ||||
| #include "common/bit_field.h" | ||||
| #include "common/common_types.h" | ||||
| #include "common/file_util.h" | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
| 
 | ||||
| #include <chrono> | ||||
| #include <cryptopp/hex.h> | ||||
| #include <fmt/format.h> | ||||
| #include "common/archives.h" | ||||
| #include "common/logging/log.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) { | ||||
|     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 | ||||
|     Common::Log::Initialize(log_file); | ||||
|     Common::Log::SetColorConsoleBackendEnabled(true); | ||||
| } | ||||
| 
 | ||||
| /// Application entry point
 | ||||
|  |  | |||
|  | @ -4,11 +4,13 @@ | |||
| 
 | ||||
| #include <cmath> | ||||
| #include <catch2/catch_test_macros.hpp> | ||||
| #include "common/logging/backend.h" | ||||
| #include "common/param_package.h" | ||||
| 
 | ||||
| namespace Common { | ||||
| 
 | ||||
| TEST_CASE("ParamPackage", "[common]") { | ||||
|     Common::Log::DisableLoggingInTests(); | ||||
|     ParamPackage original{ | ||||
|         {"abc", "xyz"}, | ||||
|         {"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, | ||||
|                                   GLsizei length, const GLchar* message, const void* user_param) { | ||||
|     Log::Level level = Log::Level::Info; | ||||
|     auto level = Common::Log::Level::Info; | ||||
|     switch (severity) { | ||||
|     case GL_DEBUG_SEVERITY_HIGH: | ||||
|         level = Log::Level::Critical; | ||||
|         level = Common::Log::Level::Critical; | ||||
|         break; | ||||
|     case GL_DEBUG_SEVERITY_MEDIUM: | ||||
|         level = Log::Level::Warning; | ||||
|         level = Common::Log::Level::Warning; | ||||
|         break; | ||||
|     case GL_DEBUG_SEVERITY_NOTIFICATION: | ||||
|     case GL_DEBUG_SEVERITY_LOW: | ||||
|         level = Log::Level::Debug; | ||||
|         level = Common::Log::Level::Debug; | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     LOG_GENERIC(Log::Class::Render_OpenGL, level, "{} {} {}: {}", GetSource(source), GetType(type), | ||||
|                 id, message); | ||||
|     LOG_GENERIC(Common::Log::Class::Render_OpenGL, level, "{} {} {}: {}", GetSource(source), | ||||
|                 GetType(type), id, message); | ||||
| } | ||||
| 
 | ||||
| Driver::Driver(Core::TelemetrySession& telemetry_session_) | ||||
|  |  | |||
|  | @ -38,23 +38,23 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL DebugUtilsCallback( | |||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     Log::Level level{}; | ||||
|     Common::Log::Level level{}; | ||||
|     switch (severity) { | ||||
|     case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: | ||||
|         level = Log::Level::Error; | ||||
|         level = Common::Log::Level::Error; | ||||
|         break; | ||||
|     case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: | ||||
|         level = Log::Level::Info; | ||||
|         level = Common::Log::Level::Info; | ||||
|         break; | ||||
|     case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: | ||||
|     case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: | ||||
|         level = Log::Level::Debug; | ||||
|         level = Common::Log::Level::Debug; | ||||
|         break; | ||||
|     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->pMessage ? callback_data->pMessage : "<null>"); | ||||
| 
 | ||||
|  | @ -69,25 +69,25 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL DebugReportCallback(VkDebugReportFlagsEXT | |||
|                                                           const char* pMessage, void* pUserData) { | ||||
| 
 | ||||
|     const VkDebugReportFlagBitsEXT severity = static_cast<VkDebugReportFlagBitsEXT>(flags); | ||||
|     Log::Level level{}; | ||||
|     Common::Log::Level level{}; | ||||
|     switch (severity) { | ||||
|     case VK_DEBUG_REPORT_ERROR_BIT_EXT: | ||||
|         level = Log::Level::Error; | ||||
|         level = Common::Log::Level::Error; | ||||
|         break; | ||||
|     case VK_DEBUG_REPORT_INFORMATION_BIT_EXT: | ||||
|         level = Log::Level::Warning; | ||||
|         level = Common::Log::Level::Warning; | ||||
|         break; | ||||
|     case VK_DEBUG_REPORT_DEBUG_BIT_EXT: | ||||
|     case VK_DEBUG_REPORT_WARNING_BIT_EXT: | ||||
|     case VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT: | ||||
|         level = Log::Level::Debug; | ||||
|         level = Common::Log::Level::Debug; | ||||
|         break; | ||||
|     default: | ||||
|         level = Log::Level::Info; | ||||
|         level = Common::Log::Level::Info; | ||||
|     } | ||||
| 
 | ||||
|     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 = {} | {}", | ||||
|                 vk::to_string(type), object, messageCode, pLayerPrefix, pMessage); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue