diff --git a/lib/base/Makefile.am b/lib/base/Makefile.am index be5b2a300..a8fadf2e6 100644 --- a/lib/base/Makefile.am +++ b/lib/base/Makefile.am @@ -58,6 +58,8 @@ libbase_la_SOURCES = \ scripttask.h \ socket.cpp \ socket.h \ + stacktrace.cpp \ + stacktrace.h \ stdiostream.cpp \ stdiostream.h \ stream.cpp \ diff --git a/lib/base/application.cpp b/lib/base/application.cpp index 92f839a48..6f9add2bc 100644 --- a/lib/base/application.cpp +++ b/lib/base/application.cpp @@ -308,10 +308,12 @@ void Application::SigAbrtHandler(int signum) { assert(signum == SIGABRT); +#ifndef _WIN32 struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = SIG_DFL; sigaction(SIGABRT, &sa, NULL); +#endif /* _WIN32 */ std::cerr << "Caught SIGABRT." << std::endl; @@ -358,13 +360,27 @@ void Application::ExceptionHandler(void) << std::endl; } - Utility::PrintStacktrace(std::cerr, 1); + StackTrace trace; + trace.Print(std::cerr, 1); DisplayBugMessage(); abort(); } +#ifdef _WIN32 +LONG CALLBACK Application::SEHUnhandledExceptionFilter(PEXCEPTION_POINTERS exi) +{ + std::cerr << "Unhandled SEH exception." << std::endl; + + StackTrace trace(exi); + trace.Print(std::cerr, 1); + + DisplayBugMessage(); + + return EXCEPTION_CONTINUE_SEARCH; +} +#endif /* _WIN32 /** * Installs the exception handlers. @@ -378,6 +394,8 @@ void Application::InstallExceptionHandlers(void) memset(&sa, 0, sizeof(sa)); sa.sa_handler = &Application::SigAbrtHandler; sigaction(SIGABRT, &sa, NULL); +#else /* _WIN32 */ + SetUnhandledExceptionFilter(&Application::SEHUnhandledExceptionFilter); #endif /* _WIN32 */ } diff --git a/lib/base/application.h b/lib/base/application.h index 08352dac5..a7c792958 100644 --- a/lib/base/application.h +++ b/lib/base/application.h @@ -101,13 +101,14 @@ private: #ifndef _WIN32 static void SigIntHandler(int signum); - static void SigAbrtHandler(int signum); #else /* _WIN32 */ static BOOL WINAPI CtrlHandler(DWORD type); + static LONG WINAPI SEHUnhandledExceptionFilter(PEXCEPTION_POINTERS exi); #endif /* _WIN32 */ static void DisplayBugMessage(void); + static void SigAbrtHandler(int signum); static void ExceptionHandler(void); static void TimeWatchThreadProc(void); diff --git a/lib/base/base.vcxproj b/lib/base/base.vcxproj index 2b5afb228..fcff863a1 100644 --- a/lib/base/base.vcxproj +++ b/lib/base/base.vcxproj @@ -20,6 +20,7 @@ + @@ -47,6 +48,7 @@ + @@ -62,6 +64,7 @@ + @@ -70,6 +73,7 @@ + @@ -177,7 +181,7 @@ Windows true - ws2_32.lib;shlwapi.lib;mmatch.lib;cJSON.lib;libeay32MTd.lib;ssleay32MTd.lib;%(AdditionalDependencies) + dbghelp.lib;ws2_32.lib;shlwapi.lib;mmatch.lib;cJSON.lib;libeay32MTd.lib;ssleay32MTd.lib;%(AdditionalDependencies) ws2_32.lib;shlwapi.lib @@ -196,7 +200,7 @@ Windows true - ws2_32.lib;shlwapi.lib;mmatch.lib;cJSON.lib;libeay32MTd.lib;ssleay32MTd.lib;%(AdditionalDependencies) + dbghelp.lib;ws2_32.lib;shlwapi.lib;mmatch.lib;cJSON.lib;libeay32MTd.lib;ssleay32MTd.lib;%(AdditionalDependencies) ws2_32.lib;shlwapi.lib @@ -220,7 +224,7 @@ true true true - ws2_32.lib;shlwapi.lib;mmatch.lib;cJSON.lib;libeay32MT.lib;ssleay32MT.lib;%(AdditionalDependencies) + dbghelp.lib;ws2_32.lib;shlwapi.lib;mmatch.lib;cJSON.lib;libeay32MT.lib;ssleay32MT.lib;%(AdditionalDependencies) ws2_32.lib;shlwapi.lib @@ -244,7 +248,7 @@ true true true - ws2_32.lib;shlwapi.lib;mmatch.lib;cJSON.lib;libeay32MT.lib;ssleay32MT.lib;%(AdditionalDependencies) + dbghelp.lib;ws2_32.lib;shlwapi.lib;mmatch.lib;cJSON.lib;libeay32MT.lib;ssleay32MT.lib;%(AdditionalDependencies) ws2_32.lib;shlwapi.lib diff --git a/lib/base/base.vcxproj.filters b/lib/base/base.vcxproj.filters index 097c74493..4bdbda5f1 100644 --- a/lib/base/base.vcxproj.filters +++ b/lib/base/base.vcxproj.filters @@ -103,6 +103,12 @@ Quelldateien + + Quelldateien + + + Quelldateien + @@ -210,6 +216,12 @@ Headerdateien + + Headerdateien + + + Headerdateien + diff --git a/lib/base/eventqueue.cpp b/lib/base/eventqueue.cpp index f959dfd19..921310e03 100644 --- a/lib/base/eventqueue.cpp +++ b/lib/base/eventqueue.cpp @@ -94,16 +94,20 @@ void EventQueue::QueueThreadProc(void) BOOST_FOREACH(const Callback& ev, events) { #ifdef _DEBUG + double st = Utility::GetTime(); + +# ifdef RUSAGE_THREAD struct rusage usage_start, usage_end; - double st = Utility::GetTime(); (void) getrusage(RUSAGE_THREAD, &usage_start); +# endif /* RUSAGE_THREAD */ #endif /* _DEBUG */ ev(); #ifdef _DEBUG double et = Utility::GetTime(); +# ifdef RUSAGE_THREAD (void) getrusage(RUSAGE_THREAD, &usage_end); double duser = (usage_end.ru_utime.tv_sec - usage_start.ru_utime.tv_sec) + @@ -119,10 +123,15 @@ void EventQueue::QueueThreadProc(void) int dvctx = usage_end.ru_nvcsw - usage_start.ru_nvcsw; int divctx = usage_end.ru_nivcsw - usage_start.ru_nivcsw; - +# endif /* RUSAGE_THREAD */ if (et - st > 0.5) { stringstream msgbuf; +# ifdef RUSAGE_THREAD msgbuf << "Event call took user:" << duser << "s, system:" << dsys << "s, wait:" << dwait << "s, minor_faults:" << dminfaults << ", major_faults:" << dmajfaults << ", voluntary_csw:" << dvctx << ", involuntary_csw:" << divctx; +# else + msgbuf << "Event call took " << (et - st) << "s"; +# endif /* RUSAGE_THREAD */ + Logger::Write(LogWarning, "base", msgbuf.str()); } #endif /* _DEBUG */ diff --git a/lib/base/i2-base.h b/lib/base/i2-base.h index 04ef5aff4..b55bc9388 100644 --- a/lib/base/i2-base.h +++ b/lib/base/i2-base.h @@ -77,6 +77,7 @@ #include #include +#include #include #include @@ -191,6 +192,7 @@ namespace signals2 = boost::signals2; #include "qstring.h" #include "utility.h" +#include "stacktrace.h" #include "object.h" #include "objectlock.h" #include "exception.h" diff --git a/lib/base/stacktrace.cpp b/lib/base/stacktrace.cpp new file mode 100644 index 000000000..a08e5bc9f --- /dev/null +++ b/lib/base/stacktrace.cpp @@ -0,0 +1,161 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012 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 "i2-base.h" +#if HAVE_BACKTRACE_SYMBOLS +# include +#endif /* HAVE_BACKTRACE_SYMBOLS */ + +using namespace icinga; + +boost::once_flag StackTrace::m_OnceFlag = BOOST_ONCE_INIT; + +#ifdef _MSC_VER +# pragma optimize("", off) +#endif /* _MSC_VER */ + +StackTrace::StackTrace(void) +{ + boost::call_once(m_OnceFlag, &StackTrace::Initialize); + +#if HAVE_BACKTRACE_SYMBOLS + m_Count = backtrace(m_Frames, sizeof(m_Frames) / sizeof(m_Frames[0])); +#else /* HAVE_BACKTRACE_SYMBOLS */ +# ifdef _WIN32 + m_Count = CaptureStackBackTrace(0, sizeof(m_Frames) / sizeof(m_Frames), m_Frames, NULL); +# else /* _WIN32 */ + m_Count = 0; +# endif /* _WIN32 */ +#endif /* HAVE_BACKTRACE_SYMBOLS */ +} + +#ifdef _MSC_VER +# pragma optimize("", on) +#endif /* _MSC_VER */ + +#ifdef _WIN32 +StackTrace::StackTrace(PEXCEPTION_POINTERS exi) +{ + boost::call_once(m_OnceFlag, &StackTrace::Initialize); + + STACKFRAME64 frame; + int architecture; + +#ifdef _WIN64 + architecture = IMAGE_FILE_MACHINE_AMD64; + + frame.AddrPC.Offset = exi->ContextRecord->Rip; + frame.AddrFrame.Offset = exi->ContextRecord->Rbp; + frame.AddrStack.Offset = exi->ContextRecord->Rsp; +#else /* _WIN64 */ + architecture = IMAGE_FILE_MACHINE_I386; + + frame.AddrPC.Offset = exi->ContextRecord->Eip; + frame.AddrFrame.Offset = exi->ContextRecord->Ebp; + frame.AddrStack.Offset = exi->ContextRecord->Esp; +#endif /* _WIN64 */ + + frame.AddrPC.Mode = AddrModeFlat; + frame.AddrFrame.Mode = AddrModeFlat; + frame.AddrStack.Mode = AddrModeFlat; + + m_Count = 0; + + while (StackWalk64(architecture, GetCurrentProcess(), GetCurrentThread(), + &frame, exi->ContextRecord, NULL, &SymFunctionTableAccess64, + &SymGetModuleBase64, NULL) && m_Count < sizeof(m_Frames) / sizeof(m_Frames[0])) { + m_Frames[m_Count] = reinterpret_cast(frame.AddrPC.Offset); + m_Count++; + } +} +#endif /* _WIN32 */ + +void StackTrace::Initialize(void) +{ + (void) SymSetOptions(SYMOPT_UNDNAME | SYMOPT_LOAD_LINES); + (void) SymInitialize(GetCurrentProcess(), NULL, TRUE); +} + +/** + * Prints a stacktrace to the specified stream. + * + * @param fp The stream. + * @param ignoreFrames The number of stackframes to ignore (in addition to + * the one this function is executing in). + * @returns true if the stacktrace was printed, false otherwise. + */ +void StackTrace::Print(ostream& fp, int ignoreFrames) +{ + fp << std::endl << "Stacktrace:" << std::endl; + +#ifndef _WIN32 +# if HAVE_BACKTRACE_SYMBOLS + char **messages = backtrace_symbols(m_Frames, m_Count); + + for (int i = ignoreFrames + 1; i < m_Count && messages != NULL; ++i) { + String message = messages[i]; + + char *sym_begin = strchr(messages[i], '('); + + if (sym_begin != NULL) { + char *sym_end = strchr(sym_begin, '+'); + + if (sym_end != NULL) { + String sym = String(sym_begin + 1, sym_end); + String sym_demangled = Utility::DemangleSymbolName(sym); + + if (sym_demangled.IsEmpty()) + sym_demangled = ""; + + message = String(messages[i], sym_begin) + ": " + sym_demangled + " (" + String(sym_end); + } + } + + fp << "\t(" << i - ignoreFrames - 1 << ") " << message << std::endl; + } + + free(messages); + + fp << std::endl; + + return true; +# else /* HAVE_BACKTRACE_SYMBOLS */ + fp << "(not available)" << std::endl; +# endif /* HAVE_BACKTRACE_SYMBOLS */ +#else /* _WIN32 */ + for (int i = ignoreFrames + 1; i < m_Count; i++) { + char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; + PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; + pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); + pSymbol->MaxNameLen = MAX_SYM_NAME; + + DWORD64 dwAddress = (DWORD64)m_Frames[i]; + DWORD dwDisplacement; + DWORD64 dwDisplacement64; + + IMAGEHLP_LINE64 line; + line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); + + (void) SymGetLineFromAddr64(GetCurrentProcess(), dwAddress, &dwDisplacement, &line); + (void) SymFromAddr(GetCurrentProcess(), dwAddress, &dwDisplacement64, pSymbol); + + fp << "\t(" << i - ignoreFrames - 1 << ") " << line.FileName << ":" << line.LineNumber << ": " << pSymbol->Name << "+" << dwDisplacement64 << std::endl; + } +#endif /* _WIN32 */ +} diff --git a/lib/base/stacktrace.h b/lib/base/stacktrace.h new file mode 100644 index 000000000..a25994ac7 --- /dev/null +++ b/lib/base/stacktrace.h @@ -0,0 +1,52 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012 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 STACKTRACE_H +#define STACKTRACE_H + +namespace icinga +{ + +/** + * A stacktrace. + * + * @ingroup base + */ +class StackTrace +{ +public: + StackTrace(void); +#ifdef _WIN32 + StackTrace(PEXCEPTION_POINTERS exi); +#endif /* _WIN32 */ + + void Print(ostream& fp, int ignoreFrames = 0); + +private: + void *m_Frames[64]; + int m_Count; + + static boost::once_flag m_OnceFlag; + + static void Initialize(void); +}; + +} + +#endif /* UTILITY_H */ diff --git a/lib/base/unix.h b/lib/base/unix.h index 25a83ddea..de72451b2 100644 --- a/lib/base/unix.h +++ b/lib/base/unix.h @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include diff --git a/lib/base/utility.cpp b/lib/base/utility.cpp index ad7726a5b..1165cf629 100644 --- a/lib/base/utility.cpp +++ b/lib/base/utility.cpp @@ -19,9 +19,6 @@ #include "i2-base.h" #include -#if HAVE_BACKTRACE_SYMBOLS -# include -#endif /* HAVE_BACKTRACE_SYMBOLS */ using namespace icinga; @@ -61,55 +58,7 @@ String Utility::GetTypeName(const type_info& ti) return DemangleSymbolName(ti.name()); } -/** - * Prints a stacktrace to the specified stream. - * - * @param fp The stream. - * @param ignoreFrames The number of stackframes to ignore (in addition to - * the one this function is executing in). - * @returns true if the stacktrace was printed, false otherwise. - */ -bool Utility::PrintStacktrace(ostream& fp, int ignoreFrames) -{ -#if HAVE_BACKTRACE_SYMBOLS - void *frames[50]; - int framecount = backtrace(frames, sizeof(frames) / sizeof(frames[0])); - char **messages = backtrace_symbols(frames, framecount); - - fp << std::endl << "Stacktrace:" << std::endl; - - for (int i = ignoreFrames + 1; i < framecount && messages != NULL; ++i) { - String message = messages[i]; - - char *sym_begin = strchr(messages[i], '('); - - if (sym_begin != NULL) { - char *sym_end = strchr(sym_begin, '+'); - - if (sym_end != NULL) { - String sym = String(sym_begin + 1, sym_end); - String sym_demangled = Utility::DemangleSymbolName(sym); - - if (sym_demangled.IsEmpty()) - sym_demangled = ""; - - message = String(messages[i], sym_begin) + ": " + sym_demangled + " (" + String(sym_end); - } - } - - fp << "\t(" << i - ignoreFrames - 1 << ") " << message << std::endl; - } - - free(messages); - - fp << std::endl; - - return true; -#else /* HAVE_BACKTRACE_SYMBOLS */ - return false; -#endif /* HAVE_BACKTRACE_SYMBOLS */ -} /** * Detaches from the controlling terminal. diff --git a/lib/base/utility.h b/lib/base/utility.h index f90176592..9035f2945 100644 --- a/lib/base/utility.h +++ b/lib/base/utility.h @@ -33,7 +33,6 @@ class I2_BASE_API Utility public: static String DemangleSymbolName(const String& sym); static String GetTypeName(const type_info& ti); - static bool PrintStacktrace(ostream& fp, int ignoreFrames = 0); static void Daemonize(void); diff --git a/lib/icinga/host.cpp b/lib/icinga/host.cpp index 0e62d4a1d..f3776473a 100644 --- a/lib/icinga/host.cpp +++ b/lib/icinga/host.cpp @@ -508,8 +508,6 @@ set Host::GetParentServices(void) const HostState Host::GetState(void) const { - assert(!OwnsLock()); - if (!IsReachable()) return HostUnreachable; @@ -594,19 +592,7 @@ Dictionary::Ptr Host::CalculateDynamicMacros(void) const macros->Set("HOSTNAME", GetName()); macros->Set("HOSTDISPLAYNAME", GetDisplayName()); macros->Set("HOSTALIAS", GetName()); - - HostState state = GetState(); - - macros->Set("HOSTSTATE", HostStateToString(GetState())); - macros->Set("HOSTSTATEID", GetState()); - - HostState lastState = GetLastState(); - StateType lastStateType = GetLastStateType(); - - macros->Set("LASTHOSTSTATE", HostStateToString(lastState)); - macros->Set("LASTHOSTSTATEID", lastState); - macros->Set("LASTHOSTSTATETYPE", Service::StateTypeToString(lastStateType)); - } + } Dictionary::Ptr cr; @@ -615,11 +601,16 @@ Dictionary::Ptr Host::CalculateDynamicMacros(void) const if (hc) { ObjectLock olock(hc); + macros->Set("HOSTSTATE", HostStateToString(GetState())); + macros->Set("HOSTSTATEID", GetState()); macros->Set("HOSTSTATETYPE", Service::StateTypeToString(hc->GetStateType())); macros->Set("HOSTATTEMPT", hc->GetCurrentCheckAttempt()); macros->Set("MAXHOSTATTEMPT", hc->GetMaxCheckAttempts()); - macros->Set("LASTHOSTSTATECHANGE", (time_t)hc->GetLastStateChange()); + macros->Set("LASTHOSTSTATE", HostStateToString(GetLastState())); + macros->Set("LASTHOSTSTATEID", GetLastState()); + macros->Set("LASTHOSTSTATETYPE", Service::StateTypeToString(GetLastStateType())); + macros->Set("LASTHOSTSTATECHANGE", (long)hc->GetLastStateChange()); cr = hc->GetLastCheckResult(); } @@ -631,7 +622,7 @@ Dictionary::Ptr Host::CalculateDynamicMacros(void) const macros->Set("HOSTOUTPUT", cr->Get("output")); macros->Set("HOSTPERFDATA", cr->Get("performance_data_raw")); - macros->Set("LASTHOSTCHECK", (time_t)cr->Get("schedule_start")); + macros->Set("LASTHOSTCHECK", (long)cr->Get("schedule_start")); } macros->Seal(); diff --git a/lib/icinga/icinga.vcxproj.filters b/lib/icinga/icinga.vcxproj.filters index b9fe2ba6b..e001ebfaf 100644 --- a/lib/icinga/icinga.vcxproj.filters +++ b/lib/icinga/icinga.vcxproj.filters @@ -58,6 +58,15 @@ Quelldateien + + Quelldateien + + + Quelldateien + + + Quelldateien + @@ -105,6 +114,15 @@ Headerdateien + + Headerdateien + + + Headerdateien + + + Headerdateien + diff --git a/lib/icinga/icingaapplication.cpp b/lib/icinga/icingaapplication.cpp index 9827f3a00..f7a477104 100644 --- a/lib/icinga/icingaapplication.cpp +++ b/lib/icinga/icingaapplication.cpp @@ -74,7 +74,7 @@ int IcingaApplication::Main(void) /* periodically dump the program state */ m_RetentionTimer = boost::make_shared(); - m_RetentionTimer->SetInterval(300); + m_RetentionTimer->SetInterval(3); m_RetentionTimer->OnTimerExpired.connect(boost::bind(&IcingaApplication::DumpProgramState, this)); m_RetentionTimer->Start(); diff --git a/lib/icinga/service-check.cpp b/lib/icinga/service-check.cpp index 043e01981..1f6d40567 100644 --- a/lib/icinga/service-check.cpp +++ b/lib/icinga/service-check.cpp @@ -416,8 +416,6 @@ void Service::ProcessCheckResult(const Dictionary::Ptr& cr) int state = cr->Get("state"); SetState(static_cast(state)); - SetLastCheckResult(cr); - double now = Utility::GetTime(); if (old_state != GetState()) { @@ -459,13 +457,19 @@ void Service::ProcessCheckResult(const Dictionary::Ptr& cr) olock.Unlock(); + /* Update macros - these are used by event handlers and notifications. */ + cr->Set("macros", CalculateAllMacros()); + + cr->Seal(); + + olock.Lock(); + SetLastCheckResult(cr); + olock.Unlock(); + /* Flush the object so other instances see the service's * new state when they receive the CheckResult message */ Flush(); - /* Update macros - these are used by event handlers and notifications. */ - cr->Set("macros", CalculateAllMacros()); - RequestMessage rm; rm.SetMethod("checker::CheckResult"); @@ -664,8 +668,6 @@ void Service::CheckCompletedHandler(const Dictionary::Ptr& checkInfo, EndpointManager::Ptr em = EndpointManager::GetInstance(); result->Set("current_checker", em->GetIdentity()); } - - result->Seal(); } if (result) diff --git a/lib/icinga/service.cpp b/lib/icinga/service.cpp index 5b7d4a68b..b289cf5ae 100644 --- a/lib/icinga/service.cpp +++ b/lib/icinga/service.cpp @@ -448,7 +448,7 @@ Dictionary::Ptr Service::CalculateDynamicMacros(void) const macros->Set("LASTSERVICESTATE", StateToString(GetLastState())); macros->Set("LASTSERVICESTATEID", GetLastState()); macros->Set("LASTSERVICESTATETYPE", StateTypeToString(GetLastStateType())); - macros->Set("LASTSERVICESTATECHANGE", (time_t)GetLastStateChange()); + macros->Set("LASTSERVICESTATECHANGE", (long)GetLastStateChange()); cr = GetLastCheckResult(); } @@ -462,7 +462,7 @@ Dictionary::Ptr Service::CalculateDynamicMacros(void) const macros->Set("SERVICEOUTPUT", cr->Get("output")); macros->Set("SERVICEPERFDATA", cr->Get("performance_data_raw")); - macros->Set("LASTSERVICECHECK", (time_t)cr->Get("schedule_start")); + macros->Set("LASTSERVICECHECK", (long)cr->Get("schedule_start")); } macros->Seal();