Compare commits

...

26 Commits

Author SHA1 Message Date
22e14d760a Use libbacktrace for Linux/macOS 2020-12-16 07:31:39 -05:00
81fb4e4c2d Link debug libnx & remove outdated nxstl includes 2020-10-21 00:35:07 -04:00
Jack Andersen
7f63cabaea Fixes for recent windows SDK changes 2020-08-22 17:28:34 -10:00
41432143fd Only use lm service when LOGVISOR_NX_LM set 2020-05-05 00:14:41 -04:00
Jack Andersen
14ea54f8b5 Add lm service for switch build 2020-05-03 20:09:21 -10:00
Jack Andersen
d62b4ce26e Update fmtlib 2020-04-11 12:44:21 -10:00
Jack Andersen
27814c5276 Exclude CMake CXX standard from MSVC 2020-04-10 18:55:45 -10:00
Jack Andersen
f04a9777eb Merge branch 'master' of ssh://git.axiodl.com:6431/AxioDL/logvisor 2019-09-30 21:21:21 -10:00
Jack Andersen
187c35f3c4 Ensure only one console logger is able to be registered 2019-09-30 21:20:45 -10:00
8c2e711362 Merge pull request #5 from lioncash/noreturn
logvisor: Mark logvisorAbort as [[noreturn]]
2019-09-06 23:26:44 -07:00
Lioncash
63cb911d09 logvisor: Mark logvisorAbort as [[noreturn]]
All variants of logvisorAbort do what they say in their name -- abort.
Given control isn't returned from this function, we can signify that
it's a noreturn function
2019-09-06 07:37:27 -04:00
f623ace3b4 Merge pull request #4 from lioncash/docs
logvisor: Amend documentation comments
2019-08-26 15:55:18 -07:00
8ea97c524c Merge pull request #3 from lioncash/forward
logvisor: std::forward arguments where applicable
2019-08-26 15:55:08 -07:00
81c72ad220 Merge pull request #2 from lioncash/char
logvisor: Use std::fputs/std::fputc where applicable
2019-08-26 15:54:58 -07:00
Lioncash
59f651f24b logvisor: Amend documentation comments
These use fmt-style formatting specifiers, not printf.
2019-08-26 10:57:19 -04:00
Lioncash
2a40c6dc92 logvisor: std::forward arguments where applicable
Forwards arguments into functions to preserve their value category
2019-08-26 10:50:35 -04:00
Lioncash
2f3f06e5ca logvisor: Add missing override specifiers 2019-08-26 10:34:40 -04:00
Lioncash
94d1c558a0 logvisor: Use std::fputs/std::fputc where applicable
We can just use std::fputc when inserting a single character instead of
using std::fputs. Likewise, we can use std::fputs over std::fprintf when
no formatting specifiers are present.
2019-08-26 10:31:45 -04:00
aa9aa0a82c Merge pull request #1 from lioncash/cexpr
logvisor: Make Module constructor constexpr
2019-08-17 22:56:59 -07:00
Lioncash
a57409828c logvisor: Make Module constructor constexpr
Allows the module instances to be constructed at compile time.
Otherwise, this is technically a runtime static constructor when an
instance is declared at file scope.
2019-08-17 19:47:20 -04:00
Jack Andersen
dcd0ffcaec Fix custom formatting of multiple character types 2019-07-25 16:31:54 -10:00
Jack Andersen
3bedd268e8 Integrate libfmt for format strings 2019-07-19 18:21:39 -10:00
Jack Andersen
a0ef17d895 Refactor of CMake for cleaner dependency handling 2019-06-11 15:53:17 -10:00
Jack Andersen
ebe7463e67 Ignore GCC truncation warning 2019-06-09 16:48:06 -10:00
Jack Andersen
7672485d81 Make logvisor publicly link libdl on platforms that have it 2019-05-25 00:14:41 -10:00
Jack Andersen
c54e2596c2 Add cmake package config files 2019-05-22 18:07:58 -10:00
9 changed files with 621 additions and 821 deletions

6
.gitmodules vendored Normal file
View File

@@ -0,0 +1,6 @@
[submodule "fmt"]
path = fmt
url = https://github.com/fmtlib/fmt
[submodule "libbacktrace"]
path = libbacktrace
url = https://github.com/status-im/libbacktrace.git

View File

