From 3bedd268e86fa8c4e83eb5dd45755732a32acdcd Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Fri, 19 Jul 2019 18:21:39 -1000 Subject: [PATCH] Integrate libfmt for format strings --- .gitmodules | 3 + CMakeLists.txt | 7 +- fmt | 1 + include/logvisor/logvisor.hpp | 171 +++++++++++++++++++---------- lib/logvisor.cpp | 195 ++++++++++++++++------------------ 5 files changed, 215 insertions(+), 162 deletions(-) create mode 100644 .gitmodules create mode 160000 fmt diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..c8edf6a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "fmt"] + path = fmt + url = https://github.com/fmtlib/fmt diff --git a/CMakeLists.txt b/CMakeLists.txt index b332ef7..f603991 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,15 +7,18 @@ endif() include (CMakePackageConfigHelpers) +add_subdirectory(fmt) + add_library(logvisor lib/logvisor.cpp include/logvisor/logvisor.hpp) -target_link_libraries(logvisor PUBLIC ${CMAKE_DL_LIBS}) +target_link_libraries(logvisor PUBLIC ${CMAKE_DL_LIBS} fmt) target_include_directories(logvisor PUBLIC $) install(DIRECTORY include/logvisor DESTINATION include) +install(DIRECTORY fmt/include/fmt DESTINATION include) set(version_config_file "${PROJECT_BINARY_DIR}/logvisorConfigVersion.cmake") set(config_file "${PROJECT_BINARY_DIR}/logvisorConfig.cmake") @@ -23,7 +26,7 @@ set(config_install_dir "lib/cmake/logvisor") # Associate target with export install( - TARGETS logvisor + TARGETS logvisor fmt EXPORT logvisorTargets ARCHIVE DESTINATION "lib" INCLUDES DESTINATION include # This sets the INTERFACE_INCLUDE_DIRECTORIES property of the target. diff --git a/fmt b/fmt new file mode 160000 index 0000000..6bcc3fd --- /dev/null +++ b/fmt @@ -0,0 +1 @@ +Subproject commit 6bcc3fd21694b5634cc006915bd049cf460a9a8d diff --git a/include/logvisor/logvisor.hpp b/include/logvisor/logvisor.hpp index fd5a5ec..ee22dd4 100644 --- a/include/logvisor/logvisor.hpp +++ b/include/logvisor/logvisor.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include @@ -9,6 +8,11 @@ #include #include +#define FMT_STRING_ALIAS 1 +#define FMT_ENFORCE_COMPILE_STRING 1 +#define FMT_USE_GRISU 0 +#include + #ifdef __SWITCH__ #include "nxstl/mutex" #endif @@ -40,13 +44,13 @@ enum Level { * @brief Backend interface for receiving app-wide log events */ struct ILogger { - virtual ~ILogger() {} - virtual void report(const char* modName, Level severity, const char* format, va_list ap) = 0; - virtual void report(const char* modName, Level severity, const wchar_t* format, va_list ap) = 0; - virtual void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, const char* format, - va_list ap) = 0; + virtual ~ILogger() = default; + virtual void report(const char* modName, Level severity, fmt::string_view format, fmt::format_args args) = 0; + virtual void report(const char* modName, Level severity, fmt::wstring_view format, fmt::wformat_args args) = 0; virtual void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, - const wchar_t* format, va_list ap) = 0; + fmt::string_view format, fmt::format_args args) = 0; + virtual void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, + fmt::wstring_view format, fmt::wformat_args args) = 0; }; /** @@ -107,7 +111,7 @@ extern LogMutex _LogMutex; * @brief Take a centralized lock for the logging output stream(s) * @return RAII mutex lock */ -static inline std::unique_lock LockLog() { return _LogMutex.lock(); } +inline std::unique_lock LockLog() { return _LogMutex.lock(); } extern uint64_t _LogCounter; @@ -115,12 +119,12 @@ extern uint64_t _LogCounter; * @brief Get current count of logging events * @return Log Count */ -static inline uint64_t GetLogCounter() { return _LogCounter; } +inline uint64_t GetLogCounter() { return _LogCounter; } /** * @brief Restore centralized logger vector to default state (silent operation) */ -static inline void UnregisterLoggers() { MainLoggers.clear(); } +inline void UnregisterLoggers() { MainLoggers.clear(); } /** * @brief Construct and register a real-time console logger singleton @@ -168,6 +172,40 @@ void RegisterFileLogger(const wchar_t* filepath); class Module { const char* m_modName; + template + void _vreport(Level severity, fmt::basic_string_view format, + fmt::basic_format_args> args) { + auto lk = LockLog(); + ++_LogCounter; + if (severity == Fatal) + RegisterConsoleLogger(); + for (auto& logger : MainLoggers) + logger->report(m_modName, severity, format, args); + if (severity == Error || severity == Fatal) + logvisorBp(); + if (severity == Fatal) + logvisorAbort(); + else if (severity == Error) + ++ErrorCount; + } + + template + void _vreportSource(Level severity, const char* file, unsigned linenum, fmt::basic_string_view format, + fmt::basic_format_args> args) { + auto lk = LockLog(); + ++_LogCounter; + if (severity == Fatal) + RegisterConsoleLogger(); + for (auto& logger : MainLoggers) + logger->reportSource(m_modName, severity, file, linenum, format, args); + if (severity == Error || severity == Fatal) + logvisorBp(); + if (severity == Fatal) + logvisorAbort(); + else if (severity == Error) + ++ErrorCount; + } + public: Module(const char* modName) : m_modName(modName) {} @@ -176,34 +214,21 @@ public: * @param severity Level of log report severity * @param format Standard printf-style format string */ - template - inline void report(Level severity, const CharType* format, ...) { + template > + void report(Level severity, const S& format, Args&&... args) { if (MainLoggers.empty() && severity != Level::Fatal) return; - va_list ap; - va_start(ap, format); - report(severity, format, ap); - va_end(ap); + _vreport(severity, fmt::to_string_view(format), + fmt::basic_format_args>( + fmt::internal::make_args_checked(format, args...))); } - template - inline void report(Level severity, const CharType* format, va_list ap) { - auto lk = LockLog(); - ++_LogCounter; - if (severity == Fatal) - RegisterConsoleLogger(); - for (auto& logger : MainLoggers) { - va_list apc; - va_copy(apc, ap); - logger->report(m_modName, severity, format, apc); - va_end(apc); - } - if (severity == Error || severity == Fatal) - logvisorBp(); - if (severity == Fatal) - logvisorAbort(); - else if (severity == Error) - ++ErrorCount; + template + void vreport(Level severity, fmt::basic_string_view format, + fmt::basic_format_args> args) { + if (MainLoggers.empty() && severity != Level::Fatal) + return; + _vreport(severity, format, args); } /** @@ -213,34 +238,66 @@ public: * @param linenum Source line number from __LINE__ macro * @param format Standard printf-style format string */ - template - inline void reportSource(Level severity, const char* file, unsigned linenum, const CharType* format, ...) { + template > + void reportSource(Level severity, const char* file, unsigned linenum, const S& format, Args&&... args) { if (MainLoggers.empty() && severity != Level::Fatal) return; - va_list ap; - va_start(ap, format); - reportSource(severity, file, linenum, format, ap); - va_end(ap); + _vreportSource( + severity, file, linenum, fmt::to_string_view(format), + fmt::basic_format_args>( + fmt::internal::make_args_checked(format, args...))); } - template - inline void reportSource(Level severity, const char* file, unsigned linenum, const CharType* format, va_list ap) { - auto lk = LockLog(); - ++_LogCounter; - if (severity == Fatal) - RegisterConsoleLogger(); - for (auto& logger : MainLoggers) { - va_list apc; - va_copy(apc, ap); - logger->reportSource(m_modName, severity, file, linenum, format, apc); - va_end(apc); - } - - if (severity == Fatal) - logvisorAbort(); - else if (severity == Error) - ++ErrorCount; + template + void vreportSource(Level severity, const char* file, unsigned linenum, fmt::basic_string_view format, + fmt::basic_format_args> args) { + if (MainLoggers.empty() && severity != Level::Fatal) + return; + _vreportSource(severity, file, linenum, format, args); } }; +#define Lfmt(str) fmt(L##str) +#define ufmt(str) fmt(u##str) +#define Ufmt(str) fmt(U##str) +#define FMT_CUSTOM_FORMATTER(tp, ...) \ +namespace fmt { \ +template <> \ +struct formatter { \ + template \ + constexpr auto parse(ParseContext &ctx) { return ctx.begin(); } \ + template \ + auto format(const tp &obj, FormatContext &ctx) { \ + return format_to(ctx.out(), __VA_ARGS__); \ + } \ +}; \ +template <> \ +struct formatter { \ + template \ + constexpr auto parse(ParseContext &ctx) { return ctx.begin(); } \ + template \ + auto format(const tp &obj, FormatContext &ctx) { \ + return format_to(ctx.out(), L##__VA_ARGS__); \ + } \ +}; \ +template <> \ +struct formatter { \ + template \ + constexpr auto parse(ParseContext &ctx) { return ctx.begin(); } \ + template \ + auto format(const tp &obj, FormatContext &ctx) { \ + return format_to(ctx.out(), u##__VA_ARGS__); \ + } \ +}; \ +template <> \ +struct formatter { \ + template \ + constexpr auto parse(ParseContext &ctx) { return ctx.begin(); } \ + template \ + auto format(const tp &obj, FormatContext &ctx) { \ + return format_to(ctx.out(), U##__VA_ARGS__); \ + } \ +}; \ +} + } // namespace logvisor diff --git a/lib/logvisor.cpp b/lib/logvisor.cpp index 0e75302..a91ae8e 100644 --- a/lib/logvisor.cpp +++ b/lib/logvisor.cpp @@ -184,25 +184,22 @@ void logvisorAbort() { readlink("/proc/self/exe", exeNameBuffer, exeBufSize); #endif - char cmdLine[1024]; #if __APPLE__ - snprintf(cmdLine, 1024, "atos -p %d", getpid()); + std::string cmdLineStr = fmt::format(fmt("atos -p {}"), getpid()); #else - snprintf(cmdLine, 1024, "2>/dev/null addr2line -C -f -e \"%s\"", exeNameBuffer); + std::string cmdLineStr = fmt::format(fmt("2>/dev/null addr2line -C -f -e \"{}\""), exeNameBuffer); #endif - std::string cmdLineStr = cmdLine; for (size_t i = 0; i < size; i++) { #if __linux__ Dl_info dlip; if (dladdr(array[i], &dlip)) - snprintf(cmdLine, 128, " %p", (void*)((uint8_t*)array[i] - (uint8_t*)dlip.dli_fbase)); + cmdLineStr += fmt::format(fmt(" 0x{:016X}"), (uintptr_t)((uint8_t*)array[i] - (uint8_t*)dlip.dli_fbase)); else - snprintf(cmdLine, 128, " %p", array[i]); + cmdLineStr += fmt::format(fmt(" 0x{:016X}"), (uintptr_t)array[i]); #else - snprintf(cmdLine, 128, " %p", array[i]); + cmdLineStr += fmt::format(fmt(" 0x{:016X}"), (uintptr_t)array[i]); #endif - cmdLineStr += cmdLine; } FILE* fp = popen(cmdLineStr.c_str(), "r"); @@ -210,26 +207,26 @@ void logvisorAbort() { char readBuf[256]; size_t readSz; while ((readSz = fread(readBuf, 1, 256, fp))) - fwrite(readBuf, 1, readSz, stderr); + std::fwrite(readBuf, 1, readSz, stderr); pclose(fp); } else { for (size_t i = 0; i < size; i++) { - fprintf(stderr, "- "); + std::fputs("- ", stderr); Dl_info dlip; if (dladdr(array[i], &dlip)) { int status; char* demangledName = abi::__cxa_demangle(dlip.dli_sname, nullptr, nullptr, &status); - fprintf(stderr, "%p(%s+%p)\n", dlip.dli_saddr, demangledName ? demangledName : dlip.dli_sname, - (void*)((uint8_t*)array[i] - (uint8_t*)dlip.dli_fbase)); - free(demangledName); + 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); } else { - fprintf(stderr, "%p\n", array[i]); + std::fprintf(stderr, "%p\n", array[i]); } } } - fflush(stderr); - fflush(stdout); + std::fflush(stderr); + std::fflush(stdout); KillProcessTree(); #ifndef NDEBUG @@ -248,19 +245,19 @@ static void AbortHandler(int signum) { _LogMutex.enabled = false; switch (signum) { case SIGSEGV: - Log.report(logvisor::Fatal, "Segmentation Fault"); + Log.report(logvisor::Fatal, fmt("Segmentation Fault")); break; case SIGILL: - Log.report(logvisor::Fatal, "Bad Execution"); + Log.report(logvisor::Fatal, fmt("Bad Execution")); break; case SIGFPE: - Log.report(logvisor::Fatal, "Floating Point Exception"); + Log.report(logvisor::Fatal, fmt("Floating Point Exception")); break; case SIGABRT: - Log.report(logvisor::Fatal, "Abort Signal"); + Log.report(logvisor::Fatal, fmt("Abort Signal")); break; default: - Log.report(logvisor::Fatal, "unknown signal %d", signum); + Log.report(logvisor::Fatal, fmt("unknown signal {}"), signum); break; } } @@ -324,10 +321,10 @@ struct ConsoleLogger : public ILogger { static void _reportHead(const char* modName, const char* sourceInfo, Level severity) { /* Clear current line out */ int width = ConsoleWidth(); - fprintf(stderr, "\r"); + std::fputs("\r", stderr); for (int w = 0; w < width; ++w) - fprintf(stderr, " "); - fprintf(stderr, "\r"); + std::fputs(" ", stderr); + std::fputs("\r", stderr); std::chrono::steady_clock::duration tm = CurrentUptime(); double tmd = tm.count() * std::chrono::steady_clock::duration::period::num / @@ -338,33 +335,33 @@ struct ConsoleLogger : public ILogger { thrName = ThreadMap[thrId]; if (XtermColor) { - fprintf(stderr, BOLD "["); - fprintf(stderr, GREEN "%5.4f ", tmd); + std::fputs(BOLD "[", stderr); + fmt::print(stderr, fmt(GREEN "{:5.4} "), tmd); uint_fast64_t fIdx = FrameIndex.load(); if (fIdx) - fprintf(stderr, "(%" PRIu64 ") ", fIdx); + fmt::print(stderr, fmt("({}) "), fIdx); switch (severity) { case Info: - fprintf(stderr, BOLD CYAN "INFO"); + std::fputs(BOLD CYAN "INFO", stderr); break; case Warning: - fprintf(stderr, BOLD YELLOW "WARNING"); + std::fputs(BOLD YELLOW "WARNING", stderr); break; case Error: - fprintf(stderr, RED BOLD "ERROR"); + std::fputs(RED BOLD "ERROR", stderr); break; case Fatal: - fprintf(stderr, BOLD RED "FATAL ERROR"); + std::fputs(BOLD RED "FATAL ERROR", stderr); break; default: break; }; - fprintf(stderr, NORMAL BOLD " %s", modName); + fmt::print(stderr, fmt(NORMAL BOLD " {}"), modName); if (sourceInfo) - fprintf(stderr, BOLD YELLOW " {%s}", sourceInfo); + fmt::print(stderr, fmt(BOLD YELLOW " {{}}"), sourceInfo); if (thrName) - fprintf(stderr, BOLD MAGENTA " (%s)", thrName); - fprintf(stderr, NORMAL BOLD "] " NORMAL); + fmt::print(stderr, fmt(BOLD MAGENTA " ({})"), thrName); + std::fputs(NORMAL BOLD "] " NORMAL, stderr); } else { #if _WIN32 #if !WINDOWS_STORE @@ -408,69 +405,65 @@ struct ConsoleLogger : public ILogger { SetConsoleTextAttribute(Term, FOREGROUND_WHITE); #endif #else - fprintf(stderr, "["); - fprintf(stderr, "%5.4f ", tmd); + std::fputs("[", stderr); + fmt::print(stderr, fmt("{:5.4} "), tmd); uint_fast64_t fIdx = FrameIndex.load(); if (fIdx) - fprintf(stderr, "(%" PRIu64 ") ", fIdx); + fmt::print(stderr, fmt("({}) "), fIdx); switch (severity) { case Info: - fprintf(stderr, "INFO"); + std::fputs("INFO", stderr); break; case Warning: - fprintf(stderr, "WARNING"); + std::fputs("WARNING", stderr); break; case Error: - fprintf(stderr, "ERROR"); + std::fputs("ERROR", stderr); break; case Fatal: - fprintf(stderr, "FATAL ERROR"); + std::fputs("FATAL ERROR", stderr); break; default: break; }; - fprintf(stderr, " %s", modName); + fmt::print(stderr, fmt(" {}"), modName); if (sourceInfo) - fprintf(stderr, " {%s}", sourceInfo); + fmt::print(stderr, fmt(" {{}}"), sourceInfo); if (thrName) - fprintf(stderr, " (%s)", thrName); - fprintf(stderr, "] "); + fmt::print(stderr, fmt(" ({})"), thrName); + std::fputs("] ", stderr); #endif } } - void report(const char* modName, Level severity, const char* format, va_list ap) { + void report(const char* modName, Level severity, fmt::string_view format, fmt::format_args args) { _reportHead(modName, nullptr, severity); - vfprintf(stderr, format, ap); - fprintf(stderr, "\n"); - fflush(stderr); + fmt::vprint(stderr, format, args); + std::fputs("\n", stderr); + std::fflush(stderr); } - void report(const char* modName, Level severity, const wchar_t* format, va_list ap) { + void report(const char* modName, Level severity, fmt::wstring_view format, fmt::wformat_args args) { _reportHead(modName, nullptr, severity); - vfwprintf(stderr, format, ap); - fprintf(stderr, "\n"); - fflush(stderr); + fmt::vprint(stderr, format, args); + std::fputs("\n", stderr); + std::fflush(stderr); } - void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, const char* format, - va_list ap) { - char sourceInfo[128]; - snprintf(sourceInfo, 128, "%s:%u", file, linenum); - _reportHead(modName, sourceInfo, severity); - vfprintf(stderr, format, ap); - fprintf(stderr, "\n"); - fflush(stderr); + void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, + fmt::string_view format, fmt::format_args args) { + _reportHead(modName, fmt::format(fmt("{}:{}"), file, linenum).c_str(), severity); + fmt::vprint(stderr, format, args); + std::fputs("\n", stderr); + std::fflush(stderr); } - void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, const wchar_t* format, - va_list ap) { - char sourceInfo[128]; - snprintf(sourceInfo, 128, "%s:%u", file, linenum); - _reportHead(modName, sourceInfo, severity); - vfwprintf(stderr, format, ap); - fprintf(stderr, "\n"); - fflush(stderr); + void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, + fmt::wstring_view format, fmt::wformat_args args) { + _reportHead(modName, fmt::format(fmt("{}:{}"), file, linenum).c_str(), severity); + fmt::vprint(stderr, format, args); + std::fputs("\n", stderr); + std::fflush(stderr); } }; @@ -502,7 +495,7 @@ void RegisterStandardExceptions() { struct FileLogger : public ILogger { FILE* fp; virtual void openFile() = 0; - virtual void closeFile() { fclose(fp); } + virtual void closeFile() { std::fclose(fp); } void _reportHead(const char* modName, const char* sourceInfo, Level severity) { std::chrono::steady_clock::duration tm = CurrentUptime(); @@ -513,70 +506,66 @@ struct FileLogger : public ILogger { if (ThreadMap.find(thrId) != ThreadMap.end()) thrName = ThreadMap[thrId]; - fprintf(fp, "["); - fprintf(fp, "%5.4f ", tmd); + std::fputs("[", fp); + std::fprintf(fp, "%5.4f ", tmd); uint_fast64_t fIdx = FrameIndex.load(); if (fIdx) - fprintf(fp, "(%" PRIu64 ") ", fIdx); + std::fprintf(fp, "(%" PRIu64 ") ", fIdx); switch (severity) { case Info: - fprintf(fp, "INFO"); + std::fputs("INFO", fp); break; case Warning: - fprintf(fp, "WARNING"); + std::fputs("WARNING", fp); break; case Error: - fprintf(fp, "ERROR"); + std::fputs("ERROR", fp); break; case Fatal: - fprintf(fp, "FATAL ERROR"); + std::fputs("FATAL ERROR", fp); break; default: break; }; - fprintf(fp, " %s", modName); + std::fprintf(fp, " %s", modName); if (sourceInfo) - fprintf(fp, " {%s}", sourceInfo); + std::fprintf(fp, " {%s}", sourceInfo); if (thrName) - fprintf(fp, " (%s)", thrName); - fprintf(fp, "] "); + std::fprintf(fp, " (%s)", thrName); + std::fputs("] ", fp); } - void report(const char* modName, Level severity, const char* format, va_list ap) { + void report(const char* modName, Level severity, fmt::string_view format, fmt::format_args args) { openFile(); _reportHead(modName, nullptr, severity); - vfprintf(fp, format, ap); - fprintf(fp, "\n"); + fmt::vprint(fp, format, args); + std::fputs("\n", fp); closeFile(); } - void report(const char* modName, Level severity, const wchar_t* format, va_list ap) { + void report(const char* modName, Level severity, fmt::wstring_view format, fmt::wformat_args args) { openFile(); _reportHead(modName, nullptr, severity); - vfwprintf(fp, format, ap); - fprintf(fp, "\n"); + fmt::vprint(fp, format, args); + std::fputs("\n", fp); closeFile(); } - void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, const char* format, - va_list ap) { + void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, + fmt::string_view format, fmt::format_args args) { openFile(); - char sourceInfo[128]; - snprintf(sourceInfo, 128, "%s:%u", file, linenum); - _reportHead(modName, sourceInfo, severity); - vfprintf(fp, format, ap); - fprintf(fp, "\n"); + _reportHead(modName, fmt::format(fmt("{}:{}"), file, linenum).c_str(), severity); + fmt::vprint(fp, format, args); + std::fputs("\n", fp); closeFile(); } - void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, const wchar_t* format, - va_list ap) { + void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, + fmt::wstring_view format, fmt::wformat_args args) { openFile(); - char sourceInfo[128]; - snprintf(sourceInfo, 128, "%s:%u", file, linenum); - _reportHead(modName, sourceInfo, severity); - vfwprintf(fp, format, ap); - fprintf(fp, "\n"); + _reportHead(modName, fmt::format(fmt("{}:{}"), file, linenum).c_str(), severity); + fmt::vprint(fp, format, args); + std::fputs("\n", fp); closeFile(); } }; @@ -584,7 +573,7 @@ struct FileLogger : public ILogger { struct FileLogger8 : public FileLogger { const char* m_filepath; FileLogger8(const char* filepath) : m_filepath(filepath) {} - void openFile() { fp = fopen(m_filepath, "a"); } + void openFile() { fp = std::fopen(m_filepath, "a"); } }; void RegisterFileLogger(const char* filepath) {