mirror of https://github.com/AxioDL/logvisor.git
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.
This commit is contained in:
parent
7901fc496d
commit
f23179e168
233
lib/logvisor.cpp
233
lib/logvisor.cpp
|
@ -32,6 +32,7 @@
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
|
#include <locale>
|
||||||
#include "logvisor/logvisor.hpp"
|
#include "logvisor/logvisor.hpp"
|
||||||
|
|
||||||
#if SENTRY_ENABLED
|
#if SENTRY_ENABLED
|
||||||
|
@ -47,6 +48,14 @@
|
||||||
#define BOLD "\x1b[1m"
|
#define BOLD "\x1b[1m"
|
||||||
#define NORMAL "\x1b[0m"
|
#define NORMAL "\x1b[0m"
|
||||||
|
|
||||||
|
#define W_RED L"\x001b[1;31m"
|
||||||
|
#define W_YELLOW L"\x001b[1;33m"
|
||||||
|
#define W_GREEN L"\x001b[1;32m"
|
||||||
|
#define W_MAGENTA L"\x001b[1;35m"
|
||||||
|
#define W_CYAN L"\x001b[1;36m"
|
||||||
|
#define W_BOLD L"\x001b[1m"
|
||||||
|
#define W_NORMAL L"\x001b[0m"
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
#define FOREGROUND_WHITE FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
|
#define FOREGROUND_WHITE FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
|
||||||
#endif
|
#endif
|
||||||
|
@ -62,6 +71,13 @@ static Module Log("logvisor");
|
||||||
|
|
||||||
static std::unordered_map<std::thread::id, const char*> ThreadMap;
|
static std::unordered_map<std::thread::id, const char*> ThreadMap;
|
||||||
|
|
||||||
|
static std::wstring Widen(std::string str) {
|
||||||
|
std::wstring wide(str.size(), L'\0');
|
||||||
|
std::locale loc;
|
||||||
|
std::use_facet<std::ctype<wchar_t>>(loc).widen(str.data(), str.data()+str.size(), wide.data());
|
||||||
|
return wide;
|
||||||
|
}
|
||||||
|
|
||||||
static void AddThreadToMap(const char* name) {
|
static void AddThreadToMap(const char* name) {
|
||||||
auto lk = LockLog();
|
auto lk = LockLog();
|
||||||
ThreadMap[std::this_thread::get_id()] = name;
|
ThreadMap[std::this_thread::get_id()] = name;
|
||||||
|
@ -140,7 +156,7 @@ void KillProcessTree() {
|
||||||
for (i = 0; i < frames; i++) {
|
for (i = 0; i < frames; i++) {
|
||||||
SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol);
|
SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol);
|
||||||
|
|
||||||
std::fprintf(stderr, "%i: %s - 0x%0llX", frames - i - 1, symbol->Name, symbol->Address);
|
std::fwprintf(stderr, L"%i: %S - 0x%0llX", frames - i - 1, symbol->Name, symbol->Address);
|
||||||
|
|
||||||
DWORD dwDisplacement;
|
DWORD dwDisplacement;
|
||||||
IMAGEHLP_LINE64 line;
|
IMAGEHLP_LINE64 line;
|
||||||
|
@ -149,9 +165,9 @@ void KillProcessTree() {
|
||||||
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
||||||
if (SymGetLineFromAddr64(process, (DWORD64)(stack[i]), &dwDisplacement, &line)) {
|
if (SymGetLineFromAddr64(process, (DWORD64)(stack[i]), &dwDisplacement, &line)) {
|
||||||
// SymGetLineFromAddr64 returned success
|
// SymGetLineFromAddr64 returned success
|
||||||
std::fprintf(stderr, " LINE %d\n", int(line.LineNumber));
|
std::fwprintf(stderr, L" LINE %d\n", int(line.LineNumber));
|
||||||
} else {
|
} else {
|
||||||
std::fputc('\n', stderr);
|
std::fputwc('\n', stderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,26 +267,49 @@ void KillProcessTree() {}
|
||||||
|
|
||||||
LogMutex _LogMutex;
|
LogMutex _LogMutex;
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
static void AbortHandler(int signum) {
|
static void AbortHandler(int signum) {
|
||||||
_LogMutex.enabled = false;
|
_LogMutex.enabled = false;
|
||||||
switch (signum) {
|
switch (signum) {
|
||||||
case SIGSEGV:
|
case SIGSEGV:
|
||||||
Log.report(logvisor::Fatal, FMT_STRING("Segmentation Fault"));
|
Log.report(logvisor::Fatal, FMT_STRING(L"Segmentation Fault"));
|
||||||
break;
|
break;
|
||||||
case SIGILL:
|
case SIGILL:
|
||||||
Log.report(logvisor::Fatal, FMT_STRING("Bad Execution"));
|
Log.report(logvisor::Fatal, FMT_STRING(L"Bad Execution"));
|
||||||
break;
|
break;
|
||||||
case SIGFPE:
|
case SIGFPE:
|
||||||
Log.report(logvisor::Fatal, FMT_STRING("Floating Point Exception"));
|
Log.report(logvisor::Fatal, FMT_STRING(L"Floating Point Exception"));
|
||||||
break;
|
break;
|
||||||
case SIGABRT:
|
case SIGABRT:
|
||||||
Log.report(logvisor::Fatal, FMT_STRING("Abort Signal"));
|
Log.report(logvisor::Fatal, FMT_STRING(L"Abort Signal"));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Log.report(logvisor::Fatal, FMT_STRING("unknown signal {}"), signum);
|
Log.report(logvisor::Fatal, FMT_STRING(L"unknown signal {}"), signum);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
uint64_t _LogCounter;
|
uint64_t _LogCounter;
|
||||||
|
|
||||||
|
@ -563,11 +602,7 @@ struct ConsoleLogger : public ILogger {
|
||||||
|
|
||||||
static void _reportHead(const char* modName, const char* sourceInfo, Level severity) {
|
static void _reportHead(const char* modName, const char* sourceInfo, Level severity) {
|
||||||
/* Clear current line out */
|
/* Clear current line out */
|
||||||
const int width = ConsoleWidth();
|
std::fprintf(stderr, "\r%*c\r", ConsoleWidth(), ' ');
|
||||||
std::fputc('\r', stderr);
|
|
||||||
for (int w = 0; w < width; ++w)
|
|
||||||
std::fputc(' ', stderr);
|
|
||||||
std::fputc('\r', stderr);
|
|
||||||
|
|
||||||
const std::chrono::steady_clock::duration tm = CurrentUptime();
|
const std::chrono::steady_clock::duration tm = CurrentUptime();
|
||||||
const double tmd = tm.count() * std::chrono::steady_clock::duration::period::num /
|
const double tmd = tm.count() * std::chrono::steady_clock::duration::period::num /
|
||||||
|
@ -679,6 +714,120 @@ struct ConsoleLogger : public ILogger {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _reportHead(const wchar_t* modName, const wchar_t* sourceInfo, Level severity) {
|
||||||
|
/* Clear current line out */
|
||||||
|
std::fwprintf(stderr, L"\r%*c\r", ConsoleWidth(), L' ');
|
||||||
|
|
||||||
|
const std::chrono::steady_clock::duration tm = CurrentUptime();
|
||||||
|
const double tmd = tm.count() * std::chrono::steady_clock::duration::period::num /
|
||||||
|
static_cast<double>(std::chrono::steady_clock::duration::period::den);
|
||||||
|
const std::thread::id thrId = std::this_thread::get_id();
|
||||||
|
const wchar_t* thrName = nullptr;
|
||||||
|
if (ThreadMap.find(thrId) != ThreadMap.end())
|
||||||
|
thrName = Widen(ThreadMap[thrId]).c_str();
|
||||||
|
|
||||||
|
if (XtermColor) {
|
||||||
|
std::fputws(W_BOLD L"[", stderr);
|
||||||
|
fmt::print(stderr, FMT_STRING(W_GREEN L"{:.4f} "), tmd);
|
||||||
|
const uint_fast64_t fIdx = FrameIndex.load();
|
||||||
|
if (fIdx != 0)
|
||||||
|
fmt::print(stderr, FMT_STRING(L"({}) "), fIdx);
|
||||||
|
switch (severity) {
|
||||||
|
case Info:
|
||||||
|
std::fputws(W_BOLD W_CYAN L"INFO", stderr);
|
||||||
|
break;
|
||||||
|
case Warning:
|
||||||
|
std::fputws(W_BOLD W_YELLOW L"WARNING", stderr);
|
||||||
|
break;
|
||||||
|
case Error:
|
||||||
|
std::fputws(W_RED W_BOLD L"ERROR", stderr);
|
||||||
|
break;
|
||||||
|
case Fatal:
|
||||||
|
std::fputws(W_BOLD W_RED L"FATAL ERROR", stderr);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
fmt::print(stderr, FMT_STRING(W_NORMAL W_BOLD L" {}"), modName);
|
||||||
|
if (sourceInfo)
|
||||||
|
fmt::print(stderr, FMT_STRING(W_BOLD W_YELLOW L" {{}}"), sourceInfo);
|
||||||
|
if (thrName)
|
||||||
|
fmt::print(stderr, FMT_STRING(W_BOLD W_MAGENTA L" ({})"), thrName);
|
||||||
|
std::fputws(W_NORMAL W_BOLD L"] " W_NORMAL, stderr);
|
||||||
|
} else {
|
||||||
|
#if _WIN32
|
||||||
|
#if !WINDOWS_STORE
|
||||||
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
|
||||||
|
std::fputwc(L'[', stderr);
|
||||||
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_GREEN);
|
||||||
|
fmt::print(stderr, FMT_STRING(L"{:.4f} "), tmd);
|
||||||
|
const uint64_t fi = FrameIndex.load();
|
||||||
|
if (fi != 0)
|
||||||
|
std::fwprintf(stderr, L"(%" PRIu64 L") ", fi);
|
||||||
|
switch (severity) {
|
||||||
|
case Info:
|
||||||
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_BLUE);
|
||||||
|
std::fputws(L"INFO", stderr);
|
||||||
|
break;
|
||||||
|
case Warning:
|
||||||
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN);
|
||||||
|
std::fputws(L"WARNING", stderr);
|
||||||
|
break;
|
||||||
|
case Error:
|
||||||
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED);
|
||||||
|
std::fputws(L"ERROR", stderr);
|
||||||
|
break;
|
||||||
|
case Fatal:
|
||||||
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED);
|
||||||
|
std::fputws(L"FATAL ERROR", stderr);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
|
||||||
|
fmt::print(stderr, FMT_STRING(L" {}"), modName);
|
||||||
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN);
|
||||||
|
if (sourceInfo)
|
||||||
|
fmt::print(stderr, FMT_STRING(L" {{}}"), sourceInfo);
|
||||||
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE);
|
||||||
|
if (thrName)
|
||||||
|
fmt::print(stderr, FMT_STRING(L" ({})"), thrName);
|
||||||
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
|
||||||
|
std::fputws(L"] ", stderr);
|
||||||
|
SetConsoleTextAttribute(Term, FOREGROUND_WHITE);
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
std::fputwc(L'[', stderr);
|
||||||
|
fmt::print(stderr, FMT_STRING(L"{:.4f} "), tmd);
|
||||||
|
uint_fast64_t fIdx = FrameIndex.load();
|
||||||
|
if (fIdx)
|
||||||
|
fmt::print(stderr, FMT_STRING(L"({}) "), fIdx);
|
||||||
|
switch (severity) {
|
||||||
|
case Info:
|
||||||
|
std::fputws(L"INFO", stderr);
|
||||||
|
break;
|
||||||
|
case Warning:
|
||||||
|
std::fputws(L"WARNING", stderr);
|
||||||
|
break;
|
||||||
|
case Error:
|
||||||
|
std::fputws(L"ERROR", stderr);
|
||||||
|
break;
|
||||||
|
case Fatal:
|
||||||
|
std::fputws(L"FATAL ERROR", stderr);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fmt::print(stderr, FMT_STRING(L" {}"), modName);
|
||||||
|
if (sourceInfo)
|
||||||
|
fmt::print(stderr, FMT_STRING(L" {{}}"), sourceInfo);
|
||||||
|
if (thrName)
|
||||||
|
fmt::print(stderr, FMT_STRING(L" ({})"), thrName);
|
||||||
|
std::fputws(L"] ", stderr);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void report(const char* modName, Level severity, fmt::string_view format, fmt::format_args args) override {
|
void report(const char* modName, Level severity, fmt::string_view format, fmt::format_args args) override {
|
||||||
_reportHead(modName, nullptr, severity);
|
_reportHead(modName, nullptr, severity);
|
||||||
fmt::vprint(stderr, format, args);
|
fmt::vprint(stderr, format, args);
|
||||||
|
@ -687,9 +836,9 @@ struct ConsoleLogger : public ILogger {
|
||||||
}
|
}
|
||||||
|
|
||||||
void report(const char* modName, Level severity, fmt::wstring_view format, fmt::wformat_args args) override {
|
void report(const char* modName, Level severity, fmt::wstring_view format, fmt::wformat_args args) override {
|
||||||
_reportHead(modName, nullptr, severity);
|
_reportHead(Widen(modName).c_str(), nullptr, severity);
|
||||||
fmt::vprint(stderr, format, args);
|
fmt::vprint(stderr, format, args);
|
||||||
std::fputc('\n', stderr);
|
std::fputwc(L'\n', stderr);
|
||||||
std::fflush(stderr);
|
std::fflush(stderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -703,9 +852,9 @@ struct ConsoleLogger : public ILogger {
|
||||||
|
|
||||||
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::wstring_view format,
|
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::wstring_view format,
|
||||||
fmt::wformat_args args) override {
|
fmt::wformat_args args) override {
|
||||||
_reportHead(modName, fmt::format(FMT_STRING("{}:{}"), file, linenum).c_str(), severity);
|
_reportHead(Widen(modName).c_str(), fmt::format(FMT_STRING(L"{}:{}"), Widen(file).c_str(), linenum).c_str(), severity);
|
||||||
fmt::vprint(stderr, format, args);
|
fmt::vprint(stderr, format, args);
|
||||||
std::fputc('\n', stderr);
|
std::fputwc(L'\n', stderr);
|
||||||
std::fflush(stderr);
|
std::fflush(stderr);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -821,6 +970,48 @@ struct FileLogger : public ILogger {
|
||||||
std::fputs("] ", fp);
|
std::fputs("] ", fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _reportHead(const wchar_t* modName, const wchar_t* 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();
|
||||||
|
const wchar_t* thrName = nullptr;
|
||||||
|
if (ThreadMap.find(thrId) != ThreadMap.end()) {
|
||||||
|
thrName = Widen(ThreadMap[thrId]).c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::fputwc(L'[', fp);
|
||||||
|
std::fwprintf(fp, L"%5.4f ", tmd);
|
||||||
|
const uint_fast64_t fIdx = FrameIndex.load();
|
||||||
|
if (fIdx != 0) {
|
||||||
|
std::fwprintf(fp, L"(%" PRIu64 L") ", fIdx);
|
||||||
|
}
|
||||||
|
switch (severity) {
|
||||||
|
case Info:
|
||||||
|
std::fputws(L"INFO", fp);
|
||||||
|
break;
|
||||||
|
case Warning:
|
||||||
|
std::fputws(L"WARNING", fp);
|
||||||
|
break;
|
||||||
|
case Error:
|
||||||
|
std::fputws(L"ERROR", fp);
|
||||||
|
break;
|
||||||
|
case Fatal:
|
||||||
|
std::fputws(L"FATAL ERROR", fp);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
std::fwprintf(fp, L" %s", modName);
|
||||||
|
if (sourceInfo) {
|
||||||
|
std::fwprintf(fp, L" {%s}", sourceInfo);
|
||||||
|
}
|
||||||
|
if (thrName) {
|
||||||
|
std::fwprintf(fp, L" (%s)", thrName);
|
||||||
|
}
|
||||||
|
std::fputws(L"] ", fp);
|
||||||
|
}
|
||||||
|
|
||||||
void report(const char* modName, Level severity, fmt::string_view format, fmt::format_args args) override {
|
void report(const char* modName, Level severity, fmt::string_view format, fmt::format_args args) override {
|
||||||
openFileIfNeeded();
|
openFileIfNeeded();
|
||||||
_reportHead(modName, nullptr, severity);
|
_reportHead(modName, nullptr, severity);
|
||||||
|
@ -830,9 +1021,9 @@ struct FileLogger : public ILogger {
|
||||||
|
|
||||||
void report(const char* modName, Level severity, fmt::wstring_view format, fmt::wformat_args args) override {
|
void report(const char* modName, Level severity, fmt::wstring_view format, fmt::wformat_args args) override {
|
||||||
openFileIfNeeded();
|
openFileIfNeeded();
|
||||||
_reportHead(modName, nullptr, severity);
|
_reportHead(Widen(modName).c_str(), nullptr, severity);
|
||||||
fmt::vprint(fp, format, args);
|
fmt::vprint(fp, format, args);
|
||||||
std::fputc('\n', fp);
|
std::fputwc(L'\n', fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::string_view format,
|
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::string_view format,
|
||||||
|
@ -846,9 +1037,9 @@ struct FileLogger : public ILogger {
|
||||||
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::wstring_view format,
|
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::wstring_view format,
|
||||||
fmt::wformat_args args) override {
|
fmt::wformat_args args) override {
|
||||||
openFileIfNeeded();
|
openFileIfNeeded();
|
||||||
_reportHead(modName, fmt::format(FMT_STRING("{}:{}"), file, linenum).c_str(), severity);
|
_reportHead(Widen(modName).c_str(), Widen(fmt::format(FMT_STRING("{}:{}"), file, linenum)).c_str(), severity);
|
||||||
fmt::vprint(fp, format, args);
|
fmt::vprint(fp, format, args);
|
||||||
std::fputc('\n', fp);
|
std::fputwc(L'\n', fp);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue