diff --git a/Build.cmake b/Build.cmake index 7e2f761..e258bc0 100644 --- a/Build.cmake +++ b/Build.cmake @@ -28,7 +28,7 @@ IF ("${CMAKE_CXX_COMPILER_ID}" MATCHES ".*Clang") ELSE() set(PLATFORM_LINK_LIBRIES rt c++abi) ENDIF() - SET(CMAKE_CXX_FLAGS "-Wall -std=c++11 -pthread -stdlib=libc++ -Wunused -D_GLIBCXX_USE_NANOSLEEP") + SET(CMAKE_CXX_FLAGS "-Wall -std=c++11 -stdlib=libc++ -Wunused -D_GLIBCXX_USE_NANOSLEEP") diff --git a/example/main_fatal_choice.cpp b/example/main_fatal_choice.cpp index 45fbac6..a7dcd81 100644 --- a/example/main_fatal_choice.cpp +++ b/example/main_fatal_choice.cpp @@ -16,6 +16,7 @@ #include #include #include +#include namespace @@ -34,34 +35,34 @@ namespace } void RaiseSIGABRT() { - std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; raise(SIGABRT); + LOG(DEBUG) << " trigger exit"; LOG(WARNING) << "Expected to have died by now..."; } void RaiseSIGFPE() { - std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; + LOG(DEBUG) << " trigger exit"; LOGF_IF(INFO, (false != true), "Exiting %s SIGFPE", "by"); raise(SIGFPE); LOG(WARNING) << "Expected to have died by now..."; } void RaiseSIGSEGV() { - std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; + LOG(DEBUG) << " trigger exit"; LOG(DEBUG) << "Exit by SIGSEGV"; raise(SIGSEGV); LOG(WARNING) << "Expected to have died by now..."; } void RaiseSIGILL() { - std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; + LOG(DEBUG) << " trigger exit"; LOGF(DEBUG, "Exit by %s", "SIGILL"); raise(SIGILL); LOG(WARNING) << "Expected to have died by now..."; } void RAiseSIGTERM() { - std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; + LOG(DEBUG) << " trigger exit"; LOGF_IF(INFO, (false != true), "Exiting %s SIGFPE", "by"); raise(SIGTERM); LOG(WARNING) << "Expected to have died by now..."; @@ -69,8 +70,7 @@ namespace int gShouldBeZero = 1; void DivisionByZero() { - std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; - std::cout << "Executing DivisionByZero: gShouldBeZero: " << gShouldBeZero << std::endl; + LOG(DEBUG) << " trigger exit Executing DivisionByZero: gShouldBeZero: " << gShouldBeZero; LOG(INFO) << "Division by zero is a big no-no"; int value = 3; auto test = value / gShouldBeZero; @@ -78,14 +78,14 @@ namespace } void IllegalPrintf() { - std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; + LOG(DEBUG) << " trigger exit"; LOG(DEBUG) << "Impending doom due to illeteracy"; LOGF(INFO, "2nd attempt at ILLEGAL PRINTF_SYNTAX %d EXAMPLE. %s %s", "hello", 1); LOG(WARNING) << "Expected to have died by now..."; } void OutOfBoundsArrayIndexing() { - std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; + LOG(DEBUG) << " trigger exit"; std::vector v; v[0] = 5; LOG(WARNING) << "Expected to have died by now..."; @@ -93,7 +93,7 @@ namespace void AccessViolation() { - std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; + LOG(DEBUG) << " trigger exit"; char *ptr = 0; LOG(INFO) << "Death by access violation is imminent"; *ptr = 0; @@ -101,12 +101,12 @@ namespace } void NoExitFunction() { - std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; + LOG(DEBUG) << " trigger exit"; CHECK(false) << "This function should never be called"; } void RaiseSIGABRTAndAccessViolation() { - std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; + LOG(DEBUG) << " trigger exit"; auto f1 = std::async(std::launch::async, &RaiseSIGABRT); auto f2 = std::async(std::launch::async, &AccessViolation); @@ -114,11 +114,32 @@ namespace f2.wait(); } - void ThrowInt() { - throw 1233210; + void AccessViolation_x10000() { + LOG(DEBUG) << " trigger exit"; + std::vector> asyncs; + asyncs.reserve(1000); + for (auto idx = 0; idx < 1000; ++idx) { + asyncs.push_back(std::async(std::launch::async, &AccessViolation)); + } + + for (const auto& a: asyncs) { + a.wait(); + } + + std::cout << __FUNCTION__ << " unexpected result. AccessViolation x many did not crash and exit the system" << std::endl; + + } + + void Throw() { + LOG(DEBUG) << " trigger exit"; + std::future empty; + empty.get(); + // --> thows future_error http://en.cppreference.com/w/cpp/thread/future_error + // example of std::exceptions can be found here: http://en.cppreference.com/w/cpp/error/exception } void FailedCHECK() { + LOG(DEBUG) << " trigger exit"; CHECK(false) << "This is fatal"; } @@ -133,7 +154,8 @@ namespace void ExecuteDeathFunction(const bool runInNewThread, int fatalChoice) { - std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; + LOG(DEBUG) << "trigger exit"; + auto exitFunction = &NoExitFunction; switch (fatalChoice) { case 1: exitFunction = &RaiseSIGABRT; break; @@ -146,8 +168,9 @@ namespace case 8: exitFunction = &OutOfBoundsArrayIndexing; break; case 9: exitFunction = &AccessViolation; break; case 10: exitFunction = &RaiseSIGABRTAndAccessViolation; break; - case 11: exitFunction = &ThrowInt; break; + case 11: exitFunction = &Throw; break; case 12: exitFunction = &FailedCHECK; break; + case 13: exitFunction = &AccessViolation_x10000; break; default: break; } if (runInNewThread) { @@ -203,8 +226,9 @@ namespace std::cout << "[8] Out of bounds array indexing " << std::endl; std::cout << "[9] Access violation" << std::endl; std::cout << "[10] Rasing SIGABRT + Access Violation in two separate threads" << std::endl; - std::cout << "[11] Just throw (in this thread)" << std::endl; + std::cout << "[11] Throw a std::future_error" << std::endl; std::cout << "[12] Just CHECK(false) (in this thread)" << std::endl; + std::cout << "[13] 1000 Continious crashes with out of counds array indexing" << std::endl; std::cout << std::flush; @@ -212,7 +236,7 @@ namespace try { std::getline(std::cin, option); choice = std::stoi(option); - if (choice <= 0 || choice > 12) { + if (choice <= 0 || choice > 13) { std::cout << "Invalid choice: [" << option << "\n\n"; } else { return choice; @@ -235,6 +259,10 @@ namespace } // namespace void breakHere() { + std::ostringstream oss; + oss << "Fatal hook function: " << __FUNCTION__ << ":" << __LINE__ " was called"; + oss << " through g3::setFatalPreLoggingHook(). setFatalPreLoggingHook should be called AFTER g3::initializeLogging()" << std::endl; + LOG(DEBUG) << oss.str(); #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) __debugbreak(); #endif diff --git a/src/crashhandler_unix.cpp b/src/crashhandler_unix.cpp index 42e0d2c..958ab5b 100644 --- a/src/crashhandler_unix.cpp +++ b/src/crashhandler_unix.cpp @@ -23,7 +23,7 @@ #include #include #include - +#include // temporary to be removed // Linux/Clang, OSX/Clang, OSX/gcc #if (defined(__clang__) || defined(__APPLE__)) @@ -34,10 +34,51 @@ namespace { + bool shouldDoExit() { + static std::atomic firstExit{0}; + auto const count = firstExit.fetch_add(1, std::memory_order_relaxed); + return (0 == count); + } + + void restoreSignalHandler(int signal_number) { + std::cerr << "\n\n" << __FUNCTION__ << " " << signal_number << " threadID: " << std::this_thread::get_id() << std::endl; +#if !(defined(DISABLE_FATAL_SIGNALHANDLING)) + 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); +#endif + } + // Dump of stack,. then exit through g3log background worker // ALL thanks to this thread at StackOverflow. Pretty much borrowed from: // Ref: http://stackoverflow.com/questions/77005/how-to-generate-a-stacktrace-when-my-gcc-c-app-crashes void signalHandler(int signal_number, siginfo_t *info, void *unused_context) { + + // Only one signal will be allowed past this point + if (false == shouldDoExit()) { + while (true) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + } + + + Kjell + // Does this make a difference? + // http://man7.org/linux/man-pages/man2/sigaltstack.2.html + stack_t ss; + ss.ss_sp = malloc(SIGSTKSZ); + if (ss.ss_sp == NULL) { + // Handle error -- no error handling at this moment + } + ss.ss_size = SIGSTKSZ; + ss.ss_flags = 0; + if (sigaltstack(&ss, NULL) == -1) { + std::cerr << "\n\n\n\n\nFAILED\n\n\n\n\n" << std::endl; + return; + } + using namespace g3::internal; { const auto dump = stackdump(); @@ -74,10 +115,11 @@ namespace g3 { // http://stackoverflow.com/questions/6878546/why-doesnt-parent-process-return-to-the-exact-location-after-handling-signal_number namespace internal { - bool blockForFatalHandling() { + bool shouldBlockForFatalHandling() { return true; // For windows we will after fatal processing change it to false } + /// 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 *rawdump) { @@ -180,20 +222,16 @@ namespace g3 { // Triggered by g3log->g3LogWorker 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) { + void exitWithDefaultSignalHandler(const LEVELS &level, g3::SignalType fatal_signal_id) { const int signal_number = static_cast(fatal_signal_id); - std::cerr << "Exiting due to " << level.text << ", " << signal_number << " " << std::flush; + restoreSignalHandler(signal_number); + std::cerr << "\n\n" << __FUNCTION__ << ":" << __LINE__ << ". Exiting due to " << level.text << ", " << signal_number << " \n\n" << std::flush; -#if !(defined(DISABLE_FATAL_SIGNALHANDLING)) - 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); -#endif - - kill(getpid(), signal_number); - abort(); // should never reach this + TODO kjell --- + what is the difference between kill and exit? + does it have any significance for stack trace + exit(signal_number); //kill(getpid(), signal_number); + } } // end g3::internal diff --git a/src/crashhandler_windows.cpp b/src/crashhandler_windows.cpp index 338081d..2873a14 100644 --- a/src/crashhandler_windows.cpp +++ b/src/crashhandler_windows.cpp @@ -148,11 +148,9 @@ 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 blockForFatalHandling() { + bool shouldBlockForFatalHandling() { return gBlockForFatal; } diff --git a/src/g3log.cpp b/src/g3log.cpp index 497797d..f3e3a65 100644 --- a/src/g3log.cpp +++ b/src/g3log.cpp @@ -233,7 +233,7 @@ namespace g3 { internal::exitWithDefaultSignalHandler(message.get()->_level, message.get()->_signal_id); } g_logger_instance->fatal(message); - while (blockForFatalHandling()) { + while (shouldBlockForFatalHandling()) { std::this_thread::sleep_for(std::chrono::seconds(1)); } } diff --git a/src/g3log/crashhandler.hpp b/src/g3log/crashhandler.hpp index 3417b5a..af891b2 100644 --- a/src/g3log/crashhandler.hpp +++ b/src/g3log/crashhandler.hpp @@ -11,11 +11,22 @@ #include #include "g3log/loglevels.hpp" #include "g3log/generated_definitions.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.hpptm + 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; @@ -35,30 +46,18 @@ namespace g3 { * 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 blockForFatalHandling(); + 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); + std::string exitReasonName(const LEVELS& level, g3::SignalType signal_number); /** return calling thread's stackdump*/ - std::string stackdump(const char *dump = nullptr); + 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); + void exitWithDefaultSignalHandler(const LEVELS& level, g3::SignalType signal_number); } // end g3::internal - - - // 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.hpptm - 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(); -} +} // g3