Added resotre and override signal handler

This commit is contained in:
Kjell Hedstrom 2015-09-10 06:20:26 -06:00
parent 6432487adc
commit 602e135f84
3 changed files with 108 additions and 94 deletions

View File

@ -147,7 +147,8 @@ namespace
void SegFaultAttempt_x10000() NOEXCEPT {
deathfunc f = []{Throw(); *(char*)0 = 0; char* ptr = 0; *ptr = 1; AccessViolation();};
deathfunc f = []{char* ptr = 0; *ptr = 1; };
Death_x10000(f, "throw uncaught exception... and then some sigsegv calls");
}
@ -293,6 +294,21 @@ int main(int argc, char **argv)
g3::initializeLogging(worker.get());
g3::setFatalPreLoggingHook(&breakHere);
// <sigh>: Not yet added example of fatal signals that can be overridden
// Example: Running this with SIGTERM turned off, and choosing to "die by SIGTERM"
// will exit the process without catching the signal
//
// Enable the code below will override the signal setup, all signals active except SIGTERM
// g3::overrideSetupSignals({ {SIGABRT, "SIGABRT"}, {SIGFPE, "SIGFPE"},{SIGILL, "SIGILL"},
// {SIGSEGV, "SIGSEGV"},});
//
// the code below.... can be used to restore the signal handler again
// In truth: I don't really see how this can be used
// ...... maybe a separate signal handler for sigterm.... and running this in a unit test?
//g3::restoreSignalHandlerToDefault();
don't compile on purpose
std::future<std::string> log_file_name = handle->call(&g3::FileSink::fileName);
std::cout << "**** G3LOG FATAL EXAMPLE ***\n\n"

View File

@ -11,7 +11,7 @@
#include "g3log/logcapture.hpp"
#include "g3log/loglevels.hpp"
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) && !defined(__GNUC__)) // windows and not mingw
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) && !defined(__GNUC__))
#error "crashhandler_unix.cpp used but it's a windows system"
#endif
@ -40,10 +40,10 @@ namespace {
const std::map<int, std::string> kSignals = {
{SIGABRT, "SIGABRT"},
{SIGFPE,"SIGFPE"},
{SIGILL,"SIGILL"},
{SIGSEGV,"SIGSEGV"},
{SIGTERM,"SIGTERM"},
{SIGFPE, "SIGFPE"},
{SIGILL, "SIGILL"},
{SIGSEGV, "SIGSEGV"},
{SIGTERM, "SIGTERM"},
};
std::map<int, std::string> gSignals = kSignals;
@ -56,7 +56,6 @@ namespace {
}
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)); //
@ -66,22 +65,11 @@ namespace {
#endif
}
void overrideSetupSignals(const std::map<int, std::string> overrideSignals) {
static std::mutex signalLock;
std::lock_guard<std::mutex> guard(signalLock);
gSignals = overrideSignals;
for(const auto& sig: kSignals) {
restoreSignalHandler(sig.first);
}
g3::installCrashHandler();
}
// 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) {
void signalHandler(int signal_number, siginfo_t* info, void* unused_context) {
// Only one signal will be allowed past this point
if (false == shouldDoExit()) {
@ -103,6 +91,33 @@ namespace {
} // 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
@ -133,22 +148,22 @@ namespace g3 {
/// 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) {
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];
void* dump[max_dump_size];
size_t size = backtrace(dump, max_dump_size);
char **messages = backtrace_symbols(dump, size); // overwrite sigaction with caller's address
char** messages = backtrace_symbols(dump, 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;
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) {
for (char* p = messages[idx]; *p; ++p) {
if (*p == '(') {
mangled_name = p;
} else if (*p == '+') {
@ -167,7 +182,7 @@ namespace g3 {
*offset_end++ = '\0';
int status;
char *real_name = abi::__cxa_demangle(mangled_name, 0, 0, &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 << "+";
@ -190,102 +205,69 @@ namespace g3 {
/// string representation of signal ID
std::string exitReasonName(const LEVELS &level, g3::SignalType fatal_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();
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();
}
}
// KJELL : TODO. The Fatal Message can contain a callback function that depending on OS and test scenario does
// different things.
// exitWithDefaultSignalHandler is called from g3logworke::bgFatal AFTER all the logging sinks have been cleared
// I.e. saving a function that has the value already encapsulated within.
// FatalMessagePtr msgPtr
// Linux/OSX --> msgPtr.get()->ContinueWithFatalExit(); --> exitWithDefaultSignalHandler(int signal_number);
// Windows ..... (if signal) --> exitWithDefaultSignalHandler(int signal_number);
// (if exception) ....
// the calling thread that is in a never-ending loop should break out of that loop
// i.e. an atomic flag should be set
// the next step should then be to re-throw the same exception
// i.e. just call the next exception handler
// we should make sure that 1) g3log exception handler is called BEFORE widows
// it should continue and then be caught in Visual Studios exception handler
//
//
// 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<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);
exit(signal_number);
}
} // end g3::internal
//
// 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());
}
// 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);
}
// 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");
#endif
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();
} // namespace g3::internal
}
} // end namespace g3

View File

@ -9,6 +9,7 @@
* ============================================================================*/
#include <string>
#include <csignal>
#include <map>
#include "g3log/loglevels.hpp"
#include "g3log/generated_definitions.hpp"
@ -37,6 +38,21 @@ namespace g3 {
void installSignalHandlerForThread();
#else
typedef int SignalType;
/// Probably only needed for unit testing. Resets the signal handling back to default
/// which might be needed in case it was previously overridden
/// The default signals are: SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGTERM
void restoreSignalHandlerToDefault();
/// 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<int, std::string> overrideSignals);
#endif