Refactor LogManager (remove GetInstance()). In headless, add option to log to OutputDebugString on Windows.

This commit is contained in:
Henrik Rydgård 2024-12-03 21:00:08 +01:00
parent 0f97578a35
commit 3ffff831c7
14 changed files with 117 additions and 120 deletions

View file

@ -81,7 +81,8 @@ bool HandleAssert(const char *function, const char *file, int line, const char *
if (!getenv("CI")) {
int msgBoxStyle = MB_ICONINFORMATION | MB_YESNO;
std::wstring wtext = ConvertUTF8ToWString(formatted) + L"\n\nTry to continue?";
std::wstring wcaption = ConvertUTF8ToWString(std::string(caption) + " " + GetCurrentThreadName());
const char *threadName = GetCurrentThreadName();
std::wstring wcaption = ConvertUTF8ToWString(std::string(caption) + " " + (threadName ? threadName : "(unknown thread)"));
OutputDebugString(wtext.c_str());
printf("%s\n", formatted);
if (IDYES != MessageBox(0, wtext.c_str(), wcaption.c_str(), msgBoxStyle)) {

View file

@ -40,7 +40,8 @@
#include "Common/File/FileUtil.h"
#include "Common/StringUtils.h"
// Don't need to savestate this.
LogManager g_logManager;
const char *hleCurrentThreadName = nullptr;
bool *g_bLogEnabledSetting = nullptr;
@ -58,31 +59,14 @@ void GenericLog(LogLevel level, Log type, const char *file, int line, const char
return;
va_list args;
va_start(args, fmt);
LogManager *instance = LogManager::GetInstance();
if (instance) {
instance->LogLine(level, type, file, line, fmt, args);
} else {
// Fall back to printf or direct android logger with a small buffer if the log manager hasn't been initialized yet.
#if PPSSPP_PLATFORM(ANDROID)
char temp[512];
vsnprintf(temp, sizeof(temp), fmt, args);
__android_log_print(ANDROID_LOG_INFO, "PPSSPP", "EARLY: %s", temp);
#else
vprintf(fmt, args);
printf("\n");
#endif
}
g_logManager.LogLine(level, type, file, line, fmt, args);
va_end(args);
}
bool GenericLogEnabled(LogLevel level, Log type) {
if (LogManager::GetInstance())
return (*g_bLogEnabledSetting) && LogManager::GetInstance()->IsEnabled(level, type);
return false;
return (*g_bLogEnabledSetting) && g_logManager.IsEnabled(level, type);
}
LogManager *LogManager::logManager_ = nullptr;
// NOTE: Needs to be kept in sync with the Log enum.
static const char * const g_logTypeNames[] = {
"SYSTEM",
@ -106,23 +90,30 @@ static const char * const g_logTypeNames[] = {
"PRINTF",
"TEXREPLACE",
"SCEAUDIO",
"SCECTRL",
"SCEDISP",
"SCEFONT",
"SCEGE",
"SCEINTC",
"SCEIO",
"SCEKERNEL",
"SCEMODULE",
"SCENET",
"SCERTC",
"SCESAS",
"SCEUTIL",
"SCEMISC",
"SCEAUDIO",
"SCECTRL",
"SCEDISP",
"SCEFONT",
"SCEGE",
"SCEINTC",
"SCEIO",
"SCEKERNEL",
"SCEMODULE",
"SCENET",
"SCERTC",
"SCESAS",
"SCEUTIL",
"SCEMISC",
};
LogManager::LogManager(bool *enabledSetting) {
void LogManager::Init(bool *enabledSetting, bool headless) {
g_bLogEnabledSetting = enabledSetting;
if (initialized_) {
// Just update the pointer, already done above.
return;
}
initialized_ = true;
g_bLogEnabledSetting = enabledSetting;
_dbg_assert_(ARRAY_SIZE(g_logTypeNames) == (size_t)Log::NUMBER_OF_LOGS);
@ -167,14 +158,18 @@ LogManager::LogManager(bool *enabledSetting) {
AddListener(stdioLog_);
#endif
#if defined(_MSC_VER) && (defined(USING_WIN_UI) || PPSSPP_PLATFORM(UWP))
if (IsDebuggerPresent() && debuggerLog_ && LOG_MSC_OUTPUTDEBUG)
if (IsDebuggerPresent() && debuggerLog_ && (LOG_MSC_OUTPUTDEBUG || headless))
AddListener(debuggerLog_);
#endif
AddListener(ringLog_);
#endif
}
void LogManager::Shutdown() {
if (!initialized_) {
// already done
return;
}
LogManager::~LogManager() {
for (int i = 0; i < (int)Log::NUMBER_OF_LOGS; ++i) {
#if !defined(MOBILE_DEVICE) || defined(_DEBUG)
RemoveListener(fileLog_);
@ -200,6 +195,14 @@ LogManager::~LogManager() {
delete debuggerLog_;
#endif
delete ringLog_;
initialized_ = false;
}
LogManager::LogManager() {}
LogManager::~LogManager() {
Shutdown();
}
void LogManager::ChangeFileLog(const char *filename) {
@ -234,6 +237,22 @@ void LogManager::LoadConfig(const Section *section, bool debugDefaults) {
}
void LogManager::LogLine(LogLevel level, Log type, const char *file, int line, const char *format, va_list args) {
char msgBuf[1024];
if (!initialized_) {
// Fall back to printf or direct android logger with a small buffer if the log manager hasn't been initialized yet.
#if PPSSPP_PLATFORM(ANDROID)
vsnprintf(msgBuf, sizeof(msgBuf), format, args);
__android_log_print(ANDROID_LOG_INFO, "PPSSPP", "EARLY: %s", msgBuf);
#elif _MSC_VER
vsnprintf(msgBuf, sizeof(msgBuf), format, args);
OutputDebugStringUTF8(msgBuf);
#else
vprintf(format, args);
printf("\n");
#endif
return;
}
const LogChannel &log = log_[(size_t)type];
if (level > log.level || !log.enabled)
return;
@ -283,7 +302,6 @@ void LogManager::LogLine(LogLevel level, Log type, const char *file, int line, c
GetCurrentTimeFormatted(message.timestamp);
char msgBuf[1024];
va_list args_copy;
va_copy(args_copy, args);
@ -311,17 +329,8 @@ bool LogManager::IsEnabled(LogLevel level, Log type) {
return true;
}
void LogManager::Init(bool *enabledSetting) {
_assert_(logManager_ == nullptr);
logManager_ = new LogManager(enabledSetting);
}
void LogManager::Shutdown() {
delete logManager_;
logManager_ = nullptr;
}
void LogManager::AddListener(LogListener *listener) {
_dbg_assert_(initialized_);
if (!listener)
return;
std::lock_guard<std::mutex> lk(listeners_lock_);
@ -329,6 +338,7 @@ void LogManager::AddListener(LogListener *listener) {
}
void LogManager::RemoveListener(LogListener *listener) {
_dbg_assert_(initialized_);
if (!listener)
return;
std::lock_guard<std::mutex> lk(listeners_lock_);

View file

@ -104,13 +104,12 @@ class StdioListener;
class LogManager {
private:
LogManager(bool *enabledSetting);
~LogManager();
// Prevent copies.
LogManager(const LogManager &) = delete;
void operator=(const LogManager &) = delete;
bool initialized_ = false;
LogChannel log_[(size_t)Log::NUMBER_OF_LOGS];
FileLogListener *fileLog_ = nullptr;
#if PPSSPP_PLATFORM(WINDOWS)
@ -119,12 +118,14 @@ private:
StdioListener *stdioLog_ = nullptr;
OutputDebugStringLogListener *debuggerLog_ = nullptr;
RingbufferLogListener *ringLog_ = nullptr;
static LogManager *logManager_; // Singleton. Ugh.
std::mutex listeners_lock_;
std::vector<LogListener*> listeners_;
public:
LogManager();
~LogManager();
void AddListener(LogListener *listener);
void RemoveListener(LogListener *listener);
@ -175,19 +176,13 @@ public:
return ringLog_;
}
static inline LogManager* GetInstance() {
return logManager_;
}
static void SetInstance(LogManager *logManager) {
logManager_ = logManager;
}
static void Init(bool *enabledSetting);
static void Shutdown();
void Init(bool *enabledSetting, bool headless = false);
void Shutdown();
void ChangeFileLog(const char *filename);
void SaveConfig(Section *section);
void LoadConfig(const Section *section, bool debugDefaults);
};
extern LogManager g_logManager;

View file

@ -1170,7 +1170,7 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) {
#ifdef _DEBUG
debugDefaults = true;
#endif
LogManager::GetInstance()->LoadConfig(log, debugDefaults);
g_logManager.LoadConfig(log, debugDefaults);
Section *recent = iniFile.GetOrCreateSection("Recent");
recent->Get("MaxRecent", &iMaxRecent, 60);
@ -1371,8 +1371,7 @@ bool Config::Save(const char *saveReason) {
control->Delete("DPadRadius");
Section *log = iniFile.GetOrCreateSection(logSectionName);
if (LogManager::GetInstance())
LogManager::GetInstance()->SaveConfig(log);
g_logManager.SaveConfig(log);
// Time tracking
Section *playTime = iniFile.GetOrCreateSection("PlayTime");

View file

@ -75,13 +75,11 @@ private:
LogBroadcaster::LogBroadcaster() {
listener_ = new DebuggerLogListener();
if (LogManager::GetInstance())
LogManager::GetInstance()->AddListener(listener_);
g_logManager.AddListener(listener_);
}
LogBroadcaster::~LogBroadcaster() {
if (LogManager::GetInstance())
LogManager::GetInstance()->RemoveListener(listener_);
g_logManager.RemoveListener(listener_);
delete listener_;
}

View file

@ -398,7 +398,7 @@ static u32 sceGeDrawSync(u32 mode) {
}
static int sceGeContinue() {
DEBUG_LOG(Log::sceGe, "sceGeContinue");
DEBUG_LOG(Log::sceGe, "sceGeContinue()");
int ret = gpu->Continue();
hleEatCycles(220);
hleReSchedule("ge continue");

View file

@ -175,7 +175,7 @@ void DevMenuScreen::CreatePopupContents(UI::ViewGroup *parent) {
scroll->Add(items);
parent->Add(scroll);
RingbufferLogListener *ring = LogManager::GetInstance()->GetRingbufferListener();
RingbufferLogListener *ring = g_logManager.GetRingbufferListener();
if (ring) {
ring->SetEnabled(true);
}
@ -236,7 +236,7 @@ void GPIGPOScreen::CreatePopupContents(UI::ViewGroup *parent) {
void LogScreen::UpdateLog() {
using namespace UI;
RingbufferLogListener *ring = LogManager::GetInstance()->GetRingbufferListener();
RingbufferLogListener *ring = g_logManager.GetRingbufferListener();
if (!ring)
return;
vert_->Clear();
@ -319,8 +319,6 @@ void LogConfigScreen::CreateViews() {
vert->Add(new ItemHeader(dev->T("Logging Channels")));
LogManager *logMan = LogManager::GetInstance();
int cellSize = 400;
UI::GridLayoutSettings gridsettings(cellSize, 64, 5);
@ -329,7 +327,7 @@ void LogConfigScreen::CreateViews() {
for (int i = 0; i < LogManager::GetNumChannels(); i++) {
Log type = (Log)i;
LogChannel *chan = logMan->GetLogChannel(type);
LogChannel *chan = g_logManager.GetLogChannel(type);
LinearLayout *row = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(cellSize - 50, WRAP_CONTENT));
row->SetSpacing(0);
row->Add(new CheckBox(&chan->enabled, "", "", new LinearLayoutParams(50, WRAP_CONTENT)));
@ -339,27 +337,24 @@ void LogConfigScreen::CreateViews() {
}
UI::EventReturn LogConfigScreen::OnToggleAll(UI::EventParams &e) {
LogManager *logMan = LogManager::GetInstance();
for (int i = 0; i < LogManager::GetNumChannels(); i++) {
LogChannel *chan = logMan->GetLogChannel((Log)i);
LogChannel *chan = g_logManager.GetLogChannel((Log)i);
chan->enabled = !chan->enabled;
}
return UI::EVENT_DONE;
}
UI::EventReturn LogConfigScreen::OnEnableAll(UI::EventParams &e) {
LogManager *logMan = LogManager::GetInstance();
for (int i = 0; i < LogManager::GetNumChannels(); i++) {
LogChannel *chan = logMan->GetLogChannel((Log)i);
LogChannel *chan = g_logManager.GetLogChannel((Log)i);
chan->enabled = true;
}
return UI::EVENT_DONE;
}
UI::EventReturn LogConfigScreen::OnDisableAll(UI::EventParams &e) {
LogManager *logMan = LogManager::GetInstance();
for (int i = 0; i < LogManager::GetNumChannels(); i++) {
LogChannel *chan = logMan->GetLogChannel((Log)i);
LogChannel *chan = g_logManager.GetLogChannel((Log)i);
chan->enabled = false;
}
return UI::EVENT_DONE;
@ -396,11 +391,10 @@ void LogLevelScreen::OnCompleted(DialogResult result) {
if (result != DR_OK)
return;
int selected = listView_->GetSelected();
LogManager *logMan = LogManager::GetInstance();
for (int i = 0; i < LogManager::GetNumChannels(); ++i) {
Log type = (Log)i;
LogChannel *chan = logMan->GetLogChannel(type);
LogChannel *chan = g_logManager.GetLogChannel(type);
if (chan->enabled)
chan->level = (LogLevel)(selected + 1);
}

View file

@ -518,9 +518,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch
DiskCachingFileLoaderCache::SetCacheDir(g_Config.appCacheDirectory);
}
if (!LogManager::GetInstance()) {
LogManager::Init(&g_Config.bEnableLogging);
}
g_logManager.Init(&g_Config.bEnableLogging);
#if !PPSSPP_PLATFORM(WINDOWS)
g_Config.SetSearchPath(GetSysDirectory(DIRECTORY_SYSTEM));
@ -530,8 +528,6 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch
g_Config.Load();
#endif
LogManager *logman = LogManager::GetInstance();
const char *fileToLog = nullptr;
Path stateToLoad;
@ -674,21 +670,21 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch
}
if (fileToLog)
LogManager::GetInstance()->ChangeFileLog(fileToLog);
g_logManager.ChangeFileLog(fileToLog);
if (forceLogLevel)
LogManager::GetInstance()->SetAllLogLevels(logLevel);
g_logManager.SetAllLogLevels(logLevel);
PostLoadConfig();
#if PPSSPP_PLATFORM(ANDROID)
logger = new AndroidLogger();
logman->AddListener(logger);
g_logManager.AddListener(logger);
#elif (defined(MOBILE_DEVICE) && !defined(_DEBUG))
// Enable basic logging for any kind of mobile device, since LogManager doesn't.
// The MOBILE_DEVICE/_DEBUG condition matches LogManager.cpp.
logger = new PrintfLogger();
logman->AddListener(logger);
g_logManager.AddListener(logger);
#endif
if (System_GetPropertyBool(SYSPROP_SUPPORTS_PERMISSIONS)) {
@ -1491,7 +1487,7 @@ void NativeShutdown() {
// Avoid shutting this down when restarting core.
if (!restarting)
LogManager::Shutdown();
g_logManager.Shutdown();
if (logger) {
delete logger;

View file

@ -74,7 +74,7 @@ void App::InitialPPSSPP() {
// because the next place it was called was in the EmuThread, and it's too late by then.
CreateSysDirectories();
LogManager::Init(&g_Config.bEnableLogging);
g_logManager.Init(&g_Config.bEnableLogging);
// Set the config path to local state by default
// it will be overrided by `NativeInit` if there is custom memStick

View file

@ -1180,11 +1180,11 @@ namespace MainWindow
void ToggleDebugConsoleVisibility() {
if (!g_Config.bEnableLogging) {
LogManager::GetInstance()->GetConsoleListener()->Show(false);
g_logManager.GetConsoleListener()->Show(false);
EnableMenuItem(menu, ID_DEBUG_LOG, MF_GRAYED);
}
else {
LogManager::GetInstance()->GetConsoleListener()->Show(true);
g_logManager.GetConsoleListener()->Show(true);
EnableMenuItem(menu, ID_DEBUG_LOG, MF_ENABLED);
}
}

View file

@ -862,7 +862,7 @@ namespace MainWindow {
break;
case ID_DEBUG_LOG:
LogManager::GetInstance()->GetConsoleListener()->Show(LogManager::GetInstance()->GetConsoleListener()->Hidden());
g_logManager.GetConsoleListener()->Show(g_logManager.GetConsoleListener()->Hidden());
break;
case ID_DEBUG_IGNOREILLEGALREADS:

View file

@ -132,7 +132,7 @@ void System_Vibrate(int length_ms) {
}
static void AddDebugRestartArgs() {
if (LogManager::GetInstance()->GetConsoleListener()->IsOpen())
if (g_logManager.GetConsoleListener()->IsOpen())
restartArgs += " -l";
}
@ -897,7 +897,7 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin
// (this is an external process.)
bool result = VulkanMayBeAvailable();
LogManager::Shutdown();
g_logManager.Shutdown();
WinMainCleanup();
return result ? EXIT_CODE_VULKAN_WORKS : EXIT_FAILURE;
}
@ -954,7 +954,7 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin
// This will be overridden by the actual config. But we do want to log during startup.
g_Config.bEnableLogging = true;
LogManager::Init(&g_Config.bEnableLogging);
g_logManager.Init(&g_Config.bEnableLogging);
// On Win32 it makes more sense to initialize the system directories here
// because the next place it was called was in the EmuThread, and it's too late by then.
@ -1038,10 +1038,10 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin
// - By default in Debug, the console should be shown by default.
// - The -l switch is expected to show the log console, REGARDLESS of config settings.
// - It should be possible to log to a file without showing the console.
LogManager::GetInstance()->GetConsoleListener()->Init(showLog, 150, 120);
g_logManager.GetConsoleListener()->Init(showLog, 150, 120);
if (debugLogLevel) {
LogManager::GetInstance()->SetAllLogLevels(LogLevel::LDEBUG);
g_logManager.SetAllLogLevels(LogLevel::LDEBUG);
}
ContextMenuInit(_hInstance);
@ -1127,7 +1127,7 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin
DialogManager::DestroyAll();
timeEndPeriod(1);
LogManager::Shutdown();
g_logManager.Shutdown();
WinMainCleanup();
return 0;

View file

@ -340,6 +340,7 @@ int main(int argc, const char* argv[])
CPUCore cpuCore = CPUCore::JIT;
int debuggerPort = -1;
bool newAtrac = false;
bool outputDebugStringLog = false;
std::vector<std::string> testFilenames;
const char *mountIso = nullptr;
@ -362,6 +363,8 @@ int main(int argc, const char* argv[])
}
else if (!strcmp(argv[i], "-l") || !strcmp(argv[i], "--log"))
fullLog = true;
else if (!strcmp(argv[i], "-o") || !strcmp(argv[i], "--odslog"))
outputDebugStringLog = true;
else if (!strcmp(argv[i], "-i"))
cpuCore = CPUCore::INTERPRETER;
else if (!strcmp(argv[i], "-j"))
@ -426,17 +429,19 @@ int main(int argc, const char* argv[])
if (testFilenames.empty())
return printUsage(argv[0], argc <= 1 ? NULL : "No executables specified");
LogManager::Init(&g_Config.bEnableLogging);
LogManager *logman = LogManager::GetInstance();
g_Config.bEnableLogging = (fullLog || outputDebugStringLog);
g_logManager.Init(&g_Config.bEnableLogging, outputDebugStringLog);
PrintfLogger *printfLogger = new PrintfLogger();
for (int i = 0; i < (int)Log::NUMBER_OF_LOGS; i++) {
Log type = (Log)i;
logman->SetEnabled(type, fullLog);
logman->SetLogLevel(type, LogLevel::LDEBUG);
g_logManager.SetEnabled(type, (fullLog || outputDebugStringLog));
g_logManager.SetLogLevel(type, LogLevel::LDEBUG);
}
if (fullLog) {
// Only with --log, add the printfLogger.
g_logManager.AddListener(printfLogger);
}
logman->AddListener(printfLogger);
// Needs to be after log so we don't interfere with test output.
g_threadManager.Init(cpu_info.num_cores, cpu_info.logical_cpu_count);
@ -485,7 +490,7 @@ int main(int argc, const char* argv[])
g_Config.iLockParentalLevel = 9;
g_Config.iInternalResolution = 1;
g_Config.iFastForwardMode = (int)FastForwardMode::CONTINUOUS;
g_Config.bEnableLogging = fullLog;
g_Config.bEnableLogging = (fullLog || outputDebugStringLog);
g_Config.bSoftwareSkinning = true;
g_Config.bVertexDecoderJit = true;
g_Config.bSoftwareRendering = coreParameter.gpuCore == GPUCORE_SOFTWARE;
@ -618,7 +623,7 @@ int main(int argc, const char* argv[])
g_headlessHost = nullptr;
g_VFS.Clear();
LogManager::Shutdown();
g_logManager.Shutdown();
delete printfLogger;
#if PPSSPP_PLATFORM(WINDOWS)

View file

@ -1210,13 +1210,12 @@ void retro_init(void)
if (environ_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log))
{
log_cb = log.log;
LogManager::Init(&g_Config.bEnableLogging);
g_logManager.Init(&g_Config.bEnableLogging);
printfLogger = new PrintfLogger(log);
LogManager* logman = LogManager::GetInstance();
logman->RemoveListener(logman->GetStdioListener());
logman->RemoveListener(logman->GetDebuggerListener());
logman->ChangeFileLog(nullptr);
logman->AddListener(printfLogger);
g_logManager.RemoveListener(g_logManager.GetStdioListener());
g_logManager.RemoveListener(g_logManager.GetDebuggerListener());
g_logManager.ChangeFileLog(nullptr);
g_logManager.AddListener(printfLogger);
}
VsyncSwapIntervalReset();
@ -1250,7 +1249,7 @@ void retro_init(void)
g_Config.iInternalResolution = 0;
// Log levels must be set after g_Config.Load
LogManager::GetInstance()->SetAllLogLevels(LogLevel::LINFO);
g_logManager.SetAllLogLevels(LogLevel::LINFO);
const char* nickname = NULL;
if (environ_cb(RETRO_ENVIRONMENT_GET_USERNAME, &nickname) && nickname)
@ -1285,7 +1284,7 @@ void retro_init(void)
void retro_deinit(void)
{
g_threadManager.Teardown();
LogManager::Shutdown();
g_logManager.Shutdown();
log_cb = NULL;
delete printfLogger;