Implemented stacktrace support for Windows.

This commit is contained in:
Gunnar Beutner 2013-03-07 15:00:26 +01:00
parent 5a166f83d7
commit fa3f01667f
17 changed files with 307 additions and 88 deletions

View File

@ -58,6 +58,8 @@ libbase_la_SOURCES = \
scripttask.h \
socket.cpp \
socket.h \
stacktrace.cpp \
stacktrace.h \
stdiostream.cpp \
stdiostream.h \
stream.cpp \

View File

@ -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 */
}

View File

@ -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);

View File

@ -20,6 +20,7 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="application.cpp" />
<ClCompile Include="attribute.cpp" />
<ClCompile Include="component.cpp" />
<ClCompile Include="connection.cpp" />
<ClCompile Include="convert.cpp" />
@ -47,6 +48,7 @@
<ClCompile Include="scriptfunction.cpp" />
<ClCompile Include="scripttask.cpp" />
<ClCompile Include="socket.cpp" />
<ClCompile Include="stacktrace.cpp" />
<ClCompile Include="stdiostream.cpp" />
<ClCompile Include="stream.cpp" />
<ClCompile Include="streamlogger.cpp" />
@ -62,6 +64,7 @@
<ItemGroup>
<ClInclude Include="application.h" />
<ClInclude Include="asynctask.h" />
<ClInclude Include="attribute.h" />
<ClInclude Include="component.h" />
<ClInclude Include="connection.h" />
<ClInclude Include="convert.h" />
@ -70,6 +73,7 @@
<ClInclude Include="dynamictype.h" />
<ClInclude Include="eventqueue.h" />
<ClInclude Include="fifo.h" />
<ClInclude Include="stacktrace.h" />
<ClInclude Include="stdiostream.h" />
<ClInclude Include="stream.h" />
<ClInclude Include="netstring.h" />
@ -177,7 +181,7 @@
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>ws2_32.lib;shlwapi.lib;mmatch.lib;cJSON.lib;libeay32MTd.lib;ssleay32MTd.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>dbghelp.lib;ws2_32.lib;shlwapi.lib;mmatch.lib;cJSON.lib;libeay32MTd.lib;ssleay32MTd.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<Lib>
<AdditionalDependencies>ws2_32.lib;shlwapi.lib</AdditionalDependencies>
@ -196,7 +200,7 @@
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>ws2_32.lib;shlwapi.lib;mmatch.lib;cJSON.lib;libeay32MTd.lib;ssleay32MTd.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>dbghelp.lib;ws2_32.lib;shlwapi.lib;mmatch.lib;cJSON.lib;libeay32MTd.lib;ssleay32MTd.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<Lib>
<AdditionalDependencies>ws2_32.lib;shlwapi.lib</AdditionalDependencies>
@ -220,7 +224,7 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>ws2_32.lib;shlwapi.lib;mmatch.lib;cJSON.lib;libeay32MT.lib;ssleay32MT.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>dbghelp.lib;ws2_32.lib;shlwapi.lib;mmatch.lib;cJSON.lib;libeay32MT.lib;ssleay32MT.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<Lib>
<AdditionalDependencies>ws2_32.lib;shlwapi.lib</AdditionalDependencies>
@ -244,7 +248,7 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>ws2_32.lib;shlwapi.lib;mmatch.lib;cJSON.lib;libeay32MT.lib;ssleay32MT.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>dbghelp.lib;ws2_32.lib;shlwapi.lib;mmatch.lib;cJSON.lib;libeay32MT.lib;ssleay32MT.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<Lib>
<AdditionalDependencies>ws2_32.lib;shlwapi.lib</AdditionalDependencies>

View File

@ -103,6 +103,12 @@
<ClCompile Include="eventqueue.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="attribute.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="stacktrace.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="application.h">
@ -210,6 +216,12 @@
<ClInclude Include="eventqueue.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="attribute.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="stacktrace.h">
<Filter>Headerdateien</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="Quelldateien">

View File

@ -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 */

View File

@ -77,6 +77,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <string>
#include <exception>
@ -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"

161
lib/base/stacktrace.cpp Normal file
View File

@ -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 <execinfo.h>
#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<void *>(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 = "<unknown function>";
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 */
}

52
lib/base/stacktrace.h Normal file
View File

@ -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 */

View File

@ -30,7 +30,6 @@
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <signal.h>
#include <libgen.h>
#include <syslog.h>
#include <sys/file.h>

View File

@ -19,9 +19,6 @@
#include "i2-base.h"
#include <mmatch.h>
#if HAVE_BACKTRACE_SYMBOLS
# include <execinfo.h>
#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 = "<unknown function>";
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.

View File

@ -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);

View File

@ -508,8 +508,6 @@ set<Service::Ptr> 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();

View File

@ -58,6 +58,15 @@
<ClCompile Include="service-notification.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="api.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="user.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="usergroup.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="i2-icinga.h">
@ -105,6 +114,15 @@
<ClInclude Include="notificationrequestmessage.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="api.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="user.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="usergroup.h">
<Filter>Headerdateien</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="Headerdateien">

View File

@ -74,7 +74,7 @@ int IcingaApplication::Main(void)
/* periodically dump the program state */
m_RetentionTimer = boost::make_shared<Timer>();
m_RetentionTimer->SetInterval(300);
m_RetentionTimer->SetInterval(3);
m_RetentionTimer->OnTimerExpired.connect(boost::bind(&IcingaApplication::DumpProgramState, this));
m_RetentionTimer->Start();

View File

@ -416,8 +416,6 @@ void Service::ProcessCheckResult(const Dictionary::Ptr& cr)
int state = cr->Get("state");
SetState(static_cast<ServiceState>(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)

View File

@ -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();