@@ -1,8 +1,91 @@
include_directories(include)
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
cmake_minimum_required(VERSION 3.10 FATAL_ERROR) # because of c++17
project(nod VERSION 0.1)
if (NOT MSVC)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()
endif()
include (CMakePackageConfigHelpers)
add_subdirectory(fmt)
add_library(logvisor
lib/logvisor.cpp
include/logvisor/logvisor.hpp)
set(LOGVISOR_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include CACHE PATH "logvisor include path" FORCE)
if(APPLE OR UNIX)
include(ExternalProject)
find_package(Git REQUIRED)
find_program(MAKE_EXE NAMES gmake make)
ExternalProject_Add(
project_libbacktrace
PREFIX ${PROJECT_BINARY_DIR}/libbacktrace
GIT_REPOSITORY https://github.com/status-im/libbacktrace.git
GIT_TAG 2e878a38dd7144c84b665019b1085ea38ebe56d1
CONFIGURE_COMMAND ${PROJECT_BINARY_DIR}/libbacktrace/src/project_libbacktrace/configure --enable-host-shared --prefix=${PROJECT_BINARY_DIR}/libbacktrace/install --enable-shared=no --enable-static=yes
BUILD_COMMAND ${MAKE_EXE}
INSTALL_COMMAND ${MAKE_EXE} install
BUILD_IN_SOURCE 1
)
add_library(libbacktrace STATIC IMPORTED GLOBAL)
set_target_properties(libbacktrace PROPERTIES IMPORTED_LOCATION "${PROJECT_BINARY_DIR}/libbacktrace/install/lib/libbacktrace.a")
set_target_properties(libbacktrace PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${PROJECT_BINARY_DIR}/libbacktrace/install/include")
# Hack for INTERFACE_INCLUDE_DIRECTORIES propagation
# https://gitlab.kitware.com/cmake/cmake/-/issues/15052
file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/libbacktrace/install/include")
add_dependencies(libbacktrace project_libbacktrace)
set(BACKTRACE_LIBS libbacktrace)
else()
set(BACKTRACE_LIBS "")
endif()
target_link_libraries(logvisor PUBLIC fmt)
if(NX)
target_link_libraries(logvisor PUBLIC debug nxd optimized nx)
else()
target_link_libraries(logvisor PRIVATE ${CMAKE_DL_LIBS} ${BACKTRACE_LIBS})
endif()
target_include_directories(logvisor PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
install(DIRECTORY include/logvisor DESTINATION include)
install(DIRECTORY fmt/include/fmt DESTINATION include)
set(version_config_file "${PROJECT_BINARY_DIR}/logvisorConfigVersion.cmake")
set(config_file "${PROJECT_BINARY_DIR}/logvisorConfig.cmake")
set(config_install_dir "lib/cmake/logvisor")
# Associate target with export
install(
TARGETS logvisor fmt
EXPORT logvisorTargets
ARCHIVE DESTINATION "lib"
INCLUDES DESTINATION include # This sets the INTERFACE_INCLUDE_DIRECTORIES property of the target.
)
# Install the target config files
install(
EXPORT logvisorTargets
NAMESPACE "logvisor::"
DESTINATION "${config_install_dir}"
)
# Generate version config file
write_basic_package_version_file(
"${version_config_file}"
COMPATIBILITY SameMajorVersion
)
# Generate config file
configure_package_config_file(
"Config.cmake.in"
"${config_file}"
INSTALL_DESTINATION "lib/cmake/logvisor"
)
# Install the config files
install(
FILES "${config_file}" "${version_config_file}"
DESTINATION ${config_install_dir}
)

4
Config.cmake.in Normal file
View File

@@ -0,0 +1,4 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/logvisorTargets.cmake")
check_required_components(logvisor)

1
fmt Submodule

Submodule fmt added at 9bdd1596ce

View File

@@ -1,6 +1,5 @@
#pragma once
#include <cstdarg>
#include <cstring>
#include <cstdio>
#include <cstdlib>
@@ -9,15 +8,19 @@
#include <memory>
#include <mutex>
#ifdef __SWITCH__
#include "nxstl/mutex"
#define FMT_STRING_ALIAS 1
#define FMT_ENFORCE_COMPILE_STRING 1
#include <fmt/format.h>
#if defined(__SWITCH__) && !defined(LOGVISOR_NX_LM)
#define LOGVISOR_NX_LM 0
#endif
extern "C" void logvisorBp();
namespace logvisor {
void logvisorAbort();
[[noreturn]] void logvisorAbort();
#if _WIN32 && UNICODE
#define LOG_UCS2 1
@@ -40,13 +43,13 @@ enum Level {
* @brief Backend interface for receiving app-wide log events
*/
struct ILogger {
virtual ~ILogger() {}
virtual void report(const char* modName, Level severity, const char* format, va_list ap) = 0;
virtual void report(const char* modName, Level severity, const wchar_t* format, va_list ap) = 0;
virtual void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, const char* format,
va_list ap) = 0;
virtual ~ILogger() = default;
virtual void report(const char* modName, Level severity, fmt::string_view format, fmt::format_args args) = 0;
virtual void report(const char* modName, Level severity, fmt::wstring_view format, fmt::wformat_args args) = 0;
virtual void reportSource(const char* modName, Level severity, const char* file, unsigned linenum,
const wchar_t* format, va_list ap) = 0;
fmt::string_view format, fmt::format_args args) = 0;
virtual void reportSource(const char* modName, Level severity, const char* file, unsigned linenum,
fmt::wstring_view format, fmt::wformat_args args) = 0;
};
/**
@@ -107,7 +110,7 @@ extern LogMutex _LogMutex;
* @brief Take a centralized lock for the logging output stream(s)
* @return RAII mutex lock
*/
static inline std::unique_lock<std::recursive_mutex> LockLog() { return _LogMutex.lock(); }
inline std::unique_lock<std::recursive_mutex> LockLog() { return _LogMutex.lock(); }
extern uint64_t _LogCounter;
@@ -115,12 +118,12 @@ extern uint64_t _LogCounter;
* @brief Get current count of logging events
* @return Log Count
*/
static inline uint64_t GetLogCounter() { return _LogCounter; }
inline uint64_t GetLogCounter() { return _LogCounter; }
/**
* @brief Restore centralized logger vector to default state (silent operation)
*/
static inline void UnregisterLoggers() { MainLoggers.clear(); }
inline void UnregisterLoggers() { MainLoggers.clear(); }
/**
* @brief Construct and register a real-time console logger singleton
@@ -163,41 +166,20 @@ void RegisterFileLogger(const wchar_t* filepath);
#endif
/**
* @brief This is constructed per-subsystem in a locally centralized fashon
* @brief This is constructed per-subsystem in a locally centralized fashion
*/
class Module {
const char* m_modName;
public:
Module(const char* modName) : m_modName(modName) {}
/**
* @brief Route new log message to centralized ILogger
* @param severity Level of log report severity
* @param format Standard printf-style format string
*/
template <typename CharType>
inline void report(Level severity, const CharType* format, ...) {
if (MainLoggers.empty() && severity != Level::Fatal)
return;
va_list ap;
va_start(ap, format);
report(severity, format, ap);
va_end(ap);
}
template <typename CharType>
inline void report(Level severity, const CharType* format, va_list ap) {
template <typename Char>
void _vreport(Level severity, fmt::basic_string_view<Char> format,
fmt::basic_format_args<fmt::buffer_context<Char>> args) {
auto lk = LockLog();
++_LogCounter;
if (severity == Fatal)
RegisterConsoleLogger();
for (auto& logger : MainLoggers) {
va_list apc;
va_copy(apc, ap);
logger->report(m_modName, severity, format, apc);
va_end(apc);
}
for (auto& logger : MainLoggers)
logger->report(m_modName, severity, format, args);
if (severity == Error || severity == Fatal)
logvisorBp();
if (severity == Fatal)
@@ -206,41 +188,119 @@ public:
++ErrorCount;
}
/**
* @brief Route new log message with source info to centralized ILogger
* @param severity Level of log report severity
* @param file Source file name from __FILE__ macro
* @param linenum Source line number from __LINE__ macro
* @param format Standard printf-style format string
*/
template <typename CharType>
inline void reportSource(Level severity, const char* file, unsigned linenum, const CharType* format, ...) {
if (MainLoggers.empty() && severity != Level::Fatal)
return;
va_list ap;
va_start(ap, format);
reportSource(severity, file, linenum, format, ap);
va_end(ap);
}
template <typename CharType>
inline void reportSource(Level severity, const char* file, unsigned linenum, const CharType* format, va_list ap) {
template <typename Char>
void _vreportSource(Level severity, const char* file, unsigned linenum, fmt::basic_string_view<Char> format,
fmt::basic_format_args<fmt::buffer_context<Char>> args) {
auto lk = LockLog();
++_LogCounter;
if (severity == Fatal)
RegisterConsoleLogger();
for (auto& logger : MainLoggers) {
va_list apc;
va_copy(apc, ap);
logger->reportSource(m_modName, severity, file, linenum, format, apc);
va_end(apc);
}
for (auto& logger : MainLoggers)
logger->reportSource(m_modName, severity, file, linenum, format, args);
if (severity == Error || severity == Fatal)
logvisorBp();
if (severity == Fatal)
logvisorAbort();
else if (severity == Error)
++ErrorCount;
}
public:
constexpr Module(const char* modName) : m_modName(modName) {}
/**
* @brief Route new log message to centralized ILogger
* @param severity Level of log report severity
* @param format fmt-style format string
*/
template <typename S, typename... Args, typename Char = fmt::char_t<S>>
void report(Level severity, const S& format, Args&&... args) {
if (MainLoggers.empty() && severity != Level::Fatal)
return;
_vreport(severity, fmt::to_string_view<Char>(format),
fmt::basic_format_args<fmt::buffer_context<Char>>(
fmt::internal::make_args_checked<Args...>(format, std::forward<Args>(args)...)));
}
template <typename Char>
void vreport(Level severity, fmt::basic_string_view<Char> format,
fmt::basic_format_args<fmt::buffer_context<Char>> args) {
if (MainLoggers.empty() && severity != Level::Fatal)
return;
_vreport(severity, format, args);
}
/**
* @brief Route new log message with source info to centralized ILogger
* @param severity Level of log report severity
* @param file Source file name from __FILE__ macro
* @param linenum Source line number from __LINE__ macro
* @param format fmt-style format string
*/
template <typename S, typename... Args, typename Char = fmt::char_t<S>>
void reportSource(Level severity, const char* file, unsigned linenum, const S& format, Args&&... args) {
if (MainLoggers.empty() && severity != Level::Fatal)
return;
_vreportSource(severity, file, linenum, fmt::to_string_view<Char>(format),
fmt::basic_format_args<fmt::buffer_context<Char>>(
fmt::internal::make_args_checked<Args...>(format, std::forward<Args>(args)...)));
}
template <typename Char>
void vreportSource(Level severity, const char* file, unsigned linenum, fmt::basic_string_view<Char> format,
fmt::basic_format_args<fmt::buffer_context<Char>> args) {
if (MainLoggers.empty() && severity != Level::Fatal)
return;
_vreportSource(severity, file, linenum, format, args);
}
};
#define FMT_CUSTOM_FORMATTER(tp, fmtstr, ...) \
namespace fmt { \
template <> \
struct formatter<tp, char> { \
template <typename ParseContext> \
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); } \
template <typename FormatContext> \
auto format(const tp &obj, FormatContext &ctx) { \
return format_to(ctx.out(), FMT_STRING(fmtstr), __VA_ARGS__); \
} \
}; \
template <> \
struct formatter<tp, wchar_t> { \
template <typename ParseContext> \
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); } \
template <typename FormatContext> \
auto format(const tp &obj, FormatContext &ctx) { \
return format_to(ctx.out(), FMT_STRING(L##fmtstr), __VA_ARGS__); \
} \
}; \
template <> \
struct formatter<tp, char16_t> { \
template <typename ParseContext> \
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); } \
template <typename FormatContext> \
auto format(const tp &obj, FormatContext &ctx) { \
return format_to(ctx.out(), FMT_STRING(u##fmtstr), __VA_ARGS__); \
} \
}; \
template <> \
struct formatter<tp, char32_t> { \
template <typename ParseContext> \
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); } \
template <typename FormatContext> \
auto format(const tp &obj, FormatContext &ctx) { \
return format_to(ctx.out(), FMT_STRING(U##fmtstr), __VA_ARGS__); \
} \
}; \
}
} // namespace logvisor
template <typename S, typename... Args, typename Char = fmt::char_t<S>>
void quicklog(const S& format, Args&&... args) {
logvisor::MainLoggers[0]->report(
"quick", logvisor::Info, fmt::to_string_view<Char>(format),
fmt::basic_format_args<fmt::buffer_context<Char>>(
fmt::internal::make_args_checked<Args...>(format, std::forward<Args>(args)...)));
}

View File

@@ -1,155 +0,0 @@
#ifndef _NXSTL_CONDVAR
#define _NXSTL_CONDVAR 1
#ifdef __SWITCH__
extern "C" {
#include <switch/kernel/condvar.h>
#include <switch/result.h>
}
#include <chrono>
#include <bits/std_mutex.h>
#include <ext/concurrence.h>
#include <bits/alloc_traits.h>
#include <bits/allocator.h>
#include <bits/unique_ptr.h>
#include <bits/shared_ptr.h>
#include <bits/cxxabi_forced.h>
namespace std _GLIBCXX_VISIBILITY(default)
{
/// cv_status
enum class cv_status { no_timeout, timeout };
/// condition_variable
class condition_variable
{
typedef chrono::system_clock __clock_t;
typedef CondVar __native_type;
__native_type _M_cond = {};
public:
typedef __native_type* native_handle_type;
constexpr condition_variable() noexcept = default;
~condition_variable() noexcept = default;
condition_variable(const condition_variable&) = delete;
condition_variable& operator=(const condition_variable&) = delete;
void
notify_one() noexcept
{
condvarWakeOne(&_M_cond);
}
void
notify_all() noexcept
{
condvarWakeAll(&_M_cond);
}
void
wait(unique_lock<mutex>& __lock) noexcept
{
condvarWait(&_M_cond, __lock.mutex()->native_handle());
}
template<typename _Predicate>
void
wait(unique_lock<mutex>& __lock, _Predicate __p)
{
while (!__p())
wait(__lock);
}
template<typename _Duration>
cv_status
wait_until(unique_lock<mutex>& __lock,
const chrono::time_point<__clock_t, _Duration>& __atime)
{ return __wait_until_impl(__lock, __atime); }
template<typename _Clock, typename _Duration>
cv_status
wait_until(unique_lock<mutex>& __lock,
const chrono::time_point<_Clock, _Duration>& __atime)
{
// DR 887 - Sync unknown clock to known clock.
const typename _Clock::time_point __c_entry = _Clock::now();
const __clock_t::time_point __s_entry = __clock_t::now();
const auto __delta = __atime - __c_entry;
const auto __s_atime = __s_entry + __delta;
return __wait_until_impl(__lock, __s_atime);
}
template<typename _Clock, typename _Duration, typename _Predicate>
bool
wait_until(unique_lock<mutex>& __lock,
const chrono::time_point<_Clock, _Duration>& __atime,
_Predicate __p)
{
while (!__p())
if (wait_until(__lock, __atime) == cv_status::timeout)
return __p();
return true;
}
template<typename _Rep, typename _Period>
cv_status
wait_for(unique_lock<mutex>& __lock,
const chrono::duration<_Rep, _Period>& __rtime)
{
using __dur = typename __clock_t::duration;
auto __reltime = chrono::duration_cast<__dur>(__rtime);
if (__reltime < __rtime)
++__reltime;
return wait_until(__lock, __clock_t::now() + __reltime);
}
template<typename _Rep, typename _Period, typename _Predicate>
bool
wait_for(unique_lock<mutex>& __lock,
const chrono::duration<_Rep, _Period>& __rtime,
_Predicate __p)
{
using __dur = typename __clock_t::duration;
auto __reltime = chrono::duration_cast<__dur>(__rtime);
if (__reltime < __rtime)
++__reltime;
return wait_until(__lock, __clock_t::now() + __reltime, std::move(__p));
}
native_handle_type
native_handle()
{ return &_M_cond; }
private:
template<typename _Dur>
cv_status
__wait_until_impl(unique_lock<mutex>& __lock,
const chrono::time_point<__clock_t, _Dur>& __atime)
{
auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime);
Result res = condvarWaitTimeout(&_M_cond, __lock.mutex()->native_handle(), __ns.count());
if (R_DESCRIPTION(res) == KernelError_Timeout)
return cv_status::timeout;
return cv_status::no_timeout;
}
};
void
notify_all_at_thread_exit(condition_variable&, unique_lock<mutex>);
struct __at_thread_exit_elt
{
__at_thread_exit_elt* _M_next;
void (*_M_cb)(void*);
};
}
#endif
#endif

