incremental
diff --git a/g3log/crashhandler_unix.cpp b/g3log/crashhandler_unix.cpp
new file mode 100644
index 0000000..3e1b7fe
--- /dev/null
+++ b/g3log/crashhandler_unix.cpp
@@ -0,0 +1,274 @@
+/** ==========================================================================
+ * 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/crashhandler.hpp"
+#include "g3log/logmessage.hpp"
+#include "g3log/logcapture.hpp"
+#include "g3log/loglevels.hpp"
+
+#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) && !defined(__GNUC__))
+#error "crashhandler_unix.cpp used but it's a windows system"
+#endif
+
+
+#include <csignal>
+#include <cstring>
+#include <unistd.h>
+#include <execinfo.h>
+#include <cxxabi.h>
+#include <cstdlib>
+#include <sstream>
+#include <iostream>
+#include <thread>
+#include <atomic>
+#include <map>
+#include <mutex>
+
+// Linux/Clang, OSX/Clang, OSX/gcc
+#if (defined(__clang__) || defined(__APPLE__))
+#include <sys/ucontext.h>
+#else
+#include <ucontext.h>
+#endif
+
+
+namespace {
+
+ const std::map<int, std::string> kSignals = {
+ {SIGABRT, "SIGABRT"},
+ {SIGFPE, "SIGFPE"},
+ {SIGILL, "SIGILL"},
+ {SIGSEGV, "SIGSEGV"},
+ {SIGTERM, "SIGTERM"},
+ };
+
+ std::map<int, std::string> gSignals = kSignals;
+
+
+ bool shouldDoExit() {
+ static std::atomic<uint64_t> firstExit{0};
+ auto const count = firstExit.fetch_add(1, std::memory_order_relaxed);
+ return (0 == count);
+ }
+
+ void restoreSignalHandler(int signal_number) {
+#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));
+ }
+ }
+
+ using namespace g3::internal;
+ {
+ const auto dump = stackdump();
+ std::ostringstream fatal_stream;
+ const auto fatal_reason = exitReasonName(g3::internal::FATAL_SIGNAL, signal_number);
+ fatal_stream << "Received fatal signal: " << fatal_reason;
+ fatal_stream << "(" << signal_number << ")\tPID: " << getpid() << std::endl;
+ fatal_stream << "\n***** SIGNAL " << fatal_reason << "(" << signal_number << ")" << std::endl;
+ LogCapture trigger(FATAL_SIGNAL, static_cast<g3::SignalType>(signal_number), dump.c_str());
+ trigger.stream() << fatal_stream.str();
+ } // message sent to g3LogWorker
+ // wait to die
+ }
+
+
+
+ //
+ // Installs FATAL signal handler that is enough to handle most fatal events
+ // on *NIX systems
+ void installSignalHandler() {
+#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
+ struct sigaction action;
+ memset(&action, 0, sizeof (action));
+ sigemptyset(&action.sa_mask);
+ action.sa_sigaction = &signalHandler; // 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
+ for (const auto& sig_pair : gSignals) {
+ if (sigaction(sig_pair.first, &action, nullptr) < 0) {
+ const std::string error = "sigaction - " + sig_pair.second;
+ perror(error.c_str());
+ }
+ }
+#endif
+ }
+
+
+
+} // end anonymous namespace
+
+
+
+
+
+
+// Redirecting and using signals. In case of fatal signals g3log should log the fatal signal
+// and flush the log queue and then "rethrow" the signal to exit
+namespace g3 {
+ // 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 {
+
+ 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) {
+ if (nullptr != rawdump && !std::string(rawdump).empty()) {
+ return {rawdump};
+ }
+
+ const size_t max_dump_size = 50;
+ void* dump[max_dump_size];
+ size_t size = backtrace(dump, max_dump_size);
+ char** messages = backtrace_symbols(dump, static_cast<int>(size)); // overwrite sigaction with caller's address
+
+ // dump stack: skip first frame, since that is here
+ std::ostringstream oss;
+ for (size_t idx = 1; idx < size && messages != nullptr; ++idx) {
+ char* mangled_name = 0, *offset_begin = 0, *offset_end = 0;
+ // find parantheses and +address offset surrounding mangled name
+ for (char* p = messages[idx]; *p; ++p) {
+ if (*p == '(') {
+ mangled_name = p;
+ } else if (*p == '+') {
+ offset_begin = p;
+ } else if (*p == ')') {
+ offset_end = p;
+ break;
+ }
+ }
+
+ // if the line could be processed, attempt to demangle the symbol
+ if (mangled_name && offset_begin && offset_end &&
+ mangled_name < offset_begin) {
+ *mangled_name++ = '\0';
+ *offset_begin++ = '\0';
+ *offset_end++ = '\0';
+
+ int status;
+ char* real_name = abi::__cxa_demangle(mangled_name, 0, 0, &status);
+ // if demangling is successful, output the demangled function name
+ if (status == 0) {
+ oss << "\n\tstack dump [" << idx << "] " << messages[idx] << " : " << real_name << "+";
+ oss << offset_begin << offset_end << std::endl;
+ }// otherwise, output the mangled function name
+ else {
+ oss << "\tstack dump [" << idx << "] " << messages[idx] << mangled_name << "+";
+ oss << offset_begin << offset_end << std::endl;
+ }
+ free(real_name); // mallocated by abi::__cxa_demangle(...)
+ } else {
+ // no demangling done -- just dump the whole line
+ oss << "\tstack dump [" << idx << "] " << messages[idx] << std::endl;
+ }
+ } // END: for(size_t idx = 1; idx < size && messages != nullptr; ++idx)
+ free(messages);
+ return oss.str();
+ }
+
+
+
+ /// string representation of signal ID
+ std::string exitReasonName(const LEVELS& level, g3::SignalType fatal_id) {
+
+ int signal_number = static_cast<int>(fatal_id);
+ 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 << ") for " << level.text;
+ return oss.str();
+ }
+ }
+
+
+
+ // 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) {
+ const int signal_number = static_cast<int>(fatal_signal_id);
+ restoreSignalHandler(signal_number);
+ std::cerr << "\n\n" << __FUNCTION__ << ":" << __LINE__ << ". Exiting due to " << level.text << ", " << signal_number << " \n\n" << std::flush;
+
+
+ kill(getpid(), signal_number);
+ exit(signal_number);
+
+ }
+ } // end g3::internal
+
+
+ // This will override the default signal handler setup and instead
+ // install a custom set of signals to handle
+ void overrideSetupSignals(const std::map<int, std::string> overrideSignals) {
+ static std::mutex signalLock;
+ std::lock_guard<std::mutex> guard(signalLock);
+ for (const auto& sig : gSignals) {
+ restoreSignalHandler(sig.first);
+ }
+
+ gSignals = overrideSignals;
+ installCrashHandler(); // installs all the signal handling for gSignals
+ }
+
+ // restores the signal handler back to default
+ void restoreSignalHandlerToDefault() {
+ overrideSetupSignals(kSignals);
+ }
+
+
+ // installs the signal handling for whatever signal set that is currently active
+ // If you want to setup your own signal handling then
+ // You should instead call overrideSetupSignals()
+ void installCrashHandler() {
+ installSignalHandler();
+ }
+} // end namespace g3
+