diff --git a/3rdParty/glog/glog-0.3.1_hacked_to_be_truly_synchronous.tgz b/3rdParty/glog/glog-0.3.1_hacked_to_be_truly_synchronous.tgz new file mode 100644 index 0000000..7c2c5ab Binary files /dev/null and b/3rdParty/glog/glog-0.3.1_hacked_to_be_truly_synchronous.tgz differ diff --git a/g2log/CMakeLists.txt b/g2log/CMakeLists.txt index 3d6bc59..ef7d88c 100644 --- a/g2log/CMakeLists.txt +++ b/g2log/CMakeLists.txt @@ -1,123 +1,157 @@ # CMakeLists.txt cmake configuration for g2log test +# g2log is a KjellKod Logger # 2011 @author Kjell Hedström, hedstrom@kjellkod.cc */ # -# g2log is a KjellKod Logger + + +# WINDOWS == README: Example how to setup environment + running an example +# 1. please use the "Visual Studio Command Prompt 2010)" +# 2. from the g2log folder +# mkdir build +# cd build; +# 3. cmake -G "Visual Studio 10" .. +# 4. msbuild g2log-example.sln +# 5. Debug\g2log-example.exe # -# == README: Example how to setup environment + running tests =============== + +# LINUX == README: Example how to setup environment + running tests for =============== # 1. Install gtest # cmake # make # make install (possibly as root) # -# -# # 2. update path to libraries # sudo /sbin/ldconfig -v | grep gtest # # the grep is only to verify that it works. It should give something like -# ... other stuff ... +# ... other stuff ... # libgtest.so.0 -> libgtest.so.0.0.0 # libgtest_main.so.0 -> libgtest_main.so.0.0.0 # -# -# -# 3. To try this out from folder g2log: +# 3. LINUX:To try this out from folder g2log: # mkdir build # cd build # cmake .. # create makefiles in g2log/build directory -# make # link active_object, g2log and example code to get an "example" executable -# ./g2log-example +# make # link active_object, g2log and example code to get an "example" executable +# ./g2log-example # # ============================================================================ cmake_minimum_required (VERSION 2.6) -project (kjellkod_logger) -set(LOG_SRC ${kjellkod_logger_SOURCE_DIR}/src) +project (g2log_by_kjellkod) +set(LOG_SRC ${g2log_by_kjellkod_SOURCE_DIR}/src) MESSAGE(" LOG_SRC = : ${LOG_SRC}") +include_directories(${LOG_SRC}) +SET(ACTIVE_CPP0xx_DIR "Release") + + IF(UNIX) - set(CMAKE_CXX_FLAGS "-Wall -Wunused -std=c++0x ${CMAKE_CXX_FLAGS_DEBUG} -pthread -I/usr/include/justthread") + MESSAGE("") + MESSAGE("cmake for *NIX ") + MESSAGE("if cmake finishes OK, do make") + MESSAGE("then run './g2log-example' or whatever performance test you feel like trying") + MESSAGE("") + set(PLATFORM_LINK_LIBRIES justthread rt) + set(CMAKE_CXX_FLAGS "-Wall -Wunused -std=c++0x ${CMAKE_CXX_FLAGS_DEBUG} -pthread -I/usr/include/justthread") + set(G2_LOG_FILES ${LOG_SRC}/logworker.h ${LOG_SRC}/logworker.cpp ${LOG_SRC}/g2log.h ${LOG_SRC}/g2log.cpp ${LOG_SRC}/crashhandler.h ${LOG_SRC}/crashhandler_unix.cpp) + include_directories("/usr/include/justthread") # SETUP for GTEST - set(GTEST_DIR ../3rdParty/gtest/gtest-1.6.0__stripped) - set(GTEST_INCLUDE_DIRECTORIES ${GTEST_DIR}/include ${GTEST_DIR} ${GTEST_DIR}/src) - include_directories(${GTEST_INCLUDE_DIRECTORIES}) - add_library(gtest_160_lib ${GTEST_DIR}/src/gtest-all.cc ${GTEST_DIR}/src/gtest_main.cc) - enable_testing(true) - - - # make the src directory available - include_directories(/usr/include/justthread) #not necessarily needed if it's in the path - - # add a ActiveObject library - set(ACTIVE_DIR ${LOG_SRC}) - include_directories(${ACTIVE_DIR}) - MESSAGE(" ACTIVE_DIR = : ${ACTIVE_DIR}") - SET(ACTIVE_CPP0xx_DIR "Release") - add_library(lib_activeobject ${ACTIVE_DIR}/active.cpp ${ACTIVE_DIR}/active.h ${ACTIVE_DIR}/shared_queue.h) - set_target_properties(lib_activeobject PROPERTIES LINKER_LANGUAGE CXX) - - - include_directories(src) - include_directories(${LOG_SRC}) - #MESSAGE(" LOG_SRC = : ${LOG_SRC}") - add_library(lib_logger ${LOG_SRC}/logworker.h ${LOG_SRC}/logworker.cpp ${LOG_SRC}/g2log.h ${LOG_SRC}/g2log.cpp ) - set_target_properties(lib_logger PROPERTIES LINKER_LANGUAGE CXX) - target_link_libraries(lib_logger lib_activeobject) - - # create the the example EXECUTABLE - add_executable(g2log-example src/main.cpp) - # link executable with the src library - target_link_libraries(g2log-example lib_activeobject lib_logger justthread rt) - - - # Below are g2log unit testTEST - # and PERFORMANCE comparisons between g2log and google's glog - # - # - include_directories(build) - - # create the the TEST executable - add_executable(g2log-unit_test ../test_main/test_main.cpp test/test_io.cpp) - target_link_libraries(g2log-unit_test lib_activeobject lib_logger gtest_160_lib justthread rt) - - # ---- Below g2log Performance ----- - # create the the g2log MEAN_PERFORMANCE executable - add_executable(g2log-performance-mean test/main_mean.cpp test/performance.h) - set_target_properties(g2log-performance-mean PROPERTIES COMPILE_DEFINITIONS "G2LOG_PERFORMANCE=1") - target_link_libraries(g2log-performance-mean lib_activeobject lib_logger justthread rt) - - # create the the g2log TWO_THREADS_MEAN_PERFORMANCE executable - add_executable(g2log-performance-2threads_mean test/main_2threads_mean.cpp test/performance.h) - set_target_properties(g2log-performance-2threads_mean PROPERTIES COMPILE_DEFINITIONS "G2LOG_PERFORMANCE=1") - target_link_libraries(g2log-performance-2threads_mean lib_activeobject lib_logger justthread rt) - - # create the the g2log TWO_THREADS_WORST_CASE_PERFORMANCE executable - add_executable(g2log-performance-2threads_worst test/main_2threads_worst.cpp test/performance.h) - set_target_properties(g2log-performance-2threads_worst PROPERTIES COMPILE_DEFINITIONS "G2LOG_PERFORMANCE=1") - target_link_libraries(g2log-performance-2threads_worst lib_activeobject lib_logger justthread rt) - - # - # ---- Below GOOGLE glog Performance ----- - # create the the GOOGLE MEAN_PERFORMANCE executable - # Generate the DEFINE (for glog) needed to differentiate between the glog and the g2log test - add_executable(google_glog-performance-mean test/main_mean.cpp test/performance.h) - set_target_properties(google_glog-performance-mean PROPERTIES COMPILE_DEFINITIONS "GOOGLE_GLOG_PERFORMANCE=1") - target_link_libraries(google_glog-performance-mean lib_activeobject glog justthread rt) - - # create the the GOOGLE MEAN_PERFORMANCE executable - add_executable(google_glog-performance-2threads_mean test/main_2threads_mean.cpp test/performance.h) - set_target_properties(google_glog-performance-2threads_mean PROPERTIES COMPILE_DEFINITIONS "GOOGLE_GLOG_PERFORMANCE=1") - target_link_libraries(google_glog-performance-2threads_mean lib_activeobject glog justthread rt) - - # create the the GOOGLE MEAN_PERFORMANCE executable - add_executable(google_glog-performance-2threads_worst test/main_2threads_worst.cpp test/performance.h) - set_target_properties(google_glog-performance-2threads_worst PROPERTIES COMPILE_DEFINITIONS "GOOGLE_GLOG_PERFORMANCE=1") - target_link_libraries(google_glog-performance-2threads_worst lib_activeobject glog justthread rt) - - - - - + set(GTEST_DIR ../3rdParty/gtest/gtest-1.6.0__stripped) + set(GTEST_INCLUDE_DIRECTORIES ${GTEST_DIR}/include ${GTEST_DIR} ${GTEST_DIR}/src) + include_directories(${GTEST_INCLUDE_DIRECTORIES}) + add_library(gtest_160_lib ${GTEST_DIR}/src/gtest-all.cc ${GTEST_DIR}/src/gtest_main.cc) + enable_testing(true) +ENDIF(UNIX) + +#Visual Studio 2010 +IF(WIN32) + MESSAGE("") + MESSAGE("cmake for Visual Studio 2010") + MESSAGE("if cmake finishes OK, do 'msbuild g2log_by_kjellkod.sln'") + MESSAGE("then run 'Debug\\g2log-example.exe' or whatever performance test you feel like trying") + MESSAGE("") + set(PLATFORM_LINK_LIBRIES $ENV{PROGRAMFILES}/JustSoftwareSolutions/JustThread/lib/justthread_vc10_mdd.lib) + set(G2_LOG_FILES ${LOG_SRC}/logworker.h ${LOG_SRC}/logworker.cpp ${LOG_SRC}/g2log.h ${LOG_SRC}/g2log.cpp ${LOG_SRC}/crashhandler.h ${LOG_SRC}/crashhandler_win.cpp) + include_directories("$ENV{PROGRAMFILES}/JustSoftwareSolutions/JustThread/include") +ENDIF(WIN32) + + + +# GENERIC STEPS + # add a ActiveObject library + add_library(lib_activeobject ${LOG_SRC}/active.cpp ${LOG_SRC}/active.h ${LOG_SRC}/shared_queue.h) + set_target_properties(lib_activeobject PROPERTIES LINKER_LANGUAGE CXX) + + # add a g2log library + include_directories(src) + include_directories(${LOG_SRC}) + MESSAGE(" LOG_SRC = : ${LOG_SRC}") + MESSAGE(" g2logger files: [${G2_LOG_FILES}]") + add_library(lib_g2logger ${G2_LOG_FILES}) + set_target_properties(lib_g2logger PROPERTIES LINKER_LANGUAGE CXX) + target_link_libraries(lib_g2logger lib_activeobject) + + + # create the the example EXECUTABLE + add_executable(g2log-example src/main.cpp) + target_link_libraries(g2log-example lib_activeobject lib_g2logger ${PLATFORM_LINK_LIBRIES}) + + + + + + # Below are g2log unit testTEST + # and PERFORMANCE comparisons between g2log and google's glog + # + # + # ---- Below g2log Performance ----- + # create the the g2log MEAN_PERFORMANCE executable + add_executable(g2log-performance-mean test/main_mean.cpp test/performance.h) + set_target_properties(g2log-performance-mean PROPERTIES COMPILE_DEFINITIONS "G2LOG_PERFORMANCE=1") + target_link_libraries(g2log-performance-mean lib_activeobject lib_g2logger ${PLATFORM_LINK_LIBRIES}) + + # create the the g2log TWO_THREADS_MEAN_PERFORMANCE executable + add_executable(g2log-performance-2threads_mean test/main_2threads_mean.cpp test/performance.h) + set_target_properties(g2log-performance-2threads_mean PROPERTIES COMPILE_DEFINITIONS "G2LOG_PERFORMANCE=1") + target_link_libraries(g2log-performance-2threads_mean lib_activeobject lib_g2logger ${PLATFORM_LINK_LIBRIES}) + + # create the the g2log TWO_THREADS_WORST_CASE_PERFORMANCE executable + add_executable(g2log-performance-2threads_worst test/main_2threads_worst.cpp test/performance.h) + set_target_properties(g2log-performance-2threads_worst PROPERTIES COMPILE_DEFINITIONS "G2LOG_PERFORMANCE=1") + target_link_libraries(g2log-performance-2threads_worst lib_activeobject lib_g2logger ${PLATFORM_LINK_LIBRIES}) + + + + +IF(UNIX) + # + # ---- Below GOOGLE glog Performance ----- + # create the the GOOGLE MEAN_PERFORMANCE executable + # Generate the DEFINE (for glog) needed to differentiate between the glog and the g2log test + add_executable(google_glog-performance-mean test/main_mean.cpp test/performance.h) + set_target_properties(google_glog-performance-mean PROPERTIES COMPILE_DEFINITIONS "GOOGLE_GLOG_PERFORMANCE=1") + target_link_libraries(google_glog-performance-mean lib_activeobject glog ${PLATFORM_LINK_LIBRIES}) + + # create the the GOOGLE MEAN_PERFORMANCE executable + add_executable(google_glog-performance-2threads_mean test/main_2threads_mean.cpp test/performance.h) + set_target_properties(google_glog-performance-2threads_mean PROPERTIES COMPILE_DEFINITIONS "GOOGLE_GLOG_PERFORMANCE=1") + target_link_libraries(google_glog-performance-2threads_mean lib_activeobject glog ${PLATFORM_LINK_LIBRIES}) + + # create the the GOOGLE MEAN_PERFORMANCE executable + add_executable(google_glog-performance-2threads_worst test/main_2threads_worst.cpp test/performance.h) + set_target_properties(google_glog-performance-2threads_worst PROPERTIES COMPILE_DEFINITIONS "GOOGLE_GLOG_PERFORMANCE=1") + target_link_libraries(google_glog-performance-2threads_worst lib_activeobject glog ${PLATFORM_LINK_LIBRIES}) + + # create the the TEST executable + add_executable(g2log-unit_test ../test_main/test_main.cpp test/test_io.cpp) + add_library(lib_test_logger ${LOG_SRC}/logworker.h ${LOG_SRC}/logworker.cpp ${LOG_SRC}/g2log.h ${LOG_SRC}/g2log.cpp ${LOG_SRC}/crashhandler.h ${LOG_SRC}/crashhandler.cpp) + set_target_properties(lib_test_logger PROPERTIES LINKER_LANGUAGE CXX) + set_target_properties(lib_test_logger PROPERTIES COMPILE_DEFINITIONS "YALLA=1") + target_link_libraries(lib_test_logger lib_activeobject) + # For unit-test only, dont 'crash' by signals, instead just throw a std::runtime_error + set_target_properties(g2log-unit_test PROPERTIES COMPILE_DEFINITIONS "YALLA=1") + target_link_libraries(g2log-unit_test lib_activeobject lib_test_logger gtest_160_lib justthread rt) ENDIF(UNIX) diff --git a/g2log/src/active.h b/g2log/src/active.h index 12a1071..7997158 100644 --- a/g2log/src/active.h +++ b/g2log/src/active.h @@ -34,8 +34,8 @@ typedef std::function Callback; class Active { private: - Active(const Active&) = delete; - Active& operator=(const Active&) = delete; + Active(const Active&); // c++11 feature not yet in vs2010 = delete; + Active& operator=(const Active&); // c++11 feature not yet in vs2010 = delete; Active(); // Construction ONLY through factory createActive(); diff --git a/g2log/src/crashhandler.h b/g2log/src/crashhandler.h new file mode 100644 index 0000000..1569155 --- /dev/null +++ b/g2log/src/crashhandler.h @@ -0,0 +1,33 @@ +#ifndef CRASH_HANDLER_H_ +#define CRASH_HANDLER_H_ + +#include +#include + +namespace g2 +{ + +// PRIVATE-INTERNAL API +namespace internal +{ +/** \return signal_name. Ref: signum.h and \ref installSignalHandler */ +std::string signalName(int signal_number); + +/** 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 g2log, LogWorker after flushing messages to file */ +void exitWithDefaultSignalHandler(int signal_number); +} // end g2::interal + + +// PUBLIC API: +/** Install signal handler that catches FATAL C-runtime or OS signals + SIGABRT ABORT (ANSI), abnormal termination + SIGFPE Floating point exception (ANSI): http://en.wikipedia.org/wiki/SIGFPE + SIGILL ILlegal instruction (ANSI) + SIGSEGV Segmentation violation i.e. illegal memory reference + SIGTERM TERMINATION (ANSI) */ +void installSignalHandler(); +} + +#endif // CRASH_HANDLER_H_ diff --git a/g2log/src/crashhandler_unix.cpp b/g2log/src/crashhandler_unix.cpp new file mode 100644 index 0000000..9fc1abd --- /dev/null +++ b/g2log/src/crashhandler_unix.cpp @@ -0,0 +1,125 @@ +#include "crashhandler.h" +#include "g2log.h" + +#include +#include +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) +// or? gcc on windows I guess,. +#error "crashhandler_unix.cpp used but it's a windows system" +#endif + + +#include // getpid, +#include +#include +#include + +namespace +{ +// simple dump of stack,. then exit through g2log background worker +// Ref: http://stackoverflow.com/questions/77005/how-to-generate-a-stacktrace-when-my-gcc-c-app-crashes +void crashHandler(int signal_number, siginfo_t *info, void *unused_context) +{ + const size_t max_dump_size = 100; + void* dump[max_dump_size]; + size_t size = backtrace(dump, max_dump_size); + // overwrite sigaction with caller's address + char** messages = backtrace_symbols(dump, size); + + std::ostringstream oss; + oss << "Received fatal signal: " << g2::internal::signalName(signal_number); + oss << "(" << signal_number << ")" << std::endl; + oss << "\tPID: " << getpid() << std::endl; + // dump stack: skip first frame, since that is here + for(size_t idx = 1; idx < size && messages != nullptr; ++idx) + { + oss << "\tstack dump [" << idx << "] " << messages[idx] << std::endl; + } + free(messages); + { // Local scope, trigger send + using namespace g2::internal; + std::ostringstream fatal_stream; + fatal_stream << "\n\n***** FATAL TRIGGER RECEIVED ******* " << std::endl; + fatal_stream << oss.str() << std::endl; + fatal_stream << "\n***** RETHROWING SIGNAL " << signalName(signal_number) << "(" << signal_number << ")" << std::endl; + + FatalMessage fatal_message(fatal_stream.str(),FatalMessage::kReasonOS_FATAL_SIGNAL, signal_number); + FatalTrigger trigger(fatal_message); std::ostringstream oss; + std::cerr << fatal_message.message_ << std::endl << std::flush; + } // message sent to LogWorker + // wait to die -- will be inside the FatalTrigger +} +} // end anonymous namespace + + + +// Redirecting and using signals. In case of fatal signals g2log should log the fatal signal +// and flush the log queue and then "rethrow" the signal to exit +namespace g2 +{ +// References: +// sigaction : change the default action if a specific signal is received +// http://linux.die.net/man/2/sigaction +// http://publib.boulder.ibm.com/infocenter/aix/v6r1/index.jsp?topic=%2Fcom.ibm.aix.basetechref%2Fdoc%2Fbasetrf2%2Fsigaction.html +// +// signal: http://linux.die.net/man/7/signal and +// http://msdn.microsoft.com/en-us/library/xdkz3x12%28vs.71%29.asp +// +// memset + sigemptyset: Maybe unnecessary to do both but there seems to be some confusion here +// ,plenty of examples when both or either are used +// http://stackoverflow.com/questions/6878546/why-doesnt-parent-process-return-to-the-exact-location-after-handling-signal_number +namespace internal +{ +std::string signalName(int signal_number) +{ + switch(signal_number) + { + 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(" << signal_number << ")"; + return oss.str(); + } +} + +// Triggered by g2log::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(int signal_number) +{ + struct sigaction action; + memset(&action, 0, sizeof(action)); // + sigemptyset(&action.sa_mask); + action.sa_handler = SIG_DFL; // take default action for the signal + sigaction(signal_number, &action, NULL); + kill(getpid(), signal_number); +} +} // end g2::internal + + +void installSignalHandler() +{ + struct sigaction action; + memset(&action, 0, sizeof(action)); + sigemptyset(&action.sa_mask); + action.sa_sigaction = &crashHandler; // callback to crashHandler for fatal signals + // sigaction to use sa_sigaction file. ref: http://www.linuxprogrammingblog.com/code-examples/sigaction + action.sa_flags = SA_SIGINFO; + + // do it verbose style - install all signal actions + if(sigaction(SIGABRT, &action, NULL) < 0) + perror("sigaction - SIGABRT"); + if(sigaction(SIGFPE, &action, NULL) < 0) + perror("sigaction - SIGFPE"); + if(sigaction(SIGILL, &action, NULL) < 0) + perror("sigaction - SIGILL"); + if(sigaction(SIGSEGV, &action, NULL) < 0) + perror("sigaction - SIGSEGV"); + if(sigaction(SIGTERM, &action, NULL) < 0) + perror("sigaction - SIGTERM"); +} +} // end namespace g2 diff --git a/g2log/src/crashhandler_win.cpp b/g2log/src/crashhandler_win.cpp new file mode 100644 index 0000000..bf315de --- /dev/null +++ b/g2log/src/crashhandler_win.cpp @@ -0,0 +1,86 @@ +#include "crashhandler.h" +#include "g2log.h" + +#include +#include +#include +#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) +#error "crashhandler_win.cpp used but not on a windows system" +#endif + +#include // getpid +#define getpid _getpid + +namespace +{ +void crashHandler(int signal_number) +{ + using namespace g2::internal; + std::ostringstream fatal_stream; + fatal_stream << "\n\n***** FATAL TRIGGER RECEIVED ******* " << std::endl; + fatal_stream << "\n***** RETHROWING SIGNAL " << signalName(signal_number) << "(" << signal_number << ")" << std::endl; + + FatalMessage fatal_message(fatal_stream.str(),FatalMessage::kReasonOS_FATAL_SIGNAL, signal_number); + FatalTrigger trigger(fatal_message); std::ostringstream oss; + std::cerr << fatal_message.message_ << std::endl << std::flush; +} // scope exit - message sent to LogWorker, wait to die... +} // end anonymous namespace + + +namespace g2 +{ +namespace internal +{ +std::string signalName(int signal_number) +{ + switch(signal_number) + { + 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(" << signal_number << ")"; + return oss.str(); + } +} + + +// Triggered by g2log::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(int signal_number) +{ + // Restore our signalhandling to default + if(SIG_ERR == signal (SIGABRT, SIG_DFL)) + perror("signal - SIGABRT"); + if(SIG_ERR == signal (SIGFPE, SIG_DFL)) + perror("signal - SIGABRT"); + if(SIG_ERR == signal (SIGSEGV, SIG_DFL)) + perror("signal - SIGABRT"); + if(SIG_ERR == signal (SIGILL, SIG_DFL)) + perror("signal - SIGABRT"); + if(SIG_ERR == signal (SIGTERM, SIG_DFL)) + perror("signal - SIGABRT"); + + raise(signal_number); +} +} // end g2::internal + + +void installSignalHandler() +{ + if(SIG_ERR == signal (SIGABRT, crashHandler)) + perror("signal - SIGABRT"); + if(SIG_ERR == signal (SIGFPE, crashHandler)) + perror("signal - SIGFPE"); + if(SIG_ERR == signal (SIGSEGV, crashHandler)) + perror("signal - SIGSEGV"); + if(SIG_ERR == signal (SIGILL, crashHandler)) + perror("signal - SIGILL"); + if(SIG_ERR == signal (SIGTERM, crashHandler)) + perror("signal - SIGTERM"); +} +} // end namespace g2 diff --git a/g2log/src/g2log.cpp b/g2log/src/g2log.cpp index 6b59579..31a4707 100644 --- a/g2log/src/g2log.cpp +++ b/g2log/src/g2log.cpp @@ -20,7 +20,10 @@ #include // vsnprintf #include #include + #include "logworker.h" +#include "crashhandler.h" +#include namespace g2 { @@ -52,10 +55,17 @@ std::string splitFileName(const std::string& str) void initializeLogging(LogWorker *bgworker) { + static bool once_only_signalhandler = false; std::lock_guard lock(internal::g_logging_init_mutex); CHECK(!internal::isLoggingInitialized()); CHECK(bgworker != nullptr); internal::g_logger_instance = bgworker; + + if(false == once_only_signalhandler) + { + installSignalHandler(); + once_only_signalhandler = true; + } } LogWorker* shutDownLogging() @@ -83,11 +93,11 @@ LogContractMessage::~LogContractMessage() std::ostringstream oss; if(0 == expression_.compare(k_fatal_log_expression)) { - oss << "[ *******\tRUNTIME EXCEPTION caused by LOG(FATAL):\t"; + oss << "\n[ *******\tEXIT trigger caused by LOG(FATAL): \n\t"; } else { - oss << "\nRUNTIME EXCEPTION caused by broken Contract: [" << expression_ << "]\t"; + oss << "\n[ *******\tEXIT trigger caused by broken Contract: CHECK(" << expression_ << ")\n\t"; } log_entry_ = oss.str(); } @@ -103,11 +113,12 @@ LogMessage::LogMessage(const std::string &file, const int line, const std::strin LogMessage::~LogMessage() { + using namespace internal; std::ostringstream oss; const bool fatal = (0 == level_.compare("FATAL")); - oss << level_ << " [" << internal::splitFileName(file_); + oss << level_ << " [" << splitFileName(file_); if(fatal) - oss << " F: " << function_ ; + oss << " at: " << function_ ; oss << " L: " << line_ << "]\t"; const std::string str(stream_.str()); @@ -117,24 +128,49 @@ LogMessage::~LogMessage() } log_entry_ += oss.str(); - if(!internal::isLoggingInitialized() ) + if(!isLoggingInitialized() ) { std::cerr << "Did you forget to call g2::InitializeLogging(LogWorker*) in your main.cpp?" << std::endl; std::cerr << log_entry_ << std::endl << std::flush; throw std::runtime_error("Logger not initialized with g2::InitializeLogging(LogWorker*) for msg:\n" + log_entry_); } - internal::g_logger_instance->save(log_entry_); // message saved - if(fatal) + + if(fatal) // os_fatal is handled by crashhandlers { - std::cerr << log_entry_ << "\t******* ]" << std::endl << std::flush; - throw std::runtime_error(log_entry_); + { // local scope - to trigger FatalMessage sending + FatalMessage::FatalType fatal_type(FatalMessage::kReasonFatal); + FatalMessage fatal_message(log_entry_, fatal_type, SIGABRT); + FatalTrigger trigger(fatal_message); + std::cerr << log_entry_ << "\t******* ]" << std::endl << std::flush; + } // will send to worker } - - + internal::g_logger_instance->save(log_entry_); // message saved } +// represents the actual fatal message +FatalMessage::FatalMessage(std::string message, FatalType type, int signal_id) + : message_(message) + , type_(type) + , signal_id_(signal_id){} + +// used to RAII trigger fatal message sending to LogWorker +FatalTrigger::FatalTrigger(const FatalMessage &message) + : message_(message){} + +// at destruction, flushes fatal message to LogWorker +FatalTrigger::~FatalTrigger() +{ + internal::g_logger_instance->fatal(message_); + #if !defined(YALLA) // don't sleep if unit-testing + // wait to die + while(true){std::this_thread::sleep_for(std::chrono::seconds(1));} +#endif +} + + + void LogMessage::messageSave(const char *printf_like_message, ...) { char finished_message[constants::kMaxMessageSize]; diff --git a/g2log/src/g2log.h b/g2log/src/g2log.h index e59dfbe..b1a524a 100644 --- a/g2log/src/g2log.h +++ b/g2log/src/g2log.h @@ -23,8 +23,14 @@ class LogWorker; +#if !(defined(__PRETTY_FUNCTION__)) +#define __PRETTY_FUNCTION__ __FUNCTION__ +#endif + + // Levels for logging, made so that it would be easy to change, remove, add levels -- KjellKod const int DEBUG = 0, INFO = 1, WARNING = 2, FATAL = 3; +static const std::string k_os_signal_fatal_text = "OS_SIGNAL_FATAL"; static const std::string k_fatal_log_expression = ""; // using LogContractMessage but no boolean expression // GCC Predefined macros: http://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html @@ -155,6 +161,28 @@ typedef std::chrono::duration > millisecond; typedef std::chrono::duration > microsecond; typedef const std::string& LogEntry; +/** Trigger for flushing the message queue and exiting the applicaition + A thread that causes a FatalMessage will sleep forever until the + application has exited (after message flush) */ +struct FatalMessage +{ + enum FatalType {kReasonFatal, kReasonOS_FATAL_SIGNAL}; + FatalMessage(std::string message, FatalType type, int signal_id); + + std::string message_; + FatalType type_; + int signal_id_; +}; +// Will trigger a FatalMessage sending +struct FatalTrigger +{ + FatalTrigger(const FatalMessage& message); + ~FatalTrigger(); + FatalMessage message_; +}; + + + // Log message for 'printf-like' or stream logging, it's a temporary message constructions class LogMessage { @@ -166,11 +194,15 @@ class LogMessage // The __attribute__ generates compiler warnings if illegal "printf" format // IMPORTANT: You muse enable the compiler flag '-Wall' for this to work! - // ref: http://www.unixwiz.net/techtips/gnu-c-attributes.html - // + // ref: http://www.unixwiz.net/techtips/gnu-c-attributes.html + // + //If the compiler does not support attributes, disable them +#ifndef __GNUC__ +#define __attribute__(x) +#endif // Coder note: Since it's C++ and not C EVERY CLASS FUNCTION always get a first // compiler given argument 'this' this must be supplied as well, hence '2,3' - // ref: http://www.codemaestro.com/reviews/18 -- ref KjellKod + // ref: http://www.codemaestro.com/reviews/18 -- ref KjellKod void messageSave(const char *printf_like_message, ...) __attribute__((format(printf,2,3) )); diff --git a/g2log/src/logworker.cpp b/g2log/src/logworker.cpp index b1ad66b..628857c 100644 --- a/g2log/src/logworker.cpp +++ b/g2log/src/logworker.cpp @@ -6,6 +6,7 @@ * ********************************************* */ #include "logworker.h" + #include #include #include @@ -16,8 +17,13 @@ #include #include + #if defined(YALLA) +#include // exceptions +#endif + #include "active.h" #include "g2log.h" +#include "crashhandler.h" using namespace g2::internal; namespace @@ -57,6 +63,7 @@ struct LogWorkerImpl ~LogWorkerImpl(); void backgroundFileWrite(g2::internal::LogEntry message); + void backgroundExitFatal(g2::internal::FatalMessage fatal_message); std::string log_file_with_path_; std::unique_ptr bg_; @@ -64,9 +71,8 @@ struct LogWorkerImpl g2::internal::time_point start_time_; private: - LogWorkerImpl& operator=(const LogWorkerImpl&) = delete; // no assignment, no copy - LogWorkerImpl(const LogWorkerImpl& other) = delete; - + LogWorkerImpl& operator=(const LogWorkerImpl&); // c++11 feature not yet in vs2010 = delete; + LogWorkerImpl(const LogWorkerImpl& other); // c++11 feature not yet in vs2010 = delete; }; @@ -127,9 +133,24 @@ void LogWorkerImpl::backgroundFileWrite(LogEntry message) out << "\n" << t.year << "/" << setw(2) << t.month << "/" << setw(2) << t.day; out << " " << setw(2) << t.hour << ":"<< setw(2) << t.minute <<":"<< setw(2) << t.second; out << "." << chrono::duration_cast(timesnapshot - start_time_).count(); //microseconds - out << "\t" << message; + out << "\t" << message << std::flush; } +void LogWorkerImpl::backgroundExitFatal(FatalMessage fatal_message) +{ + backgroundFileWrite(fatal_message.message_); + + #if defined(YALLA) + // If running unit test - we simplify matters by not sending the signal, but + // by just throwing an exception + throw std::runtime_error(fatal_message.message_); + return; +#endif + + out.close(); + exitWithDefaultSignalHandler(fatal_message.signal_id_); + perror("g2log exited after receiving FATAL trigger. Flush message status: "); // should never reach this point +} @@ -144,7 +165,7 @@ void LogWorkerImpl::backgroundFileWrite(LogEntry message) LogWorker::~LogWorker() { pimpl_.reset(); - //std::cout << "\nLogWorker finished with log: " << log_file_with_path_ << std::endl << std::flush; + std::cout << "\nExiting, log location: " << log_file_with_path_ << std::endl << std::flush; } void LogWorker::save(g2::internal::LogEntry msg) @@ -152,6 +173,10 @@ void LogWorkerImpl::backgroundFileWrite(LogEntry message) pimpl_->bg_->send(std::tr1::bind(&LogWorkerImpl::backgroundFileWrite, pimpl_.get(), msg)); } + void LogWorker::fatal(g2::internal::FatalMessage fatal_message) + { + pimpl_->bg_->send(std::tr1::bind(&LogWorkerImpl::backgroundExitFatal, pimpl_.get(), fatal_message)); + } std::string LogWorker::logFileName() const { diff --git a/g2log/src/logworker.h b/g2log/src/logworker.h index a2ac824..19af1fc 100644 --- a/g2log/src/logworker.h +++ b/g2log/src/logworker.h @@ -22,7 +22,12 @@ public: virtual ~LogWorker(); /// pushes in background thread (asynchronously) input messages to log file - void save(g2::internal::LogEntry); + void save(g2::internal::LogEntry entry); + + /// Will push 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(g2::internal::FatalMessage fatal_message); /// basically only needed for unit-testing or specific log management post logging std::string logFileName() const; @@ -31,8 +36,8 @@ private: std::unique_ptr pimpl_; const std::string log_file_with_path_; - LogWorker(const LogWorker&) = delete; // no assignment, no copy - LogWorker& operator=(const LogWorker&) = delete; + LogWorker(const LogWorker&); // c++11 feature not yet in vs2010 = delete; + LogWorker& operator=(const LogWorker&); // c++11 feature not yet in vs2010 = delete; }; diff --git a/g2log/src/main.cpp b/g2log/src/main.cpp index f652c11..cab7c63 100644 --- a/g2log/src/main.cpp +++ b/g2log/src/main.cpp @@ -10,13 +10,22 @@ #include "logworker.h" #include +namespace +{ +#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) + const std::string path_to_log_file = "./"; +#else + const std::string path_to_log_file = "/tmp/"; +#endif +} + int main(int argc, char** argv) { double pi_d = 3.1415926535897932384626433832795; float pi_f = 3.1415926535897932384626433832795f; - LogWorker logger(argv[0], "/tmp/"); + LogWorker logger(argv[0], path_to_log_file); g2::initializeLogging(&logger); std::cout << "****** A NUMBER of 'runtime exceptions' will be printed on this screen" << std::endl; @@ -90,7 +99,7 @@ int main(int argc, char** argv) CHECK(1<2) << "SHOULD NOT SEE THIS MESSAGE"; CHECK(1>2) << "Test to see if contract works: onetwothree: " << 123 << ". This should be inside an exception"; } - catch(std::exception& exc) + catch(...) { std::cout << "\n***** All good, the 'exception' was part of the example\n\n\n" << std::endl; return 0; diff --git a/g2log/src/shared_queue.h b/g2log/src/shared_queue.h index f1586fa..b0806fc 100644 --- a/g2log/src/shared_queue.h +++ b/g2log/src/shared_queue.h @@ -29,8 +29,8 @@ class shared_queue mutable std::mutex m_; std::condition_variable data_cond_; - shared_queue& operator=(const shared_queue&) = delete; - shared_queue(const shared_queue& other) = delete; + shared_queue& operator=(const shared_queue&); // c++11 feature not yet in vs2010 = delete; + shared_queue(const shared_queue& other); // c++11 feature not yet in vs2010 = delete; public: shared_queue(){} diff --git a/g2log/test/main_2threads_worst.cpp b/g2log/test/main_2threads_worst.cpp index 8c2dab4..f695bec 100644 --- a/g2log/test/main_2threads_worst.cpp +++ b/g2log/test/main_2threads_worst.cpp @@ -97,7 +97,7 @@ int main(int argc, char** argv) oss.str(""); oss << "Number of values rounted to milliseconds and put to [millisecond bucket] were dumped to file: " << g_measurement_bucket_dump << std::endl; - oss << "Format: bucket_of_ms, number_of_values_in_bucket"; + oss << "Format: bucket_of_ms, number_of_values_in_bucket\n\n" << std::endl; std::cout << oss.str() << std::endl; for(auto iter = value_amounts.begin(); iter != value_amounts.end(); ++iter) diff --git a/g2log/test/main_mean.cpp b/g2log/test/main_mean.cpp index e9c016d..1c77fdd 100644 --- a/g2log/test/main_mean.cpp +++ b/g2log/test/main_mean.cpp @@ -40,8 +40,6 @@ int main(int argc, char** argv) google::InitGoogleLogging(argv[0]); #endif - - auto start_time = std::chrono::steady_clock::now(); doLogWrites(title); auto application_end_time = std::chrono::steady_clock::now(); diff --git a/g2log/test/performance.h b/g2log/test/performance.h index 41c0210..fb1b7c9 100644 --- a/g2log/test/performance.h +++ b/g2log/test/performance.h @@ -66,6 +66,7 @@ bool writeTextToFile(const std::string& filename, const std::string& msg, const std::cerr << ss_error.str().c_str() << std::endl << std::flush; return false; } + out << msg; return true; } diff --git a/g2log/test/test_io.cpp b/g2log/test/test_io.cpp index 93cbb31..bd8b0f8 100644 --- a/g2log/test/test_io.cpp +++ b/g2log/test/test_io.cpp @@ -16,6 +16,8 @@ namespace { +const int k_wait_time = 5; // 5s wait between LOG/CHECK FATAL till we say it's too long time + bool verifyContent(const std::string &total_text,std::string msg_to_find) { std::string content(total_text); @@ -173,6 +175,7 @@ TEST(LogTest, LOGF__FATAL) try { LOGF(FATAL, "This message should throw %d",0); + sleep(k_wait_time); } catch (std::exception const &e) { @@ -200,6 +203,7 @@ TEST(LogTest, LOG_FATAL) try { LOG(FATAL) << "This message should throw"; + sleep(k_wait_time); } catch (std::exception const &e) { @@ -227,6 +231,7 @@ TEST(LogTest, LOGF_IF__FATAL) try { LOGF_IF(FATAL, (2<3), "This message%sshould throw"," "); + sleep(k_wait_time); } catch (std::exception const &e) { @@ -255,6 +260,7 @@ TEST(LogTest, LOG_IF__FATAL) { LOG_IF(WARNING, (0 != t_info.compare(t_info))) << "This message should NOT be written"; LOG_IF(FATAL, (0 != t_info.compare(t_info2))) << "This message should throw"; + sleep(k_wait_time); } catch (std::exception const &e) { @@ -326,6 +332,7 @@ TEST(CHECK_F_Test, CHECK_F__thisWILL_PrintErrorMsg) try { CHECK_F(1 >= 2, msg.c_str(), arg1.c_str(), arg2.c_str()); + sleep(k_wait_time); } catch (std::exception const &e) {