What a mess to merge from Github/KjellKod/g3log to BitBucket/KjellKod/g3log

This commit is contained in:
KjellKod 2015-02-02 00:31:43 -07:00
parent 1f844f5ea1
commit 938fc55971
27 changed files with 1509 additions and 768 deletions

View File

@ -7,6 +7,12 @@ compiler:
- gcc
#- clang
# whitelist
branches:
only:
- master
before_install:
# use http://lint.travis-ci.org/ to validate changes
# sudo add-apt-repository -y ppa:h-rayflood/llvm;

View File

@ -32,6 +32,7 @@ ELSEIF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
ELSEIF(MSVC)
set(PLATFORM_LINK_LIBRIES dbghelp)
# VC11 bug: http://code.google.com/p/googletest/issues/detail?id=408
# add_definition(-D_VARIADIC_MAX=10)
# https://github.com/anhstudios/swganh/pull/186/files
@ -59,7 +60,7 @@ ENDIF()
IF (MSVC OR MINGW)
list(REMOVE_ITEM SRC_FILES ${LOG_SRC}/crashhandler_unix.cpp)
ELSE()
list(REMOVE_ITEM SRC_FILES ${LOG_SRC}/crashhandler_win.cpp)
list(REMOVE_ITEM SRC_FILES ${LOG_SRC}/crashhandler_windows.cpp ${LOG_SRC}/stacktrace_windows.hpp ${LOG_SRC}/stacktrace_windows.cpp)
ENDIF (MSVC OR MINGW)
set(SRC_FILES ${SRC_FILES} ${SRC_PLATFORM_SPECIFIC})
@ -69,8 +70,11 @@ ENDIF()
#MESSAGE(" g3logger files: [${SRC_FILES}]")
add_library(g3logger ${SRC_FILES})
set_target_properties(g3logger PROPERTIES LINKER_LANGUAGE CXX)
target_link_libraries(g3logger ${PLATFORM_LINK_LIBRIES})
add_library(g3logger_shared SHARED ${SRC_FILES})
set_target_properties(g3logger_shared PROPERTIES LINKER_LANGUAGE CXX)
target_link_libraries(g3logger_shared ${PLATFORM_LINK_LIBRIES})
SET(G3LOG_SHARED_LIBRARY g3logger_shared)
SET(G3LOG_LIBRARY g3logger)

View File

@ -20,6 +20,7 @@
# mkdir build
# cd build;
# 3. cmake -DCMAKE_BUILD_TYPE=Release -G "Visual Studio XXX" ..
# (cmake -DCMAKE_BUILD_TYPE=Release -G "Visual Studio 12")
# (XXX is the Visual Studio version you are running)
# 4. msbuild g3log.sln /p:Configuration=Release
#
@ -75,17 +76,19 @@ endif()
# ============================================================================
# G3LOG DYNAMIC LEVELS OPTIONS
# G3LOG OPTIONAL FEATURES
# ============================================================================
# ENABLE WITH: -USE_DYNAMIC_LOGGING_LEVELS=ON : run-type turn on/off levels
INCLUDE (${g3log_SOURCE_DIR}/Dynamic.cmake)
# Dynamic logging: ENABLE WITH: -DUSE_DYNAMIC_LOGGING_LEVELS=ON : run-type turn on/off levels
# DBUG instead of DEBUG logging level: ENABLE WITH: -DCHANGE_G3LOG_DEBUG_TO_DBUG=ON
INCLUDE (${g3log_SOURCE_DIR}/Options.cmake)
# ============================================================================
# EXAMPLE OPTIONS: By defauls is ON. This will create 'g3log-FATAL-* examples'
# ============================================================================
# DISABLE WITH: -DUSE_SIMPLE_EXAMPLE=OFF
# DISABLE WITH: -DUSE_FATAL_EXAMPLE=OFF
INCLUDE (${g3log_SOURCE_DIR}/example/Example.cmake)

32
Options.cmake Normal file
View File

@ -0,0 +1,32 @@
# -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("\t\tUse [g2::setLogLevel(LEVEL boolean)] to enable/disable logging on specified levels")
ELSE()
MESSAGE("-DUSE_DYNAMIC_LOGGING_LEVELS=OFF")
ENDIF(USE_DYNAMIC_LOGGING_LEVELS)
# -DCHANGE_G3LOG_DEBUG_TO_DBUG=ON : change the DEBUG logging level to be DBUG to avoid clash with other libraries that might have
# 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")
ELSE()
MESSAGE("-DCHANGE_G3LOG_DEBUG_TO_DBUG=OFF. Debuggin logging level is 'DEBUG'")
ENDIF(CHANGE_G3LOG_DEBUG_TO_DBUG)

View File

