Console: Remove WX rubbish and replace

This commit is contained in:
Stenzek 2024-01-11 18:08:16 +10:00 committed by Connor McLaughlin
parent efae58de52
commit fa00069068
27 changed files with 772 additions and 1230 deletions

View File

@ -1,524 +1,500 @@
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: LGPL-3.0+
#include "common/Threading.h"
#include "common/TraceLog.h"
#include "common/Console.h"
#include "common/Assertions.h"
#include "common/RedtapeWindows.h" // OutputDebugString
#include "common/FileSystem.h"
#include "common/SmallString.h"
#include "common/Timer.h"
using namespace Threading;
#include "fmt/format.h"
// thread-local console indentation setting.
static thread_local int conlog_Indent(0);
#include <mutex>
#include <vector>
// thread-local console color storage.
static thread_local ConsoleColors conlog_Color(DefaultConsoleColor);
#ifdef __POSIX__
#ifdef _WIN32
#include "common/RedtapeWindows.h"
#else
#include <unistd.h>
static FILE* stdout_fp = stdout;
static bool checkSupportsColor()
{
if (!isatty(fileno(stdout_fp)))
return false;
char* term = getenv("TERM");
if (!term || (0 == strcmp(term, "dumb")))
return false;
return true; // Probably supports color
}
static bool supports_color = checkSupportsColor();
void Console_SetStdout(FILE* fp)
{
stdout_fp = fp;
}
#endif
// This function re-assigns the console log writer(s) to the specified target. It makes sure
// to flush any contents from the buffered console log (which typically accumulates due to
// log suspension during log file/window re-init operations) into the new log.
//
// Important! Only Assert and Null console loggers are allowed during C++ startup init (when
// the program or DLL first loads). Other log targets rely on the static buffer and a
// threaded mutex lock, which are only valid after C++ initialization has finished.
void Console_SetActiveHandler(const IConsoleWriter& writer, FILE* flushfp)
using namespace std::string_view_literals;
// Dummy objects, need to get rid of them...
ConsoleLogWriter<LOGLEVEL_INFO> Console;
ConsoleLogWriter<LOGLEVEL_DEV> DevCon;
#ifdef _DEBUG
ConsoleLogWriter<LOGLEVEL_DEBUG> DbgConWriter;
#else
NullLogWriter DbgConWriter;
#endif
#define TIMESTAMP_FORMAT_STRING "[{:10.4f}] "
#define TIMESTAMP_PRINTF_STRING "[%10.4f] "
namespace Log
{
pxAssertMsg(
(writer.WriteRaw != NULL) && (writer.DoWriteLn != NULL) &&
(writer.Newline != NULL) && (writer.SetTitle != NULL) &&
(writer.DoSetColor != NULL),
"Invalid IConsoleWriter object! All function pointer interfaces must be implemented.");
static void WriteToConsole(LOGLEVEL level, ConsoleColors color, std::string_view message);
static void WriteToDebug(LOGLEVEL level, ConsoleColors color, std::string_view message);
static void WriteToFile(LOGLEVEL level, ConsoleColors color, std::string_view message);
Console = writer;
DevConWriter = writer;
static void UpdateMaxLevel();
#ifdef PCSX2_DEBUG
DbgCon = writer;
static void ExecuteCallbacks(LOGLEVEL level, ConsoleColors color, std::string_view message);
static Common::Timer::Value s_start_timestamp = Common::Timer::GetCurrentValue();
static LOGLEVEL s_max_level = LOGLEVEL_NONE;
static LOGLEVEL s_console_level = LOGLEVEL_NONE;
static LOGLEVEL s_debug_level = LOGLEVEL_NONE;
static LOGLEVEL s_file_level = LOGLEVEL_NONE;
static LOGLEVEL s_host_level = LOGLEVEL_NONE;
static bool s_log_timestamps = true;
static FileSystem::ManagedCFilePtr s_file_handle;
static std::string s_file_path;
static std::mutex s_file_mutex;
static HostCallbackType s_host_callback;
#ifdef _WIN32
static HANDLE s_hConsoleStdIn = NULL;
static HANDLE s_hConsoleStdOut = NULL;
static HANDLE s_hConsoleStdErr = NULL;
#endif
} // namespace Log
float Log::GetCurrentMessageTime()
{
return static_cast<float>(Common::Timer::ConvertValueToSeconds(Common::Timer::GetCurrentValue() - s_start_timestamp));
}
__ri void Log::WriteToConsole(LOGLEVEL level, ConsoleColors color, std::string_view message)
{
static constexpr std::string_view s_ansi_color_codes[ConsoleColors_Count] = {
"\033[0m"sv, // default
"\033[30m\033[1m"sv, // black
"\033[32m"sv, // green
"\033[31m"sv, // red
"\033[34m"sv, // blue
"\033[35m"sv, // magenta
"\033[35m"sv, // orange (FIXME)
"\033[37m"sv, // gray
"\033[36m"sv, // cyan
"\033[33m"sv, // yellow
"\033[37m"sv, // white
"\033[30m\033[1m"sv, // strong black
"\033[31m\033[1m"sv, // strong red
"\033[32m\033[1m"sv, // strong green
"\033[34m\033[1m"sv, // strong blue
"\033[35m\033[1m"sv, // strong magenta
"\033[35m\033[1m"sv, // strong orange (FIXME)
"\033[37m\033[1m"sv, // strong gray
"\033[36m\033[1m"sv, // strong cyan
"\033[33m\033[1m"sv, // strong yellow
"\033[37m\033[1m"sv, // strong white
};
static constexpr size_t BUFFER_SIZE = 512;
SmallStackString<BUFFER_SIZE> buffer;
buffer.reserve(32 + message.length());
buffer.append(s_ansi_color_codes[color]);
if (s_log_timestamps)
buffer.append_fmt(TIMESTAMP_FORMAT_STRING, Log::GetCurrentMessageTime());
buffer.append(message);
buffer.append('\n');
#ifdef _WIN32
const HANDLE hOutput = (level <= LOGLEVEL_WARNING) ? s_hConsoleStdErr : s_hConsoleStdOut;
// Convert to UTF-16 first so Unicode characters display correctly. NT is going to do it anyway...
wchar_t wbuf[BUFFER_SIZE];
wchar_t* wmessage_buf = wbuf;
int wmessage_buflen = static_cast<int>(std::size(wbuf) - 1);
if (buffer.length() >= std::size(wbuf))
{
wmessage_buflen = static_cast<int>(buffer.length());
wmessage_buf = static_cast<wchar_t*>(std::malloc(static_cast<size_t>(wmessage_buflen) * sizeof(wchar_t)));
if (!wmessage_buf)
return;
}
const int wmessage_size = MultiByteToWideChar(CP_UTF8, 0, buffer.data(), static_cast<int>(buffer.length()), wmessage_buf, wmessage_buflen);
if (wmessage_size <= 0)
goto cleanup;
DWORD chars_written;
WriteConsoleW(hOutput, wmessage_buf, static_cast<DWORD>(wmessage_size), &chars_written, nullptr);
cleanup:
if (wmessage_buf != wbuf)
std::free(wmessage_buf);
#else
const int fd = (level <= LOGLEVEL_WARNING) ? STDERR_FILENO : STDOUT_FILENO;
write(fd, buffer.data(), buffer.length());
#endif
}
// Writes text to the Visual Studio Output window (Microsoft Windows only).
// On all other platforms this pipes to Stdout instead.
static void MSW_OutputDebugString(const char* text)
bool Log::IsConsoleOutputEnabled()
{
return (s_console_level > LOGLEVEL_NONE);
}
void Log::SetConsoleOutputLevel(LOGLEVEL level)
{
if (s_console_level == level)
return;
const bool was_enabled = (s_console_level > LOGLEVEL_NONE);
const bool now_enabled = (level > LOGLEVEL_NONE);
s_console_level = level;
UpdateMaxLevel();
if (was_enabled == now_enabled)
return;
// Worst that happens here is we write to a bad handle..
#if defined(_WIN32)
static constexpr auto enable_virtual_terminal_processing = [](HANDLE hConsole) {
DWORD old_mode;
if (!GetConsoleMode(hConsole, &old_mode))
return;
// already enabled?
if (old_mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)
return;
SetConsoleMode(hConsole, old_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
};
// On windows, no console is allocated by default on a windows based application
static bool console_was_allocated = false;
static HANDLE old_stdin = NULL;
static HANDLE old_stdout = NULL;
static HANDLE old_stderr = NULL;
if (now_enabled)
{
old_stdin = GetStdHandle(STD_INPUT_HANDLE);
old_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
old_stderr = GetStdHandle(STD_ERROR_HANDLE);
if (!old_stdout)
{
// Attach to the parent console if we're running from a command window
if (!AttachConsole(ATTACH_PARENT_PROCESS) && !AllocConsole())
return;
s_hConsoleStdIn = GetStdHandle(STD_INPUT_HANDLE);
s_hConsoleStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
s_hConsoleStdErr = GetStdHandle(STD_ERROR_HANDLE);
enable_virtual_terminal_processing(s_hConsoleStdOut);
enable_virtual_terminal_processing(s_hConsoleStdErr);
std::FILE* fp;
freopen_s(&fp, "CONIN$", "r", stdin);
freopen_s(&fp, "CONOUT$", "w", stdout);
freopen_s(&fp, "CONOUT$", "w", stderr);
console_was_allocated = true;
}
else
{
s_hConsoleStdIn = old_stdin;
s_hConsoleStdOut = old_stdout;
s_hConsoleStdErr = old_stderr;
}
}
else
{
if (console_was_allocated)
{
console_was_allocated = false;
std::FILE* fp;
freopen_s(&fp, "NUL:", "w", stderr);
freopen_s(&fp, "NUL:", "w", stdout);
freopen_s(&fp, "NUL:", "w", stdin);
SetStdHandle(STD_ERROR_HANDLE, old_stderr);
SetStdHandle(STD_OUTPUT_HANDLE, old_stdout);
SetStdHandle(STD_INPUT_HANDLE, old_stdin);
s_hConsoleStdIn = NULL;
s_hConsoleStdOut = NULL;
s_hConsoleStdErr = NULL;
FreeConsole();
}
}
#endif
}
__ri void Log::WriteToDebug(LOGLEVEL level, ConsoleColors color, std::string_view message)
{
#ifdef _WIN32
static bool hasDebugger = IsDebuggerPresent();
if (hasDebugger)
OutputDebugStringA(text);
#else
fputs(text, stdout_fp);
fflush(stdout_fp);
#endif
}
static constexpr size_t BUFFER_SIZE = 512;
// --------------------------------------------------------------------------------------
// ConsoleNull
// --------------------------------------------------------------------------------------
static void ConsoleNull_SetTitle(const char* title) {}
static void ConsoleNull_DoSetColor(ConsoleColors color) {}
static void ConsoleNull_Newline() {}
static void ConsoleNull_DoWrite(const char* fmt) {}
static void ConsoleNull_DoWriteLn(const char* fmt) {}
const IConsoleWriter ConsoleWriter_Null =
// Convert to UTF-16 first so Unicode characters display correctly. NT is going to do it anyway...
wchar_t wbuf[BUFFER_SIZE];
wchar_t* wmessage_buf = wbuf;
int wmessage_buflen = static_cast<int>(std::size(wbuf) - 1);
if (message.length() >= std::size(wbuf))
{
ConsoleNull_DoWrite,
ConsoleNull_DoWriteLn,
ConsoleNull_DoSetColor,
ConsoleNull_DoWrite,
ConsoleNull_Newline,
ConsoleNull_SetTitle,
0, // instance-level indentation (should always be 0)
};
// --------------------------------------------------------------------------------------
// Console_Stdout
// --------------------------------------------------------------------------------------
#if defined(__POSIX__)
static __fi const char* GetLinuxConsoleColor(ConsoleColors color)
{
switch (color)
{
case Color_Black:
case Color_StrongBlack:
return "\033[30m\033[1m";
case Color_Red:
return "\033[31m";
case Color_StrongRed:
return "\033[31m\033[1m";
case Color_Green:
return "\033[32m";
case Color_StrongGreen:
return "\033[32m\033[1m";
case Color_Yellow:
return "\033[33m";
case Color_StrongYellow:
return "\033[33m\033[1m";
case Color_Blue:
return "\033[34m";
case Color_StrongBlue:
return "\033[34m\033[1m";
// No orange, so use magenta.
case Color_Orange:
case Color_Magenta:
return "\033[35m";
case Color_StrongOrange:
case Color_StrongMagenta:
return "\033[35m\033[1m";
case Color_Cyan:
return "\033[36m";
case Color_StrongCyan:
return "\033[36m\033[1m";
// Use 'white' instead of grey.
case Color_Gray:
case Color_White:
return "\033[37m";
case Color_StrongGray:
case Color_StrongWhite:
return "\033[37m\033[1m";
// On some other value being passed, clear any formatting.
case Color_Default:
default:
return "\033[0m";
wmessage_buflen = static_cast<int>(message.length());
wmessage_buf = static_cast<wchar_t*>(std::malloc((static_cast<size_t>(wmessage_buflen) + 2) * sizeof(wchar_t)));
if (!wmessage_buf)
return;
}
}
int wmessage_size = 0;
if (!message.empty()) [[likely]]
{
wmessage_size = MultiByteToWideChar(CP_UTF8, 0, message.data(), static_cast<int>(message.length()), wmessage_buf, wmessage_buflen);
if (wmessage_size <= 0)
goto cleanup;
}
wmessage_buf[wmessage_size++] = L'\n';
wmessage_buf[wmessage_size++] = 0;
OutputDebugStringW(wmessage_buf);
cleanup:
if (wmessage_buf != wbuf)
std::free(wmessage_buf);
#endif
// One possible default write action at startup and shutdown is to use the stdout.
static void ConsoleStdout_DoWrite(const char* fmt)
{
MSW_OutputDebugString(fmt);
}
// Default write action at startup and shutdown is to use the stdout.
static void ConsoleStdout_DoWriteLn(const char* fmt)
bool Log::IsDebugOutputEnabled()
{
MSW_OutputDebugString(fmt);
MSW_OutputDebugString("\n");
return (s_console_level > LOGLEVEL_NONE);
}
static void ConsoleStdout_Newline()
void Log::SetDebugOutputLevel(LOGLEVEL level)
{
MSW_OutputDebugString("\n");
s_debug_level = level;
UpdateMaxLevel();
}
static void ConsoleStdout_DoSetColor(ConsoleColors color)
__ri void Log::WriteToFile(LOGLEVEL level, ConsoleColors color, std::string_view message)
{
#if defined(__POSIX__)
if (!supports_color)
std::unique_lock lock(s_file_mutex);
if (!s_file_handle) [[unlikely]]
return;
fprintf(stdout_fp, "\033[0m%s", GetLinuxConsoleColor(color));
fflush(stdout_fp);
#endif
}
static void ConsoleStdout_SetTitle(const char* title)
{
#if defined(__POSIX__)
if (supports_color)
fputs("\033]0;", stdout_fp);
fputs(title, stdout_fp);
if (supports_color)
fputs("\007", stdout_fp);
#endif
}
const IConsoleWriter ConsoleWriter_Stdout =
if (!message.empty()) [[likely]]
{
ConsoleStdout_DoWrite, // Writes without newlines go to buffer to avoid error log spam.
ConsoleStdout_DoWriteLn,
ConsoleStdout_DoSetColor,
ConsoleNull_DoWrite, // writes from re-piped stdout are ignored here, lest we create infinite loop hell >_<
ConsoleStdout_Newline,
ConsoleStdout_SetTitle,
0, // instance-level indentation (should always be 0)
};
// --------------------------------------------------------------------------------------
// ConsoleAssert
// --------------------------------------------------------------------------------------
static void ConsoleAssert_DoWrite(const char* fmt)
{
pxFailRel("Console class has not been initialized");
}
static void ConsoleAssert_DoWriteLn(const char* fmt)
{
pxFailRel("Console class has not been initialized");
}
const IConsoleWriter ConsoleWriter_Assert =
{
ConsoleAssert_DoWrite,
ConsoleAssert_DoWriteLn,
ConsoleNull_DoSetColor,
ConsoleNull_DoWrite,
ConsoleNull_Newline,
ConsoleNull_SetTitle,
0, // instance-level indentation (should always be 0)
};
// =====================================================================================================
// IConsoleWriter (implementations)
// =====================================================================================================
// (all non-virtual members that do common work and then pass the result through DoWrite
// or DoWriteLn)
// Parameters:
// glob_indent - this parameter is used to specify a global indentation setting. It is used by
// WriteLn function, but defaults to 0 for Warning and Error calls. Local indentation always
// applies to all writes.
std::string IConsoleWriter::_addIndentation(const std::string& src, int glob_indent = 0) const
{
const int indent = glob_indent + _imm_indentation;
std::string indentStr;
for (int i = 0; i < indent; i++)
indentStr += '\t';
std::string result;
result.reserve(src.length() + 16 * indent);
result.append(indentStr);
result.append(src);
std::string::size_type pos = result.find('\n');
while (pos != std::string::npos)
{
result.insert(pos + 1, indentStr);
pos = result.find('\n', pos + 1);
}
return result;
}
// Sets the indentation to be applied to all WriteLn's. The indentation is added to the
// primary write, and to any newlines specified within the write. Note that this applies
// to calls to WriteLn *only* -- calls to Write bypass the indentation parser.
const IConsoleWriter& IConsoleWriter::SetIndent(int tabcount) const
{
conlog_Indent += tabcount;
pxAssert(conlog_Indent >= 0);
return *this;
}
IConsoleWriter IConsoleWriter::Indent(int tabcount) const
{
IConsoleWriter retval = *this;
retval._imm_indentation = tabcount;
return retval;
}
// Changes the active console color.
// This color will be unset by calls to colored text methods
// such as ErrorMsg and Notice.
const IConsoleWriter& IConsoleWriter::SetColor(ConsoleColors color) const
{
// Ignore current color requests since, well, the current color is already set. ;)
if (color == Color_Current)
return *this;
pxAssertMsg((color > Color_Current) && (color < ConsoleColors_Count), "Invalid ConsoleColor specified.");
if (conlog_Color != color)
DoSetColor(conlog_Color = color);
return *this;
}
ConsoleColors IConsoleWriter::GetColor() const
{
return conlog_Color;
}
// Restores the console color to default (usually black, or low-intensity white if the console uses a black background)
const IConsoleWriter& IConsoleWriter::ClearColor() const
{
if (conlog_Color != DefaultConsoleColor)
DoSetColor(conlog_Color = DefaultConsoleColor);
return *this;
}
// --------------------------------------------------------------------------------------
// ASCII/UTF8 (char*)
// --------------------------------------------------------------------------------------
bool IConsoleWriter::FormatV(const char* fmt, va_list args) const
{
// TODO: Make this less rubbish
if ((_imm_indentation + conlog_Indent) > 0)
{
DoWriteLn(_addIndentation(StringUtil::StdStringFromFormatV(fmt, args), conlog_Indent).c_str());
if (s_log_timestamps)
{
std::fprintf(s_file_handle.get(), TIMESTAMP_PRINTF_STRING "%.*s\n", GetCurrentMessageTime(),
static_cast<int>(message.size()), message.data());
}
else
{
std::fprintf(s_file_handle.get(), "%.*s\n", static_cast<int>(message.size()), message.data());
}
}
else
{
DoWriteLn(StringUtil::StdStringFromFormatV(fmt, args).c_str());
if (s_log_timestamps)
{
std::fprintf(s_file_handle.get(), TIMESTAMP_PRINTF_STRING "\n", GetCurrentMessageTime());
}
else
{
std::fputc('\n', s_file_handle.get());
}
}
return false;
std::fflush(s_file_handle.get());
}
bool IConsoleWriter::WriteLn(const char* fmt, ...) const
bool Log::IsFileOutputEnabled()
{
va_list args;
va_start(args, fmt);
FormatV(fmt, args);
va_end(args);
return false;
return (s_file_level > LOGLEVEL_NONE);
}
bool IConsoleWriter::WriteLn(ConsoleColors color, const char* fmt, ...) const
bool Log::SetFileOutputLevel(LOGLEVEL level, std::string path)
{
va_list args;
va_start(args, fmt);
ConsoleColorScope cs(color);
FormatV(fmt, args);
va_end(args);
std::unique_lock lock(s_file_mutex);
return false;
}
bool IConsoleWriter::Error(const char* fmt, ...) const
{
va_list args;
va_start(args, fmt);
ConsoleColorScope cs(Color_StrongRed);
FormatV(fmt, args);
va_end(args);
return false;
}
bool IConsoleWriter::Warning(const char* fmt, ...) const
{
va_list args;
va_start(args, fmt);
ConsoleColorScope cs(Color_StrongOrange);
FormatV(fmt, args);
va_end(args);
return false;
}
bool IConsoleWriter::WriteLn(ConsoleColors color, const std::string& str) const
{
ConsoleColorScope cs(color);
return WriteLn(str);
}
bool IConsoleWriter::WriteLn(const std::string& str) const
{
// TODO: Make this less rubbish
if ((_imm_indentation + conlog_Indent) > 0)
const bool was_enabled = (s_file_level > LOGLEVEL_NONE);
const bool new_enabled = (level > LOGLEVEL_NONE && !path.empty());
if (was_enabled != new_enabled || (new_enabled && path == s_file_path))
{
DoWriteLn(_addIndentation(str, conlog_Indent).c_str());
if (new_enabled)
{
if (!s_file_handle || s_file_path != path)
{
s_file_handle.reset();
s_file_handle = FileSystem::OpenManagedCFile(path.c_str(), "wb");
if (s_file_handle)
{
s_file_path = std::move(path);
}
else
{
s_file_path = {};
if (IsConsoleOutputEnabled())
WriteToConsole(LOGLEVEL_ERROR, Color_StrongRed, TinyString::from_fmt("Failed to open log file '{}'", path));
}
}
}
else
{
s_file_handle.reset();
s_file_path = {};
}
}
s_file_level = s_file_handle ? level : LOGLEVEL_NONE;
return IsFileOutputEnabled();
}
std::FILE* Log::GetFileLogHandle()
{
std::unique_lock lock(s_file_mutex);
return s_file_handle.get();
}
bool Log::IsHostOutputEnabled()
{
return (s_host_level > LOGLEVEL_NONE);
}
void Log::SetHostOutputLevel(LOGLEVEL level, HostCallbackType callback)
{
s_host_callback = callback;
s_host_level = callback ? level : LOGLEVEL_NONE;
UpdateMaxLevel();
}
bool Log::AreTimestampsEnabled()
{
return s_log_timestamps;
}
void Log::SetTimestampsEnabled(bool enabled)
{
s_log_timestamps = enabled;
}
LOGLEVEL Log::GetMaxLevel()
{
return s_max_level;
}
__ri void Log::UpdateMaxLevel()
{
s_max_level = std::max(s_console_level, std::max(s_debug_level, std::max(s_file_level, s_host_level)));
}
void Log::ExecuteCallbacks(LOGLEVEL level, ConsoleColors color, std::string_view message)
{
// TODO: Cache the message time.
// Split newlines into separate messages.
std::string_view::size_type start_pos = 0;
if (std::string_view::size_type end_pos = message.find('\n'); end_pos != std::string::npos) [[unlikely]]
{
for (;;)
{
std::string_view message_line;
if (start_pos != end_pos)
message_line = message.substr(start_pos, (end_pos == std::string_view::npos) ? end_pos : end_pos - start_pos);
ExecuteCallbacks(level, color, message_line);
if (end_pos == std::string_view::npos)
return;
start_pos = end_pos + 1;
end_pos = message.find('\n', start_pos);
}
return;
}
pxAssert(level > LOGLEVEL_NONE);
if (level <= s_console_level)
WriteToConsole(level, color, message);
if (level <= s_debug_level)
WriteToDebug(level, color, message);
if (level <= s_file_level)
WriteToFile(level, color, message);
if (level <= s_host_level)
{
// double check in case of race here
const HostCallbackType callback = s_host_callback;
if (callback)
s_host_callback(level, color, message);
}
}
void Log::Write(LOGLEVEL level, ConsoleColors color, std::string_view message)
{
if (level > s_max_level)
return;
ExecuteCallbacks(level, color, message);
}
void Log::Writef(LOGLEVEL level, ConsoleColors color, const char* format, ...)
{
std::va_list ap;
va_start(ap, format);
Writev(level, color, format, ap);
va_end(ap);
}
void Log::Writev(LOGLEVEL level, ConsoleColors color, const char* format, va_list ap)
{
if (level > s_max_level)
return;
std::va_list ap_copy;
va_copy(ap_copy, ap);
#ifdef _WIN32
const u32 required_size = static_cast<u32>(_vscprintf(format, ap_copy));
#else
const u32 required_size = std::vsnprintf(nullptr, 0, format, ap_copy);
#endif
va_end(ap_copy);
if (required_size < 512)
{
char buffer[512];
const int len = std::vsnprintf(buffer, std::size(buffer), format, ap);
if (len > 0)
ExecuteCallbacks(level, color, std::string_view(buffer, static_cast<size_t>(len)));
}
else
{
DoWriteLn(str.c_str());
}
return false;
}
bool IConsoleWriter::Error(const std::string& str) const
{
return WriteLn(Color_StrongRed, str);
}
bool IConsoleWriter::Warning(const std::string& str) const
{
return WriteLn(Color_StrongOrange, str);
}
// --------------------------------------------------------------------------------------
// ConsoleColorScope / ConsoleIndentScope
// --------------------------------------------------------------------------------------
ConsoleColorScope::ConsoleColorScope(ConsoleColors newcolor)
{
m_IsScoped = false;
m_newcolor = newcolor;
EnterScope();
}
ConsoleColorScope::~ConsoleColorScope()
{
LeaveScope();
}
void ConsoleColorScope::EnterScope()
{
if (!m_IsScoped)
{
m_old_color = Console.GetColor();
Console.SetColor(m_newcolor);
m_IsScoped = true;
char* buffer = new char[required_size + 1];
const int len = std::vsnprintf(buffer, required_size + 1, format, ap);
if (len > 0)
ExecuteCallbacks(level, color, std::string_view(buffer, static_cast<size_t>(len)));
delete[] buffer;
}
}
void ConsoleColorScope::LeaveScope()
void Log::WriteFmtArgs(LOGLEVEL level, ConsoleColors color, fmt::string_view fmt, fmt::format_args args)
{
m_IsScoped = m_IsScoped && (Console.SetColor(m_old_color), false);
}
ConsoleIndentScope::ConsoleIndentScope(int tabs)
{
m_IsScoped = false;
m_amount = tabs;
EnterScope();
}
ConsoleIndentScope::~ConsoleIndentScope()
{
LeaveScope();
}
void ConsoleIndentScope::EnterScope()
{
m_IsScoped = m_IsScoped || (Console.SetIndent(m_amount), true);
}
void ConsoleIndentScope::LeaveScope()
{
m_IsScoped = m_IsScoped && (Console.SetIndent(-m_amount), false);
}
ConsoleAttrScope::ConsoleAttrScope(ConsoleColors newcolor, int indent)
{
m_old_color = Console.GetColor();
Console.SetIndent(m_tabsize = indent);
Console.SetColor(newcolor);
}
ConsoleAttrScope::~ConsoleAttrScope()
{
Console.SetColor(m_old_color);
Console.SetIndent(-m_tabsize);
}
// --------------------------------------------------------------------------------------
// Default Writer for C++ init / startup:
// --------------------------------------------------------------------------------------
// Currently all build types default to Stdout, which is very functional on Linux but not
// always so useful on Windows (which itself lacks a proper stdout console without using
// platform specific code). Under windows Stdout will attempt to write to the IDE Debug
// console, if one is available (such as running pcsx2 via MSVC). If not available, then
// the log message will pretty much be lost into the ether.
//
#define _DefaultWriter_ ConsoleWriter_Stdout
IConsoleWriter Console = _DefaultWriter_;
IConsoleWriter DevConWriter = _DefaultWriter_;
bool DevConWriterEnabled = false;
#ifdef PCSX2_DEBUG
IConsoleWriter DbgConWriter = _DefaultWriter_;
#endif
NullConsoleWriter NullCon = {};
// --------------------------------------------------------------------------------------
// ConsoleLogSource (implementations)
// --------------------------------------------------------------------------------------
// Writes to the console using the specified color. This overrides the default color setting
// for this log.
bool ConsoleLogSource::WriteV(ConsoleColors color, const char* fmt, va_list list) const
{
ConsoleColorScope cs(color);
Console.WriteLn(StringUtil::StdStringFromFormatV(fmt, list));
return false;
}
// Writes to the console using the source's default color. Note that the source's default
// color will always be used, thus ConsoleColorScope() will not be effectual unless the
// console's default color is Color_Default.
bool ConsoleLogSource::WriteV(const char* fmt, va_list list) const
{
WriteV(DefaultColor, fmt, list);
return false;
if (level > s_max_level)
return;
fmt::memory_buffer buffer;
fmt::vformat_to(std::back_inserter(buffer), fmt, args);
ExecuteCallbacks(level, color, std::string_view(buffer.data(), buffer.size()));
}

View File

@ -1,16 +1,19 @@
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: LGPL-3.0+
#pragma once
#include "Pcsx2Defs.h"
#include "fmt/core.h"
#include <cstdarg>
#include <string>
// TODO: This whole thing needs to get ripped out.
enum ConsoleColors
{
Color_Current = -1,
Color_Default = 0,
Color_Black,
@ -42,191 +45,145 @@ enum ConsoleColors
ConsoleColors_Count
};
static const ConsoleColors DefaultConsoleColor = Color_Default;
// ----------------------------------------------------------------------------------------
// IConsoleWriter -- For printing messages to the console.
// ----------------------------------------------------------------------------------------
// General ConsoleWrite Threading Guideline:
// PCSX2 is a threaded environment and multiple threads can write to the console asynchronously.
// Individual calls to ConsoleWriter APIs will be written in atomic fashion, however "partial"
// logs may end up interrupted by logs on other threads. This is usually not a problem for
// WriteLn, but may be undesirable for typical uses of Write. In cases where you want to
// print multi-line blocks of uninterrupted logs, compound the entire log into a single large
// string and issue that to WriteLn.
//
struct IConsoleWriter
enum LOGLEVEL
{
// A direct console write, without tabbing or newlines. Useful to devs who want to do quick
// logging of various junk; but should *not* be used in production code due.
void(* WriteRaw)(const char* fmt);
LOGLEVEL_NONE, // Silences all log traffic
LOGLEVEL_ERROR,
LOGLEVEL_WARNING,
LOGLEVEL_INFO,
LOGLEVEL_DEV,
LOGLEVEL_DEBUG,
LOGLEVEL_TRACE,
// WriteLn implementation for internal use only. Bypasses tabbing, prefixing, and other
// formatting.
void(* DoWriteLn)(const char* fmt);
// SetColor implementation for internal use only.
void(* DoSetColor)(ConsoleColors color);
// Special implementation of DoWrite that's pretty much for MSVC use only.
// All implementations should map to DoWrite, except Stdio which should map to Null.
// (This avoids circular/recursive stdio output)
void(* DoWriteFromStdout)(const char* fmt);
void(* Newline)();
void(* SetTitle)(const char* title);
// internal value for indentation of individual lines. Use the Indent() member to invoke.
int _imm_indentation;
// For internal use only.
std::string _addIndentation(const std::string& src, int glob_indent) const;
// ----------------------------------------------------------------------------
// Public members; call these to print stuff to console!
//
// All functions always return false. Return value is provided only so that we can easily
// disable logs at compile time using the "0&&action" macro trick.
ConsoleColors GetColor() const;
const IConsoleWriter& SetColor(ConsoleColors color) const;
const IConsoleWriter& ClearColor() const;
const IConsoleWriter& SetIndent(int tabcount = 1) const;
IConsoleWriter Indent(int tabcount = 1) const;
bool FormatV(const char* fmt, va_list args) const;
bool WriteLn(ConsoleColors color, const char* fmt, ...) const;
bool WriteLn(const char* fmt, ...) const;
bool Error(const char* fmt, ...) const;
bool Warning(const char* fmt, ...) const;
bool WriteLn(ConsoleColors color, const std::string& str) const;
bool WriteLn(const std::string& str) const;
bool Error(const std::string& str) const;
bool Warning(const std::string& str) const;
LOGLEVEL_COUNT,
};
// --------------------------------------------------------------------------------------
// NullConsoleWriter
// --------------------------------------------------------------------------------------
// Used by Release builds for Debug and Devel writes (DbgCon / DevCon). Inlines to NOPs. :)
//
struct NullConsoleWriter
// TODO: Move this elsewhere, add channels.
namespace Log
{
void WriteRaw(const char* fmt) {}
void DoWriteLn(const char* fmt) {}
void DoSetColor(ConsoleColors color) {}
void DoWriteFromStdout(const char* fmt) {}
void Newline() {}
void SetTitle(const char* title) {}
// log message callback type
using HostCallbackType = void (*)(LOGLEVEL level, ConsoleColors color, std::string_view message);
// returns the time in seconds since the start of the process
float GetCurrentMessageTime();
ConsoleColors GetColor() const { return Color_Current; }
const NullConsoleWriter& SetColor(ConsoleColors color) const { return *this; }
const NullConsoleWriter& ClearColor() const { return *this; }
const NullConsoleWriter& SetIndent(int tabcount = 1) const { return *this; }
// adds a standard console output
bool IsConsoleOutputEnabled();
void SetConsoleOutputLevel(LOGLEVEL level);
NullConsoleWriter Indent(int tabcount = 1) const { return NullConsoleWriter(); }
// adds a debug console output
bool IsDebugOutputEnabled();
void SetDebugOutputLevel(LOGLEVEL level);
bool FormatV(const char* fmt, va_list args) const { return false; }
bool WriteLn(ConsoleColors color, const char* fmt, ...) const { return false; }
bool WriteLn(const char* fmt, ...) const { return false; }
bool Error(const char* fmt, ...) const { return false; }
bool Warning(const char* fmt, ...) const { return false; }
// adds a file output
bool IsFileOutputEnabled();
bool SetFileOutputLevel(LOGLEVEL level, std::string path);
// returns the log file, this is really dangerous to use if it changes...
std::FILE* GetFileLogHandle();
// adds host output
bool IsHostOutputEnabled();
void SetHostOutputLevel(LOGLEVEL level, HostCallbackType callback);
// sets logging timestamps
bool AreTimestampsEnabled();
void SetTimestampsEnabled(bool enabled);
// Returns the current global filtering level.
LOGLEVEL GetMaxLevel();
// writes a message to the log
void Write(LOGLEVEL level, ConsoleColors color, std::string_view message);
void Writef(LOGLEVEL level, ConsoleColors color, const char* format, ...);
void Writev(LOGLEVEL level, ConsoleColors color, const char* format, va_list ap);
void WriteFmtArgs(LOGLEVEL level, ConsoleColors color, fmt::string_view fmt, fmt::format_args args);
template <typename... T>
__fi static void Write(LOGLEVEL level, ConsoleColors color, fmt::format_string<T...> fmt, T&&... args)
{
// Avoid arg packing if filtered.
if (level <= GetMaxLevel())
return WriteFmtArgs(level, color, fmt, fmt::make_format_args(args...));
}
} // namespace Log
// Adapter classes to handle old code.
template <LOGLEVEL level>
struct ConsoleLogWriter
{
__fi static void Error(std::string_view str) { Log::Write(level, Color_StrongRed, str); }
__fi static void Warning(std::string_view str) { Log::Write(level, Color_StrongOrange, str); }
__fi static void WriteLn(std::string_view str) { Log::Write(level, Color_Default, str); }
__fi static void WriteLn(ConsoleColors color, std::string_view str) { Log::Write(level, color, str); }
__fi static void WriteLn() { Log::Write(level, Color_Default, std::string_view()); }
__fi static void FormatV(const char* format, va_list ap) { Log::Writev(level, Color_Default, format, ap); }
__fi static void FormatV(ConsoleColors color, const char* format, va_list ap) { Log::Writev(level, color, format, ap); }
#define MAKE_PRINTF_CONSOLE_WRITER(color) \
do \
{ \
std::va_list ap; \
va_start(ap, format); \
Log::Writev(level, color, format, ap); \
va_end(ap); \
} while (0)
// clang-format off
__fi static void Error(const char* format, ...) { MAKE_PRINTF_CONSOLE_WRITER(Color_StrongRed); }
__fi static void Warning(const char* format, ...) { MAKE_PRINTF_CONSOLE_WRITER(Color_StrongOrange); }
__fi static void WriteLn(const char* format, ...) { MAKE_PRINTF_CONSOLE_WRITER(Color_Default); }
__fi static void WriteLn(ConsoleColors color, const char* format, ...) { MAKE_PRINTF_CONSOLE_WRITER(color); }
// clang-format on
#undef MAKE_PRINTF_CONSOLE_WRITER
#define MAKE_FMT_CONSOLE_WRITER(color) do \
{ \
if (level <= Log::GetMaxLevel()) \
Log::WriteFmtArgs(level, color, fmt, fmt::make_format_args(args...)); \
} \
while (0)
// clang-format off
template<typename... T> __fi static void ErrorFmt(fmt::format_string<T...> fmt, T&&... args) { MAKE_FMT_CONSOLE_WRITER(Color_StrongRed); }
template<typename... T> __fi static void WarningFmt(fmt::format_string<T...> fmt, T&&... args) { MAKE_FMT_CONSOLE_WRITER(Color_StrongOrange); }
template<typename... T> __fi static void WriteLnFmt(fmt::format_string<T...> fmt, T&&... args) { MAKE_FMT_CONSOLE_WRITER(Color_Default); }
template<typename... T> __fi static void WriteLnFmt(ConsoleColors color, fmt::format_string<T...> fmt, T&&... args) { MAKE_FMT_CONSOLE_WRITER(color); }
// clang-format on
#undef MAKE_FMT_CONSOLE_WRITER
};
// --------------------------------------------------------------------------------------
// ConsoleIndentScope
// --------------------------------------------------------------------------------------
// Provides a scoped indentation of the IConsoleWriter interface for the current thread.
// Any console writes performed from this scope will be indented by the specified number
// of tab characters.
//
// Scoped Object Notes: Like most scoped objects, this is intended to be used as a stack
// or temporary object only. Using it in a situation where the object's lifespan out-lives
// a scope will almost certainly result in unintended /undefined side effects.
//
class ConsoleIndentScope
struct NullLogWriter
{
DeclareNoncopyableObject(ConsoleIndentScope);
// clang-format off
__fi static bool Error(std::string_view str) { return false; }
__fi static bool Warning(std::string_view str) { return false; }
__fi static bool WriteLn(std::string_view str) { return false; }
__fi static bool WriteLn(ConsoleColors color, std::string_view str) { return false; }
__fi static bool WriteLn() { return false; }
protected:
int m_amount;
bool m_IsScoped;
__fi static bool Error(const char* format, ...) { return false; }
__fi static bool Warning(const char* format, ...) { return false; }
__fi static bool WriteLn(const char* format, ...) { return false; }
__fi static bool WriteLn(ConsoleColors color, const char* format, ...) { return false; }
public:
// Constructor: The specified number of tabs will be appended to the current indentation
// setting. The tabs will be unrolled when the object leaves scope or is destroyed.
ConsoleIndentScope(int tabs = 1);
virtual ~ConsoleIndentScope();
void EnterScope();
void LeaveScope();
template<typename... T> __fi static bool ErrorFmt(fmt::format_string<T...> fmt, T&&... args) { return false; }
template<typename... T> __fi static bool WarningFmt(fmt::format_string<T...> fmt, T&&... args) { return false; }
template<typename... T> __fi static bool WriteLnFmt(fmt::format_string<T...> fmt, T&&... args) { return false; }
template<typename... T> __fi static bool WriteLnFmt(ConsoleColors color, fmt::format_string<T...> fmt, T&&... args) { return false; }
// clang-format on
};
// --------------------------------------------------------------------------------------
// ConsoleColorScope
// --------------------------------------------------------------------------------------
class ConsoleColorScope
{
DeclareNoncopyableObject(ConsoleColorScope);
extern ConsoleLogWriter<LOGLEVEL_INFO> Console;
extern ConsoleLogWriter<LOGLEVEL_DEV> DevCon;
protected:
ConsoleColors m_newcolor;
ConsoleColors m_old_color;
bool m_IsScoped;
public:
ConsoleColorScope(ConsoleColors newcolor);
virtual ~ConsoleColorScope();
void EnterScope();
void LeaveScope();
};
// --------------------------------------------------------------------------------------
// ConsoleAttrScope
// --------------------------------------------------------------------------------------
// Applies both color and tab attributes in a single object constructor.
//
class ConsoleAttrScope
{
DeclareNoncopyableObject(ConsoleAttrScope);
protected:
ConsoleColors m_old_color;
int m_tabsize;
public:
ConsoleAttrScope(ConsoleColors newcolor, int indent = 0);
virtual ~ConsoleAttrScope();
};
extern IConsoleWriter Console;
#if defined(__POSIX__)
extern void Console_SetStdout(FILE* fp);
#endif
extern void Console_SetActiveHandler(const IConsoleWriter& writer, FILE* flushfp = NULL);
extern const IConsoleWriter ConsoleWriter_Null;
extern const IConsoleWriter ConsoleWriter_Stdout;
extern const IConsoleWriter ConsoleWriter_Assert;
extern NullConsoleWriter NullCon;
extern IConsoleWriter DevConWriter;
extern bool DevConWriterEnabled;
#ifdef PCSX2_DEVBUILD
#define DevCon DevConWriter
#else
#define DevCon DevConWriterEnabled&& DevConWriter
#endif
#ifdef PCSX2_DEBUG
extern IConsoleWriter DbgConWriter;
#ifdef _DEBUG
extern ConsoleLogWriter<LOGLEVEL_DEBUG> DbgConWriter;
#define DbgCon DbgConWriter
#else
#define DbgCon 0 && NullCon
extern NullLogWriter DbgConWriter;
#define DbgCon 0 && DbgConWriter
#endif

View File

@ -183,7 +183,15 @@ public:
return false;
}
bool WriteV(const char* fmt, va_list list) const;
bool WriteV(const char* fmt, va_list list) const
{
Console.FormatV(fmt, list);
return false;
}
bool WriteV(ConsoleColors color, const char* fmt, va_list list) const;
bool WriteV(ConsoleColors color, const char* fmt, va_list list) const
{
Console.FormatV(color, fmt, list);
return false;
}
};

