mirror of
https://github.com/KjellKod/g3log.git
synced 2025-01-19 08:46:42 +01:00
Merge pull request #4 from KjellKod/windows-vector-exceptions
Windows vector exceptions
This commit is contained in:
commit
29d7aa7267
@ -31,7 +31,7 @@ ELSEIF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
|
||||
|
||||
|
||||
ELSEIF(MSVC)
|
||||
ELSEIF (MSVC OR MINGW)
|
||||
set(PLATFORM_LINK_LIBRIES dbghelp)
|
||||
# VC11 bug: http://code.google.com/p/googletest/issues/detail?id=408
|
||||
# add_definition(-D_VARIADIC_MAX=10)
|
||||
|
@ -1,13 +1,10 @@
|
||||
# -DUSE_DYNAMIC_LOGGING_LEVELS=ON : run-type turn on/off levels
|
||||
option (USE_DYNAMIC_LOGGING_LEVELS
|
||||
"Turn ON/OFF log levels. An disabled level will not push logs of that level to the sink. By default dynamic logging is disabled" OFF)
|
||||
|
||||
|
||||
|
||||
IF(USE_DYNAMIC_LOGGING_LEVELS)
|
||||
add_definitions(-DG2_DYNAMIC_LOGGING)
|
||||
MESSAGE("-DUSE_DYNAMIC_LOGGING_LEVELS=ON
|
||||
\tDynamic logging levels can be turned on. Make sure to have \n\t\t[#define G2_DYNAMIC_LOGGING 1] in your source code")
|
||||
MESSAGE("-DUSE_DYNAMIC_LOGGING_LEVELS=ON")
|
||||
MESSAGE("\tDynamic logging levels can be turned on. Make sure to have \n\t\t[#define G2_DYNAMIC_LOGGING 1] in your source code")
|
||||
MESSAGE("\t\tUse [g2::setLogLevel(LEVEL boolean)] to enable/disable logging on specified levels")
|
||||
ELSE()
|
||||
MESSAGE("-DUSE_DYNAMIC_LOGGING_LEVELS=OFF")
|
||||
@ -20,13 +17,32 @@ ENDIF(USE_DYNAMIC_LOGGING_LEVELS)
|
||||
# predefined DEBUG for their own purposes
|
||||
option (CHANGE_G3LOG_DEBUG_TO_DBUG
|
||||
"Use DBUG logging level instead of DEBUG. By default DEBUG is the debugging level" OFF)
|
||||
|
||||
IF(CHANGE_G3LOG_DEBUG_TO_DBUG)
|
||||
add_definitions(-DCHANGE_G3LOG_DEBUG_TO_DBUG)
|
||||
MESSAGE("-DCHANGE_G3LOG_DEBUG_TO_DBUG=ON
|
||||
\tDBUG instead of DEBUG logging level can be used. Make sure to have \n\t\t[#define CHANGE_G3LOG_DEBUG_TO_DBUG 1] in your source code")
|
||||
MESSAGE("-DCHANGE_G3LOG_DEBUG_TO_DBUG=ON")
|
||||
MESSAGE("\tDBUG instead of DEBUG logging level can be used. Make sure to have \n\t\t[#define CHANGE_G3LOG_DEBUG_TO_DBUG 1] in your source code")
|
||||
ELSE()
|
||||
MESSAGE("-DCHANGE_G3LOG_DEBUG_TO_DBUG=OFF. Debuggin logging level is 'DEBUG'")
|
||||
MESSAGE("-DCHANGE_G3LOG_DEBUG_TO_DBUG=OFF")
|
||||
MESSAGE("\tDebuggin logging level is 'DEBUG'")
|
||||
ENDIF(CHANGE_G3LOG_DEBUG_TO_DBUG)
|
||||
|
||||
|
||||
|
||||
# WINDOWS OPTION
|
||||
# -DENABLE_VECTORED_EXCEPTIONHANDLING=ON : defualt change the
|
||||
# By default vectored exception handling is enabled, you can disable it with this option.
|
||||
# Please know that only known fatal exceptions will be caught, these exceptions are the ones
|
||||
# enumerated in src/stacktrace_windows.cpp
|
||||
IF (MSVC OR MINGW)
|
||||
option (ENABLE_VECTORED_EXCEPTIONHANDLING
|
||||
"Vectored exception / crash handling with improved stack trace" ON)
|
||||
|
||||
IF(NOT ENABLE_VECTORED_EXCEPTIONHANDLING)
|
||||
add_definitions(-DDISABLE_VECTORED_EXCEPTIONHANDLING)
|
||||
MESSAGE("-DENABLE_VECTORED_EXCEPTIONHANDLING=OFF")
|
||||
MESSAGE("\tVectored exception handling is disabled")
|
||||
ELSE()
|
||||
MESSAGE("-DENABLE_VECTORED_EXCEPTIONHANDLING=ON")
|
||||
MESSAGE("\tVectored exception handling is enabled")
|
||||
ENDIF(NOT ENABLE_VECTORED_EXCEPTIONHANDLING)
|
||||
ENDIF (MSVC OR MINGW)
|
@ -136,6 +136,7 @@ void ExecuteDeathFunction(const bool runInNewThread, int fatalChoice) {
|
||||
case 8: exitFunction = &OutOfBoundsArrayIndexing; break;
|
||||
case 9: exitFunction = &AccessViolation; break;
|
||||
case 10: exitFunction = &RaiseSIGABRTAndAccessViolation; break;
|
||||
case 11: throw 1233210; break;
|
||||
default: break;
|
||||
}
|
||||
if (runInNewThread) {
|
||||
@ -191,12 +192,14 @@ int ChoiceOfFatalExit() {
|
||||
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" << std::endl;
|
||||
|
||||
std::cout << std::flush;
|
||||
|
||||
try {
|
||||
std::getline(std::cin, option);
|
||||
choice = std::stoi(option);
|
||||
if (choice <= 0 || choice > 10) {
|
||||
if (choice <= 0 || choice > 11) {
|
||||
std::cout << "Invalid choice: [" << option << "\n\n";
|
||||
} else {
|
||||
return choice;
|
||||
|
@ -37,7 +37,7 @@ namespace {
|
||||
// Dump of stack,. then exit through g2log 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) {
|
||||
using namespace g2::internal;
|
||||
{
|
||||
const auto dump = stackdump();
|
||||
@ -47,7 +47,7 @@ void signalHandler(int signal_number, siginfo_t *info, void *unused_context) {
|
||||
fatal_stream << "(" << signal_number << ")\tPID: " << getpid() << std::endl;
|
||||
fatal_stream << "\n***** SIGNAL " << fatal_reason << "(" << signal_number << ")" << std::endl;
|
||||
LogCapture trigger(FATAL_SIGNAL, static_cast<g2::SignalType>(signal_number), dump.c_str());
|
||||
trigger.stream() << fatal_stream.str();
|
||||
trigger.stream() << fatal_stream.str();
|
||||
} // message sent to g2LogWorker
|
||||
// wait to die
|
||||
}
|
||||
@ -80,22 +80,22 @@ bool blockForFatalHandling() {
|
||||
|
||||
/// 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 == '+') {
|
||||
@ -114,7 +114,7 @@ std::string stackdump(const char *rawdump) {
|
||||
*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 << "+";
|
||||
@ -183,11 +183,15 @@ std::string exitReasonName(const LEVELS& level, g2::SignalType fatal_id) {
|
||||
void exitWithDefaultSignalHandler(const LEVELS& level, g2::SignalType fatal_signal_id) {
|
||||
const int signal_number = static_cast<int>(fatal_signal_id);
|
||||
std::cerr << "Exiting due to " << level.text << ", " << signal_number << " " << 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
|
||||
}
|
||||
@ -198,6 +202,7 @@ void exitWithDefaultSignalHandler(const LEVELS& level, g2::SignalType fatal_sign
|
||||
// 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);
|
||||
@ -216,6 +221,7 @@ void installSignalHandler() {
|
||||
perror("sigaction - SIGSEGV");
|
||||
if (sigaction(SIGTERM, &action, NULL) < 0)
|
||||
perror("sigaction - SIGTERM");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#include <intrin.h>
|
||||
#include <csignal>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
@ -24,21 +25,29 @@
|
||||
#include "g2logmessage.hpp"
|
||||
#include "g2logmessagecapture.hpp"
|
||||
|
||||
|
||||
|
||||
namespace {
|
||||
std::atomic<bool> gBlockForFatal {true};
|
||||
//void* g_vector_exception_handler = nullptr;
|
||||
LPTOP_LEVEL_EXCEPTION_FILTER g_previous_unexpected_exception_handler = nullptr;
|
||||
|
||||
#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
|
||||
g2_thread_local bool g_installed_thread_signal_handler = false;
|
||||
#endif
|
||||
|
||||
#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING))
|
||||
void* g_vector_exception_handler = nullptr;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// Restore back to default fatal event handling
|
||||
void ReverseToOriginalFatalHandling() {
|
||||
SetUnhandledExceptionFilter (g_previous_unexpected_exception_handler);
|
||||
//RemoveVectoredExceptionHandler (g_vector_exception_handler);
|
||||
|
||||
#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING))
|
||||
RemoveVectoredExceptionHandler (g_vector_exception_handler);
|
||||
#endif
|
||||
|
||||
#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
|
||||
if (SIG_ERR == signal(SIGABRT, SIG_DFL))
|
||||
perror("signal - SIGABRT");
|
||||
|
||||
@ -53,9 +62,11 @@ void ReverseToOriginalFatalHandling() {
|
||||
|
||||
if (SIG_ERR == signal(SIGTERM, SIG_DFL))
|
||||
perror("signal - SIGABRT");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
// called for fatal signals SIGABRT, SIGFPE, SIGSEGV, SIGILL, SIGTERM
|
||||
void signalHandler(int signal_number) {
|
||||
using namespace g2::internal;
|
||||
@ -68,42 +79,61 @@ void signalHandler(int signal_number) {
|
||||
|
||||
LogCapture trigger(FATAL_SIGNAL, static_cast<g2::SignalType>(signal_number), dump.c_str());
|
||||
trigger.stream() << fatal_stream.str();
|
||||
|
||||
// Trigger debug break point, if we're in debug. This breakpoint CAN cause a slowdown when it happens.
|
||||
// Be patient. The "Debug" dialogue should pop-up eventually if you doing it in Visual Studio.
|
||||
// For fatal signals only, not exceptions.
|
||||
// This is a way to tell the IDE (if in dev mode) that it can stop at this breakpoint
|
||||
// Note that at this time the fatal log event with stack trace is NOT yet flushed to the logger
|
||||
#if (!defined(NDEBUG) && defined(DEBUG_BREAK_AT_FATAL_SIGNAL))
|
||||
__debugbreak();
|
||||
#endif
|
||||
} // scope exit - message sent to LogWorker, wait to die...
|
||||
|
||||
|
||||
|
||||
// Unhandled exception catching
|
||||
LONG WINAPI exceptionHandling(EXCEPTION_POINTERS* info) {
|
||||
LONG WINAPI exceptionHandling(EXCEPTION_POINTERS* info, const std::string& handler) {
|
||||
std::string dump = stacktrace::stackdump(info);
|
||||
|
||||
std::ostringstream fatal_stream;
|
||||
const g2::SignalType exception_code = info->ExceptionRecord->ExceptionCode;
|
||||
fatal_stream << "\n***** Received fatal exception " << g2::internal::exitReasonName(g2::internal::FATAL_EXCEPTION, exception_code);
|
||||
fatal_stream << "\n***** " << handler << ": Received fatal exception " << g2::internal::exitReasonName(g2::internal::FATAL_EXCEPTION, exception_code);
|
||||
fatal_stream << "\tPID: " << getpid() << std::endl;
|
||||
|
||||
const auto fatal_id = static_cast<g2::SignalType>(exception_code);
|
||||
LogCapture trigger(g2::internal::FATAL_EXCEPTION, fatal_id, dump.c_str());
|
||||
trigger.stream() << fatal_stream.str();
|
||||
return EXCEPTION_CONTINUE_SEARCH; //EXCEPTION_EXECUTE_HANDLER;
|
||||
// FATAL Exception: It doesn't necessarily stop here we pass on continue search
|
||||
// if no one else will catch that then it's goodbye anyhow,
|
||||
// the RISK here is if someone is cathing this and returning "EXCEPTION_EXECUTE_HANDLER"
|
||||
// but does not shutdown then the software will be running with g3log shutdown.
|
||||
// .... However... this must be seen as a bug from standard handling of fatal exceptions
|
||||
// https://msdn.microsoft.com/en-us/library/6wxdsc38.aspx
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Unhandled exception catching
|
||||
LONG WINAPI unexpectedExceptionHandling(EXCEPTION_POINTERS* info) {
|
||||
ReverseToOriginalFatalHandling();
|
||||
return exceptionHandling(info);
|
||||
return exceptionHandling(info, "Unexpected Exception Handler");
|
||||
}
|
||||
|
||||
|
||||
/// Setup through (Windows API) AddVectoredExceptionHandler
|
||||
/// Ref: http://blogs.msdn.com/b/zhanli/archive/2010/06/25/c-tips-addvectoredexceptionhandler-addvectoredcontinuehandler-and-setunhandledexceptionfilter.aspx
|
||||
#if 0
|
||||
LONG WINAPI vectorExceptionHandling(PEXCEPTION_POINTERS p) {
|
||||
ReverseToOriginalFatalHandling();
|
||||
return exceptionHandling(p);
|
||||
}
|
||||
#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING))
|
||||
LONG WINAPI vectorExceptionHandling(PEXCEPTION_POINTERS p) {
|
||||
const g2::SignalType exception_code = p->ExceptionRecord->ExceptionCode;
|
||||
if (false == stacktrace::isKnownException(exception_code)) {
|
||||
LOG(WARNING) << "Vectored exception handling received an UNKNOWN exception: " << exception_code << ". The exception is IGNORED and hopefully caught by another exception handler";
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
} else {
|
||||
ReverseToOriginalFatalHandling();
|
||||
return exceptionHandling(p, "Vectored Exception Handler");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@ -124,7 +154,7 @@ bool blockForFatalHandling() {
|
||||
|
||||
|
||||
/// 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
|
||||
/// non-empty just use that one. i.e. the latter case is only for
|
||||
/// Windows and test purposes
|
||||
std::string stackdump(const char* dump) {
|
||||
if (nullptr != dump && !std::string(dump).empty()) {
|
||||
@ -192,6 +222,7 @@ void installSignalHandler() {
|
||||
/// you can also use this function call, per thread so make sure these three
|
||||
/// fatal signals are covered in your thread (even if you don't do a LOG(...) call
|
||||
void installSignalHandlerForThread() {
|
||||
#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
|
||||
if (!g_installed_thread_signal_handler) {
|
||||
g_installed_thread_signal_handler = true;
|
||||
if (SIG_ERR == signal(SIGTERM, signalHandler))
|
||||
@ -205,14 +236,20 @@ void installSignalHandlerForThread() {
|
||||
if (SIG_ERR == signal(SIGILL, signalHandler))
|
||||
perror("signal - SIGILL");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void installCrashHandler() {
|
||||
internal::installSignalHandler();
|
||||
//const size_t kFirstExceptionHandler = 1; // Kept here for documentational purposes. last exception seems more what we want
|
||||
const size_t kLastExceptionHandler = 0;
|
||||
//g_vector_exception_handler = AddVectoredExceptionHandler(kLastExceptionHandler, vectorExceptionHandling);
|
||||
g_previous_unexpected_exception_handler = SetUnhandledExceptionFilter(unexpectedExceptionHandling);
|
||||
|
||||
#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING))
|
||||
// const size_t kFirstExceptionHandler = 1;
|
||||
// kFirstExeptionsHandler is kept here for documentational purposes.
|
||||
// The last exception seems more what we want
|
||||
const size_t kLastExceptionHandler = 0;
|
||||
g_vector_exception_handler = AddVectoredExceptionHandler(kLastExceptionHandler, vectorExceptionHandling);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // end namespace g2
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include "g2logmessagecapture.hpp"
|
||||
#include "crashhandler.hpp"
|
||||
|
||||
|
||||
// For Windows we need force a thread_local install per thread of three
|
||||
// signals that must have a signal handler instealled per thread-basis
|
||||
// It is really a royal pain. Seriously Microsoft? Seriously?
|
||||
|
@ -34,7 +34,7 @@
|
||||
#define g2_MAP_PAIR_STRINGIFY(x) {x, #x}
|
||||
|
||||
namespace {
|
||||
g2_thread_local bool g_thread_local_recursive_crash_check = false;
|
||||
g2_thread_local size_t g_thread_local_recursive_crash_check = 0;
|
||||
|
||||
const std::map<g2::SignalType, std::string> kExceptionsAsText = {
|
||||
g2_MAP_PAIR_STRINGIFY(EXCEPTION_ACCESS_VIOLATION)
|
||||
@ -146,19 +146,24 @@ std::string convertFramesToText(std::vector<uint64_t>& frame_pointers) {
|
||||
|
||||
|
||||
namespace stacktrace {
|
||||
const std::string kUnknown = {"UNKNOWN EXCEPTION"};
|
||||
/// return the text description of a Windows exception code
|
||||
/// From MSDN GetExceptionCode http://msdn.microsoft.com/en-us/library/windows/desktop/ms679356(v=vs.85).aspx
|
||||
std::string exceptionIdToText(g2::SignalType id) {
|
||||
const auto iter = kExceptionsAsText.find(id);
|
||||
if ( iter == kExceptionsAsText.end()) {
|
||||
std::string unknown {"Unknown/" + std::to_string(id)};
|
||||
std::string unknown = {kUnknown + ":" + std::to_string(id)};
|
||||
return unknown;
|
||||
}
|
||||
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
|
||||
/// Yes a double lookup: first for isKnownException and then exceptionIdToText
|
||||
/// for vectored exceptions we only deal with known exceptions so this tiny
|
||||
/// overhead we can live with
|
||||
bool isKnownException(g2::SignalType id) {
|
||||
return (kExceptionsAsText.end() != kExceptionsAsText.find(id));
|
||||
}
|
||||
|
||||
/// helper function: retrieve stackdump from no excisting exception pointer
|
||||
std::string stackdump() {
|
||||
@ -179,12 +184,12 @@ std::string stackdump(EXCEPTION_POINTERS* info) {
|
||||
/// main stackdump function. retrieve stackdump, from the given context
|
||||
std::string stackdump(CONTEXT* context) {
|
||||
|
||||
if (g_thread_local_recursive_crash_check) {
|
||||
if (g_thread_local_recursive_crash_check >= 2) { // In Debug scenarious we allow one extra pass
|
||||
std::string recursive_crash = {"\n\n\n***** Recursive crash detected"};
|
||||
recursive_crash.append(", cannot continue stackdump traversal. *****\n\n\n");
|
||||
return recursive_crash;
|
||||
}
|
||||
g_thread_local_recursive_crash_check = true;
|
||||
++g_thread_local_recursive_crash_check;
|
||||
|
||||
static std::mutex m;
|
||||
std::lock_guard<std::mutex> lock(m);
|
||||
|
@ -27,6 +27,10 @@ namespace stacktrace {
|
||||
/// return the text description of a Windows exception code
|
||||
std::string exceptionIdToText(g2::SignalType id);
|
||||
|
||||
/// return whether or not the exception is a known exception, i.e.
|
||||
/// an exception that we should treat as a fatal event
|
||||
bool isKnownException(g2::SignalType id);
|
||||
|
||||
/// helper function: retrieve stackdump from no excisting exception pointer
|
||||
std::string stackdump();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user