mirror of
https://github.com/KjellKod/g3log.git
synced 2024-12-13 10:42:56 +01:00
What a mess to merge from Github/KjellKod/g3log to BitBucket/KjellKod/g3log
This commit is contained in:
parent
1f844f5ea1
commit
938fc55971
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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
32
Options.cmake
Normal 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)
|
||||
|
||||
|
@ -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)*
|
||||
|
@ -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)
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
215
src/crashhandler_windows.cpp
Normal file
215
src/crashhandler_windows.cpp
Normal 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
|
300
src/g2log.cpp
300
src/g2log.cpp
@ -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
|
||||
|
||||
|
||||
|
@ -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__)
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
218
src/stacktrace_windows.cpp
Normal 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(¤t_context, 0, sizeof(CONTEXT));
|
||||
RtlCaptureContext(¤t_context);
|
||||
return stackdump(¤t_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
|
||||
|
||||
|
||||
|
37
src/stacktrace_windows.hpp
Normal file
37
src/stacktrace_windows.hpp
Normal 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
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
#
|
||||
|
44
test_unit/test_crashhandler_windows.cpp
Normal file
44
test_unit/test_crashhandler_windows.cpp
Normal 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
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user