View File

@@ -1,112 +0,0 @@
#ifndef _NXSTL_MUTEX
#define _NXSTL_MUTEX 1
#ifdef __SWITCH__
extern "C" {
#include <switch/kernel/mutex.h>
}
namespace std _GLIBCXX_VISIBILITY(default)
{
// Common base class for std::mutex
class __mutex_base
{
protected:
typedef Mutex __native_type;
__native_type _M_mutex = {};
constexpr __mutex_base() noexcept = default;
__mutex_base(const __mutex_base&) = delete;
__mutex_base& operator=(const __mutex_base&) = delete;
};
/// The standard mutex type.
class mutex : private __mutex_base
{
public:
typedef __native_type* native_handle_type;
constexpr mutex() noexcept = default;
~mutex() = default;
mutex(const mutex&) = delete;
mutex& operator=(const mutex&) = delete;
void
lock()
{
mutexLock(&_M_mutex);
}
bool
try_lock() noexcept
{
return mutexTryLock(&_M_mutex);
}
void
unlock()
{
mutexUnlock(&_M_mutex);
}
native_handle_type
native_handle() noexcept
{ return &_M_mutex; }
};
// Common base class for std::recursive_mutex
class __recursive_mutex_base
{
protected:
typedef RMutex __native_type;
__recursive_mutex_base(const __recursive_mutex_base&) = delete;
__recursive_mutex_base& operator=(const __recursive_mutex_base&) = delete;
__native_type _M_mutex = {};
__recursive_mutex_base() = default;
};
/// The standard recursive mutex type.
class recursive_mutex : private __recursive_mutex_base
{
public:
typedef __native_type* native_handle_type;
constexpr recursive_mutex() = default;
~recursive_mutex() = default;
recursive_mutex(const recursive_mutex&) = delete;
recursive_mutex& operator=(const recursive_mutex&) = delete;
void
lock()
{
rmutexLock(&_M_mutex);
}
bool
try_lock() noexcept
{
return rmutexTryLock(&_M_mutex);
}
void
unlock()
{
rmutexUnlock(&_M_mutex);
}
native_handle_type
native_handle() noexcept
{ return &_M_mutex; }
};
}
#endif
#endif

