638 lines
22 KiB
C++
638 lines
22 KiB
C++
// ******************************************************************
|
|
// *
|
|
// * 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>
|
|
// * (c) 2019 ergo720
|
|
// *
|
|
// * All rights reserved
|
|
// *
|
|
// ******************************************************************
|
|
#ifndef _LOGGING_H
|
|
#define _LOGGING_H
|
|
|
|
#include <windows.h> // For DWORD
|
|
#include <sstream> // For std::stringstream
|
|
#include <iostream> // For std::cout
|
|
#include <iomanip> // For std::setw
|
|
#include <atomic> // For atomic_bool and atomic_uint
|
|
#include "common\util\CxbxUtil.h" // For g_bPrintfOn and to_underlying
|
|
|
|
// NOTE: using ERROR2 since windows.h imports an ERROR macro which would conflict otherwise
|
|
typedef enum class _LOG_LEVEL {
|
|
DEBUG = 0,
|
|
INFO,
|
|
WARNING,
|
|
ERROR2,
|
|
FATAL,
|
|
MAX,
|
|
}LOG_LEVEL;
|
|
|
|
typedef enum class _CXBXR_MODULE: unsigned int {
|
|
// general
|
|
CXBXR = 0,
|
|
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,
|
|
RINP,
|
|
JVS,
|
|
LIBUSB,
|
|
// kernel
|
|
KRNL,
|
|
LOG,
|
|
XBOX,
|
|
XBDM,
|
|
AV,
|
|
DBG,
|
|
EX,
|
|
FSC,
|
|
HAL,
|
|
IO,
|
|
KD,
|
|
KE,
|
|
KI,
|
|
MM,
|
|
NT,
|
|
OB,
|
|
PS,
|
|
RTL,
|
|
XC,
|
|
XE,
|
|
// max
|
|
MAX,
|
|
}CXBXR_MODULE;
|
|
|
|
extern std::atomic_bool g_EnabledModules[to_underlying(CXBXR_MODULE::MAX)];
|
|
extern const char* g_EnumModules2String[to_underlying(CXBXR_MODULE::MAX)];
|
|
extern std::atomic_int g_CurrentLogLevel;
|
|
extern std::atomic_bool g_CurrentLogPopupTestCase;
|
|
|
|
// print out a log message to the console or kernel debug log file if level is high enough
|
|
void EmuLogEx(CXBXR_MODULE cxbxr_module, LOG_LEVEL level, const char *szWarningMessage, ...);
|
|
void EmuLogInit(LOG_LEVEL level, const char *szWarningMessage, ...);
|
|
|
|
#define EmuLog(level, fmt, ...) EmuLogEx(LOG_PREFIX, level, fmt, ##__VA_ARGS__)
|
|
|
|
extern inline void log_get_settings();
|
|
|
|
extern inline void log_sync_config();
|
|
|
|
void log_set_config(int LogLevel, unsigned int* LoggedModules, bool LogPopupTestCase);
|
|
|
|
void log_generate_active_filter_output(const CXBXR_MODULE cxbxr_module);
|
|
|
|
// Use emulation environment to manage popup messages
|
|
// If log_init_popup_msg is not called at earliest point of emulation.
|
|
// Then users will have a chance of popup message appear during start of emulation in full screen.
|
|
void log_init_popup_msg();
|
|
|
|
typedef enum class _PopupIcon {
|
|
Unknown = 0,
|
|
Question,
|
|
Info,
|
|
Warning,
|
|
Error
|
|
} PopupIcon;
|
|
|
|
typedef enum class _PopupButtons {
|
|
Unknown = 0,
|
|
Ok,
|
|
OkCancel,
|
|
AbortRetryIgnore,
|
|
YesNoCancel,
|
|
YesNo,
|
|
RetryCancel
|
|
} PopupButtons;
|
|
|
|
typedef enum class _PopupReturn {
|
|
Unknown = 0,
|
|
Ok,
|
|
Cancel,
|
|
Abort,
|
|
Retry,
|
|
Ignore,
|
|
Yes,
|
|
No
|
|
} PopupReturn;
|
|
|
|
PopupReturn PopupCustomEx(const void* hwnd, const CXBXR_MODULE cxbxr_module, const LOG_LEVEL level, const PopupIcon icon, const PopupButtons buttons, const PopupReturn ret_default, const char* message, ...);
|
|
|
|
#define PopupCustom(hwnd, level, icon, buttons, ret_default, fmt, ...) PopupCustomEx(hwnd, LOG_PREFIX, level, icon, buttons, ret_default, fmt, ## __VA_ARGS__)
|
|
#define PopupQuestionEx(hwnd, level, buttons, ret_default, fmt, ...) PopupCustom(hwnd, level, PopupIcon::Question, buttons, ret_default, fmt, ## __VA_ARGS__)
|
|
#define PopupQuestion(hwnd, fmt, ...) PopupQuestionEx(hwnd, LOG_LEVEL::INFO, PopupButtons::YesNoCancel, PopupReturn::Cancel, fmt, ## __VA_ARGS__)
|
|
#define PopupInfoEx(hwnd, buttons, ret_default, fmt, ...) PopupCustom(hwnd, LOG_LEVEL::INFO, PopupIcon::Info, buttons, ret_default, fmt, ## __VA_ARGS__)
|
|
#define PopupInfo(hwnd, fmt, ...) (void)PopupInfoEx(hwnd, PopupButtons::Ok, PopupReturn::Ok, fmt, ## __VA_ARGS__)
|
|
#define PopupWarningEx(hwnd, buttons, ret_default, fmt, ...) PopupCustom(hwnd, LOG_LEVEL::WARNING, PopupIcon::Warning, buttons, ret_default, fmt, ## __VA_ARGS__)
|
|
#define PopupWarning(hwnd, fmt, ...) (void)PopupWarningEx(hwnd, PopupButtons::Ok, PopupReturn::Ok, fmt, ## __VA_ARGS__)
|
|
#define PopupErrorEx(hwnd, buttons, ret_default, fmt, ...) PopupCustom(hwnd, LOG_LEVEL::ERROR2, PopupIcon::Error, buttons, ret_default, fmt, ## __VA_ARGS__)
|
|
#define PopupError(hwnd, fmt, ...) (void)PopupErrorEx(hwnd, PopupButtons::Ok, PopupReturn::Ok, fmt, ## __VA_ARGS__)
|
|
#define PopupFatalEx(hwnd, buttons, ret_default, fmt, ...) PopupCustom(hwnd, LOG_LEVEL::FATAL, PopupIcon::Error, buttons, ret_default, fmt, ## __VA_ARGS__)
|
|
#define PopupFatal(hwnd, fmt, ...) (void)PopupFatalEx(hwnd, PopupButtons::Ok, PopupReturn::Ok, fmt, ## __VA_ARGS__)
|
|
|
|
// For LOG_TEST_CASE
|
|
extern inline void EmuLogOutputEx(const CXBXR_MODULE cxbxr_module, const LOG_LEVEL level, const char *szWarningMessage, ...);
|
|
|
|
//
|
|
// __FILENAME__
|
|
//
|
|
|
|
// From https://stackoverflow.com/questions/31050113/how-to-extract-the-source-filename-without-path-and-suffix-at-compile-time
|
|
constexpr const char* str_end(const char *str) {
|
|
return *str ? str_end(str + 1) : str;
|
|
}
|
|
|
|
constexpr bool str_slant(const char *str) {
|
|
return *str == '\\' ? true : (*str ? str_slant(str + 1) : false);
|
|
}
|
|
|
|
constexpr const char* r_slant(const char* str) {
|
|
return *str == '\\' ? (str + 1) : r_slant(str - 1);
|
|
}
|
|
constexpr const char* file_name(const char* str) {
|
|
return str_slant(str) ? r_slant(str_end(str)) : str;
|
|
}
|
|
|
|
#define __FILENAME__ file_name(__FILE__)
|
|
|
|
|
|
//
|
|
// Character escaping functions
|
|
//
|
|
|
|
extern const bool needs_escape(const wint_t _char);
|
|
extern inline void output_char(std::ostream& os, char c);
|
|
extern inline void output_wchar(std::ostream& os, wchar_t c);
|
|
|
|
|
|
//
|
|
// Data sanitization functions
|
|
//
|
|
|
|
// By default, sanitization functions simply return the given argument
|
|
// (type and value) which results in calls to standard output writers.
|
|
template<class T>
|
|
inline auto _log_sanitize(T value, int ignored_length = 0)
|
|
{
|
|
// This is necessary because C++20 has deleted the overloaded operator<< of ostream for wchar_t, char8_t, char16_t and char32_t.
|
|
// The proper solution is to use wide strings in all our logging macros/functions, see https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/issues/743
|
|
if constexpr (std::is_same_v<xbox::wchar_xt, std::remove_cvref_t<std::remove_pointer_t<T>>>) {
|
|
if constexpr (std::is_pointer_v<T>) {
|
|
const wchar_t *wstr = reinterpret_cast<const wchar_t *>(value);
|
|
std::size_t len = std::wcslen(wstr);
|
|
std::string dst(len + 1, '\0');
|
|
std::mbstate_t ps{};
|
|
std::wcsrtombs(dst.data(), &wstr, len, &ps);
|
|
return dst;
|
|
}
|
|
else {
|
|
const wchar_t *wstr = reinterpret_cast<const wchar_t *>(&value);
|
|
std::string dst(2, '\0');
|
|
std::mbstate_t ps{};
|
|
std::wcsrtombs(dst.data(), &wstr, 1, &ps);
|
|
return dst;
|
|
}
|
|
}
|
|
else {
|
|
return value;
|
|
}
|
|
}
|
|
|
|
#if 0 // TODO FIXME : Disabled for now, as this is incorrectly called for INT types too
|
|
// Convert booleans to strings properly
|
|
inline const char * _log_sanitize(BOOL value, int ignored_length = 0)
|
|
{
|
|
return value ? "TRUE" : "FALSE";
|
|
}
|
|
#endif
|
|
|
|
// Macro to ease declaring a _log_sanitize overload (invokeable via C) for type T
|
|
#define LOG_SANITIZE_HEADER(C, T) \
|
|
std::ostream& operator<<( \
|
|
std::ostream& os, \
|
|
const Sane##C& container) \
|
|
|
|
#define LOG_SANITIZE(C, T) \
|
|
struct Sane##C \
|
|
{ \
|
|
T value; \
|
|
int max; \
|
|
Sane##C(T _v, int _m = 80) : value(_v), max(_m) { } \
|
|
}; \
|
|
\
|
|
inline Sane##C C(T value, int max = 80) \
|
|
{ \
|
|
return Sane##C(value, max); \
|
|
} \
|
|
\
|
|
inline Sane##C _log_sanitize(T value, int max = 80) \
|
|
{ \
|
|
return C(value, max); \
|
|
} \
|
|
\
|
|
extern LOG_SANITIZE_HEADER(C, T) \
|
|
|
|
// Hex output (type safe)
|
|
// https://stackoverflow.com/questions/673240/how-do-i-print-an-unsigned-char-as-hex-in-c-using-ostream
|
|
LOG_SANITIZE(hex1, uint8_t);
|
|
LOG_SANITIZE(hex2, uint16_t);
|
|
LOG_SANITIZE(hex4, uint32_t);
|
|
|
|
// Character output (escaped into a C-string representation)
|
|
// https://en.wikipedia.org/wiki/Escape_sequences_in_C
|
|
LOG_SANITIZE(sanitized_char, char);
|
|
LOG_SANITIZE(sanitized_wchar, wchar_t);
|
|
LOG_SANITIZE(sanitized_char_pointer, char *);
|
|
LOG_SANITIZE(sanitized_wchar_pointer, wchar_t *);
|
|
|
|
|
|
//
|
|
// Function (and argument) logging defines
|
|
//
|
|
|
|
constexpr const int str_length(const char* str) {
|
|
return str_end(str) - str;
|
|
}
|
|
|
|
constexpr const char* str_skip_prefix(const char* str, const char *prefix) {
|
|
return (*str == *prefix) ? str_skip_prefix(str + 1, prefix + 1) : str;
|
|
}
|
|
|
|
constexpr const char* remove_prefix(const char* str, const char *prefix) {
|
|
return (str_skip_prefix(str, prefix) == str + str_length(prefix)) ? str_skip_prefix(str, prefix) : str;
|
|
}
|
|
|
|
static constexpr const char* xbox_prefix = "xbox::";
|
|
static constexpr const char* emupatch_prefix = "EmuPatch_"; // See #define EMUPATCH
|
|
|
|
constexpr const char* remove_emupatch_prefix(const char* str) {
|
|
// return an empty string when str isn't given
|
|
// skip xbox:: and/or EmuPatch_ prefix if present
|
|
return remove_prefix(remove_prefix(str, xbox_prefix), emupatch_prefix);
|
|
}
|
|
|
|
#define LOG_ARG_START "\n " << std::setfill(' ') << std::setw(20) << std::left
|
|
#define LOG_ARG_OUT_START "\n OUT " << std::setfill(' ') << std::setw(18) << std::left
|
|
|
|
#ifndef LOG_PREFIX
|
|
#define LOG_PREFIX __FILENAME__
|
|
#endif // LOG_PREFIX
|
|
|
|
// 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
|
|
extern thread_local std::string _logThreadPrefix;
|
|
|
|
// Checks if this log should be printed or not
|
|
#define LOG_CHECK_ENABLED_EX(cxbxr_module, level) \
|
|
if (g_EnabledModules[to_underlying(cxbxr_module)] && to_underlying(level) >= g_CurrentLogLevel)
|
|
|
|
// Checks if this log should be printed or not
|
|
#define LOG_CHECK_ENABLED(level) \
|
|
LOG_CHECK_ENABLED_EX(LOG_PREFIX, level)
|
|
|
|
#define LOG_THREAD_INIT \
|
|
if (_logThreadPrefix.length() == 0) { \
|
|
std::stringstream tmp; \
|
|
tmp << "[" << hexstring16 << GetCurrentThreadId() << "] "; \
|
|
_logThreadPrefix = tmp.str(); \
|
|
}
|
|
|
|
#define LOG_FUNC_INIT(func) \
|
|
static thread_local std::string _logFuncPrefix; \
|
|
if (_logFuncPrefix.length() == 0) { \
|
|
std::stringstream tmp; \
|
|
tmp << g_EnumModules2String[to_underlying(LOG_PREFIX)] << (func != nullptr ? remove_emupatch_prefix(func) : ""); \
|
|
_logFuncPrefix = tmp.str(); \
|
|
}
|
|
|
|
#define LOG_INIT \
|
|
LOG_THREAD_INIT \
|
|
LOG_FUNC_INIT(__func__)
|
|
|
|
#define LOG_FINIT \
|
|
_logFuncPrefix.clear(); // Reset prefix, to show caller changes
|
|
|
|
#define LOG_FUNC_BEGIN_NO_INIT \
|
|
do { if(g_bPrintfOn) { \
|
|
bool _had_arg = false; \
|
|
std::stringstream msg; \
|
|
msg << _logThreadPrefix << _logFuncPrefix << "(";
|
|
|
|
#define LOG_FUNC_BEGIN \
|
|
LOG_INIT \
|
|
LOG_CHECK_ENABLED(LOG_LEVEL::DEBUG) { \
|
|
LOG_FUNC_BEGIN_NO_INIT
|
|
|
|
// LOG_FUNC_ARG writes output via all available ostream << operator overloads, sanitizing and adding detail where possible
|
|
#define LOG_FUNC_ARG(arg) \
|
|
_had_arg = true; \
|
|
msg << LOG_ARG_START << #arg << " : " << _log_sanitize(arg);
|
|
|
|
// LOG_FUNC_ARG_TYPE writes output using the overloaded << operator of the given type
|
|
#define LOG_FUNC_ARG_TYPE(type, arg) \
|
|
_had_arg = true; \
|
|
msg << LOG_ARG_START << #arg << " : " << (type)arg;
|
|
|
|
// LOG_FUNC_ARG_OUT prevents expansion of types, by only rendering as a pointer
|
|
#define LOG_FUNC_ARG_OUT(arg) \
|
|
_had_arg = true; \
|
|
msg << LOG_ARG_OUT_START << #arg << " : " << hex4((uint32_t)arg);
|
|
|
|
// LOG_FUNC_END closes off function and optional argument logging
|
|
#define LOG_FUNC_END \
|
|
if (_had_arg) msg << "\n"; \
|
|
msg << ");\n"; \
|
|
std::cout << msg.str(); \
|
|
} } while (0); \
|
|
}
|
|
|
|
#define LOG_FUNC_BEGIN_ARG_RESULT_NO_INIT \
|
|
do { if(g_bPrintfOn) { \
|
|
bool _had_arg = false; \
|
|
std::stringstream msg; \
|
|
msg << _logThreadPrefix << _logFuncPrefix << " returns OUT {";
|
|
|
|
#define LOG_FUNC_BEGIN_ARG_RESULT \
|
|
LOG_CHECK_ENABLED(LOG_LEVEL::DEBUG) { \
|
|
LOG_FUNC_BEGIN_ARG_RESULT_NO_INIT
|
|
|
|
// LOG_FUNC_ARG_RESULT writes output via all available ostream << operator overloads, sanitizing and adding detail where possible
|
|
#define LOG_FUNC_ARG_RESULT(arg) \
|
|
_had_arg = true; \
|
|
msg << LOG_ARG_START << "*"#arg << " : "; \
|
|
if (arg != nullptr) { \
|
|
msg << _log_sanitize(*arg); \
|
|
} else { \
|
|
msg << "NOT SET"; \
|
|
}
|
|
|
|
// LOG_FUNC_ARG_RESULT_TYPE writes result output using the overloaded << operator of the given type
|
|
#define LOG_FUNC_ARG_RESULT_TYPE(type, arg) \
|
|
_had_arg = true; \
|
|
msg << LOG_ARG_START << "*"#arg << " : "; \
|
|
if (arg != nullptr) { \
|
|
msg << (type)*arg; \
|
|
} else { \
|
|
msg << "NOT SET"; \
|
|
}
|
|
|
|
// LOG_FUNC_END_ARG_RESULT closes off function and optional argument result logging
|
|
#define LOG_FUNC_END_ARG_RESULT \
|
|
if (_had_arg) msg << "\n"; \
|
|
msg << "};\n"; \
|
|
std::cout << msg.str(); \
|
|
} } while (0); \
|
|
}
|
|
|
|
// LOG_FUNC_RESULT logs the function return result
|
|
#define LOG_FUNC_RESULT(r) \
|
|
std::cout << _logThreadPrefix << _logFuncPrefix << " returns " << _log_sanitize(r) << "\n";
|
|
|
|
// LOG_FUNC_RESULT_TYPE logs the function return result using the overloaded << operator of the given type
|
|
#define LOG_FUNC_RESULT_TYPE(type, r) \
|
|
std::cout << _logThreadPrefix << _logFuncPrefix << " returns " << (type)r << "\n";
|
|
|
|
// LOG_FORWARD indicates that an api is implemented by a forward to another API
|
|
#define LOG_FORWARD(api) \
|
|
LOG_INIT \
|
|
LOG_CHECK_ENABLED(LOG_LEVEL::DEBUG) { \
|
|
do { if(g_bPrintfOn) { \
|
|
std::cout << _logThreadPrefix << _logFuncPrefix << " forwarding to "#api"...\n"; \
|
|
} } while (0); \
|
|
}
|
|
|
|
// LOG_IGNORED indicates that Cxbx consiously ignores an api
|
|
#define LOG_IGNORED() \
|
|
do { \
|
|
static bool b_echoOnce = true; \
|
|
if(g_bPrintfOn && b_echoOnce) { \
|
|
LOG_CHECK_ENABLED(LOG_LEVEL::INFO) { \
|
|
LOG_THREAD_INIT \
|
|
LOG_FUNC_INIT(__func__) \
|
|
std::cout << _logThreadPrefix << "WARN: " << _logFuncPrefix << " ignored!\n"; \
|
|
b_echoOnce = false; \
|
|
} \
|
|
} \
|
|
} while(0)
|
|
|
|
// LOG_UNIMPLEMENTED indicates that Cxbx is missing an implementation of an api
|
|
#define LOG_UNIMPLEMENTED() \
|
|
do { \
|
|
static bool b_echoOnce = true; \
|
|
if(g_bPrintfOn && b_echoOnce) { \
|
|
LOG_CHECK_ENABLED(LOG_LEVEL::INFO) { \
|
|
LOG_THREAD_INIT \
|
|
LOG_FUNC_INIT(__func__) \
|
|
std::cout << _logThreadPrefix << "WARN: " << _logFuncPrefix << " unimplemented!\n"; \
|
|
b_echoOnce = false; \
|
|
} \
|
|
} \
|
|
} while(0)
|
|
|
|
// LOG_INCOMPLETE indicates that Cxbx is missing part of an implementation of an api
|
|
#define LOG_INCOMPLETE() \
|
|
do { \
|
|
static bool b_echoOnce = true; \
|
|
if(g_bPrintfOn && b_echoOnce) { \
|
|
LOG_CHECK_ENABLED(LOG_LEVEL::INFO) { \
|
|
LOG_THREAD_INIT \
|
|
LOG_FUNC_INIT(__func__) \
|
|
std::cout << _logThreadPrefix << "WARN: " << _logFuncPrefix << " incomplete!\n"; \
|
|
b_echoOnce = false; \
|
|
} \
|
|
} \
|
|
} while(0)
|
|
|
|
// LOG_NOT_SUPPORTED indicates that Cxbx cannot implement (part of) an api
|
|
#define LOG_NOT_SUPPORTED() \
|
|
do { \
|
|
static bool b_echoOnce = true; \
|
|
if(g_bPrintfOn && b_echoOnce) { \
|
|
LOG_CHECK_ENABLED(LOG_LEVEL::INFO) { \
|
|
LOG_THREAD_INIT \
|
|
LOG_FUNC_INIT(__func__) \
|
|
std::cout << _logThreadPrefix << "WARN: " << _logFuncPrefix << " not supported!\n"; \
|
|
b_echoOnce = false; \
|
|
} \
|
|
} \
|
|
} while(0)
|
|
|
|
//
|
|
// Short hand function logging defines :
|
|
//
|
|
|
|
// Log function without arguments
|
|
#define LOG_FUNC() LOG_FUNC_BEGIN LOG_FUNC_END
|
|
|
|
// Log function with one argument
|
|
#define LOG_FUNC_ONE_ARG(arg) LOG_FUNC_BEGIN LOG_FUNC_ARG(arg) LOG_FUNC_END
|
|
|
|
// Log function with one typed argument
|
|
#define LOG_FUNC_ONE_ARG_TYPE(type, arg) LOG_FUNC_BEGIN LOG_FUNC_ARG_TYPE(type, arg) LOG_FUNC_END
|
|
|
|
// Log function with one out argument
|
|
#define LOG_FUNC_ONE_ARG_OUT(arg) LOG_FUNC_BEGIN LOG_FUNC_ARG_OUT(arg) LOG_FUNC_END
|
|
|
|
// RETURN logs the given result and then returns it (so this should appear last in functions)
|
|
#define RETURN(r) do { if (g_bPrintfOn) LOG_CHECK_ENABLED(LOG_LEVEL::DEBUG) { LOG_FUNC_RESULT(r) } return r; } while (0)
|
|
|
|
// RETURN_TYPE logs the given typed result and then returns it (so this should appear last in functions)
|
|
#define RETURN_TYPE(type, r) do { if (g_bPrintfOn) LOG_CHECK_ENABLED(LOG_LEVEL::DEBUG) { LOG_FUNC_RESULT_TYPE(type, r) } return r; } while (0)
|
|
|
|
//
|
|
// Headers for rendering enums, flags and (pointer-to-)types :
|
|
//
|
|
|
|
// Macro to ease declaration of a render function per Type:
|
|
#define LOGRENDER_HEADER_BY_REF(Type) std::ostream& operator<<(std::ostream& os, const Type& value)
|
|
#define LOGRENDER_HEADER_BY_PTR(Type) std::ostream& operator<<(std::ostream& os, const Type *value)
|
|
|
|
#define TYPE2PCHAR(Type) Type##ToPCHAR
|
|
#define TYPE2PCHAR_HEADER(Type) const char * TYPE2PCHAR(Type)(const Type &value)
|
|
|
|
// Macro for implementation of rendering any Type-ToString :
|
|
#define LOGRENDER_TYPE(Type) LOGRENDER_HEADER_BY_REF(Type) \
|
|
{ return os << "("#Type")" << hex4((int)value) << " = " << TYPE2PCHAR(Type)(value); }
|
|
|
|
// Macro's for Enum-ToString conversions :
|
|
#define ENUM2STR_HEADER(EnumType) extern TYPE2PCHAR_HEADER(EnumType); LOGRENDER_HEADER_BY_REF(EnumType);
|
|
#define ENUM2STR_START(EnumType) TYPE2PCHAR_HEADER(EnumType) { switch (value) {
|
|
#define ENUM2STR_CASE(a) case a: return #a;
|
|
#define ENUM2STR_END(EnumType) default: return "Unknown_"#EnumType; } }
|
|
#define ENUM2STR_END_and_LOGRENDER(EnumType) ENUM2STR_END(EnumType) LOGRENDER_TYPE(EnumType)
|
|
|
|
// Macro's for Flags-ToString conversions :
|
|
#define FLAGS2STR_HEADER(FlagType) LOGRENDER_HEADER_BY_REF(FlagType);
|
|
#define FLAGS2STR_START(FlagType) std::string TYPE2PCHAR(FlagType)(const FlagType &value) { std::stringstream ss;
|
|
#define FLAG2STR(f) if (((uint32_t)value & f) == f) ss << #f << "|";
|
|
#define FLAG2STR_MASK(f) ss << #f"=" << (value & f) << "|";
|
|
#define FLAGS2STR_END std::string res = ss.str(); if (!res.empty()) res.pop_back(); return res; }
|
|
#define FLAGS2STR_END_and_LOGRENDER(FlagType) FLAGS2STR_END LOGRENDER_TYPE(FlagType)
|
|
|
|
// Macro's for Struct-member rendering :
|
|
#define LOGRENDER_MEMBER_ARRAY_OFFSET(Name, Offset) << LOG_ARG_START << (Name) << "[" << (Offset) << "]: {"
|
|
#define LOGRENDER_MEMBER_NAME_VALUE(Name, Value) << LOG_ARG_START << (Name) << " : " << (Value)
|
|
#define LOGRENDER_MEMBER_NAME_TYPE_PTR(Name, Type, Value) << LOG_ARG_START << (Name) << " : " << hex4((uint32_t)Value) << " -> " << (Type) << "* {"
|
|
#define LOGRENDER_MEMBER_NAME_TYPE_VALUE(Name, Type, Value) << LOG_ARG_START << (Name) << " : " << (Type)(Value)
|
|
#define LOGRENDER_MEMBER_NAME(Member) << LOG_ARG_START << "."#Member << " : "
|
|
#define LOGRENDER_MEMBER(Member) LOGRENDER_MEMBER_NAME_VALUE("."#Member, value.Member)
|
|
#define LOGRENDER_MEMBER_TYPE(Type, Member) LOGRENDER_MEMBER_NAME(Member) << (Type)value.Member
|
|
#define LOGRENDER_MEMBER_SANITIZED(Member, MemberType, Length) LOGRENDER_MEMBER_NAME(Member) << _log_sanitize((MemberType)value.Member, Length)
|
|
|
|
// Macro to ease declaration of two render functions, for type and pointer-to-type :
|
|
#define LOGRENDER_HEADER(Type) LOGRENDER_HEADER_BY_PTR(Type); LOGRENDER_HEADER_BY_REF(Type);
|
|
|
|
// Macro's for dynamic array structure rendering :
|
|
template<class A, class B>
|
|
static std::string
|
|
LogRenderGenerate_Array(std::string MemberStr, A MemberValue, B Count) {
|
|
B counter = 0;
|
|
std::ostringstream strGenerator;
|
|
while (counter < Count) {
|
|
strGenerator LOGRENDER_MEMBER_ARRAY_OFFSET(MemberStr, counter)
|
|
<< *MemberValue << "}";
|
|
MemberValue++;
|
|
counter++;
|
|
}
|
|
strGenerator << "}";
|
|
return strGenerator.str();
|
|
}
|
|
#define LOGRENDER_MEMBER_ARRAY_TYPE(Type, Member, Count) LOGRENDER_MEMBER_NAME_TYPE_PTR("."#Member, #Type, value.Member) << LogRenderGenerate_Array("."#Member, (Type*)value.Member, value.Count)
|
|
|
|
// Traits to switch ostreams to our preferred rendering of hexadecimal values
|
|
template <class _CharT, class _Traits>
|
|
std::basic_ostream<_CharT, _Traits>&
|
|
hexstring8(std::basic_ostream<_CharT, _Traits>&os)
|
|
{
|
|
// std::noshowbase is not neccessary to set (it's the default, and we never use std::showbase)
|
|
return os << "0x" << std::setfill('0') << std::setw(2) << std::right << std::hex << std::uppercase;
|
|
}
|
|
|
|
template <class _CharT, class _Traits>
|
|
std::basic_ostream<_CharT, _Traits>&
|
|
hexstring16(std::basic_ostream<_CharT, _Traits>&os)
|
|
{
|
|
return os << "0x" << std::setfill('0') << std::setw(4) << std::right << std::hex << std::uppercase;
|
|
}
|
|
|
|
template <class _CharT, class _Traits>
|
|
std::basic_ostream<_CharT, _Traits>&
|
|
hexstring32(std::basic_ostream<_CharT, _Traits>&os)
|
|
{
|
|
return os << "0x" << std::setfill('0') << std::setw(8) << std::right << std::hex << std::uppercase;
|
|
}
|
|
|
|
// Macro combining pointer-to-type implementation and type rendering header :
|
|
#define LOGRENDER(Type) \
|
|
LOGRENDER_HEADER_BY_PTR(Type) \
|
|
{ \
|
|
os << hexstring32 << reinterpret_cast<uint32_t>(value); \
|
|
if (value) \
|
|
os << " -> "#Type"* {" << *value << "}"; \
|
|
\
|
|
return os; \
|
|
} \
|
|
\
|
|
LOGRENDER_HEADER_BY_REF(Type) \
|
|
|
|
//
|
|
// An example type rendering, for PVOID
|
|
//
|
|
|
|
LOGRENDER_HEADER_BY_REF(PVOID);
|
|
|
|
#endif _LOGGING_H
|