mirror of
https://github.com/KjellKod/g3log.git
synced 2024-12-12 18:30:25 +01:00
Finally the right sublime AstyleFormat options pointer-align:type and reference-align:type
Fixed bug with dynamic logging levels and DBUG (instead of DEBUG)
This commit is contained in:
parent
fb30aef9e8
commit
c5bb9b96d8
@ -40,7 +40,7 @@ void RaiseSIGABRT() {
|
||||
|
||||
void RaiseSIGFPE() {
|
||||
std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush;
|
||||
LOGF_IF(INFO, false != true, "Exiting %s SIGFPE", "by");
|
||||
LOGF_IF(INFO, false != true, "Exiting %s SIGFPE", "by");
|
||||
raise(SIGFPE);
|
||||
LOG(WARNING) << "Expected to have died by now...";
|
||||
}
|
||||
@ -61,7 +61,7 @@ void RaiseSIGILL() {
|
||||
|
||||
void RAiseSIGTERM() {
|
||||
std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush;
|
||||
LOGF_IF(INFO, false != true, "Exiting %s SIGFPE", "by");
|
||||
LOGF_IF(INFO, false != true, "Exiting %s SIGFPE", "by");
|
||||
raise(SIGTERM);
|
||||
LOG(WARNING) << "Expected to have died by now...";
|
||||
}
|
||||
@ -94,7 +94,7 @@ void OutOfBoundsArrayIndexing() {
|
||||
|
||||
void AccessViolation() {
|
||||
std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush;
|
||||
char *ptr = 0;
|
||||
char* ptr = 0;
|
||||
LOG(INFO) << "Death by access violation is imminent";
|
||||
*ptr = 0;
|
||||
LOG(WARNING) << "Expected to have died by now...";
|
||||
@ -200,7 +200,7 @@ void ChooseFatalExit() {
|
||||
}
|
||||
} // 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());
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -10,20 +10,20 @@
|
||||
#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;
|
||||
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
|
||||
#else
|
||||
typedef int SignalType;
|
||||
#endif
|
||||
|
||||
@ -41,7 +41,7 @@ bool blockForFatalHandling();
|
||||
std::string exitReasonName(const LEVELS& level, g2::SignalType signal_number);
|
||||
|
||||
/** return calling thread's stackdump*/
|
||||
std::string stackdump(const char *dump = nullptr);
|
||||
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
|
||||
|
@ -34,7 +34,7 @@
|
||||
// not all signal handlers need to be re-installed
|
||||
namespace {
|
||||
std::atomic<bool> gBlockForFatal {true};
|
||||
void *g_vector_exception_handler = nullptr;
|
||||
void* g_vector_exception_handler = nullptr;
|
||||
LPTOP_LEVEL_EXCEPTION_FILTER g_previous_unexpected_exception_handler = nullptr;
|
||||
|
||||
// Restore back to default fatal event handling
|
||||
@ -79,7 +79,7 @@ void signalHandler(int signal_number) {
|
||||
|
||||
//
|
||||
// Unhandled exception catching
|
||||
LONG WINAPI exceptionHandling(EXCEPTION_POINTERS *info) {
|
||||
LONG WINAPI exceptionHandling(EXCEPTION_POINTERS* info) {
|
||||
const g2::SignalType exception_code = info->ExceptionRecord->ExceptionCode;
|
||||
//const auto exceptionText = stacktrace::exceptionIdToText(exception_code);
|
||||
/**** START HACK ****/
|
||||
@ -103,7 +103,7 @@ LONG WINAPI exceptionHandling(EXCEPTION_POINTERS *info) {
|
||||
|
||||
//
|
||||
// Unhandled exception catching
|
||||
LONG WINAPI unexpectedExceptionHandling(EXCEPTION_POINTERS *info) {
|
||||
LONG WINAPI unexpectedExceptionHandling(EXCEPTION_POINTERS* info) {
|
||||
ReverseToOriginalFatalHandling();
|
||||
return exceptionHandling(info);
|
||||
}
|
||||
@ -138,7 +138,7 @@ bool blockForFatalHandling() {
|
||||
|
||||
/// Generate stackdump. Or in case a stackdump was pre-generated and non-empty just use that one
|
||||
/// i.e. the latter case is only for Windows and test purposes
|
||||
std::string stackdump(const char *dump) {
|
||||
std::string stackdump(const char* dump) {
|
||||
if (nullptr != dump && !std::string(dump).empty()) {
|
||||
return {dump};
|
||||
}
|
||||
@ -149,7 +149,7 @@ std::string stackdump(const char *dump) {
|
||||
|
||||
|
||||
/// string representation of signal ID or Windows exception id
|
||||
std::string exitReasonName(const LEVELS &level, g2::SignalType fatal_id) {
|
||||
std::string exitReasonName(const LEVELS& level, g2::SignalType fatal_id) {
|
||||
//
|
||||
std::cout << __FUNCTION__ << " exit reason: " << fatal_id << std::endl;
|
||||
if (level == g2::internal::FATAL_EXCEPTION) {
|
||||
@ -178,7 +178,7 @@ std::string exitReasonName(const LEVELS &level, g2::SignalType fatal_id) {
|
||||
// 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) {
|
||||
void exitWithDefaultSignalHandler(const LEVELS& level, g2::SignalType fatal_signal_id) {
|
||||
|
||||
ReverseToOriginalFatalHandling();
|
||||
// For windows exceptions we want to continue the possibility of exception handling
|
||||
|
299
src/g2log.cpp
299
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,157 +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, []() {
|
||||
installCrashHandler(); });
|
||||
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 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)});
|
||||
}
|
||||
|
||||
/** 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
|
||||
/** 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
|
||||
|
||||
|
||||
|
@ -48,7 +48,7 @@ namespace g2 {
|
||||
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
|
||||
@ -56,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
|
||||
@ -14,18 +14,18 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
// Users of Juce or other libraries might have a define DEBUG which clashes with
|
||||
// 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
|
||||
#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>
|
||||
|
||||
@ -36,11 +36,13 @@ struct LEVELS {
|
||||
// "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); }
|
||||
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;
|
||||
@ -48,32 +50,40 @@ struct LEVELS {
|
||||
|
||||
|
||||
|
||||
namespace g2 {
|
||||
static const int kDebugVaulue = 0;
|
||||
}
|
||||
|
||||
static const int kDebugVaulue = 0;
|
||||
#if (defined(CHANGE_G3LOG_DEBUG_TO_DBUG))
|
||||
const LEVELS DBUG{kDebugVaulue, {"DEBUG"}},
|
||||
const LEVELS DBUG {
|
||||
g2::kDebugVaulue, {"DEBUG"}
|
||||
},
|
||||
#else
|
||||
const LEVELS DEBUG{kDebugVaulue, {"DEBUG"}},
|
||||
const LEVELS DEBUG {
|
||||
g2::kDebugVaulue, {"DEBUG"}
|
||||
},
|
||||
#endif
|
||||
INFO{kDebugVaulue + 1, {"INFO"}},
|
||||
WARNING{INFO.value + 1, {"WARNING"}},
|
||||
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"}};
|
||||
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);
|
||||
}
|
||||
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
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
#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?
|
||||
@ -35,7 +35,7 @@ LogCapture::~LogCapture() {
|
||||
|
||||
|
||||
/// 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::LogCapture(const LEVELS &level, g2::SignalType fatal_signal, const char* dump)
|
||||
: LogCapture("", 0, "", level, "", fatal_signal, dump) {
|
||||
}
|
||||
|
||||
@ -45,8 +45,8 @@ LogCapture::LogCapture(const LEVELS &level, g2::SignalType fatal_signal, const c
|
||||
* @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)
|
||||
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)) {
|
||||
@ -61,7 +61,7 @@ LogCapture::LogCapture(const char *file, const int line, const char *function, c
|
||||
* 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, ...) __attribute__((format(printf, 2, 3))) {
|
||||
void LogCapture::capturef(const char* printf_like_message, ...) __attribute__((format(printf, 2, 3))) {
|
||||
static const int kMaxMessageSize = 2048;
|
||||
static const std::string kTruncatedWarningText = "[...truncated...]";
|
||||
char finished_message[kMaxMessageSize];
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include <csignal>
|
||||
#include "g2loglevels.hpp"
|
||||
#include "g2log.hpp"
|
||||
#include "crashhandler.hpp"
|
||||
#include "crashhandler.hpp"
|
||||
|
||||
/**
|
||||
* Simple struct for capturing log/fatal entries. At destruction the captured message is
|
||||
@ -23,7 +23,7 @@
|
||||
*/
|
||||
struct LogCapture {
|
||||
/// Called from crash handler when a fatal signal has occurred (SIGSEGV etc)
|
||||
LogCapture(const LEVELS &level, g2::SignalType fatal_signal, const char *dump = nullptr);
|
||||
LogCapture(const LEVELS& level, g2::SignalType fatal_signal, const char* dump = nullptr);
|
||||
|
||||
|
||||
/**
|
||||
@ -32,8 +32,8 @@ struct LogCapture {
|
||||
* @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 = "", g2::SignalType fatal_signal = SIGABRT, const char *dump = nullptr);
|
||||
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.
|
||||
@ -49,11 +49,11 @@ struct LogCapture {
|
||||
#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))); // 2,3 ref: http://www.codemaestro.com/reviews/18
|
||||
void capturef(const char* printf_like_message, ...) __attribute__((format(printf, 2, 3))); // 2,3 ref: http://www.codemaestro.com/reviews/18
|
||||
|
||||
|
||||
|
||||
/// prettifying API for this completely open struct
|
||||
std::ostringstream &stream() {
|
||||
std::ostringstream& stream() {
|
||||
return _stream;
|
||||
}
|
||||
|
||||
@ -61,11 +61,11 @@ struct LogCapture {
|
||||
|
||||
std::ostringstream _stream;
|
||||
std::string _stack_trace;
|
||||
const char *_file;
|
||||
const char* _file;
|
||||
const int _line;
|
||||
const char *_function;
|
||||
const LEVELS &_level;
|
||||
const char *_expression;
|
||||
const char* _function;
|
||||
const LEVELS& _level;
|
||||
const char* _expression;
|
||||
const g2::SignalType _fatal_signal;
|
||||
|
||||
};
|
||||
|
@ -60,7 +60,7 @@ const std::map<g2::SignalType, std::string> kExceptionsAsText = {
|
||||
|
||||
// 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) {
|
||||
void captureStackTrace(CONTEXT* context, std::vector<uint64_t> &frame_pointers) {
|
||||
DWORD machine_type = 0;
|
||||
STACKFRAME64 frame = {};
|
||||
frame.AddrPC.Mode = AddrModeFlat;
|
||||
@ -105,7 +105,7 @@ std::string getSymbolInformation(size_t index, std::vector<uint64_t> &frame_poin
|
||||
DWORD64 displacement64;
|
||||
DWORD displacement;
|
||||
char symbol_buffer[sizeof(SYMBOL_INFO) + 256];
|
||||
SYMBOL_INFO *symbol = reinterpret_cast<SYMBOL_INFO *>(symbol_buffer);
|
||||
SYMBOL_INFO* symbol = reinterpret_cast<SYMBOL_INFO*>(symbol_buffer);
|
||||
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
symbol->MaxNameLen = MAX_SYM_NAME;
|
||||
|
||||
@ -163,14 +163,14 @@ std::string stackdump() {
|
||||
}
|
||||
|
||||
/// helper function: retrieve stackdump, starting from an exception pointer
|
||||
std::string stackdump(EXCEPTION_POINTERS *info) {
|
||||
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) {
|
||||
std::string stackdump(CONTEXT* context) {
|
||||
stack_trace sttrace(context); // if there is a windows exception then call it like THIS
|
||||
auto crashreport = sttrace.to_string();
|
||||
return crashreport;
|
||||
|
@ -13,127 +13,127 @@
|
||||
// temporary
|
||||
#include <cassert>
|
||||
|
||||
class sym_handler
|
||||
{
|
||||
public:
|
||||
static sym_handler& get_instance()
|
||||
{
|
||||
static sym_handler instance;
|
||||
return instance;
|
||||
}
|
||||
class sym_handler
|
||||
{
|
||||
public:
|
||||
static sym_handler &get_instance()
|
||||
{
|
||||
static sym_handler instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
std::string get_symbol_info(DWORD64 addr)
|
||||
{
|
||||
std::string get_symbol_info(DWORD64 addr)
|
||||
{
|
||||
|
||||
std::stringstream ss;
|
||||
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 = 255;
|
||||
std::stringstream ss;
|
||||
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 = 255;
|
||||
|
||||
IMAGEHLP_LINE64 line;
|
||||
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
||||
IMAGEHLP_LINE64 line;
|
||||
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
||||
|
||||
// ss << addr;
|
||||
if (m_initialized)
|
||||
{
|
||||
if (SymFromAddr(GetCurrentProcess(),
|
||||
addr,
|
||||
&displacement64,
|
||||
symbol))
|
||||
{
|
||||
ss << " " << std::string(symbol->Name, symbol->NameLen) << std::endl;
|
||||
if (SymGetLineFromAddr64(GetCurrentProcess(),
|
||||
addr,
|
||||
&displacement,
|
||||
&line))
|
||||
{
|
||||
ss << "File Name:" << line.FileName << " line: " << line.LineNumber;
|
||||
}
|
||||
// ss << addr;
|
||||
if (m_initialized)
|
||||
{
|
||||
if (SymFromAddr(GetCurrentProcess(),
|
||||
addr,
|
||||
&displacement64,
|
||||
symbol))
|
||||
{
|
||||
ss << " " << std::string(symbol->Name, symbol->NameLen) << std::endl;
|
||||
if (SymGetLineFromAddr64(GetCurrentProcess(),
|
||||
addr,
|
||||
&displacement,
|
||||
&line))
|
||||
{
|
||||
ss << "File Name:" << line.FileName << " line: " << line.LineNumber;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
DWORD err = GetLastError();
|
||||
}
|
||||
}
|
||||
else{
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
DWORD err = GetLastError();
|
||||
void capture_stack_trace(CONTEXT* context, DWORD64* frame_ptrs, size_t count)
|
||||
{
|
||||
if (m_initialized)
|
||||
{
|
||||
|
||||
assert(context);
|
||||
//CONTEXT current_context;
|
||||
//// In the case of a windows exception then the context
|
||||
//// wil be pre-set (ref: windows_exception_handler)
|
||||
//// ACTUALLY ... Why can't the signal handler call
|
||||
//// RtlCaptureContext.
|
||||
//if (!context)
|
||||
//{
|
||||
// RtlCaptureContext(¤t_context);
|
||||
// context = ¤t_context;
|
||||
//}
|
||||
|
||||
DWORD machine_type;
|
||||
STACKFRAME64 frame;
|
||||
ZeroMemory(&frame, sizeof(frame));
|
||||
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
|
||||
for (size_t i = 0; i < count; i++)
|
||||
{
|
||||
if (StackWalk64(machine_type,
|
||||
GetCurrentProcess(),
|
||||
GetCurrentThread(),
|
||||
&frame,
|
||||
context,
|
||||
NULL,
|
||||
SymFunctionTableAccess64,
|
||||
SymGetModuleBase64,
|
||||
NULL))
|
||||
{
|
||||
frame_ptrs[i] = frame.AddrPC.Offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
}
|
||||
|
||||
void capture_stack_trace(CONTEXT* context, DWORD64* frame_ptrs, size_t count)
|
||||
{
|
||||
if (m_initialized)
|
||||
{
|
||||
private:
|
||||
sym_handler()
|
||||
{
|
||||
m_initialized = SymInitialize(GetCurrentProcess(), NULL, TRUE) == TRUE;
|
||||
}
|
||||
|
||||
assert(context);
|
||||
//CONTEXT current_context;
|
||||
//// In the case of a windows exception then the context
|
||||
//// wil be pre-set (ref: windows_exception_handler)
|
||||
//// ACTUALLY ... Why can't the signal handler call
|
||||
//// RtlCaptureContext.
|
||||
//if (!context)
|
||||
//{
|
||||
// RtlCaptureContext(¤t_context);
|
||||
// context = ¤t_context;
|
||||
//}
|
||||
~sym_handler()
|
||||
{
|
||||
if (m_initialized)
|
||||
{
|
||||
SymCleanup(GetCurrentProcess());
|
||||
m_initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
DWORD machine_type;
|
||||
STACKFRAME64 frame;
|
||||
ZeroMemory(&frame, sizeof(frame));
|
||||
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
|
||||
for (size_t i = 0; i < count; i++)
|
||||
{
|
||||
if (StackWalk64(machine_type,
|
||||
GetCurrentProcess(),
|
||||
GetCurrentThread(),
|
||||
&frame,
|
||||
context,
|
||||
NULL,
|
||||
SymFunctionTableAccess64,
|
||||
SymGetModuleBase64,
|
||||
NULL))
|
||||
{
|
||||
frame_ptrs[i] = frame.AddrPC.Offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
sym_handler()
|
||||
{
|
||||
m_initialized = SymInitialize(GetCurrentProcess(), NULL, TRUE) == TRUE;
|
||||
}
|
||||
|
||||
~sym_handler()
|
||||
{
|
||||
if (m_initialized)
|
||||
{
|
||||
SymCleanup(GetCurrentProcess());
|
||||
m_initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool m_initialized;
|
||||
};
|
||||
bool m_initialized;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -141,38 +141,38 @@ private:
|
||||
|
||||
|
||||
|
||||
class stack_trace
|
||||
{
|
||||
public:
|
||||
stack_trace(CONTEXT* context)
|
||||
{
|
||||
ZeroMemory(m_frame_ptrs, sizeof(m_frame_ptrs));
|
||||
sym_handler::get_instance().capture_stack_trace(context,
|
||||
m_frame_ptrs,
|
||||
max_frame_ptrs);
|
||||
class stack_trace
|
||||
{
|
||||
public:
|
||||
stack_trace(CONTEXT* context)
|
||||
{
|
||||
ZeroMemory(m_frame_ptrs, sizeof(m_frame_ptrs));
|
||||
sym_handler::get_instance().capture_stack_trace(context,
|
||||
m_frame_ptrs,
|
||||
max_frame_ptrs);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
for (size_t i = 0;
|
||||
i < max_frame_ptrs && m_frame_ptrs[i];
|
||||
++i)
|
||||
{
|
||||
ss << sym_handler::get_instance().get_symbol_info(m_frame_ptrs[i]) << "\n";
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
std::string to_string() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
for (size_t i = 0;
|
||||
i < max_frame_ptrs && m_frame_ptrs[i];
|
||||
++i)
|
||||
{
|
||||
ss << sym_handler::get_instance().get_symbol_info(m_frame_ptrs[i]) << "\n";
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
private:
|
||||
static const size_t max_frame_ptrs = 16;
|
||||
DWORD64 m_frame_ptrs[max_frame_ptrs];
|
||||
};
|
||||
private:
|
||||
static const size_t max_frame_ptrs = 16;
|
||||
DWORD64 m_frame_ptrs[max_frame_ptrs];
|
||||
};
|
||||
|
||||
|
||||
inline std::string to_string(const stack_trace& trace)
|
||||
{
|
||||
return trace.to_string();
|
||||
}
|
||||
inline std::string to_string(const stack_trace &trace)
|
||||
{
|
||||
return trace.to_string();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user