View File

@ -35,7 +35,6 @@
#include "pcsx2/INISettingsInterface.h"
#include "pcsx2/ImGui/ImGuiManager.h"
#include "pcsx2/Input/InputManager.h"
#include "pcsx2/LogSink.h"
#include "pcsx2/MTGS.h"
#include "pcsx2/SIO/Pad/Pad.h"
#include "pcsx2/PerformanceMetrics.h"
@ -442,7 +441,7 @@ void GSRunner::InitializeConsole()
const char* var = std::getenv("PCSX2_NOCONSOLE");
s_no_console = (var && StringUtil::FromChars<bool>(var).value_or(false));
if (!s_no_console)
LogSink::InitializeEarlyConsole();
Log::SetConsoleOutputLevel(LOGLEVEL_DEBUG);
}
bool GSRunner::ParseCommandLineArgs(int argc, char* argv[], VMBootParameters& params)
@ -568,7 +567,7 @@ bool GSRunner::ParseCommandLineArgs(int argc, char* argv[], VMBootParameters& pa
{
// disable timestamps, since we want to be able to diff the logs
Console.WriteLn("Logging to %s...", logfile);
LogSink::SetFileLogPath(logfile);
VMManager::Internal::SetFileLogPath(logfile);
s_settings_interface.SetBoolValue("Logging", "EnableFileLogging", true);
s_settings_interface.SetBoolValue("Logging", "EnableTimestamps", false);
}
@ -698,7 +697,6 @@ int main(int argc, char* argv[])
VMManager::Internal::CPUThreadShutdown();
GSRunner::DestroyPlatformWindow();
LogSink::CloseFileLog();
return EXIT_SUCCESS;
}

