Implement support for high-level stack traces.

Refs #5085
This commit is contained in:
Gunnar Beutner 2013-11-19 07:49:41 +01:00 committed by Gunnar Beutner
parent 062110339b
commit 92dd32eb68
17 changed files with 237 additions and 15 deletions

View File

@ -29,6 +29,7 @@
#include "base/zlibstream.h" #include "base/zlibstream.h"
#include "base/application.h" #include "base/application.h"
#include "base/convert.h" #include "base/convert.h"
#include "base/context.h"
#include <fstream> #include <fstream>
using namespace icinga; using namespace icinga;
@ -358,6 +359,8 @@ void ClusterListener::LogGlobHandler(std::vector<int>& files, const String& file
void ClusterListener::ReplayLog(const Endpoint::Ptr& endpoint, const Stream::Ptr& stream) void ClusterListener::ReplayLog(const Endpoint::Ptr& endpoint, const Stream::Ptr& stream)
{ {
CONTEXT("Replaying log for Endpoint '" + endpoint->GetName() + "'");
int count = -1; int count = -1;
double peer_ts = endpoint->GetLocalLogPosition(); double peer_ts = endpoint->GetLocalLogPosition();
bool last_sync = false; bool last_sync = false;
@ -495,6 +498,8 @@ void ClusterListener::ConfigGlobHandler(const Dictionary::Ptr& config, const Str
*/ */
void ClusterListener::NewClientHandler(const Socket::Ptr& client, TlsRole role) void ClusterListener::NewClientHandler(const Socket::Ptr& client, TlsRole role)
{ {
CONTEXT("Handling new cluster client connection");
NetworkStream::Ptr netStream = make_shared<NetworkStream>(client); NetworkStream::Ptr netStream = make_shared<NetworkStream>(client);
TlsStream::Ptr tlsStream = make_shared<TlsStream>(netStream, role, m_SSLContext); TlsStream::Ptr tlsStream = make_shared<TlsStream>(netStream, role, m_SSLContext);
@ -963,6 +968,8 @@ void ClusterListener::MessageExceptionHandler(boost::exception_ptr exp)
void ClusterListener::MessageHandler(const Endpoint::Ptr& sender, const Dictionary::Ptr& message) void ClusterListener::MessageHandler(const Endpoint::Ptr& sender, const Dictionary::Ptr& message)
{ {
CONTEXT("Processing cluster message of type '" + message->Get("method") + "'");
sender->SetSeen(Utility::GetTime()); sender->SetSeen(Utility::GetTime());
if (message->Contains("ts")) { if (message->Contains("ts")) {

View File

@ -122,6 +122,8 @@ void IdoMysqlConnection::Reconnect(void)
{ {
AssertOnWorkQueue(); AssertOnWorkQueue();
CONTEXT("Reconnecting to MySQL IDO database '" + GetName() + "'");
{ {
boost::mutex::scoped_lock lock(m_ConnectionMutex); boost::mutex::scoped_lock lock(m_ConnectionMutex);

View File

@ -24,6 +24,7 @@
#include "base/application.h" #include "base/application.h"
#include "base/dynamictype.h" #include "base/dynamictype.h"
#include "base/exception.h" #include "base/exception.h"
#include "base/context.h"
#include "db_ido/dbtype.h" #include "db_ido/dbtype.h"
#include "db_ido/dbvalue.h" #include "db_ido/dbvalue.h"
#include "db_ido_pgsql/idopgsqlconnection.h" #include "db_ido_pgsql/idopgsqlconnection.h"
@ -121,6 +122,8 @@ void IdoPgsqlConnection::Reconnect(void)
{ {
AssertOnWorkQueue(); AssertOnWorkQueue();
CONTEXT("Reconnecting to PostgreSQL IDO database '" + GetName() + "'");
{ {
boost::mutex::scoped_lock lock(m_ConnectionMutex); boost::mutex::scoped_lock lock(m_ConnectionMutex);

View File

@ -27,6 +27,7 @@
#include "base/exception.h" #include "base/exception.h"
#include "base/convert.h" #include "base/convert.h"
#include "base/scriptvariable.h" #include "base/scriptvariable.h"
#include "base/context.h"
#include "config.h" #include "config.h"
#include <boost/program_options.hpp> #include <boost/program_options.hpp>
#include <boost/tuple/tuple.hpp> #include <boost/tuple/tuple.hpp>
@ -63,6 +64,8 @@ static String LoadAppType(const String& typeSpec)
static bool LoadConfigFiles(const String& appType, bool validateOnly) static bool LoadConfigFiles(const String& appType, bool validateOnly)
{ {
CONTEXT("Loading configuration files");
ConfigCompilerContext::GetInstance()->Reset(); ConfigCompilerContext::GetInstance()->Reset();
if (g_AppParams.count("config") > 0) { if (g_AppParams.count("config") > 0) {

View File

@ -26,7 +26,7 @@ mkclass_target(sysloglogger.ti sysloglogger.th)
add_library(base SHARED add_library(base SHARED
application.cpp application.th array.cpp bufferedstream.cpp consolelogger.cpp application.cpp application.th array.cpp bufferedstream.cpp consolelogger.cpp
consolelogger.th convert.cpp dictionary.cpp dynamicobject.cpp consolelogger.th context.cpp convert.cpp dictionary.cpp dynamicobject.cpp
dynamicobject.th dynamictype.cpp exception.cpp fifo.cpp filelogger.cpp dynamicobject.th dynamictype.cpp exception.cpp fifo.cpp filelogger.cpp
filelogger.th logger.cpp logger.th netstring.cpp networkstream.cpp object.cpp filelogger.th logger.cpp logger.th netstring.cpp networkstream.cpp object.cpp
objectlock.cpp process.cpp process-unix.cpp process-windows.cpp qstring.cpp objectlock.cpp process.cpp process-unix.cpp process-windows.cpp qstring.cpp

View File

@ -363,6 +363,7 @@ void Application::SigAbrtHandler(int)
std::cerr << "Caught SIGABRT." << std::endl; std::cerr << "Caught SIGABRT." << std::endl;
StackTrace trace; StackTrace trace;
std::cerr << "Stacktrace:" << std::endl;
trace.Print(std::cerr, 1); trace.Print(std::cerr, 1);
DisplayBugMessage(); DisplayBugMessage();
@ -417,6 +418,7 @@ LONG CALLBACK Application::SEHUnhandledExceptionFilter(PEXCEPTION_POINTERS exi)
std::cerr << "Unhandled SEH exception." << std::endl; std::cerr << "Unhandled SEH exception." << std::endl;
StackTrace trace(exi); StackTrace trace(exi);
std::cerr << "Stacktrace:" << std::endl;
trace.Print(std::cerr, 1); trace.Print(std::cerr, 1);
DisplayBugMessage(); DisplayBugMessage();

70
lib/base/context.cpp Normal file
View File

@ -0,0 +1,70 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2013 Icinga Development Team (http://www.icinga.org/) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "base/context.h"
#include <boost/thread/tss.hpp>
#include <boost/foreach.hpp>
using namespace icinga;
static boost::thread_specific_ptr<std::list<String> > l_Frames;
ContextFrame::ContextFrame(const String& message)
{
GetFrames().push_front(message);
}
ContextFrame::~ContextFrame(void)
{
GetFrames().pop_front();
}
std::list<String>& ContextFrame::GetFrames(void)
{
if (l_Frames.get() == NULL)
l_Frames.reset(new std::list<String>());
return *l_Frames;
}
ContextTrace::ContextTrace(void)
: m_Frames(ContextFrame::GetFrames())
{ }
void ContextTrace::Print(std::ostream& fp) const
{
fp << std::endl;
int i = 0;
BOOST_FOREACH(const String& frame, m_Frames) {
fp << "\t(" << i << ") " << frame << std::endl;
i++;
}
}
size_t ContextTrace::GetLength(void) const
{
return m_Frames.size();
}
std::ostream& icinga::operator<<(std::ostream& stream, const ContextTrace& trace)
{
trace.Print(stream);
return stream;
}

67
lib/base/context.h Normal file
View File

@ -0,0 +1,67 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2013 Icinga Development Team (http://www.icinga.org/) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#ifndef CONTEXT_H
#define CONTEXT_H
#include "base/i2-base.h"
#include "base/qstring.h"
#include <list>
namespace icinga
{
class I2_BASE_API ContextTrace
{
public:
ContextTrace(void);
void Print(std::ostream& fp) const;
size_t GetLength(void) const;
private:
std::list<String> m_Frames;
};
I2_BASE_API std::ostream& operator<<(std::ostream& stream, const ContextTrace& trace);
/**
* A context frame.
*
* @ingroup base
*/
class I2_BASE_API ContextFrame
{
public:
ContextFrame(const String& message);
~ContextFrame(void);
private:
static std::list<String>& GetFrames(void);
friend class ContextTrace;
};
/* The currentContextFrame variable has to be volatile in order to prevent
* the compiler from optimizing it away. */
#define CONTEXT(message) volatile icinga::ContextFrame currentContextFrame(message)
}
#endif /* CONTEXT_H */

View File

@ -21,7 +21,8 @@
using namespace icinga; using namespace icinga;
static boost::thread_specific_ptr<StackTrace> l_LastStackTrace; static boost::thread_specific_ptr<StackTrace> l_LastExceptionStack;
static boost::thread_specific_ptr<ContextTrace> l_LastExceptionContext;
#ifndef _WIN32 #ifndef _WIN32
extern "C" extern "C"
@ -43,15 +44,22 @@ void __cxa_throw(void *obj, void *pvtinfo, void (*dest)(void *))
thrown_ptr = *(void **)thrown_ptr; thrown_ptr = *(void **)thrown_ptr;
#endif /* __APPLE__ */ #endif /* __APPLE__ */
StackTrace trace; StackTrace stack;
SetLastExceptionStack(trace); SetLastExceptionStack(stack);
ContextTrace context;
SetLastExceptionContext(context);
#ifndef __APPLE__ #ifndef __APPLE__
/* Check if thrown_ptr inherits from boost::exception. */ /* Check if thrown_ptr inherits from boost::exception. */
if (boost_exc->__do_catch(tinfo, &thrown_ptr, 1)) { if (boost_exc->__do_catch(tinfo, &thrown_ptr, 1)) {
boost::exception *ex = (boost::exception *)thrown_ptr; boost::exception *ex = (boost::exception *)thrown_ptr;
*ex << StackTraceErrorInfo(trace); if (boost::get_error_info<StackTraceErrorInfo>(*ex) == NULL)
*ex << StackTraceErrorInfo(stack);
if (boost::get_error_info<ContextTraceErrorInfo>(*ex) == NULL)
*ex << ContextTraceErrorInfo(context);
} }
#endif /* __APPLE__ */ #endif /* __APPLE__ */
@ -61,28 +69,43 @@ void __cxa_throw(void *obj, void *pvtinfo, void (*dest)(void *))
StackTrace *icinga::GetLastExceptionStack(void) StackTrace *icinga::GetLastExceptionStack(void)
{ {
return l_LastStackTrace.get(); return l_LastExceptionStack.get();
} }
void icinga::SetLastExceptionStack(const StackTrace& trace) void icinga::SetLastExceptionStack(const StackTrace& trace)
{ {
l_LastStackTrace.reset(new StackTrace(trace)); l_LastExceptionStack.reset(new StackTrace(trace));
}
ContextTrace *icinga::GetLastExceptionContext(void)
{
return l_LastExceptionContext.get();
}
void icinga::SetLastExceptionContext(const ContextTrace& context)
{
l_LastExceptionContext.reset(new ContextTrace(context));
} }
String icinga::DiagnosticInformation(boost::exception_ptr eptr) String icinga::DiagnosticInformation(boost::exception_ptr eptr)
{ {
StackTrace *pt = GetLastExceptionStack(); StackTrace *pt = GetLastExceptionStack();
StackTrace trace; StackTrace stack;
ContextTrace *pc = GetLastExceptionContext();
ContextTrace context;
if (pt) if (pt)
trace = *pt; stack = *pt;
if (pc)
context = *pc;
try { try {
boost::rethrow_exception(eptr); boost::rethrow_exception(eptr);
} catch (const std::exception& ex) { } catch (const std::exception& ex) {
return DiagnosticInformation(ex, pt ? &trace : NULL); return DiagnosticInformation(ex, pt ? &stack : NULL, pc ? &context : NULL);
} }
return boost::diagnostic_information(eptr); return boost::diagnostic_information(eptr);
} }

View File

@ -23,6 +23,7 @@
#include "base/i2-base.h" #include "base/i2-base.h"
#include "base/qstring.h" #include "base/qstring.h"
#include "base/stacktrace.h" #include "base/stacktrace.h"
#include "base/context.h"
#include <sstream> #include <sstream>
#include <boost/thread/tss.hpp> #include <boost/thread/tss.hpp>
#include <boost/exception/errinfo_api_function.hpp> #include <boost/exception/errinfo_api_function.hpp>
@ -41,10 +42,14 @@ namespace icinga
I2_BASE_API StackTrace *GetLastExceptionStack(void); I2_BASE_API StackTrace *GetLastExceptionStack(void);
I2_BASE_API void SetLastExceptionStack(const StackTrace& trace); I2_BASE_API void SetLastExceptionStack(const StackTrace& trace);
I2_BASE_API ContextTrace *GetLastExceptionContext(void);
I2_BASE_API void SetLastExceptionContext(const ContextTrace& context);
typedef boost::error_info<StackTrace, StackTrace> StackTraceErrorInfo; typedef boost::error_info<StackTrace, StackTrace> StackTraceErrorInfo;
typedef boost::error_info<ContextTrace, ContextTrace> ContextTraceErrorInfo;
template<typename T> template<typename T>
String DiagnosticInformation(const T& ex, StackTrace *trace = NULL) String DiagnosticInformation(const T& ex, StackTrace *stack = NULL, ContextTrace *context = NULL)
{ {
std::ostringstream result; std::ostringstream result;
@ -53,12 +58,21 @@ String DiagnosticInformation(const T& ex, StackTrace *trace = NULL)
if (boost::get_error_info<StackTraceErrorInfo>(ex) == NULL) { if (boost::get_error_info<StackTraceErrorInfo>(ex) == NULL) {
result << std::endl; result << std::endl;
if (trace) if (stack)
result << *trace; result << *stack;
else else
result << *GetLastExceptionStack(); result << *GetLastExceptionStack();
} }
if (boost::get_error_info<ContextTraceErrorInfo>(ex) == NULL) {
result << std::endl;
if (context)
result << *context;
else
result << *GetLastExceptionContext();
}
return result.str(); return result.str();
} }

View File

@ -23,6 +23,8 @@
#include "base/dynamictype.h" #include "base/dynamictype.h"
#include "base/utility.h" #include "base/utility.h"
#include "base/objectlock.h" #include "base/objectlock.h"
#include "base/context.h"
#include "base/convert.h"
#include <boost/make_shared.hpp> #include <boost/make_shared.hpp>
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <iostream> #include <iostream>
@ -73,6 +75,16 @@ void icinga::Log(LogSeverity severity, const String& facility,
entry.Facility = facility; entry.Facility = facility;
entry.Message = message; entry.Message = message;
if (severity >= LogWarning) {
ContextTrace context;
if (context.GetLength() > 0) {
std::ostringstream trace;
trace << context;
entry.Message += "\nContext:" + trace.str();
}
}
bool processed = false; bool processed = false;
BOOST_FOREACH(const Logger::Ptr& logger, Logger::GetLoggers()) { BOOST_FOREACH(const Logger::Ptr& logger, Logger::GetLoggers()) {

View File

@ -107,7 +107,7 @@ void StackTrace::Initialize(void)
*/ */
void StackTrace::Print(std::ostream& fp, int ignoreFrames) const void StackTrace::Print(std::ostream& fp, int ignoreFrames) const
{ {
fp << std::endl << "Stacktrace:" << std::endl; fp << std::endl;
#ifndef _WIN32 #ifndef _WIN32
# ifdef HAVE_BACKTRACE_SYMBOLS # ifdef HAVE_BACKTRACE_SYMBOLS

View File

@ -21,6 +21,7 @@
#include "config/configitem.h" #include "config/configitem.h"
#include "base/logger_fwd.h" #include "base/logger_fwd.h"
#include "base/utility.h" #include "base/utility.h"
#include "base/context.h"
#include <sstream> #include <sstream>
#include <fstream> #include <fstream>
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
@ -126,6 +127,8 @@ void ConfigCompiler::HandleLibrary(const String& library)
*/ */
void ConfigCompiler::CompileStream(const String& path, std::istream *stream) void ConfigCompiler::CompileStream(const String& path, std::istream *stream)
{ {
CONTEXT("Compiling configuration stream with name '" + path + "'");
stream->exceptions(std::istream::badbit); stream->exceptions(std::istream::badbit);
ConfigCompiler ctx(path, stream); ConfigCompiler ctx(path, stream);
@ -140,6 +143,8 @@ void ConfigCompiler::CompileStream(const String& path, std::istream *stream)
*/ */
void ConfigCompiler::CompileFile(const String& path) void ConfigCompiler::CompileFile(const String& path)
{ {
CONTEXT("Compiling configuration file '" + path + "'");
std::ifstream stream; std::ifstream stream;
stream.open(path.CStr(), std::ifstream::in); stream.open(path.CStr(), std::ifstream::in);

View File

@ -23,6 +23,7 @@
#include "base/array.h" #include "base/array.h"
#include "base/objectlock.h" #include "base/objectlock.h"
#include "base/logger_fwd.h" #include "base/logger_fwd.h"
#include "base/context.h"
#include <boost/tuple/tuple.hpp> #include <boost/tuple/tuple.hpp>
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
@ -60,6 +61,8 @@ Value MacroProcessor::ResolveMacros(const Value& str, const std::vector<MacroRes
bool MacroProcessor::ResolveMacro(const String& macro, const std::vector<MacroResolver::Ptr>& resolvers, bool MacroProcessor::ResolveMacro(const String& macro, const std::vector<MacroResolver::Ptr>& resolvers,
const CheckResult::Ptr& cr, String *result) const CheckResult::Ptr& cr, String *result)
{ {
CONTEXT("Resolving macro '" + macro + "'");
BOOST_FOREACH(const MacroResolver::Ptr& resolver, resolvers) { BOOST_FOREACH(const MacroResolver::Ptr& resolver, resolvers) {
if (resolver->ResolveMacro(macro, cr, result)) if (resolver->ResolveMacro(macro, cr, result))
return true; return true;
@ -72,6 +75,8 @@ bool MacroProcessor::ResolveMacro(const String& macro, const std::vector<MacroRe
String MacroProcessor::InternalResolveMacros(const String& str, const std::vector<MacroResolver::Ptr>& resolvers, String MacroProcessor::InternalResolveMacros(const String& str, const std::vector<MacroResolver::Ptr>& resolvers,
const CheckResult::Ptr& cr, const MacroProcessor::EscapeCallback& escapeFn, const Array::Ptr& escapeMacros) const CheckResult::Ptr& cr, const MacroProcessor::EscapeCallback& escapeFn, const Array::Ptr& escapeMacros)
{ {
CONTEXT("Resolving macros for string '" + str + "'");
size_t offset, pos_first, pos_second; size_t offset, pos_first, pos_second;
offset = 0; offset = 0;

View File

@ -27,6 +27,7 @@
#include "base/convert.h" #include "base/convert.h"
#include "base/utility.h" #include "base/utility.h"
#include "base/exception.h" #include "base/exception.h"
#include "base/context.h"
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/replace.hpp>
@ -440,6 +441,8 @@ String Service::StateTypeToString(StateType type)
void Service::ExecuteCheck(void) void Service::ExecuteCheck(void)
{ {
CONTEXT("Executing service check for service '" + GetShortName() + "' on host '" + GetHost()->GetName() + "'");
ASSERT(!OwnsLock()); ASSERT(!OwnsLock());
bool reachable = IsReachable(); bool reachable = IsReachable();

View File

@ -20,6 +20,7 @@
#include "icinga/service.h" #include "icinga/service.h"
#include "icinga/eventcommand.h" #include "icinga/eventcommand.h"
#include "icinga/icingaapplication.h" #include "icinga/icingaapplication.h"
#include "base/context.h"
using namespace icinga; using namespace icinga;
@ -45,6 +46,8 @@ EventCommand::Ptr Service::GetEventCommand(void) const
void Service::ExecuteEventHandler(void) void Service::ExecuteEventHandler(void)
{ {
CONTEXT("Executing event handler for service '" + GetShortName() + "' on host '" + GetHost()->GetName() + "'");
if (!IcingaApplication::GetInstance()->GetEnableEventHandlers() || !GetEnableEventHandler()) if (!IcingaApplication::GetInstance()->GetEnableEventHandlers() || !GetEnableEventHandler())
return; return;

View File

@ -25,6 +25,7 @@
#include "base/timer.h" #include "base/timer.h"
#include "base/utility.h" #include "base/utility.h"
#include "base/exception.h" #include "base/exception.h"
#include "base/context.h"
#include "config/configitembuilder.h" #include "config/configitembuilder.h"
#include <boost/tuple/tuple.hpp> #include <boost/tuple/tuple.hpp>
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
@ -44,6 +45,8 @@ void Service::ResetNotificationNumbers(void)
void Service::SendNotifications(NotificationType type, const CheckResult::Ptr& cr, const String& author, const String& text) void Service::SendNotifications(NotificationType type, const CheckResult::Ptr& cr, const String& author, const String& text)
{ {
CONTEXT("Sending notifications for service '" + GetShortName() + "' on host '" + GetHost()->GetName() + "'");
bool force = GetForceNextNotification(); bool force = GetForceNextNotification();
if (!IcingaApplication::GetInstance()->GetEnableNotifications() || !GetEnableNotifications()) { if (!IcingaApplication::GetInstance()->GetEnableNotifications() || !GetEnableNotifications()) {