mirror of
https://github.com/KjellKod/g3log.git
synced 2024-12-13 10:42:56 +01:00
Added resotre and override signal handler
This commit is contained in:
parent
6432487adc
commit
602e135f84
@ -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"
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user