@ -76,7 +76,7 @@ The code is given for free as public domain. This gives the option to change, us
The *std::string* comes pre-formatted. The *g2::LogMessageMover* is a wrapped struct that contains the raw data for custom handling in your own sink.
It is easy to either use pre-made sinks or make your own. When a sink is set to be used by the G3log it is added to the logger inside a ```std::unique_ptr```. The sink can be called though its public API through a *handler* which will asynchronously forward the call to the receiving sink.
A sink is *owned* by the G3log and is added to the logger inside a ```std::unique_ptr```. The sink can be called though its public API through a *handler* which will asynchronously forward the call to the receiving sink.
```
auto sinkHandle = logworker->addSink(std2::make_unique<CustomSink>(),
&CustomSink::ReceiveLogMessage);
@ -203,4 +203,4 @@ or at <Hedstrom at KjellKod dot cc>
Cheers
Kjell *(a.k.a. KjellKod)*
Kjell *(a.k.a. KjellKod)*

View File

@ -10,21 +10,21 @@
set(DIR_EXAMPLE ${g3log_SOURCE_DIR}/example)
option (USE_SIMPLE_EXAMPLE "Simple (fatal-crash/contract) examples " ON)
option (USE_FATAL_EXAMPLE "Fatal (fatal-crashes/contract) examples " ON)
IF (USE_SIMPLE_EXAMPLE)
IF (USE_FATAL_EXAMPLE)
MESSAGE("-DUSE_SIMPLE_EXAMPLE=ON")
MESSAGE("\tg3log-FATAL-contract and g3log-FATAL-sigsegv shows fatal examples of when g3log comes in handy")
MESSAGE("-DUSE_FATAL_EXAMPLE=ON")
MESSAGE("\tg3log-FATAL- [contract][sigsegv][choice] are examples of when g3log comes in handy")
include_directories (${DIR_EXAMPLE})
add_executable(g3log-FATAL-contract ${DIR_EXAMPLE}/main_contract.cpp)
add_executable(g3log-FATAL-sigsegv ${DIR_EXAMPLE}/main_sigsegv.cpp)
add_executable(g3log-FATAL-choice ${DIR_EXAMPLE}/main_fatal_choice.cpp)
target_link_libraries(g3log-FATAL-contract ${G3LOG_LIBRARY} ${PLATFORM_LINK_LIBRIES})
target_link_libraries(g3log-FATAL-sigsegv ${G3LOG_LIBRARY} ${PLATFORM_LINK_LIBRIES})
target_link_libraries(g3log-FATAL-choice ${G3LOG_LIBRARY} ${PLATFORM_LINK_LIBRIES})
target_link_libraries(g3log-FATAL-contract ${G3LOG_LIBRARY})
target_link_libraries(g3log-FATAL-sigsegv ${G3LOG_LIBRARY})
target_link_libraries(g3log-FATAL-choice ${G3LOG_LIBRARY})
ELSE()
MESSAGE("-DUSE_SIMPLE_EXAMPLE=OFF")
ENDIF (USE_SIMPLE_EXAMPLE)
ENDIF (USE_FATAL_EXAMPLE)

View File

@ -32,41 +32,36 @@ void ToLower(std::string &str)
}
}
void sleep_for(size_t seconds) {
std::this_thread::sleep_for(std::chrono::seconds(seconds));
}
void RaiseSIGABRT() {
std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush;
sleep_for(2);
raise(SIGABRT);
LOG(WARNING) << "Expected to have died by now...";
}
void RaiseSIGFPE() {
std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush;
sleep_for(2);
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;
sleep_for(2);
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;
sleep_for(2);
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;
sleep_for(2);
LOGF_IF(INFO, (false != true), "Exiting %s SIGFPE", "by");
raise(SIGTERM);
LOG(WARNING) << "Expected to have died by now...";
}
@ -75,23 +70,21 @@ int gShouldBeZero = 1;
void DivisionByZero() {
std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush;
std::cout << "Executing DivisionByZero: gShouldBeZero: " << gShouldBeZero << std::endl;
sleep_for(2);
LOG(INFO) << "Division by zero is a big no-no";
int value = 3;
auto test = value / gShouldBeZero;
LOG(WARNING) << "Expected to have died by now...";
LOG(WARNING) << "Expected to have died by now..., test value: " << test;
}
void IllegalPrintf() {
std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush;
sleep_for(2);
printf("ILLEGAL PRINTF_SYNTAX %d EXAMPLE. %s %s", "hello", 1);
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;
sleep_for(2);
std::vector<int> v;
v[0] = 5;
LOG(WARNING) << "Expected to have died by now...";
@ -100,8 +93,8 @@ void OutOfBoundsArrayIndexing() {
void AccessViolation() {
std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush;
sleep_for(2);
char *ptr = 0;
char* ptr = 0;
LOG(INFO) << "Death by access violation is imminent";
*ptr = 0;
LOG(WARNING) << "Expected to have died by now...";
}
@ -111,6 +104,23 @@ void NoExitFunction() {
CHECK(false) << "This function should never be called";
}
void RaiseSIGABRTAndAccessViolation() {
std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush;
auto f1 = std::async(std::launch::async, &RaiseSIGABRT);
auto f2 = std::async(std::launch::async, &AccessViolation);
f1.wait();
f2.wait();
}
void CallActualExitFunction(std::function<void()> fatal_function) {
fatal_function();
}
void CallExitFunction(std::function<void()> fatal_function) {
CallActualExitFunction(fatal_function);
}
void ExecuteDeathFunction(const bool runInNewThread, int fatalChoice) {
std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush;
@ -121,17 +131,18 @@ void ExecuteDeathFunction(const bool runInNewThread, int fatalChoice) {
case 3: exitFunction = &RaiseSIGSEGV; break;
case 4: exitFunction = &RaiseSIGILL; break;
case 5: exitFunction = &RAiseSIGTERM; break;
case 6: exitFunction = &DivisionByZero; gShouldBeZero = 0; break;
case 6: exitFunction = &DivisionByZero; gShouldBeZero = 0; DivisionByZero(); break;
case 7: exitFunction = &IllegalPrintf; break;
case 8: exitFunction = &OutOfBoundsArrayIndexing; break;
case 9: exitFunction = &AccessViolation; break;
case 10: exitFunction = &RaiseSIGABRTAndAccessViolation; break;
default: break;
}
if (runInNewThread) {
auto dieInNearFuture = std::async(std::launch::async, exitFunction);
auto dieInNearFuture = std::async(std::launch::async, CallExitFunction, exitFunction);
dieInNearFuture.wait();
} else {
exitFunction();
CallExitFunction(exitFunction);
}
std::string unexpected = "Expected to exit by FATAL event. That did not happen (printf choice in Windows?).";
@ -178,13 +189,14 @@ int ChoiceOfFatalExit() {
std::cout << "[6] Division By Zero" << std::endl;
std::cout << "[7] Illegal printf" << std::endl;
std::cout << "[8] Out of bounds array indexing " << std::endl;
std::cout << "[9] Access violation \n\n" << std::endl;
std::cout << std::flush;
std::cout << "[9] Access violation" << std::endl;
std::cout << "[10] Rasing SIGABRT + Access Violation in two separate threads" << std::endl;
std::cout << std::flush;
try {
std::getline(std::cin, option);
choice = std::stoi(option);
if (choice <= 0 || choice > 9) {
if (choice <= 0 || choice > 10) {
std::cout << "Invalid choice: [" << option << "\n\n";
} else {
return choice;
@ -195,15 +207,18 @@ int ChoiceOfFatalExit() {
}
}
void ForwardChoiceForFatalExit(bool runInNewThread, int fatalChoice) {
ExecuteDeathFunction(runInNewThread, fatalChoice);
}
void ChooseFatalExit() {
const bool runInNewThread = AskForAsyncDeath();
const int choiceOfFatalExit = ChoiceOfFatalExit();
ExecuteDeathFunction(runInNewThread, choiceOfFatalExit);
const int exitChoice = ChoiceOfFatalExit();
ForwardChoiceForFatalExit(runInNewThread, exitChoice);
}
} // namespace
int main(int argc, char **argv)
int main(int argc, char** argv)
{
auto logger_n_handle = g2::LogWorker::createWithDefaultLogger(argv[0], path_to_log_file);
g2::initializeLogging(logger_n_handle.worker.get());
@ -214,7 +229,7 @@ int main(int argc, char **argv)
<< "The logfile is generated at: [" << log_file_name.get() << "]\n\n" << std::endl;
LOGF(INFO, "Fatal exit example starts now, it's as easy as %d", 123);
LOGF(DEBUG, "Fatal exit example starts now, it's as easy as %d", 123);
LOG(INFO) << "Feel free to read the source code also in g3log/example/main_fatal_choice.cpp";
while (true) {

View File

@ -2,7 +2,7 @@
* 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
* ============================================================================*/
@ -23,80 +23,80 @@ const std::string path_to_log_file = "/tmp/";
namespace example_fatal
{
// on Ubunti this caused get a compiler warning with gcc4.6
// from gcc 4.7.2 (at least) it causes a crash (as expected)
// On windows it'll probably crash too.
void tryToKillWithIllegalPrintout()
{
std::cout << "\n\n***** Be ready this last example may 'abort' if on Windows/Linux_gcc4.7 " << std::endl << std::flush;
std::cout << "************************************************************\n\n" << std::endl << std::flush;
std::this_thread::sleep_for(std::chrono::seconds(1));
const std::string logging = "logging";
LOGF(DEBUG, "ILLEGAL PRINTF_SYNTAX EXAMPLE. WILL GENERATE compiler warning.\n\nbadly formatted message:[Printf-type %s is the number 1 for many %s]", logging.c_str());
}
// on Ubunti this caused get a compiler warning with gcc4.6
// from gcc 4.7.2 (at least) it causes a crash (as expected)
// On windows it'll probably crash too.
void tryToKillWithIllegalPrintout()
{
std::cout << "\n\n***** Be ready this last example may 'abort' if on Windows/Linux_gcc4.7 " << std::endl << std::flush;
std::cout << "************************************************************\n\n" << std::endl << std::flush;
std::this_thread::sleep_for(std::chrono::seconds(1));
const std::string logging = "logging";
LOGF(DEBUG, "ILLEGAL PRINTF_SYNTAX EXAMPLE. WILL GENERATE compiler warning.\n\nbadly formatted message:[Printf-type %s is the number 1 for many %s]", logging.c_str());
}
// The function above 'tryToKillWithIllegalPrintout' IS system / compiler dependent. Older compilers sometimes did NOT generate a SIGSEGV
// fault as expected by the illegal printf-format usage. just in case we exit by zero division"
void killByZeroDivision(int value)
{
int zero = 0; // trying to fool the compiler to automatically warn
LOG(INFO) << "This is a bad operation [value/zero] : " << value/zero;
}
// The function above 'tryToKillWithIllegalPrintout' IS system / compiler dependent. Older compilers sometimes did NOT generate a SIGSEGV
// fault as expected by the illegal printf-format usage. just in case we exit by zero division"
void killByZeroDivision(int value)
{
int zero = 0; // trying to fool the compiler to automatically warn
LOG(INFO) << "This is a bad operation [value/zero] : " << value / zero;
}
} // example fatal
int main(int argc, char** argv)
{
double pi_d = 3.1415926535897932384626433832795;
float pi_f = 3.1415926535897932384626433832795f;
double pi_d = 3.1415926535897932384626433832795;
float pi_f = 3.1415926535897932384626433832795f;
using namespace g2;
using namespace g2;
std::unique_ptr<LogWorker> logworker{LogWorker::createWithNoSink()};
auto sinkHandle = logworker->addSink(std2::make_unique<FileSink>(argv[0], path_to_log_file),
&FileSink::fileWrite);
std::unique_ptr<LogWorker> logworker {LogWorker::createWithNoSink()};
auto sinkHandle = logworker->addSink(std2::make_unique<FileSink>(argv[0], path_to_log_file),
&FileSink::fileWrite);
initializeLogging(logworker.get());
std::future<std::string> log_file_name = sinkHandle->call(&FileSink::fileName);
std::cout << "* This is an example of g2log. It WILL exit by a FATAL trigger" << std::endl;
std::cout << "* Please see the generated log and compare to the code at" << std::endl;
std::cout << "* g2log/test_example/main.cpp" << std::endl;
std::cout << "*\n* Log file: [" << log_file_name.get() << "]\n\n" << std::endl;
initializeLogging(logworker.get());
std::future<std::string> log_file_name = sinkHandle->call(&FileSink::fileName);
std::cout << "* This is an example of g2log. It WILL exit by a FATAL trigger" << std::endl;
std::cout << "* Please see the generated log and compare to the code at" << std::endl;
std::cout << "* g2log/test_example/main.cpp" << std::endl;
std::cout << "*\n* Log file: [" << log_file_name.get() << "]\n\n" << std::endl;
LOGF(INFO, "Hi log %d", 123);
LOG(INFO) << "Test SLOG INFO";
LOG(DEBUG) << "Test SLOG DEBUG";
LOG(INFO) << "one: " << 1;
LOG(INFO) << "two: " << 2;
LOG(INFO) << "one and two: " << 1 << " and " << 2;
LOG(DEBUG) << "float 2.14: " << 1000/2.14f;
LOG(DEBUG) << "pi double: " << pi_d;
LOG(DEBUG) << "pi float: " << pi_f;
LOG(DEBUG) << "pi float (width 10): " << std::setprecision(10) << pi_f;
LOGF(INFO, "pi float printf:%f", pi_f);
LOGF(INFO, "Hi log %d", 123);
LOG(INFO) << "Test SLOG INFO";
LOG(DEBUG) << "Test SLOG DEBUG";
LOG(INFO) << "one: " << 1;
LOG(INFO) << "two: " << 2;
LOG(INFO) << "one and two: " << 1 << " and " << 2;
LOG(DEBUG) << "float 2.14: " << 1000 / 2.14f;
LOG(DEBUG) << "pi double: " << pi_d;
LOG(DEBUG) << "pi float: " << pi_f;
LOG(DEBUG) << "pi float (width 10): " << std::setprecision(10) << pi_f;
LOGF(INFO, "pi float printf:%f", pi_f);
//
// START: LOG Entris that were in the CodeProject article
//
//LOG(UNKNOWN_LEVEL) << "This log attempt will cause a compiler error";
//
// START: LOG Entris that were in the CodeProject article
//
//LOG(UNKNOWN_LEVEL) << "This log attempt will cause a compiler error";
LOG(INFO) << "Simple to use with streaming syntax, easy as abc or " << 123;
LOGF(WARNING, "Printf-style syntax is also %s", "available");
LOG_IF(INFO, (1 < 2)) << "If true this text will be logged";
LOGF_IF(INFO, (1<2), "if %d<%d : then this text will be logged", 1,2);
LOG_IF(FATAL, (2>3)) << "This message should NOT throw";
LOGF(DEBUG, "This API is popular with some %s", "programmers");
LOGF_IF(DEBUG, (1<2), "If true, then this %s will be logged", "message");
LOG(INFO) << "Simple to use with streaming syntax, easy as abc or " << 123;
LOGF(WARNING, "Printf-style syntax is also %s", "available");
LOG_IF(INFO, (1 < 2)) << "If true this text will be logged";
LOGF_IF(INFO, (1 < 2), "if %d<%d : then this text will be logged", 1, 2);
LOG_IF(FATAL, (2 > 3)) << "This message should NOT throw";
LOGF(DEBUG, "This API is popular with some %s", "programmers");
LOGF_IF(DEBUG, (1 < 2), "If true, then this %s will be logged", "message");
// OK --- on Ubunti this caused get a compiler warning with gcc4.6
// from gcc 4.7.2 (at least) it causes a crash (as expected)
// On windows itll probably crash
//example_fatal::tryToKillWithIllegalPrintout();
// OK --- on Ubunti this caused get a compiler warning with gcc4.6
// from gcc 4.7.2 (at least) it causes a crash (as expected)
// On windows itll probably crash
//example_fatal::tryToKillWithIllegalPrintout();
int value = 1; // system dependent but it SHOULD never reach this line
example_fatal::killByZeroDivision(value);
return 0;
int value = 1; // system dependent but it SHOULD never reach this line
example_fatal::killByZeroDivision(value);
return 0;
}

View File

@ -4,27 +4,49 @@
* 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 <string>
#include <csignal>
#include "g2loglevels.hpp"
// kjell. Separera på crashhandler.hpp och crashhanlder_internal.hpp
// implementationsfilen kan vara den samma
namespace g2 {
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
typedef unsigned long SignalType;
/// SIGFPE, SIGILL, and SIGSEGV handling must be installed per thread
/// on Windows. This is automatically done if you do at least one LOG(...) call
/// 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();
#else
typedef int SignalType;
#endif
namespace internal {
/** \return signal_name. Ref: signum.hpp and \ref installSignalHandler */
std::string signalName(int signal_number);
/** return whether or any fatal handling is still ongoing
* this is used by g2log::fatalCallToLogger
* 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();
/** \return signal_name Ref: signum.hpp and \ref installSignalHandler
* or for Windows exception name */
std::string exitReasonName(const LEVELS& level, g2::SignalType signal_number);
/** return calling thread's stackdump*/
std::string stackdump();
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 g2log, g2LogWorker after flushing messages to file */
void exitWithDefaultSignalHandler(int signal_number);
void exitWithDefaultSignalHandler(const LEVELS& level, g2::SignalType signal_number);
} // end g2::internal
@ -37,5 +59,5 @@ void exitWithDefaultSignalHandler(int signal_number);
SIGILL ILlegal instruction (ANSI)
SIGSEGV Segmentation violation i.e. illegal memory reference
SIGTERM TERMINATION (ANSI) */
void installSignalHandler();
void installCrashHandler();
}

View File

@ -2,7 +2,7 @@
* 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
* ============================================================================*/
@ -16,7 +16,7 @@
#error "crashhandler_unix.cpp used but it's a windows system"
#endif
#include <unistd.h>
#include <unistd.h>
#include <execinfo.h>
#include <cxxabi.h>
#include <cstdlib>
@ -26,7 +26,7 @@
// Linux/Clang, OSX/Clang, OSX/gcc
#if (defined(__clang__) || defined(__APPLE__))
#if (defined(__clang__) || defined(__APPLE__))
#include <sys/ucontext.h>
#else
#include <ucontext.h>
@ -34,27 +34,23 @@
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 crashHandler(int signal_number, siginfo_t *info, void *unused_context) {
using namespace g2::internal;
std::ostringstream oss;
oss << "Received fatal signal: " << g2::internal::signalName(signal_number);
oss << "(" << signal_number << ")\tPID: " << getpid() << std::endl;
//oss << stackdump();
{ // Local scope, trigger send
std::ostringstream fatal_stream;
fatal_stream << oss.str() << std::endl;
fatal_stream << "\n***** SIGNAL " << signalName(signal_number) << "(" << signal_number << ")" << std::endl;
LogCapture trigger(FATAL_SIGNAL, signal_number);
trigger.stream() << fatal_stream.str();
} // message sent to g2LogWorker
// wait to die
}
// 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) {
using namespace g2::internal;
{
const auto dump = stackdump();
std::ostringstream fatal_stream;
const auto fatal_reason = exitReasonName(g2::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<g2::SignalType>(signal_number), dump.c_str());
trigger.stream() << fatal_stream.str();
} // message sent to g2LogWorker
// wait to die
}
} // end anonymous namespace
@ -65,122 +61,169 @@ namespace {
// Redirecting and using signals. In case of fatal signals g2log should log the fatal signal
// and flush the log queue and then "rethrow" the signal to exit
namespace g2 {
// 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 {
// 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 {
std::string stackdump() {
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, size); // overwrite sigaction with caller's address
bool blockForFatalHandling() {
return true; // For windows we will after fatal processing change it to false
}
// 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;
}
}
/// 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};
}
// 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';
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, size); // overwrite sigaction with caller's address
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();
}
std::string signalName(int signal_number) {
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 << ")";
return oss.str();
// 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;
}
}
// Triggered by g2log->g2LogWorker 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
// 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';
void exitWithDefaultSignalHandler(int signal_number) {
std::cerr << "Exiting - FATAL SIGNAL: " << signal_number << " " << std::flush;
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);
kill(getpid(), signal_number);
abort(); // should never reach this
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 g2::internal
} // END: for(size_t idx = 1; idx < size && messages != nullptr; ++idx)
free(messages);
return oss.str();
}
void installSignalHandler() {
struct sigaction action;
memset(&action, 0, sizeof (action));
sigemptyset(&action.sa_mask);
action.sa_sigaction = &crashHandler; // 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
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");
/// string representation of signal ID
std::string exitReasonName(const LEVELS& level, g2::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();
}
}
// KJELL : TODO. The Fatal Message can contain a callback function that depending on OS and test scenario does
// different things.
// exitWithDefaultSignalHandler is called from g2logworke::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) g2log exception handler is called BEFORE widows
// it should continue and then be caught in Visual Studios exception handler
//
//
// Triggered by g2log->g2LogWorker 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, 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;
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);
kill(getpid(), signal_number);
abort(); // should never reach this
}
} // end g2::internal
//
// Installs FATAL signal handler that is enough to handle most fatal events
// on *NIX systems
void installSignalHandler() {
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
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");
}
void installCrashHandler() {
installSignalHandler();
} // namespace g2::internal
} // end namespace g2

View File

@ -1,99 +0,0 @@
/** ==========================================================================
* 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 "crashhandler.hpp"
#include "g2logmessage.hpp"
#include "g2logmessagecapture.hpp"
#include <csignal>
#include <cstring>
#include <cstdlib>
#include <sstream>
#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
#error "crashhandler_win.cpp used but not on a windows system"
#endif
#include <process.h> // getpid
#define getpid _getpid
namespace {
void crashHandler(int signal_number) {
using namespace g2::internal;
std::ostringstream fatal_stream;
fatal_stream << "\n\n***** FATAL TRIGGER RECEIVED ******* " << std::endl;
fatal_stream << "\n***** RETHROWING SIGNAL " << signalName(signal_number) << "(" << signal_number << ")" << std::endl;
LogCapture trigger(FATAL_SIGNAL, signal_number);
trigger.stream() << fatal_stream.str();
} // scope exit - message sent to LogWorker, wait to die...
} // end anonymous namespace
namespace g2 {
namespace internal {
std::string stackdump() {
std::string temp;
return temp;
}
std::string signalName(int signal_number) {
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 << ")";
return oss.str();
}
}
// Triggered by g2log::LogWorker 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(int signal_number) {
// Restore our signalhandling to default
if (SIG_ERR == signal(SIGABRT, SIG_DFL))
perror("signal - SIGABRT");
if (SIG_ERR == signal(SIGFPE, SIG_DFL))
perror("signal - SIGABRT");
if (SIG_ERR == signal(SIGSEGV, SIG_DFL))
perror("signal - SIGABRT");
if (SIG_ERR == signal(SIGILL, SIG_DFL))
perror("signal - SIGABRT");
if (SIG_ERR == signal(SIGTERM, SIG_DFL))
perror("signal - SIGABRT");
raise(signal_number);
}
} // end g2::internal
void installSignalHandler() {
if (SIG_ERR == signal(SIGABRT, crashHandler))
perror("signal - SIGABRT");
if (SIG_ERR == signal(SIGFPE, crashHandler))
perror("signal - SIGFPE");
if (SIG_ERR == signal(SIGSEGV, crashHandler))
perror("signal - SIGSEGV");
if (SIG_ERR == signal(SIGILL, crashHandler))
perror("signal - SIGILL");
if (SIG_ERR == signal(SIGTERM, crashHandler))
perror("signal - SIGTERM");
}
} // end namespace g2

View File

@ -0,0 +1,215 @@
/** ==========================================================================
* 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
* ============================================================================*/
#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
#error "crashhandler_windows.cpp used but not on a windows system"
#endif
#include <windows.h>
#include <csignal>
#include <cstring>
#include <cstdlib>
#include <sstream>
#include <atomic>
#include <process.h> // getpid
#define getpid _getpid
#include "crashhandler.hpp"
#include "stacktrace_windows.hpp"
#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;
// Restore back to default fatal event handling
void ReverseToOriginalFatalHandling() {
SetUnhandledExceptionFilter (g_previous_unexpected_exception_handler);
RemoveVectoredExceptionHandler (g_vector_exception_handler);
if (SIG_ERR == signal(SIGABRT, SIG_DFL))
perror("signal - SIGABRT");
if (SIG_ERR == signal(SIGFPE, SIG_DFL))
perror("signal - SIGABRT");
if (SIG_ERR == signal(SIGSEGV, SIG_DFL))
perror("signal - SIGABRT");
if (SIG_ERR == signal(SIGILL, SIG_DFL))
perror("signal - SIGABRT");
if (SIG_ERR == signal(SIGTERM, SIG_DFL))
perror("signal - SIGABRT");
}
// called for fatal signals SIGABRT, SIGFPE, SIGSEGV, SIGILL, SIGTERM
void signalHandler(int signal_number) {
using namespace g2::internal;
std::string dump = stacktrace::stackdump();
std::ostringstream fatal_stream;
fatal_stream << "\n***** Received fatal signal " << g2::internal::exitReasonName(g2::internal::FATAL_SIGNAL, signal_number);
fatal_stream << "(" << signal_number << ")\tPID: " << getpid() << std::endl;
LogCapture trigger(FATAL_SIGNAL, static_cast<g2::SignalType>(signal_number), dump.c_str());
trigger.stream() << fatal_stream.str();
} // scope exit - message sent to LogWorker, wait to die...
// Unhandled exception catching
LONG WINAPI exceptionHandling(EXCEPTION_POINTERS* info) {
std::string dump = stacktrace::stackdump();
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 << "\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;
}
// Unhandled exception catching
LONG WINAPI unexpectedExceptionHandling(EXCEPTION_POINTERS* info) {
ReverseToOriginalFatalHandling();
return exceptionHandling(info);
}
/// Setup through (Windows API) AddVectoredExceptionHandler
/// Ref: http://blogs.msdn.com/b/zhanli/archive/2010/06/25/c-tips-addvectoredexceptionhandler-addvectoredcontinuehandler-and-setunhandledexceptionfilter.aspx
LONG WINAPI vectorExceptionHandling(PEXCEPTION_POINTERS p) {
ReverseToOriginalFatalHandling();
return exceptionHandling(p);
}
} // end anonymous namespace
namespace g2 {
namespace internal {
// For windows exceptions this might ONCE be set to false, in case of a
// windows exceptions and not a signal
bool blockForFatalHandling() {
return gBlockForFatal;
}
/// 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* dump) {
if (nullptr != dump && !std::string(dump).empty()) {
return {dump};
}
return stacktrace::stackdump();
}
/// string representation of signal ID or Windows exception id
std::string exitReasonName(const LEVELS& level, g2::SignalType fatal_id) {
if (level == g2::internal::FATAL_EXCEPTION) {
return stacktrace::exceptionIdToText(fatal_id);
}
switch (fatal_id) {
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(" << fatal_id << ")";
return oss.str();
}
}
// Triggered by g2log::LogWorker 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, g2::SignalType fatal_signal_id) {
ReverseToOriginalFatalHandling();
// For windows exceptions we want to continue the possibility of
// exception handling now when the log and stacktrace are flushed
// to sinks. We therefore avoid to kill the preocess here. Instead
// it will be the exceptionHandling functions above that
// will let exception handling continue with: EXCEPTION_CONTINUE_SEARCH
if (g2::internal::FATAL_EXCEPTION == level) {
gBlockForFatal = false;
return;
}
// for a sigal however, we exit through that fatal signal
const int signal_number = static_cast<int>(fatal_signal_id);
raise(signal_number);
}
void installSignalHandler() {
g2::installSignalHandlerForThread();
if (SIG_ERR == signal(SIGABRT, signalHandler))
perror("signal - SIGABRT");
if (SIG_ERR == signal(SIGTERM, signalHandler))
perror("signal - SIGTERM");
}
} // end g2::internal
/// SIGFPE, SIGILL, and SIGSEGV handling must be installed per thread
/// on Windows. This is automatically done if you do at least one LOG(...) call
/// 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 (SIG_ERR == signal(SIGFPE, signalHandler))
perror("signal - SIGFPE");
if (SIG_ERR == signal(SIGSEGV, signalHandler))
perror("signal - SIGSEGV");
if (SIG_ERR == signal(SIGILL, signalHandler))
perror("signal - SIGILL");
}
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);
}
} // end namespace g2

View File

@ -2,7 +2,7 @@
* 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
* ============================================================================
*
@ -31,13 +31,13 @@
#include "g2logmessage.hpp"
namespace {
std::once_flag g_initialize_flag;
g2::LogWorker* g_logger_instance = nullptr; // instantiated and OWNED somewhere else (main)
std::mutex g_logging_init_mutex;
std::once_flag g_initialize_flag;
g2::LogWorker* g_logger_instance = nullptr; // instantiated and OWNED somewhere else (main)
std::mutex g_logging_init_mutex;
std::unique_ptr<g2::LogMessage> g_first_unintialized_msg = {nullptr};
std::once_flag g_set_first_uninitialized_flag;
std::once_flag g_save_first_unintialized_flag;
std::unique_ptr<g2::LogMessage> g_first_unintialized_msg = {nullptr};
std::once_flag g_set_first_uninitialized_flag;
std::once_flag g_save_first_unintialized_flag;
}
@ -46,158 +46,158 @@ namespace {
namespace g2 {
// signalhandler and internal clock is only needed to install once
// for unit testing purposes the initializeLogging might be called
// several times...
// for all other practical use, it shouldn't!
// signalhandler and internal clock is only needed to install once
// for unit testing purposes the initializeLogging might be called
// several times...
// for all other practical use, it shouldn't!
void initializeLogging(LogWorker *bgworker) {
std::call_once(g_initialize_flag, []() {
installSignalHandler(); });
std::lock_guard<std::mutex> lock(g_logging_init_mutex);
CHECK(!internal::isLoggingInitialized());
CHECK(bgworker != nullptr);
void initializeLogging(LogWorker* bgworker) {
std::call_once(g_initialize_flag, []() {
installCrashHandler();
});
std::lock_guard<std::mutex> lock(g_logging_init_mutex);
CHECK(!internal::isLoggingInitialized());
CHECK(bgworker != nullptr);
// Save the first uninitialized message, if any
std::call_once(g_save_first_unintialized_flag, [&bgworker] {
if (g_first_unintialized_msg) {
bgworker->save(LogMessagePtr{std::move(g_first_unintialized_msg)});
}
// Save the first uninitialized message, if any
std::call_once(g_save_first_unintialized_flag, [&bgworker] {
if (g_first_unintialized_msg) {
bgworker->save(LogMessagePtr {std::move(g_first_unintialized_msg)});
}
});
g_logger_instance = bgworker;
}
namespace internal {
bool isLoggingInitialized() {
return g_logger_instance != nullptr;
}
/**
* Shutdown the logging by making the pointer to the background logger to nullptr. The object is not deleted
* that is the responsibility of its owner. *
*/
void shutDownLogging() {
std::lock_guard<std::mutex> lock(g_logging_init_mutex);
g_logger_instance = nullptr;
}
/** Same as the Shutdown above but called by the destructor of the LogWorker, thus ensuring that no further
* LOG(...) calls can happen to a non-existing LogWorker.
* @param active MUST BE the LogWorker initialized for logging. If it is not then this call is just ignored
* and the logging continues to be active.
* @return true if the correct worker was given,. and shutDownLogging was called
*/
bool shutDownLoggingForActiveOnly(LogWorker* active) {
if (isLoggingInitialized() && nullptr != active && (active != g_logger_instance)) {
LOG(WARNING) << "\n\t\tAttempted to shut down logging, but the ID of the Logger is not the one that is active."
<< "\n\t\tHaving multiple instances of the g2::LogWorker is likely a BUG"
<< "\n\t\tEither way, this call to shutDownLogging was ignored"
<< "\n\t\tTry g2::internal::shutDownLogging() instead";
return false;
}
shutDownLogging();
return true;
}
/** explicits copy of all input. This is makes it possibly to use g3log across dynamically loaded libraries
* i.e. (dlopen + dlsym) */
void saveMessage(const char* entry, const char* file, int line, const char* function, const LEVELS& level,
const char* boolean_expression, int fatal_signal, const char* stack_trace) {
LEVELS msgLevel {level};
LogMessagePtr message {std2::make_unique<LogMessage>(file, line, function, msgLevel)};
message.get()->write().append(entry);
message.get()->setExpression(boolean_expression);
if (internal::wasFatal(level)) {
message.get()->write().append(stack_trace);
FatalMessagePtr fatal_message {std2::make_unique<FatalMessage>(*(message._move_only.get()), fatal_signal)};
// At destruction, flushes fatal message to g2LogWorker
// either we will stay here until the background worker has received the fatal
// message, flushed the crash message to the sinks and exits with the same fatal signal
//..... OR it's in unit-test mode then we throw a std::runtime_error (and never hit sleep)
fatalCall(fatal_message);
} else {
pushMessageToLogger(message);
}
}
/**
* save the message to the logger. In case of called before the logger is instantiated
* the first message will be saved. Any following subsequent unitnialized log calls
* will be ignored.
*
* The first initialized log entry will also save the first uninitialized log message, if any
* @param log_entry to save to logger
*/
void pushMessageToLogger(LogMessagePtr incoming) { // todo rename to Push SavedMessage To Worker
// Uninitialized messages are ignored but does not CHECK/crash the logger
if (!internal::isLoggingInitialized()) {
std::call_once(g_set_first_uninitialized_flag, [&] {
g_first_unintialized_msg = incoming.release();
std::string err = {"LOGGER NOT INITIALIZED:\n\t\t"};
err.append(g_first_unintialized_msg->message());
std::string& str = g_first_unintialized_msg->write();
str.clear();
str.append(err); // replace content
std::cerr << str << std::endl;
});
g_logger_instance = bgworker;
return;
}
// logger is initialized
g_logger_instance->save(incoming);
}
/** Fatal call saved to logger. This will trigger SIGABRT or other fatal signal
* to exit the program. After saving the fatal message the calling thread
* will sleep forever (i.e. until the background thread catches up, saves the fatal
* message and kills the software with the fatal signal.
*/
void pushFatalMessageToLogger(FatalMessagePtr message) {
if (!isLoggingInitialized()) {
std::ostringstream error;
error << "FATAL CALL but logger is NOT initialized\n"
<< "CAUSE: " << message.get()->reason()
<< "\nMessage: \n" << message.get()->toString() << std::flush;
std::cerr << error.str() << std::flush;
internal::exitWithDefaultSignalHandler(message.get()->_level, message.get()->_signal_id);
}
g_logger_instance->fatal(message);
while (blockForFatalHandling()) {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
// By default this function pointer goes to \ref pushFatalMessageToLogger;
std::function<void(FatalMessagePtr) > g_fatal_to_g2logworker_function_ptr = pushFatalMessageToLogger;
/** The default, initial, handling to send a 'fatal' event to g2logworker
* the caller will stay here, eternally, until the software is aborted
* ... in the case of unit testing it is the given "Mock" fatalCall that will
* define the behaviour.
*/
void fatalCall(FatalMessagePtr message) {
g_fatal_to_g2logworker_function_ptr(FatalMessagePtr {std::move(message)});
}
namespace internal {
bool isLoggingInitialized() {
return g_logger_instance != nullptr;
}
/**
* Shutdown the logging by making the pointer to the background logger to nullptr. The object is not deleted
* that is the responsibility of its owner. *
*/
void shutDownLogging() {
std::lock_guard<std::mutex> lock(g_logging_init_mutex);
g_logger_instance = nullptr;
}
/** Same as the Shutdown above but called by the destructor of the LogWorker, thus ensuring that no further
* LOG(...) calls can happen to a non-existing LogWorker.
* @param active MUST BE the LogWorker initialized for logging. If it is not then this call is just ignored
* and the logging continues to be active.
* @return true if the correct worker was given,. and shutDownLogging was called
*/
bool shutDownLoggingForActiveOnly(LogWorker* active) {
if (isLoggingInitialized() && nullptr != active && (active != g_logger_instance)) {
LOG(WARNING) << "\n\t\tAttempted to shut down logging, but the ID of the Logger is not the one that is active."
<< "\n\t\tHaving multiple instances of the g2::LogWorker is likely a BUG"
<< "\n\t\tEither way, this call to shutDownLogging was ignored"
<< "\n\t\tTry g2::internal::shutDownLogging() instead";
return false;
}
shutDownLogging();
return true;
}
// explicits copy of all input. This is makes it possibly to use g3log across dynamically loaded libraries
// i.e. (dlopen + dlsym)
void saveMessage(const char* entry, const char* file, int line, const char* function, const LEVELS& level,
const char* boolean_expression, int fatal_signal, const char* stack_trace) {
LEVELS msgLevel{level};
LogMessagePtr message{std2::make_unique<LogMessage>(file, line, function, msgLevel)};
message.get()->write().append(entry);
message.get()->setExpression(boolean_expression);
if (internal::wasFatal(level)) {
message.get()->write().append(stack_trace);
FatalMessagePtr fatal_message{std2::make_unique<FatalMessage>(*(message._move_only.get()), fatal_signal)};
// At destruction, flushes fatal message to g2LogWorker
// either we will stay here until the background worker has received the fatal
// message, flushed the crash message to the sinks and exits with the same fatal signal
//..... OR it's in unit-test mode then we throw a std::runtime_error (and never hit sleep)
fatalCall(fatal_message);
} else {
pushMessageToLogger(message);
}
}
/**
* save the message to the logger. In case of called before the logger is instantiated
* the first message will be saved. Any following subsequent unitnialized log calls
* will be ignored.
*
* The first initialized log entry will also save the first uninitialized log message, if any
* @param log_entry to save to logger
*/
void pushMessageToLogger(LogMessagePtr incoming) { // todo rename to Push SavedMessage To Worker
// Uninitialized messages are ignored but does not CHECK/crash the logger
if (!internal::isLoggingInitialized()) {
std::call_once(g_set_first_uninitialized_flag, [&] {
g_first_unintialized_msg = incoming.release();
std::string err = {"LOGGER NOT INITIALIZED:\n\t\t"};
err.append(g_first_unintialized_msg->message());
std::string& str = g_first_unintialized_msg->write();
str.clear();
str.append(err); // replace content
std::cerr << str << std::endl;
});
return;
}
// logger is initialized
g_logger_instance->save(incoming);
}
/** Fatal call saved to logger. This will trigger SIGABRT or other fatal signal
* to exit the program. After saving the fatal message the calling thread
* will sleep forever (i.e. until the background thread catches up, saves the fatal
* message and kills the software with the fatal signal.
*/
void fatalCallToLogger(FatalMessagePtr message) {
if (!isLoggingInitialized()) {
std::ostringstream error;
error << "FATAL CALL but logger is NOT initialized\n"
<< "SIGNAL: " << message.get()->signal()
<< "\nMessage: \n" << message.get()->toString() << std::flush;
std::cerr << error.str() << std::flush;
internal::exitWithDefaultSignalHandler(message.get()->_signal_id);
}
g_logger_instance->fatal(message);
while (true) {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
// By default this function pointer goes to \ref fatalCallToLogger;
std::function<void(FatalMessagePtr) > g_fatal_to_g2logworker_function_ptr = fatalCallToLogger;
/** The default, initial, handling to send a 'fatal' event to g2logworker
* the caller will stay here, eternally, until the software is aborted
* ... in the case of unit testing it is the given "Mock" fatalCall that will
* define the behaviour.
*/
void fatalCall(FatalMessagePtr message) {
g_fatal_to_g2logworker_function_ptr(FatalMessagePtr{std::move(message)});
}
/** REPLACE fatalCallToLogger for fatalCallForUnitTest
* This function switches the function pointer so that only
* 'unitTest' mock-fatal calls are made.
* */
void changeFatalInitHandlerForUnitTesting(std::function<void(FatalMessagePtr) > fatal_call) {
g_fatal_to_g2logworker_function_ptr = fatal_call;
}
} // internal
/** REPLACE fatalCallToLogger for fatalCallForUnitTest
* This function switches the function pointer so that only
* 'unitTest' mock-fatal calls are made.
* */
void setFatalExitHandler(std::function<void(FatalMessagePtr) > fatal_call) {
g_fatal_to_g2logworker_function_ptr = fatal_call;
}
} // internal
} // g2

View File

@ -64,10 +64,26 @@ namespace g2 {
void saveMessage(const char* message, const char* file, int line, const char* function, const LEVELS& level,
const char* boolean_expression, int fatal_signal, const char* stack_trace);
// forwards the message to all sinks
void pushMessageToLogger(LogMessagePtr log_entry);
// forwards a FATAL message to all sinks,. after which the g2logworker
// will trigger crashhandler / g2::internal::exitWithDefaultSignalHandler
//
// By default the "fatalCall" will forward a Fatalessageptr to this function
// this behaviour can be changed if you set a different fatal handler through
// "setFatalExitHandler"
void pushFatalMessageToLogger(FatalMessagePtr message);
// Save the created FatalMessage to any existing sinks and exit with
// the originating fatal signal,. or SIGABRT if it originated from a broken contract
// By default forwards to: pushFatalMessageToLogger, see "setFatalExitHandler" to override
//
// If you override it then you probably want to call "pushFatalMessageToLogger" after your
// custom fatal handler is done. This will make sure that the fatal message the pushed
// to sinks as well as shutting down the process
void fatalCall(FatalMessagePtr message);
@ -77,8 +93,6 @@ namespace g2 {
// Shutdown logging, but ONLY if the active logger corresponds to the one currently initialized
bool shutDownLoggingForActiveOnly(LogWorker* active);
/** By default the g2log will call g2LogWorker::fatal(...) which will
* abort() the system after flushing the logs to file. This makes unit
* test of FATAL level cumbersome. A work around is to change the
@ -87,7 +101,10 @@ namespace g2 {
* The bool return values in the fatal_call is whether or not the fatal_call should
*
*/
void changeFatalInitHandlerForUnitTesting(std::function<void(FatalMessagePtr) > fatal_call);
void setFatalExitHandler(std::function<void(FatalMessagePtr)> fatal_call);
} // internal
} // g2
@ -175,3 +192,4 @@ And here is possible output
if (false == (boolean_expression)) INTERNAL_CONTRACT_MESSAGE(#boolean_expression).capturef(printf_like_message, ##__VA_ARGS__)

View File

@ -15,6 +15,22 @@
#include "g2log.hpp"
#include <atomic>
#include <cassert>
namespace {
void checkLevel(const int level) {
#if (defined(CHANGE_G3LOG_DEBUG_TO_DBUG))
CHECK((level >= DBUG.value) && (level <= FATAL.value));
#else
CHECK((level >= DEBUG.value) && (level <= FATAL.value));
#endif
}
} // anonymous namespace
namespace g2 {
namespace internal {
bool wasFatal(const LEVELS& level) {
@ -29,11 +45,10 @@ namespace g2 {
} // internal
#ifdef G2_DYNAMIC_LOGGING
void setLogLevel(LEVELS log_level, bool enabled) {
assert(internal::g_level_size == 4 && "Mismatch between number of logging levels and their use");
int level = log_level.value;
CHECK((level >= DEBUG.value) && (level <= FATAL.value));
CHECK((level >= g2::kDebugVaulue) && (level <= FATAL.value));
internal::g_log_level_status[level].store(enabled, std::memory_order_release);
}
#endif
@ -41,7 +56,7 @@ namespace g2 {
bool logLevel(LEVELS log_level) {
#ifdef G2_DYNAMIC_LOGGING
int level = log_level.value;
CHECK((level >= DEBUG.value) && (level <= FATAL.value));
CHECK((level >= g2::kDebugVaulue) && (level <= FATAL.value));
bool status = (internal::g_log_level_status[level].load(std::memory_order_acquire));
return status;
#endif

View File

@ -2,7 +2,7 @@
* 2012 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
* ============================================================================
* Filename:g2loglevels.hpp Part of Framework for Logging and Design By Contract
@ -13,39 +13,77 @@
#pragma once
// Users of Juce or other libraries might have a define DEBUG which clashes with
// the DEBUG logging level for G3log. In that case they can instead use the define
// "CHANGE_G3LOG_DEBUG_TO_DBUG" and G3log's logging level DEBUG is changed to be DBUG
#if (defined(CHANGE_G3LOG_DEBUG_TO_DBUG))
#if (defined(DBUG))
#error "DEBUG is already defined elsewhere which clashes with G3Log's log level DEBUG"
#endif
#else
#if (defined(DEBUG))
#error "DEBUG is already defined elsewhere which clashes with G3Log's log level DEBUG"
#endif
#endif
#include <string>
// Levels for logging, made so that it would be easy to change, remove, add levels -- KjellKod
struct LEVELS {
// force internal copy of the const char*. This is a simple safeguard for when g3log is used in a
// "dynamic, runtime loading of shared libraries"
LEVELS(const LEVELS& other)
: value(other.value), text(other.text.c_str()) {}
: value(other.value), text(other.text.c_str()) {}
LEVELS(int id, const char* idtext) : value(id), text(idtext) {}
friend bool operator==(const LEVELS& lhs, const LEVELS& rhs) {
return (lhs.value == rhs.value && lhs.text == rhs.text);
}
const int value;
const std::string text;
};
const LEVELS DEBUG{0, {"DEBUG"}}, INFO{DEBUG.value + 1, {"INFO"}},
WARNING{INFO.value + 1, {"WARNING"}},
// Insert here *any* extra logging levels that is needed
// 1) Remember to update the FATAL initialization below
// 2) Remember to update the initialization of "g2loglevels.cpp/g_log_level_status"
FATAL{WARNING.value + 1, {"FATAL"}};
namespace g2 {
namespace internal {
const LEVELS CONTRACT{100, {"CONTRACT"}}, FATAL_SIGNAL{101, {"FATAL_SIGNAL"}};
bool wasFatal(const LEVELS& level);
}
static const int kDebugVaulue = 0;
}
#if (defined(CHANGE_G3LOG_DEBUG_TO_DBUG))
const LEVELS DBUG {
g2::kDebugVaulue, {"DEBUG"}
},
#else
const LEVELS DEBUG {
g2::kDebugVaulue, {"DEBUG"}
},
#endif
INFO {g2::kDebugVaulue + 1, {"INFO"}},
WARNING {INFO.value + 1, {"WARNING"}},
// Insert here *any* extra logging levels that is needed
// 1) Remember to update the FATAL initialization below
// 2) Remember to update the initialization of "g2loglevels.cpp/g_log_level_status"
FATAL {WARNING.value + 1, {"FATAL"}};
namespace g2 {
namespace internal {
const LEVELS CONTRACT {
100, {"CONTRACT"}
}, FATAL_SIGNAL {101, {"FATAL_SIGNAL"}},
FATAL_EXCEPTION {102, {"FATAL_EXCEPTION"}};
bool wasFatal(const LEVELS& level);
}
#ifdef G2_DYNAMIC_LOGGING
// Enable/Disable a log level {DEBUG,INFO,WARNING,FATAL}
void setLogLevel(LEVELS level, bool enabled_status);
// Enable/Disable a log level {DEBUG,INFO,WARNING,FATAL}
void setLogLevel(LEVELS level, bool enabled_status);
#endif
bool logLevel(LEVELS level);
bool logLevel(LEVELS level);
} // g2

View File

@ -2,7 +2,7 @@
* 2012 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
* ============================================================================
* Filename:g2logmessage.cpp Part of Framework for Logging and Design By Contract
@ -18,68 +18,124 @@
#include <mutex>
namespace {
std::once_flag g_start_time_flag;
std::chrono::steady_clock::time_point g_start_time;
std::once_flag g_start_time_flag;
std::chrono::steady_clock::time_point g_start_time;
int64_t microsecondsCounter() {
std::call_once(g_start_time_flag, []() { g_start_time = std::chrono::steady_clock::now(); });
auto now = std::chrono::steady_clock::now();
return std::chrono::duration_cast<std::chrono::microseconds>(now - g_start_time).count();
}
int64_t microsecondsCounter() {
std::call_once(g_start_time_flag, []() {
g_start_time = std::chrono::steady_clock::now();
});
auto now = std::chrono::steady_clock::now();
return std::chrono::duration_cast<std::chrono::microseconds>(now - g_start_time).count();
}
std::string splitFileName(const std::string& str) {
size_t found;
found = str.find_last_of("(/\\");
return str.substr(found + 1);
}
std::string splitFileName(const std::string &str) {
size_t found;
found = str.find_last_of("(/\\");
return str.substr(found + 1);
}
} // anonymous
namespace g2 {
std::string LogMessage::toString() const {
// helper for setting the normal log details in an entry
std::string LogDetailsToString(const LogMessage& msg) {
std::string out;
out.append("\n" + timestamp() + "." + microseconds() + "\t"
+ level() + " [" + file() + " L: " + line() + "]\t");
// Non-fatal Log Message
if (false == wasFatal()) {
out.append('"' + message() + '"');
return out;
}
if (internal::FATAL_SIGNAL.value == _level.value) {
out.clear(); // clear any previous text and formatting
out.append("\n" + timestamp() + "." + microseconds()
+ "\n\n***** FATAL SIGNAL RECEIVED ******* \n"
+ '"' + message() + '"');
return out;
}
// Not crash scenario but LOG or CONTRACT
auto level_value = _level.value;
if (FATAL.value == level_value) {
static const std::string fatalExitReason = {"EXIT trigger caused by LOG(FATAL) entry: "};
out.append("\n\t*******\t " + fatalExitReason + "\n\t" + '"' + message() + '"');
} else if (internal::CONTRACT.value == level_value) {
static const std::string contractExitReason = {"EXIT trigger caused by broken Contract:"};
out.append("\n\t*******\t " + contractExitReason + " CHECK(" + _expression + ")\n\t"
+ '"' + message() + '"');
} else {
static const std::string errorUnknown = {"UNKNOWN Log Message Type"};
out.append("\n\t*******" + errorUnknown + "\t\n" + '"' + message() + '"');
}
out.append("\n" + msg.timestamp() + "." + msg.microseconds() + "\t"
+ msg.level() + " [" + msg.file() + " L: " + msg.line() + "]\t");
return out;
}
std::string LogMessage::timestamp(const std::string & time_look) const {
return localtime_formatted(_timestamp, time_look);
// helper for normal
std::string normalToString(const LogMessage& msg) {
auto out = LogDetailsToString(msg);
out.append('"' + msg.message() + '"');
return out;
}
// helper for fatal signal
std::string fatalSignalToString(const LogMessage &msg) {
std::string out; // clear any previous text and formatting
out.append("\n" + msg.timestamp() + "." + msg.microseconds()
+ "\n\n***** FATAL SIGNAL RECEIVED ******* \n"
+ '"' + msg.message() + '"');
return out;
}
// helper for fatal exception (windows only)
std::string fatalExceptionToString(const LogMessage &msg) {
std::string out; // clear any previous text and formatting
out.append("\n" + msg.timestamp() + "." + msg.microseconds()
+ "\n\n***** FATAL EXCEPTION RECEIVED ******* \n"
+ '"' + msg.message() + '"');
return out;
}
// helper for fatal LOG
std::string fatalLogToString(const LogMessage& msg) {
auto out = LogDetailsToString(msg);
static const std::string fatalExitReason = {"EXIT trigger caused by LOG(FATAL) entry: "};
out.append("\n\t*******\t " + fatalExitReason + "\n\t" + '"' + msg.message() + '"');
return out;
}
// helper for fatal CHECK
std::string fatalCheckToString(const LogMessage& msg) {
auto out = LogDetailsToString(msg);
static const std::string contractExitReason = {"EXIT trigger caused by broken Contract:"};
out.append("\n\t*******\t " + contractExitReason + " CHECK(" + msg.expression() + ")\n\t"
+ '"' +msg. message() + '"');
return out;
}
// Format the log message according to it's type
std::string LogMessage::toString() const {
if (false == wasFatal()) {
return normalToString(*this);
}
LogMessage::LogMessage(const std::string &file, const int line,
const std::string& function, const LEVELS& level)
const auto level_value = _level.value;
if (internal::FATAL_SIGNAL.value == _level.value) {
return fatalSignalToString(*this);
}
if (internal::FATAL_EXCEPTION.value == _level.value) {
return fatalExceptionToString(*this);
}
if (FATAL.value == _level.value) {
return fatalLogToString(*this);
}
if (internal::CONTRACT.value == level_value) {
return fatalCheckToString(*this);
}
// What? Did we hit a custom made level?
auto out = LogDetailsToString(*this);
static const std::string errorUnknown = {"UNKNOWN or Custom made Log Message Type"};
out.append("\n\t*******" + errorUnknown + "\t\n" + '"' + message() + '"');
return out;
}
std::string LogMessage::timestamp(const std::string &time_look) const {
return localtime_formatted(_timestamp, time_look);
}
LogMessage::LogMessage(const std::string &file, const int line,
const std::string &function, const LEVELS &level)
: _timestamp(g2::systemtime_now())
, _call_thread_id(std::this_thread::get_id())
, _microseconds(microsecondsCounter())
@ -90,12 +146,12 @@ namespace g2 {
{}
LogMessage::LogMessage(const std::string& fatalOsSignalCrashMessage)
LogMessage::LogMessage(const std::string &fatalOsSignalCrashMessage)
: LogMessage({""}, 0, {""}, internal::FATAL_SIGNAL) {
_message.append(fatalOsSignalCrashMessage);
}
LogMessage::LogMessage(const LogMessage& other)
_message.append(fatalOsSignalCrashMessage);
}
LogMessage::LogMessage(const LogMessage &other)
: _timestamp(other._timestamp)
, _call_thread_id(other._call_thread_id)
, _microseconds(other._microseconds)
@ -105,11 +161,11 @@ namespace g2 {
, _level(other._level)
, _expression(other._expression)
, _message(other._message)
{
}
{
}
LogMessage::LogMessage(LogMessage&& other)
LogMessage::LogMessage(LogMessage &&other)
: _timestamp(other._timestamp)
, _call_thread_id(other._call_thread_id)
, _microseconds(other._microseconds)
@ -119,31 +175,31 @@ namespace g2 {
, _level(other._level)
, _expression(std::move(other._expression))
, _message(std::move(other._message)) {
}
std::string LogMessage::threadID() const {
std::ostringstream oss;
oss << _call_thread_id;
return oss.str();
}
}
FatalMessage::FatalMessage(const LogMessage& details, int signal_id)
std::string LogMessage::threadID() const {
std::ostringstream oss;
oss << _call_thread_id;
return oss.str();
}
FatalMessage::FatalMessage(const LogMessage &details, g2::SignalType signal_id)
: LogMessage(details), _signal_id(signal_id) { }
FatalMessage::FatalMessage(const FatalMessage& other)
FatalMessage::FatalMessage(const FatalMessage &other)
: LogMessage(other), _signal_id(other._signal_id) {}
LogMessage FatalMessage::copyToLogMessage() const {
return LogMessage(*this);
}
std::string FatalMessage::signal() const{
return internal::signalName(_signal_id);
}
LogMessage FatalMessage::copyToLogMessage() const {
return LogMessage(*this);
}
std::string FatalMessage::reason() const {
return internal::exitReasonName(_level, _signal_id);
}
} // g2

View File

@ -22,8 +22,8 @@
#include "g2loglevels.hpp"
#include "g2time.hpp"
#include "g2moveoncopy.hpp"
#include "crashhandler.hpp"
#include <memory>
namespace g2 {
/** LogMessage contains all the data collected from the LOG(...) call.
@ -86,14 +86,14 @@ namespace g2 {
* A thread that causes a FatalMessage will sleep forever until the
* application has exited (after message flush) */
struct FatalMessage : public LogMessage {
FatalMessage(const LogMessage& details, int signal_id);
FatalMessage(const LogMessage& details, g2::SignalType signal_id);
FatalMessage(const FatalMessage&);
virtual ~FatalMessage(){};
LogMessage copyToLogMessage() const;
std::string signal() const;
std::string reason() const;
const int _signal_id;
const SignalType _signal_id;
};

View File

@ -2,13 +2,87 @@
* 2013 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 "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?
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
#define SIGNAL_HANDLER_VERIFY() g2::installSignalHandlerForThread()
#else
// Does nothing --- enforces that semicolon must be written
#define SIGNAL_HANDLER_VERIFY() do {} while(0)
#endif
/** logCapture is a simple struct for capturing log/fatal entries. At destruction the
* captured message is forwarded to background worker.
* As a safety precaution: No memory allocated here will be moved into the background
* worker in case of dynamic loaded library reasons instead the arguments are copied
* inside of g2log.cpp::saveMessage*/
LogCapture::~LogCapture() {
using namespace g2::internal;
SIGNAL_HANDLER_VERIFY();
saveMessage(_stream.str().c_str(), _file, _line, _function, _level, _expression, _fatal_signal, _stack_trace.c_str());
}
/// Called from crash handler when a fatal signal has occurred (SIGSEGV etc)
LogCapture::LogCapture(const LEVELS &level, g2::SignalType fatal_signal, const char* dump)
: LogCapture("", 0, "", level, "", fatal_signal, dump) {
}
/**
* @file, line, function are given in g2log.hpp from macros
* @level INFO/DEBUG/WARNING/FATAL
* @expression for CHECK calls
* @fatal_signal for failed CHECK:SIGABRT or fatal signal caught in the signal handler
*/
LogCapture::LogCapture(const char* file, const int line, const char* function, const LEVELS &level,
const char* expression, g2::SignalType fatal_signal, const char* dump)
: _file(file), _line(line), _function(function), _level(level), _expression(expression), _fatal_signal(fatal_signal) {
if (g2::internal::wasFatal(level)) {
_stack_trace = {"\n*******\tSTACKDUMP *******\n"};
_stack_trace.append(g2::internal::stackdump(dump));
}
}
/**
* capturef, used for "printf" like API in CHECKF, LOGF, LOGF_IF
* See also for the attribute formatting ref: http://www.codemaestro.com/reviews/18
*/
void LogCapture::capturef(const char* printf_like_message, ...) {
static const int kMaxMessageSize = 2048;
static const std::string kTruncatedWarningText = "[...truncated...]";
char finished_message[kMaxMessageSize];
va_list arglist;
va_start(arglist, printf_like_message);
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) && !defined(__GNUC__))
const int nbrcharacters = vsnprintf_s(finished_message, _countof(finished_message), _TRUNCATE, printf_like_message, arglist);
#else
const int nbrcharacters = vsnprintf(finished_message, sizeof (finished_message), printf_like_message, arglist);
#endif
va_end(arglist);
if (nbrcharacters <= 0) {
stream() << "\n\tERROR LOG MSG NOTIFICATION: Failure to parse successfully the message";
stream() << '"' << printf_like_message << '"' << std::endl;
} else if (nbrcharacters > kMaxMessageSize) {
stream() << finished_message << kTruncatedWarningText;
} else {
stream() << finished_message;
}
}

View File

@ -2,7 +2,7 @@
* 2013 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
* ============================================================================*/
@ -10,75 +10,49 @@
#include <string>
#include <sstream>
#include <cstdarg>
#include <csignal>
#include "g2loglevels.hpp"
#include "crashhandler.hpp"
#include "g2log.hpp"
#include "crashhandler.hpp"
/**
* Simple struct for capturing log/fatal entries. At destruction the captured message is
* forwarded to background worker.
* As a safety precaution: No memory allocated here will be moved into the background
* worker in case of dynamic loaded library reasons
*/
struct LogCapture {
/// Called from crash handler when a fatal signal has occurred (SIGSEGV etc)
LogCapture(const LEVELS& level, int fatal_signal)
: LogCapture("", 0, "", level, "", fatal_signal) {
}
LogCapture(const LEVELS& level, g2::SignalType fatal_signal, const char* dump = nullptr);
/**
* Simple struct for capturing log/fatal entries. At destruction the captured message is forwarded to background worker.
* -- As a safety precaution: No memory allocated here will be moved into the background worker in case of dynamic loaded library reasons
* -- instead the arguments are copied inside of g2log.cpp::saveMessage
* @file, line, function are given in g2log.hpp from macros
* @level INFO/DEBUG/WARNING/FATAL
* @expression for CHECK calls
* @fatal_signal for failed CHECK:SIGABRT or fatal signal caught in the signal handler
*/
LogCapture(const char* file, const int line, const char* function, const LEVELS& level, const char* expression = "", int fatal_signal = SIGABRT)
: _file(file), _line(line), _function(function), _level(level), _expression(expression), _fatal_signal(fatal_signal) {
if (g2::internal::wasFatal(level)) {
_stack_trace = {"\n*******\tSTACKDUMP *******\n"};
_stack_trace.append(g2::internal::stackdump());
}
}
LogCapture(const char* file, const int line, const char* function, const LEVELS& level,
const char* expression = "", g2::SignalType fatal_signal = SIGABRT, const char* dump = nullptr);
// At destruction the message will be forwarded to the g2log worker.
// in case of dynamically (at runtime) loaded libraries the important thing to know is that
// At destruction the message will be forwarded to the g2log worker.
// in case of dynamically (at runtime) loaded libraries the important thing to know is that
// all strings are copied so the original are not destroyed at the receiving end, only the copy
~LogCapture();
virtual ~LogCapture();
// Use "-Wall" to generate warnings in case of illegal printf format.
// Use "-Wall" to generate warnings in case of illegal printf format.
// Ref: http://www.unixwiz.net/techtips/gnu-c-attributes.html
#ifndef __GNUC__
#define __attribute__(x) // Disable 'attributes' if compiler does not support 'em
#endif
void capturef(const char *printf_like_message, ...) __attribute__((format(printf, 2, 3))) // ref: http://www.codemaestro.com/reviews/18
{
static const int kMaxMessageSize = 2048;
static const std::string kTruncatedWarningText = "[...truncated...]";
char finished_message[kMaxMessageSize];
va_list arglist;
va_start(arglist, printf_like_message);
void capturef(const char* printf_like_message, ...) __attribute__((format(printf, 2, 3))); // 2,3 ref: http://www.codemaestro.com/reviews/18
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) && !defined(__GNUC__))
const int nbrcharacters = vsnprintf_s(finished_message, _countof(finished_message), _TRUNCATE, printf_like_message, arglist);
#else
const int nbrcharacters = vsnprintf(finished_message, sizeof (finished_message), printf_like_message, arglist);
#endif
va_end(arglist);
if (nbrcharacters <= 0) {
stream() << "\n\tERROR LOG MSG NOTIFICATION: Failure to parse successfully the message";
stream() << '"' << printf_like_message << '"' << std::endl;
} else if (nbrcharacters > kMaxMessageSize) {
stream() << finished_message << kTruncatedWarningText;
} else {
stream() << finished_message;
}
}
/// prettifying API for this completely open struct
std::ostringstream& stream() {
return _stream;
}
@ -92,7 +66,7 @@ struct LogCapture {
const char* _function;
const LEVELS& _level;
const char* _expression;
const int _fatal_signal;
const g2::SignalType _fatal_signal;
};
//} // g2

View File

@ -12,6 +12,7 @@
* ********************************************* */
#include "g2logworker.hpp"
#include "g2logmessage.hpp"
#include <cassert>
#include <functional>
@ -46,12 +47,19 @@ namespace g2 {
// safe to shutdown logging now
g2::internal::shutDownLogging();
std::string signal = msgPtr.get()->signal();
auto fatal_signal_id = msgPtr.get()->_signal_id;
std::string reason = msgPtr.get()->reason();
const auto level = msgPtr.get()->_level;
const auto fatal_id = msgPtr.get()->_signal_id;
std::unique_ptr<LogMessage> uniqueMsg(std::move(msgPtr.get()));
uniqueMsg->write().append("\nExiting after fatal event (").append(uniqueMsg->level());
uniqueMsg->write().append("). Exiting with signal: ").append(signal)
// Change output in case of a fatal signal (or windows exception)
std::string exiting = {"Fatal type: "};
uniqueMsg->write().append("). ").append(exiting).append(" ").append(reason)
.append("\nLog content flushed flushed sucessfully to sink\n\n");
std::cerr << uniqueMsg->message() << std::flush;
@ -64,7 +72,7 @@ namespace g2 {
// This clear is absolutely necessary
// All sinks are forced to receive the fatal message above before we continue
_sinks.clear(); // flush all queues
internal::exitWithDefaultSignalHandler(fatal_signal_id);
internal::exitWithDefaultSignalHandler(level, fatal_id);
// should never reach this point
perror("g2log exited after receiving FATAL trigger. Flush message status: ");

218
src/stacktrace_windows.cpp Normal file
View File

@ -0,0 +1,218 @@
/** ==========================================================================
* Original code made by Robert Engeln. Given as a PUBLIC DOMAIN dedication for
* the benefit of g3log. It was originally published at:
* http://code-freeze.blogspot.com/2012/01/generating-stack-traces-from-c.html
* 2014-2015: adapted for g3log by Kjell Hedstrom (KjellKod).
*
* 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 "stacktrace_windows.hpp"
#include <windows.h>
#include <DbgHelp.h>
#include <map>
#include <memory>
#include <atomic>
#include <cassert>
#include <vector>
#include <cstdlib>
#include <mutex>
#pragma once
#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
#error "stacktrace_win.cpp used but not on a windows system"
#endif
#define g2_MAP_PAIR_STRINGIFY(x) {x, #x}
#define thread_local __declspec(thread)
namespace {
thread_local bool g_thread_local_recursive_crash_check = false;
const std::map<g2::SignalType, std::string> kExceptionsAsText = {
g2_MAP_PAIR_STRINGIFY(EXCEPTION_ACCESS_VIOLATION)
, g2_MAP_PAIR_STRINGIFY(EXCEPTION_ARRAY_BOUNDS_EXCEEDED)
, g2_MAP_PAIR_STRINGIFY(EXCEPTION_BREAKPOINT)
, g2_MAP_PAIR_STRINGIFY(EXCEPTION_DATATYPE_MISALIGNMENT)
, g2_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_DENORMAL_OPERAND)
, g2_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_DIVIDE_BY_ZERO)
, g2_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INEXACT_RESULT)
, g2_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INEXACT_RESULT)
, g2_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INVALID_OPERATION)
, g2_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_OVERFLOW)
, g2_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_STACK_CHECK)
, g2_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_UNDERFLOW)
, g2_MAP_PAIR_STRINGIFY(EXCEPTION_ILLEGAL_INSTRUCTION)
, g2_MAP_PAIR_STRINGIFY(EXCEPTION_IN_PAGE_ERROR)
, g2_MAP_PAIR_STRINGIFY(EXCEPTION_INT_DIVIDE_BY_ZERO)
, g2_MAP_PAIR_STRINGIFY(EXCEPTION_INT_OVERFLOW)
, g2_MAP_PAIR_STRINGIFY(EXCEPTION_INVALID_DISPOSITION)
, g2_MAP_PAIR_STRINGIFY(EXCEPTION_NONCONTINUABLE_EXCEPTION)
, g2_MAP_PAIR_STRINGIFY(EXCEPTION_PRIV_INSTRUCTION)
, g2_MAP_PAIR_STRINGIFY(EXCEPTION_SINGLE_STEP)
, g2_MAP_PAIR_STRINGIFY(EXCEPTION_STACK_OVERFLOW)
};
// Using the given context, fill in all the stack frames.
// Which then later can be interpreted to human readable text
void captureStackTrace(CONTEXT* context, std::vector<uint64_t>& frame_pointers) {
DWORD machine_type = 0;
STACKFRAME64 frame = {}; // force zeroeing
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Mode = AddrModeFlat;
#ifdef _M_X64
frame.AddrPC.Offset = context->Rip;
frame.AddrFrame.Offset = context->Rbp;
frame.AddrStack.Offset = context->Rsp;
machine_type = IMAGE_FILE_MACHINE_AMD64;
#else
frame.AddrPC.Offset = context->Eip;
frame.AddrPC.Offset = context->Ebp;
frame.AddrPC.Offset = context->Esp;
machine_type = IMAGE_FILE_MACHINE_I386;
#endif
auto noErrors = TRUE; // Why, Oh Why have Microsoft redefined bool?
for (size_t index = 0; index < frame_pointers.size(); ++index)
{
if (StackWalk64(machine_type,
GetCurrentProcess(),
GetCurrentThread(),
&frame,
context,
NULL,
SymFunctionTableAccess64,
SymGetModuleBase64,
NULL)) {
frame_pointers[index] = frame.AddrPC.Offset;
} else {
break;
}
}
}
// extract readable text from a given stack frame. All thanks to
// using SymFromAddr and SymGetLineFromAddr64 with the stack pointer
std::string getSymbolInformation(const size_t index, const std::vector<uint64_t>& frame_pointers) {
auto addr = frame_pointers[index];
std::string frame_dump = "stack dump [" + std::to_string(index) + "]\t";
DWORD64 displacement64;
DWORD displacement;
char symbol_buffer[sizeof(SYMBOL_INFO) + 256];
SYMBOL_INFO* symbol = reinterpret_cast<SYMBOL_INFO*>(symbol_buffer);
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
symbol->MaxNameLen = MAX_SYM_NAME;
IMAGEHLP_LINE64 line;
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
std::string lineInformation;
std::string callInformation;
if (SymFromAddr(GetCurrentProcess(), addr, &displacement64, symbol)) {
callInformation.append(" ").append({symbol->Name, symbol->NameLen});
if (SymGetLineFromAddr64(GetCurrentProcess(), addr, &displacement, &line)) {
lineInformation.append("\t").append(line.FileName).append(" L: ");
lineInformation.append(std::to_string(line.LineNumber));
}
}
frame_dump.append(lineInformation).append(callInformation);
return frame_dump;
}
// Retrieves all the symbols for the stack frames, fills them witin a text representation and returns it
std::string convertFramesToText(std::vector<uint64_t>& frame_pointers) {
std::string dump; // slightly more efficient than ostringstream
const size_t kSize = frame_pointers.size();
for (size_t index = 0; index < kSize && frame_pointers[index]; ++index) {
dump += getSymbolInformation(index, frame_pointers);
dump += "\n";
}
return dump;
}
} // anonymous
namespace stacktrace {
/// 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)};
return unknown;
}
return iter->second;
}
/// helper function: retrieve stackdump from no excisting exception pointer
std::string stackdump() {
CONTEXT current_context;
memset(&current_context, 0, sizeof(CONTEXT));
RtlCaptureContext(&current_context);
return stackdump(&current_context);
}
/// helper function: retrieve stackdump, starting from an exception pointer
std::string stackdump(EXCEPTION_POINTERS* info) {
auto context = info->ContextRecord;
return stackdump(context);
}
/// main stackdump function. retrieve stackdump, from the given context
std::string stackdump(CONTEXT* context) {
if (g_thread_local_recursive_crash_check) {
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;
static std::mutex m;
std::lock_guard<std::mutex> lock(m);
const BOOL kLoadSymModules = TRUE;
const auto initialized = SymInitialize(GetCurrentProcess(), nullptr, kLoadSymModules);
if (TRUE != initialized) {
return {"Error: Cannot call SymInitialize(...) for retrieving symbols in stack"};
}
std::shared_ptr<void> RaiiSymCleaner(nullptr, [&](void*) {
SymCleanup(GetCurrentProcess());
}); // Raii sym cleanup
const size_t kmax_frame_dump_size = 64;
std::vector<uint64_t> frame_pointers(kmax_frame_dump_size);
// C++11: size set and values are zeroed
assert(frame_pointers.size() == kmax_frame_dump_size);
captureStackTrace(context, frame_pointers);
return convertFramesToText(frame_pointers);
}
} // stacktrace

View File

@ -0,0 +1,37 @@
/** ==========================================================================
* 2014 by KjellKod.cc AND Robert Engeln.
* The stacktrace code was given as a public domain dedication by Robert Engeln
* It was originally published at: http://code-freeze.blogspot.com/2012/01/generating-stack-traces-from-c.html
* It was (here) modified for g3log purposes.
*
* 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
* ============================================================================*/
#pragma once
#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
#error "stacktrace_win.cpp used but not on a windows system"
#endif
#include <string>
#include <windows.h>
#include "crashhandler.hpp"
namespace stacktrace {
/// return the text description of a Windows exception code
std::string exceptionIdToText(g2::SignalType id);
/// helper function: retrieve stackdump from no excisting exception pointer
std::string stackdump();
/// helper function: retrieve stackdump, starting from an exception pointer
std::string stackdump(EXCEPTION_POINTERS* info);
/// main stackdump function. retrieve stackdump, from the given context
std::string stackdump(CONTEXT* context);
} // stacktrace

View File

@ -2,7 +2,7 @@
* 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
* ============================================================================*/
@ -16,18 +16,26 @@
#include <cmath>
#if defined(G2LOG_PERFORMANCE)
const std::string title{"G2LOG"};
const std::string title {
"G2LOG"
};
#elif defined(GOOGLE_GLOG_PERFORMANCE)
const std::string title{"GOOGLE__GLOG"};
const std::string title {
"GOOGLE__GLOG"
};
#else
#error G2LOG_PERFORMANCE or GOOGLE_GLOG_PERFORMANCE was not defined
#endif
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
const std::string g_path{"./"};
const std::string g_path {
"./"
};
#else
const std::string g_path{"/tmp/"};
const std::string g_path {
"/tmp/"
};
#endif
@ -41,147 +49,153 @@ using namespace g2_test;
//
int main(int argc, char** argv)
{
size_t number_of_threads{0};
if(argc == 2)
{
number_of_threads = atoi(argv[1]);
}
if(argc != 2 || number_of_threads == 0)
{
std::cerr << "USAGE is: " << argv[0] << " number_threads" << std::endl;
return 1;
}
std::ostringstream thread_count_oss;
thread_count_oss << number_of_threads;
size_t number_of_threads {0};
if (argc == 2)
{
number_of_threads = atoi(argv[1]);
}
if (argc != 2 || number_of_threads == 0)
{
std::cerr << "USAGE is: " << argv[0] << " number_threads" << std::endl;
return 1;
}
std::ostringstream thread_count_oss;
thread_count_oss << number_of_threads;
const std::string g_prefix_log_name = title + "-performance-" + thread_count_oss.str() + "threads-WORST_LOG";
const std::string g_measurement_dump= g_path + g_prefix_log_name + "_RESULT.txt";
const std::string g_measurement_bucket_dump= g_path + g_prefix_log_name + "_RESULT_buckets.txt";
const uint64_t us_to_ms{1000};
const uint64_t us_to_s{1000000};
const std::string g_prefix_log_name = title + "-performance-" + thread_count_oss.str() + "threads-WORST_LOG";
const std::string g_measurement_dump = g_path + g_prefix_log_name + "_RESULT.txt";
const std::string g_measurement_bucket_dump = g_path + g_prefix_log_name + "_RESULT_buckets.txt";
const uint64_t us_to_ms {
1000
};
const uint64_t us_to_s {
1000000
};
std::ostringstream oss;
oss << "\n\n" << title << " performance " << number_of_threads << " threads WORST (PEAK) times\n";
oss << "Each thread running #: " << g_loop << " * " << g_iterations << " iterations of log entries" << std::endl; // worst mean case is about 10us per log entry
const uint64_t xtra_margin{2};
oss << "*** It can take som time. Please wait: Approximate wait time on MY PC was: " << number_of_threads * (uint64_t)(g_iterations * 10 * xtra_margin / us_to_s) << " seconds" << std::endl;
writeTextToFile(g_measurement_dump, oss.str(), kAppend);
oss.str(""); // clear the stream
std::ostringstream oss;
oss << "\n\n" << title << " performance " << number_of_threads << " threads WORST (PEAK) times\n";
oss << "Each thread running #: " << g_loop << " * " << g_iterations << " iterations of log entries" << std::endl; // worst mean case is about 10us per log entry
const uint64_t xtra_margin {
2
};
oss << "*** It can take som time. Please wait: Approximate wait time on MY PC was: " << number_of_threads * (uint64_t)(g_iterations * 10 * xtra_margin / us_to_s) << " seconds" << std::endl;
writeTextToFile(g_measurement_dump, oss.str(), kAppend);
oss.str(""); // clear the stream
#if defined(G2LOG_PERFORMANCE)
auto logger_n_handle = g2::LogWorker::createWithDefaultLogger(g_prefix_log_name, g_path);
g2::initializeLogging(logger_n_handle.worker.get());
auto logger_n_handle = g2::LogWorker::createWithDefaultLogger(g_prefix_log_name, g_path);
g2::initializeLogging(logger_n_handle.worker.get());
#elif defined(GOOGLE_GLOG_PERFORMANCE)
google::InitGoogleLogging(argv[0]);
google::InitGoogleLogging(argv[0]);
#endif
std::thread* threads = new std::thread[number_of_threads];
std::vector<uint64_t>* threads_result = new std::vector<uint64_t>[number_of_threads];
std::thread* threads = new std::thread[number_of_threads];
std::vector<uint64_t>* threads_result = new std::vector<uint64_t>[number_of_threads];
// kiss: just loop, create threads, store them then join
// could probably do this more elegant with lambdas
for(uint64_t idx = 0; idx < number_of_threads; ++idx)
{
threads_result[idx].reserve(g_iterations);
}
// kiss: just loop, create threads, store them then join
// could probably do this more elegant with lambdas
for (uint64_t idx = 0; idx < number_of_threads; ++idx)
{
threads_result[idx].reserve(g_iterations);
}
auto start_time = std::chrono::high_resolution_clock::now();
for(uint64_t idx = 0; idx < number_of_threads; ++idx)
{
std::ostringstream count;
count << idx+1;
std::string thread_name = title + "_T" + count.str();
std::cout << "Creating thread: " << thread_name << std::endl;
threads[idx] = std::thread(measurePeakDuringLogWrites,thread_name, std::ref(threads_result[idx]));
}
// wait for thread finishing
for(uint64_t idx = 0; idx < number_of_threads; ++idx)
{
threads[idx].join();
}
auto application_end_time = std::chrono::high_resolution_clock::now();
delete [] threads;
auto start_time = std::chrono::high_resolution_clock::now();
for (uint64_t idx = 0; idx < number_of_threads; ++idx)
{
std::ostringstream count;
count << idx + 1;
std::string thread_name = title + "_T" + count.str();
std::cout << "Creating thread: " << thread_name << std::endl;
threads[idx] = std::thread(measurePeakDuringLogWrites, thread_name, std::ref(threads_result[idx]));
}
// wait for thread finishing
for (uint64_t idx = 0; idx < number_of_threads; ++idx)
{
threads[idx].join();
}
auto application_end_time = std::chrono::high_resolution_clock::now();
delete [] threads;
#if defined(G2LOG_PERFORMANCE)
logger_n_handle.worker.reset(); // will flush anything in the queue to file
logger_n_handle.worker.reset(); // will flush anything in the queue to file
#elif defined(GOOGLE_GLOG_PERFORMANCE)
google::ShutdownGoogleLogging();
google::ShutdownGoogleLogging();
#endif
auto worker_end_time = std::chrono::high_resolution_clock::now();
uint64_t application_time_us = std::chrono::duration_cast<microsecond>(application_end_time - start_time).count();
uint64_t total_time_us = std::chrono::duration_cast<microsecond>(worker_end_time - start_time).count();
auto worker_end_time = std::chrono::high_resolution_clock::now();
uint64_t application_time_us = std::chrono::duration_cast<microsecond>(application_end_time - start_time).count();
uint64_t total_time_us = std::chrono::duration_cast<microsecond>(worker_end_time - start_time).count();
oss << "\n" << number_of_threads << "*" << g_iterations << " log entries took: [" << total_time_us / us_to_s << " s] to write to disk" << std::endl;
oss << "[Application(" << number_of_threads << "_threads+overhead time for measurement):\t" << application_time_us/us_to_ms << " ms]" << std::endl;
oss << "[Background thread to finish:\t\t\t\t" << total_time_us/us_to_ms << " ms]" << std::endl;
oss << "\nAverage time per log entry:" << std::endl;
oss << "[Application: " << application_time_us/(number_of_threads*g_iterations) << " us]" << std::endl;
oss << "\n" << number_of_threads << "*" << g_iterations << " log entries took: [" << total_time_us / us_to_s << " s] to write to disk" << std::endl;
oss << "[Application(" << number_of_threads << "_threads+overhead time for measurement):\t" << application_time_us / us_to_ms << " ms]" << std::endl;
oss << "[Background thread to finish:\t\t\t\t" << total_time_us / us_to_ms << " ms]" << std::endl;
oss << "\nAverage time per log entry:" << std::endl;
oss << "[Application: " << application_time_us / (number_of_threads * g_iterations) << " us]" << std::endl;
for(uint64_t idx = 0; idx < number_of_threads; ++idx)
{
std::vector<uint64_t>& t_result = threads_result[idx];
uint64_t worstUs = (*std::max_element(t_result.begin(), t_result.end()));
oss << "[Application t" << idx+1 << " worst took: " << worstUs / uint64_t(1000) << " ms (" << worstUs << " us)] " << std::endl;
}
writeTextToFile(g_measurement_dump,oss.str(), kAppend);
std::cout << "Result can be found at:" << g_measurement_dump << std::endl;
for (uint64_t idx = 0; idx < number_of_threads; ++idx)
{
std::vector<uint64_t> &t_result = threads_result[idx];
uint64_t worstUs = (*std::max_element(t_result.begin(), t_result.end()));
oss << "[Application t" << idx + 1 << " worst took: " << worstUs / uint64_t(1000) << " ms (" << worstUs << " us)] " << std::endl;
}
writeTextToFile(g_measurement_dump, oss.str(), kAppend);
std::cout << "Result can be found at:" << g_measurement_dump << std::endl;
// now split the result in buckets of 10ms each so that it's obvious how the peaks go
std::vector<uint64_t> all_measurements;
all_measurements.reserve(g_iterations * number_of_threads);
for(uint64_t idx = 0; idx < number_of_threads; ++idx)
{
std::vector<uint64_t>& t_result = threads_result[idx];
all_measurements.insert(all_measurements.end(), t_result.begin(), t_result.end());
}
delete [] threads_result; // finally get rid of them
// now split the result in buckets of 10ms each so that it's obvious how the peaks go
std::vector<uint64_t> all_measurements;
all_measurements.reserve(g_iterations * number_of_threads);
for (uint64_t idx = 0; idx < number_of_threads; ++idx)
{
std::vector<uint64_t> &t_result = threads_result[idx];
all_measurements.insert(all_measurements.end(), t_result.begin(), t_result.end());
}
delete [] threads_result; // finally get rid of them
std::sort (all_measurements.begin(), all_measurements.end());
std::map<uint64_t, uint64_t> value_amounts;
std::map<uint64_t, uint64_t> value_amounts_for_0ms_bucket;
for(auto iter = all_measurements.begin(); iter != all_measurements.end(); ++iter)
{
uint64_t value = (*iter)/us_to_ms; // convert to ms
++value_amounts[value]; // asuming uint64_t is default 0 when initialized
if(0 == value) {
++value_amounts_for_0ms_bucket[*iter];
}
}
std::sort (all_measurements.begin(), all_measurements.end());
std::map<uint64_t, uint64_t> value_amounts;
std::map<uint64_t, uint64_t> value_amounts_for_0ms_bucket;
oss.str("");
oss << "Number of values rounded to milliseconds and put to [millisecond bucket] were dumped to file: " << g_measurement_bucket_dump << std::endl;
if(1 == value_amounts.size()) {
oss << "Format: bucket of us inside bucket0 for ms\nFormat:bucket_of_ms, number_of_values_in_bucket\n\n" << std::endl;
oss << "\n";
}
else {
oss << "Format:bucket_of_ms, number_of_values_in_bucket\n\n" << std::endl;
}
std::cout << oss.str() << std::endl;
for (auto iter = all_measurements.begin(); iter != all_measurements.end(); ++iter)
{
uint64_t value = (*iter) / us_to_ms; // convert to ms
++value_amounts[value]; // asuming uint64_t is default 0 when initialized
//
// If all values are for the 0ms bucket then instead show us buckets
//
if(1 == value_amounts.size()) {
oss << "\n\n***** Microsecond bucket measurement for all measurements that went inside the '0 millisecond bucket' ****\n";
for(auto us_bucket: value_amounts_for_0ms_bucket) {
oss << us_bucket.first << "\t" << us_bucket.second << std::endl;
}
oss << "\n\n***** Millisecond bucket measurement ****\n";
}
for(auto ms_bucket: value_amounts)
{
oss << ms_bucket.first << "\t, " << ms_bucket.second << std::endl;
}
writeTextToFile(g_measurement_bucket_dump,oss.str(), kAppend, false);
if (0 == value) {
++value_amounts_for_0ms_bucket[*iter];
}
}
oss.str("");
oss << "Number of values rounded to milliseconds and put to [millisecond bucket] were dumped to file: " << g_measurement_bucket_dump << std::endl;
if (1 == value_amounts.size()) {
oss << "Format: bucket of us inside bucket0 for ms\nFormat:bucket_of_ms, number_of_values_in_bucket\n\n" << std::endl;
oss << "\n";
}
else {
oss << "Format:bucket_of_ms, number_of_values_in_bucket\n\n" << std::endl;
}
std::cout << oss.str() << std::endl;
//
// If all values are for the 0ms bucket then instead show us buckets
//
if (1 == value_amounts.size()) {
oss << "\n\n***** Microsecond bucket measurement for all measurements that went inside the '0 millisecond bucket' ****\n";
for (auto us_bucket : value_amounts_for_0ms_bucket) {
oss << us_bucket.first << "\t" << us_bucket.second << std::endl;
}
oss << "\n\n***** Millisecond bucket measurement ****\n";
}
for (auto ms_bucket : value_amounts)
{
oss << ms_bucket.first << "\t, " << ms_bucket.second << std::endl;
}
writeTextToFile(g_measurement_bucket_dump, oss.str(), kAppend, false);
return 0;
return 0;
}

View File

@ -39,7 +39,11 @@ option (USE_G3LOG_UNIT_TEST
# and this: http://stackoverflow.com/questions/2257464/google-test-and-visual-studio-2010-rc
SET(tests_to_run test_filechange test_io test_configuration test_concept_sink test_sink)
IF (MSVC OR MINGW)
SET(OS_SPECIFIC_TEST test_crashhandler_windows)
ENDIF(MSVC OR MINGW)
SET(tests_to_run test_filechange test_io test_configuration test_concept_sink test_sink ${OS_SPECIFIC_TEST})
SET(helper ${DIR_UNIT_TEST}/testing_helpers.h ${DIR_UNIT_TEST}/testing_helpers.cpp)
include_directories(${DIR_UNIT_TEST})
@ -56,7 +60,7 @@ option (USE_G3LOG_UNIT_TEST
IF( NOT(MSVC))
set_target_properties(${test} PROPERTIES COMPILE_FLAGS "-isystem -pthread ")
ENDIF( NOT(MSVC))
target_link_libraries(${test} g3logger gtest_170_lib ${PLATFORM_LINK_LIBRIES})
target_link_libraries(${test} g3logger gtest_170_lib)
ENDFOREACH(test)
#

View File

@ -0,0 +1,44 @@
/** ==========================================================================
* 2014 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 <gtest/gtest.h>
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
#include "stacktrace_windows.hpp"
#include <windows.h>
TEST(CrashHandler_Windows, ExceptionType) {
EXPECT_EQ(stacktrace::exceptionIdToText(123), "Unknown/123");
EXPECT_EQ(stacktrace::exceptionIdToText(1), "Unknown/1");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_ACCESS_VIOLATION), "EXCEPTION_ACCESS_VIOLATION");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_ARRAY_BOUNDS_EXCEEDED), "EXCEPTION_ARRAY_BOUNDS_EXCEEDED");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_BREAKPOINT),"EXCEPTION_BREAKPOINT");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_DATATYPE_MISALIGNMENT),"EXCEPTION_DATATYPE_MISALIGNMENT");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_DENORMAL_OPERAND),"EXCEPTION_FLT_DENORMAL_OPERAND");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_DIVIDE_BY_ZERO),"EXCEPTION_FLT_DIVIDE_BY_ZERO");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_INEXACT_RESULT),"EXCEPTION_FLT_INEXACT_RESULT");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_INEXACT_RESULT),"EXCEPTION_FLT_INEXACT_RESULT");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_INVALID_OPERATION),"EXCEPTION_FLT_INVALID_OPERATION");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_OVERFLOW),"EXCEPTION_FLT_OVERFLOW");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_STACK_CHECK),"EXCEPTION_FLT_STACK_CHECK");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_UNDERFLOW),"EXCEPTION_FLT_UNDERFLOW");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_ILLEGAL_INSTRUCTION),"EXCEPTION_ILLEGAL_INSTRUCTION");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_IN_PAGE_ERROR),"EXCEPTION_IN_PAGE_ERROR");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_INT_DIVIDE_BY_ZERO),"EXCEPTION_INT_DIVIDE_BY_ZERO");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_INT_OVERFLOW),"EXCEPTION_INT_OVERFLOW");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_INVALID_DISPOSITION),"EXCEPTION_INVALID_DISPOSITION");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_NONCONTINUABLE_EXCEPTION),"EXCEPTION_NONCONTINUABLE_EXCEPTION");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_PRIV_INSTRUCTION),"EXCEPTION_PRIV_INSTRUCTION");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_SINGLE_STEP),"EXCEPTION_SINGLE_STEP");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_STACK_OVERFLOW),"EXCEPTION_STACK_OVERFLOW");
}
#endif // defined WIN32

View File

@ -111,7 +111,7 @@ namespace testing_helpers {
using namespace g2;
g2::initializeLogging(_scope->_currentWorker.get());
clearMockFatal();
internal::changeFatalInitHandlerForUnitTesting(&mockFatalCall);
internal::setFatalExitHandler(&mockFatalCall);
auto filename = _handle->call(&FileSink::fileName);
if (!filename.valid()) ADD_FAILURE();