From 39dcf20a89740cc8982dc01cd1a5948533b9e05e Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Wed, 20 Nov 2013 21:55:14 +0100 Subject: [PATCH] Implement support for stack traces for STL exceptions. Fixes #5114 --- components/checker/checkercomponent.cpp | 4 +- components/cluster/clusterlistener.cpp | 5 +-- components/cluster/endpoint.cpp | 5 ++- components/cluster/jsonrpc.cpp | 1 - components/compat/externalcommandlistener.cpp | 3 +- components/compat/statusdatawriter.cpp | 1 - .../db_ido_mysql/idomysqlconnection.cpp | 3 +- .../db_ido_pgsql/idopgsqlconnection.cpp | 3 +- components/livestatus/listener.cpp | 3 +- components/livestatus/query.cpp | 10 ++--- .../notification/notificationcomponent.cpp | 4 +- components/perfdata/graphitewriter.cpp | 4 +- lib/base/application.cpp | 14 +------ lib/base/exception.cpp | 29 +++++++++++--- lib/base/exception.h | 39 ++++++++++++------- lib/base/threadpool.cpp | 4 +- lib/base/workqueue.cpp | 1 - lib/config/config_parser.yy | 4 +- lib/icinga/externalcommandprocessor.cpp | 4 +- lib/icinga/notification.cpp | 4 +- lib/icinga/service-check.cpp | 4 +- lib/icinga/service-notification.cpp | 4 +- lib/python/pythonlanguage.cpp | 3 +- 23 files changed, 80 insertions(+), 76 deletions(-) diff --git a/components/checker/checkercomponent.cpp b/components/checker/checkercomponent.cpp index 63849166b..482c1e17e 100644 --- a/components/checker/checkercomponent.cpp +++ b/components/checker/checkercomponent.cpp @@ -24,7 +24,7 @@ #include "base/objectlock.h" #include "base/utility.h" #include "base/logger_fwd.h" -#include +#include "base/exception.h" #include using namespace icinga; @@ -151,7 +151,7 @@ void CheckerComponent::ExecuteCheckHelper(const Service::Ptr& service) try { service->ExecuteCheck(); } catch (const std::exception& ex) { - Log(LogCritical, "checker", "Exception occured while checking service '" + service->GetName() + "': " + boost::diagnostic_information(ex)); + Log(LogCritical, "checker", "Exception occured while checking service '" + service->GetName() + "': " + DiagnosticInformation(ex)); } { diff --git a/components/cluster/clusterlistener.cpp b/components/cluster/clusterlistener.cpp index dacfda585..e61c550f7 100644 --- a/components/cluster/clusterlistener.cpp +++ b/components/cluster/clusterlistener.cpp @@ -30,7 +30,6 @@ #include "base/application.h" #include "base/convert.h" #include -#include using namespace icinga; @@ -646,7 +645,7 @@ void ClusterListener::ClusterTimerHandler(void) } catch (std::exception& ex) { std::ostringstream msgbuf; msgbuf << "Exception occured while reconnecting to endpoint '" - << endpoint->GetName() << "': " << boost::diagnostic_information(ex); + << endpoint->GetName() << "': " << DiagnosticInformation(ex); Log(LogWarning, "cluster", msgbuf.str()); } } @@ -959,7 +958,7 @@ void ClusterListener::AsyncMessageHandler(const Endpoint::Ptr& sender, const Dic void ClusterListener::MessageExceptionHandler(boost::exception_ptr exp) { - Log(LogCritical, "cluster", "Exception while processing cluster message: " + boost::diagnostic_information(exp)); + Log(LogCritical, "cluster", "Exception while processing cluster message: " + DiagnosticInformation(exp)); } void ClusterListener::MessageHandler(const Endpoint::Ptr& sender, const Dictionary::Ptr& message) diff --git a/components/cluster/endpoint.cpp b/components/cluster/endpoint.cpp index 396ecd751..65cc1e15e 100644 --- a/components/cluster/endpoint.cpp +++ b/components/cluster/endpoint.cpp @@ -24,6 +24,7 @@ #include "base/objectlock.h" #include "base/utility.h" #include "base/logger_fwd.h" +#include "base/exception.h" #include "config/configitembuilder.h" using namespace icinga; @@ -74,7 +75,7 @@ void Endpoint::SendMessage(const Dictionary::Ptr& message) JsonRpc::SendMessage(client, message); } catch (const std::exception& ex) { std::ostringstream msgbuf; - msgbuf << "Error while sending JSON-RPC message for endpoint '" << GetName() << "': " << boost::diagnostic_information(ex); + msgbuf << "Error while sending JSON-RPC message for endpoint '" << GetName() << "': " << DiagnosticInformation(ex); Log(LogWarning, "cluster", msgbuf.str()); m_Client.reset(); @@ -91,7 +92,7 @@ void Endpoint::MessageThreadProc(const Stream::Ptr& stream) try { message = JsonRpc::ReadMessage(stream); } catch (const std::exception& ex) { - Log(LogWarning, "cluster", "Error while reading JSON-RPC message for endpoint '" + GetName() + "': " + boost::diagnostic_information(ex)); + Log(LogWarning, "cluster", "Error while reading JSON-RPC message for endpoint '" + GetName() + "': " + DiagnosticInformation(ex)); m_Client.reset(); diff --git a/components/cluster/jsonrpc.cpp b/components/cluster/jsonrpc.cpp index 4ad5ab5f5..de93b242b 100644 --- a/components/cluster/jsonrpc.cpp +++ b/components/cluster/jsonrpc.cpp @@ -22,7 +22,6 @@ #include "base/objectlock.h" #include "base/logger_fwd.h" #include "base/serializer.h" -#include #include using namespace icinga; diff --git a/components/compat/externalcommandlistener.cpp b/components/compat/externalcommandlistener.cpp index 599010588..536d80fc3 100644 --- a/components/compat/externalcommandlistener.cpp +++ b/components/compat/externalcommandlistener.cpp @@ -23,7 +23,6 @@ #include "base/logger_fwd.h" #include "base/exception.h" #include "base/application.h" -#include using namespace icinga; @@ -120,7 +119,7 @@ void ExternalCommandListener::CommandPipeThread(const String& commandPath) ExternalCommandProcessor::Execute(command); } catch (const std::exception& ex) { std::ostringstream msgbuf; - msgbuf << "External command failed: " << boost::diagnostic_information(ex); + msgbuf << "External command failed: " << DiagnosticInformation(ex); Log(LogWarning, "compat", msgbuf.str()); } } diff --git a/components/compat/statusdatawriter.cpp b/components/compat/statusdatawriter.cpp index da05da571..4601e59d3 100644 --- a/components/compat/statusdatawriter.cpp +++ b/components/compat/statusdatawriter.cpp @@ -35,7 +35,6 @@ #include "base/application.h" #include #include -#include #include #include diff --git a/components/db_ido_mysql/idomysqlconnection.cpp b/components/db_ido_mysql/idomysqlconnection.cpp index f42b9fa68..f872047b9 100644 --- a/components/db_ido_mysql/idomysqlconnection.cpp +++ b/components/db_ido_mysql/idomysqlconnection.cpp @@ -27,7 +27,6 @@ #include "db_ido/dbtype.h" #include "db_ido/dbvalue.h" #include "db_ido_mysql/idomysqlconnection.h" -#include #include #include @@ -67,7 +66,7 @@ void IdoMysqlConnection::Stop(void) void IdoMysqlConnection::ExceptionHandler(boost::exception_ptr exp) { - Log(LogCritical, "db_ido_mysql", "Exception during database operation: " + boost::diagnostic_information(exp)); + Log(LogCritical, "db_ido_mysql", "Exception during database operation: " + DiagnosticInformation(exp)); boost::mutex::scoped_lock lock(m_ConnectionMutex); diff --git a/components/db_ido_pgsql/idopgsqlconnection.cpp b/components/db_ido_pgsql/idopgsqlconnection.cpp index c731bffb7..b86ccf210 100644 --- a/components/db_ido_pgsql/idopgsqlconnection.cpp +++ b/components/db_ido_pgsql/idopgsqlconnection.cpp @@ -27,7 +27,6 @@ #include "db_ido/dbtype.h" #include "db_ido/dbvalue.h" #include "db_ido_pgsql/idopgsqlconnection.h" -#include #include #include @@ -67,7 +66,7 @@ void IdoPgsqlConnection::Stop(void) void IdoPgsqlConnection::ExceptionHandler(boost::exception_ptr exp) { - Log(LogCritical, "db_ido_pgsql", "Exception during database operation: " + boost::diagnostic_information(exp)); + Log(LogCritical, "db_ido_pgsql", "Exception during database operation: " + DiagnosticInformation(exp)); boost::mutex::scoped_lock lock(m_ConnectionMutex); diff --git a/components/livestatus/listener.cpp b/components/livestatus/listener.cpp index 32395de62..c23af4ecf 100644 --- a/components/livestatus/listener.cpp +++ b/components/livestatus/listener.cpp @@ -29,7 +29,6 @@ #include "base/application.h" #include "base/scriptfunction.h" #include "base/convert.h" -#include using namespace icinga; @@ -139,7 +138,7 @@ void LivestatusListener::ClientThreadProc(const Socket::Ptr& client) } catch (const std::exception& ex) { std::ostringstream info; info << "Exception thrown while running livestatus query: " << std::endl - << boost::diagnostic_information(ex); + << DiagnosticInformation(ex); Log(LogCritical, "livestatus", info.str()); return; } diff --git a/components/livestatus/query.cpp b/components/livestatus/query.cpp index cb394b36c..91bb244cd 100644 --- a/components/livestatus/query.cpp +++ b/components/livestatus/query.cpp @@ -498,7 +498,7 @@ void Query::SendResponse(const Stream::Ptr& stream, int code, const String& data } catch (const std::exception& ex) { std::ostringstream info; info << "Exception thrown while writing to the livestatus socket: " << std::endl - << boost::diagnostic_information(ex); + << DiagnosticInformation(ex); Log(LogCritical, "livestatus", info.str()); } } @@ -518,7 +518,7 @@ void Query::PrintFixed16(const Stream::Ptr& stream, int code, const String& data } catch (const std::exception& ex) { std::ostringstream info; info << "Exception thrown while writing to the livestatus socket: " << std::endl - << boost::diagnostic_information(ex); + << DiagnosticInformation(ex); Log(LogCritical, "livestatus", info.str()); } } @@ -537,11 +537,7 @@ bool Query::Execute(const Stream::Ptr& stream) else BOOST_THROW_EXCEPTION(std::runtime_error("Invalid livestatus query verb.")); } catch (const std::exception& ex) { - StackTrace *st = Exception::GetLastStackTrace(); - std::ostringstream info; - st->Print(info); - Log(LogDebug, "livestatus", info.str()); - SendResponse(stream, LivestatusErrorQuery, boost::diagnostic_information(ex)); + SendResponse(stream, LivestatusErrorQuery, DiagnosticInformation(ex)); } if (!m_KeepAlive) { diff --git a/components/notification/notificationcomponent.cpp b/components/notification/notificationcomponent.cpp index f92d690f5..ab047b7dc 100644 --- a/components/notification/notificationcomponent.cpp +++ b/components/notification/notificationcomponent.cpp @@ -23,7 +23,7 @@ #include "base/objectlock.h" #include "base/logger_fwd.h" #include "base/utility.h" -#include +#include "base/exception.h" #include using namespace icinga; @@ -90,7 +90,7 @@ void NotificationComponent::NotificationTimerHandler(void) } catch (const std::exception& ex) { std::ostringstream msgbuf; msgbuf << "Exception occured during notification for service '" - << GetName() << "': " << boost::diagnostic_information(ex); + << GetName() << "': " << DiagnosticInformation(ex); String message = msgbuf.str(); Log(LogWarning, "icinga", message); diff --git a/components/perfdata/graphitewriter.cpp b/components/perfdata/graphitewriter.cpp index 949ab7da8..b76845a70 100644 --- a/components/perfdata/graphitewriter.cpp +++ b/components/perfdata/graphitewriter.cpp @@ -33,12 +33,12 @@ #include "base/stream.h" #include "base/networkstream.h" #include "base/bufferedstream.h" +#include "base/exception.h" #include #include #include #include #include -#include using namespace icinga; @@ -143,7 +143,7 @@ void GraphiteWriter::SendMetric(const String& prefix, const String& name, double } catch (const std::exception& ex) { std::ostringstream msgbuf; msgbuf << "Exception thrown while writing to the Graphite socket: " << std::endl - << boost::diagnostic_information(ex); + << DiagnosticInformation(ex); Log(LogCritical, "base", msgbuf.str()); diff --git a/lib/base/application.cpp b/lib/base/application.cpp index 690240e3d..e69398a14 100644 --- a/lib/base/application.cpp +++ b/lib/base/application.cpp @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include @@ -399,23 +398,12 @@ void Application::ExceptionHandler(void) sigaction(SIGABRT, &sa, NULL); #endif /* _WIN32 */ - bool has_trace = false; - try { throw; } catch (const std::exception& ex) { std::cerr << std::endl - << boost::diagnostic_information(ex) + << DiagnosticInformation(ex) << std::endl; - - has_trace = (boost::get_error_info(ex) != NULL); - } catch (...) { - std::cerr << "Exception of unknown type." << std::endl; - } - - if (!has_trace) { - StackTrace trace; - trace.Print(std::cerr, 1); } DisplayBugMessage(); diff --git a/lib/base/exception.cpp b/lib/base/exception.cpp index 3b89a735d..d5508292a 100644 --- a/lib/base/exception.cpp +++ b/lib/base/exception.cpp @@ -21,7 +21,7 @@ using namespace icinga; -boost::thread_specific_ptr Exception::m_LastStackTrace; +static boost::thread_specific_ptr l_LastStackTrace; #ifndef _WIN32 extern "C" @@ -44,7 +44,7 @@ void __cxa_throw(void *obj, void *pvtinfo, void (*dest)(void *)) #endif /* __APPLE__ */ StackTrace trace; - Exception::SetLastStackTrace(trace); + SetLastExceptionStack(trace); #ifndef __APPLE__ /* Check if thrown_ptr inherits from boost::exception. */ @@ -59,13 +59,30 @@ void __cxa_throw(void *obj, void *pvtinfo, void (*dest)(void *)) } #endif /* _WIN32 */ -StackTrace *Exception::GetLastStackTrace(void) +StackTrace *icinga::GetLastExceptionStack(void) { - return m_LastStackTrace.get(); + return l_LastStackTrace.get(); } -void Exception::SetLastStackTrace(const StackTrace& trace) +void icinga::SetLastExceptionStack(const StackTrace& trace) { - m_LastStackTrace.reset(new StackTrace(trace)); + l_LastStackTrace.reset(new StackTrace(trace)); +} + +String icinga::DiagnosticInformation(boost::exception_ptr eptr) +{ + StackTrace *pt = GetLastExceptionStack(); + StackTrace trace; + + if (pt) + trace = *pt; + + try { + boost::rethrow_exception(eptr); + } catch (const std::exception& ex) { + return DiagnosticInformation(ex, pt ? &trace : NULL); + } + + return boost::diagnostic_information(eptr); } diff --git a/lib/base/exception.h b/lib/base/exception.h index 365f17549..88bd56749 100644 --- a/lib/base/exception.h +++ b/lib/base/exception.h @@ -28,6 +28,8 @@ #include #include #include +#include +#include #ifdef _WIN32 # include @@ -36,23 +38,32 @@ namespace icinga { -/** - * Base class for all exceptions. - * - * @ingroup base - */ -class I2_BASE_API Exception -{ -public: - static StackTrace *GetLastStackTrace(void); - static void SetLastStackTrace(const StackTrace& trace); - -private: - static boost::thread_specific_ptr m_LastStackTrace; -}; +I2_BASE_API StackTrace *GetLastExceptionStack(void); +I2_BASE_API void SetLastExceptionStack(const StackTrace& trace); typedef boost::error_info StackTraceErrorInfo; +template +String DiagnosticInformation(const T& ex, StackTrace *trace = NULL) +{ + std::ostringstream result; + + result << boost::diagnostic_information(ex); + + if (boost::get_error_info(ex) == NULL) { + result << std::endl; + + if (trace) + result << *trace; + else + result << *GetLastExceptionStack(); + } + + return result.str(); +} + +I2_BASE_API String DiagnosticInformation(boost::exception_ptr eptr); + class I2_BASE_API posix_error : virtual public std::exception, virtual public boost::exception { }; #ifdef _WIN32 diff --git a/lib/base/threadpool.cpp b/lib/base/threadpool.cpp index 91facda70..5fd3c341e 100644 --- a/lib/base/threadpool.cpp +++ b/lib/base/threadpool.cpp @@ -24,10 +24,10 @@ #include "base/utility.h" #include "base/scriptvariable.h" #include "base/application.h" +#include "base/exception.h" #include #include #include -#include #include using namespace icinga; @@ -124,7 +124,7 @@ void ThreadPool::QueueThreadProc(int tid) } catch (const std::exception& ex) { std::ostringstream msgbuf; msgbuf << "Exception thrown in event handler: " << std::endl - << boost::diagnostic_information(ex); + << DiagnosticInformation(ex); Log(LogCritical, "base", msgbuf.str()); } catch (...) { diff --git a/lib/base/workqueue.cpp b/lib/base/workqueue.cpp index c98eed454..3d08f9dc9 100644 --- a/lib/base/workqueue.cpp +++ b/lib/base/workqueue.cpp @@ -22,7 +22,6 @@ #include "base/debug.h" #include "base/logger_fwd.h" #include -#include using namespace icinga; diff --git a/lib/config/config_parser.yy b/lib/config/config_parser.yy index 3d0b25ec1..ced75b6a0 100644 --- a/lib/config/config_parser.yy +++ b/lib/config/config_parser.yy @@ -31,9 +31,9 @@ #include "base/utility.h" #include "base/array.h" #include "base/scriptvariable.h" +#include "base/exception.h" #include #include -#include #include #define YYLTYPE icinga::DebugInfo @@ -140,7 +140,7 @@ void ConfigCompiler::Compile(void) try { yyparse(this); } catch (const std::exception& ex) { - ConfigCompilerContext::GetInstance()->AddMessage(true, boost::diagnostic_information(ex)); + ConfigCompilerContext::GetInstance()->AddMessage(true, DiagnosticInformation(ex)); } } diff --git a/lib/icinga/externalcommandprocessor.cpp b/lib/icinga/externalcommandprocessor.cpp index dfd22d773..32efedd7a 100644 --- a/lib/icinga/externalcommandprocessor.cpp +++ b/lib/icinga/externalcommandprocessor.cpp @@ -30,10 +30,10 @@ #include "base/objectlock.h" #include "base/application.h" #include "base/utility.h" +#include "base/exception.h" #include #include #include -#include #include using namespace icinga; @@ -982,7 +982,7 @@ void ExternalCommandProcessor::ProcessFile(double, const std::vector& ar Execute(line); } catch (const std::exception& ex) { std::ostringstream msgbuf; - msgbuf << "External command failed: " << boost::diagnostic_information(ex); + msgbuf << "External command failed: " << DiagnosticInformation(ex); Log(LogWarning, "icinga", msgbuf.str()); } } diff --git a/lib/icinga/notification.cpp b/lib/icinga/notification.cpp index f22efb822..3705ffd06 100644 --- a/lib/icinga/notification.cpp +++ b/lib/icinga/notification.cpp @@ -26,9 +26,9 @@ #include "base/logger_fwd.h" #include "base/utility.h" #include "base/convert.h" +#include "base/exception.h" #include #include -#include using namespace icinga; @@ -296,7 +296,7 @@ void Notification::ExecuteNotificationHelper(NotificationType type, const User:: } catch (const std::exception& ex) { std::ostringstream msgbuf; msgbuf << "Exception occured during notification for service '" - << GetService()->GetName() << "': " << boost::diagnostic_information(ex); + << GetService()->GetName() << "': " << DiagnosticInformation(ex); Log(LogWarning, "icinga", msgbuf.str()); } } diff --git a/lib/icinga/service-check.cpp b/lib/icinga/service-check.cpp index 972bb598b..a43381a7d 100644 --- a/lib/icinga/service-check.cpp +++ b/lib/icinga/service-check.cpp @@ -26,8 +26,8 @@ #include "base/logger_fwd.h" #include "base/convert.h" #include "base/utility.h" +#include "base/exception.h" #include -#include #include using namespace icinga; @@ -478,7 +478,7 @@ void Service::ExecuteCheck(void) } catch (const std::exception& ex) { std::ostringstream msgbuf; msgbuf << "Exception occured during check for service '" - << GetName() << "': " << boost::diagnostic_information(ex); + << GetName() << "': " << DiagnosticInformation(ex); String message = msgbuf.str(); Log(LogWarning, "icinga", message); diff --git a/lib/icinga/service-notification.cpp b/lib/icinga/service-notification.cpp index cb15c06b3..a1f8e3a96 100644 --- a/lib/icinga/service-notification.cpp +++ b/lib/icinga/service-notification.cpp @@ -24,10 +24,10 @@ #include "base/logger_fwd.h" #include "base/timer.h" #include "base/utility.h" +#include "base/exception.h" #include "config/configitembuilder.h" #include #include -#include using namespace icinga; @@ -68,7 +68,7 @@ void Service::SendNotifications(NotificationType type, const CheckResult::Ptr& c } catch (const std::exception& ex) { std::ostringstream msgbuf; msgbuf << "Exception occured during notification for service '" - << GetName() << "': " << boost::diagnostic_information(ex); + << GetName() << "': " << DiagnosticInformation(ex); String message = msgbuf.str(); Log(LogWarning, "icinga", message); diff --git a/lib/python/pythonlanguage.cpp b/lib/python/pythonlanguage.cpp index d6cedf5e5..b4844c066 100644 --- a/lib/python/pythonlanguage.cpp +++ b/lib/python/pythonlanguage.cpp @@ -26,7 +26,6 @@ #include "base/array.h" #include #include -#include using namespace icinga; @@ -330,7 +329,7 @@ PyObject *PythonLanguage::PyCallNativeFunction(PyObject *self, PyObject *args) } catch (const std::exception& ex) { PyEval_RestoreThread(tstate); - String message = boost::diagnostic_information(ex); + String message = DiagnosticInformation(ex); PyErr_SetString(PyExc_RuntimeError, message.CStr()); return NULL;