logs.hpp: refactoring (logs::message)

Make .error/.warning/... callable objects which can be pointed to.
Make .always() more hard to access.
Memory layout optimizations.
This commit is contained in:
Nekotekina 2021-05-19 14:30:39 +03:00
parent 1d0f6eebdc
commit 04cac6cd33
9 changed files with 95 additions and 69 deletions

View file

@ -70,7 +70,7 @@ namespace vk
vkGetPhysicalDeviceMemoryProperties(pdev, &memory_properties); vkGetPhysicalDeviceMemoryProperties(pdev, &memory_properties);
get_physical_device_features(allow_extensions); get_physical_device_features(allow_extensions);
rsx_log.always("Found vulkan-compatible GPU: '%s' running on driver %s", get_name(), get_driver_version()); rsx_log.always()("Found vulkan-compatible GPU: '%s' running on driver %s", get_name(), get_driver_version());
if (get_driver_vendor() == driver_vendor::RADV && get_name().find("LLVM 8.0.0") != umax) if (get_driver_vendor() == driver_vendor::RADV && get_name().find("LLVM 8.0.0") != umax)
{ {

View file

@ -187,7 +187,7 @@ struct fatal_error_listener final : logs::listener
void log(u64 /*stamp*/, const logs::message& msg, const std::string& prefix, const std::string& text) override void log(u64 /*stamp*/, const logs::message& msg, const std::string& prefix, const std::string& text) override
{ {
if (msg.sev == logs::level::fatal) if (msg == logs::level::fatal)
{ {
std::string _msg = "RPCS3: "; std::string _msg = "RPCS3: ";
@ -197,9 +197,9 @@ struct fatal_error_listener final : logs::listener
_msg += ": "; _msg += ": ";
} }
if (msg.ch && '\0' != *msg.ch->name) if (msg->name && '\0' != *msg->name)
{ {
_msg += msg.ch->name; _msg += msg->name;
_msg += ": "; _msg += ": ";
} }
@ -446,31 +446,19 @@ int main(int argc, char** argv)
{ {
// Write RPCS3 version // Write RPCS3 version
logs::stored_message ver; logs::stored_message ver{sys_log.always()};
ver.m.ch = nullptr;
ver.m.sev = logs::level::always;
ver.stamp = 0;
ver.text = fmt::format("RPCS3 v%s | %s", rpcs3::get_version().to_string(), rpcs3::get_branch()); ver.text = fmt::format("RPCS3 v%s | %s", rpcs3::get_version().to_string(), rpcs3::get_branch());
// Write System information // Write System information
logs::stored_message sys; logs::stored_message sys{sys_log.always()};
sys.m.ch = nullptr;
sys.m.sev = logs::level::always;
sys.stamp = 0;
sys.text = utils::get_system_info(); sys.text = utils::get_system_info();
// Write OS version // Write OS version
logs::stored_message os; logs::stored_message os{sys_log.always()};
os.m.ch = nullptr;
os.m.sev = logs::level::always;
os.stamp = 0;
os.text = utils::get_OS_version(); os.text = utils::get_OS_version();
// Write Qt version // Write Qt version
logs::stored_message qt; logs::stored_message qt{(strcmp(QT_VERSION_STR, qVersion()) != 0) ? sys_log.error : sys_log.notice};
qt.m.ch = nullptr;
qt.m.sev = (strcmp(QT_VERSION_STR, qVersion()) != 0) ? logs::level::error : logs::level::notice;
qt.stamp = 0;
qt.text = fmt::format("Qt version: Compiled against Qt %s | Run-time uses Qt %s", QT_VERSION_STR, qVersion()); qt.text = fmt::format("Qt version: Compiled against Qt %s | Run-time uses Qt %s", QT_VERSION_STR, qVersion());
logs::set_init({std::move(ver), std::move(sys), std::move(os), std::move(qt)}); logs::set_init({std::move(ver), std::move(sys), std::move(os), std::move(qt)});

View file

@ -43,7 +43,7 @@ void main_application::InitializeEmulator(const std::string& user, bool show_gui
// Log Firmware Version after Emu was initialized // Log Firmware Version after Emu was initialized
const std::string firmware_version = utils::get_firmware_version(); const std::string firmware_version = utils::get_firmware_version();
const std::string firmware_string = firmware_version.empty() ? "Missing Firmware" : ("Firmware version: " + firmware_version); const std::string firmware_string = firmware_version.empty() ? "Missing Firmware" : ("Firmware version: " + firmware_version);
sys_log.always("%s", firmware_string); sys_log.always()("%s", firmware_string);
} }
/** RPCS3 emulator has functions it desires to call from the GUI at times. Initialize them in here. */ /** RPCS3 emulator has functions it desires to call from the GUI at times. Initialize them in here. */

View file

@ -254,7 +254,7 @@ void gui_settings::SaveCurrentConfig(const QString& config_name)
logs::level gui_settings::GetLogLevel() const logs::level gui_settings::GetLogLevel() const
{ {
return logs::level{GetValue(gui::l_level).toUInt()}; return logs::level(GetValue(gui::l_level).toUInt());
} }
bool gui_settings::GetGamelistColVisibility(int col) const bool gui_settings::GetGamelistColVisibility(int col) const

View file

@ -178,7 +178,7 @@ namespace gui
const gui_save fs_dev_usb000_list = gui_save(fs, "dev_usb000_list", QStringList()); const gui_save fs_dev_usb000_list = gui_save(fs, "dev_usb000_list", QStringList());
const gui_save l_tty = gui_save(logger, "TTY", true); const gui_save l_tty = gui_save(logger, "TTY", true);
const gui_save l_level = gui_save(logger, "level", static_cast<uint>(logs::level::success)); const gui_save l_level = gui_save(logger, "level", static_cast<uchar>(logs::level::success));
const gui_save l_prefix = gui_save(logger, "prefix_on", false); const gui_save l_prefix = gui_save(logger, "prefix_on", false);
const gui_save l_stack = gui_save(logger, "stack", true); const gui_save l_stack = gui_save(logger, "stack", true);
const gui_save l_stack_tty = gui_save(logger, "TTY_stack", false); const gui_save l_stack_tty = gui_save(logger, "TTY_stack", false);

View file

@ -26,7 +26,7 @@ constexpr auto qstr = QString::fromStdString;
struct gui_listener : logs::listener struct gui_listener : logs::listener
{ {
atomic_t<logs::level> enabled{logs::level{UINT_MAX}}; atomic_t<logs::level> enabled{logs::level{UCHAR_MAX}};
struct packet_t struct packet_t
{ {
@ -55,10 +55,10 @@ struct gui_listener : logs::listener
{ {
Q_UNUSED(stamp) Q_UNUSED(stamp)
if (msg.sev <= enabled) if (msg <= enabled)
{ {
packet_t p,* _new = &p; packet_t p,* _new = &p;
_new->sev = msg.sev; _new->sev = msg;
if (show_prefix && !prefix.empty()) if (show_prefix && !prefix.empty())
{ {
@ -67,12 +67,12 @@ struct gui_listener : logs::listener
_new->msg += "} "; _new->msg += "} ";
} }
if (msg.ch && '\0' != *msg.ch->name) if (msg->name && '\0' != *msg->name)
{ {
_new->msg += msg.ch->name; _new->msg += msg->name;
_new->msg += msg.sev == logs::level::todo ? " TODO: " : ": "; _new->msg += msg == logs::level::todo ? " TODO: " : ": ";
} }
else if (msg.sev == logs::level::todo) else if (msg == logs::level::todo)
{ {
_new->msg += "TODO: "; _new->msg += "TODO: ";
} }

View file

@ -65,6 +65,14 @@ void fmt_class_string<logs::level>::format(std::string& out, u64 arg)
namespace logs namespace logs
{ {
static_assert(std::is_empty_v<message> && sizeof(message) == 1);
static_assert(sizeof(channel) == alignof(channel));
static_assert(uchar(level::always) == 0);
static_assert(uchar(level::fatal) == 1);
static_assert(uchar(level::trace) == 7);
static_assert((offsetof(channel, fatal) & 7) == 1);
static_assert((offsetof(channel, trace) & 7) == 7);
// Memory-mapped buffer size // Memory-mapped buffer size
constexpr u64 s_log_size = 32 * 1024 * 1024; constexpr u64 s_log_size = 32 * 1024 * 1024;
@ -618,7 +626,7 @@ void logs::file_listener::log(u64 stamp, const logs::message& msg, const std::st
text.reserve(50000); text.reserve(50000);
// Used character: U+00B7 (Middle Dot) // Used character: U+00B7 (Middle Dot)
switch (msg.sev) switch (msg)
{ {
case level::always: text = reinterpret_cast<const char*>(u8"·A "); break; case level::always: text = reinterpret_cast<const char*>(u8"·A "); break;
case level::fatal: text = reinterpret_cast<const char*>(u8"·F "); break; case level::fatal: text = reinterpret_cast<const char*>(u8"·F "); break;
@ -637,7 +645,7 @@ void logs::file_listener::log(u64 stamp, const logs::message& msg, const std::st
const u64 frac = (stamp % 1'000'000); const u64 frac = (stamp % 1'000'000);
fmt::append(text, "%u:%02u:%02u.%06u ", hours, mins, secs, frac); fmt::append(text, "%u:%02u:%02u.%06u ", hours, mins, secs, frac);
if (msg.ch == nullptr && stamp == 0) if (stamp == 0)
{ {
// Workaround for first special messages to keep backward compatibility // Workaround for first special messages to keep backward compatibility
text.clear(); text.clear();
@ -650,12 +658,12 @@ void logs::file_listener::log(u64 stamp, const logs::message& msg, const std::st
text += "} "; text += "} ";
} }
if (msg.ch && '\0' != *msg.ch->name) if (msg->name && '\0' != *msg->name)
{ {
text += msg.ch->name; text += msg->name;
text += msg.sev == level::todo ? " TODO: " : ": "; text += msg == level::todo ? " TODO: " : ": ";
} }
else if (msg.sev == level::todo) else if (msg == level::todo)
{ {
text += "TODO: "; text += "TODO: ";
} }

View file

@ -10,16 +10,16 @@
namespace logs namespace logs
{ {
enum class level : unsigned enum class level : unsigned char
{ {
always, // Highest log severity (cannot be disabled) always = 0, // Highest log severity (cannot be disabled)
fatal, fatal = 1,
error, error = 2,
todo, todo = 3,
success, success = 4,
warning, warning = 5,
notice, notice = 6,
trace, // Lowest severity (usually disabled) trace = 7, // Lowest severity (usually disabled)
}; };
struct channel; struct channel;
@ -27,8 +27,27 @@ namespace logs
// Message information // Message information
struct message struct message
{ {
channel* ch; // Default constructor
level sev; consteval message() = default;
// Cannot be moved because it relies on its location
message(const message&) = delete;
message& operator =(const message&) = delete;
// Send log message to the given channel with severity
template <typename... Args>
void operator()(const const_str& fmt, const Args&... args) const;
operator level() const
{
return level(reinterpret_cast<uptr>(this) & 7);
}
const channel* operator->() const
{
return reinterpret_cast<const channel*>(reinterpret_cast<uptr>(this) & -16);
}
private: private:
// Send log message to global logger instance // Send log message to global logger instance
@ -39,7 +58,7 @@ namespace logs
struct stored_message struct stored_message
{ {
message m; const message& m;
u64 stamp; u64 stamp;
std::string prefix; std::string prefix;
std::string text; std::string text;
@ -67,7 +86,7 @@ namespace logs
void broadcast(const stored_message&) const; void broadcast(const stored_message&) const;
}; };
struct channel struct alignas(16) channel : private message
{ {
// Channel prefix (added to every log message) // Channel prefix (added to every log message)
const char* const name; const char* const name;
@ -76,31 +95,22 @@ namespace logs
atomic_t<level> enabled; atomic_t<level> enabled;
// Initialize channel // Initialize channel
constexpr channel(const char* name) noexcept consteval channel(const char* name) noexcept
: name(name) : message{}
, name(name)
, enabled(level::notice) , enabled(level::notice)
{ {
} }
#define GEN_LOG_METHOD(_sev)\ // Special access to "always visible" channel which shouldn't be used
const message msg_##_sev{this, level::_sev};\ const message& always() const
template <typename... Args>\ {
void _sev(const const_str& fmt, const Args&... args)\ return *this;
{\ }
if (level::_sev <= enabled.observe()) [[unlikely]]\
{\ #define GEN_LOG_METHOD(_sev)\
if constexpr (sizeof...(Args) > 0)\ const message _sev{};\
{\
msg_##_sev.broadcast(fmt, fmt::type_info_v<Args...>, u64{fmt_unveil<Args>::get(args)}...);\
}\
else\
{\
msg_##_sev.broadcast(fmt, nullptr);\
}\
}\
}\
GEN_LOG_METHOD(always)
GEN_LOG_METHOD(fatal) GEN_LOG_METHOD(fatal)
GEN_LOG_METHOD(error) GEN_LOG_METHOD(error)
GEN_LOG_METHOD(todo) GEN_LOG_METHOD(todo)
@ -112,6 +122,22 @@ namespace logs
#undef GEN_LOG_METHOD #undef GEN_LOG_METHOD
}; };
template <typename... Args>
FORCE_INLINE SAFE_BUFFERS(void) message::operator()(const const_str& fmt, const Args&... args) const
{
if (*this < (*this)->enabled) [[unlikely]]
{
if constexpr (sizeof...(Args) > 0)
{
broadcast(fmt, fmt::type_info_v<Args...>, u64{fmt_unveil<Args>::get(args)}...);
}
else
{
broadcast(fmt, nullptr);
}
}
}
struct registerer struct registerer
{ {
registerer(channel& _ch); registerer(channel& _ch);

View file

@ -73,6 +73,10 @@ namespace std
} }
#endif #endif
#if defined(__INTELLISENSE__)
#define consteval constexpr
#endif
using schar = signed char; using schar = signed char;
using uchar = unsigned char; using uchar = unsigned char;
using ushort = unsigned short; using ushort = unsigned short;