Files

826 lines
23 KiB
C++
Raw Permalink Normal View History

2015-07-03 12:08:41 -10:00
#if _WIN32
2015-08-30 17:31:31 -10:00
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
2015-09-26 18:33:36 -10:00
#ifndef NOMINMAX
#define NOMINMAX
#endif
2015-07-03 19:20:02 -10:00
#include <windows.h>
2015-11-04 14:02:40 -10:00
#include <io.h>
2018-01-09 20:14:40 -10:00
#include <DbgHelp.h>
#include <TlHelp32.h>
2018-10-06 16:56:33 -10:00
#elif defined(__SWITCH__)
#include <cstring>
2020-05-03 20:09:21 -10:00
#include <switch.h>
2015-09-02 11:58:37 -10:00
#else
#include <sys/ioctl.h>
#include <unistd.h>
2016-09-17 11:30:17 -10:00
#include <dlfcn.h>
#include <cxxabi.h>
2017-12-28 21:53:09 -10:00
#include <cstring>
2018-06-01 14:01:11 -10:00
#if __linux__
#include <sys/prctl.h>
#endif
2015-07-03 12:08:41 -10:00
#endif
#include <fcntl.h>
2015-07-03 12:08:41 -10:00
#include <chrono>
#include <mutex>
#include <thread>
2016-09-18 15:32:20 -10:00
#include <string>
2015-07-03 12:08:41 -10:00
#include <unordered_map>
2017-12-28 21:53:09 -10:00
#include <cstdio>
#include <cinttypes>
2020-05-05 00:14:41 -04:00
#include <csignal>
2016-03-04 10:11:37 -10:00
#include "logvisor/logvisor.hpp"
2015-07-03 12:08:41 -10:00
/* ANSI sequences */
2015-07-03 20:41:44 -10:00
#define RED "\x1b[1;31m"
#define YELLOW "\x1b[1;33m"
#define GREEN "\x1b[1;32m"
#define MAGENTA "\x1b[1;35m"
#define CYAN "\x1b[1;36m"
2015-07-03 12:08:41 -10:00
#define BOLD "\x1b[1m"
#define NORMAL "\x1b[0m"
2015-07-22 09:06:24 -10:00
#if _WIN32
2018-12-07 19:17:15 -10:00
#define FOREGROUND_WHITE FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
2015-07-22 09:06:24 -10:00
#endif
2020-08-22 17:28:34 -10:00
#ifndef _MSC_VER
2019-06-09 16:48:06 -10:00
#pragma GCC diagnostic ignored "-Wformat-truncation"
2020-08-22 17:28:34 -10:00
#endif
2019-06-09 16:48:06 -10:00
2016-03-04 13:01:18 -10:00
void logvisorBp() {}
2015-11-25 21:32:50 -10:00
2018-12-07 19:17:15 -10:00
namespace logvisor {
static Module Log("logvisor");
2015-07-03 12:08:41 -10:00
static std::unordered_map<std::thread::id, const char*> ThreadMap;
2017-01-16 15:30:32 -10:00
2018-12-07 19:17:15 -10:00
static void AddThreadToMap(const char* name) {
auto lk = LockLog();
ThreadMap[std::this_thread::get_id()] = name;
2017-01-16 15:30:32 -10:00
}
2018-12-07 19:17:15 -10:00
void RegisterThreadName(const char* name) {
AddThreadToMap(name);
2015-07-03 12:08:41 -10:00
#if __APPLE__
2018-12-07 19:17:15 -10:00
pthread_setname_np(name);
2015-07-03 12:08:41 -10:00
#elif __linux__
2018-12-07 19:17:15 -10:00
prctl(PR_SET_NAME, name);
2015-07-03 19:20:02 -10:00
#elif _MSC_VER
2018-12-07 19:17:15 -10:00
struct {
DWORD dwType; // Must be 0x1000.
LPCSTR szName; // Pointer to name (in user addr space).
DWORD dwThreadID; // Thread ID (-1=caller thread).
DWORD dwFlags; // Reserved for future use, must be zero.
} info = {0x1000, name, (DWORD)-1, 0};
__try {
RaiseException(0x406D1388, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
} __except (EXCEPTION_EXECUTE_HANDLER) {}
2015-07-03 12:08:41 -10:00
#endif
}
#if _WIN32
#pragma comment(lib, "Dbghelp.lib")
2017-12-05 17:20:32 -10:00
#if defined(WINAPI_FAMILY) && WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP
#define WINDOWS_STORE 1
#else
#define WINDOWS_STORE 0
#endif
2018-12-07 19:17:15 -10:00
void KillProcessTree() {
DWORD myprocID = GetCurrentProcessId();
2020-08-22 17:28:34 -10:00
PROCESSENTRY32W pe = {};
pe.dwSize = sizeof(pe);
2018-01-09 20:14:40 -10:00
2018-12-07 19:17:15 -10:00
HANDLE hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
2018-01-09 20:14:40 -10:00
2020-08-22 17:28:34 -10:00
if (::Process32FirstW(hSnap, &pe)) {
2018-12-07 19:17:15 -10:00
BOOL bContinue = TRUE;
2018-01-09 20:14:40 -10:00
2018-12-07 19:17:15 -10:00
// kill child processes
while (bContinue) {
2019-05-09 18:06:21 -10:00
// only kill child processes and let console window remain
2020-08-22 17:28:34 -10:00
if (pe.th32ParentProcessID == myprocID && std::wcscmp(pe.szExeFile, L"conhost.exe") != 0) {
2018-12-07 19:17:15 -10:00
HANDLE hChildProc = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID);
2018-01-09 20:14:40 -10:00
2018-12-07 19:17:15 -10:00
if (hChildProc) {
::TerminateProcess(hChildProc, 1);
::CloseHandle(hChildProc);
2018-01-09 20:14:40 -10:00
}
2018-12-07 19:17:15 -10:00
}
2020-08-22 17:28:34 -10:00
bContinue = ::Process32NextW(hSnap, &pe);
2018-01-09 20:14:40 -10:00
}
2018-12-07 19:17:15 -10:00
}
2018-01-09 20:14:40 -10:00
}
[[noreturn]] void logvisorAbort() {
2017-12-05 17:20:32 -10:00
#if !WINDOWS_STORE
2018-12-07 19:17:15 -10:00
unsigned int i;
void* stack[100];
unsigned short frames;
SYMBOL_INFO* symbol;
HANDLE process;
2018-12-07 19:17:15 -10:00
process = GetCurrentProcess();
SymInitialize(process, NULL, TRUE);
frames = CaptureStackBackTrace(0, 100, stack, NULL);
symbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
symbol->MaxNameLen = 255;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
2018-12-07 19:17:15 -10:00
for (i = 0; i < frames; i++) {
SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol);
std::fprintf(stderr, "%i: %s - 0x%0llX", frames - i - 1, symbol->Name, symbol->Address);
2018-12-07 19:17:15 -10:00
DWORD dwDisplacement;
IMAGEHLP_LINE64 line;
SymSetOptions(SYMOPT_LOAD_LINES);
2018-12-07 19:17:15 -10:00
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
if (SymGetLineFromAddr64(process, (DWORD64)(stack[i]), &dwDisplacement, &line)) {
// SymGetLineFromAddr64 returned success
std::fprintf(stderr, " LINE %d\n", int(line.LineNumber));
2018-12-07 19:17:15 -10:00
} else {
std::fputc('\n', stderr);
}
2018-12-07 19:17:15 -10:00
}
std::fflush(stderr);
std::free(symbol);
2017-12-05 17:20:32 -10:00
#endif
2018-01-09 20:14:40 -10:00
2018-12-07 19:17:15 -10:00
KillProcessTree();
2018-01-09 20:14:40 -10:00
2018-12-07 19:17:15 -10:00
// If you caught one of the above signals, it is likely you just
// want to quit your program right now.
#ifndef NDEBUG
2018-12-07 19:17:15 -10:00
signal(SIGABRT, SIG_DFL);
abort();
#else
2018-12-07 19:17:15 -10:00
exit(1);
#endif
}
2018-10-06 16:56:33 -10:00
#elif defined(__SWITCH__)
2020-05-03 20:09:21 -10:00
[[noreturn]] void logvisorAbort() {
MainLoggers.clear();
2020-05-05 00:14:41 -04:00
nvExit();
2020-05-03 20:09:21 -10:00
exit(1);
}
#else
2018-01-09 20:14:40 -10:00
void KillProcessTree() {}
2020-12-16 07:31:39 -05:00
#include <backtrace.h>
2016-09-17 11:30:17 -10:00
2020-12-16 07:31:39 -05:00
void backtrace_error(void* data, const char* msg, int errnum) {
std::fprintf(stderr, "Error retrieving stack trace: %d %s\n", errnum, msg);
}
2016-09-17 11:30:17 -10:00
2020-12-16 07:31:39 -05:00
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);
2018-12-07 19:17:15 -10:00
}
2020-12-16 07:31:39 -05:00
return 0;
}
2020-12-16 07:31:39 -05:00
[[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);
2018-12-07 19:17:15 -10:00
}
2019-07-19 18:21:39 -10:00
std::fflush(stderr);
std::fflush(stdout);
2018-12-07 19:17:15 -10:00
KillProcessTree();
#ifndef NDEBUG
2018-12-07 19:17:15 -10:00
signal(SIGABRT, SIG_DFL);
abort();
#else
2018-12-07 19:17:15 -10:00
exit(1);
#endif
}
#endif
2017-01-18 10:54:00 -10:00
LogMutex _LogMutex;
2018-12-07 19:17:15 -10:00
static void AbortHandler(int signum) {
_LogMutex.enabled = false;
switch (signum) {
case SIGSEGV:
2020-04-11 12:44:21 -10:00
Log.report(logvisor::Fatal, FMT_STRING("Segmentation Fault"));
2019-02-17 19:45:51 -10:00
break;
2018-12-07 19:17:15 -10:00
case SIGILL:
2020-04-11 12:44:21 -10:00
Log.report(logvisor::Fatal, FMT_STRING("Bad Execution"));
2019-02-17 19:45:51 -10:00
break;
2018-12-07 19:17:15 -10:00
case SIGFPE:
2020-04-11 12:44:21 -10:00
Log.report(logvisor::Fatal, FMT_STRING("Floating Point Exception"));
2019-02-17 19:45:51 -10:00
break;
2018-12-07 19:17:15 -10:00
case SIGABRT:
2020-04-11 12:44:21 -10:00
Log.report(logvisor::Fatal, FMT_STRING("Abort Signal"));
2019-02-17 19:45:51 -10:00
break;
2018-12-07 19:17:15 -10:00
default:
2020-04-11 12:44:21 -10:00
Log.report(logvisor::Fatal, FMT_STRING("unknown signal {}"), signum);
2019-02-17 19:45:51 -10:00
break;
2018-12-07 19:17:15 -10:00
}
}
2018-03-23 11:38:33 -10:00
uint64_t _LogCounter;
2015-07-03 12:08:41 -10:00
std::vector<std::unique_ptr<ILogger>> MainLoggers;
std::atomic_size_t ErrorCount(0);
2015-07-03 12:08:41 -10:00
static std::chrono::steady_clock MonoClock;
static std::chrono::steady_clock::time_point GlobalStart = MonoClock.now();
2018-12-07 19:17:15 -10:00
static inline std::chrono::steady_clock::duration CurrentUptime() { return MonoClock.now() - GlobalStart; }
2015-07-26 10:55:17 -10:00
std::atomic_uint_fast64_t FrameIndex(0);
2016-03-04 17:21:18 -10:00
2018-12-07 19:17:15 -10:00
static inline int ConsoleWidth() {
int retval = 80;
2015-09-02 11:58:37 -10:00
#if _WIN32
2017-12-05 17:20:32 -10:00
#if !WINDOWS_STORE
2018-12-07 19:17:15 -10:00
CONSOLE_SCREEN_BUFFER_INFO info;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info);
retval = info.dwSize.X - 1;
2017-12-05 17:20:32 -10:00
#endif
2018-10-06 16:56:33 -10:00
#elif defined(__SWITCH__)
2018-12-07 19:17:15 -10:00
return 80;
2015-09-02 11:58:37 -10:00
#else
2018-12-07 19:17:15 -10:00
struct winsize w;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1)
retval = w.ws_col;
2015-09-02 11:58:37 -10:00
#endif
2018-12-07 19:17:15 -10:00
if (retval < 10)
return 10;
return retval;
2015-09-02 11:58:37 -10:00
}
2015-07-03 12:08:41 -10:00
2020-05-05 00:14:41 -04:00
#if LOGVISOR_NX_LM
2020-05-03 20:09:21 -10:00
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
2015-07-03 12:08:41 -10:00
#if _WIN32
static HANDLE Term = 0;
#else
static const char* Term = nullptr;
#endif
2015-10-07 13:18:37 -10:00
bool XtermColor = false;
2018-12-07 19:17:15 -10:00
struct ConsoleLogger : public ILogger {
ConsoleLogger() {
2015-07-03 12:08:41 -10:00
#if _WIN32
2017-12-05 17:20:32 -10:00
#if !WINDOWS_STORE
2018-12-07 19:17:15 -10:00
const char* conemuANSI = getenv("ConEmuANSI");
if (conemuANSI && !strcmp(conemuANSI, "ON"))
XtermColor = true;
2017-12-05 17:20:32 -10:00
#endif
2018-12-07 19:17:15 -10:00
if (!Term)
Term = GetStdHandle(STD_ERROR_HANDLE);
2015-07-03 12:08:41 -10:00
#else
2018-12-07 19:17:15 -10:00
if (!Term) {
Term = getenv("TERM");
if (Term && !strncmp(Term, "xterm", 5)) {
XtermColor = true;
putenv((char*)"TERM=xterm-16color");
}
2015-07-03 12:08:41 -10:00
}
2018-12-07 19:17:15 -10:00
#endif
}
2015-07-03 12:08:41 -10:00
2018-12-07 19:17:15 -10:00
static void _reportHead(const char* modName, const char* sourceInfo, Level severity) {
/* Clear current line out */
const int width = ConsoleWidth();
std::fputc('\r', stderr);
for (int w = 0; w < width; ++w)
std::fputc(' ', stderr);
std::fputc('\r', stderr);
2016-03-04 17:21:18 -10:00
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();
2018-12-07 19:17:15 -10:00
const char* thrName = nullptr;
if (ThreadMap.find(thrId) != ThreadMap.end())
2018-12-07 19:17:15 -10:00
thrName = ThreadMap[thrId];
2015-07-03 12:08:41 -10:00
2018-12-07 19:17:15 -10:00
if (XtermColor) {
2019-07-19 18:21:39 -10:00
std::fputs(BOLD "[", stderr);
2020-04-11 12:44:21 -10:00
fmt::print(stderr, FMT_STRING(GREEN "{:.4f} "), tmd);
const uint_fast64_t fIdx = FrameIndex.load();
if (fIdx != 0)
2020-04-11 12:44:21 -10:00
fmt::print(stderr, FMT_STRING("({}) "), fIdx);
2018-12-07 19:17:15 -10:00
switch (severity) {
case Info:
2019-07-19 18:21:39 -10:00
std::fputs(BOLD CYAN "INFO", stderr);
2018-12-07 19:17:15 -10:00
break;
case Warning:
2019-07-19 18:21:39 -10:00
std::fputs(BOLD YELLOW "WARNING", stderr);
2018-12-07 19:17:15 -10:00
break;
case Error:
2019-07-19 18:21:39 -10:00
std::fputs(RED BOLD "ERROR", stderr);
2018-12-07 19:17:15 -10:00
break;
case Fatal:
2019-07-19 18:21:39 -10:00
std::fputs(BOLD RED "FATAL ERROR", stderr);
2018-12-07 19:17:15 -10:00
break;
default:
break;
};
2020-04-11 12:44:21 -10:00
fmt::print(stderr, FMT_STRING(NORMAL BOLD " {}"), modName);
if (sourceInfo)
2020-04-11 12:44:21 -10:00
fmt::print(stderr, FMT_STRING(BOLD YELLOW " {{}}"), sourceInfo);
if (thrName)
2020-04-11 12:44:21 -10:00
fmt::print(stderr, FMT_STRING(BOLD MAGENTA " ({})"), thrName);
2019-07-19 18:21:39 -10:00
std::fputs(NORMAL BOLD "] " NORMAL, stderr);
2018-12-07 19:17:15 -10:00
} else {
2015-09-02 11:58:37 -10:00
#if _WIN32
2017-12-05 17:20:32 -10:00
#if !WINDOWS_STORE
2018-12-07 19:17:15 -10:00
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
std::fputc('[', stderr);
2018-12-07 19:17:15 -10:00
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_GREEN);
2020-04-11 12:44:21 -10:00
fmt::print(stderr, FMT_STRING("{:.4f} "), tmd);
const uint64_t fi = FrameIndex.load();
if (fi != 0)
std::fprintf(stderr, "(%" PRIu64 ") ", fi);
2018-12-07 19:17:15 -10:00
switch (severity) {
case Info:
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_BLUE);
std::fputs("INFO", stderr);
2018-12-07 19:17:15 -10:00
break;
case Warning:
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN);
std::fputs("WARNING", stderr);
2018-12-07 19:17:15 -10:00
break;
case Error:
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED);
std::fputs("ERROR", stderr);
2018-12-07 19:17:15 -10:00
break;
case Fatal:
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED);
std::fputs("FATAL ERROR", stderr);
2018-12-07 19:17:15 -10:00
break;
default:
break;
}
2018-12-07 19:17:15 -10:00
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
2020-04-11 12:44:21 -10:00
fmt::print(stderr, FMT_STRING(" {}"), modName);
2018-12-07 19:17:15 -10:00
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN);
if (sourceInfo)
2020-04-11 12:44:21 -10:00
fmt::print(stderr, FMT_STRING(" {{}}"), sourceInfo);
2018-12-07 19:17:15 -10:00
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE);
if (thrName)
2020-04-11 12:44:21 -10:00
fmt::print(stderr, FMT_STRING(" ({})"), thrName);
2018-12-07 19:17:15 -10:00
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
std::fputs("] ", stderr);
2018-12-07 19:17:15 -10:00
SetConsoleTextAttribute(Term, FOREGROUND_WHITE);
2017-12-05 17:20:32 -10:00
#endif
2015-09-02 11:58:37 -10:00
#else
std::fputc('[', stderr);
2020-04-11 12:44:21 -10:00
fmt::print(stderr, FMT_STRING("{:.4f} "), tmd);
2018-12-07 19:17:15 -10:00
uint_fast64_t fIdx = FrameIndex.load();
if (fIdx)
2020-04-11 12:44:21 -10:00
fmt::print(stderr, FMT_STRING("({}) "), fIdx);
2018-12-07 19:17:15 -10:00
switch (severity) {
case Info:
2019-07-19 18:21:39 -10:00
std::fputs("INFO", stderr);
2018-12-07 19:17:15 -10:00
break;
case Warning:
2019-07-19 18:21:39 -10:00
std::fputs("WARNING", stderr);
2018-12-07 19:17:15 -10:00
break;
case Error:
2019-07-19 18:21:39 -10:00
std::fputs("ERROR", stderr);
2018-12-07 19:17:15 -10:00
break;
case Fatal:
2019-07-19 18:21:39 -10:00
std::fputs("FATAL ERROR", stderr);
2018-12-07 19:17:15 -10:00
break;
default:
break;
}
2020-04-11 12:44:21 -10:00
fmt::print(stderr, FMT_STRING(" {}"), modName);
if (sourceInfo)
2020-04-11 12:44:21 -10:00
fmt::print(stderr, FMT_STRING(" {{}}"), sourceInfo);
if (thrName)
2020-04-11 12:44:21 -10:00
fmt::print(stderr, FMT_STRING(" ({})"), thrName);
2019-07-19 18:21:39 -10:00
std::fputs("] ", stderr);
2015-07-03 12:08:41 -10:00
#endif
}
2018-12-07 19:17:15 -10:00
}
2015-07-03 12:08:41 -10:00
2019-08-26 10:34:40 -04:00
void report(const char* modName, Level severity, fmt::string_view format, fmt::format_args args) override {
2018-12-07 19:17:15 -10:00
_reportHead(modName, nullptr, severity);
2019-07-19 18:21:39 -10:00
fmt::vprint(stderr, format, args);
std::fputc('\n', stderr);
2019-07-19 18:21:39 -10:00
std::fflush(stderr);
2018-12-07 19:17:15 -10:00
}
2015-07-03 12:08:41 -10:00
2019-08-26 10:34:40 -04:00
void report(const char* modName, Level severity, fmt::wstring_view format, fmt::wformat_args args) override {
2018-12-07 19:17:15 -10:00
_reportHead(modName, nullptr, severity);
2019-07-19 18:21:39 -10:00
fmt::vprint(stderr, format, args);
std::fputc('\n', stderr);
2019-07-19 18:21:39 -10:00
std::fflush(stderr);
2018-12-07 19:17:15 -10:00
}
2015-07-03 20:41:44 -10:00
2019-08-26 10:34:40 -04:00
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::string_view format,
fmt::format_args args) override {
2020-04-11 12:44:21 -10:00
_reportHead(modName, fmt::format(FMT_STRING("{}:{}"), file, linenum).c_str(), severity);
2019-07-19 18:21:39 -10:00
fmt::vprint(stderr, format, args);
std::fputc('\n', stderr);
2019-07-19 18:21:39 -10:00
std::fflush(stderr);
2018-12-07 19:17:15 -10:00
}
2015-07-03 20:41:44 -10:00
2019-08-26 10:34:40 -04:00
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::wstring_view format,
fmt::wformat_args args) override {
2020-04-11 12:44:21 -10:00
_reportHead(modName, fmt::format(FMT_STRING("{}:{}"), file, linenum).c_str(), severity);
2019-07-19 18:21:39 -10:00
fmt::vprint(stderr, format, args);
std::fputc('\n', stderr);
2019-07-19 18:21:39 -10:00
std::fflush(stderr);
2018-12-07 19:17:15 -10:00
}
2015-07-03 12:08:41 -10:00
};
2020-05-03 20:09:21 -10:00
#endif
2015-07-03 12:08:41 -10:00
static bool ConsoleLoggerRegistered = false;
2018-12-07 19:17:15 -10:00
void RegisterConsoleLogger() {
/* Otherwise construct new console logger */
if (!ConsoleLoggerRegistered) {
MainLoggers.emplace_back(new ConsoleLogger);
ConsoleLoggerRegistered = true;
}
2015-07-03 12:08:41 -10:00
}
2015-11-04 14:02:40 -10:00
#if _WIN32
2018-12-07 19:17:15 -10:00
void CreateWin32Console() {
2017-12-05 17:20:32 -10:00
#if !WINDOWS_STORE
2018-12-07 19:17:15 -10:00
/* Debug console */
AllocConsole();
2015-11-04 14:02:40 -10:00
std::freopen("CONIN$", "r", stdin);
std::freopen("CONOUT$", "w", stdout);
std::freopen("CONOUT$", "w", stderr);
2017-12-05 17:20:32 -10:00
#endif
2015-11-04 14:02:40 -10:00
}
#endif
2018-12-07 19:17:15 -10:00
void RegisterStandardExceptions() {
signal(SIGABRT, AbortHandler);
signal(SIGSEGV, AbortHandler);
signal(SIGILL, AbortHandler);
signal(SIGFPE, AbortHandler);
}
2018-12-07 19:17:15 -10:00
struct FileLogger : public ILogger {
FILE* fp;
virtual void openFile() = 0;
2019-07-19 18:21:39 -10:00
virtual void closeFile() { std::fclose(fp); }
2015-07-03 12:08:41 -10:00
2018-12-07 19:17:15 -10:00
void _reportHead(const char* modName, const char* sourceInfo, Level severity) {
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();
2018-12-07 19:17:15 -10:00
const char* thrName = nullptr;
if (ThreadMap.find(thrId) != ThreadMap.end()) {
2018-12-07 19:17:15 -10:00
thrName = ThreadMap[thrId];
}
2015-07-03 12:08:41 -10:00
std::fputc('[', fp);
2019-07-19 18:21:39 -10:00
std::fprintf(fp, "%5.4f ", tmd);
const uint_fast64_t fIdx = FrameIndex.load();
if (fIdx != 0) {
2019-07-19 18:21:39 -10:00
std::fprintf(fp, "(%" PRIu64 ") ", fIdx);
}
2018-12-07 19:17:15 -10:00
switch (severity) {
case Info:
2019-07-19 18:21:39 -10:00
std::fputs("INFO", fp);
2018-12-07 19:17:15 -10:00
break;
case Warning:
2019-07-19 18:21:39 -10:00
std::fputs("WARNING", fp);
2018-12-07 19:17:15 -10:00
break;
case Error:
2019-07-19 18:21:39 -10:00
std::fputs("ERROR", fp);
2018-12-07 19:17:15 -10:00
break;
case Fatal:
2019-07-19 18:21:39 -10:00
std::fputs("FATAL ERROR", fp);
2018-12-07 19:17:15 -10:00
break;
default:
break;
};
2019-07-19 18:21:39 -10:00
std::fprintf(fp, " %s", modName);
if (sourceInfo) {
2019-07-19 18:21:39 -10:00
std::fprintf(fp, " {%s}", sourceInfo);
}
if (thrName) {
2019-07-19 18:21:39 -10:00
std::fprintf(fp, " (%s)", thrName);
}
2019-07-19 18:21:39 -10:00
std::fputs("] ", fp);
2018-12-07 19:17:15 -10:00
}
2015-07-03 12:08:41 -10:00
2019-08-26 10:34:40 -04:00
void report(const char* modName, Level severity, fmt::string_view format, fmt::format_args args) override {
2018-12-07 19:17:15 -10:00
openFile();
_reportHead(modName, nullptr, severity);
2019-07-19 18:21:39 -10:00
fmt::vprint(fp, format, args);
std::fputc('\n', fp);
2018-12-07 19:17:15 -10:00
closeFile();
}
2015-07-03 12:08:41 -10:00
2019-08-26 10:34:40 -04:00
void report(const char* modName, Level severity, fmt::wstring_view format, fmt::wformat_args args) override {
2018-12-07 19:17:15 -10:00
openFile();
_reportHead(modName, nullptr, severity);
2019-07-19 18:21:39 -10:00
fmt::vprint(fp, format, args);
std::fputc('\n', fp);
2018-12-07 19:17:15 -10:00
closeFile();
}
2015-07-03 20:41:44 -10:00
2019-08-26 10:34:40 -04:00
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::string_view format,
fmt::format_args args) override {
2018-12-07 19:17:15 -10:00
openFile();
2020-04-11 12:44:21 -10:00
_reportHead(modName, fmt::format(FMT_STRING("{}:{}"), file, linenum).c_str(), severity);
2019-07-19 18:21:39 -10:00
fmt::vprint(fp, format, args);
std::fputc('\n', fp);
2018-12-07 19:17:15 -10:00
closeFile();
}
2015-07-03 20:41:44 -10:00
2019-08-26 10:34:40 -04:00
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::wstring_view format,
fmt::wformat_args args) override {
2018-12-07 19:17:15 -10:00
openFile();
2020-04-11 12:44:21 -10:00
_reportHead(modName, fmt::format(FMT_STRING("{}:{}"), file, linenum).c_str(), severity);
2019-07-19 18:21:39 -10:00
fmt::vprint(fp, format, args);
std::fputc('\n', fp);
2018-12-07 19:17:15 -10:00
closeFile();
}
2015-07-03 12:08:41 -10:00
};
2018-12-07 19:17:15 -10:00
struct FileLogger8 : public FileLogger {
const char* m_filepath;
FileLogger8(const char* filepath) : m_filepath(filepath) {}
2019-08-26 10:34:40 -04:00
void openFile() override { fp = std::fopen(m_filepath, "a"); }
2015-07-03 12:08:41 -10:00
};
2018-12-07 19:17:15 -10:00
void RegisterFileLogger(const char* filepath) {
/* Otherwise construct new file logger */
MainLoggers.emplace_back(new FileLogger8(filepath));
2015-07-03 12:08:41 -10:00
}
#if LOG_UCS2
2018-12-07 19:17:15 -10:00
struct FileLogger16 : public FileLogger {
const wchar_t* m_filepath;
FileLogger16(const wchar_t* filepath) : m_filepath(filepath) {}
2019-08-26 10:34:40 -04:00
void openFile() override { fp = _wfopen(m_filepath, L"a"); }
2015-07-03 12:08:41 -10:00
};
2018-12-07 19:17:15 -10:00
void RegisterFileLogger(const wchar_t* filepath) {
/* Determine if file logger already added */
for (auto& logger : MainLoggers) {
FileLogger16* filelogger = dynamic_cast<FileLogger16*>(logger.get());
if (filelogger) {
if (!wcscmp(filepath, filelogger->m_filepath))
return;
2015-07-03 12:08:41 -10:00
}
2018-12-07 19:17:15 -10:00
}
2015-07-03 12:08:41 -10:00
2018-12-07 19:17:15 -10:00
/* Otherwise construct new file logger */
MainLoggers.emplace_back(new FileLogger16(filepath));
2015-07-03 12:08:41 -10:00
}
#endif
2018-12-07 19:17:15 -10:00
} // namespace logvisor