2015-07-03 15:08:41 -07:00
|
|
|
#if _WIN32
|
2015-08-30 20:31:31 -07:00
|
|
|
#ifndef WIN32_LEAN_AND_MEAN
|
|
|
|
#define WIN32_LEAN_AND_MEAN 1
|
|
|
|
#endif
|
2015-09-26 21:33:36 -07:00
|
|
|
#ifndef NOMINMAX
|
|
|
|
#define NOMINMAX
|
|
|
|
#endif
|
2015-07-03 22:20:02 -07:00
|
|
|
#include <windows.h>
|
2015-11-04 16:02:40 -08:00
|
|
|
#include <io.h>
|
2018-01-09 22:14:40 -08:00
|
|
|
#include <DbgHelp.h>
|
|
|
|
#include <TlHelp32.h>
|
2018-10-06 19:56:33 -07:00
|
|
|
#elif defined(__SWITCH__)
|
|
|
|
#include <cstring>
|
2020-05-03 23:09:21 -07:00
|
|
|
#include <switch.h>
|
2015-09-02 14:58:37 -07:00
|
|
|
#else
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <unistd.h>
|
2016-09-17 14:30:17 -07:00
|
|
|
#include <dlfcn.h>
|
|
|
|
#include <cxxabi.h>
|
2017-12-28 23:53:09 -08:00
|
|
|
#include <cstring>
|
2018-06-01 17:01:11 -07:00
|
|
|
#if __linux__
|
|
|
|
#include <sys/prctl.h>
|
|
|
|
#endif
|
2015-07-03 15:08:41 -07:00
|
|
|
#endif
|
|
|
|
|
2016-09-07 23:13:49 -07:00
|
|
|
#include <fcntl.h>
|
2015-07-03 15:08:41 -07:00
|
|
|
#include <chrono>
|
|
|
|
#include <mutex>
|
|
|
|
#include <thread>
|
2016-09-18 18:32:20 -07:00
|
|
|
#include <string>
|
2015-07-03 15:08:41 -07:00
|
|
|
#include <unordered_map>
|
2017-12-28 23:53:09 -08:00
|
|
|
#include <cstdio>
|
|
|
|
#include <cinttypes>
|
2020-05-04 21:14:41 -07:00
|
|
|
#include <csignal>
|
Wide char homogeneity
If wide char stdlib functions are used on a narrow char stream, they attempt to narrow the character or string. If this fails, nothing is written to the stream and an error is indicated. The {fmt} library, at its core, uses stdlib functions for printing to a stream. However, it takes it a step further and acts upon errors while printing by throwing an exception.
If narrow char stdlib functions are used on a wide char stream, they throw an assertion. The {fmt} library somehow does not throw an assertion, but whatever it ends up printing is garbage anyhow.
So from either end, it is generally a bad idea to mix narrow and wide character printing. While you can get away with using wide char functions on a narrow char stream, the moment you start using anything other than English characters, it all falls apart. Just as well, narrow chars on wide char streams do not work at all. Despite this, I found many situations where wide char printing and narrow char printing was being used at the same time. This PR makes character printing homogeneous to avoid the previously discussed issues.
2021-05-07 22:52:03 -07:00
|
|
|
#include <locale>
|
2021-06-19 14:43:23 -07:00
|
|
|
#include <optional>
|
2016-03-04 12:11:37 -08:00
|
|
|
#include "logvisor/logvisor.hpp"
|
2015-07-03 15:08:41 -07:00
|
|
|
|
2021-04-04 15:20:48 -07:00
|
|
|
#if SENTRY_ENABLED
|
|
|
|
#include <sentry.h>
|
|
|
|
#endif
|
|
|
|
|
2015-07-03 15:08:41 -07:00
|
|
|
/* ANSI sequences */
|
2015-07-03 23:41:44 -07: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 15:08:41 -07:00
|
|
|
#define BOLD "\x1b[1m"
|
|
|
|
#define NORMAL "\x1b[0m"
|
|
|
|
|
2015-07-22 12:06:24 -07:00
|
|
|
#if _WIN32
|
2018-12-07 21:17:15 -08:00
|
|
|
#define FOREGROUND_WHITE FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
|
2015-07-22 12:06:24 -07:00
|
|
|
#endif
|
|
|
|
|
2020-08-22 20:28:34 -07:00
|
|
|
#ifndef _MSC_VER
|
2019-06-09 19:48:06 -07:00
|
|
|
#pragma GCC diagnostic ignored "-Wformat-truncation"
|
2020-08-22 20:28:34 -07:00
|
|
|
#endif
|
2019-06-09 19:48:06 -07:00
|
|
|
|
2016-03-04 15:01:18 -08:00
|
|
|
void logvisorBp() {}
|
2015-11-25 23:32:50 -08:00
|
|
|
|
2018-12-07 21:17:15 -08:00
|
|
|
namespace logvisor {
|
2016-09-07 23:13:49 -07:00
|
|
|
static Module Log("logvisor");
|
2015-07-03 15:08:41 -07:00
|
|
|
|
|
|
|
static std::unordered_map<std::thread::id, const char*> ThreadMap;
|
2017-01-16 17:30:32 -08:00
|
|
|
|
2018-12-07 21:17:15 -08:00
|
|
|
static void AddThreadToMap(const char* name) {
|
|
|
|
auto lk = LockLog();
|
|
|
|
ThreadMap[std::this_thread::get_id()] = name;
|
2017-01-16 17:30:32 -08:00
|
|
|
}
|
|
|
|
|
2018-12-07 21:17:15 -08:00
|
|
|
void RegisterThreadName(const char* name) {
|
|
|
|
AddThreadToMap(name);
|
2015-07-03 15:08:41 -07:00
|
|
|
#if __APPLE__
|
2018-12-07 21:17:15 -08:00
|
|
|
pthread_setname_np(name);
|
2015-07-03 15:08:41 -07:00
|
|
|
#elif __linux__
|
2018-12-07 21:17:15 -08:00
|
|
|
prctl(PR_SET_NAME, name);
|
2015-07-03 22:20:02 -07:00
|
|
|
#elif _MSC_VER
|
2018-12-07 21:17:15 -08: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 15:08:41 -07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-09-07 23:13:49 -07:00
|
|
|
#if _WIN32
|
|
|
|
#pragma comment(lib, "Dbghelp.lib")
|
|
|
|
|
2017-12-05 19:20:32 -08:00
|
|
|
#if defined(WINAPI_FAMILY) && WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP
|
|
|
|
#define WINDOWS_STORE 1
|
|
|
|
#else
|
|
|
|
#define WINDOWS_STORE 0
|
|
|
|
#endif
|
|
|
|
|
2018-12-07 21:17:15 -08:00
|
|
|
void KillProcessTree() {
|
|
|
|
DWORD myprocID = GetCurrentProcessId();
|
2020-08-22 20:28:34 -07:00
|
|
|
PROCESSENTRY32W pe = {};
|
|
|
|
pe.dwSize = sizeof(pe);
|
2018-12-07 21:17:15 -08:00
|
|
|
|
|
|
|
HANDLE hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
|
|
|
|
2021-06-22 12:13:37 -07:00
|
|
|
if (::Process32FirstW(hSnap, &pe) == TRUE) {
|
2018-12-07 21:17:15 -08:00
|
|
|
BOOL bContinue = TRUE;
|
|
|
|
|
|
|
|
// kill child processes
|
2021-06-22 12:13:37 -07:00
|
|
|
while (bContinue == TRUE) {
|
2019-05-09 21:06:21 -07:00
|
|
|
// only kill child processes and let console window remain
|
2020-08-22 20:28:34 -07:00
|
|
|
if (pe.th32ParentProcessID == myprocID && std::wcscmp(pe.szExeFile, L"conhost.exe") != 0) {
|
2018-12-07 21:17:15 -08:00
|
|
|
HANDLE hChildProc = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID);
|
|
|
|
|
2021-06-22 12:13:37 -07:00
|
|
|
if (hChildProc != nullptr) {
|
2018-12-07 21:17:15 -08:00
|
|
|
::TerminateProcess(hChildProc, 1);
|
|
|
|
::CloseHandle(hChildProc);
|
2018-01-09 22:14:40 -08:00
|
|
|
}
|
2018-12-07 21:17:15 -08:00
|
|
|
}
|
|
|
|
|
2020-08-22 20:28:34 -07:00
|
|
|
bContinue = ::Process32NextW(hSnap, &pe);
|
2018-01-09 22:14:40 -08:00
|
|
|
}
|
2018-12-07 21:17:15 -08:00
|
|
|
}
|
2018-01-09 22:14:40 -08:00
|
|
|
}
|
|
|
|
|
2019-09-06 04:37:22 -07:00
|
|
|
[[noreturn]] void logvisorAbort() {
|
2017-12-05 19:20:32 -08:00
|
|
|
#if !WINDOWS_STORE
|
2018-12-07 21:17:15 -08:00
|
|
|
unsigned int i;
|
|
|
|
void* stack[100];
|
|
|
|
unsigned short frames;
|
|
|
|
SYMBOL_INFO* symbol;
|
|
|
|
HANDLE process;
|
|
|
|
|
|
|
|
process = GetCurrentProcess();
|
2021-06-22 12:13:37 -07:00
|
|
|
SymInitialize(process, nullptr, TRUE);
|
|
|
|
frames = CaptureStackBackTrace(0, 100, stack, nullptr);
|
|
|
|
symbol = static_cast<SYMBOL_INFO*>(calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1));
|
2018-12-07 21:17:15 -08:00
|
|
|
symbol->MaxNameLen = 255;
|
|
|
|
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
|
|
|
|
|
|
|
for (i = 0; i < frames; i++) {
|
|
|
|
SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol);
|
|
|
|
|
Wide char homogeneity
If wide char stdlib functions are used on a narrow char stream, they attempt to narrow the character or string. If this fails, nothing is written to the stream and an error is indicated. The {fmt} library, at its core, uses stdlib functions for printing to a stream. However, it takes it a step further and acts upon errors while printing by throwing an exception.
If narrow char stdlib functions are used on a wide char stream, they throw an assertion. The {fmt} library somehow does not throw an assertion, but whatever it ends up printing is garbage anyhow.
So from either end, it is generally a bad idea to mix narrow and wide character printing. While you can get away with using wide char functions on a narrow char stream, the moment you start using anything other than English characters, it all falls apart. Just as well, narrow chars on wide char streams do not work at all. Despite this, I found many situations where wide char printing and narrow char printing was being used at the same time. This PR makes character printing homogeneous to avoid the previously discussed issues.
2021-05-07 22:52:03 -07:00
|
|
|
std::fwprintf(stderr, L"%i: %S - 0x%0llX", frames - i - 1, symbol->Name, symbol->Address);
|
2018-12-07 21:17:15 -08:00
|
|
|
|
|
|
|
DWORD dwDisplacement;
|
|
|
|
IMAGEHLP_LINE64 line;
|
|
|
|
SymSetOptions(SYMOPT_LOAD_LINES);
|
|
|
|
|
|
|
|
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
|
|
|
if (SymGetLineFromAddr64(process, (DWORD64)(stack[i]), &dwDisplacement, &line)) {
|
|
|
|
// SymGetLineFromAddr64 returned success
|
Wide char homogeneity
If wide char stdlib functions are used on a narrow char stream, they attempt to narrow the character or string. If this fails, nothing is written to the stream and an error is indicated. The {fmt} library, at its core, uses stdlib functions for printing to a stream. However, it takes it a step further and acts upon errors while printing by throwing an exception.
If narrow char stdlib functions are used on a wide char stream, they throw an assertion. The {fmt} library somehow does not throw an assertion, but whatever it ends up printing is garbage anyhow.
So from either end, it is generally a bad idea to mix narrow and wide character printing. While you can get away with using wide char functions on a narrow char stream, the moment you start using anything other than English characters, it all falls apart. Just as well, narrow chars on wide char streams do not work at all. Despite this, I found many situations where wide char printing and narrow char printing was being used at the same time. This PR makes character printing homogeneous to avoid the previously discussed issues.
2021-05-07 22:52:03 -07:00
|
|
|
std::fwprintf(stderr, L" LINE %d\n", int(line.LineNumber));
|
2018-12-07 21:17:15 -08:00
|
|
|
} else {
|
Wide char homogeneity
If wide char stdlib functions are used on a narrow char stream, they attempt to narrow the character or string. If this fails, nothing is written to the stream and an error is indicated. The {fmt} library, at its core, uses stdlib functions for printing to a stream. However, it takes it a step further and acts upon errors while printing by throwing an exception.
If narrow char stdlib functions are used on a wide char stream, they throw an assertion. The {fmt} library somehow does not throw an assertion, but whatever it ends up printing is garbage anyhow.
So from either end, it is generally a bad idea to mix narrow and wide character printing. While you can get away with using wide char functions on a narrow char stream, the moment you start using anything other than English characters, it all falls apart. Just as well, narrow chars on wide char streams do not work at all. Despite this, I found many situations where wide char printing and narrow char printing was being used at the same time. This PR makes character printing homogeneous to avoid the previously discussed issues.
2021-05-07 22:52:03 -07:00
|
|
|
std::fputwc('\n', stderr);
|
2016-09-07 23:13:49 -07:00
|
|
|
}
|
2018-12-07 21:17:15 -08:00
|
|
|
}
|
2016-09-07 23:13:49 -07:00
|
|
|
|
2019-08-26 07:18:21 -07:00
|
|
|
std::fflush(stderr);
|
|
|
|
std::free(symbol);
|
2016-09-07 23:13:49 -07:00
|
|
|
|
2017-12-05 19:20:32 -08:00
|
|
|
#endif
|
2018-01-09 22:14:40 -08:00
|
|
|
|
2018-12-07 21:17:15 -08:00
|
|
|
KillProcessTree();
|
2018-01-09 22:14:40 -08:00
|
|
|
|
2018-12-07 21:17:15 -08:00
|
|
|
// If you caught one of the above signals, it is likely you just
|
|
|
|
// want to quit your program right now.
|
2018-01-21 14:01:16 -08:00
|
|
|
#ifndef NDEBUG
|
2018-12-07 21:17:15 -08:00
|
|
|
signal(SIGABRT, SIG_DFL);
|
|
|
|
abort();
|
2018-01-21 14:01:16 -08:00
|
|
|
#else
|
2018-12-07 21:17:15 -08:00
|
|
|
exit(1);
|
2018-01-21 14:01:16 -08:00
|
|
|
#endif
|
2016-09-07 23:13:49 -07:00
|
|
|
}
|
2018-10-06 19:56:33 -07:00
|
|
|
|
|
|
|
#elif defined(__SWITCH__)
|
2020-05-03 23:09:21 -07:00
|
|
|
[[noreturn]] void logvisorAbort() {
|
|
|
|
MainLoggers.clear();
|
2020-05-04 21:14:41 -07:00
|
|
|
nvExit();
|
2020-05-03 23:09:21 -07:00
|
|
|
exit(1);
|
|
|
|
}
|
2022-08-03 15:11:46 -07:00
|
|
|
#elif defined(EMSCRIPTEN)
|
|
|
|
[[noreturn]] void logvisorAbort() {
|
|
|
|
abort();
|
|
|
|
}
|
2016-09-07 23:13:49 -07:00
|
|
|
#else
|
|
|
|
|
2018-01-09 22:14:40 -08:00
|
|
|
void KillProcessTree() {}
|
|
|
|
|
2016-09-07 23:13:49 -07:00
|
|
|
#include <execinfo.h>
|
2019-09-06 04:37:22 -07:00
|
|
|
[[noreturn]] void logvisorAbort() {
|
2018-12-07 21:17:15 -08:00
|
|
|
void* array[128];
|
|
|
|
size_t size = backtrace(array, 128);
|
2016-09-17 14:30:17 -07:00
|
|
|
|
2018-12-07 21:17:15 -08:00
|
|
|
constexpr size_t exeBufSize = 1024 + 1;
|
|
|
|
char exeNameBuffer[exeBufSize] = {};
|
2016-09-17 14:30:17 -07:00
|
|
|
|
|
|
|
#if __linux__
|
2018-12-07 21:17:15 -08:00
|
|
|
readlink("/proc/self/exe", exeNameBuffer, exeBufSize);
|
2016-09-17 14:30:17 -07:00
|
|
|
#endif
|
2016-09-07 23:13:49 -07:00
|
|
|
|
2016-09-17 14:30:17 -07:00
|
|
|
#if __APPLE__
|
2020-04-11 15:44:21 -07:00
|
|
|
std::string cmdLineStr = fmt::format(FMT_STRING("atos -p {}"), getpid());
|
2016-09-17 14:30:17 -07:00
|
|
|
#else
|
2020-04-11 15:44:21 -07:00
|
|
|
std::string cmdLineStr = fmt::format(FMT_STRING("2>/dev/null addr2line -C -f -e \"{}\""), exeNameBuffer);
|
2016-09-17 14:30:17 -07:00
|
|
|
#endif
|
2016-09-18 16:46:05 -07:00
|
|
|
|
2018-12-07 21:17:15 -08:00
|
|
|
for (size_t i = 0; i < size; i++) {
|
2017-11-02 02:20:23 -07:00
|
|
|
#if __linux__
|
2018-12-07 21:17:15 -08:00
|
|
|
Dl_info dlip;
|
|
|
|
if (dladdr(array[i], &dlip))
|
2020-04-11 15:44:21 -07:00
|
|
|
cmdLineStr += fmt::format(FMT_STRING(" 0x{:016X}"), (uintptr_t)((uint8_t*)array[i] - (uint8_t*)dlip.dli_fbase));
|
2018-12-07 21:17:15 -08:00
|
|
|
else
|
2020-04-11 15:44:21 -07:00
|
|
|
cmdLineStr += fmt::format(FMT_STRING(" 0x{:016X}"), (uintptr_t)array[i]);
|
2017-11-02 02:20:23 -07:00
|
|
|
#else
|
2020-04-11 15:44:21 -07:00
|
|
|
cmdLineStr += fmt::format(FMT_STRING(" 0x{:016X}"), (uintptr_t)array[i]);
|
2017-11-02 02:20:23 -07:00
|
|
|
#endif
|
2018-12-07 21:17:15 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
FILE* fp = popen(cmdLineStr.c_str(), "r");
|
|
|
|
if (fp) {
|
|
|
|
char readBuf[256];
|
|
|
|
size_t readSz;
|
|
|
|
while ((readSz = fread(readBuf, 1, 256, fp)))
|
2019-07-19 21:21:39 -07:00
|
|
|
std::fwrite(readBuf, 1, readSz, stderr);
|
2018-12-07 21:17:15 -08:00
|
|
|
pclose(fp);
|
|
|
|
} else {
|
|
|
|
for (size_t i = 0; i < size; i++) {
|
2019-07-19 21:21:39 -07:00
|
|
|
std::fputs("- ", stderr);
|
2018-12-07 21:17:15 -08:00
|
|
|
Dl_info dlip;
|
|
|
|
if (dladdr(array[i], &dlip)) {
|
|
|
|
int status;
|
|
|
|
char* demangledName = abi::__cxa_demangle(dlip.dli_sname, nullptr, nullptr, &status);
|
2019-07-19 21:21:39 -07:00
|
|
|
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));
|
|
|
|
std::free(demangledName);
|
2018-12-07 21:17:15 -08:00
|
|
|
} else {
|
2019-07-19 21:21:39 -07:00
|
|
|
std::fprintf(stderr, "%p\n", array[i]);
|
2018-12-07 21:17:15 -08:00
|
|
|
}
|
2016-09-18 16:46:05 -07:00
|
|
|
}
|
2018-12-07 21:17:15 -08:00
|
|
|
}
|
2016-09-18 16:46:05 -07:00
|
|
|
|
2019-07-19 21:21:39 -07:00
|
|
|
std::fflush(stderr);
|
|
|
|
std::fflush(stdout);
|
2018-12-07 21:17:15 -08:00
|
|
|
KillProcessTree();
|
2018-01-13 22:06:42 -08:00
|
|
|
|
|
|
|
#ifndef NDEBUG
|
2018-12-07 21:17:15 -08:00
|
|
|
signal(SIGABRT, SIG_DFL);
|
|
|
|
abort();
|
2018-01-13 22:06:42 -08:00
|
|
|
#else
|
2018-12-07 21:17:15 -08:00
|
|
|
exit(1);
|
2018-01-13 22:06:42 -08:00
|
|
|
#endif
|
2016-09-07 23:13:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2017-01-18 12:54:00 -08:00
|
|
|
LogMutex _LogMutex;
|
|
|
|
|
Wide char homogeneity
If wide char stdlib functions are used on a narrow char stream, they attempt to narrow the character or string. If this fails, nothing is written to the stream and an error is indicated. The {fmt} library, at its core, uses stdlib functions for printing to a stream. However, it takes it a step further and acts upon errors while printing by throwing an exception.
If narrow char stdlib functions are used on a wide char stream, they throw an assertion. The {fmt} library somehow does not throw an assertion, but whatever it ends up printing is garbage anyhow.
So from either end, it is generally a bad idea to mix narrow and wide character printing. While you can get away with using wide char functions on a narrow char stream, the moment you start using anything other than English characters, it all falls apart. Just as well, narrow chars on wide char streams do not work at all. Despite this, I found many situations where wide char printing and narrow char printing was being used at the same time. This PR makes character printing homogeneous to avoid the previously discussed issues.
2021-05-07 22:52:03 -07:00
|
|
|
static void AbortHandler(int signum) {
|
|
|
|
_LogMutex.enabled = false;
|
|
|
|
switch (signum) {
|
|
|
|
case SIGSEGV:
|
|
|
|
Log.report(logvisor::Fatal, FMT_STRING("Segmentation Fault"));
|
|
|
|
break;
|
|
|
|
case SIGILL:
|
|
|
|
Log.report(logvisor::Fatal, FMT_STRING("Bad Execution"));
|
|
|
|
break;
|
|
|
|
case SIGFPE:
|
|
|
|
Log.report(logvisor::Fatal, FMT_STRING("Floating Point Exception"));
|
|
|
|
break;
|
|
|
|
case SIGABRT:
|
|
|
|
Log.report(logvisor::Fatal, FMT_STRING("Abort Signal"));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
Log.report(logvisor::Fatal, FMT_STRING("unknown signal {}"), signum);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-09-07 23:13:49 -07:00
|
|
|
|
2018-03-23 14:38:33 -07:00
|
|
|
uint64_t _LogCounter;
|
|
|
|
|
2015-07-03 15:08:41 -07:00
|
|
|
std::vector<std::unique_ptr<ILogger>> MainLoggers;
|
2015-07-25 19:41:37 -07:00
|
|
|
std::atomic_size_t ErrorCount(0);
|
2021-06-22 12:13:37 -07:00
|
|
|
using MonoClock = std::chrono::steady_clock;
|
|
|
|
static MonoClock::time_point GlobalStart = MonoClock::now();
|
|
|
|
static inline MonoClock::duration CurrentUptime() { return MonoClock::now() - GlobalStart; }
|
2015-07-26 13:55:17 -07:00
|
|
|
std::atomic_uint_fast64_t FrameIndex(0);
|
2016-03-04 19:21:18 -08:00
|
|
|
|
2018-12-07 21:17:15 -08:00
|
|
|
static inline int ConsoleWidth() {
|
|
|
|
int retval = 80;
|
2015-09-02 14:58:37 -07:00
|
|
|
#if _WIN32
|
2017-12-05 19:20:32 -08:00
|
|
|
#if !WINDOWS_STORE
|
2018-12-07 21:17:15 -08:00
|
|
|
CONSOLE_SCREEN_BUFFER_INFO info;
|
|
|
|
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info);
|
|
|
|
retval = info.dwSize.X - 1;
|
2017-12-05 19:20:32 -08:00
|
|
|
#endif
|
2018-10-06 19:56:33 -07:00
|
|
|
#elif defined(__SWITCH__)
|
2018-12-07 21:17:15 -08:00
|
|
|
return 80;
|
2015-09-02 14:58:37 -07:00
|
|
|
#else
|
2018-12-07 21:17:15 -08:00
|
|
|
struct winsize w;
|
|
|
|
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1)
|
|
|
|
retval = w.ws_col;
|
2015-09-02 14:58:37 -07:00
|
|
|
#endif
|
2018-12-07 21:17:15 -08:00
|
|
|
if (retval < 10)
|
|
|
|
return 10;
|
|
|
|
return retval;
|
2015-09-02 14:58:37 -07:00
|
|
|
}
|
2015-07-03 15:08:41 -07:00
|
|
|
|
2020-05-04 21:14:41 -07:00
|
|
|
#if LOGVISOR_NX_LM
|
2020-05-03 23:09:21 -07: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 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);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
2015-07-03 15:08:41 -07:00
|
|
|
#if _WIN32
|
|
|
|
static HANDLE Term = 0;
|
|
|
|
#else
|
|
|
|
static const char* Term = nullptr;
|
|
|
|
#endif
|
2015-10-07 16:18:37 -07:00
|
|
|
bool XtermColor = false;
|
2018-12-07 21:17:15 -08:00
|
|
|
struct ConsoleLogger : public ILogger {
|
2020-12-31 13:53:26 -08:00
|
|
|
ConsoleLogger() : ILogger(log_typeid(ConsoleLogger)) {
|
2015-07-03 15:08:41 -07:00
|
|
|
#if _WIN32
|
2017-12-05 19:20:32 -08:00
|
|
|
#if !WINDOWS_STORE
|
2018-12-07 21:17:15 -08:00
|
|
|
const char* conemuANSI = getenv("ConEmuANSI");
|
|
|
|
if (conemuANSI && !strcmp(conemuANSI, "ON"))
|
|
|
|
XtermColor = true;
|
2017-12-05 19:20:32 -08:00
|
|
|
#endif
|
2018-12-07 21:17:15 -08:00
|
|
|
if (!Term)
|
|
|
|
Term = GetStdHandle(STD_ERROR_HANDLE);
|
2015-07-03 15:08:41 -07:00
|
|
|
#else
|
2018-12-07 21:17:15 -08:00
|
|
|
if (!Term) {
|
|
|
|
Term = getenv("TERM");
|
|
|
|
if (Term && !strncmp(Term, "xterm", 5)) {
|
|
|
|
XtermColor = true;
|
|
|
|
putenv((char*)"TERM=xterm-16color");
|
|
|
|
}
|
2015-07-03 15:08:41 -07:00
|
|
|
}
|
2018-12-07 21:17:15 -08:00
|
|
|
#endif
|
|
|
|
}
|
2020-12-31 13:53:26 -08:00
|
|
|
~ConsoleLogger() override = default;
|
2018-12-07 21:17:15 -08:00
|
|
|
|
|
|
|
static void _reportHead(const char* modName, const char* sourceInfo, Level severity) {
|
|
|
|
/* Clear current line out */
|
2021-06-22 12:13:37 -07:00
|
|
|
// std::fprintf(stderr, "\r%*c\r", ConsoleWidth(), ' ');
|
2019-08-26 07:18:21 -07: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 21:17:15 -08:00
|
|
|
const char* thrName = nullptr;
|
2019-10-01 00:20:45 -07:00
|
|
|
if (ThreadMap.find(thrId) != ThreadMap.end())
|
2018-12-07 21:17:15 -08:00
|
|
|
thrName = ThreadMap[thrId];
|
|
|
|
|
|
|
|
if (XtermColor) {
|
2019-07-19 21:21:39 -07:00
|
|
|
std::fputs(BOLD "[", stderr);
|
2020-04-11 15:44:21 -07:00
|
|
|
fmt::print(stderr, FMT_STRING(GREEN "{:.4f} "), tmd);
|
2019-08-26 07:18:21 -07:00
|
|
|
const uint_fast64_t fIdx = FrameIndex.load();
|
2019-10-01 00:20:45 -07:00
|
|
|
if (fIdx != 0)
|
2020-04-11 15:44:21 -07:00
|
|
|
fmt::print(stderr, FMT_STRING("({}) "), fIdx);
|
2018-12-07 21:17:15 -08:00
|
|
|
switch (severity) {
|
|
|
|
case Info:
|
2019-07-19 21:21:39 -07:00
|
|
|
std::fputs(BOLD CYAN "INFO", stderr);
|
2018-12-07 21:17:15 -08:00
|
|
|
break;
|
|
|
|
case Warning:
|
2019-07-19 21:21:39 -07:00
|
|
|
std::fputs(BOLD YELLOW "WARNING", stderr);
|
2018-12-07 21:17:15 -08:00
|
|
|
break;
|
|
|
|
case Error:
|
2019-07-19 21:21:39 -07:00
|
|
|
std::fputs(RED BOLD "ERROR", stderr);
|
2018-12-07 21:17:15 -08:00
|
|
|
break;
|
|
|
|
case Fatal:
|
2019-07-19 21:21:39 -07:00
|
|
|
std::fputs(BOLD RED "FATAL ERROR", stderr);
|
2018-12-07 21:17:15 -08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
};
|
2020-04-11 15:44:21 -07:00
|
|
|
fmt::print(stderr, FMT_STRING(NORMAL BOLD " {}"), modName);
|
2019-10-01 00:20:45 -07:00
|
|
|
if (sourceInfo)
|
2020-04-11 15:44:21 -07:00
|
|
|
fmt::print(stderr, FMT_STRING(BOLD YELLOW " {{}}"), sourceInfo);
|
2019-10-01 00:20:45 -07:00
|
|
|
if (thrName)
|
2020-04-11 15:44:21 -07:00
|
|
|
fmt::print(stderr, FMT_STRING(BOLD MAGENTA " ({})"), thrName);
|
2019-07-19 21:21:39 -07:00
|
|
|
std::fputs(NORMAL BOLD "] " NORMAL, stderr);
|
2018-12-07 21:17:15 -08:00
|
|
|
} else {
|
2015-09-02 14:58:37 -07:00
|
|
|
#if _WIN32
|
2017-12-05 19:20:32 -08:00
|
|
|
#if !WINDOWS_STORE
|
2018-12-07 21:17:15 -08:00
|
|
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
|
2019-08-26 07:18:21 -07:00
|
|
|
std::fputc('[', stderr);
|
2018-12-07 21:17:15 -08:00
|
|
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_GREEN);
|
2020-04-11 15:44:21 -07:00
|
|
|
fmt::print(stderr, FMT_STRING("{:.4f} "), tmd);
|
2019-08-26 07:18:21 -07:00
|
|
|
const uint64_t fi = FrameIndex.load();
|
2019-10-01 00:20:45 -07:00
|
|
|
if (fi != 0)
|
2019-08-26 07:18:21 -07:00
|
|
|
std::fprintf(stderr, "(%" PRIu64 ") ", fi);
|
2018-12-07 21:17:15 -08:00
|
|
|
switch (severity) {
|
|
|
|
case Info:
|
|
|
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_BLUE);
|
2019-08-26 07:18:21 -07:00
|
|
|
std::fputs("INFO", stderr);
|
2018-12-07 21:17:15 -08:00
|
|
|
break;
|
|
|
|
case Warning:
|
|
|
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN);
|
2019-08-26 07:18:21 -07:00
|
|
|
std::fputs("WARNING", stderr);
|
2018-12-07 21:17:15 -08:00
|
|
|
break;
|
|
|
|
case Error:
|
|
|
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED);
|
2019-08-26 07:18:21 -07:00
|
|
|
std::fputs("ERROR", stderr);
|
2018-12-07 21:17:15 -08:00
|
|
|
break;
|
|
|
|
case Fatal:
|
|
|
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED);
|
2019-08-26 07:18:21 -07:00
|
|
|
std::fputs("FATAL ERROR", stderr);
|
2018-12-07 21:17:15 -08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2019-08-26 07:18:21 -07:00
|
|
|
}
|
2018-12-07 21:17:15 -08:00
|
|
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
|
2020-04-11 15:44:21 -07:00
|
|
|
fmt::print(stderr, FMT_STRING(" {}"), modName);
|
2018-12-07 21:17:15 -08:00
|
|
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN);
|
2019-10-01 00:20:45 -07:00
|
|
|
if (sourceInfo)
|
2020-04-11 15:44:21 -07:00
|
|
|
fmt::print(stderr, FMT_STRING(" {{}}"), sourceInfo);
|
2018-12-07 21:17:15 -08:00
|
|
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE);
|
2019-10-01 00:20:45 -07:00
|
|
|
if (thrName)
|
2020-04-11 15:44:21 -07:00
|
|
|
fmt::print(stderr, FMT_STRING(" ({})"), thrName);
|
2018-12-07 21:17:15 -08:00
|
|
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
|
2019-08-26 07:18:21 -07:00
|
|
|
std::fputs("] ", stderr);
|
2018-12-07 21:17:15 -08:00
|
|
|
SetConsoleTextAttribute(Term, FOREGROUND_WHITE);
|
2017-12-05 19:20:32 -08:00
|
|
|
#endif
|
2015-09-02 14:58:37 -07:00
|
|
|
#else
|
2019-08-26 07:18:21 -07:00
|
|
|
std::fputc('[', stderr);
|
2020-04-11 15:44:21 -07:00
|
|
|
fmt::print(stderr, FMT_STRING("{:.4f} "), tmd);
|
2018-12-07 21:17:15 -08:00
|
|
|
uint_fast64_t fIdx = FrameIndex.load();
|
|
|
|
if (fIdx)
|
2020-04-11 15:44:21 -07:00
|
|
|
fmt::print(stderr, FMT_STRING("({}) "), fIdx);
|
2018-12-07 21:17:15 -08:00
|
|
|
switch (severity) {
|
|
|
|
case Info:
|
2019-07-19 21:21:39 -07:00
|
|
|
std::fputs("INFO", stderr);
|
2018-12-07 21:17:15 -08:00
|
|
|
break;
|
|
|
|
case Warning:
|
2019-07-19 21:21:39 -07:00
|
|
|
std::fputs("WARNING", stderr);
|
2018-12-07 21:17:15 -08:00
|
|
|
break;
|
|
|
|
case Error:
|
2019-07-19 21:21:39 -07:00
|
|
|
std::fputs("ERROR", stderr);
|
2018-12-07 21:17:15 -08:00
|
|
|
break;
|
|
|
|
case Fatal:
|
2019-07-19 21:21:39 -07:00
|
|
|
std::fputs("FATAL ERROR", stderr);
|
2018-12-07 21:17:15 -08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2019-08-26 07:18:21 -07:00
|
|
|
}
|
2020-04-11 15:44:21 -07:00
|
|
|
fmt::print(stderr, FMT_STRING(" {}"), modName);
|
2019-10-01 00:20:45 -07:00
|
|
|
if (sourceInfo)
|
2020-04-11 15:44:21 -07:00
|
|
|
fmt::print(stderr, FMT_STRING(" {{}}"), sourceInfo);
|
2019-10-01 00:20:45 -07:00
|
|
|
if (thrName)
|
2020-04-11 15:44:21 -07:00
|
|
|
fmt::print(stderr, FMT_STRING(" ({})"), thrName);
|
2019-07-19 21:21:39 -07:00
|
|
|
std::fputs("] ", stderr);
|
2015-07-03 15:08:41 -07:00
|
|
|
#endif
|
2015-07-03 23:41:44 -07:00
|
|
|
}
|
2018-12-07 21:17:15 -08:00
|
|
|
}
|
2015-07-03 23:41:44 -07:00
|
|
|
|
2019-08-26 07:34:40 -07:00
|
|
|
void report(const char* modName, Level severity, fmt::string_view format, fmt::format_args args) override {
|
2018-12-07 21:17:15 -08:00
|
|
|
_reportHead(modName, nullptr, severity);
|
2019-07-19 21:21:39 -07:00
|
|
|
fmt::vprint(stderr, format, args);
|
2019-08-26 07:18:21 -07:00
|
|
|
std::fputc('\n', stderr);
|
2019-07-19 21:21:39 -07:00
|
|
|
std::fflush(stderr);
|
2018-12-07 21:17:15 -08:00
|
|
|
}
|
2015-07-03 23:41:44 -07:00
|
|
|
|
2019-08-26 07:34:40 -07: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 15:44:21 -07:00
|
|
|
_reportHead(modName, fmt::format(FMT_STRING("{}:{}"), file, linenum).c_str(), severity);
|
2019-07-19 21:21:39 -07:00
|
|
|
fmt::vprint(stderr, format, args);
|
2019-08-26 07:18:21 -07:00
|
|
|
std::fputc('\n', stderr);
|
2019-07-19 21:21:39 -07:00
|
|
|
std::fflush(stderr);
|
2018-12-07 21:17:15 -08:00
|
|
|
}
|
2015-07-03 15:08:41 -07:00
|
|
|
};
|
2020-05-03 23:09:21 -07:00
|
|
|
#endif
|
2015-07-03 15:08:41 -07:00
|
|
|
|
2019-10-01 00:20:45 -07:00
|
|
|
static bool ConsoleLoggerRegistered = false;
|
|
|
|
|
2018-12-07 21:17:15 -08:00
|
|
|
void RegisterConsoleLogger() {
|
|
|
|
/* Otherwise construct new console logger */
|
2019-10-01 00:20:45 -07:00
|
|
|
if (!ConsoleLoggerRegistered) {
|
|
|
|
MainLoggers.emplace_back(new ConsoleLogger);
|
|
|
|
ConsoleLoggerRegistered = true;
|
2021-06-22 12:13:37 -07:00
|
|
|
#if _WIN32
|
|
|
|
#if 0
|
|
|
|
if (GetACP() != CP_UTF8) {
|
|
|
|
Log.report(Fatal, FMT_STRING("UTF-8 codepage not active! (Windows 10 1903+ required)"));
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
SetConsoleOutputCP(CP_UTF8);
|
|
|
|
#endif
|
|
|
|
#endif
|
2019-10-01 00:20:45 -07:00
|
|
|
}
|
2015-07-03 15:08:41 -07:00
|
|
|
}
|
|
|
|
|
2015-11-04 16:02:40 -08:00
|
|
|
#if _WIN32
|
2018-12-07 21:17:15 -08:00
|
|
|
void CreateWin32Console() {
|
2017-12-05 19:20:32 -08:00
|
|
|
#if !WINDOWS_STORE
|
2018-12-07 21:17:15 -08:00
|
|
|
/* Debug console */
|
|
|
|
AllocConsole();
|
2015-11-04 16:02:40 -08:00
|
|
|
|
2019-08-26 07:18:21 -07:00
|
|
|
std::freopen("CONIN$", "r", stdin);
|
|
|
|
std::freopen("CONOUT$", "w", stdout);
|
|
|
|
std::freopen("CONOUT$", "w", stderr);
|
2017-12-05 19:20:32 -08:00
|
|
|
#endif
|
2015-11-04 16:02:40 -08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-12-07 21:17:15 -08:00
|
|
|
void RegisterStandardExceptions() {
|
|
|
|
signal(SIGABRT, AbortHandler);
|
|
|
|
signal(SIGSEGV, AbortHandler);
|
|
|
|
signal(SIGILL, AbortHandler);
|
|
|
|
signal(SIGFPE, AbortHandler);
|
2016-09-07 23:13:49 -07:00
|
|
|
}
|
|
|
|
|
2021-04-04 15:20:48 -07:00
|
|
|
#if SENTRY_ENABLED
|
|
|
|
void RegisterSentry(const char* appName, const char* appVersion, const char* cacheDir) {
|
|
|
|
sentry_options_t *options = sentry_options_new();
|
|
|
|
sentry_options_set_database_path(options, cacheDir);
|
|
|
|
sentry_options_set_auto_session_tracking(options, true);
|
|
|
|
sentry_options_set_symbolize_stacktraces(options, true);
|
|
|
|
sentry_options_set_dsn(options, SENTRY_DSN);
|
|
|
|
|
|
|
|
#ifdef NDEBUG
|
|
|
|
sentry_options_set_environment(options, "release");
|
|
|
|
#else
|
|
|
|
sentry_options_set_environment(options, "debug");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
std::string release = fmt::format(FMT_STRING("{}@{}"), appName, appVersion);
|
|
|
|
sentry_options_set_release(options, release.c_str());
|
|
|
|
|
|
|
|
sentry_init(options);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-12-07 21:17:15 -08:00
|
|
|
struct FileLogger : public ILogger {
|
2021-04-03 09:42:44 -07:00
|
|
|
FILE* fp = nullptr;
|
2020-12-31 13:53:26 -08:00
|
|
|
FileLogger(uint64_t typeHash) : ILogger(typeHash) {}
|
2018-12-07 21:17:15 -08:00
|
|
|
virtual void openFile() = 0;
|
2021-04-03 09:42:44 -07:00
|
|
|
void openFileIfNeeded() {
|
|
|
|
if (!fp) {
|
|
|
|
openFile();
|
|
|
|
}
|
|
|
|
}
|
2021-06-22 12:13:37 -07:00
|
|
|
virtual void closeFile() {
|
2021-04-03 09:42:44 -07:00
|
|
|
if (fp) {
|
2022-01-10 23:27:06 -08:00
|
|
|
std::fflush(fp);
|
2021-04-03 09:42:44 -07:00
|
|
|
std::fclose(fp);
|
|
|
|
fp = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
virtual ~FileLogger() { closeFile(); }
|
2018-12-07 21:17:15 -08:00
|
|
|
|
|
|
|
void _reportHead(const char* modName, const char* sourceInfo, Level severity) {
|
2019-08-26 07:18:21 -07: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 21:17:15 -08:00
|
|
|
const char* thrName = nullptr;
|
2019-08-26 07:18:21 -07:00
|
|
|
if (ThreadMap.find(thrId) != ThreadMap.end()) {
|
2018-12-07 21:17:15 -08:00
|
|
|
thrName = ThreadMap[thrId];
|
2019-08-26 07:18:21 -07:00
|
|
|
}
|
2018-12-07 21:17:15 -08:00
|
|
|
|
2019-08-26 07:18:21 -07:00
|
|
|
std::fputc('[', fp);
|
2019-07-19 21:21:39 -07:00
|
|
|
std::fprintf(fp, "%5.4f ", tmd);
|
2019-08-26 07:18:21 -07:00
|
|
|
const uint_fast64_t fIdx = FrameIndex.load();
|
|
|
|
if (fIdx != 0) {
|
2019-07-19 21:21:39 -07:00
|
|
|
std::fprintf(fp, "(%" PRIu64 ") ", fIdx);
|
2019-08-26 07:18:21 -07:00
|
|
|
}
|
2018-12-07 21:17:15 -08:00
|
|
|
switch (severity) {
|
|
|
|
case Info:
|
2019-07-19 21:21:39 -07:00
|
|
|
std::fputs("INFO", fp);
|
2018-12-07 21:17:15 -08:00
|
|
|
break;
|
|
|
|
case Warning:
|
2019-07-19 21:21:39 -07:00
|
|
|
std::fputs("WARNING", fp);
|
2018-12-07 21:17:15 -08:00
|
|
|
break;
|
|
|
|
case Error:
|
2019-07-19 21:21:39 -07:00
|
|
|
std::fputs("ERROR", fp);
|
2018-12-07 21:17:15 -08:00
|
|
|
break;
|
|
|
|
case Fatal:
|
2019-07-19 21:21:39 -07:00
|
|
|
std::fputs("FATAL ERROR", fp);
|
2018-12-07 21:17:15 -08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
};
|
2019-07-19 21:21:39 -07:00
|
|
|
std::fprintf(fp, " %s", modName);
|
2019-08-26 07:18:21 -07:00
|
|
|
if (sourceInfo) {
|
2019-07-19 21:21:39 -07:00
|
|
|
std::fprintf(fp, " {%s}", sourceInfo);
|
2019-08-26 07:18:21 -07:00
|
|
|
}
|
|
|
|
if (thrName) {
|
2019-07-19 21:21:39 -07:00
|
|
|
std::fprintf(fp, " (%s)", thrName);
|
2019-08-26 07:18:21 -07:00
|
|
|
}
|
2019-07-19 21:21:39 -07:00
|
|
|
std::fputs("] ", fp);
|
2018-12-07 21:17:15 -08:00
|
|
|
}
|
|
|
|
|
2019-08-26 07:34:40 -07:00
|
|
|
void report(const char* modName, Level severity, fmt::string_view format, fmt::format_args args) override {
|
2021-04-03 09:42:44 -07:00
|
|
|
openFileIfNeeded();
|
2018-12-07 21:17:15 -08:00
|
|
|
_reportHead(modName, nullptr, severity);
|
2019-07-19 21:21:39 -07:00
|
|
|
fmt::vprint(fp, format, args);
|
2019-08-26 07:18:21 -07:00
|
|
|
std::fputc('\n', fp);
|
2018-12-07 21:17:15 -08:00
|
|
|
}
|
|
|
|
|
2019-08-26 07:34:40 -07:00
|
|
|
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::string_view format,
|
|
|
|
fmt::format_args args) override {
|
2021-04-03 09:42:44 -07:00
|
|
|
openFileIfNeeded();
|
2020-04-11 15:44:21 -07:00
|
|
|
_reportHead(modName, fmt::format(FMT_STRING("{}:{}"), file, linenum).c_str(), severity);
|
2019-07-19 21:21:39 -07:00
|
|
|
fmt::vprint(fp, format, args);
|
2019-08-26 07:18:21 -07:00
|
|
|
std::fputc('\n', fp);
|
2018-12-07 21:17:15 -08:00
|
|
|
}
|
2015-07-03 15:08:41 -07:00
|
|
|
};
|
|
|
|
|
2018-12-07 21:17:15 -08:00
|
|
|
struct FileLogger8 : public FileLogger {
|
|
|
|
const char* m_filepath;
|
2021-06-22 12:13:37 -07:00
|
|
|
explicit FileLogger8(const char* filepath) : FileLogger(log_typeid(FileLogger8)), m_filepath(filepath) {}
|
2019-08-26 07:34:40 -07:00
|
|
|
void openFile() override { fp = std::fopen(m_filepath, "a"); }
|
2020-12-31 13:53:26 -08:00
|
|
|
~FileLogger8() override = default;
|
2015-07-03 15:08:41 -07:00
|
|
|
};
|
|
|
|
|
2018-12-07 21:17:15 -08:00
|
|
|
void RegisterFileLogger(const char* filepath) {
|
|
|
|
/* Otherwise construct new file logger */
|
|
|
|
MainLoggers.emplace_back(new FileLogger8(filepath));
|
2015-07-03 15:08:41 -07:00
|
|
|
}
|
|
|
|
|
2018-12-07 21:17:15 -08:00
|
|
|
} // namespace logvisor
|