View File

@@ -1,295 +0,0 @@
#ifndef _NXSTL_THREAD
#define _NXSTL_THREAD 1
#ifdef __SWITCH__
extern "C" {
#include <switch/kernel/thread.h>
#include <switch/arm/tls.h>
#include <switch/result.h>
}
#include <memory>
namespace std _GLIBCXX_VISIBILITY(default)
{
/// thread
class thread
{
public:
// Abstract base class for types that wrap arbitrary functors to be
// invoked in the new thread of execution.
struct _State
{
virtual ~_State() = default;
virtual void _M_run() = 0;
};
using _State_ptr = unique_ptr<_State>;
typedef Thread native_handle_type;
/// thread::id
class id
{
native_handle_type _M_thread;
public:
id() noexcept : _M_thread() { }
explicit
id(native_handle_type __id) : _M_thread(__id) { }
private:
friend class thread;
friend class hash<thread::id>;
friend bool
operator==(thread::id __x, thread::id __y) noexcept;
friend bool
operator<(thread::id __x, thread::id __y) noexcept;
template<class _CharT, class _Traits>
friend basic_ostream<_CharT, _Traits>&
operator<<(basic_ostream<_CharT, _Traits>& __out, thread::id __id);
};
private:
id _M_id;
public:
thread() noexcept = default;
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 2097. packaged_task constructors should be constrained
thread(thread&) = delete;
thread(const thread&) = delete;
thread(const thread&&) = delete;
thread(thread&& __t) noexcept
{ swap(__t); }
template<typename _Callable, typename... _Args>
explicit
thread(_Callable&& __f, _Args&&... __args)
{
_M_start_thread(_S_make_state(
__make_invoker(std::forward<_Callable>(__f),
std::forward<_Args>(__args)...)));
}
~thread()
{
if (joinable())
std::terminate();
}
thread& operator=(const thread&) = delete;
thread& operator=(thread&& __t) noexcept
{
if (joinable())
std::terminate();
swap(__t);
return *this;
}
void
swap(thread& __t) noexcept
{ std::swap(_M_id, __t._M_id); }
bool
joinable() const noexcept
{ return !(_M_id == id()); }
void
join()
{
threadWaitForExit(&_M_id._M_thread);
_M_id = id();
}
void
detach()
{
_M_id = id();
}
thread::id
get_id() const noexcept
{ return _M_id; }
/** @pre thread is joinable
*/
native_handle_type
native_handle()
{ return _M_id._M_thread; }
// Returns a value that hints at the number of hardware thread contexts.
static unsigned int
hardware_concurrency() noexcept
{
return 3;
}
private:
template<typename _Callable>
struct _State_impl : public _State
{
_Callable _M_func;
_State_impl(_Callable&& __f) : _M_func(std::forward<_Callable>(__f))
{ }
void
_M_run() { _M_func(); }
};
static void
execute_native_thread_routine(void *arg)
{
reinterpret_cast<_State*>(arg)->_M_run();
}
void
_M_start_thread(_State_ptr state)
{
Result res = threadCreate(&_M_id._M_thread, execute_native_thread_routine,
state.get(), 8192, 0x2B, -2);
if (R_FAILED(res))
__throw_system_error(res);
res = threadStart(&_M_id._M_thread);
if (R_FAILED(res))
__throw_system_error(res);
state.release();
}
template<typename _Callable>
static _State_ptr
_S_make_state(_Callable&& __f)
{
using _Impl = _State_impl<_Callable>;
return _State_ptr{new _Impl{std::forward<_Callable>(__f)}};
}
private:
// A call wrapper that does INVOKE(forwarded tuple elements...)
template<typename _Tuple>
struct _Invoker
{
_Tuple _M_t;
template<size_t _Index>
static __tuple_element_t<_Index, _Tuple>&&
_S_declval();
template<size_t... _Ind>
auto
_M_invoke(_Index_tuple<_Ind...>)
noexcept(noexcept(std::__invoke(_S_declval<_Ind>()...)))
-> decltype(std::__invoke(_S_declval<_Ind>()...))
{ return std::__invoke(std::get<_Ind>(std::move(_M_t))...); }
using _Indices
= typename _Build_index_tuple<tuple_size<_Tuple>::value>::__type;
auto
operator()()
noexcept(noexcept(std::declval<_Invoker&>()._M_invoke(_Indices())))
-> decltype(std::declval<_Invoker&>()._M_invoke(_Indices()))
{ return _M_invoke(_Indices()); }
};
template<typename... _Tp>
using __decayed_tuple = tuple<typename std::decay<_Tp>::type...>;
public:
// Returns a call wrapper that stores
// tuple{DECAY_COPY(__callable), DECAY_COPY(__args)...}.
template<typename _Callable, typename... _Args>
static _Invoker<__decayed_tuple<_Callable, _Args...>>
__make_invoker(_Callable&& __callable, _Args&&... __args)
{
return { __decayed_tuple<_Callable, _Args...>{
std::forward<_Callable>(__callable), std::forward<_Args>(__args)...
} };
}
};
inline void
swap(thread& __x, thread& __y) noexcept
{ __x.swap(__y); }
inline bool
operator==(thread::id __x, thread::id __y) noexcept
{
// pthread_equal is undefined if either thread ID is not valid, so we
// can't safely use __gthread_equal on default-constructed values (nor
// the non-zero value returned by this_thread::get_id() for
// single-threaded programs using GNU libc). Assume EqualityComparable.
return __x._M_thread.handle == __y._M_thread.handle;
}
inline bool
operator!=(thread::id __x, thread::id __y) noexcept
{ return !(__x == __y); }
inline bool
operator<(thread::id __x, thread::id __y) noexcept
{
// Pthreads doesn't define any way to do this, so we just have to
// assume native_handle_type is LessThanComparable.
return __x._M_thread.handle < __y._M_thread.handle;
}
inline bool
operator<=(thread::id __x, thread::id __y) noexcept
{ return !(__y < __x); }
inline bool
operator>(thread::id __x, thread::id __y) noexcept
{ return __y < __x; }
inline bool
operator>=(thread::id __x, thread::id __y) noexcept
{ return !(__x < __y); }
// DR 889.
/// std::hash specialization for thread::id.
template<>
struct hash<thread::id>
: public __hash_base<size_t, thread::id>
{
size_t
operator()(const thread::id& __id) const noexcept
{ return std::_Hash_impl::hash(__id._M_thread); }
};
template<class _CharT, class _Traits>
inline basic_ostream<_CharT, _Traits>&
operator<<(basic_ostream<_CharT, _Traits>& __out, thread::id __id)
{
if (__id == thread::id())
return __out << "thread::id of a non-executing thread";
else
return __out << __id._M_thread;
}
namespace this_thread
{
/// get_id
inline thread::id
get_id() noexcept
{
Thread ret;
uint8_t* tls = (uint8_t*)armGetTls();
uint8_t* threadCtx = *(uint8_t**)(tls + 0x1F8);
ret.handle = *(Handle*)(threadCtx + 0x1B8);
ret.stack_mem = *(void**)(threadCtx + 0x48);
ret.stack_mirror = *(void**)(threadCtx + 0x50);
ret.stack_sz = *(size_t*)(threadCtx + 0x58);
return thread::id(ret);
}
}
}
#endif
#endif

