From a2429cb920a05aa5317e1b00c134d077c780d13a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A4seToatz?= Date: Mon, 17 Nov 2025 04:48:58 +0100 Subject: [PATCH] Initial commit --- .gitignore | 2 + PeakMenu.sln | 28 +++ PeakMenu/PeakMenu.vcxproj | 156 ++++++++++++ PeakMenu/PeakMenu.vcxproj.filters | 78 ++++++ PeakMenu/PeakMenu.vcxproj.user | 4 + PeakMenu/common.hpp | 15 ++ PeakMenu/g3log/crashhandler_windows.cpp | 249 ++++++++++++++++++++ PeakMenu/g3log/filesink.cpp | 117 +++++++++ PeakMenu/g3log/filesinkhelper.ipp | 127 ++++++++++ PeakMenu/g3log/g2log.hpp | 22 ++ PeakMenu/g3log/g3log.cpp | 242 +++++++++++++++++++ PeakMenu/g3log/g3log/active.hpp | 67 ++++++ PeakMenu/g3log/g3log/atomicbool.hpp | 47 ++++ PeakMenu/g3log/g3log/crashhandler.hpp | 81 +++++++ PeakMenu/g3log/g3log/filesink.hpp | 46 ++++ PeakMenu/g3log/g3log/future.hpp | 60 +++++ PeakMenu/g3log/g3log/g3log.hpp | 226 ++++++++++++++++++ PeakMenu/g3log/g3log/logcapture.hpp | 75 ++++++ PeakMenu/g3log/g3log/loglevels.hpp | 194 +++++++++++++++ PeakMenu/g3log/g3log/logmessage.hpp | 148 ++++++++++++ PeakMenu/g3log/g3log/logworker.hpp | 143 +++++++++++ PeakMenu/g3log/g3log/moveoncopy.hpp | 51 ++++ PeakMenu/g3log/g3log/shared_queue.hpp | 78 ++++++ PeakMenu/g3log/g3log/sink.hpp | 76 ++++++ PeakMenu/g3log/g3log/sinkhandle.hpp | 59 +++++ PeakMenu/g3log/g3log/sinkwrapper.hpp | 21 ++ PeakMenu/g3log/g3log/stacktrace_windows.hpp | 41 ++++ PeakMenu/g3log/g3log/stlpatch_future.hpp | 78 ++++++ PeakMenu/g3log/g3log/time.hpp | 78 ++++++ PeakMenu/g3log/logcapture.cpp | 113 +++++++++ PeakMenu/g3log/loglevels.cpp | 128 ++++++++++ PeakMenu/g3log/logmessage.cpp | 193 +++++++++++++++ PeakMenu/g3log/logworker.cpp | 125 ++++++++++ PeakMenu/g3log/stacktrace_windows.cpp | 196 +++++++++++++++ PeakMenu/g3log/time.cpp | 151 ++++++++++++ PeakMenu/logger.hpp | 134 +++++++++++ PeakMenu/main.cpp | 37 +++ PeakMenu/memory.hpp | 24 ++ PeakMenu/offsets.hpp | 13 + PeakMenu/pointers.cpp | 15 ++ PeakMenu/pointers.hpp | 15 ++ 41 files changed, 3753 insertions(+) create mode 100644 .gitignore create mode 100644 PeakMenu.sln create mode 100644 PeakMenu/PeakMenu.vcxproj create mode 100644 PeakMenu/PeakMenu.vcxproj.filters create mode 100644 PeakMenu/PeakMenu.vcxproj.user create mode 100644 PeakMenu/common.hpp create mode 100644 PeakMenu/g3log/crashhandler_windows.cpp create mode 100644 PeakMenu/g3log/filesink.cpp create mode 100644 PeakMenu/g3log/filesinkhelper.ipp create mode 100644 PeakMenu/g3log/g2log.hpp create mode 100644 PeakMenu/g3log/g3log.cpp create mode 100644 PeakMenu/g3log/g3log/active.hpp create mode 100644 PeakMenu/g3log/g3log/atomicbool.hpp create mode 100644 PeakMenu/g3log/g3log/crashhandler.hpp create mode 100644 PeakMenu/g3log/g3log/filesink.hpp create mode 100644 PeakMenu/g3log/g3log/future.hpp create mode 100644 PeakMenu/g3log/g3log/g3log.hpp create mode 100644 PeakMenu/g3log/g3log/logcapture.hpp create mode 100644 PeakMenu/g3log/g3log/loglevels.hpp create mode 100644 PeakMenu/g3log/g3log/logmessage.hpp create mode 100644 PeakMenu/g3log/g3log/logworker.hpp create mode 100644 PeakMenu/g3log/g3log/moveoncopy.hpp create mode 100644 PeakMenu/g3log/g3log/shared_queue.hpp create mode 100644 PeakMenu/g3log/g3log/sink.hpp create mode 100644 PeakMenu/g3log/g3log/sinkhandle.hpp create mode 100644 PeakMenu/g3log/g3log/sinkwrapper.hpp create mode 100644 PeakMenu/g3log/g3log/stacktrace_windows.hpp create mode 100644 PeakMenu/g3log/g3log/stlpatch_future.hpp create mode 100644 PeakMenu/g3log/g3log/time.hpp create mode 100644 PeakMenu/g3log/logcapture.cpp create mode 100644 PeakMenu/g3log/loglevels.cpp create mode 100644 PeakMenu/g3log/logmessage.cpp create mode 100644 PeakMenu/g3log/logworker.cpp create mode 100644 PeakMenu/g3log/stacktrace_windows.cpp create mode 100644 PeakMenu/g3log/time.cpp create mode 100644 PeakMenu/logger.hpp create mode 100644 PeakMenu/main.cpp create mode 100644 PeakMenu/memory.hpp create mode 100644 PeakMenu/offsets.hpp create mode 100644 PeakMenu/pointers.cpp create mode 100644 PeakMenu/pointers.hpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4ae41f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/.vs +x64 \ No newline at end of file diff --git a/PeakMenu.sln b/PeakMenu.sln new file mode 100644 index 0000000..5d1db76 --- /dev/null +++ b/PeakMenu.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35506.116 d17.12 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PeakMenu", "PeakMenu\PeakMenu.vcxproj", "{AF72AD5F-FAC1-489C-A022-36C08515D36B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AF72AD5F-FAC1-489C-A022-36C08515D36B}.Debug|x64.ActiveCfg = Debug|x64 + {AF72AD5F-FAC1-489C-A022-36C08515D36B}.Debug|x64.Build.0 = Debug|x64 + {AF72AD5F-FAC1-489C-A022-36C08515D36B}.Debug|x86.ActiveCfg = Debug|Win32 + {AF72AD5F-FAC1-489C-A022-36C08515D36B}.Debug|x86.Build.0 = Debug|Win32 + {AF72AD5F-FAC1-489C-A022-36C08515D36B}.Release|x64.ActiveCfg = Release|x64 + {AF72AD5F-FAC1-489C-A022-36C08515D36B}.Release|x64.Build.0 = Release|x64 + {AF72AD5F-FAC1-489C-A022-36C08515D36B}.Release|x86.ActiveCfg = Release|Win32 + {AF72AD5F-FAC1-489C-A022-36C08515D36B}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/PeakMenu/PeakMenu.vcxproj b/PeakMenu/PeakMenu.vcxproj new file mode 100644 index 0000000..f2bfc18 --- /dev/null +++ b/PeakMenu/PeakMenu.vcxproj @@ -0,0 +1,156 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {af72ad5f-fac1-489c-a022-36c08515d36b} + PeakMenu + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + DynamicLibrary + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + C:\Users\raefb\Documents\GitHub\PeakMenu\PeakMenu\g3log;%(AdditionalIncludeDirectories) + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PeakMenu/PeakMenu.vcxproj.filters b/PeakMenu/PeakMenu.vcxproj.filters new file mode 100644 index 0000000..37e66e7 --- /dev/null +++ b/PeakMenu/PeakMenu.vcxproj.filters @@ -0,0 +1,78 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {06b8dc19-35a5-4810-9c1c-bc3255a43a94} + + + + + Source Files + + + Source Files\g3log + + + Source Files\g3log + + + Source Files\g3log + + + Source Files\g3log + + + Source Files\g3log + + + Source Files\g3log + + + Source Files\g3log + + + Source Files\g3log + + + Source Files\g3log + + + Source Files + + + + + Header Files + + + Source Files\g3log + + + Source Files\g3log + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/PeakMenu/PeakMenu.vcxproj.user b/PeakMenu/PeakMenu.vcxproj.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/PeakMenu/PeakMenu.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/PeakMenu/common.hpp b/PeakMenu/common.hpp new file mode 100644 index 0000000..df566f2 --- /dev/null +++ b/PeakMenu/common.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include +#include +#include + +namespace peak +{ + using namespace std::chrono_literals; + inline bool isRunning{true}; + inline HMODULE hModule; + inline HANDLE mainThread{}; + inline DWORD mainThreadId{}; +} \ No newline at end of file diff --git a/PeakMenu/g3log/crashhandler_windows.cpp b/PeakMenu/g3log/crashhandler_windows.cpp new file mode 100644 index 0000000..757ab98 --- /dev/null +++ b/PeakMenu/g3log/crashhandler_windows.cpp @@ -0,0 +1,249 @@ +/** ========================================================================== + * 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes + * with no warranties. This code is yours to share, use and modify with no + * strings attached and no restrictions or obligations. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org + * ============================================================================*/ + +#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) +#error "crashhandler_windows.cpp used but not on a windows system" +#endif + +#include // getpid +#include +#include +#include +#include +#include +#include "g3log/crashhandler.hpp" +#include "g3log/g3log.hpp" +#include "g3log/logcapture.hpp" +#include "g3log/stacktrace_windows.hpp" + +#define getpid _getpid + +namespace { + std::atomic gBlockForFatal{true}; + LPTOP_LEVEL_EXCEPTION_FILTER g_previous_unexpected_exception_handler = nullptr; + +#if !(defined(DISABLE_FATAL_SIGNALHANDLING)) + thread_local bool g_installed_thread_signal_handler = false; +#endif + +#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING)) + void* g_vector_exception_handler = nullptr; +#endif + + // called for fatal signals SIGABRT, SIGFPE, SIGSEGV, SIGILL, SIGTERM + void signalHandler(int signal_number) { + using namespace g3::internal; + std::string dump = stacktrace::stackdump(); + + std::ostringstream fatal_stream; + fatal_stream << "\n***** Received fatal signal " << g3::internal::exitReasonName(g3::internal::FATAL_SIGNAL, signal_number); + fatal_stream << "(" << signal_number << ")\tPID: " << getpid() << std::endl; + + LogCapture trigger(FATAL_SIGNAL, static_cast(signal_number), dump.c_str()); + trigger.stream() << fatal_stream.str(); + + // Trigger debug break point, if we're in debug. This breakpoint CAN cause a slowdown when it happens. + // Be patient. The "Debug" dialog should pop-up eventually if you doing it in Visual Studio. + // For fatal signals only, not exceptions. + // This is a way to tell the IDE (if in dev mode) that it can stop at this breakpoint + // Note that at this time the fatal log event with stack trace is NOT yet flushed to the logger + // This call will do nothing unless we're in DEBUG and "DEBUG_BREAK_AT_FATAL_SIGNAL" is enabled + // ref: g3log/Options.cmake +#if (!defined(NDEBUG) && defined(DEBUG_BREAK_AT_FATAL_SIGNAL)) + __debugbreak(); +#endif + } // scope exit - message sent to LogWorker, wait to die... + + // Unhandled exception catching + LONG WINAPI exceptionHandling(EXCEPTION_POINTERS* info, const std::string& handler) { + std::string dump = stacktrace::stackdump(info); + + std::ostringstream fatal_stream; + const g3::SignalType exception_code = info->ExceptionRecord->ExceptionCode; + fatal_stream << "\n***** " << handler << ": Received fatal exception " << g3::internal::exitReasonName(g3::internal::FATAL_EXCEPTION, exception_code); + fatal_stream << "\tPID: " << getpid() << std::endl; + + const auto fatal_id = static_cast(exception_code); + LogCapture trigger(g3::internal::FATAL_EXCEPTION, fatal_id, dump.c_str()); + trigger.stream() << fatal_stream.str(); + // FATAL Exception: It doesn't necessarily stop here. we pass on continue search + // if no one else will catch that then it's goodbye anyhow. + // The RISK here is if someone is catching this and returning "EXCEPTION_EXECUTE_HANDLER" + // but does not shutdown then the software will be running with g3log shutdown. + // .... However... this must be seen as a bug from standard handling of fatal exceptions + // https://msdn.microsoft.com/en-us/library/6wxdsc38.aspx + return EXCEPTION_CONTINUE_SEARCH; + } + + // Unhandled exception catching + LONG WINAPI unexpectedExceptionHandling(EXCEPTION_POINTERS* info) { + g3::internal::restoreFatalHandlingToDefault(); + return exceptionHandling(info, "Unexpected Exception Handler"); + } + + /// Setup through (Windows API) AddVectoredExceptionHandler + /// Ref: http://blogs.msdn.com/b/zhanli/archive/2010/06/25/c-tips-addvectoredexceptionhandler-addvectoredcontinuehandler-and-setunhandledexceptionfilter.aspx +#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING)) + LONG WINAPI vectorExceptionHandling(PEXCEPTION_POINTERS p) { + const g3::SignalType exception_code = p->ExceptionRecord->ExceptionCode; + if (false == stacktrace::isKnownException(exception_code)) { + // The unknown exception is ignored. Since it is not a Windows + // fatal exception generated by the OS we leave the + // responsibility to deal with this by the client software. + return EXCEPTION_CONTINUE_SEARCH; + } else { + g3::internal::restoreFatalHandlingToDefault(); + return exceptionHandling(p, "Vectored Exception Handler"); + } + } +#endif +} // end anonymous namespace + +namespace g3 { + namespace internal { + // For windows exceptions this might ONCE be set to false, in case of a + // windows exceptions and not a signal + bool shouldBlockForFatalHandling() { + return gBlockForFatal; + } + + /// Generate stackdump. Or in case a stackdump was pre-generated and + /// non-empty just use that one. i.e. the latter case is only for + /// Windows and test purposes + std::string stackdump(const char* dump) { + if (nullptr != dump && !std::string(dump).empty()) { + return {dump}; + } + + return stacktrace::stackdump(); + } + + /// string representation of signal ID or Windows exception id + std::string exitReasonName(const LEVELS& level, g3::SignalType fatal_id) { + if (level == g3::internal::FATAL_EXCEPTION) { + return stacktrace::exceptionIdToText(fatal_id); + } + + switch (fatal_id) { + case SIGABRT: + return "SIGABRT"; + break; + case SIGFPE: + return "SIGFPE"; + break; + case SIGSEGV: + return "SIGSEGV"; + break; + case SIGILL: + return "SIGILL"; + break; + case SIGTERM: + return "SIGTERM"; + break; + default: + std::ostringstream oss; + oss << "UNKNOWN SIGNAL(" << fatal_id << ")"; + return oss.str(); + } + } + + // Triggered by g3log::LogWorker after receiving a FATAL trigger + // which is LOG(FATAL), CHECK(false) or a fatal signal our signalhandler caught. + // --- If LOG(FATAL) or CHECK(false) the signal_number will be SIGABRT + void exitWithDefaultSignalHandler(const LEVELS& level, g3::SignalType fatal_signal_id) { + restoreFatalHandlingToDefault(); + // For windows exceptions we want to continue the possibility of + // exception handling now when the log and stacktrace are flushed + // to sinks. We therefore avoid to kill the process here. Instead + // it will be the exceptionHandling functions above that + // will let exception handling continue with: EXCEPTION_CONTINUE_SEARCH + if (g3::internal::FATAL_EXCEPTION == level) { + gBlockForFatal = false; + return; + } + + // for a signal however, we exit through that fatal signal + const int signal_number = static_cast(fatal_signal_id); + raise(signal_number); + } + + // FYI: Concept of async-signal-safe operations does not exist on windows + // we stick to perror for lack of better alternatives. + size_t writeErrorMessage(const char* message) { + perror(message); + return std::strlen(message); + } + + // Restore back to default fatal event handling + void restoreFatalHandlingToDefault() { +#if !(defined(DISABLE_FATAL_SIGNALHANDLING)) + SetUnhandledExceptionFilter(g_previous_unexpected_exception_handler); + +#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING)) + RemoveVectoredExceptionHandler(g_vector_exception_handler); +#endif + + if (SIG_ERR == signal(SIGABRT, SIG_DFL)) + internal::writeErrorMessage("signal - SIGABRT"); + + if (SIG_ERR == signal(SIGFPE, SIG_DFL)) + internal::writeErrorMessage("signal - SIGABRT"); + + if (SIG_ERR == signal(SIGSEGV, SIG_DFL)) + internal::writeErrorMessage("signal - SIGABRT"); + + if (SIG_ERR == signal(SIGILL, SIG_DFL)) + internal::writeErrorMessage("signal - SIGABRT"); + + if (SIG_ERR == signal(SIGTERM, SIG_DFL)) + internal::writeErrorMessage("signal - SIGABRT"); +#endif + } + + void installSignalHandler() { + g3::installSignalHandlerForThread(); + } + + } // namespace internal + + /// SIGFPE, SIGILL, and SIGSEGV handling must be installed per thread + /// on Windows. This is automatically done if you do at least one LOG(...) call + /// you can also use this function call, per thread so make sure these three + /// fatal signals are covered in your thread (even if you don't do a LOG(...) call + void installSignalHandlerForThread() { +#if !(defined(DISABLE_FATAL_SIGNALHANDLING)) + if (!g_installed_thread_signal_handler) { + g_installed_thread_signal_handler = true; + if (SIG_ERR == signal(SIGTERM, signalHandler)) + internal::writeErrorMessage("signal - SIGTERM"); + if (SIG_ERR == signal(SIGABRT, signalHandler)) + internal::writeErrorMessage("signal - SIGABRT"); + if (SIG_ERR == signal(SIGFPE, signalHandler)) + internal::writeErrorMessage("signal - SIGFPE"); + if (SIG_ERR == signal(SIGSEGV, signalHandler)) + internal::writeErrorMessage("signal - SIGSEGV"); + if (SIG_ERR == signal(SIGILL, signalHandler)) + internal::writeErrorMessage("signal - SIGILL"); + } +#endif + } + + void installCrashHandler() { + internal::installSignalHandler(); + g_previous_unexpected_exception_handler = SetUnhandledExceptionFilter(unexpectedExceptionHandling); + +#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING)) + // const size_t kFirstExceptionHandler = 1; + // kFirstExceptionsHandler is kept here for documentation purposes. + // The last exception seems more like what we want. + const size_t kLastExceptionHandler = 0; + g_vector_exception_handler = AddVectoredExceptionHandler(kLastExceptionHandler, vectorExceptionHandling); +#endif + } + +} // end namespace g3 diff --git a/PeakMenu/g3log/filesink.cpp b/PeakMenu/g3log/filesink.cpp new file mode 100644 index 0000000..2e559d0 --- /dev/null +++ b/PeakMenu/g3log/filesink.cpp @@ -0,0 +1,117 @@ +/** ========================================================================== + * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes + * with no warranties. This code is yours to share, use and modify with no + * strings attached and no restrictions or obligations. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org + * ============================================================================*/ + +#include "g3log/filesink.hpp" +#include +#include +#include "filesinkhelper.ipp" + +namespace g3 { + using namespace internal; + + FileSink::FileSink(const std::string& log_prefix, const std::string& log_directory, const std::string& logger_id, size_t write_to_log_every_x_message) : + _log_details_func(&LogMessage::DefaultLogDetailsToString), + _log_file_with_path(log_directory), + _log_prefix_backup(log_prefix), + _outptr(new std::ofstream), + _header("\t\tLOG format: [YYYY/MM/DD hh:mm:ss uuu* LEVEL FILE->FUNCTION:LINE] message\n\n\t\t(uuu*: microseconds fractions of the seconds value)\n\n"), + _firstEntry(true), + _write_counter(0), + _write_to_log_every_x_message(write_to_log_every_x_message) { + _log_prefix_backup = prefixSanityFix(log_prefix); + if (!isValidFilename(_log_prefix_backup)) { + std::cerr << "g3log: forced abort due to illegal log prefix [" << log_prefix << "]" << std::endl; + abort(); + } + + std::string file_name = createLogFileName(_log_prefix_backup, logger_id); + _log_file_with_path = pathSanityFix(_log_file_with_path, file_name); + _outptr = createLogFile(_log_file_with_path); + + if (!_outptr) { + std::cerr << "Cannot write log file to location, attempting current directory" << std::endl; + _log_file_with_path = "./" + file_name; + _outptr = createLogFile(_log_file_with_path); + } + assert(_outptr && "cannot open log file at startup"); + } + + FileSink::~FileSink() { + std::string exit_msg = {"g3log g3FileSink shutdown at: "}; + auto now = std::chrono::system_clock::now(); + exit_msg.append(localtime_formatted(now, internal::time_formatted)).append("\n"); + + // write anything buffered up and then end with the exit msg + filestream() << _write_buffer << exit_msg << std::flush; + + exit_msg.append("Log file at: [").append(_log_file_with_path).append("]\n"); + std::cerr << exit_msg << std::flush; + } + + // The actual log receiving function + void FileSink::fileWrite(LogMessageMover message) { + if (_firstEntry) { + addLogFileHeader(); + _firstEntry = false; + } + + auto data = message.get().toString(_log_details_func); + + _write_buffer.append(data); + if (++_write_counter % _write_to_log_every_x_message == 0) { + filestream() << _write_buffer << std::flush; + _write_buffer.clear(); + } + } + + std::string FileSink::changeLogFile(const std::string& directory, const std::string& logger_id) { + + auto now = std::chrono::system_clock::now(); + auto now_formatted = g3::localtime_formatted(now, {internal::date_formatted + " " + internal::time_formatted}); + + std::string file_name = createLogFileName(_log_prefix_backup, logger_id); + std::string prospect_log = directory + file_name; + std::unique_ptr log_stream = createLogFile(prospect_log); + if (nullptr == log_stream) { + filestream() << "\n" + << now_formatted << " Unable to change log file. Illegal filename or busy? Unsuccessful log name was: " << prospect_log; + return {}; // no success + } + + addLogFileHeader(); + std::ostringstream ss_change; + ss_change << "\n\tChanging log file from : " << _log_file_with_path; + ss_change << "\n\tto new location: " << prospect_log << "\n"; + filestream() << now_formatted << ss_change.str(); + ss_change.str(""); + + std::string old_log = _log_file_with_path; + _log_file_with_path = std::move(prospect_log); + _outptr = std::move(log_stream); + ss_change << "\n\tNew log file. The previous log file was at: "; + ss_change << old_log << "\n"; + filestream() << now_formatted << ss_change.str(); + return _log_file_with_path; + } + + std::string FileSink::fileName() { + return _log_file_with_path; + } + + void FileSink::overrideLogDetails(LogMessage::LogDetailsFunc func) { + _log_details_func = func; + } + + void FileSink::overrideLogHeader(const std::string& change) { + _header = change; + } + + void FileSink::addLogFileHeader() { + filestream() << header(_header); + } +} // namespace g3 diff --git a/PeakMenu/g3log/filesinkhelper.ipp b/PeakMenu/g3log/filesinkhelper.ipp new file mode 100644 index 0000000..1e42999 --- /dev/null +++ b/PeakMenu/g3log/filesinkhelper.ipp @@ -0,0 +1,127 @@ +/** ========================================================================== + * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes + * with no warranties. This code is yours to share, use and modify with no + * strings attached and no restrictions or obligations. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org + * ============================================================================*/ + +#pragma once + + +#include +#include +#include +#include +#include +#include +#include + + +namespace g3 { + namespace internal { + static const std::string file_name_time_formatted = "%Y%m%d-%H%M%S"; + + // check for filename validity - filename should not be part of PATH + bool isValidFilename(const std::string &prefix_filename) { + std::string illegal_characters("/,|<>:#$%{}[]\'\"^!?+* "); + size_t pos = prefix_filename.find_first_of(illegal_characters, 0); + if (pos != std::string::npos) { + std::cerr << "Illegal character [" << prefix_filename.at(pos) << "] in logname prefix: " << "[" << prefix_filename << "]" << std::endl; + return false; + } else if (prefix_filename.empty()) { + std::cerr << "Empty filename prefix is not allowed" << std::endl; + return false; + } + + return true; + } + + std::string prefixSanityFix(std::string prefix) { + prefix.erase(std::remove_if(prefix.begin(), prefix.end(), ::isspace), prefix.end()); + prefix.erase(std::remove(prefix.begin(), prefix.end(), '/'), prefix.end()); + prefix.erase(std::remove(prefix.begin(), prefix.end(), '\\'), prefix.end()); + prefix.erase(std::remove(prefix.begin(), prefix.end(), '.'), prefix.end()); + prefix.erase(std::remove(prefix.begin(), prefix.end(), ':'), prefix.end()); + if (!isValidFilename(prefix)) { + return + { + }; + } + return prefix; + } + + std::string pathSanityFix(std::string path, const std::string &file_name) { + // Unify the delimeters,. maybe sketchy solution but it seems to work + // on at least win7 + ubuntu. All bets are off for older windows + std::replace(path.begin(), path.end(), '\\', '/'); + + // clean up in case of multiples + auto contains_end = [&](std::string & in) -> bool { + size_t size = in.size(); + if (!size) return false; + char end = in[size - 1]; + return (end == '/' || end == ' '); + }; + + while (contains_end(path)) { + path.erase(path.size() - 1); + } + + if (!path.empty()) { + path.insert(path.end(), '/'); + } + + path.insert(path.size(), file_name); + return path; + } + + std::string header(const std::string& headerFormat) { + std::ostringstream ss_entry; + // Day Month Date Time Year: is written as "%a %b %d %H:%M:%S %Y" and formatted output as : Wed Sep 19 08:28:16 2012 + auto now = std::chrono::system_clock::now(); + ss_entry << "\t\tg3log created log at: " << g3::localtime_formatted(now, "%a %b %d %H:%M:%S %Y") << "\n"; + ss_entry << headerFormat; + return ss_entry.str(); + } + + std::string createLogFileName(const std::string &verified_prefix, const std::string &logger_id) { + std::stringstream oss_name; + oss_name << verified_prefix << "."; + if( !logger_id.empty() ) { + oss_name << logger_id << "."; + } + auto now = std::chrono::system_clock::now(); + oss_name << g3::localtime_formatted(now, file_name_time_formatted); + oss_name << ".log"; + return oss_name.str(); + } + + bool openLogFile(const std::string &complete_file_with_path, std::ofstream &outstream) { + std::ios_base::openmode mode = std::ios_base::out; // for clarity: it's really overkill since it's an ofstream + mode |= std::ios_base::trunc; + outstream.open(complete_file_with_path, mode); + if (!outstream.is_open()) { + std::ostringstream ss_error; + ss_error << "FILE ERROR: could not open log file:[" << complete_file_with_path << "]"; + ss_error << "\n\t\t std::ios_base state = " << outstream.rdstate(); + std::cerr << ss_error.str().c_str() << std::endl; + outstream.close(); + return false; + } + return true; + } + + std::unique_ptr createLogFile(const std::string &file_with_full_path) { + std::unique_ptr out(new std::ofstream); + std::ofstream &stream(*(out.get())); + bool success_with_open_file = openLogFile(file_with_full_path, stream); + if (false == success_with_open_file) { + out.reset(); + } + return out; + } + + + } +} diff --git a/PeakMenu/g3log/g2log.hpp b/PeakMenu/g3log/g2log.hpp new file mode 100644 index 0000000..2ab9ee9 --- /dev/null +++ b/PeakMenu/g3log/g2log.hpp @@ -0,0 +1,22 @@ +/** ========================================================================== +* 2015 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes +* with no warranties. This code is yours to share, use and modify with no +* strings attached and no restrictions or obligations. +* +* For more information see g3log/LICENSE or refer refer to http://unlicense.org +* ============================================================================*/ + +#pragma once + +// For convenience: If you don't want to do a recursive search and replace in your source code +// for replacing g2log.hpp for g3log/g3log.hpp then you can choose to add this header file to your +// code. It will get the necessary includes +// +// +// Btw: replacing g2log for g3log include is easy on Linux +// find . -name "*.cpp*" -print | xargs sed -i -e 's/\g2log\.hpp/\g3log\/g3log\.hpp/g' + +#include +#include +#include +#include diff --git a/PeakMenu/g3log/g3log.cpp b/PeakMenu/g3log/g3log.cpp new file mode 100644 index 0000000..a5bfa52 --- /dev/null +++ b/PeakMenu/g3log/g3log.cpp @@ -0,0 +1,242 @@ +/** ========================================================================== + * 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes + * with no warranties. This code is yours to share, use and modify with no + * strings attached and no restrictions or obligations. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org + * ============================================================================ + * + * Filename:g3log.cpp Framework for Logging and Design By Contract + * Created: 2011 by Kjell Hedström + * + * PUBLIC DOMAIN and Not copyrighted since it was built on public-domain software and at least in "spirit" influenced + * from the following sources + * 1. kjellkod.cc ;) + * 2. Dr.Dobbs, Petru Marginean: http://drdobbs.com/article/printableArticle.jhtml?articleId=201804215&dept_url=/cpp/ + * 3. Dr.Dobbs, Michael Schulze: http://drdobbs.com/article/printableArticle.jhtml?articleId=225700666&dept_url=/cpp/ + * 4. Google 'glog': http://google-glog.googlecode.com/svn/trunk/doc/glog.html + * 5. Various Q&A at StackOverflow + * ********************************************* */ + +#include "g3log/g3log.hpp" +#include "g3log/crashhandler.hpp" +#include "g3log/loglevels.hpp" +#include "g3log/logmessage.hpp" +#include "g3log/logworker.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace { + std::once_flag g_initialize_flag; + g3::LogWorker* g_logger_instance = nullptr; // instantiated and OWNED somewhere else (main) + std::mutex g_logging_init_mutex; + + std::unique_ptr g_first_uninitialized_msg = {nullptr}; + std::once_flag g_set_first_uninitialized_flag; + std::once_flag g_save_first_uninitialized_flag; + const std::function g_pre_fatal_hook_that_does_nothing = [] { /*does nothing */ }; + std::function g_fatal_pre_logging_hook; + + std::atomic g_fatal_hook_recursive_counter = {0}; +} // namespace + +namespace g3 { + // signalhandler and internal clock is only needed to install once + // for unit testing purposes the initializeLogging might be called + // several times... + // for all other practical use, it shouldn't! + + void initializeLogging(LogWorker* bgworker) { + std::call_once(g_initialize_flag, [] { + installCrashHandler(); + }); + std::lock_guard lock(g_logging_init_mutex); + if (internal::isLoggingInitialized() || nullptr == bgworker) { + std::ostringstream exitMsg; + exitMsg << __FILE__ "->" << __FUNCTION__ << ":" << __LINE__ << std::endl; + exitMsg << "\tFatal exit due to illegal initialization of g3::LogWorker\n"; + exitMsg << "\t(due to multiple initializations? : " << std::boolalpha << internal::isLoggingInitialized(); + exitMsg << ", due to nullptr == bgworker? : " << std::boolalpha << (nullptr == bgworker) << ")"; + std::cerr << exitMsg.str() << std::endl; + std::exit(EXIT_FAILURE); + } + + // Save the first uninitialized message, if any + std::call_once(g_save_first_uninitialized_flag, [&bgworker] { + if (g_first_uninitialized_msg) { + bgworker->save(LogMessagePtr{std::move(g_first_uninitialized_msg)}); + } + }); + + g_logger_instance = bgworker; + // by default the pre fatal logging hook does nothing + // if it WOULD do something it would happen in + setFatalPreLoggingHook(g_pre_fatal_hook_that_does_nothing); + // recursive crash counter re-set to zero + g_fatal_hook_recursive_counter.store(0); + } + + /** + * default does nothing, @ref ::g_pre_fatal_hook_that_does_nothing + * It will be called just before sending the fatal message, @ref pushFatalmessageToLogger + * It will be reset to do nothing in ::initializeLogging(...) + * so please call this function, if you ever need to, after initializeLogging(...) + */ + void setFatalPreLoggingHook(std::function pre_fatal_hook) { + static std::mutex m; + std::lock_guard lock(m); + g_fatal_pre_logging_hook = pre_fatal_hook; + } + + // By default this function pointer goes to \ref pushFatalMessageToLogger; + std::function g_fatal_to_g3logworker_function_ptr = internal::pushFatalMessageToLogger; + + /** REPLACE fatalCallToLogger for fatalCallForUnitTest + * This function switches the function pointer so that only + * 'unitTest' mock-fatal calls are made. + * */ + void setFatalExitHandler(std::function fatal_call) { + g_fatal_to_g3logworker_function_ptr = fatal_call; + } + + namespace internal { + + bool isLoggingInitialized() { + return g_logger_instance != nullptr; + } + + /** + * Shutdown the logging by making the pointer to the background logger to nullptr. The object is not deleted + * that is the responsibility of its owner. * + */ + void shutDownLogging() { + std::lock_guard lock(g_logging_init_mutex); + g_logger_instance = nullptr; + } + + /** Same as the Shutdown above but called by the destructor of the LogWorker, thus ensuring that no further + * LOG(...) calls can happen to a non-existing LogWorker. + * @param active MUST BE the LogWorker initialized for logging. If it is not then this call is just ignored + * and the logging continues to be active. + * @return true if the correct worker was given,. and shutDownLogging was called + */ + bool shutDownLoggingForActiveOnly(LogWorker* active) { + if (isLoggingInitialized() && nullptr != active && (active != g_logger_instance)) { + LOG(WARNING) << "\n\t\tAttempted to shut down logging, but the ID of the Logger is not the one that is active." + << "\n\t\tHaving multiple instances of the g3::LogWorker is likely a BUG" + << "\n\t\tEither way, this call to shutDownLogging was ignored" + << "\n\t\tTry g3::internal::shutDownLogging() instead"; + return false; + } + shutDownLogging(); + return true; + } + + /** explicitly copy of all input. This is makes it possibly to use g3log across dynamically loaded libraries + * i.e. (dlopen + dlsym) */ + void saveMessage(const char* entry, const char* file, int line, const char* function, const LEVELS& level, + const char* boolean_expression, int fatal_signal, const char* stack_trace) { + LEVELS msgLevel{level}; + LogMessagePtr message{std::make_unique(file, line, function, msgLevel)}; + message.get()->write().append(entry); + message.get()->setExpression(boolean_expression); + + if (internal::wasFatal(level)) { + saveFatalMessage(stack_trace, message, fatal_signal); + } else { + pushMessageToLogger(message); + } + } + + void saveFatalMessage(const char* stack_trace, g3::LogMessagePtr& message, int& fatal_signal) { + auto fatalhook = g_fatal_pre_logging_hook; + // In case the fatal_pre logging actually will cause a crash in its turn + // let's not do recursive crashing! + setFatalPreLoggingHook(g_pre_fatal_hook_that_does_nothing); + ++g_fatal_hook_recursive_counter; // thread safe counter + // "benign" race here. If two threads crashes, with recursive crashes + // then it's possible that the "other" fatal stack trace will be shown + // that's OK since it was anyhow the first crash detected + static const std::string first_stack_trace = stack_trace; + fatalhook(); + message.get()->write().append(stack_trace); + + if (g_fatal_hook_recursive_counter.load() > 1) { + message.get()->write().append( + "\n\n\nWARNING\n" + "A recursive crash detected. It is likely the hook set with 'setFatalPreLoggingHook(...)' is responsible\n\n") + .append("---First crash stacktrace: ") + .append(first_stack_trace) + .append("\n---End of first stacktrace\n"); + } + FatalMessagePtr fatal_message{std::make_unique(*(message._move_only.get()), fatal_signal)}; + // At destruction, flushes fatal message to g3LogWorker + // either we will stay here until the background worker has received the fatal + // message, flushed the crash message to the sinks and exits with the same fatal signal + //..... OR it's in unit-test mode then we throw a std::runtime_error (and never hit sleep) + fatalCall(fatal_message); + } + /** + * save the message to the logger. In case of called before the logger is instantiated + * the first message will be saved. Any following subsequent uninitialized log calls + * will be ignored. + * + * The first initialized log entry will also save the first uninitialized log message, if any + * @param log_entry to save to logger + */ + void pushMessageToLogger(LogMessagePtr incoming) { // todo rename to Push SavedMessage To Worker + // Uninitialized messages are ignored but does not CHECK/crash the logger + if (!internal::isLoggingInitialized()) { + std::call_once(g_set_first_uninitialized_flag, [&] { + g_first_uninitialized_msg = incoming.release(); + std::string err = {"LOGGER NOT INITIALIZED:\n\t\t"}; + err.append(g_first_uninitialized_msg->message()); + std::string& str = g_first_uninitialized_msg->write(); + str.clear(); + str.append(err); // replace content + std::cerr << str << std::endl; }); + return; + } + + // logger is initialized + g_logger_instance->save(incoming); + } + + /** Fatal call saved to logger. This will trigger SIGABRT or other fatal signal + * to exit the program. After saving the fatal message the calling thread + * will sleep forever (i.e. until the background thread catches up, saves the fatal + * message and kills the software with the fatal signal. + */ + void pushFatalMessageToLogger(FatalMessagePtr message) { + if (!isLoggingInitialized()) { + std::ostringstream error; + error << "FATAL CALL but logger is NOT initialized\n" + << "CAUSE: " << message.get()->reason() + << "\nMessage: \n" + << message.get()->toString() << std::flush; + std::cerr << error.str() << std::flush; + internal::exitWithDefaultSignalHandler(message.get()->_level, message.get()->_signal_id); + } + g_logger_instance->fatal(message); + while (shouldBlockForFatalHandling()) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + } + + /** The default, initial, handling to send a 'fatal' event to g3logworker + * the caller will stay here, eternally, until the software is aborted + * ... in the case of unit testing it is the given "Mock" fatalCall that will + * define the behaviour. + */ + void fatalCall(FatalMessagePtr message) { + g_fatal_to_g3logworker_function_ptr(FatalMessagePtr{std::move(message)}); + } + + } // namespace internal +} // namespace g3 diff --git a/PeakMenu/g3log/g3log/active.hpp b/PeakMenu/g3log/g3log/active.hpp new file mode 100644 index 0000000..3711f9d --- /dev/null +++ b/PeakMenu/g3log/g3log/active.hpp @@ -0,0 +1,67 @@ +/** ========================================================================== + * 2010 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes + * with no warranties. This code is yours to share, use and modify with no + * strings attached and no restrictions or obligations. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org + * ============================================================================ + * + * Example of a Active Object, using C++11 std::thread mechanisms to make it + * safe for thread communication. + * + * This was originally published at http://sites.google.com/site/kjellhedstrom2/active-object-with-cpp0x + * and inspired from Herb Sutter's C++11 Active Object + * http://herbsutter.com/2010/07/12/effective-concurrency-prefer-using-active-objects-instead-of-naked-threads + * + * Last update 2013-12-19 by Kjell Hedstrom, + * e-mail: hedstrom at kjellkod dot cc + * linkedin: http://linkedin.com/se/kjellkod */ + +#pragma once + +#include +#include +#include +#include "g3log/shared_queue.hpp" + +namespace kjellkod { + typedef std::function Callback; + + class Active { + private: + Active() : + done_(false) {} // Construction ONLY through factory createActive(); + Active(const Active&) = delete; + Active& operator=(const Active&) = delete; + + void run() { + while (!done_) { + Callback func; + mq_.wait_and_pop(func); + func(); + } + } + + shared_queue mq_; + std::thread thd_; + bool done_; + + public: + virtual ~Active() { + send([this]() noexcept { done_ = true; }); + thd_.join(); + } + + void send(Callback msg_) { + mq_.push(msg_); + } + + /// Factory: safe construction of object before thread start + static std::unique_ptr createActive() { + std::unique_ptr aPtr(new Active()); + aPtr->thd_ = std::thread(&Active::run, aPtr.get()); + return aPtr; + } + }; + +} // namespace kjellkod diff --git a/PeakMenu/g3log/g3log/atomicbool.hpp b/PeakMenu/g3log/g3log/atomicbool.hpp new file mode 100644 index 0000000..d6e51fb --- /dev/null +++ b/PeakMenu/g3log/g3log/atomicbool.hpp @@ -0,0 +1,47 @@ +/** ========================================================================== +* 2015 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes +* with no warranties. This code is yours to share, use and modify with no +* strings attached and no restrictions or obligations. +* +* For more information see g3log/LICENSE or refer refer to http://unlicense.org +* ============================================================================*/ + +#pragma once + +#include + +namespace g3 { + /// As suggested in: http://stackoverflow.com/questions/13193484/how-to-declare-a-vector-of-atomic-in-c + struct atomicbool { + private: + std::atomic value_; + + public: + atomicbool() : + value_{false} {} + atomicbool(bool value) : + value_{value} {} + atomicbool(const std::atomic& value) : + value_{value.load(std::memory_order_acquire)} {} + atomicbool(const atomicbool& other) : + value_{other.value_.load(std::memory_order_acquire)} {} + + atomicbool& operator=(const atomicbool& other) { + value_.store(other.value_.load(std::memory_order_acquire), std::memory_order_release); + return *this; + } + + atomicbool& operator=(const bool other) { + value_.store(other, std::memory_order_release); + return *this; + } + + bool operator==(const atomicbool& rhs) const { + return (value_.load(std::memory_order_acquire) == rhs.value_.load(std::memory_order_acquire)); + } + + bool value() { return value_.load(std::memory_order_acquire); } + std::atomic& get() { return value_; } + }; +} // namespace g3 + // explicit whitespace/EOF for VS15 \ No newline at end of file diff --git a/PeakMenu/g3log/g3log/crashhandler.hpp b/PeakMenu/g3log/g3log/crashhandler.hpp new file mode 100644 index 0000000..bf355f3 --- /dev/null +++ b/PeakMenu/g3log/g3log/crashhandler.hpp @@ -0,0 +1,81 @@ +#pragma once + +/** ========================================================================== + * 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes + * with no warranties. This code is yours to share, use and modify with no + * strings attached and no restrictions or obligations. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org + * ============================================================================*/ +#include +#include +#include +#include "g3log/loglevels.hpp" + +// kjell. Separera på crashhandler.hpp och crashhanlder_internal.hpp +// implementationsfilen kan vara den samma +namespace g3 { + + // PUBLIC API: + /** Install signal handler that catches FATAL C-runtime or OS signals + See the wikipedia site for details http://en.wikipedia.org/wiki/SIGFPE + See the this site for example usage: http://www.tutorialspoint.com/cplusplus/cpp_signal_handling + SIGABRT ABORT (ANSI), abnormal termination + SIGFPE Floating point exception (ANSI) + SIGILL ILlegal instruction (ANSI) + SIGSEGV Segmentation violation i.e. illegal memory reference + SIGTERM TERMINATION (ANSI) */ + void installCrashHandler(); + +#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) + typedef unsigned long SignalType; + /// SIGFPE, SIGILL, and SIGSEGV handling must be installed per thread + /// on Windows. This is automatically done if you do at least one LOG(...) call + /// you can also use this function call, per thread so make sure these three + /// fatal signals are covered in your thread (even if you don't do a LOG(...) call + void installSignalHandlerForThread(); +#else + typedef int SignalType; + std::string signalToStr(int signal_number); + + // restore to whatever signal handler was used before signal handler installation + void restoreSignalHandler(int signal_number); + + /// Overrides the existing signal handling for custom signals + /// For example: usage of zcmq relies on its own signal handler for SIGTERM + /// so users of g3log with zcmq should then use the @ref overrideSetupSignals + /// , likely with the original set of signals but with SIGTERM removed + /// + /// call example: + /// g3::overrideSetupSignals({ {SIGABRT, "SIGABRT"}, {SIGFPE, "SIGFPE"},{SIGILL, "SIGILL"}, + // {SIGSEGV, "SIGSEGV"},}); + void overrideSetupSignals(const std::map overrideSignals); +#endif + + namespace internal { + /// Resets the fatal signal/exception handling back to default + /// which might be needed in case it was previously overridden + /// The default signals are: SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGTERM + void restoreFatalHandlingToDefault(); + + /** return whether or any fatal handling is still ongoing + * this is used by g3log::fatalCallToLogger + * only in the case of Windows exceptions (not fatal signals) + * are we interested in changing this from false to true to + * help any other exceptions handler work with 'EXCEPTION_CONTINUE_SEARCH'*/ + bool shouldBlockForFatalHandling(); + + /** \return signal_name Ref: signum.hpp and \ref installSignalHandler + * or for Windows exception name */ + std::string exitReasonName(const LEVELS& level, g3::SignalType signal_number); + + /** return calling thread's stackdump*/ + std::string stackdump(const char* dump = nullptr); + + /** Re-"throw" a fatal signal, previously caught. This will exit the application + * This is an internal only function. Do not use it elsewhere. It is triggered + * from g3log, g3LogWorker after flushing messages to file */ + void exitWithDefaultSignalHandler(const LEVELS& level, g3::SignalType signal_number); + size_t writeErrorMessage(const char* message); + } // namespace internal +} // namespace g3 diff --git a/PeakMenu/g3log/g3log/filesink.hpp b/PeakMenu/g3log/g3log/filesink.hpp new file mode 100644 index 0000000..af39e51 --- /dev/null +++ b/PeakMenu/g3log/g3log/filesink.hpp @@ -0,0 +1,46 @@ +/** ========================================================================== + * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes + * with no warranties. This code is yours to share, use and modify with no + * strings attached and no restrictions or obligations. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org + * ============================================================================*/ +#pragma once + +#include +#include + +#include "g3log/logmessage.hpp" +namespace g3 { + + class FileSink { + public: + FileSink(const std::string& log_prefix, const std::string& log_directory, const std::string& logger_id = "g3log", size_t write_to_log_every_x_message = 100); + virtual ~FileSink(); + + void fileWrite(LogMessageMover message); + std::string changeLogFile(const std::string& directory, const std::string& logger_id); + std::string fileName(); + void overrideLogDetails(LogMessage::LogDetailsFunc func); + void overrideLogHeader(const std::string& change); + + private: + LogMessage::LogDetailsFunc _log_details_func; + std::string _log_file_with_path; + std::string _log_prefix_backup; // needed in case of future log file changes of directory + std::unique_ptr _outptr; + std::string _header; + bool _firstEntry; + std::string _write_buffer; + size_t _write_counter; + size_t _write_to_log_every_x_message; + + void addLogFileHeader(); + std::ofstream& filestream() { + return *(_outptr.get()); + } + + FileSink& operator=(const FileSink&) = delete; + FileSink(const FileSink& other) = delete; + }; +} // namespace g3 diff --git a/PeakMenu/g3log/g3log/future.hpp b/PeakMenu/g3log/g3log/future.hpp new file mode 100644 index 0000000..568a163 --- /dev/null +++ b/PeakMenu/g3log/g3log/future.hpp @@ -0,0 +1,60 @@ +#pragma once +/** ========================================================================== +* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes +* with no warranties. This code is yours to share, use and modify with no +* strings attached and no restrictions or obligations. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org +* ============================================================================ +* Filename:g3future.hpp +* Helper functionality to put packaged_tasks in standard container. This +* is especially helpful for background thread processing a la async but through +* an actor pattern (active object), thread pool or similar. +* Created: 2012 by Kjell Hedström +* +* COMMUNITY THANKS: +* The code below is in large thanks to exemplifying code snippets from StackOverflow +* question/answer: http://stackoverflow.com/questions/6230893/developing-c-concurrency-library-with-futures-or-similar-paradigm +* and a discussion between Lars Gullik Bjønnes and Jonathan Wakely's at: http://gcc.gnu.org/ml/gcc-help/2011-11/msg00052.html +* +* Both are highly recommended reads if you are interested in c++ concurrency library +* - Kjell, 2012 +* +* PUBLIC DOMAIN and NOT under copywrite protection. +* ********************************************* */ + +#include +#include "g3log/active.hpp" +#include "g3log/moveoncopy.hpp" +#include "g3log/stlpatch_future.hpp" + +namespace g3 { + // Generic helper function to avoid repeating the steps for managing + // asynchronous task job (by active object) that returns a future results + // could of course be made even more generic if done more in the way of + // std::async, ref: http://en.cppreference.com/w/cpp/thread/async + // + // Example usage: + // std::unique_ptr bgWorker{Active::createActive()}; + // ... + // auto msg_call=[=](){return ("Hello from the Background");}; + // auto future_msg = g3::spawn_task(msg_lambda, bgWorker.get()); + template + std::future> spawn_task(Func func, BgWorker* worker) { + typedef std::invoke_result_t result_type; + typedef std::packaged_task task_type; + + if (nullptr == worker) { + auto p = std::make_shared>(); + std::future future_result = p->get_future(); + p->set_exception(std::make_exception_ptr(std::runtime_error("nullptr instantiated worker"))); + return future_result; + } + + task_type task(std::move(func)); + + std::future result = task.get_future(); + worker->send(MoveOnCopy(std::move(task))); + return result; + } +} // end namespace g3 diff --git a/PeakMenu/g3log/g3log/g3log.hpp b/PeakMenu/g3log/g3log/g3log.hpp new file mode 100644 index 0000000..c5f7f6f --- /dev/null +++ b/PeakMenu/g3log/g3log/g3log.hpp @@ -0,0 +1,226 @@ +/** ========================================================================== + * 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes + * with no warranties. This code is yours to share, use and modify with no + * strings attached and no restrictions or obligations. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org + * ============================================================================ + * + * Filename:g3log.hpp Framework for Logging and Design By Contract + * Created: 2011 by Kjell Hedström + * + * PUBLIC DOMAIN and Not copywrited since it was built on public-domain software and influenced + * at least in "spirit" from the following sources + * 1. kjellkod.cc ;) + * 2. Dr.Dobbs, Petru Marginean: http://drdobbs.com/article/printableArticle.jhtml?articleId=201804215&dept_url=/caddpp/ + * 3. Dr.Dobbs, Michael Schulze: http://drdobbs.com/article/printableArticle.jhtml?articleId=225700666&dept_url=/cpp/ + * 4. Google 'glog': http://google-glog.googlecode.com/svn/trunk/doc/glog.html + * 5. Various Q&A at StackOverflow + * ********************************************* */ + +#pragma once + +#include "g3log/logcapture.hpp" +#include "g3log/loglevels.hpp" +#include "g3log/logmessage.hpp" + +#include +#include + +#if defined(_MSC_VER) && (defined(WINDOWS_FUNCSIG)) // Microsoft +#define G3LOG_PRETTY_FUNCTION __FUNCSIG__ +#elif defined(__GNUC__) && defined(PRETTY_FUNCTION) // GCC compatible +#define G3LOG_PRETTY_FUNCTION __PRETTY_FUNCTION__ +#else +#define G3LOG_PRETTY_FUNCTION __FUNCTION__ +#endif + +// thread_local doesn't exist before VS2013 +// it exists on VS2015 +#if !(defined(thread_local)) && defined(_MSC_VER) && _MSC_VER < 1900 +#define thread_local __declspec(thread) +#endif + +/** namespace for LOG() and CHECK() frameworks + * History lesson: Why the names 'g3' and 'g3log'?: + * The framework was made in my own free time as PUBLIC DOMAIN but the + * first commercial project to use it used 'g3' as an internal denominator for + * the current project. g3 as in 'generation 2'. I decided to keep the g3 and g3log names + * to give credit to the people in that project (you know who you are :) and I guess also + * for 'sentimental' reasons. That a big influence was Google's glog is just a happy + * coincidence or subconscious choice. Either way g3log became the name for this logger. + * + * --- Thanks for a great 2011 and good luck with 'g3' --- KjellKod + */ +namespace g3 { + class LogWorker; + struct LogMessage; + struct FatalMessage; + + /** Should be called at very first startup of the software with \ref g3LogWorker + * pointer. Ownership of the \ref g3LogWorker is the responsibility of the caller */ + void initializeLogging(LogWorker* logger); + + /** setFatalPreLoggingHook() provides an optional extra step before the fatalExitHandler is called + * + * Set a function-hook before a fatal message will be sent to the logger + * i.e. this is a great place to put a break point, either in your debugger + * or programmatically to catch LOG(FATAL), CHECK(...) or an OS fatal event (exception or signal) + * This will be reset to default (does nothing) at initializeLogging(...); + * + * Example usage: + * Windows: g3::setFatalPreLoggingHook([]{__debugbreak();}); // remember #include + * WARNING: '__debugbreak()' when not running in Debug in your Visual Studio IDE will likely + * trigger a recursive crash if used here. It should only be used when debugging + * in your Visual Studio IDE. Recursive crashes are handled but are unnecessary. + * + * Linux: g3::setFatalPreLoggingHook([]{ raise(SIGTRAP); }); + */ + void setFatalPreLoggingHook(std::function pre_fatal_hook); + + /** If the @ref setFatalPreLoggingHook is not enough and full fatal exit handling is needed then + * use "setFatalExithandler". Please see g3log.cpp and crashhandler_windows.cpp or crashhandler_unix for + * example of restoring signal and exception handlers, flushing the log and shutting down. + */ + void setFatalExitHandler(std::function fatal_call); + +#ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE + // only_change_at_initialization namespace is for changes to be done only during initialization. More specifically + // items here would be called prior to calling other parts of g3log + namespace only_change_at_initialization { + // Sets the MaxMessageSize to be used when capturing log messages. Currently this value is set to 2KB. Messages + // Longer than this are bound to 2KB with the string "[...truncated...]" at the end. This function allows + // this limit to be changed. + void setMaxMessageSize(size_t max_size); + } // namespace only_change_at_initialization +#endif /* G3_DYNAMIC_MAX_MESSAGE_SIZE */ + + // internal namespace is for completely internal or semi-hidden from the g3 namespace due to that it is unlikely + // that you will use these + namespace internal { + /// @returns true if logger is initialized + bool isLoggingInitialized(); + + // Save the created LogMessage to any existing sinks + void saveMessage(const char* message, const char* file, int line, const char* function, const LEVELS& level, + const char* boolean_expression, int fatal_signal, const char* stack_trace); + + void saveFatalMessage(const char* stack_trace, g3::LogMessagePtr& message, int& fatal_signal); + + // forwards the message to all sinks + void pushMessageToLogger(LogMessagePtr log_entry); + + // forwards a FATAL message to all sinks,. after which the g3logworker + // will trigger crashhandler / g3::internal::exitWithDefaultSignalHandler + // + // By default the "fatalCall" will forward a FatalMessageptr to this function + // this behavior can be changed if you set a different fatal handler through + // "setFatalExitHandler" + void pushFatalMessageToLogger(FatalMessagePtr message); + + // Saves the created FatalMessage to any existing sinks and exits with + // the originating fatal signal,. or SIGABRT if it originated from a broken contract. + // By default forwards to: pushFatalMessageToLogger, see "setFatalExitHandler" to override + // + // If you override it then you probably want to call "pushFatalMessageToLogger" after your + // custom fatal handler is done. This will make sure that the fatal message the pushed + // to sinks as well as shutting down the process + void fatalCall(FatalMessagePtr message); + + // Shuts down logging. No object cleanup but further LOG(...) calls will be ignored. + void shutDownLogging(); + + // Shutdown logging, but ONLY if the active logger corresponds to the one currently initialized + bool shutDownLoggingForActiveOnly(LogWorker* active); + + } // namespace internal +} // namespace g3 + // clang-format off +#define INTERNAL_LOG_MESSAGE(level) LogCapture(__FILE__, __LINE__, static_cast(G3LOG_PRETTY_FUNCTION), level) + +#define INTERNAL_CONTRACT_MESSAGE(boolean_expression) \ + LogCapture(__FILE__, __LINE__, G3LOG_PRETTY_FUNCTION, g3::internal::CONTRACT, boolean_expression) + + +// LOG(level) is the API for the stream log +#define LOG(level) if (!g3::logLevel(level)) {} else INTERNAL_LOG_MESSAGE(level).stream() + + +// 'Conditional' stream log +#define LOG_IF(level, boolean_expression) \ + if (!g3::logLevel(level) || false == (boolean_expression)) {} else INTERNAL_LOG_MESSAGE(level).stream() + +// 'Design By Contract' stream API. Broken Contracts will exit the application by using fatal signal SIGABRT +// For unit testing, you can override the fatal handling using setFatalExitHandler(...). See tes_io.cpp for examples +#define CHECK(boolean_expression) \ + if (true == (boolean_expression)) {} else INTERNAL_CONTRACT_MESSAGE(#boolean_expression).stream() + + +/** For details please see this + * REFERENCE: http://www.cppreference.com/wiki/io/c/printf_format + * \verbatim + * + There are different %-codes for different variable types, as well as options to + limit the length of the variables and whatnot. + Code Format + %[flags][width][.precision][length]specifier + SPECIFIERS + ---------- + %c character + %d signed integers + %i signed integers + %e scientific notation, with a lowercase “e” + %E scientific notation, with a uppercase “E” + %f floating point + %g use %e or %f, whichever is shorter + %G use %E or %f, whichever is shorter + %o octal + %s a string of characters + %u unsigned integer + %x unsigned hexadecimal, with lowercase letters + %X unsigned hexadecimal, with uppercase letters + %p a pointer + %n the argument shall be a pointer to an integer into which is placed the number of characters written so far + +For flags, width, precision etc please see the above references. +EXAMPLES: +{ + LOGF(INFO, "Characters: %c %c \n", 'a', 65); + LOGF(INFO, "Decimals: %d %ld\n", 1977, 650000L); // printing long + LOGF(INFO, "Preceding with blanks: %10d \n", 1977); + LOGF(INFO, "Preceding with zeros: %010d \n", 1977); + LOGF(INFO, "Some different radixes: %d %x %o %#x %#o \n", 100, 100, 100, 100, 100); + LOGF(INFO, "floats: %4.2f %+.0e %E \n", 3.1416, 3.1416, 3.1416); + LOGF(INFO, "Width trick: %*d \n", 5, 10); + LOGF(INFO, "%s \n", "A string"); + return 0; +} +And here is possible output +: Characters: a A +: Decimals: 1977 650000 +: Preceding with blanks: 1977 +: Preceding with zeros: 0000001977 +: Some different radixes: 100 64 144 0x64 0144 +: floats: 3.14 +3e+000 3.141600E+000 +: Width trick: 10 +: A string \endverbatim */ +#define LOGF(level, printf_like_message, ...) \ + if (!g3::logLevel(level)) {} else INTERNAL_LOG_MESSAGE(level).capturef(printf_like_message, ##__VA_ARGS__) + +// Conditional log printf syntax +#define LOGF_IF(level,boolean_expression, printf_like_message, ...) \ + if (!g3::logLevel(level) || false == (boolean_expression)) {} else INTERNAL_LOG_MESSAGE(level).capturef(printf_like_message, ##__VA_ARGS__) + +// Design By Contract, printf-like API syntax with variadic input parameters. +// Calls the signal handler if the contract failed with the default exit for a failed contract. This is typically SIGABRT +// See g3log, setFatalExitHandler(...) which can be overriden for unit tests (ref test_io.cpp) +#define CHECKF(boolean_expression, printf_like_message, ...) \ + if (true == (boolean_expression)) {} else INTERNAL_CONTRACT_MESSAGE(#boolean_expression).capturef(printf_like_message, ##__VA_ARGS__) + +// Backwards compatible. The same as CHECKF. +// Design By Contract, printf-like API syntax with variadic input parameters. +// Calls the signal handler if the contract failed. See g3log, setFatalExitHandler(...) which can be overriden for unit tests +// (ref test_io.cpp) +#define CHECK_F(boolean_expression, printf_like_message, ...) \ + if (true == (boolean_expression)) {} else INTERNAL_CONTRACT_MESSAGE(#boolean_expression).capturef(printf_like_message, ##__VA_ARGS__) + // clang-format on \ No newline at end of file diff --git a/PeakMenu/g3log/g3log/logcapture.hpp b/PeakMenu/g3log/g3log/logcapture.hpp new file mode 100644 index 0000000..a28101a --- /dev/null +++ b/PeakMenu/g3log/g3log/logcapture.hpp @@ -0,0 +1,75 @@ +/** ========================================================================== + * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes + * with no warranties. This code is yours to share, use and modify with no + * strings attached and no restrictions or obligations. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org + * ============================================================================*/ + +#pragma once + +#include "g3log/crashhandler.hpp" +#include "g3log/loglevels.hpp" + +#include +#include +#include +#include +#ifdef _MSC_VER +#include +#endif + +/** + * Simple struct for capturing log/fatal entries. At destruction the captured message is + * forwarded to background worker. + * As a safety precaution: No memory allocated here will be moved into the background + * worker in case of dynamic loaded library reasons +*/ +struct LogCapture { + /// Called from crash handler when a fatal signal has occurred (SIGSEGV etc) + LogCapture(const LEVELS& level, g3::SignalType fatal_signal, const char* dump = nullptr); + + /** + * @file, line, function are given in g3log.hpp from macros + * @level INFO/DEBUG/WARNING/FATAL + * @expression for CHECK calls + * @fatal_signal for failed CHECK:SIGABRT or fatal signal caught in the signal handler + */ + LogCapture(const char* file, const int line, const char* function, const LEVELS& level, const char* expression = "", g3::SignalType fatal_signal = SIGABRT, const char* dump = nullptr); + + // At destruction the message will be forwarded to the g3log worker. + // In the case of dynamically (at runtime) loaded libraries, the important thing to know is that + // all strings are copied, so the original are not destroyed at the receiving end, only the copy + virtual ~LogCapture() noexcept(false); + +#ifdef _MSC_VER +#if _MSC_VER >= 1400 +#define G3LOG_FORMAT_STRING _Printf_format_string_ +#else +#define G3LOG_FORMAT_STRING __format_string +#endif + + void capturef(G3LOG_FORMAT_STRING const char* printf_like_message, ...); +#else +#define G3LOG_FORMAT_STRING + + // Use "-Wall" to generate warnings in case of illegal printf format. + // Ref: http://www.unixwiz.net/techtips/gnu-c-attributes.html + [[gnu::format(printf, 2, 3)]] void capturef(G3LOG_FORMAT_STRING const char* printf_like_message, ...); // 2,3 ref: http://www.codemaestro.com/reviews/18 +#endif + + /// prettifying API for this completely open struct + std::ostringstream& stream() { + return _stream; + } + + std::ostringstream _stream; + std::string _stack_trace; + const char* _file; + const int _line; + const char* _function; + const LEVELS& _level; + const char* _expression; + const g3::SignalType _fatal_signal; +}; +//} // g3 diff --git a/PeakMenu/g3log/g3log/loglevels.hpp b/PeakMenu/g3log/g3log/loglevels.hpp new file mode 100644 index 0000000..862a3ba --- /dev/null +++ b/PeakMenu/g3log/g3log/loglevels.hpp @@ -0,0 +1,194 @@ +/** ========================================================================== +* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes +* with no warranties. This code is yours to share, use and modify with no +* strings attached and no restrictions or obligations. +* +* For more information see g3log/LICENSE or refer refer to http://unlicense.org +* ============================================================================*/ + +#pragma once + +// Users of Juce or other libraries might have a define DEBUG which clashes with +// the DEBUG logging level for G3log. In that case they can instead use the define +// "CHANGE_G3LOG_DEBUG_TO_DBUG" and G3log's logging level DEBUG is changed to be DBUG +#if (defined(CHANGE_G3LOG_DEBUG_TO_DBUG)) +#if (defined(DBUG)) +#error "DBUG is already defined elsewhere which clashes with G3Log's log level DBUG" +#endif +#else +#if (defined(DEBUG)) +#error "DEBUG is already defined elsewhere which clashes with G3Log's log level DEBUG" +#endif +#endif + +#include +#include +#include +#include +#include + +// Levels for logging, made so that it would be easy to change, remove, add levels -- KjellKod +struct LEVELS { + // force internal copy of the const char*. This is a simple safeguard for when g3log is used in a + // "dynamic, runtime loading of shared libraries" + + LEVELS(const LEVELS& other) : + value(other.value), + text(other.text.c_str()) {} + + LEVELS(int id, const std::string& idtext) : + value(id), + text(idtext) {} + + bool operator==(const LEVELS& rhs) const { + return (value == rhs.value && text == rhs.text); + } + + bool operator!=(const LEVELS& rhs) const { + return (value != rhs.value || text != rhs.text); + } + + friend void swap(LEVELS& first, LEVELS& second) { + using std::swap; + swap(first.value, second.value); + swap(first.text, second.text); + } + + LEVELS& operator=(LEVELS other) { + swap(*this, other); + return *this; + } + + int value; + std::string text; +}; + +// If you want to add any extra logging level then please add to your own source file the logging level you need +// 1. If the cmake option G3_DYNAMIC_LOGGING is enabled then you must use g3::only_change_at_initialization::addLogLevel(...). +// to give g3log a record of your logging level and if it is an enabled or disbled logging level. +// +// 2. If the cmake dynamic logging option is turned OFF +// then giving g3log a record of your logging level with 'addLogLevel(...) is NOT needed since no "disbled/enabled" +// check will happen - all logging levels will be considered enabled. +// 3. See also the [g3log/API.markdown](https://github.com/KjellKod/g3log/blob/master/API.markdown) for for information. +// +// example: MyLoggingLevel.h +// #pragma once +// const LEVELS MYINFO {WARNING.value +1, "MyInfoLevel"}; +// const LEVELS MYFATAL {FATAL.value +1, "MyFatalLevel"}; +// +// ... somewhere else when G3_DYNAMIC_LOGGING is enabled +// addLogLevel(MYINFO, true); +// LOG(MYINFO) << "some text"; +// +// ... another example, when G3_DYNAMIC_LOGGING is enabled +// 'addLogLevel' is NOT required +// LOG(MYFATAL) << "this will just work, and it will be counted as a FATAL event"; +namespace g3 { + static const int kDebugValue = 100; + static const int kInfoValue = 300; + static const int kWarningValue = 500; + static const int kFatalValue = 1000; + static const int kInternalFatalValue = 2000; +} // namespace g3 + +const LEVELS G3LOG_DEBUG{g3::kDebugValue, "DEBUG"}, + INFO{g3::kInfoValue, "INFO"}, + WARNING{g3::kWarningValue, "WARNING"}, + FATAL{g3::kFatalValue, "FATAL"}; + +namespace g3 { + // Logging level and atomic status collection struct + struct LoggingLevel { + atomicbool status; + LEVELS level; + + // default operator needed for std::map compliance + LoggingLevel() : + status(false), + level(INFO){}; + LoggingLevel(const LoggingLevel& lvl) : + status(lvl.status), + level(lvl.level) {} + LoggingLevel(const LEVELS& lvl) : + status(true), + level(lvl){}; + LoggingLevel(const LEVELS& lvl, bool enabled) : + status(enabled), + level(lvl){}; + ~LoggingLevel() = default; + + LoggingLevel& operator=(const LoggingLevel& other) { + status = other.status; + level = other.level; + return *this; + } + + bool operator==(const LoggingLevel& rhs) const { + return (status == rhs.status && level == rhs.level); + } + }; +} // namespace g3 + +namespace g3 { + namespace internal { + const LEVELS CONTRACT{g3::kInternalFatalValue, {"CONTRACT"}}, + FATAL_SIGNAL{g3::kInternalFatalValue + 1, {"FATAL_SIGNAL"}}, + FATAL_EXCEPTION{kInternalFatalValue + 2, {"FATAL_EXCEPTION"}}; + + /// helper function to tell the logger if a log message was fatal. If it is it will force + /// a shutdown after all log entries are saved to the sinks + bool wasFatal(const LEVELS& level); + } // namespace internal + +#ifdef G3_DYNAMIC_LOGGING + // Only safe if done at initialization in a single-thread context + namespace only_change_at_initialization { + + /// add a custom level - enabled or disabled + void addLogLevel(LEVELS level, bool enabled); + + /// add a custom level - enabled + void addLogLevel(LEVELS level); + + /// reset all default logging levels to enabled + /// remove any added logging levels so that the only ones left are + /// {DEBUG,INFO,WARNING,FATAL} + void reset(); + } // namespace only_change_at_initialization + + namespace log_levels { + /// Enable log level >= log_level. + /// log levels below will be disabled + /// log levels equal or higher will be enabled. + void setHighest(LEVELS level); + + void set(LEVELS level, bool enabled); + void disable(LEVELS level); + void enable(LEVELS level); + + /// WARNING: This will also disable FATAL events from being logged + void disableAll(); + void enableAll(); + + /// print all levels with their disabled or enabled status + std::string to_string(std::map levelsToPrint); + + /// print snapshot of system levels with their + /// disabled or enabled status + std::string to_string(); + + /// Snapshot view of the current logging levels' status + std::map getAll(); + + enum class status { Absent, + Enabled, + Disabled }; + status getStatus(LEVELS level); + } // namespace log_levels + +#endif + /// Enabled status for the given logging level + bool logLevel(const LEVELS& level); + +} // namespace g3 diff --git a/PeakMenu/g3log/g3log/logmessage.hpp b/PeakMenu/g3log/g3log/logmessage.hpp new file mode 100644 index 0000000..9fc2d23 --- /dev/null +++ b/PeakMenu/g3log/g3log/logmessage.hpp @@ -0,0 +1,148 @@ +/** ========================================================================== +* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes +* with no warranties. This code is yours to share, use and modify with no +* strings attached and no restrictions or obligations. +* +* For more information see g3log/LICENSE or refer refer to http://unlicense.org +* ============================================================================*/ + +#pragma once + +#include "g3log/crashhandler.hpp" +#include "g3log/loglevels.hpp" +#include "g3log/moveoncopy.hpp" +#include "g3log/time.hpp" + +#include +#include +#include +#include + +namespace g3 { + + /** LogMessage contains all the data collected from the LOG(...) call. + * If the sink receives a std::string it will be the std::string toString()... function + * that will format the data into a string + * + * For sinks that receive a LogMessage they can either use the toString() function, or use + * the helper functions or even the public raw data to format the saved log message any + * desired way. + */ + struct LogMessage { + std::string file_path() const { + return _file_path; + } + std::string file() const { + return _file; + } + std::string line() const { + return std::to_string(_line); + } + std::string function() const { + return _function; + } + std::string level() const { + return _level.text; + } + + /// use a different format string to get a different look on the time. + // default look is Y/M/D H:M:S + std::string timestamp(const std::string& time_format = {internal::date_formatted + " " + internal::time_formatted}) const; + + std::string message() const { + return _message; + } + std::string& write() const { + return _message; + } + + std::string expression() const { + return _expression; + } + bool wasFatal() const { + return internal::wasFatal(_level); + } + + std::string threadID() const; + + void setExpression(std::string expression) { + _expression = std::move(expression); + } + + LogMessage& operator=(LogMessage other); + + LogMessage(std::string file, const int line, std::string function, const LEVELS level); + + explicit LogMessage(const std::string& fatalOsSignalCrashMessage); + LogMessage(const LogMessage& other); + LogMessage(LogMessage&& other); + virtual ~LogMessage() {} + + // helper log printing functions used by "toString()" + static std::string splitFileName(const std::string& str); + static std::string fatalSignalToString(const LogMessage& msg); + // windows only: fatalExceptionToString + static std::string fatalExceptionToString(const LogMessage& msg); + static std::string fatalLogToString(const LogMessage& msg); + static std::string fatalCheckToString(const LogMessage& msg); + static std::string normalToString(const LogMessage& msg); + + // the default formatting option + static std::string DefaultLogDetailsToString(const LogMessage& msg); + + // this function can be used by the logging sink to add thread ID + // see this concept and it is easy to make your own custom formatting + static std::string FullLogDetailsToString(const LogMessage& msg); + + using LogDetailsFunc = std::string (*)(const LogMessage&); + std::string toString(LogDetailsFunc formattingFunc = DefaultLogDetailsToString) const; + + void overrideLogDetailsFunc(LogDetailsFunc func) const; + + // + // Complete access to the raw data in case the helper functions above + // are not enough. + // + mutable LogDetailsFunc _logDetailsToStringFunc; + g3::high_resolution_time_point _timestamp; + std::thread::id _call_thread_id; + std::string _file; + std::string _file_path; + int _line; + std::string _function; + LEVELS _level; + std::string _expression; // only with content for CHECK(...) calls + mutable std::string _message; + + friend void swap(LogMessage& first, LogMessage& second) { + using std::swap; + swap(first._timestamp, second._timestamp); + swap(first._call_thread_id, second._call_thread_id); + swap(first._file, second._file); + swap(first._line, second._line); + swap(first._function, second._function); + swap(first._level, second._level); + swap(first._expression, second._expression); + swap(first._message, second._message); + } + }; + + /** Trigger for flushing the message queue and exiting the application + * A thread that causes a FatalMessage will sleep forever until the + * application has exited (after message flush) */ + struct FatalMessage : public LogMessage { + FatalMessage(const LogMessage& details, g3::SignalType signal_id); + FatalMessage(const FatalMessage&); + FatalMessage& operator=(const FatalMessage&) = delete; + virtual ~FatalMessage() {} + + LogMessage copyToLogMessage() const; + std::string reason() const; + + const SignalType _signal_id; + }; + + typedef MoveOnCopy> FatalMessagePtr; + typedef MoveOnCopy> LogMessagePtr; + typedef MoveOnCopy LogMessageMover; +} // namespace g3 diff --git a/PeakMenu/g3log/g3log/logworker.hpp b/PeakMenu/g3log/g3log/logworker.hpp new file mode 100644 index 0000000..de0ba21 --- /dev/null +++ b/PeakMenu/g3log/g3log/logworker.hpp @@ -0,0 +1,143 @@ +#pragma once +/** ========================================================================== + * 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes + * with no warranties. This code is yours to share, use and modify with no + * strings attached and no restrictions or obligations. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org + * ============================================================================ + * Filename:g3logworker.h Framework for Logging and Design By Contract + * Created: 2011 by Kjell Hedström + * + * PUBLIC DOMAIN and Not copyrighted. First published at KjellKod.cc + * ********************************************* */ +#include +#include "g3log/filesink.hpp" +#include "g3log/g3log.hpp" +#include "g3log/logmessage.hpp" +#include "g3log/sinkhandle.hpp" +#include "g3log/sinkwrapper.hpp" + +#include +#include +#include + +namespace g3 { + class LogWorker; + struct LogWorkerImpl; + using FileSinkHandle = g3::SinkHandle; + + /// Background side of the LogWorker. Internal use only + struct LogWorkerImpl final { + typedef std::shared_ptr SinkWrapperPtr; + std::vector _sinks; + std::unique_ptr _bg; // do not change declaration order. _bg must be destroyed before sinks + + LogWorkerImpl(); + ~LogWorkerImpl() = default; + + void bgSave(g3::LogMessagePtr msgPtr); + void bgFatal(FatalMessagePtr msgPtr); + + LogWorkerImpl(const LogWorkerImpl&) = delete; + LogWorkerImpl& operator=(const LogWorkerImpl&) = delete; + }; + + /// Front end of the LogWorker. API that is useful is + /// addSink( sink, default_call ) which returns a handle to the sink. See below and README for usage example + /// save( msg ) : internal use + /// fatal ( fatal_msg ) : internal use + class LogWorker final { + LogWorker() = default; + void addWrappedSink(std::shared_ptr wrapper); + + LogWorkerImpl _impl; + LogWorker(const LogWorker&) = delete; + LogWorker& operator=(const LogWorker&) = delete; + + public: + ~LogWorker(); + + /// Creates the LogWorker with no sinks. See example below on @ref addSink for how to use it + /// if you want to use the default file logger then see below for @ref addDefaultLogger + static std::unique_ptr createLogWorker(); + + /** + A convenience function to add the default g3::FileSink to the log worker + @param log_prefix that you want + @param log_directory where the log is to be stored. + @return a handle for API access to the sink. See the README for example usage + + @verbatim + Example: + using namespace g3; + std::unique_ptr logworker {LogWorker::createLogWorker()}; + auto handle = addDefaultLogger("my_test_log", "/tmp"); + initializeLogging(logworker.get()); // ref. g3log.hpp + + std::future log_file_name = sinkHandle->call(&FileSink::fileName); + std::cout << "The filename is: " << log_file_name.get() << std::endl; + // something like: /tmp/my_test_log.g3log.20150819-100300.log + */ + std::unique_ptr addDefaultLogger(const std::string& log_prefix, const std::string& log_directory, const std::string& default_id = "g3log"); + + /// Adds a sink and returns the handle for access to the sink + /// @param real_sink unique_ptr ownership is passed to the log worker + /// @param call the default call that should receive either a std::string or a LogMessageMover message + /// @return handle to the sink for API access. See usage example below at @ref addDefaultLogger + template + std::unique_ptr> addSink(std::unique_ptr real_sink, DefaultLogCall call) { + using namespace g3; + using namespace g3::internal; + auto sink = std::make_shared>(std::move(real_sink), call); + addWrappedSink(sink); + return std::make_unique>(sink); + } + + /// Removes a sink. This is a synchronous call. + /// You are guaranteed that the sink is removed by the time the call returns + /// @param sink_handle the ownership of the sink handle is given + template + void removeSink(std::unique_ptr> sink_handle) { + if (sink_handle) { + // sink_handle->sink().use_count() is 1 at this point + // i.e. this would be safe as long as no other weak_ptr to shared_ptr conversion + // was made by the client: assert(sink_handle->sink().use_count() == 0); + auto weak_ptr_sink = sink_handle->sink(); + { + auto bg_removesink_call = [this, weak_ptr_sink] { + auto shared_sink = weak_ptr_sink.lock(); + if (shared_sink) { + _impl._sinks.erase(std::remove(_impl._sinks.begin(), _impl._sinks.end(), shared_sink), _impl._sinks.end()); + } + }; + auto token_done = g3::spawn_task(bg_removesink_call, _impl._bg.get()); + token_done.wait(); + } + // sink_handle->sink().use_count() is 1 at this point. + // i.e. this would be safe: assert(sink_handle->sink().use_count() == 0); + // as long as the client has not converted more instances from the weak_ptr + } + } + + /// This will clear/remove all the sinks. If a sink shared_ptr was retrieved via the sink + /// handle then the sink will be removed internally but will live on in the client's instance + void removeAllSinks() { + auto bg_clear_sink_call = [this]() noexcept { + _impl._sinks.clear(); + }; + auto token_cleared = g3::spawn_task(bg_clear_sink_call, _impl._bg.get()); + token_cleared.wait(); + } + + /// internal: + /// pushes in background thread (asynchronously) input messages to log file + void save(LogMessagePtr entry); + + /// internal: + // pushes a fatal message on the queue, this is the last message to be processed + /// this way it's ensured that all existing entries were flushed before 'fatal' + /// Will abort the application! + void fatal(FatalMessagePtr fatal_message); + }; +} // namespace g3 diff --git a/PeakMenu/g3log/g3log/moveoncopy.hpp b/PeakMenu/g3log/g3log/moveoncopy.hpp new file mode 100644 index 0000000..32a44b3 --- /dev/null +++ b/PeakMenu/g3log/g3log/moveoncopy.hpp @@ -0,0 +1,51 @@ +/** ========================================================================== +* 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes +* with no warranties. This code is yours to share, use and modify with no +* strings attached and no restrictions or obligations. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org +* ============================================================================*/ + +#pragma once +namespace g3 { + + // A straightforward technique to move around packaged_tasks. + // Instances of std::packaged_task are MoveConstructible and MoveAssignable, but + // not CopyConstructible or CopyAssignable. To put them in a std container they need + // to be wrapped and their internals "moved" when tried to be copied. + + template + struct MoveOnCopy { + mutable Moveable _move_only; + + explicit MoveOnCopy(Moveable&& m) : + _move_only(std::move(m)) {} + MoveOnCopy(MoveOnCopy const& t) : + _move_only(std::move(t._move_only)) {} + MoveOnCopy(MoveOnCopy&& t) : + _move_only(std::move(t._move_only)) {} + + MoveOnCopy& operator=(MoveOnCopy const& other) { + _move_only = std::move(other._move_only); + return *this; + } + + MoveOnCopy& operator=(MoveOnCopy&& other) { + _move_only = std::move(other._move_only); + return *this; + } + + void operator()() { + _move_only(); + } + + Moveable& get() { + return _move_only; + } + + Moveable release() { + return std::move(_move_only); + } + }; + +} // namespace g3 diff --git a/PeakMenu/g3log/g3log/shared_queue.hpp b/PeakMenu/g3log/g3log/shared_queue.hpp new file mode 100644 index 0000000..82a73d9 --- /dev/null +++ b/PeakMenu/g3log/g3log/shared_queue.hpp @@ -0,0 +1,78 @@ +/** ========================================================================== +* 2010 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes +* with no warranties. This code is yours to share, use and modify with no +* strings attached and no restrictions or obligations. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org +* ============================================================================ +* +* Example of a normal std::queue protected by a mutex for operations, +* making it safe for thread communication, using std::mutex from C++0x with +* the help from the std::thread library from JustSoftwareSolutions +* ref: http://www.stdthread.co.uk/doc/headers/mutex.html +* +* This example was totally inspired by Anthony Williams lock-based data structures in +* Ref: "C++ Concurrency In Action" http://www.manning.com/williams */ + +#pragma once + +#include +#include +#include +#include + +/** Multiple producer, multiple consumer thread safe queue +* Since 'return by reference' is used this queue won't throw */ +template +class shared_queue { + std::queue queue_; + mutable std::mutex m_; + std::condition_variable data_cond_; + + shared_queue& operator=(const shared_queue&) = delete; + shared_queue(const shared_queue& other) = delete; + + public: + shared_queue() = default; + + void push(T item) { + { + std::lock_guard lock(m_); + queue_.push(std::move(item)); + } + data_cond_.notify_one(); + } + + /// \return immediately, with true if successful retrieval + bool try_and_pop(T& popped_item) { + std::lock_guard lock(m_); + if (queue_.empty()) { + return false; + } + popped_item = std::move(queue_.front()); + queue_.pop(); + return true; + } + + /// Try to retrieve, if no items, wait till an item is available and try again + void wait_and_pop(T& popped_item) { + std::unique_lock lock(m_); + while (queue_.empty()) { + data_cond_.wait(lock); + // This 'while' loop is equal to + // data_cond_.wait(lock, [](bool result){return !queue_.empty();}); + } + popped_item = std::move(queue_.front()); + queue_.pop(); + } + + bool empty() const { + std::lock_guard lock(m_); + return queue_.empty(); + } + + unsigned size() const { + std::lock_guard lock(m_); + return queue_.size(); + } +}; diff --git a/PeakMenu/g3log/g3log/sink.hpp b/PeakMenu/g3log/g3log/sink.hpp new file mode 100644 index 0000000..61f8850 --- /dev/null +++ b/PeakMenu/g3log/g3log/sink.hpp @@ -0,0 +1,76 @@ +/** ========================================================================== +* 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes +* with no warranties. This code is yours to share, use and modify with no +* strings attached and no restrictions or obligations. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org +* ============================================================================*/ + +#pragma once + +#include "g3log/active.hpp" +#include "g3log/future.hpp" +#include "g3log/logmessage.hpp" +#include "g3log/sinkwrapper.hpp" + +#include +#include +#include + +namespace g3 { + namespace internal { + typedef std::function AsyncMessageCall; + + /// The asynchronous Sink has an active object, incoming requests for actions + // will be processed in the background by the specific object the Sink represents. + // + // The Sink will wrap either + // a Sink with Message object receiving call + // or a Sink with a LogEntry (string) receiving call + // + // The Sink can also be used through the SinkHandler to call Sink specific function calls + // Ref: send(Message) deals with incoming log entries (converted if necessary to string) + // Ref: send(Call call, Args... args) deals with calls + // to the real sink's API + + template + struct Sink : public SinkWrapper { + std::unique_ptr _real_sink; + std::unique_ptr _bg; + AsyncMessageCall _default_log_call; + + template + Sink(std::unique_ptr sink, DefaultLogCall call) : + SinkWrapper(), + _real_sink{std::move(sink)}, + _bg(kjellkod::Active::createActive()), + _default_log_call(std::bind(call, _real_sink.get(), std::placeholders::_1)) { + } + + Sink(std::unique_ptr sink, void (T::*Call)(std::string)) : + SinkWrapper(), + _real_sink{std::move(sink)}, + _bg(kjellkod::Active::createActive()) { + std::function adapter = std::bind(Call, _real_sink.get(), std::placeholders::_1); + _default_log_call = [=](LogMessageMover m) { + adapter(m.get().toString()); + }; + } + + virtual ~Sink() { + _bg.reset(); // TODO: to remove + } + + void send(LogMessageMover msg) override { + _bg->send([this, msg] { + _default_log_call(msg); + }); + } + + template + auto async(Call call, Args&&... args) -> std::future> { + return g3::spawn_task(std::bind(call, _real_sink.get(), std::forward(args)...), _bg.get()); + } + }; + } // namespace internal +} // namespace g3 diff --git a/PeakMenu/g3log/g3log/sinkhandle.hpp b/PeakMenu/g3log/g3log/sinkhandle.hpp new file mode 100644 index 0000000..34848d2 --- /dev/null +++ b/PeakMenu/g3log/g3log/sinkhandle.hpp @@ -0,0 +1,59 @@ +/** ========================================================================== +* 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes +* with no warranties. This code is yours to share, use and modify with no +* strings attached and no restrictions or obligations. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org +* ============================================================================*/ + +#pragma once + +#include "g3log/sink.hpp" + +#include +#include + +namespace g3 { + + // The Sinkhandle is the client's access point to the specific sink instance. + // Only through the Sinkhandle can, and should, the real sink's specific API + // be called. + // + // The real sink will be owned by g3log. If the real sink is deleted + // calls to sink's API through the SinkHandle will return an exception embedded + // in the resulting future. Ref: SinkHandle::call + template + class SinkHandle { + std::weak_ptr> _sink; + + public: + SinkHandle(std::shared_ptr> sink) : + _sink(sink) {} + + ~SinkHandle() = default; + + // Asynchronous call to the real sink. If the real sink is already deleted + // the returned future will contain a bad_weak_ptr exception instead of the + // call result. + template + auto call(AsyncCall func, Args&&... args) -> std::future> { + try { + std::shared_ptr> sink(_sink); + return sink->async(func, std::forward(args)...); + } catch (const std::bad_weak_ptr& e) { + typedef std::invoke_result_t PromiseType; + std::promise promise; + promise.set_exception(std::make_exception_ptr(e)); + return std::move(promise.get_future()); + } + } + + /// Get weak_ptr access to the sink(). Make sure to check that the returned pointer is valid, + /// auto p = sink(); auto ptr = p.lock(); if (ptr) { .... } + /// ref: https://en.cppreference.com/w/cpp/memory/weak_ptr/lock + std::weak_ptr> sink() { + return _sink.lock(); + } + }; + +} // namespace g3 diff --git a/PeakMenu/g3log/g3log/sinkwrapper.hpp b/PeakMenu/g3log/g3log/sinkwrapper.hpp new file mode 100644 index 0000000..f9c8fc2 --- /dev/null +++ b/PeakMenu/g3log/g3log/sinkwrapper.hpp @@ -0,0 +1,21 @@ +/** ========================================================================== +* 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes +* with no warranties. This code is yours to share, use and modify with no +* strings attached and no restrictions or obligations. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org +* ============================================================================*/ + +#pragma once + +#include "g3log/logmessage.hpp" + +namespace g3 { + namespace internal { + + struct SinkWrapper { + virtual ~SinkWrapper() {} + virtual void send(LogMessageMover msg) = 0; + }; + } // namespace internal +} // namespace g3 diff --git a/PeakMenu/g3log/g3log/stacktrace_windows.hpp b/PeakMenu/g3log/g3log/stacktrace_windows.hpp new file mode 100644 index 0000000..8d3d889 --- /dev/null +++ b/PeakMenu/g3log/g3log/stacktrace_windows.hpp @@ -0,0 +1,41 @@ +/** ========================================================================== + * 2014 by KjellKod.cc AND Robert Engeln. + * The stacktrace code was given as a public domain dedication by Robert Engeln + * It was originally published at: http://code-freeze.blogspot.com/2012/01/generating-stack-traces-from-c.html + * It was (here) modified for g3log purposes. + * + * This is PUBLIC DOMAIN to use at your own risk and comes + * with no warranties. This code is yours to share, use and modify with no + * strings attached and no restrictions or obligations. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org + * ============================================================================*/ + +#pragma once +#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) +#error "stacktrace_win.cpp used but not on a windows system" +#endif + +#include "g3log/crashhandler.hpp" + +#include +#include + +namespace stacktrace { + /// return the text description of a Windows exception code + std::string exceptionIdToText(g3::SignalType id); + + /// return whether or not the exception is a known exception, i.e. + /// an exception that we should treat as a fatal event + bool isKnownException(g3::SignalType id); + + /// helper function: retrieve stackdump from no excisting exception pointer + std::string stackdump(); + + /// helper function: retrieve stackdump, starting from an exception pointer + std::string stackdump(EXCEPTION_POINTERS* info); + + /// main stackdump function. retrieve stackdump, from the given context + std::string stackdump(CONTEXT* context); + +} // namespace stacktrace diff --git a/PeakMenu/g3log/g3log/stlpatch_future.hpp b/PeakMenu/g3log/g3log/stlpatch_future.hpp new file mode 100644 index 0000000..9022c97 --- /dev/null +++ b/PeakMenu/g3log/g3log/stlpatch_future.hpp @@ -0,0 +1,78 @@ +/** ========================================================================== +* 2013 This is PUBLIC DOMAIN to use at your own risk and comes +* with no warranties. This code is yours to share, use and modify with no +* strings attached and no restrictions or obligations. +* +* For more information see g3log/LICENSE or refer refer to http://unlicense.org +* +* +* 2013/12/28 Bugfix for Visual Studio 2013 which does not handle well +* std::packaged_task. Thanks to Michael Rasmussen (lap777) +* Ref: workarounds at http://connect.microsoft.com/VisualStudio/feedback/details/791185/std-packaged-task-t-where-t-is-void-or-a-reference-class-are-not-movable +* ============================================================================*/ + +#pragma once +#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__) && (_MSC_VER <= 1800) +namespace std { + + template + class packaged_task { + promise _my_promise; + function _my_func; + + public: + packaged_task() { + } + + template + explicit packaged_task(_Fty2&& _Fnarg) : + _my_func(_Fnarg) { + } + + packaged_task(packaged_task&& _Other) : + _my_promise(move(_Other._my_promise)), + _my_func(move(_Other._my_func)) { + } + + packaged_task& operator=(packaged_task&& _Other) { + _my_promise = move(_Other._my_promise); + _my_func = move(_Other._my_func); + return (*this); + } + + packaged_task(const packaged_task&) = delete; + packaged_task& operator=(const packaged_task&) = delete; + + ~packaged_task() { + } + + void swap(packaged_task& _Other) { + swap(_my_promise, _Other._my_promise); + swap(_my_func, _Other._my_func); + } + + explicit operator bool() const { + return _my_func != false; + } + + bool valid() const { + return _my_func != false; + } + + future get_future() { + return _my_promise.get_future(); + } + + void operator()(_ArgTypes... _Args) { + _my_func(forward<_ArgTypes>(_Args)...); + _my_promise.set_value(); + } + + void reset() { + _my_promise.swap(promise()); + _my_func.swap(function()); + } + }; + +}; // namespace std +#endif // defined(WIN32) ... diff --git a/PeakMenu/g3log/g3log/time.hpp b/PeakMenu/g3log/g3log/time.hpp new file mode 100644 index 0000000..fd675d4 --- /dev/null +++ b/PeakMenu/g3log/g3log/time.hpp @@ -0,0 +1,78 @@ +#pragma once +/** ========================================================================== +* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes +* with no warranties. This code is yours to share, use and modify with no +* strings attached and no restrictions or obligations. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org +* ============================================================================ +* Filename:g3time.h cross-platform, thread-safe replacement for C++11 non-thread-safe +* localtime (and similar) +* Created: 2012 by Kjell Hedström +* +* PUBLIC DOMAIN and Not under copywrite protection. First published for g3log at KjellKod.cc +* ********************************************* */ + +#include +#include +#include + +// FYI: +// namespace g3::internal ONLY in g3time.cpp +// std::string put_time(const struct tm* tmb, const char* c_time_format) + +namespace g3 { + typedef std::chrono::time_point system_time_point; + typedef std::chrono::time_point high_resolution_time_point; + typedef std::chrono::milliseconds milliseconds; + typedef std::chrono::microseconds microseconds; + + namespace internal { + enum class Fractional { Millisecond, + Microsecond, + Nanosecond, + NanosecondDefault }; + Fractional getFractional(const std::string& format_buffer, size_t pos); + std::string to_string(const g3::system_time_point& ts, Fractional fractional); + std::string localtime_formatted_fractions(const g3::system_time_point& ts, std::string format_buffer); + static const std::string date_formatted = "%Y/%m/%d"; + // %f: fractions of seconds (%f is nanoseconds) + // %f3: milliseconds, 3 digits: 001 + // %6: microseconds: 6 digits: 000001 --- default for the time_format + // %f9, %f: nanoseconds, 9 digits: 000000001 + static const std::string time_formatted = "%H:%M:%S %f6"; + } // namespace internal + + // This mimics the original "std::put_time(const std::tm* tmb, const charT* fmt)" + // This is needed since latest version (at time of writing) of gcc4.7 does not implement this library function yet. + // return value is SIMPLIFIED to only return a std::string + std::string put_time(const struct tm* tmb, const char* c_time_format); + + /** return time representing POD struct (ref ctime + wchar) that is normally + * retrieved with std::localtime. g3::localtime is threadsafe which std::localtime is not. + * g3::localtime is probably used together with @ref g3::systemtime_now */ + tm localtime(std::time_t time); + + /** format string must conform to std::put_time's demands. + * WARNING: At time of writing there is only so-so compiler support for + * std::put_time. A possible fix if your c++11 library is not updated is to + * modify this to use std::strftime instead */ + std::string localtime_formatted(const system_time_point& ts, const std::string& time_format); + + inline system_time_point to_system_time(const high_resolution_time_point& ts) { + // On some (windows) systems, the system_clock does not provide the highest possible time + // resolution. Thus g3log uses high_resolution_clock for message time stamps. However, + // unlike system_clock, high_resolution_clock cannot be converted to a time and date as + // it usually measures reflects the time since power-up. + // Thus, hrs_now and sys_now are recorded once when the program starts to be able to convert + // timestamps to dime and date using to_system_time(). The precision of the absolute time is + // of course that of system_clock() with some error added due to the non-simultaneous initialization + // of the two static variables but relative times within one log will be as precise as + // high_resolution_clock. + using namespace std::chrono; + static const auto hrs_now = high_resolution_clock::now(); + static const auto sys_now = system_clock::now(); + + return time_point_cast(sys_now + (ts - hrs_now)); + } +} // namespace g3 diff --git a/PeakMenu/g3log/logcapture.cpp b/PeakMenu/g3log/logcapture.cpp new file mode 100644 index 0000000..ad448c9 --- /dev/null +++ b/PeakMenu/g3log/logcapture.cpp @@ -0,0 +1,113 @@ +/** ========================================================================== + * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes + * with no warranties. This code is yours to share, use and modify with no + * strings attached and no restrictions or obligations. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org + * ============================================================================*/ + +#include "g3log/logcapture.hpp" +#include "g3log/crashhandler.hpp" +#include "g3log/g3log.hpp" + +#ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE +#include +#endif /* G3_DYNAMIC_MAX_MESSAGE_SIZE */ + +// For Windows we need force a thread_local install per thread of three +// signals that must have a signal handler installed per thread-basis +// It is really a royal pain. Seriously Microsoft? Seriously? +#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) +#define SIGNAL_HANDLER_VERIFY() g3::installSignalHandlerForThread() +#else +// Does nothing --- enforces that semicolon must be written +#define SIGNAL_HANDLER_VERIFY() \ + do { \ + } while (0) +#endif + +#ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE +// MaxMessageSize is message limit used with vsnprintf/vsnprintf_s +static int MaxMessageSize = 2048; + +void g3::only_change_at_initialization::setMaxMessageSize(size_t max_size) { + MaxMessageSize = max_size; +} +#endif /* G3_DYNAMIC_MAX_MESSAGE_SIZE */ + +/** logCapture is a simple struct for capturing log/fatal entries. At destruction the +* captured message is forwarded to background worker. +* As a safety precaution: No memory allocated here will be moved into the background +* worker in case of dynamic loaded library reasons instead the arguments are copied +* inside of g3log.cpp::saveMessage*/ +LogCapture::~LogCapture() noexcept(false) { + using namespace g3::internal; + SIGNAL_HANDLER_VERIFY(); + saveMessage(_stream.str().c_str(), _file, _line, _function, _level, _expression, _fatal_signal, _stack_trace.c_str()); +} + +/// Called from crash handler when a fatal signal has occurred (SIGSEGV etc) +LogCapture::LogCapture(const LEVELS& level, g3::SignalType fatal_signal, const char* dump) : + LogCapture("", 0, "", level, "", fatal_signal, dump) { +} + +/** + * @file, line, function are given in g3log.hpp from macros + * @level INFO/DEBUG/WARNING/FATAL + * @expression for CHECK calls + * @fatal_signal for failed CHECK:SIGABRT or fatal signal caught in the signal handler + */ +LogCapture::LogCapture(const char* file, const int line, const char* function, const LEVELS& level, + const char* expression, g3::SignalType fatal_signal, const char* dump) : + _file(file), + _line(line), + _function(function), + _level(level), + _expression(expression), + _fatal_signal(fatal_signal) { + + if (g3::internal::wasFatal(level)) { + _stack_trace = std::string{"\n*******\tSTACKDUMP *******\n"}; + _stack_trace.append(g3::internal::stackdump(dump)); + } +} + +/** +* capturef, used for "printf" like API in CHECKF, LOGF, LOGF_IF +* See also for the attribute formatting ref: http://www.codemaestro.com/reviews/18 +*/ +void LogCapture::capturef(const char* printf_like_message, ...) { + static const std::string kTruncatedWarningText = "[...truncated...]"; +#ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE + std::vector finished_message_backing(MaxMessageSize); + char* finished_message = finished_message_backing.data(); + auto finished_message_len = MaxMessageSize; +#else + static const int kMaxMessageSize = 2048; + char finished_message[kMaxMessageSize]; +#if ((defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__GNUC__)) + auto finished_message_len = _countof(finished_message); +#else + int finished_message_len = sizeof(finished_message); +#endif +#endif /* G3_DYNAMIC_MAX_MESSAGE_SIZE*/ + + va_list arglist; + va_start(arglist, printf_like_message); + +#if ((defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__GNUC__)) + const int nbrcharacters = vsnprintf_s(finished_message, finished_message_len, _TRUNCATE, printf_like_message, arglist); +#else + const int nbrcharacters = vsnprintf(finished_message, finished_message_len, printf_like_message, arglist); +#endif + va_end(arglist); + + if (nbrcharacters < 0) { + stream() << "\n\tERROR LOG MSG NOTIFICATION: Failure to successfully parse the message"; + stream() << '"' << printf_like_message << '"' << std::endl; + } else if (nbrcharacters > finished_message_len) { + stream() << finished_message << kTruncatedWarningText; + } else { + stream() << finished_message; + } +} diff --git a/PeakMenu/g3log/loglevels.cpp b/PeakMenu/g3log/loglevels.cpp new file mode 100644 index 0000000..a148e8f --- /dev/null +++ b/PeakMenu/g3log/loglevels.cpp @@ -0,0 +1,128 @@ +/** ========================================================================== +* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes +* with no warranties. This code is yours to share, use and modify with no +* strings attached and no restrictions or obligations. +* +* For more information see g3log/LICENSE or refer refer to http://unlicense.org +* ============================================================================*/ + +#include "g3log/loglevels.hpp" +#include + +#include + +namespace g3 { + namespace internal { + bool wasFatal(const LEVELS& level) { + return level.value >= FATAL.value; + } + +#ifdef G3_DYNAMIC_LOGGING + const std::map g_log_level_defaults = { + {G3LOG_DEBUG.value, {G3LOG_DEBUG}}, + {INFO.value, {INFO}}, + {WARNING.value, {WARNING}}, + {FATAL.value, {FATAL}}}; + + std::map g_log_levels = g_log_level_defaults; +#endif + } // namespace internal + +#ifdef G3_DYNAMIC_LOGGING + namespace only_change_at_initialization { + + void addLogLevel(LEVELS lvl, bool enabled) { + int value = lvl.value; + internal::g_log_levels[value] = {lvl, enabled}; + } + + void addLogLevel(LEVELS level) { + addLogLevel(level, true); + } + + void reset() { + g3::internal::g_log_levels = g3::internal::g_log_level_defaults; + } + } // namespace only_change_at_initialization + + namespace log_levels { + + void setHighest(LEVELS enabledFrom) { + auto it = internal::g_log_levels.find(enabledFrom.value); + if (it != internal::g_log_levels.end()) { + for (auto& v : internal::g_log_levels) { + if (v.first < enabledFrom.value) { + disable(v.second.level); + } else { + enable(v.second.level); + } + } + } + } + + void set(LEVELS level, bool enabled) { + auto it = internal::g_log_levels.find(level.value); + if (it != internal::g_log_levels.end()) { + internal::g_log_levels[level.value] = {level, enabled}; + } + } + + void disable(LEVELS level) { + set(level, false); + } + + void enable(LEVELS level) { + set(level, true); + } + + void disableAll() { + for (auto& v : internal::g_log_levels) { + v.second.status = false; + } + } + + void enableAll() { + for (auto& v : internal::g_log_levels) { + v.second.status = true; + } + } + + std::string to_string(std::map levelsToPrint) { + std::string levels; + for (auto& v : levelsToPrint) { + levels += "name: " + v.second.level.text + " level: " + std::to_string(v.first) + " status: " + std::to_string(v.second.status.value()) + "\n"; + } + return levels; + } + + std::string to_string() { + return to_string(internal::g_log_levels); + } + + std::map getAll() { + return internal::g_log_levels; + } + + // status : {Absent, Enabled, Disabled}; + status getStatus(LEVELS level) { + const auto it = internal::g_log_levels.find(level.value); + if (internal::g_log_levels.end() == it) { + return status::Absent; + } + + return (it->second.status.get().load() ? status::Enabled : status::Disabled); + } + } // namespace log_levels + +#endif + + bool logLevel(const LEVELS& log_level) { +#ifdef G3_DYNAMIC_LOGGING + int level = log_level.value; + bool status = internal::g_log_levels[level].status.value(); + return status; +#else + return true; +#endif + } +} // namespace g3 diff --git a/PeakMenu/g3log/logmessage.cpp b/PeakMenu/g3log/logmessage.cpp new file mode 100644 index 0000000..5e6faa9 --- /dev/null +++ b/PeakMenu/g3log/logmessage.cpp @@ -0,0 +1,193 @@ +/** ========================================================================== +* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes +* with no warranties. This code is yours to share, use and modify with no +* strings attached and no restrictions or obligations. +* +* For more information see g3log/LICENSE or refer refer to http://unlicense.org +* ============================================================================*/ + +#include "g3log/logmessage.hpp" +#include +#include "g3log/crashhandler.hpp" +#include "g3log/time.hpp" + +namespace g3 { + + std::string LogMessage::splitFileName(const std::string& str) { + size_t found; + found = str.find_last_of("(/\\"); + return str.substr(found + 1); + } + + // helper for fatal signal + std::string LogMessage::fatalSignalToString(const LogMessage& msg) { + std::string out; // clear any previous text and formatting + out.append(msg.timestamp() + "\n\n***** FATAL SIGNAL RECEIVED ******* \n" + msg.message() + '\n'); + return out; + } + + // helper for fatal exception (windows only) + std::string LogMessage::fatalExceptionToString(const LogMessage& msg) { + std::string out; // clear any previous text and formatting + out.append(msg.timestamp() + "\n\n***** FATAL EXCEPTION RECEIVED ******* \n" + msg.message() + '\n'); + return out; + } + + // helper for fatal LOG + std::string LogMessage::fatalLogToString(const LogMessage& msg) { + auto out = msg._logDetailsToStringFunc(msg); + static const std::string fatalExitReason = {"EXIT trigger caused by LOG(FATAL) entry: "}; + out.append("\n\t*******\t " + fatalExitReason + "\n\t" + '"' + msg.message() + '"'); + return out; + } + + // helper for fatal CHECK + std::string LogMessage::fatalCheckToString(const LogMessage& msg) { + auto out = msg._logDetailsToStringFunc(msg); + static const std::string contractExitReason = {"EXIT trigger caused by broken Contract:"}; + out.append("\n\t*******\t " + contractExitReason + " CHECK(" + msg.expression() + ")\n\t" + '"' + msg.message() + '"'); + return out; + } + + // helper for setting the normal log details in an entry + std::string LogMessage::DefaultLogDetailsToString(const LogMessage& msg) { + std::string out; + out.append(msg.timestamp() + "\t" + msg.level() + " [" + msg.file() + "->" + msg.function() + ":" + msg.line() + "]\t"); + return out; + } + + std::string LogMessage::FullLogDetailsToString(const LogMessage& msg) { + std::string out; + out.append(msg.timestamp() + "\t" + msg.level() + " [" + msg.threadID() + " " + msg.file() + "->" + msg.function() + ":" + msg.line() + "]\t"); + return out; + } + + // helper for normal + std::string LogMessage::normalToString(const LogMessage& msg) { + auto out = msg._logDetailsToStringFunc(msg); + out.append(msg.message() + '\n'); + return out; + } + + // end static functions section + + void LogMessage::overrideLogDetailsFunc(LogDetailsFunc func) const { + _logDetailsToStringFunc = func; + } + + // Format the log message according to it's type + std::string LogMessage::toString(LogDetailsFunc formattingFunc) const { + overrideLogDetailsFunc(formattingFunc); + + if (false == wasFatal()) { + return LogMessage::normalToString(*this); + } + + const auto level_value = _level.value; + if (internal::FATAL_SIGNAL.value == _level.value) { + return LogMessage::fatalSignalToString(*this); + } + + if (internal::FATAL_EXCEPTION.value == _level.value) { + return LogMessage::fatalExceptionToString(*this); + } + + if (FATAL.value == _level.value) { + return LogMessage::fatalLogToString(*this); + } + + if (internal::CONTRACT.value == level_value) { + return LogMessage::fatalCheckToString(*this); + } + + // What? Did we hit a custom made level? + auto out = _logDetailsToStringFunc(*this); + static const std::string errorUnknown = {"UNKNOWN or Custom made Log Message Type"}; + out.append("\t*******" + errorUnknown + "\n\t" + message() + '\n'); + return out; + } + + std::string LogMessage::timestamp(const std::string& time_look) const { + return g3::localtime_formatted(to_system_time(_timestamp), time_look); + } + + // By copy, not by reference. See this explanation for details: + // http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom + LogMessage& LogMessage::operator=(LogMessage other) { + swap(*this, other); + return *this; + } + + LogMessage::LogMessage(std::string file, const int line, + std::string function, const LEVELS level) : + _logDetailsToStringFunc(LogMessage::DefaultLogDetailsToString), + _timestamp(std::chrono::high_resolution_clock::now()), + _call_thread_id(std::this_thread::get_id()) +#if defined(G3_LOG_FULL_FILENAME) + , + _file(file) +#else + , + _file(LogMessage::splitFileName(file)) +#endif + , + _file_path(file), + _line(line), + _function(std::move(function)), + _level(level) { + } + + LogMessage::LogMessage(const std::string& fatalOsSignalCrashMessage) : + LogMessage({""}, 0, {""}, internal::FATAL_SIGNAL) { + _message.append(fatalOsSignalCrashMessage); + } + + LogMessage::LogMessage(const LogMessage& other) : + _logDetailsToStringFunc(other._logDetailsToStringFunc), + _timestamp(other._timestamp), + _call_thread_id(other._call_thread_id), + _file(other._file), + _file_path(other._file_path), + _line(other._line), + _function(other._function), + _level(other._level), + _expression(other._expression), + _message(other._message) { + } + + LogMessage::LogMessage(LogMessage&& other) : + _logDetailsToStringFunc(other._logDetailsToStringFunc), + _timestamp(other._timestamp), + _call_thread_id(other._call_thread_id), + _file(std::move(other._file)), + _file_path(std::move(other._file_path)), + _line(other._line), + _function(std::move(other._function)), + _level(other._level), + _expression(std::move(other._expression)), + _message(std::move(other._message)) { + } + + std::string LogMessage::threadID() const { + std::ostringstream oss; + oss << _call_thread_id; + return oss.str(); + } + + FatalMessage::FatalMessage(const LogMessage& details, g3::SignalType signal_id) : + LogMessage(details), + _signal_id(signal_id) {} + + FatalMessage::FatalMessage(const FatalMessage& other) : + LogMessage(other), + _signal_id(other._signal_id) {} + + LogMessage FatalMessage::copyToLogMessage() const { + return LogMessage(*this); + } + + std::string FatalMessage::reason() const { + return internal::exitReasonName(_level, _signal_id); + } + +} // namespace g3 diff --git a/PeakMenu/g3log/logworker.cpp b/PeakMenu/g3log/logworker.cpp new file mode 100644 index 0000000..4bda245 --- /dev/null +++ b/PeakMenu/g3log/logworker.cpp @@ -0,0 +1,125 @@ +/** ========================================================================== +* 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes +* with no warranties. This code is yours to share, use and modify with no +* strings attached and no restrictions or obligations. +* +* For more information see g3log/LICENSE or refer refer to http://unlicense.org +* ============================================================================*/ + +#include "g3log/logworker.hpp" +#include "g3log/active.hpp" +#include "g3log/crashhandler.hpp" +#include "g3log/future.hpp" +#include "g3log/g3log.hpp" +#include "g3log/logmessage.hpp" + +#include + +namespace g3 { + + LogWorkerImpl::LogWorkerImpl() : + _bg(kjellkod::Active::createActive()) {} + + void LogWorkerImpl::bgSave(g3::LogMessagePtr msgPtr) { + std::unique_ptr uniqueMsg(std::move(msgPtr.get())); + + for (auto& sink : _sinks) { + LogMessage msg(*(uniqueMsg)); + sink->send(LogMessageMover(std::move(msg))); + } + + if (_sinks.empty()) { + std::string err_msg{"g3logworker has no sinks. Message: ["}; + err_msg.append(uniqueMsg.get()->toString()).append("]\n"); + std::cerr << err_msg; + } + } + + void LogWorkerImpl::bgFatal(FatalMessagePtr msgPtr) { + // this will be the last message. Only the active logworker can receive a FATAL call so it's + // safe to shutdown logging now + g3::internal::shutDownLogging(); + + std::string reason = msgPtr.get()->reason(); + const auto level = msgPtr.get()->_level; + const auto fatal_id = msgPtr.get()->_signal_id; + + std::unique_ptr uniqueMsg(std::move(msgPtr.get())); + uniqueMsg->write().append("\nExiting after fatal event (").append(uniqueMsg->level()); + + // Change output in case of a fatal signal (or windows exception) + std::string exiting = {"Fatal type: "}; + + uniqueMsg->write().append("). ").append(exiting).append(" ").append(reason).append("\nLog content flushed successfully to sink\n\n"); + + std::cerr << uniqueMsg->toString() << std::flush; + for (auto& sink : _sinks) { + LogMessage msg(*(uniqueMsg)); + sink->send(LogMessageMover(std::move(msg))); + } + + // This clear is absolutely necessary + // All sinks are forced to receive the fatal message above before we continue + _sinks.clear(); // flush all queues + internal::exitWithDefaultSignalHandler(level, fatal_id); + + // should never reach this point + perror("g3log exited after receiving FATAL trigger. Flush message status: "); + } + + LogWorker::~LogWorker() { + g3::internal::shutDownLoggingForActiveOnly(this); + + // The sinks WILL automatically be cleared at exit of this destructor + // The waiting inside removeAllSinks ensures that all messages until this point are + // taken care of before any internals/LogWorkerImpl of LogWorker starts to be destroyed. + // i.e. this avoids a race with another thread slipping through the "shutdownLogging" and + // calling ::save or ::fatal through LOG/CHECK with lambda messages and "partly + // deconstructed LogWorkerImpl" + // + // Any messages put into the queue will be OK due to: + // *) If it is before the wait below then they will be executed + // *) If it is AFTER the wait below then they will be ignored and NEVER executed + removeAllSinks(); + + // The background worker WILL be automatically cleared at the exit of the destructor + // However, the explicitly clearing of the background worker (below) makes sure that there can + // be no thread that manages to add another sink after the call to clear the sinks above. + // i.e. this manages the extremely unlikely case of another thread calling + // addWrappedSink after the sink clear above. Normally adding of sinks should be done in main.cpp + // and be closely coupled with the existence of the LogWorker. Sharing this adding of sinks to + // other threads that do not know the state of LogWorker is considered a bug but it is dealt with + // nonetheless below. + // + // If sinks would already have been added after the sink clear above then this reset will deal with it + // without risking lambda execution with a partially deconstructed LogWorkerImpl + // Calling g3::spawn_task on a nullptr Active object will not crash but return + // a future containing an appropriate exception. + _impl._bg.reset(nullptr); + } + + void LogWorker::save(LogMessagePtr msg) { + _impl._bg->send([this, msg] { _impl.bgSave(msg); }); + } + + void LogWorker::fatal(FatalMessagePtr fatal_message) { + _impl._bg->send([this, fatal_message] { _impl.bgFatal(fatal_message); }); + } + + void LogWorker::addWrappedSink(std::shared_ptr sink) { + auto bg_addsink_call = [this, sink] { + _impl._sinks.push_back(sink); + }; + auto token_done = g3::spawn_task(bg_addsink_call, _impl._bg.get()); + token_done.wait(); + } + + std::unique_ptr LogWorker::createLogWorker() { + return std::unique_ptr(new LogWorker); + } + + std::unique_ptr LogWorker::addDefaultLogger(const std::string& log_prefix, const std::string& log_directory, const std::string& default_id) { + return addSink(std::make_unique(log_prefix, log_directory, default_id), &FileSink::fileWrite); + } + +} // namespace g3 diff --git a/PeakMenu/g3log/stacktrace_windows.cpp b/PeakMenu/g3log/stacktrace_windows.cpp new file mode 100644 index 0000000..f9b483e --- /dev/null +++ b/PeakMenu/g3log/stacktrace_windows.cpp @@ -0,0 +1,196 @@ +/** ========================================================================== + * Original code made by Robert Engeln. Given as a PUBLIC DOMAIN dedication for + * the benefit of g3log. It was originally published at: + * http://code-freeze.blogspot.com/2012/01/generating-stack-traces-from-c.html + + * 2014-2015: adapted for g3log by Kjell Hedstrom (KjellKod). + * + * This is PUBLIC DOMAIN to use at your own risk and comes + * with no warranties. This code is yours to share, use and modify with no + * strings attached and no restrictions or obligations. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org + * ============================================================================*/ + +#include "g3log/stacktrace_windows.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma comment(lib, "dbghelp.lib") + +#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) +#error "stacktrace_win.cpp used but not on a windows system" +#endif + +#define g3_MAP_PAIR_STRINGIFY(x) \ + { x, #x } + +namespace { + thread_local size_t g_thread_local_recursive_crash_check = 0; + + const std::map kExceptionsAsText = { + g3_MAP_PAIR_STRINGIFY(EXCEPTION_ACCESS_VIOLATION), g3_MAP_PAIR_STRINGIFY(EXCEPTION_ARRAY_BOUNDS_EXCEEDED), g3_MAP_PAIR_STRINGIFY(EXCEPTION_DATATYPE_MISALIGNMENT), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_DENORMAL_OPERAND), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_DIVIDE_BY_ZERO), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INEXACT_RESULT), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INEXACT_RESULT), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INVALID_OPERATION), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_OVERFLOW), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_STACK_CHECK), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_UNDERFLOW), g3_MAP_PAIR_STRINGIFY(EXCEPTION_ILLEGAL_INSTRUCTION), g3_MAP_PAIR_STRINGIFY(EXCEPTION_IN_PAGE_ERROR), g3_MAP_PAIR_STRINGIFY(EXCEPTION_INT_DIVIDE_BY_ZERO), g3_MAP_PAIR_STRINGIFY(EXCEPTION_INT_OVERFLOW), g3_MAP_PAIR_STRINGIFY(EXCEPTION_INVALID_DISPOSITION), g3_MAP_PAIR_STRINGIFY(EXCEPTION_NONCONTINUABLE_EXCEPTION), g3_MAP_PAIR_STRINGIFY(EXCEPTION_PRIV_INSTRUCTION), g3_MAP_PAIR_STRINGIFY(EXCEPTION_STACK_OVERFLOW), g3_MAP_PAIR_STRINGIFY(EXCEPTION_BREAKPOINT), g3_MAP_PAIR_STRINGIFY(EXCEPTION_SINGLE_STEP) + + }; + + // Using the given context, fill in all the stack frames. + // Which then later can be interpreted to human readable text + void captureStackTrace(CONTEXT* context, std::vector& frame_pointers) { + DWORD machine_type = 0; + STACKFRAME64 frame = {}; // force zeroing + frame.AddrPC.Mode = AddrModeFlat; + frame.AddrFrame.Mode = AddrModeFlat; + frame.AddrStack.Mode = AddrModeFlat; +#if defined(_M_ARM64) + frame.AddrPC.Offset = context->Pc; + frame.AddrFrame.Offset = context->Fp; + frame.AddrStack.Offset = context->Sp; + machine_type = IMAGE_FILE_MACHINE_ARM64; +#elif defined(_M_ARM) + frame.AddrPC.Offset = context->Pc; + frame.AddrFrame.Offset = context->R11; + frame.AddrStack.Offset = context->Sp; + machine_type = IMAGE_FILE_MACHINE_ARM; +#elif defined(_M_X64) + frame.AddrPC.Offset = context->Rip; + frame.AddrFrame.Offset = context->Rbp; + frame.AddrStack.Offset = context->Rsp; + machine_type = IMAGE_FILE_MACHINE_AMD64; +#else + frame.AddrPC.Offset = context->Eip; + frame.AddrPC.Offset = context->Ebp; + frame.AddrPC.Offset = context->Esp; + machine_type = IMAGE_FILE_MACHINE_I386; +#endif + for (size_t index = 0; index < frame_pointers.size(); ++index) { + if (StackWalk64(machine_type, + GetCurrentProcess(), + GetCurrentThread(), + &frame, + context, + NULL, + SymFunctionTableAccess64, + SymGetModuleBase64, + NULL)) { + frame_pointers[index] = frame.AddrPC.Offset; + } else { + break; + } + } + } + + // extract readable text from a given stack frame. All thanks to + // using SymFromAddr and SymGetLineFromAddr64 with the stack pointer + std::string getSymbolInformation(const size_t index, const std::vector& frame_pointers) { + auto addr = frame_pointers[index]; + std::string frame_dump = "stack dump [" + std::to_string(index) + "]\t"; + + DWORD64 displacement64; + DWORD displacement; + alignas(SYMBOL_INFO) char symbol_buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME]; + SYMBOL_INFO* symbol = reinterpret_cast(symbol_buffer); + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol->MaxNameLen = MAX_SYM_NAME; + + IMAGEHLP_LINE64 line; + line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); + std::string lineInformation; + std::string callInformation; + if (SymFromAddr(GetCurrentProcess(), addr, &displacement64, symbol)) { + callInformation.append(" ").append(std::string(symbol->Name, symbol->NameLen)); + if (SymGetLineFromAddr64(GetCurrentProcess(), addr, &displacement, &line)) { + lineInformation.append("\t").append(line.FileName).append(" L: "); + lineInformation.append(std::to_string(line.LineNumber)); + } + } + frame_dump.append(lineInformation).append(callInformation); + return frame_dump; + } + + // Retrieves all the symbols for the stack frames, fills them within a text representation and returns it + std::string convertFramesToText(std::vector& frame_pointers) { + std::string dump; // slightly more efficient than ostringstream + const size_t kSize = frame_pointers.size(); + for (size_t index = 0; index < kSize && frame_pointers[index]; ++index) { + dump += getSymbolInformation(index, frame_pointers); + dump += "\n"; + } + return dump; + } +} // namespace + +namespace stacktrace { + const std::string kUnknown = {"UNKNOWN EXCEPTION"}; + /// return the text description of a Windows exception code + /// From MSDN GetExceptionCode http://msdn.microsoft.com/en-us/library/windows/desktop/ms679356(v=vs.85).aspx + std::string exceptionIdToText(g3::SignalType id) { + const auto iter = kExceptionsAsText.find(id); + if (iter == kExceptionsAsText.end()) { + std::string unknown = {kUnknown + ":" + std::to_string(id)}; + return unknown; + } + return iter->second; + } + + /// Yes a double lookup: first for isKnownException and then exceptionIdToText + /// for vectored exceptions we only deal with known exceptions so this tiny + /// overhead we can live with + bool isKnownException(g3::SignalType id) { + return (kExceptionsAsText.end() != kExceptionsAsText.find(id)); + } + + /// helper function: retrieve stackdump from no excisting exception pointer + std::string stackdump() { + CONTEXT current_context; + memset(¤t_context, 0, sizeof(CONTEXT)); + RtlCaptureContext(¤t_context); + return stackdump(¤t_context); + } + + /// helper function: retrieve stackdump, starting from an exception pointer + std::string stackdump(EXCEPTION_POINTERS* info) { + auto context = info->ContextRecord; + return stackdump(context); + } + + /// main stackdump function. retrieve stackdump, from the given context + std::string stackdump(CONTEXT* context) { + + if (g_thread_local_recursive_crash_check >= 2) { // In Debug scenarios we allow one extra pass + std::string recursive_crash = {"\n\n\n***** Recursive crash detected"}; + recursive_crash.append(", cannot continue stackdump traversal. *****\n\n\n"); + return recursive_crash; + } + ++g_thread_local_recursive_crash_check; + + static std::mutex m; + std::lock_guard lock(m); + { + const BOOL kLoadSymModules = TRUE; + const auto initialized = SymInitialize(GetCurrentProcess(), nullptr, kLoadSymModules); + if (TRUE != initialized) { + return {"Error: Cannot call SymInitialize(...) for retrieving symbols in stack"}; + } + + std::shared_ptr RaiiSymCleaner(nullptr, [&](void*) { + SymCleanup(GetCurrentProcess()); + }); // Raii sym cleanup + + constexpr size_t kmax_frame_dump_size = 64; + std::vector frame_pointers(kmax_frame_dump_size); + // C++11: size set and values are zeroed + + assert(frame_pointers.size() == kmax_frame_dump_size); + captureStackTrace(context, frame_pointers); + return convertFramesToText(frame_pointers); + } + } + +} // namespace stacktrace diff --git a/PeakMenu/g3log/time.cpp b/PeakMenu/g3log/time.cpp new file mode 100644 index 0000000..a94d394 --- /dev/null +++ b/PeakMenu/g3log/time.cpp @@ -0,0 +1,151 @@ +/** ========================================================================== +* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes +* with no warranties. This code is yours to share, use and modify with no +* strings attached and no restrictions or obligations. +* +* For more information see g3log/LICENSE or refer refer to http://unlicense.org +* ============================================================================*/ + +#include "g3log/time.hpp" + +#include +#include +#include +#include +#include +#include +#include +#ifdef __MACH__ +#include +#endif + +namespace g3 { + namespace internal { + const std::string kFractionalIdentier = "%f"; + const size_t kFractionalIdentierSize = 2; + + Fractional getFractional(const std::string& format_buffer, size_t pos) { + char ch = (format_buffer.size() > pos + kFractionalIdentierSize ? format_buffer.at(pos + kFractionalIdentierSize) : '\0'); + Fractional type = Fractional::NanosecondDefault; + switch (ch) { + case '3': + type = Fractional::Millisecond; + break; + case '6': + type = Fractional::Microsecond; + break; + case '9': + type = Fractional::Nanosecond; + break; + default: + type = Fractional::NanosecondDefault; + break; + } + return type; + } + + // Returns the fractional as a string with padded zeroes + // 1 ms --> 001 + // 1 us --> 000001 + // 1 ns --> 000000001 + std::string to_string(const g3::system_time_point& ts, Fractional fractional) { + auto duration = ts.time_since_epoch(); + auto sec_duration = std::chrono::duration_cast(duration); + duration -= sec_duration; + auto ns = std::chrono::duration_cast(duration).count(); + + auto zeroes = 9; // default ns + auto digitsToCut = 1; // default ns, divide by 1 makes no change + switch (fractional) { + case Fractional::Millisecond: { + zeroes = 3; + digitsToCut = 1000000; + break; + } + case Fractional::Microsecond: { + zeroes = 6; + digitsToCut = 1000; + break; + } + case Fractional::Nanosecond: + case Fractional::NanosecondDefault: + default: + zeroes = 9; + digitsToCut = 1; + } + + ns /= digitsToCut; + auto value = std::string(std::to_string(ns)); + return std::string(zeroes - value.size(), '0') + value; + } + + std::string localtime_formatted_fractions(const g3::system_time_point& ts, std::string format_buffer) { + // iterating through every "%f" instance in the format string + auto identifierExtraSize = 0; + for (size_t pos = 0; + (pos = format_buffer.find(g3::internal::kFractionalIdentier, pos)) != std::string::npos; + pos += g3::internal::kFractionalIdentierSize + identifierExtraSize) { + // figuring out whether this is nano, micro or milli identifier + auto type = g3::internal::getFractional(format_buffer, pos); + auto value = g3::internal::to_string(ts, type); + auto padding = 0; + if (type != g3::internal::Fractional::NanosecondDefault) { + padding = 1; + } + + // replacing "%f[3|6|9]" with sec fractional part value + format_buffer.replace(pos, g3::internal::kFractionalIdentier.size() + padding, value); + } + return format_buffer; + } + + } // namespace internal +} // namespace g3 + +namespace g3 { + // This mimics the original "std::put_time(const std::tm* tmb, const charT* fmt)" + // This is needed since latest version (at time of writing) of gcc4.7 does not implement this library function yet. + // return value is SIMPLIFIED to only return a std::string + std::string put_time(const struct tm* tmb, const char* c_time_format) { +#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__) + std::ostringstream oss; + oss.fill('0'); + // BOGUS hack done for VS2012: C++11 non-conformant since it SHOULD take a "const struct tm* " + oss << std::put_time(const_cast(tmb), c_time_format); + return oss.str(); +#else // LINUX + const size_t size = 1024; + char buffer[size]; // IMPORTANT: check now and then for when gcc will implement std::put_time. + // ... also ... This is way more buffer space then we need + + auto success = std::strftime(buffer, size, c_time_format, tmb); + // In DEBUG the assert will trigger a process exit. Once inside the if-statement + // the 'always true' expression will be displayed as reason for the exit + // + // In Production mode + // the assert will do nothing but the format string will instead be returned + if (0 == success) { + assert((0 != success) && "strftime fails with illegal formatting"); + return c_time_format; + } + return buffer; +#endif + } + + tm localtime(std::time_t ts) { + struct tm tm_snapshot; +#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) + localtime_s(&tm_snapshot, &ts); // windsows +#else + localtime_r(&ts, &tm_snapshot); // POSIX +#endif + return tm_snapshot; + } + + std::string localtime_formatted(const g3::system_time_point& ts, const std::string& time_format) { + auto format_buffer = internal::localtime_formatted_fractions(ts, time_format); + auto time_point = std::chrono::system_clock::to_time_t(ts); + std::tm t = localtime(time_point); + return g3::put_time(&t, format_buffer.c_str()); // format example: //"%Y/%m/%d %H:%M:%S"); + } +} // namespace g3 diff --git a/PeakMenu/logger.hpp b/PeakMenu/logger.hpp new file mode 100644 index 0000000..c44cba4 --- /dev/null +++ b/PeakMenu/logger.hpp @@ -0,0 +1,134 @@ +#pragma once + +#include "common.hpp" +#include "g3log/g3log.hpp" +#include "g3log/logworker.hpp" + +namespace peak +{ + const LEVELS SUCCESS{200, {"SUCCESS"}}; + const LEVELS ERR{800, "ERROR"}; + + class Logger; + inline Logger* logger{}; + + class Logger + { + public: + explicit Logger() : + worker(g3::LogWorker::createLogWorker()) + { + AllocConsole(); + consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTitleA("PeakMenu"); + SetConsoleOutputCP(CP_UTF8); + RemoveMenu(GetSystemMenu(GetConsoleWindow(), FALSE), SC_CLOSE, MF_DISABLED); + SetConsoleCtrlHandler([](DWORD) -> BOOL + { + isRunning = false; + return TRUE; + }, TRUE); + consoleOut.open("CONOUT$", std::ios_base::out | std::ios_base::app); + size_t size = MAX_PATH; + char* buffer; + _dupenv_s(&buffer, &size, "APPDATA"); + filePath = (std::filesystem::path)buffer / "PeakMenu"; + std::filesystem::path logPath = filePath / "logs"; + if (!std::filesystem::exists(filePath)) + { + std::filesystem::create_directory(filePath); + } + else if (!std::filesystem::is_directory(filePath)) + { + std::filesystem::remove(filePath); + std::filesystem::create_directory(filePath); + } + if (!std::filesystem::exists(logPath)) + { + std::filesystem::create_directory(logPath); + } + else if (!std::filesystem::is_directory(logPath)) + { + std::filesystem::remove(logPath); + std::filesystem::create_directory(logPath); + } + filePath /= "PeakMenu.log"; + if (std::filesystem::exists(filePath)) + { + tm localTime; + time_t creationDate = std::chrono::system_clock::to_time_t(std::chrono::time_point_cast(std::filesystem::last_write_time(filePath) - std::filesystem::file_time_type::clock::now() + std::chrono::system_clock::now())); + localtime_s(&localTime, &creationDate); + std::ostringstream day, month, year, hour, minute, second; + day << std::setfill('0') << std::setw(2) << localTime.tm_mday; + month << "-" << std::setfill('0') << std::setw(2) << localTime.tm_mon + 1; + year << "-" << std::setfill('0') << std::setw(4) << localTime.tm_year + 1900; + hour << " " << std::setfill('0') << std::setw(2) << localTime.tm_hour; + minute << "-" << std::setfill('0') << std::setw(2) << localTime.tm_min; + second << "-" << std::setfill('0') << std::setw(2) << localTime.tm_sec; + std::filesystem::copy_file(filePath, logPath / (day.str() + month.str() + year.str() + hour.str() + minute.str() + second.str())); + } + fileOut.open(filePath, std::ios_base::out | std::ios_base::trunc); + worker->addSink(std::make_unique(), &Sink::callback); + g3::initializeLogging(worker.get()); + g3::setFatalExitHandler(&Sink::fatalCallback); + logger = this; + } + + ~Logger() + { + worker.reset(); + FreeConsole(); + logger = nullptr; + } + + struct Sink + { + std::map colors = {{SUCCESS.text, FOREGROUND_GREEN | FOREGROUND_INTENSITY}, {INFO.text, FOREGROUND_BLUE | FOREGROUND_INTENSITY}, {WARNING.text, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY}, {ERR.text, FOREGROUND_RED | FOREGROUND_INTENSITY}, {FATAL.text, FOREGROUND_RED | FOREGROUND_INTENSITY}}; + + void callback(g3::LogMessageMover log) + { + g3::LogMessage msg = log.get(); + SetConsoleTextAttribute(logger->consoleHandle, colors.find(msg.level())->second); + std::stringstream message(msg.toString([](const g3::LogMessage&)->std::string {return ""; })); + std::string line; + std::ostringstream fmt, fileFmt; + fmt << "[" << msg.timestamp("%d/%m/%Y %H:%M:%S") << "] "; + fileFmt << "[" << msg.timestamp("%d/%m/%Y %H:%M:%S") << "] [" << msg.level() << "] "; + while (std::getline(message, line, '\n')) + { + logger->consoleOut << fmt.str() << line << std::endl << std::flush; + if (std::filesystem::exists(logger->filePath) && !std::filesystem::is_directory(logger->filePath)) + { + logger->fileOut << fileFmt.str() << line << std::endl << std::flush; + } + } + } + + static void fatalCallback(g3::FatalMessagePtr log) + { + SetConsoleTextAttribute(logger->consoleHandle, FOREGROUND_RED | FOREGROUND_INTENSITY); + std::stringstream message(log.get()->toString([](const g3::LogMessage&)->std::string {return ""; })); + std::string line; + std::ostringstream fmt, fileFmt; + fmt << "[" << log.get()->timestamp("%d/%m/%Y %H:%M:%S") << "] "; + fileFmt << "[" << log.get()->timestamp("%d/%m/%Y %H:%M:%S") << "] [FATAL] "; + while (std::getline(message, line, '\n')) + { + logger->consoleOut << fmt.str() << line << std::endl << std::flush; + if (std::filesystem::exists(logger->filePath) && !std::filesystem::is_directory(logger->filePath)) + { + logger->fileOut << fileFmt.str() << line << std::endl << std::flush; + } + } + + } + }; + + private: + HANDLE consoleHandle{}; + std::ofstream consoleOut; + std::filesystem::path filePath; + std::ofstream fileOut; + std::unique_ptr worker; + }; +} \ No newline at end of file diff --git a/PeakMenu/main.cpp b/PeakMenu/main.cpp new file mode 100644 index 0000000..d4d3776 --- /dev/null +++ b/PeakMenu/main.cpp @@ -0,0 +1,37 @@ +#include "common.hpp" +#include "logger.hpp" +#include "pointers.hpp" + +BOOL WINAPI DllMain(HMODULE hmod, DWORD reason, PVOID) +{ + using namespace peak; + if (reason == DLL_PROCESS_ATTACH) + { + hModule = hmod; + mainThread = CreateThread(nullptr, 0, [](PVOID) -> DWORD { + auto loggerInstance = std::make_unique(); + try + { + auto pointerInstance = std::make_unique(); + LOG(SUCCESS) << "Patterns found."; + while (isRunning) + { + pointers->character->infiniteStamina = true; + std::this_thread::sleep_for(100ms); + } + pointers->character->infiniteStamina = false; + pointerInstance.reset(); + LOG(SUCCESS) << "Removed patterns"; + } + catch (std::exception& exc) + { + LOG(FATAL) << exc.what(); + } + std::this_thread::sleep_for(1s); + loggerInstance.reset(); + CloseHandle(mainThread); + FreeLibraryAndExitThread(hModule, 0); + }, nullptr, 0, &mainThreadId); + } + return TRUE; +} \ No newline at end of file diff --git a/PeakMenu/memory.hpp b/PeakMenu/memory.hpp new file mode 100644 index 0000000..c9356b3 --- /dev/null +++ b/PeakMenu/memory.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "common.hpp" +#include "logger.hpp" + +namespace peak +{ + class Memory + { + public: + template + static T pointerToObject(uintptr_t pointer, std::vector offsets) + { + HANDLE process = GetCurrentProcess(); + uintptr_t address = (uintptr_t)GetModuleHandle(L"UnityPlayer.dll") + pointer; + for (int i = 0; i < offsets.size(); i++) + { + ReadProcessMemory(process, (BYTE*)address, &address, sizeof(address), 0); + address += offsets[i]; + } + return (T)address; + } + }; +} \ No newline at end of file diff --git a/PeakMenu/offsets.hpp b/PeakMenu/offsets.hpp new file mode 100644 index 0000000..b0d8737 --- /dev/null +++ b/PeakMenu/offsets.hpp @@ -0,0 +1,13 @@ +#pragma once + +struct CharacterData +{ + char _pad0[0x208]; + float extraStamina; +}; + +struct Character +{ + char _pad0[0xCB]; + bool infiniteStamina; +}; diff --git a/PeakMenu/pointers.cpp b/PeakMenu/pointers.cpp new file mode 100644 index 0000000..be62b81 --- /dev/null +++ b/PeakMenu/pointers.cpp @@ -0,0 +1,15 @@ +#include "pointers.hpp" + +namespace peak +{ + Pointers::Pointers() + { + character = Memory::pointerToObject(0x01EB35D0, {0x430, 0x9B0, 0x10, 0xC8, 0x8, 0x110, 0x0}); + pointers = this; + } + + Pointers::~Pointers() + { + pointers = nullptr; + } +} \ No newline at end of file diff --git a/PeakMenu/pointers.hpp b/PeakMenu/pointers.hpp new file mode 100644 index 0000000..5586adf --- /dev/null +++ b/PeakMenu/pointers.hpp @@ -0,0 +1,15 @@ +#include "memory.hpp" +#include "offsets.hpp" + +namespace peak +{ + class Pointers + { + public: + Pointers(); + ~Pointers(); + Character* character{}; + }; + + inline Pointers* pointers{}; +} \ No newline at end of file