View File

@ -27,7 +27,6 @@
#include "pcsx2/GSDumpReplayer.h"
#include "pcsx2/GameList.h"
#include "pcsx2/Host.h"
#include "pcsx2/LogSink.h"
#include "pcsx2/MTGS.h"
#include "pcsx2/PerformanceMetrics.h"
#include "pcsx2/Recording/InputRecording.h"

View File

@ -26,7 +26,6 @@
#include "pcsx2/ImGui/ImGuiManager.h"
#include "pcsx2/ImGui/ImGuiOverlays.h"
#include "pcsx2/Input/InputManager.h"
#include "pcsx2/LogSink.h"
#include "pcsx2/MTGS.h"
#include "pcsx2/PerformanceMetrics.h"
#include "pcsx2/SysForwardDefs.h"
@ -64,6 +63,7 @@ EmuThread* g_emu_thread = nullptr;
//////////////////////////////////////////////////////////////////////////
namespace QtHost
{
static void InitializeEarlyConsole();
static void PrintCommandLineVersion();
static void PrintCommandLineHelp(const std::string_view& progname);
static std::shared_ptr<VMBootParameters>& AutoBoot(std::shared_ptr<VMBootParameters>& autoboot);
@ -1240,7 +1240,8 @@ bool QtHost::InitializeConfig()
s_run_setup_wizard =
s_run_setup_wizard || s_base_settings_interface->GetBoolValue("UI", "SetupWizardIncomplete", false);
LogSink::SetBlockSystemConsole(QtHost::InNoGUIMode());
// TODO: -nogui console block?
VMManager::Internal::LoadStartupSettings();
InstallTranslator(nullptr);
return true;
@ -1575,12 +1576,27 @@ static void SignalHandler(int signal)
#endif
}
#ifdef _WIN32
static BOOL WINAPI ConsoleCtrlHandler(DWORD dwCtrlType)
{
if (dwCtrlType != CTRL_C_EVENT)
return FALSE;
SignalHandler(SIGTERM);
return TRUE;
}
#endif
void QtHost::HookSignals()
{
std::signal(SIGINT, SignalHandler);
std::signal(SIGTERM, SignalHandler);
#ifdef __linux__
#if defined(_WIN32)
SetConsoleCtrlHandler(ConsoleCtrlHandler, FALSE);
#elif defined(__linux__)
// Ignore SIGCHLD by default on Linux, since we kick off aplay asynchronously.
struct sigaction sa_chld = {};
sigemptyset(&sa_chld.sa_mask);
@ -1589,9 +1605,14 @@ void QtHost::HookSignals()
#endif
}
void QtHost::InitializeEarlyConsole()
{
Log::SetConsoleOutputLevel(LOGLEVEL_DEBUG);
}
void QtHost::PrintCommandLineVersion()
{
LogSink::InitializeEarlyConsole();
InitializeEarlyConsole();
std::fprintf(stderr, "%s\n", (GetAppNameAndVersion() + GetAppConfigSuffix()).toUtf8().constData());
std::fprintf(stderr, "https://pcsx2.net/\n");
std::fprintf(stderr, "\n");
@ -1715,7 +1736,7 @@ bool QtHost::ParseCommandLineOptions(const QStringList& args, std::shared_ptr<VM
}
else if (CHECK_ARG_PARAM(QStringLiteral("-logfile")))
{
LogSink::SetFileLogPath((++it)->toStdString());
VMManager::Internal::SetFileLogPath((++it)->toStdString());
continue;
}
else if (CHECK_ARG(QStringLiteral("-bios")))
@ -1736,7 +1757,7 @@ bool QtHost::ParseCommandLineOptions(const QStringList& args, std::shared_ptr<VM
}
else if (CHECK_ARG(QStringLiteral("-earlyconsolelog")))
{
LogSink::InitializeEarlyConsole();
InitializeEarlyConsole();
continue;
}
else if (CHECK_ARG(QStringLiteral("-bigpicture")))
@ -1798,7 +1819,6 @@ bool QtHost::ParseCommandLineOptions(const QStringList& args, std::shared_ptr<VM
// or disc, we don't want to actually start.
if (autoboot && !autoboot->source_type.has_value() && autoboot->filename.empty() && autoboot->elf_override.empty())
{
LogSink::InitializeEarlyConsole();
Console.Warning("Skipping autoboot due to no boot parameters.");
autoboot.reset();
}
@ -1956,8 +1976,5 @@ shutdown_and_exit:
if (s_base_settings_interface->IsDirty())
s_base_settings_interface->Save();
// Ensure emulog is flushed.
LogSink::CloseFileLog();
return result;
}

View File

@ -476,7 +476,7 @@ static CDVDDiscType GetPS2ElfName(IsoReader& isor, std::string* name, std::strin
if (value.empty() && (lineno == (lines.size() - 1)))
{
Console.Warning("(SYSTEM.CNF) Unusual or malformed entry in SYSTEM.CNF ignored:");
Console.Indent().WriteLn(std::string(line));
Console.WarningFmt(" {}", line);
continue;
}

View File

@ -260,14 +260,12 @@ bool InputIsoFile::Open(std::string srcfile, Error* error, bool testOnly)
Console.WriteLn(Color_StrongBlue, "isoFile open ok: %s", m_filename.c_str());
ConsoleIndentScope indent;
Console.WriteLn("Image type = %s", nameFromType(m_type));
//Console.WriteLn("Fileparts = %u", m_numparts); // Pointless print, it's 1 unless it says otherwise above
DevCon.WriteLn("blocks = %u", m_blocks);
DevCon.WriteLn("offset = %d", m_offset);
DevCon.WriteLn("blocksize = %u", m_blocksize);
DevCon.WriteLn("blockoffset = %d", m_blockofs);
Console.WriteLn(" Image type = %s", nameFromType(m_type));
//Console.WriteLn(" Fileparts = %u", m_numparts); // Pointless print, it's 1 unless it says otherwise above
DevCon.WriteLn(" blocks = %u", m_blocks);
DevCon.WriteLn(" offset = %d", m_offset);
DevCon.WriteLn(" blocksize = %u", m_blocksize);
DevCon.WriteLn(" blockoffset = %d", m_blockofs);
return true;
}

View File

@ -77,7 +77,6 @@ set(pcsx2Sources
HwRead.cpp
HwWrite.cpp
LayeredSettingsInterface.cpp
LogSink.cpp
INISettingsInterface.cpp
Interpreter.cpp
IopBios.cpp
@ -169,7 +168,6 @@ set(pcsx2Headers
IopHw.h
IopMem.h
LayeredSettingsInterface.h
LogSink.h
PINE.h
Mdec.h
MTGS.h

View File

@ -417,7 +417,7 @@ void UpdateVSyncRate(bool force)
Console.WriteLn(Color_Green, "(UpdateVSyncRate) Mode Changed to %s.", ReportVideoMode());
if (custom && video_mode_initialized)
Console.Indent().WriteLn(Color_StrongGreen, "... with user configured refresh rate: %.02f Hz", vertical_frequency);
Console.WriteLn(Color_StrongGreen, " ... with user configured refresh rate: %.02f Hz", vertical_frequency);
hsyncCounter.CycleT = (hsyncCounter.Mode == MODE_HBLANK) ? vSyncInfo.hBlank : vSyncInfo.hRender;
vsyncCounter.CycleT = (vsyncCounter.Mode == MODE_GSBLANK) ?

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: LGPL-3.0+
#pragma once
@ -7,8 +7,10 @@
#include "Config.h"
#include "Memory.h"
#include <string>
// TODO: Purge emuLog and all this other nonsense, just go through Log with LOGLEVEL_TRACE.
extern FILE *emuLog;
extern std::string emuLogName;
extern char* disVU0MicroUF(u32 code, u32 pc);
extern char* disVU0MicroLF(u32 code, u32 pc);
@ -214,23 +216,34 @@ class ConsoleLogFromVM : public BaseTraceLogSource
public:
ConsoleLogFromVM( const TraceLogDescriptor* desc ) : _parent( desc ) {}
bool Write( const char* msg ) const
bool Write(std::string_view msg)
{
ConsoleColorScope cs(conColor);
Console.WriteRaw(msg);
// Buffered output isn't compatible with the testsuite. The end of test
// doesn't always get flushed. Let's just flush all the output if EE/IOP
// print anything.
fflush(NULL);
for (const char ch : msg)
{
if (ch == '\n')
{
if (!m_buffer.empty())
{
Console.WriteLn(conColor, m_buffer);
m_buffer.clear();
}
}
else if (ch < 0x20)
{
// Ignore control characters.
// Otherwise you get fun bells going off.
}
else
{
m_buffer.push_back(ch);
}
}
return false;
}
bool Write(const std::string& msg) const
{
return Write(msg.c_str());
}
private:
std::string m_buffer;
};
// --------------------------------------------------------------------------------------

View File

@ -171,7 +171,7 @@ static bool OpenGSDevice(GSRendererType renderer, bool clear_state_on_fail, bool
GSConfig.OsdShowGPU = GSConfig.OsdShowGPU && g_gs_device->SetGPUTimingEnabled(true);
Console.WriteLn(Color_StrongGreen, "%s Graphics Driver Info:", GSDevice::RenderAPIToString(new_api));
Console.Indent().WriteLn(g_gs_device->GetDriverInfo());
Console.WriteLn(g_gs_device->GetDriverInfo());
return true;
}

View File

@ -115,7 +115,7 @@ void ReportIPU()
Console.WriteLn("coded_block_pattern = 0x%x.", coded_block_pattern);
Console.WriteLn("g_decoder = 0x%x.", &decoder);
Console.WriteLn(ipu_cmd.desc());
Console.Newline();
Console.WriteLn();
}
bool SaveStateBase::ipuFreeze()

View File

@ -1,427 +0,0 @@
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
// SPDX-License-Identifier: LGPL-3.0+
#include "DebugTools/Debug.h"
#include "Host.h"
#include "LogSink.h"
#include "common/Assertions.h"
#include "common/Console.h"
#include "common/FileSystem.h"
#include "common/Path.h"
#include "common/SettingsInterface.h"
#include "common/StringUtil.h"
#include "common/Timer.h"
#ifdef _WIN32
#include "common/RedtapeWindows.h"
#endif
#include <csignal>
#include "fmt/core.h"
// Used on both Windows and Linux.
#ifdef _WIN32
static const wchar_t s_console_colors[][ConsoleColors_Count] = {
#define CC(x) L##x
#else
static const char s_console_colors[][ConsoleColors_Count] = {
#define CC(x) x
#endif
CC("\033[0m"), // default
CC("\033[30m\033[1m"), // black
CC("\033[32m"), // green
CC("\033[31m"), // red
CC("\033[34m"), // blue
CC("\033[35m"), // magenta
CC("\033[35m"), // orange (FIXME)
CC("\033[37m"), // gray
CC("\033[36m"), // cyan
CC("\033[33m"), // yellow
CC("\033[37m"), // white
CC("\033[30m\033[1m"), // strong black
CC("\033[31m\033[1m"), // strong red
CC("\033[32m\033[1m"), // strong green
CC("\033[34m\033[1m"), // strong blue
CC("\033[35m\033[1m"), // strong magenta
CC("\033[35m\033[1m"), // strong orange (FIXME)
CC("\033[37m\033[1m"), // strong gray
CC("\033[36m\033[1m"), // strong cyan
CC("\033[33m\033[1m"), // strong yellow
CC("\033[37m\033[1m") // strong white
};
#undef CC
static bool s_block_system_console = false;
static Common::Timer::Value s_log_start_timestamp = Common::Timer::GetCurrentValue();
static bool s_log_timestamps = false;
static std::mutex s_log_mutex;
// Replacement for Console so we actually get output to our console window on Windows.
#ifdef _WIN32
static bool s_debugger_attached = false;
static bool s_console_handle_set = false;
static bool s_console_allocated = false;
static HANDLE s_console_handle = INVALID_HANDLE_VALUE;
static HANDLE s_old_console_stdin = NULL;
static HANDLE s_old_console_stdout = NULL;
static HANDLE s_old_console_stderr = NULL;
static BOOL WINAPI ConsoleCtrlHandler(DWORD dwCtrlType)
{
Console.WriteLn("Handler %u", dwCtrlType);
if (dwCtrlType != CTRL_C_EVENT)
return FALSE;
::raise(SIGTERM);
return TRUE;
}
static bool EnableVirtualTerminalProcessing(HANDLE hConsole)
{
if (hConsole == INVALID_HANDLE_VALUE)
return false;
DWORD old_mode;
if (!GetConsoleMode(hConsole, &old_mode))
return false;
// already enabled?
if (old_mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)
return true;
return SetConsoleMode(hConsole, old_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
}
#endif
static void ConsoleQt_SetTitle(const char* title)
{
#ifdef _WIN32
SetConsoleTitleW(StringUtil::UTF8StringToWideString(title).c_str());
#else
std::fprintf(stdout, "\033]0;%s\007", title);
#endif
}
static void ConsoleQt_DoSetColor(ConsoleColors color)
{
#ifdef _WIN32
if (s_console_handle == INVALID_HANDLE_VALUE)
return;
const wchar_t* colortext = s_console_colors[static_cast<u32>(color)];
DWORD written;
WriteConsoleW(s_console_handle, colortext, std::wcslen(colortext), &written, nullptr);
#else
const char* colortext = s_console_colors[static_cast<u32>(color)];
std::fputs(colortext, stdout);
#endif
}
static void ConsoleQt_DoWrite(const char* fmt)
{
std::unique_lock lock(s_log_mutex);
#ifdef _WIN32
if (s_console_handle != INVALID_HANDLE_VALUE || s_debugger_attached)
{
// TODO: Put this on the stack.
std::wstring wfmt(StringUtil::UTF8StringToWideString(fmt));
if (s_debugger_attached)
OutputDebugStringW(wfmt.c_str());
if (s_console_handle != INVALID_HANDLE_VALUE)
{
DWORD written;
WriteConsoleW(s_console_handle, wfmt.c_str(), static_cast<DWORD>(wfmt.length()), &written, nullptr);
}
}
#else
std::fputs(fmt, stdout);
#endif
if (emuLog)
{
std::fputs(fmt, emuLog);
}
}
static void ConsoleQt_DoWriteLn(const char* fmt)
{
std::unique_lock lock(s_log_mutex);
// find time since start of process, but save a syscall if we're not writing timestamps
float message_time =
s_log_timestamps ?
static_cast<float>(Common::Timer::ConvertValueToSeconds(Common::Timer::GetCurrentValue() - s_log_start_timestamp)) :
0.0f;
// split newlines up
const char* start = fmt;
do
{
const char* end = std::strchr(start, '\n');
std::string_view line;
if (end)
{
line = std::string_view(start, end - start);
start = end + 1;
}
else
{
line = std::string_view(start);
start = nullptr;
}
#ifdef _WIN32
if (s_console_handle != INVALID_HANDLE_VALUE || s_debugger_attached)
{
// TODO: Put this on the stack.
std::wstring wfmt(StringUtil::UTF8StringToWideString(line));
if (s_debugger_attached)
{
// VS already timestamps logs (at least with the productivity power tools).
if (!wfmt.empty())
OutputDebugStringW(wfmt.c_str());
OutputDebugStringW(L"\n");
}
if (s_console_handle != INVALID_HANDLE_VALUE)
{
DWORD written;
if (s_log_timestamps)
{
wchar_t timestamp_text[128];
const int timestamp_len = _swprintf(timestamp_text, L"[%10.4f] ", message_time);
WriteConsoleW(s_console_handle, timestamp_text, static_cast<DWORD>(timestamp_len), &written, nullptr);
}
if (!wfmt.empty())
WriteConsoleW(s_console_handle, wfmt.c_str(), static_cast<DWORD>(wfmt.length()), &written, nullptr);
WriteConsoleW(s_console_handle, L"\n", 1, &written, nullptr);
}
}
#else
if (s_log_timestamps)
{
std::fprintf(stdout, "[%10.4f] %.*s\n", message_time, static_cast<int>(line.length()), line.data());
}
else
{
if (!line.empty())
std::fwrite(line.data(), line.length(), 1, stdout);
std::fputc('\n', stdout);
}
#endif
if (emuLog)
{
if (s_log_timestamps)
{
std::fprintf(emuLog, "[%10.4f] %.*s\n", message_time, static_cast<int>(line.length()), line.data());
}
else
{
std::fwrite(line.data(), line.length(), 1, emuLog);
std::fputc('\n', emuLog);
}
}
} while (start);
}
static void ConsoleQt_Newline()
{
ConsoleQt_DoWriteLn("");
}
static const IConsoleWriter ConsoleWriter_WinQt = {
ConsoleQt_DoWrite,
ConsoleQt_DoWriteLn,
ConsoleQt_DoSetColor,
ConsoleQt_DoWrite,
ConsoleQt_Newline,
ConsoleQt_SetTitle,
};
static void UpdateLoggingSinks(bool system_console, bool file_log)
{
#ifdef _WIN32
const bool debugger_attached = IsDebuggerPresent();
s_debugger_attached = debugger_attached;
if (system_console)
{
if (!s_console_handle_set)
{
s_old_console_stdin = GetStdHandle(STD_INPUT_HANDLE);
s_old_console_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
s_old_console_stderr = GetStdHandle(STD_ERROR_HANDLE);
bool handle_valid = (GetConsoleWindow() != NULL);
if (!handle_valid)
{
s_console_allocated = AttachConsole(ATTACH_PARENT_PROCESS) || AllocConsole();
handle_valid = (GetConsoleWindow() != NULL);
}
if (handle_valid)
{
s_console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
if (s_console_handle != INVALID_HANDLE_VALUE)
{
s_console_handle_set = true;
SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
// This gets us unix-style coloured output.
EnableVirtualTerminalProcessing(GetStdHandle(STD_OUTPUT_HANDLE));
EnableVirtualTerminalProcessing(GetStdHandle(STD_ERROR_HANDLE));
// Redirect stdout/stderr.
std::FILE* fp;
freopen_s(&fp, "CONIN$", "r", stdin);
freopen_s(&fp, "CONOUT$", "w", stdout);
freopen_s(&fp, "CONOUT$", "w", stderr);
}
}
}
// just in case it fails
system_console = s_console_handle_set;
}
else
{
if (s_console_handle_set)
{
s_console_handle_set = false;
SetConsoleCtrlHandler(ConsoleCtrlHandler, FALSE);
// redirect stdout/stderr back to null.
std::FILE* fp;
freopen_s(&fp, "NUL:", "w", stderr);
freopen_s(&fp, "NUL:", "w", stdout);
freopen_s(&fp, "NUL:", "w", stdin);
// release console and restore state
SetStdHandle(STD_INPUT_HANDLE, s_old_console_stdin);
SetStdHandle(STD_OUTPUT_HANDLE, s_old_console_stdout);
SetStdHandle(STD_ERROR_HANDLE, s_old_console_stderr);
s_old_console_stdin = NULL;
s_old_console_stdout = NULL;
s_old_console_stderr = NULL;
if (s_console_allocated)
{
s_console_allocated = false;
FreeConsole();
}
}
}
#else
const bool debugger_attached = false;
#endif
if (file_log)
{
if (!emuLog)
{
if (emuLogName.empty())
emuLogName = Path::Combine(EmuFolders::Logs, "emulog.txt");
emuLog = FileSystem::OpenCFile(emuLogName.c_str(), "wb");
file_log = (emuLog != nullptr);
}
}
else
{
if (emuLog)
{
std::fclose(emuLog);
emuLog = nullptr;
emuLogName = {};
}
}
// Discard logs completely if there's no sinks.
if (debugger_attached || system_console || file_log)
Console_SetActiveHandler(ConsoleWriter_WinQt);
else
Console_SetActiveHandler(ConsoleWriter_Null);
}
void LogSink::SetFileLogPath(std::string path)
{
if (emuLogName == path)
return;
emuLogName = std::move(path);
// reopen on change
if (emuLog)
{
std::fclose(emuLog);
if (!emuLogName.empty())
emuLog = FileSystem::OpenCFile(emuLogName.c_str(), "wb");
}
}
void LogSink::CloseFileLog()
{
if (!emuLog)
return;
std::fclose(emuLog);
emuLog = nullptr;
}
void LogSink::SetBlockSystemConsole(bool block)
{
s_block_system_console = block;
}
void LogSink::InitializeEarlyConsole()
{
UpdateLoggingSinks(true, false);
}
void LogSink::UpdateLogging(SettingsInterface& si)
{
const bool system_console_enabled = !s_block_system_console && si.GetBoolValue("Logging", "EnableSystemConsole", false);
const bool file_logging_enabled = si.GetBoolValue("Logging", "EnableFileLogging", false);
s_log_timestamps = si.GetBoolValue("Logging", "EnableTimestamps", true);
const bool any_logging_sinks = system_console_enabled || file_logging_enabled;
DevConWriterEnabled = any_logging_sinks && (IsDevBuild || si.GetBoolValue("Logging", "EnableVerbose", false));
const bool ee_console_enabled = any_logging_sinks && si.GetBoolValue("Logging", "EnableEEConsole", false);
SysConsole.eeConsole.Enabled = ee_console_enabled;
SysConsole.iopConsole.Enabled = any_logging_sinks && si.GetBoolValue("Logging", "EnableIOPConsole", false);
SysTrace.IOP.R3000A.Enabled = true;
SysTrace.IOP.COP2.Enabled = true;
SysTrace.IOP.Memory.Enabled = true;
SysTrace.SIF.Enabled = true;
// Input Recording Logs
SysConsole.recordingConsole.Enabled = any_logging_sinks && si.GetBoolValue("Logging", "EnableInputRecordingLogs", true);
SysConsole.controlInfo.Enabled = any_logging_sinks && si.GetBoolValue("Logging", "EnableControllerLogs", false);
UpdateLoggingSinks(system_console_enabled, file_logging_enabled);
}
void LogSink::SetDefaultLoggingSettings(SettingsInterface& si)
{
si.SetBoolValue("Logging", "EnableSystemConsole", false);
si.SetBoolValue("Logging", "EnableFileLogging", false);
si.SetBoolValue("Logging", "EnableTimestamps", true);
si.SetBoolValue("Logging", "EnableVerbose", false);
si.SetBoolValue("Logging", "EnableEEConsole", false);
si.SetBoolValue("Logging", "EnableIOPConsole", false);
si.SetBoolValue("Logging", "EnableInputRecordingLogs", true);
si.SetBoolValue("Logging", "EnableControllerLogs", false);
}

View File

@ -1,27 +0,0 @@
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
// SPDX-License-Identifier: LGPL-3.0+
#pragma once
class SettingsInterface;
namespace LogSink
{
/// Overrides the filename used for the file log.
void SetFileLogPath(std::string path);
/// Ensures file log is flushed and closed.
void CloseFileLog();
/// Prevents the system console from being displayed.
void SetBlockSystemConsole(bool block);
/// Updates the Console handler based on the current configuration.
void UpdateLogging(SettingsInterface& si);
/// Initializes early console logging (for printing command line arguments).
void InitializeEarlyConsole();
/// Stores default logging settings to the specified file.
void SetDefaultLoggingSettings(SettingsInterface& si);
}

View File

@ -180,7 +180,7 @@ bool SysMemory::AllocateMemoryMap()
void SysMemory::DumpMemoryMap()
{
#define DUMP_REGION(name, base, offset, size) \
DevCon.WriteLn(Color_Gray, "%-32s @ 0x%016" PRIXPTR " -> 0x%016" PRIXPTR " %s", name, \
DevCon.WriteLn(Color_Gray, " %-32s @ 0x%016" PRIXPTR " -> 0x%016" PRIXPTR " %s", name, \
(uptr)(base + offset), (uptr)(base + offset + size), fmt::format("[{}mb]", size / _1mb).c_str());
DUMP_REGION("EE Main Memory", s_data_memory, HostMemoryMap::EEmemOffset, HostMemoryMap::EEmemSize);
@ -227,8 +227,6 @@ bool SysMemory::Allocate()
{
DevCon.WriteLn(Color_StrongBlue, "Allocating host memory for virtual systems...");
ConsoleIndentScope indent(1);
if (!AllocateMemoryMap())
return false;
@ -245,7 +243,6 @@ bool SysMemory::Allocate()
void SysMemory::Reset()
{
DevCon.WriteLn(Color_StrongBlue, "Resetting host memory for virtual systems...");
ConsoleIndentScope indent(1);
memReset();
iopMemReset();
@ -258,7 +255,6 @@ void SysMemory::Reset()
void SysMemory::Release()
{
Console.WriteLn(Color_Blue, "Releasing host memory for virtual systems...");
ConsoleIndentScope indent(1);
vtlb_Core_Free(); // Just to be sure... (calling order could result in it getting missed during Decommit).

View File

@ -93,7 +93,6 @@ void MultipartFileReader::FindParts()
return;
DevCon.WriteLn( Color_Blue, "isoFile: multi-part %s detected...", StringUtil::toUpper(curext).c_str() );
ConsoleIndentScope indent;
int bsize = m_parts[0].reader->GetBlockSize();
int blocks = m_parts[0].end;

View File

@ -7,6 +7,7 @@
#include "common/ByteSwap.h"
#include "common/FileSystem.h"
#include "common/Path.h"
#include "common/SmallString.h"
#include "common/StringUtil.h"
#include "common/ZipHelpers.h"
@ -99,9 +100,9 @@ namespace Patch
bool operator==(const PatchCommand& p) const { return std::memcmp(this, &p, sizeof(*this)) == 0; }
bool operator!=(const PatchCommand& p) const { return std::memcmp(this, &p, sizeof(*this)) != 0; }
std::string ToString() const
SmallString ToString() const
{
return fmt::format("{},{},{},{:08x},{:x}", s_place_to_string[static_cast<u8>(placetopatch)],
return SmallString::from_fmt("{},{},{},{:08x},{:x}", s_place_to_string[static_cast<u8>(placetopatch)],
s_cpu_to_string[static_cast<u8>(cpu)], s_type_to_string[static_cast<u8>(type)], addr, data);
}
};
@ -616,8 +617,8 @@ u32 Patch::EnablePatches(const PatchList& patches, const EnablePatchList& enable
for (const PatchCommand& ip : p.patches)
{
// print the actual patch lines only in verbose mode (even in devel)
if (DevConWriterEnabled)
DevCon.Indent().WriteLn(ip.ToString());
if (Log::GetMaxLevel() >= LOGLEVEL_DEV)
DevCon.WriteLnFmt(" {}", ip.ToString());
s_active_patches.push_back(&ip);
}

View File

@ -152,11 +152,10 @@ bool SaveStateBase::FreezeBios()
if (bioscheck != BiosChecksum)
{
Console.Newline();
Console.Indent(1).Error( "Warning: BIOS Version Mismatch, savestate may be unstable!" );
Console.Indent(2).Error(
"Current BIOS: %s (crc=0x%08x)\n"
"Savestate BIOS: %s (crc=0x%08x)\n",
Console.Error("\n Warning: BIOS Version Mismatch, savestate may be unstable!");
Console.Error(
" Current BIOS: %s (crc=0x%08x)\n"
" Savestate BIOS: %s (crc=0x%08x)\n",
BiosDescription.c_str(), BiosChecksum,
biosdesc, bioscheck
);
@ -355,7 +354,7 @@ static bool SysState_ComponentFreezeIn(zip_file_t* zf, SysState_Component comp)
if (comp.freeze(FreezeAction::Size, &fP) != 0)
fP.size = 0;
Console.Indent().WriteLn("Loading %s", comp.name);
Console.WriteLn(" Loading %s", comp.name);
std::unique_ptr<u8[]> data;
if (fP.size > 0)
@ -394,7 +393,7 @@ static bool SysState_ComponentFreezeOut(SaveStateBase& writer, SysState_Componen
const int size = fP.size;
writer.PrepBlock(size);
Console.Indent().WriteLn("Saving %s", comp.name);
Console.WriteLn(" Saving %s", comp.name);
fP.data = writer.GetBlockPtr();
if (comp.freeze(FreezeAction::Save, &fP) != 0)

View File

@ -25,7 +25,6 @@
using namespace R5900;
FILE* emuLog;
std::string emuLogName;
SysTraceLogPack SysTrace;
SysConsoleLogPack SysConsole;

View File

@ -21,7 +21,6 @@
#include "ImGui/ImGuiOverlays.h"
#include "Input/InputManager.h"
#include "IopBios.h"
#include "LogSink.h"
#include "MTGS.h"
#include "MTVU.h"
#include "PINE.h"
@ -79,6 +78,9 @@
namespace VMManager
{
static void SetDefaultLoggingSettings(SettingsInterface& si);
static void UpdateLoggingSettings(SettingsInterface& si);
static void LogCPUCapabilities();
static void InitializeCPUProviders();
static void ShutdownCPUProviders();
@ -145,6 +147,9 @@ static constexpr u32 SETTINGS_VERSION = 1;
static std::unique_ptr<INISettingsInterface> s_game_settings_interface;
static std::unique_ptr<INISettingsInterface> s_input_settings_interface;
static bool s_log_block_system_console = false;
static bool s_log_force_file_log = false;
static std::atomic<VMState> s_state{VMState::Shutdown};
static bool s_cpu_implementation_changed = false;
static Threading::ThreadHandle s_vm_thread_handle;
@ -411,6 +416,80 @@ void VMManager::Internal::CPUThreadShutdown()
#ifdef _WIN32
CoUninitialize();
#endif
// Ensure emulog gets flushed.
Log::SetFileOutputLevel(LOGLEVEL_NONE, std::string());
}
void VMManager::Internal::SetFileLogPath(std::string path)
{
s_log_force_file_log = Log::SetFileOutputLevel(LOGLEVEL_DEBUG, std::move(path));
emuLog = Log::GetFileLogHandle();
}
void VMManager::Internal::SetBlockSystemConsole(bool block)
{
s_log_block_system_console = block;
}
void VMManager::UpdateLoggingSettings(SettingsInterface& si)
{
#ifdef _DEBUG
constexpr LOGLEVEL level = LOGLEVEL_DEBUG;
#else
const LOGLEVEL level = (IsDevBuild || si.GetBoolValue("Logging", "EnableVerbose", false)) ? LOGLEVEL_DEV : LOGLEVEL_INFO;
#endif
const bool system_console_enabled = !s_log_block_system_console && si.GetBoolValue("Logging", "EnableSystemConsole", false);
const bool log_window_enabled = !s_log_block_system_console && si.GetBoolValue("Logging", "EnableLogWindow", false);
const bool file_logging_enabled = s_log_force_file_log || si.GetBoolValue("Logging", "EnableFileLogging", false);
if (system_console_enabled != Log::IsConsoleOutputEnabled())
Log::SetConsoleOutputLevel(system_console_enabled ? level : LOGLEVEL_NONE);
if (file_logging_enabled != Log::IsFileOutputEnabled())
{
std::string path = Path::Combine(EmuFolders::Logs, "emulog.txt");
Log::SetFileOutputLevel(file_logging_enabled ? level : LOGLEVEL_NONE, std::move(path));
}
// Debug console only exists on Windows.
#ifdef _WIN32
const bool debug_console_enabled = IsDebuggerPresent() && si.GetBoolValue("Logging", "EnableDebugConsole", false);
Log::SetDebugOutputLevel(debug_console_enabled ? level : LOGLEVEL_NONE);
#else
constexpr bool debug_console_enabled = false;
#endif
const bool timestamps_enabled = si.GetBoolValue("Logging", "EnableTimestamps", true);
Log::SetTimestampsEnabled(timestamps_enabled);
const bool any_logging_sinks = system_console_enabled || log_window_enabled || file_logging_enabled || debug_console_enabled;
const bool ee_console_enabled = any_logging_sinks && si.GetBoolValue("Logging", "EnableEEConsole", false);
SysConsole.eeConsole.Enabled = ee_console_enabled;
SysConsole.iopConsole.Enabled = any_logging_sinks && si.GetBoolValue("Logging", "EnableIOPConsole", false);
SysTrace.IOP.R3000A.Enabled = true;
SysTrace.IOP.COP2.Enabled = true;
SysTrace.IOP.Memory.Enabled = true;
SysTrace.SIF.Enabled = true;
// Input Recording Logs
SysConsole.recordingConsole.Enabled = any_logging_sinks && si.GetBoolValue("Logging", "EnableInputRecordingLogs", true);
SysConsole.controlInfo.Enabled = any_logging_sinks && si.GetBoolValue("Logging", "EnableControllerLogs", false);
}
void VMManager::SetDefaultLoggingSettings(SettingsInterface& si)
{
si.SetBoolValue("Logging", "EnableSystemConsole", false);
si.SetBoolValue("Logging", "EnableFileLogging", false);
si.SetBoolValue("Logging", "EnableTimestamps", true);
si.SetBoolValue("Logging", "EnableVerbose", false);
si.SetBoolValue("Logging", "EnableEEConsole", false);
si.SetBoolValue("Logging", "EnableIOPConsole", false);
si.SetBoolValue("Logging", "EnableInputRecordingLogs", true);
si.SetBoolValue("Logging", "EnableControllerLogs", false);
}
bool VMManager::Internal::CheckSettingsVersion()
@ -426,7 +505,9 @@ void VMManager::Internal::LoadStartupSettings()
SettingsInterface* bsi = Host::Internal::GetBaseSettingsLayer();
EmuFolders::LoadConfig(*bsi);
EmuFolders::EnsureFoldersExist();
LogSink::UpdateLogging(*bsi);
// We need to create the console window early, otherwise it appears behind the main window.
UpdateLoggingSettings(*bsi);
#ifdef ENABLE_RAINTEGRATION
// RAIntegration switch must happen before the UI is created.
@ -455,7 +536,7 @@ void VMManager::SetDefaultSettings(
si.SetBoolValue("EmuCore", "EnableFastBoot", true);
SetHardwareDependentDefaultSettings(si);
LogSink::SetDefaultLoggingSettings(si);
SetDefaultLoggingSettings(si);
}
if (controllers)
{
@ -482,7 +563,7 @@ void VMManager::LoadSettings()
Host::LoadSettings(*si, lock);
InputManager::ReloadSources(*si, lock);
InputManager::ReloadBindings(*si, *Host::GetSettingsInterfaceForBindings());
LogSink::UpdateLogging(*si);
UpdateLoggingSettings(*si);
if (HasValidOrInitializingVM())
{
@ -2152,26 +2233,22 @@ void VMManager::LogCPUCapabilities()
SVN_REV);
}
Console.WriteLn("Savestate version: 0x%x", g_SaveVersion);
Console.Newline();
Console.WriteLnFmt("Savestate version: 0x{:x}\n", g_SaveVersion);
Console.WriteLn(Color_StrongBlack, "Host Machine Init:");
Console.Indent().WriteLn(
"Operating System = %s\n"
"Physical RAM = %u MB",
Console.WriteLnFmt(
" Operating System = {}\n"
" Physical RAM = {} MB",
GetOSVersionString(),
GetPhysicalMemory() / _1mb);
GetOSVersionString().c_str(),
(u32)(GetPhysicalMemory() / _1mb));
Console.Indent().WriteLn("Processor = %s", cpuinfo_get_package(0)->name);
Console.Indent().WriteLn("Core Count = %u cores", cpuinfo_get_cores_count());
Console.Indent().WriteLn("Thread Count = %u threads", cpuinfo_get_processors_count());
Console.Newline();
Console.WriteLnFmt(" Processor = {}", cpuinfo_get_package(0)->name);
Console.WriteLnFmt(" Core Count = {} cores", cpuinfo_get_cores_count());
Console.WriteLnFmt(" Thread Count = {} threads", cpuinfo_get_processors_count());
Console.WriteLn();
std::string features;
if (cpuinfo_has_x86_avx())
features += "AVX ";
if (cpuinfo_has_x86_avx2())
@ -2180,9 +2257,8 @@ void VMManager::LogCPUCapabilities()
StringUtil::StripWhitespace(&features);
Console.WriteLn(Color_StrongBlack, "x86 Features Detected:");
Console.Indent().WriteLn("%s", features.c_str());
Console.Newline();
Console.WriteLnFmt(" {}", features);
Console.WriteLn();
}

View File

@ -222,6 +222,12 @@ namespace VMManager
/// Loads early settings. Call once on startup.
void LoadStartupSettings();
/// Overrides the filename used for the file log.
void SetFileLogPath(std::string path);
/// Prevents the system console from being displayed.
void SetBlockSystemConsole(bool block);
/// Initializes common host state, called on the CPU thread.
bool CPUThreadInitialize();

View File

@ -230,7 +230,6 @@
<ClCompile Include="Input\XInputSource.cpp" />
<ClCompile Include="IopGte.cpp" />
<ClCompile Include="LayeredSettingsInterface.cpp" />
<ClCompile Include="LogSink.cpp" />
<ClCompile Include="PINE.cpp" />
<ClCompile Include="FW.cpp" />
<ClCompile Include="PerformanceMetrics.cpp" />
@ -578,7 +577,6 @@
<ClInclude Include="IopGte.h" />
<ClInclude Include="IPU\mpeg2_vlc.h" />
<ClInclude Include="LayeredSettingsInterface.h" />
<ClInclude Include="LogSink.h" />
<ClInclude Include="PINE.h" />
<ClInclude Include="FW.h" />
<ClInclude Include="PerformanceMetrics.h" />

View File

@ -1331,9 +1331,6 @@
<ClCompile Include="Host.cpp">
<Filter>System</Filter>
</ClCompile>
<ClCompile Include="LogSink.cpp">
<Filter>Misc</Filter>
</ClCompile>
<ClCompile Include="windows\Optimus.cpp">
<Filter>Misc</Filter>
</ClCompile>
@ -2253,9 +2250,6 @@
<ClInclude Include="Host.h">
<Filter>System</Filter>
</ClInclude>
<ClInclude Include="LogSink.h">
<Filter>Misc</Filter>
</ClInclude>
<ClInclude Include="GameList.h">
<Filter>Misc</Filter>
</ClInclude>

View File

@ -2083,7 +2083,6 @@ static void memory_protect_recompiled_code(u32 startpc, u32 size)
xJC(DispatchPageReset);
// note: clearcnt is measured per-page, not per-block!
ConsoleColorScope cs(Color_Gray);
eeRecPerfLog.Write("Manual block @ %08X : size =%3d page/offs = 0x%05X/0x%03X inpgsz = %d clearcnt = %d",
startpc, size, inpage_ptr >> 12, inpage_ptr & 0xfff, inpage_sz, manual_counter[inpage_ptr >> 12]);
}

View File

@ -370,9 +370,9 @@ void VifUnpackSSE_Init()
nVifGen(a, b, c);
DevCon.WriteLn("Unpack function generation complete. Generated function statistics:");
DevCon.Indent().WriteLn(
"Reserved buffer : %zu bytes @ 0x%016" PRIXPTR "\n"
"x86 code generated : %zu bytes\n",
DevCon.WriteLn(
" Reserved buffer : %zu bytes @ 0x%016" PRIXPTR "\n"
" x86 code generated : %zu bytes\n",
SysMemory::GetVIFUnpackRecEnd() - SysMemory::GetVIFUnpackRec(),
SysMemory::GetVIFUnpackRec(),
xGetPtr() - SysMemory::GetVIFUnpackRec()

View File

@ -31,36 +31,6 @@ static constexpr size_t kInputBufSize = ((size_t)1 << 18);
static constexpr ISzAlloc g_Alloc = {SzAlloc, SzFree};
#endif
static std::FILE* s_file_console_stream;
static constexpr IConsoleWriter s_file_console_writer = {
[](const char* fmt) { // WriteRaw
std::fputs(fmt, s_file_console_stream);
std::fflush(s_file_console_stream);
},
[](const char* fmt) { // DoWriteLn
std::fputs(fmt, s_file_console_stream);
std::fputc('\n', s_file_console_stream);
std::fflush(s_file_console_stream);
},
[](ConsoleColors) { // DoSetColor
},
[](const char* fmt) { // DoWriteFromStdout
std::fputs(fmt, s_file_console_stream);
std::fflush(s_file_console_stream);
},
[]() { // Newline
std::fputc('\n', s_file_console_stream);
std::fflush(s_file_console_stream);
},
[](const char*) { // SetTitle
}};
static void CloseConsoleFile()
{
if (s_file_console_stream)
std::fclose(s_file_console_stream);
}
Updater::Updater(ProgressCallback* progress)
: m_progress(progress)
{
@ -74,16 +44,11 @@ Updater::~Updater()
void Updater::SetupLogging(ProgressCallback* progress, const std::string& destination_directory)
{
const std::string log_path(Path::Combine(destination_directory, "updater.log"));
s_file_console_stream = FileSystem::OpenCFile(log_path.c_str(), "w");
if (!s_file_console_stream)
{
progress->DisplayFormattedModalError("Failed to open log file '%s'", log_path.c_str());
return;
}
Log::SetDebugOutputLevel(LOGLEVEL_DEBUG);
Console_SetActiveHandler(s_file_console_writer);
std::atexit(CloseConsoleFile);
std::string log_path = Path::Combine(destination_directory, "updater.log");
if (!Log::SetFileOutputLevel(LOGLEVEL_DEBUG, std::move(log_path)))
progress->DisplayFormattedModalError("Failed to open log file '%s'", log_path.c_str());
}
bool Updater::Initialize(std::string destination_directory)