View File

@@ -11,7 +11,7 @@
#include <TlHelp32.h>
#elif defined(__SWITCH__)
#include <cstring>
#include "nxstl/thread"
#include <switch.h>
#else
#include <sys/ioctl.h>
#include <unistd.h>
@@ -31,7 +31,7 @@
#include <unordered_map>
#include <cstdio>
#include <cinttypes>
#include <signal.h>
#include <csignal>
#include "logvisor/logvisor.hpp"
/* ANSI sequences */
@@ -47,6 +47,10 @@
#define FOREGROUND_WHITE FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
#endif
#ifndef _MSC_VER
#pragma GCC diagnostic ignored "-Wformat-truncation"
#endif
void logvisorBp() {}
namespace logvisor {
@@ -89,18 +93,18 @@ void RegisterThreadName(const char* name) {
void KillProcessTree() {
DWORD myprocID = GetCurrentProcessId();
PROCESSENTRY32 pe = {};
pe.dwSize = sizeof(PROCESSENTRY32);
PROCESSENTRY32W pe = {};
pe.dwSize = sizeof(pe);
HANDLE hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (::Process32First(hSnap, &pe)) {
if (::Process32FirstW(hSnap, &pe)) {
BOOL bContinue = TRUE;
// kill child processes
while (bContinue) {
// only kill child processes and let console window remain
if (pe.th32ParentProcessID == myprocID && wcscmp(pe.szExeFile, L"conhost.exe")) {
if (pe.th32ParentProcessID == myprocID && std::wcscmp(pe.szExeFile, L"conhost.exe") != 0) {
HANDLE hChildProc = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID);
if (hChildProc) {
@@ -109,12 +113,12 @@ void KillProcessTree() {
}
}
bContinue = ::Process32Next(hSnap, &pe);
bContinue = ::Process32NextW(hSnap, &pe);
}
}
}
void logvisorAbort() {
[[noreturn]] void logvisorAbort() {
#if !WINDOWS_STORE
unsigned int i;
void* stack[100];
@@ -132,7 +136,7 @@ void logvisorAbort() {
for (i = 0; i < frames; i++) {
SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol);
fprintf(stderr, "%i: %s - 0x%0llX", frames - i - 1, symbol->Name, symbol->Address);
std::fprintf(stderr, "%i: %s - 0x%0llX", frames - i - 1, symbol->Name, symbol->Address);
DWORD dwDisplacement;
IMAGEHLP_LINE64 line;
@@ -141,14 +145,14 @@ void logvisorAbort() {
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
if (SymGetLineFromAddr64(process, (DWORD64)(stack[i]), &dwDisplacement, &line)) {
// SymGetLineFromAddr64 returned success
fprintf(stderr, " LINE %d\n", int(line.LineNumber));
std::fprintf(stderr, " LINE %d\n", int(line.LineNumber));
} else {
fprintf(stderr, "\n");
std::fputc('\n', stderr);
}
}
fflush(stderr);
free(symbol);
std::fflush(stderr);
std::free(symbol);
#endif
@@ -165,69 +169,39 @@ void logvisorAbort() {
}
#elif defined(__SWITCH__)
void logvisorAbort() { exit(1); }
[[noreturn]] void logvisorAbort() {
MainLoggers.clear();
nvExit();
exit(1);
}
#else
void KillProcessTree() {}
#include <execinfo.h>
void logvisorAbort() {
void* array[128];
size_t size = backtrace(array, 128);
#include <backtrace.h>
constexpr size_t exeBufSize = 1024 + 1;
char exeNameBuffer[exeBufSize] = {};
#if __linux__
readlink("/proc/self/exe", exeNameBuffer, exeBufSize);
#endif
char cmdLine[1024];
#if __APPLE__
snprintf(cmdLine, 1024, "atos -p %d", getpid());
#else
snprintf(cmdLine, 1024, "2>/dev/null addr2line -C -f -e \"%s\"", exeNameBuffer);
#endif
std::string cmdLineStr = cmdLine;
for (size_t i = 0; i < size; i++) {
#if __linux__
Dl_info dlip;
if (dladdr(array[i], &dlip))
snprintf(cmdLine, 128, " %p", (void*)((uint8_t*)array[i] - (uint8_t*)dlip.dli_fbase));
else
snprintf(cmdLine, 128, " %p", array[i]);
#else
snprintf(cmdLine, 128, " %p", array[i]);
#endif
cmdLineStr += cmdLine;
void backtrace_error(void* data, const char* msg, int errnum) {
std::fprintf(stderr, "Error retrieving stack trace: %d %s\n", errnum, msg);
}
FILE* fp = popen(cmdLineStr.c_str(), "r");
if (fp) {
char readBuf[256];
size_t readSz;
while ((readSz = fread(readBuf, 1, 256, fp)))
fwrite(readBuf, 1, readSz, stderr);
pclose(fp);
} else {
for (size_t i = 0; i < size; i++) {
fprintf(stderr, "- ");
Dl_info dlip;
if (dladdr(array[i], &dlip)) {
int status;
char* demangledName = abi::__cxa_demangle(dlip.dli_sname, nullptr, nullptr, &status);
fprintf(stderr, "%p(%s+%p)\n", dlip.dli_saddr, demangledName ? demangledName : dlip.dli_sname,
(void*)((uint8_t*)array[i] - (uint8_t*)dlip.dli_fbase));
free(demangledName);
} else {
fprintf(stderr, "%p\n", array[i]);
}
int backtrace_callback(void* data, uintptr_t pc, const char* filename, int lineno, const char* function) {
int status = -255;
char* demangledName = abi::__cxa_demangle(function, nullptr, nullptr, &status);
std::fprintf(stderr, "0x%lX %s\n\t%s:%d\n", pc, status == 0 ? demangledName : function, filename, lineno);
if (demangledName != nullptr) {
std::free(demangledName);
}
return 0;
}
fflush(stderr);
fflush(stdout);
[[noreturn]] void logvisorAbort() {
backtrace_state* state = backtrace_create_state(nullptr, 0, backtrace_error, nullptr);
if (state != nullptr) {
backtrace_full(state, 0, backtrace_callback, backtrace_error, nullptr);
}
std::fflush(stderr);
std::fflush(stdout);
KillProcessTree();
#ifndef NDEBUG
@@ -246,19 +220,19 @@ static void AbortHandler(int signum) {
_LogMutex.enabled = false;
switch (signum) {
case SIGSEGV:
Log.report(logvisor::Fatal, "Segmentation Fault");
Log.report(logvisor::Fatal, FMT_STRING("Segmentation Fault"));
break;
case SIGILL:
Log.report(logvisor::Fatal, "Bad Execution");
Log.report(logvisor::Fatal, FMT_STRING("Bad Execution"));
break;
case SIGFPE:
Log.report(logvisor::Fatal, "Floating Point Exception");
Log.report(logvisor::Fatal, FMT_STRING("Floating Point Exception"));
break;
case SIGABRT:
Log.report(logvisor::Fatal, "Abort Signal");
Log.report(logvisor::Fatal, FMT_STRING("Abort Signal"));
break;
default:
Log.report(logvisor::Fatal, "unknown signal %d", signum);
Log.report(logvisor::Fatal, FMT_STRING("unknown signal {}"), signum);
break;
}
}
@@ -292,6 +266,238 @@ static inline int ConsoleWidth() {
return retval;
}
#if LOGVISOR_NX_LM
struct ConsoleLogger : public ILogger {
Service m_svc{};
Service m_logger{};
bool m_ready = false;
struct MessageHeader {
enum Flags : u32 {
IsHead = 1,
IsTail = 2,
};
enum Severity : u32 {
Trace,
Info,
Warning,
Error,
Critical,
};
u64 pid;
u64 thread_context;
//union {
//BitField<0, 16, Flags> flags;
//BitField<16, 8, Severity> severity;
//BitField<24, 8, u32> verbosity;
//};
u32 flags;
u32 payload_size;
Flags GetFlags() const {
return Flags(flags & u32(0xffff));
}
void SetFlags(Flags f) {
flags &= ~u32(0xffff);
flags |= f;
}
Severity GetSeverity() const {
return Severity((flags >> u32(16)) & u32(0xff));
}
void SetSeverity(Severity f) {
flags &= ~u32(0xff0000);
flags |= f << u32(16);
}
u32 GetVerbosity() const {
return u32((flags >> u32(24)) & u32(0xff));
}
void SetVerbosity(u32 f) {
flags &= ~u32(0xff000000);
flags |= f << u32(24);
}
bool IsHeadLog() const {
return flags & IsHead;
}
bool IsTailLog() const {
return flags & IsTail;
}
};
static_assert(sizeof(MessageHeader) == 0x18, "MessageHeader is incorrect size");
enum class Field : u8 {
Skip = 1,
Message = 2,
Line = 3,
Filename = 4,
Function = 5,
Module = 6,
Thread = 7,
};
static constexpr MessageHeader::Severity LevelToSeverity(Level l) {
switch (l) {
case Level::Info:
default:
return MessageHeader::Info;
case Level::Warning:
return MessageHeader::Warning;
case Level::Error:
return MessageHeader::Error;
case Level::Fatal:
return MessageHeader::Critical;
}
}
ConsoleLogger() {
if (R_SUCCEEDED(smGetService(&m_svc, "lm"))) {
auto pid = getpid();
if (R_SUCCEEDED(serviceDispatchIn(&m_svc, 0, pid, .out_num_objects = 1, .out_objects = &m_logger))) {
m_ready = true;
MessageHeader head{};
head.pid = getpid();
head.SetFlags(MessageHeader::IsHead);
serviceDispatch(&m_logger, 0,
.buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_In },
.buffers = { { &head, sizeof(head) } });
}
}
}
~ConsoleLogger() override {
if (m_ready) {
MessageHeader head{};
head.pid = getpid();
head.SetFlags(MessageHeader::IsTail);
serviceDispatch(&m_logger, 0,
.buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_In },
.buffers = { { &head, sizeof(head) } });
}
serviceClose(&m_logger);
serviceClose(&m_svc);
}
void SendBuffer(const std::vector<u8>& buf) {
serviceDispatch(&m_logger, 0,
.buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_In },
.buffers = { { buf.data(), buf.size() } });
}
void report(const char* modName, Level severity, fmt::string_view format, fmt::format_args args) override {
if (!m_ready)
return;
const std::thread::id thrId = std::this_thread::get_id();
const char* thrName = nullptr;
size_t thrNameSize = 0;
if (ThreadMap.find(thrId) != ThreadMap.end()) {
thrName = ThreadMap[thrId];
thrNameSize = std::min(std::strlen(thrName), size_t(255));
}
auto modNameSize = std::min(std::strlen(modName), size_t(255));
auto message = fmt::vformat(format, args);
auto messageSize = std::min(message.size(), size_t(255));
std::vector<u8> bufOut(sizeof(MessageHeader) + (thrNameSize ? 2 + thrNameSize : 0) + 2 + modNameSize + 2 + messageSize, '\0');
auto it = bufOut.begin();
auto& head = *reinterpret_cast<MessageHeader*>(&*it);
head.pid = getpid();
head.payload_size = bufOut.size() - sizeof(MessageHeader);
head.SetSeverity(LevelToSeverity(severity));
it += sizeof(MessageHeader);
if (thrNameSize) {
*it++ = u8(Field::Thread);
*it++ = thrNameSize;
std::memcpy(&*it, thrName, thrNameSize);
it += thrNameSize;
}
*it++ = u8(Field::Module);
*it++ = modNameSize;
std::memcpy(&*it, modName, modNameSize);
it += modNameSize;
*it++ = u8(Field::Message);
*it++ = messageSize;
std::memcpy(&*it, message.data(), messageSize);
it += messageSize;
SendBuffer(bufOut);
}
void report(const char* modName, Level severity, fmt::wstring_view format, fmt::wformat_args args) override {}
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::string_view format,
fmt::format_args args) override {
if (!m_ready)
return;
const std::thread::id thrId = std::this_thread::get_id();
const char* thrName = nullptr;
size_t thrNameSize = 0;
if (ThreadMap.find(thrId) != ThreadMap.end()) {
thrName = ThreadMap[thrId];
thrNameSize = std::min(std::strlen(thrName), size_t(255));
}
auto modNameSize = std::min(std::strlen(modName), size_t(255));
auto fileNameSize = std::min(std::strlen(file), size_t(255));
auto message = fmt::vformat(format, args);
auto messageSize = std::min(message.size(), size_t(255));
std::vector<u8> bufOut(sizeof(MessageHeader) + (thrNameSize ? 2 + thrNameSize : 0) + 2 + modNameSize + 2 + fileNameSize + 3 + 4 + 2 + messageSize, '\0');
auto it = bufOut.begin();
auto& head = *reinterpret_cast<MessageHeader*>(&*it);
head.pid = getpid();
head.payload_size = bufOut.size() - sizeof(MessageHeader);
head.SetSeverity(LevelToSeverity(severity));
it += sizeof(MessageHeader);
if (thrNameSize) {
*it++ = u8(Field::Thread);
*it++ = thrNameSize;
std::memcpy(&*it, thrName, thrNameSize);
it += thrNameSize;
}
*it++ = u8(Field::Module);
*it++ = modNameSize;
std::memcpy(&*it, modName, modNameSize);
it += modNameSize;
*it++ = u8(Field::Filename);
*it++ = fileNameSize;
std::memcpy(&*it, file, fileNameSize);
it += fileNameSize;
*it++ = u8(Field::Line);
*it++ = 4;
*it++ = u8(Field::Skip);
std::memcpy(&*it, &linenum, 4);
it += 4;
*it++ = u8(Field::Message);
*it++ = messageSize;
std::memcpy(&*it, message.data(), messageSize);
it += messageSize;
SendBuffer(bufOut);
}
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::wstring_view format,
fmt::wformat_args args) override {}
};
#else
#if _WIN32
static HANDLE Term = 0;
#else
@@ -321,160 +527,162 @@ struct ConsoleLogger : public ILogger {
static void _reportHead(const char* modName, const char* sourceInfo, Level severity) {
/* Clear current line out */
int width = ConsoleWidth();
fprintf(stderr, "\r");
const int width = ConsoleWidth();
std::fputc('\r', stderr);
for (int w = 0; w < width; ++w)
fprintf(stderr, " ");
fprintf(stderr, "\r");
std::fputc(' ', stderr);
std::fputc('\r', stderr);
std::chrono::steady_clock::duration tm = CurrentUptime();
double tmd = tm.count() * std::chrono::steady_clock::duration::period::num /
(double)std::chrono::steady_clock::duration::period::den;
std::thread::id thrId = std::this_thread::get_id();
const std::chrono::steady_clock::duration tm = CurrentUptime();
const double tmd = tm.count() * std::chrono::steady_clock::duration::period::num /
static_cast<double>(std::chrono::steady_clock::duration::period::den);
const std::thread::id thrId = std::this_thread::get_id();
const char* thrName = nullptr;
if (ThreadMap.find(thrId) != ThreadMap.end())
thrName = ThreadMap[thrId];
if (XtermColor) {
fprintf(stderr, BOLD "[");
fprintf(stderr, GREEN "%5.4f ", tmd);
uint_fast64_t fIdx = FrameIndex.load();
if (fIdx)
fprintf(stderr, "(%" PRIu64 ") ", fIdx);
std::fputs(BOLD "[", stderr);
fmt::print(stderr, FMT_STRING(GREEN "{:.4f} "), tmd);
const uint_fast64_t fIdx = FrameIndex.load();
if (fIdx != 0)
fmt::print(stderr, FMT_STRING("({}) "), fIdx);
switch (severity) {
case Info:
fprintf(stderr, BOLD CYAN "INFO");
std::fputs(BOLD CYAN "INFO", stderr);
break;
case Warning:
fprintf(stderr, BOLD YELLOW "WARNING");
std::fputs(BOLD YELLOW "WARNING", stderr);
break;
case Error:
fprintf(stderr, RED BOLD "ERROR");
std::fputs(RED BOLD "ERROR", stderr);
break;
case Fatal:
fprintf(stderr, BOLD RED "FATAL ERROR");
std::fputs(BOLD RED "FATAL ERROR", stderr);
break;
default:
break;
};
fprintf(stderr, NORMAL BOLD " %s", modName);
fmt::print(stderr, FMT_STRING(NORMAL BOLD " {}"), modName);
if (sourceInfo)
fprintf(stderr, BOLD YELLOW " {%s}", sourceInfo);
fmt::print(stderr, FMT_STRING(BOLD YELLOW " {{}}"), sourceInfo);
if (thrName)
fprintf(stderr, BOLD MAGENTA " (%s)", thrName);
fprintf(stderr, NORMAL BOLD "] " NORMAL);
fmt::print(stderr, FMT_STRING(BOLD MAGENTA " ({})"), thrName);
std::fputs(NORMAL BOLD "] " NORMAL, stderr);
} else {
#if _WIN32
#if !WINDOWS_STORE
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
fprintf(stderr, "[");
std::fputc('[', stderr);
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_GREEN);
fprintf(stderr, "%5.4f ", tmd);
uint64_t fi = FrameIndex.load();
if (fi)
fprintf(stderr, "(%" PRIu64 ") ", fi);
fmt::print(stderr, FMT_STRING("{:.4f} "), tmd);
const uint64_t fi = FrameIndex.load();
if (fi != 0)
std::fprintf(stderr, "(%" PRIu64 ") ", fi);
switch (severity) {
case Info:
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_BLUE);
fprintf(stderr, "INFO");
std::fputs("INFO", stderr);
break;
case Warning:
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN);
fprintf(stderr, "WARNING");
std::fputs("WARNING", stderr);
break;
case Error:
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED);
fprintf(stderr, "ERROR");
std::fputs("ERROR", stderr);
break;
case Fatal:
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED);
fprintf(stderr, "FATAL ERROR");
std::fputs("FATAL ERROR", stderr);
break;
default:
break;
};
}
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
fprintf(stderr, " %s", modName);
fmt::print(stderr, FMT_STRING(" {}"), modName);
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN);
if (sourceInfo)
fprintf(stderr, " {%s}", sourceInfo);
fmt::print(stderr, FMT_STRING(" {{}}"), sourceInfo);
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE);
if (thrName)
fprintf(stderr, " (%s)", thrName);
fmt::print(stderr, FMT_STRING(" ({})"), thrName);
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
fprintf(stderr, "] ");
std::fputs("] ", stderr);
SetConsoleTextAttribute(Term, FOREGROUND_WHITE);
#endif
#else
fprintf(stderr, "[");
fprintf(stderr, "%5.4f ", tmd);
std::fputc('[', stderr);
fmt::print(stderr, FMT_STRING("{:.4f} "), tmd);
uint_fast64_t fIdx = FrameIndex.load();
if (fIdx)
fprintf(stderr, "(%" PRIu64 ") ", fIdx);
fmt::print(stderr, FMT_STRING("({}) "), fIdx);
switch (severity) {
case Info:
fprintf(stderr, "INFO");
std::fputs("INFO", stderr);
break;
case Warning:
fprintf(stderr, "WARNING");
std::fputs("WARNING", stderr);
break;
case Error:
fprintf(stderr, "ERROR");
std::fputs("ERROR", stderr);
break;
case Fatal:
fprintf(stderr, "FATAL ERROR");
std::fputs("FATAL ERROR", stderr);
break;
default:
break;
};
fprintf(stderr, " %s", modName);
}
fmt::print(stderr, FMT_STRING(" {}"), modName);
if (sourceInfo)
fprintf(stderr, " {%s}", sourceInfo);
fmt::print(stderr, FMT_STRING(" {{}}"), sourceInfo);
if (thrName)
fprintf(stderr, " (%s)", thrName);
fprintf(stderr, "] ");
fmt::print(stderr, FMT_STRING(" ({})"), thrName);
std::fputs("] ", stderr);
#endif
}
}
void report(const char* modName, Level severity, const char* format, va_list ap) {
void report(const char* modName, Level severity, fmt::string_view format, fmt::format_args args) override {
_reportHead(modName, nullptr, severity);
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
fflush(stderr);
fmt::vprint(stderr, format, args);
std::fputc('\n', stderr);
std::fflush(stderr);
}
void report(const char* modName, Level severity, const wchar_t* format, va_list ap) {
void report(const char* modName, Level severity, fmt::wstring_view format, fmt::wformat_args args) override {
_reportHead(modName, nullptr, severity);
vfwprintf(stderr, format, ap);
fprintf(stderr, "\n");
fflush(stderr);
fmt::vprint(stderr, format, args);
std::fputc('\n', stderr);
std::fflush(stderr);
}
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, const char* format,
va_list ap) {
char sourceInfo[128];
snprintf(sourceInfo, 128, "%s:%u", file, linenum);
_reportHead(modName, sourceInfo, severity);
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
fflush(stderr);
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::string_view format,
fmt::format_args args) override {
_reportHead(modName, fmt::format(FMT_STRING("{}:{}"), file, linenum).c_str(), severity);
fmt::vprint(stderr, format, args);
std::fputc('\n', stderr);
std::fflush(stderr);
}
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, const wchar_t* format,
va_list ap) {
char sourceInfo[128];
snprintf(sourceInfo, 128, "%s:%u", file, linenum);
_reportHead(modName, sourceInfo, severity);
vfwprintf(stderr, format, ap);
fprintf(stderr, "\n");
fflush(stderr);
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::wstring_view format,
fmt::wformat_args args) override {
_reportHead(modName, fmt::format(FMT_STRING("{}:{}"), file, linenum).c_str(), severity);
fmt::vprint(stderr, format, args);
std::fputc('\n', stderr);
std::fflush(stderr);
}
};
#endif
static bool ConsoleLoggerRegistered = false;
void RegisterConsoleLogger() {
/* Otherwise construct new console logger */
if (!ConsoleLoggerRegistered) {
MainLoggers.emplace_back(new ConsoleLogger);
ConsoleLoggerRegistered = true;
}
}
#if _WIN32
@@ -483,9 +691,9 @@ void CreateWin32Console() {
/* Debug console */
AllocConsole();
freopen("CONIN$", "r", stdin);
freopen("CONOUT$", "w", stdout);
freopen("CONOUT$", "w", stderr);
std::freopen("CONIN$", "r", stdin);
std::freopen("CONOUT$", "w", stdout);
std::freopen("CONOUT$", "w", stderr);
#endif
}
#endif
@@ -500,81 +708,81 @@ void RegisterStandardExceptions() {
struct FileLogger : public ILogger {
FILE* fp;
virtual void openFile() = 0;
virtual void closeFile() { fclose(fp); }
virtual void closeFile() { std::fclose(fp); }
void _reportHead(const char* modName, const char* sourceInfo, Level severity) {
std::chrono::steady_clock::duration tm = CurrentUptime();
double tmd = tm.count() * std::chrono::steady_clock::duration::period::num /
(double)std::chrono::steady_clock::duration::period::den;
std::thread::id thrId = std::this_thread::get_id();
const std::chrono::steady_clock::duration tm = CurrentUptime();
const double tmd = tm.count() * std::chrono::steady_clock::duration::period::num /
static_cast<double>(std::chrono::steady_clock::duration::period::den);
const std::thread::id thrId = std::this_thread::get_id();
const char* thrName = nullptr;
if (ThreadMap.find(thrId) != ThreadMap.end())
if (ThreadMap.find(thrId) != ThreadMap.end()) {
thrName = ThreadMap[thrId];
}
fprintf(fp, "[");
fprintf(fp, "%5.4f ", tmd);
uint_fast64_t fIdx = FrameIndex.load();
if (fIdx)
fprintf(fp, "(%" PRIu64 ") ", fIdx);
std::fputc('[', fp);
std::fprintf(fp, "%5.4f ", tmd);
const uint_fast64_t fIdx = FrameIndex.load();
if (fIdx != 0) {
std::fprintf(fp, "(%" PRIu64 ") ", fIdx);
}
switch (severity) {
case Info:
fprintf(fp, "INFO");
std::fputs("INFO", fp);
break;
case Warning:
fprintf(fp, "WARNING");
std::fputs("WARNING", fp);
break;
case Error:
fprintf(fp, "ERROR");
std::fputs("ERROR", fp);
break;
case Fatal:
fprintf(fp, "FATAL ERROR");
std::fputs("FATAL ERROR", fp);
break;
default:
break;
};
fprintf(fp, " %s", modName);
if (sourceInfo)
fprintf(fp, " {%s}", sourceInfo);
if (thrName)
fprintf(fp, " (%s)", thrName);
fprintf(fp, "] ");
std::fprintf(fp, " %s", modName);
if (sourceInfo) {
std::fprintf(fp, " {%s}", sourceInfo);
}
if (thrName) {
std::fprintf(fp, " (%s)", thrName);
}
std::fputs("] ", fp);
}
void report(const char* modName, Level severity, const char* format, va_list ap) {
void report(const char* modName, Level severity, fmt::string_view format, fmt::format_args args) override {
openFile();
_reportHead(modName, nullptr, severity);
vfprintf(fp, format, ap);
fprintf(fp, "\n");
fmt::vprint(fp, format, args);
std::fputc('\n', fp);
closeFile();
}
void report(const char* modName, Level severity, const wchar_t* format, va_list ap) {
void report(const char* modName, Level severity, fmt::wstring_view format, fmt::wformat_args args) override {
openFile();
_reportHead(modName, nullptr, severity);
vfwprintf(fp, format, ap);
fprintf(fp, "\n");
fmt::vprint(fp, format, args);
std::fputc('\n', fp);
closeFile();
}
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, const char* format,
va_list ap) {
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::string_view format,
fmt::format_args args) override {
openFile();
char sourceInfo[128];
snprintf(sourceInfo, 128, "%s:%u", file, linenum);
_reportHead(modName, sourceInfo, severity);
vfprintf(fp, format, ap);
fprintf(fp, "\n");
_reportHead(modName, fmt::format(FMT_STRING("{}:{}"), file, linenum).c_str(), severity);
fmt::vprint(fp, format, args);
std::fputc('\n', fp);
closeFile();
}
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, const wchar_t* format,
va_list ap) {
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::wstring_view format,
fmt::wformat_args args) override {
openFile();
char sourceInfo[128];
snprintf(sourceInfo, 128, "%s:%u", file, linenum);
_reportHead(modName, sourceInfo, severity);
vfwprintf(fp, format, ap);
fprintf(fp, "\n");
_reportHead(modName, fmt::format(FMT_STRING("{}:{}"), file, linenum).c_str(), severity);
fmt::vprint(fp, format, args);
std::fputc('\n', fp);
closeFile();
}
};
@@ -582,7 +790,7 @@ struct FileLogger : public ILogger {
struct FileLogger8 : public FileLogger {
const char* m_filepath;
FileLogger8(const char* filepath) : m_filepath(filepath) {}
void openFile() { fp = fopen(m_filepath, "a"); }
void openFile() override { fp = std::fopen(m_filepath, "a"); }
};
void RegisterFileLogger(const char* filepath) {
@@ -595,7 +803,7 @@ void RegisterFileLogger(const char* filepath) {
struct FileLogger16 : public FileLogger {
const wchar_t* m_filepath;
FileLogger16(const wchar_t* filepath) : m_filepath(filepath) {}
void openFile() { fp = _wfopen(m_filepath, L"a"); }
void openFile() override { fp = _wfopen(m_filepath, L"a"); }
};
void RegisterFileLogger(const wchar_t* filepath) {