Cxbx-Reloaded/src/common/Logging.cpp

392 lines
9.3 KiB
C++

// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// ******************************************************************
// *
// * This file is part of the Cxbx project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them under the terms of the GNU General Public
// * License as published by the Free Software Foundation; either
// * version 2 of the license, or (at your option) any later version.
// *
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// * GNU General Public License for more details.
// *
// * You should have recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * (c) 2016 Patrick van Logchem <pvanlogchem@gmail.com>
// *
// * All rights reserved
// *
// ******************************************************************
#include <windows.h> // for PULONG
#include "Logging.h"
#include "common\Settings.hpp"
#include "EmuShared.h"
// For thread_local, see : https://en.cppreference.com/w/cpp/language/storage_duration
// TODO : Use Boost.Format https://www.boost.org/doc/libs/1_53_0/libs/format/index.html
thread_local std::string _logThreadPrefix;
std::atomic_bool g_EnabledModules[to_underlying(CXBXR_MODULE::MAX)] = { false };
const char* g_EnumModules2String[to_underlying(CXBXR_MODULE::MAX)] = {
"CXBXR ",
"XBE ",
"INIT ",
"VMEM ",
"PMEM ",
"GUI ",
"EEPR ",
"RSA ",
"POOLMEM ",
"D3D8 ",
"D3DST ",
"D3DCVT ",
"DSOUND ",
"XAPI ",
"XACT ",
"XGRP ",
"XONLINE ",
"FS ",
"PSHB ",
"PXSH ",
"VTXSH ",
"VSHCACHE",
"VTXB ",
"DINP ",
"XINP ",
"SDL ",
"FILE ",
"X86 ",
"HLE ",
"NET ",
"MCPX ",
"NV2A ",
"SMC ",
"OHCI ",
"USB ",
"HUB ",
"XIDCTRL ",
"ADM ",
"INPSYS ",
"DSBUFFER",
"DSSTREAM",
"DS3DCALC",
"XMO ",
"KRNL ",
"LOG ",
"XBOX ",
"XBDM ",
"AV ",
"DBG ",
"EX ",
"FSC ",
"HAL ",
"IO ",
"KD ",
"KE ",
"KI ",
"MM ",
"NT ",
"OB ",
"PS ",
"RTL ",
"XC ",
"XE ",
};
std::atomic_int g_CurrentLogLevel = to_underlying(LOG_LEVEL::INFO);
const char log_debug[] = "DEBUG: ";
const char log_info[] = "INFO : ";
const char log_warn[] = "WARN : ";
const char log_error[] = "ERROR: ";
const char log_fatal[] = "FATAL: ";
const char log_unkwn[] = "???? : ";
// Do not use EmuLogOutput function outside of this file.
void EmuLogOutput(CXBXR_MODULE cxbxr_module, LOG_LEVEL level, const char *szWarningMessage, va_list argp)
{
LOG_THREAD_INIT;
const char* level_str;
switch (level) {
default:
level_str = log_unkwn;
break;
case LOG_LEVEL::DEBUG:
level_str = log_debug;
break;
case LOG_LEVEL::INFO:
level_str = log_info;
break;
case LOG_LEVEL::WARNING:
level_str = log_warn;
break;
case LOG_LEVEL::ERROR2:
level_str = log_error;
break;
case LOG_LEVEL::FATAL:
level_str = log_fatal;
break;
}
std::cout << _logThreadPrefix << level_str
<< g_EnumModules2String[to_underlying(cxbxr_module)];
vfprintf(stdout, szWarningMessage, argp);
fprintf(stdout, "\n");
fflush(stdout);
}
// print out a custom message to the console or kernel debug log file
void NTAPI EmuLogEx(CXBXR_MODULE cxbxr_module, LOG_LEVEL level, const char *szWarningMessage, ...)
{
if (szWarningMessage == NULL) {
return;
}
LOG_CHECK_ENABLED_EX(cxbxr_module, level) {
if (g_bPrintfOn) {
LOG_THREAD_INIT;
va_list argp;
va_start(argp, szWarningMessage);
EmuLogOutput(cxbxr_module, level, szWarningMessage, argp);
va_end(argp);
}
}
}
void NTAPI EmuLogInit(LOG_LEVEL level, const char *szWarningMessage, ...)
{
if (szWarningMessage == NULL) {
return;
}
va_list argp;
va_start(argp, szWarningMessage);
EmuLogOutput(CXBXR_MODULE::INIT, level, szWarningMessage, argp);
va_end(argp);
}
// Set up the logging variables for the GUI process
inline void log_get_settings()
{
log_set_config(g_Settings->m_core.LogLevel, g_Settings->m_core.LoggedModules);
}
inline void log_sync_config()
{
int LogLevel;
unsigned int LoggedModules[NUM_INTEGERS_LOG];
g_EmuShared->GetLogLv(&LogLevel);
g_EmuShared->GetLogModules(LoggedModules);
log_set_config(LogLevel, LoggedModules);
}
void log_set_config(int LogLevel, unsigned int* LoggedModules)
{
g_CurrentLogLevel = LogLevel;
for (unsigned int index = to_underlying(CXBXR_MODULE::CXBXR); index < to_underlying(CXBXR_MODULE::MAX); index++) {
if (LoggedModules[index / 32] & (1 << (index % 32))) {
g_EnabledModules[index] = true;
}
else {
g_EnabledModules[index] = false;
}
}
}
// Generate active log filter output.
void log_generate_active_filter_output(const CXBXR_MODULE cxbxr_module)
{
LOG_THREAD_INIT;
std::string generic_output_str = _logThreadPrefix + log_info + g_EnumModules2String[to_underlying(cxbxr_module)];
std::cout << generic_output_str << "Current log level: " << g_CurrentLogLevel << std::endl;
generic_output_str.append("Active log filter: ");
for (unsigned int index = to_underlying(CXBXR_MODULE::CXBXR); index < to_underlying(CXBXR_MODULE::MAX); index++) {
if (g_EnabledModules[index]) {
std::cout << generic_output_str << g_EnumModules2String[index] << "\n";
}
}
std::cout << std::flush;
}
const bool needs_escape(const wint_t _char)
{
// Escaping is needed for control characters,
// for double quote, and for backslash :
return iswcntrl(_char) || (_char == '"') || (_char == '\\');
}
inline void output_char(std::ostream& os, char c)
{
if (needs_escape((int)c))
{
switch (c)
{
// Render escaped double quote as \", and escaped backslash as \\ :
case '"': os << "\\\""; break;
case '\\': os << "\\\\"; break;
// See https://en.wikipedia.org/wiki/Escape_sequences_in_C#Table_of_escape_sequences
case '\a': os << "\\t"; break;
case '\b': os << "\\b"; break;
case '\f': os << "\\f"; break;
case '\n': os << "\\n"; break;
case '\r': os << "\\r"; break;
case '\t': os << "\\t"; break;
case '\v': os << "\\v"; break;
// All other to-escape-characters are rendered as hexadecimal :
default: os << "\\x" << std::setfill('0') << std::setw(2) << std::right << std::hex << std::uppercase << (int)c;
}
}
else
os << c;
}
inline void output_wchar(std::ostream& os, wchar_t c)
{
if (needs_escape((wint_t)c))
{
switch (c)
{
// Render escaped double quote as \", and escaped backslash as \\ :
case '"': os << "\\\""; break;
case '\\': os << "\\\\"; break;
// See https://en.wikipedia.org/wiki/Escape_sequences_in_C#Table_of_escape_sequences
case '\a': os << "\\t"; break;
case '\b': os << "\\b"; break;
case '\f': os << "\\f"; break;
case '\n': os << "\\n"; break;
case '\r': os << "\\r"; break;
case '\t': os << "\\t"; break;
case '\v': os << "\\v"; break;
// All other to-escape-characters are rendered as hexadecimal :
default: os << "\\x" << std::setfill('0') << std::setw(4) << std::right << std::hex << std::uppercase << (wint_t)c;
}
}
else
os << c;
}
LOG_SANITIZE_HEADER(hex1, uint8_t)
{
return os << hexstring8 << (int)container.value;
}
LOG_SANITIZE_HEADER(hex2, uint16_t)
{
return os << hexstring16 << (int)container.value;
}
LOG_SANITIZE_HEADER(hex4, uint32_t)
{
return os << hexstring32 << (int)container.value;
}
LOG_SANITIZE_HEADER(sanitized_char, char)
{
output_char(os, container.value);
return os;
}
LOG_SANITIZE_HEADER(sanitized_wchar, wchar_t)
{
output_wchar(os, container.value);
return os;
}
LOG_SANITIZE_HEADER(sanitized_char_pointer, char *)
{
char *v = container.value;
os << "(char *)";
if (v == nullptr)
return os << "NULL";
bool needsEscaping = false;
int max_length = container.max;
while (*v && max_length--)
if (needs_escape(*v++))
{
needsEscaping = true;
break;
}
v = container.value;
os << hexstring32 << (uint32_t)v << " = \"";
max_length = container.max;
if (needsEscaping)
{
while (*v && max_length--)
output_char(os, *v++);
}
else {
while (*v && max_length--) {
os << *v++;
}
}
return os << "\"";
}
LOG_SANITIZE_HEADER(sanitized_wchar_pointer, wchar_t *)
{
wchar_t *v = container.value;
os << "(wchar *)";
if (v == nullptr)
return os << "NULL";
bool needsEscaping = false;
int max_length = container.max;
while (*v && max_length--)
if (needs_escape(*v++))
{
needsEscaping = true;
break;
}
v = container.value;
os << hexstring32 << (uint32_t)v << " = \"";
max_length = container.max;
if (needsEscaping)
{
while (*v && max_length--)
output_wchar(os, *v++);
}
else
#if 0
os << v; // TODO : FIXME - VS2015 doesn''t render this string (instead, it shows a hexadecimal memory address)
#else // For now, render unicode as ANSI (replacing non-printables with '?')
{
while (*v && max_length--) {
output_char(os, *v <= 0xFF ? (char)*v : '?');
v++;
}
}
#endif
return os << "\"";
}
LOGRENDER_HEADER_BY_REF(PVOID)
{
return os << hex4((uint32_t)value);
}