Compare commits

...

9 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
8 changed files with 356 additions and 673 deletions

3
.gitmodules vendored
View File

@@ -1,3 +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,9 +1,11 @@
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)
set(CMAKE_CXX_STANDARD 17)
if (NOT MSVC)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()
endif()
include (CMakePackageConfigHelpers)
@@ -13,8 +15,38 @@ add_library(logvisor
lib/logvisor.cpp
include/logvisor/logvisor.hpp)
target_link_libraries(logvisor PUBLIC ${CMAKE_DL_LIBS} fmt)
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)

2
fmt

Submodule fmt updated: 25ff2efc0a...9bdd1596ce

View File

@@ -10,11 +10,10 @@
#define FMT_STRING_ALIAS 1
#define FMT_ENFORCE_COMPILE_STRING 1
#define FMT_USE_GRISU 0
#include <fmt/format.h>
#ifdef __SWITCH__
#include "nxstl/mutex"
#if defined(__SWITCH__) && !defined(LOGVISOR_NX_LM)
#define LOGVISOR_NX_LM 0
#endif
extern "C" void logvisorBp();
@@ -264,7 +263,7 @@ struct formatter<tp, char> { \
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); } \
template <typename FormatContext> \
auto format(const tp &obj, FormatContext &ctx) { \
return format_to(ctx.out(), fmt(fmtstr), __VA_ARGS__); \
return format_to(ctx.out(), FMT_STRING(fmtstr), __VA_ARGS__); \
} \
}; \
template <> \
@@ -273,7 +272,7 @@ struct formatter<tp, wchar_t> { \
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); } \
template <typename FormatContext> \
auto format(const tp &obj, FormatContext &ctx) { \
return format_to(ctx.out(), fmt(L##fmtstr), __VA_ARGS__); \
return format_to(ctx.out(), FMT_STRING(L##fmtstr), __VA_ARGS__); \
} \
}; \
template <> \
@@ -282,7 +281,7 @@ struct formatter<tp, char16_t> { \
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); } \
template <typename FormatContext> \
auto format(const tp &obj, FormatContext &ctx) { \
return format_to(ctx.out(), fmt(u##fmtstr), __VA_ARGS__); \
return format_to(ctx.out(), FMT_STRING(u##fmtstr), __VA_ARGS__); \
} \
}; \
template <> \
@@ -291,9 +290,17 @@ struct formatter<tp, char32_t> { \
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); } \
template <typename FormatContext> \
auto format(const tp &obj, FormatContext &ctx) { \
return format_to(ctx.out(), fmt(U##fmtstr), __VA_ARGS__); \
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,7 +47,9 @@
#define FOREGROUND_WHITE FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
#endif
#ifndef _MSC_VER
#pragma GCC diagnostic ignored "-Wformat-truncation"
#endif
void logvisorBp() {}
@@ -91,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) {
@@ -111,7 +113,7 @@ void KillProcessTree() {
}
}
bContinue = ::Process32Next(hSnap, &pe);
bContinue = ::Process32NextW(hSnap, &pe);
}
}
}
@@ -167,62 +169,35 @@ void KillProcessTree() {
}
#elif defined(__SWITCH__)
[[noreturn]] void logvisorAbort() { exit(1); }
[[noreturn]] void logvisorAbort() {
MainLoggers.clear();
nvExit();
exit(1);
}
#else
void KillProcessTree() {}
#include <execinfo.h>
[[noreturn]] 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
#if __APPLE__
std::string cmdLineStr = fmt::format(fmt("atos -p {}"), getpid());
#else
std::string cmdLineStr = fmt::format(fmt("2>/dev/null addr2line -C -f -e \"{}\""), exeNameBuffer);
#endif
for (size_t i = 0; i < size; i++) {
#if __linux__
Dl_info dlip;
if (dladdr(array[i], &dlip))
cmdLineStr += fmt::format(fmt(" 0x{:016X}"), (uintptr_t)((uint8_t*)array[i] - (uint8_t*)dlip.dli_fbase));
else
cmdLineStr += fmt::format(fmt(" 0x{:016X}"), (uintptr_t)array[i]);
#else
cmdLineStr += fmt::format(fmt(" 0x{:016X}"), (uintptr_t)array[i]);
#endif
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)))
std::fwrite(readBuf, 1, readSz, stderr);
pclose(fp);
} else {
for (size_t i = 0; i < size; i++) {
std::fputs("- ", stderr);
Dl_info dlip;
if (dladdr(array[i], &dlip)) {
int status;
char* demangledName = abi::__cxa_demangle(dlip.dli_sname, nullptr, nullptr, &status);
std::fprintf(stderr, "%p(%s+%p)\n", dlip.dli_saddr, demangledName ? demangledName : dlip.dli_sname,
(void*)((uint8_t*)array[i] - (uint8_t*)dlip.dli_fbase));
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);
} else {
std::fprintf(stderr, "%p\n", array[i]);
}
return 0;
}
[[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);
@@ -245,19 +220,19 @@ static void AbortHandler(int signum) {
_LogMutex.enabled = false;
switch (signum) {
case SIGSEGV:
Log.report(logvisor::Fatal, fmt("Segmentation Fault"));
Log.report(logvisor::Fatal, FMT_STRING("Segmentation Fault"));
break;
case SIGILL:
Log.report(logvisor::Fatal, fmt("Bad Execution"));
Log.report(logvisor::Fatal, FMT_STRING("Bad Execution"));
break;
case SIGFPE:
Log.report(logvisor::Fatal, fmt("Floating Point Exception"));
Log.report(logvisor::Fatal, FMT_STRING("Floating Point Exception"));
break;
case SIGABRT:
Log.report(logvisor::Fatal, fmt("Abort Signal"));
Log.report(logvisor::Fatal, FMT_STRING("Abort Signal"));
break;
default:
Log.report(logvisor::Fatal, fmt("unknown signal {}"), signum);
Log.report(logvisor::Fatal, FMT_STRING("unknown signal {}"), signum);
break;
}
}
@@ -291,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
@@ -322,9 +529,8 @@ struct ConsoleLogger : public ILogger {
/* Clear current line out */
const int width = ConsoleWidth();
std::fputc('\r', stderr);
for (int w = 0; w < width; ++w) {
for (int w = 0; w < width; ++w)
std::fputc(' ', stderr);
}
std::fputc('\r', stderr);
const std::chrono::steady_clock::duration tm = CurrentUptime();
@@ -332,17 +538,15 @@ struct ConsoleLogger : public ILogger {
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];
}
if (XtermColor) {
std::fputs(BOLD "[", stderr);
fmt::print(stderr, fmt(GREEN "{:5.4} "), tmd);
fmt::print(stderr, FMT_STRING(GREEN "{:.4f} "), tmd);
const uint_fast64_t fIdx = FrameIndex.load();
if (fIdx != 0) {
fmt::print(stderr, fmt("({}) "), fIdx);
}
if (fIdx != 0)
fmt::print(stderr, FMT_STRING("({}) "), fIdx);
switch (severity) {
case Info:
std::fputs(BOLD CYAN "INFO", stderr);
@@ -359,13 +563,11 @@ struct ConsoleLogger : public ILogger {
default:
break;
};
fmt::print(stderr, fmt(NORMAL BOLD " {}"), modName);
if (sourceInfo) {
fmt::print(stderr, fmt(BOLD YELLOW " {{}}"), sourceInfo);
}
if (thrName) {
fmt::print(stderr, fmt(BOLD MAGENTA " ({})"), thrName);
}
fmt::print(stderr, FMT_STRING(NORMAL BOLD " {}"), modName);
if (sourceInfo)
fmt::print(stderr, FMT_STRING(BOLD YELLOW " {{}}"), sourceInfo);
if (thrName)
fmt::print(stderr, FMT_STRING(BOLD MAGENTA " ({})"), thrName);
std::fputs(NORMAL BOLD "] " NORMAL, stderr);
} else {
#if _WIN32
@@ -373,11 +575,10 @@ struct ConsoleLogger : public ILogger {
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
std::fputc('[', stderr);
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_GREEN);
std::fprintf(stderr, "%5.4f ", tmd);
fmt::print(stderr, FMT_STRING("{:.4f} "), tmd);
const uint64_t fi = FrameIndex.load();
if (fi != 0) {
if (fi != 0)
std::fprintf(stderr, "(%" PRIu64 ") ", fi);
}
switch (severity) {
case Info:
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_BLUE);
@@ -399,25 +600,23 @@ struct ConsoleLogger : public ILogger {
break;
}
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
std::fprintf(stderr, " %s", modName);
fmt::print(stderr, FMT_STRING(" {}"), modName);
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN);
if (sourceInfo) {
std::fprintf(stderr, " {%s}", sourceInfo);
}
if (sourceInfo)
fmt::print(stderr, FMT_STRING(" {{}}"), sourceInfo);
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE);
if (thrName) {
std::fprintf(stderr, " (%s)", thrName);
}
if (thrName)
fmt::print(stderr, FMT_STRING(" ({})"), thrName);
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
std::fputs("] ", stderr);
SetConsoleTextAttribute(Term, FOREGROUND_WHITE);
#endif
#else
std::fputc('[', stderr);
fmt::print(stderr, fmt("{:5.4} "), tmd);
fmt::print(stderr, FMT_STRING("{:.4f} "), tmd);
uint_fast64_t fIdx = FrameIndex.load();
if (fIdx)
fmt::print(stderr, fmt("({}) "), fIdx);
fmt::print(stderr, FMT_STRING("({}) "), fIdx);
switch (severity) {
case Info:
std::fputs("INFO", stderr);
@@ -434,13 +633,11 @@ struct ConsoleLogger : public ILogger {
default:
break;
}
fmt::print(stderr, fmt(" {}"), modName);
if (sourceInfo) {
fmt::print(stderr, fmt(" {{}}"), sourceInfo);
}
if (thrName) {
fmt::print(stderr, fmt(" ({})"), thrName);
}
fmt::print(stderr, FMT_STRING(" {}"), modName);
if (sourceInfo)
fmt::print(stderr, FMT_STRING(" {{}}"), sourceInfo);
if (thrName)
fmt::print(stderr, FMT_STRING(" ({})"), thrName);
std::fputs("] ", stderr);
#endif
}
@@ -462,7 +659,7 @@ struct ConsoleLogger : public ILogger {
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("{}:{}"), file, linenum).c_str(), severity);
_reportHead(modName, fmt::format(FMT_STRING("{}:{}"), file, linenum).c_str(), severity);
fmt::vprint(stderr, format, args);
std::fputc('\n', stderr);
std::fflush(stderr);
@@ -470,16 +667,22 @@ struct ConsoleLogger : public ILogger {
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("{}:{}"), file, linenum).c_str(), severity);
_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
@@ -568,7 +771,7 @@ struct FileLogger : public ILogger {
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::string_view format,
fmt::format_args args) override {
openFile();
_reportHead(modName, fmt::format(fmt("{}:{}"), file, linenum).c_str(), severity);
_reportHead(modName, fmt::format(FMT_STRING("{}:{}"), file, linenum).c_str(), severity);
fmt::vprint(fp, format, args);
std::fputc('\n', fp);
closeFile();
@@ -577,7 +780,7 @@ struct FileLogger : public ILogger {
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::wstring_view format,
fmt::wformat_args args) override {
openFile();
_reportHead(modName, fmt::format(fmt("{}:{}"), file, linenum).c_str(), severity);
_reportHead(modName, fmt::format(FMT_STRING("{}:{}"), file, linenum).c_str(), severity);
fmt::vprint(fp, format, args);
std::fputc('\n', fp);
closeFile();