src/agent/subagents/skeleton/skeleton.cpp -text
src/agent/subagents/skeleton/skeleton.dsp -text
src/agent/subagents/skeleton/skeleton.dsw -text
+src/agent/subagents/winperf/collect.cpp -text
src/agent/subagents/winperf/tools.cpp -text
src/agent/subagents/winperf/winperf.cpp -text
src/agent/subagents/winperf/winperf.dsp -text
--- /dev/null
+/*
+** Windows Performance NetXMS subagent
+** Copyright (C) 2004 Victor Kirhenshtein
+**
+** 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** $module: collect.cpp
+**
+**/
+
+#include "winperf.h"
+
+
+//
+// Static data
+//
+
+WINPERF_COUNTER_SET m_cntSet[3] =
+{
+ { 1000, 0, NULL, _T('A') },
+ { 5000, 0, NULL, _T('B') },
+ { 60000, 0, NULL, _T('C') }
+};
+
+
+//
+// Check that counter's name is valid and determine counter's type
+//
+
+static int CheckCounter(TCHAR *pszName)
+{
+ HQUERY hQuery;
+ HCOUNTER hCounter;
+ PDH_STATUS rc;
+ PDH_COUNTER_INFO ci;
+ DWORD dwSize;
+ static TCHAR szFName[] = _T("CheckCounter");
+
+ if ((rc = PdhOpenQuery(NULL, 0, &hQuery)) != ERROR_SUCCESS)
+ {
+ ReportPdhError(szFName, _T("PdhOpenQuery"), rc);
+ return -1;
+ }
+
+ if ((rc = PdhAddCounter(hQuery, pszName, 0, &hCounter)) != ERROR_SUCCESS)
+ {
+ ReportPdhError(szFName, _T("PdhAddCounter"), rc);
+ PdhCloseQuery(hQuery);
+ return -1;
+ }
+
+ dwSize = sizeof(ci);
+ if ((rc = PdhGetCounterInfo(hCounter, FALSE, &dwSize, &ci)) != ERROR_SUCCESS)
+ {
+ ReportPdhError(szFName, _T("PdhGetCounterInfo"), rc);
+ PdhCloseQuery(hQuery);
+ return -1;
+ }
+
+ PdhCloseQuery(hQuery);
+ return (ci.dwType & PERF_SIZE_LARGE) ? COUNTER_TYPE_INT64 : COUNTER_TYPE_INT32;
+}
+
+
+//
+// Add counter to set
+//
+
+WINPERF_COUNTER *AddCounter(TCHAR *pszName, int iClass, int iNumSamples, int iDataType)
+{
+ WINPERF_COUNTER *pCnt;
+ int iType;
+
+ // Check for valid class
+ if ((iClass < 0) || (iClass > 2))
+ return NULL;
+
+ // Check counter's name and get it's type
+ iType = CheckCounter(pszName);
+ if (iType == -1)
+ return NULL;
+
+ // Create new counter
+ pCnt = (WINPERF_COUNTER *)malloc(sizeof(WINPERF_COUNTER));
+ memset(pCnt, 0, sizeof(WINPERF_COUNTER));
+ pCnt->pszName = _tcsdup(pszName);
+ pCnt->wType = (iDataType == COUNTER_TYPE_AUTO) ? iType : iDataType;
+ pCnt->wNumSamples = iNumSamples;
+ pCnt->pRawValues = (PDH_RAW_COUNTER *)malloc(sizeof(PDH_RAW_COUNTER) * iNumSamples);
+ memset(pCnt->pRawValues, 0, sizeof(PDH_RAW_COUNTER) * iNumSamples);
+ switch(pCnt->wType)
+ {
+ case COUNTER_TYPE_INT32:
+ pCnt->dwFormat = PDH_FMT_LONG;
+ break;
+ case COUNTER_TYPE_INT64:
+ pCnt->dwFormat = PDH_FMT_LARGE;
+ break;
+ case COUNTER_TYPE_FLOAT:
+ pCnt->dwFormat = PDH_FMT_DOUBLE;
+ break;
+ }
+
+ // Add counter to set
+ m_cntSet[iClass].dwNumCounters++;
+ m_cntSet[iClass].ppCounterList =
+ (WINPERF_COUNTER **)realloc(m_cntSet[iClass].ppCounterList, sizeof(WINPERF_COUNTER *) * m_cntSet[iClass].dwNumCounters);
+ m_cntSet[iClass].ppCounterList[m_cntSet[iClass].dwNumCounters - 1] = pCnt;
+
+ return pCnt;
+}
+
+
+//
+// Add custom counter from configuration file
+//
+
+BOOL AddCounterFromConfig(TCHAR *pszStr)
+{
+ return FALSE;
+}
+
+
+//
+// Collector thread
+//
+
+static THREAD_RESULT THREAD_CALL CollectorThread(WINPERF_COUNTER_SET *pSet)
+{
+ HQUERY hQuery;
+ PDH_STATUS rc;
+ PDH_STATISTICS statData;
+ DWORD i, dwOKCounters = 0;
+ TCHAR szFName[] = _T("CollectorThread_X");
+
+ szFName[16] = pSet->cClass;
+ if (pSet->dwNumCounters == 0)
+ {
+ NxWriteAgentLog(EVENTLOG_INFORMATION_TYPE, "Counter set %c is empty, "
+ "collector thread for that set will not start",
+ pSet->cClass);
+ return 0;
+ }
+
+ if ((rc = PdhOpenQuery(NULL, 0, &hQuery)) != ERROR_SUCCESS)
+ {
+ ReportPdhError(szFName, _T("PdhOpenQuery"), rc);
+ return -1;
+ }
+
+ // Add counters to query
+ for(i = 0; i < pSet->dwNumCounters; i++)
+ if ((rc = PdhAddCounter(hQuery, pSet->ppCounterList[i]->pszName,
+ 0, &pSet->ppCounterList[i]->handle)) != ERROR_SUCCESS)
+ {
+ TCHAR szBuffer[1024];
+
+ NxWriteAgentLog(EVENTLOG_WARNING_TYPE, _T("%s: Unable to add counter \"%s\" to query (%s)"),
+ szFName, pSet->ppCounterList[i]->pszName, GetPdhErrorText(rc, szBuffer, 1024));
+ pSet->ppCounterList[i]->handle = NULL;
+ }
+ else
+ {
+ dwOKCounters++;
+ }
+
+ // Check if we was able to add at least one counter
+ if (dwOKCounters == 0)
+ {
+ PdhCloseQuery(hQuery);
+ NxWriteAgentLog(EVENTLOG_WARNING_TYPE, "Failed to add any counter to query for counter set %c, "
+ "collector thread for that set will not start",
+ pSet->cClass);
+ return 0;
+ }
+
+ // Main collection loop
+ while(1)
+ {
+ if (WaitForSingleObject(g_hCondShutdown, pSet->dwInterval) != WAIT_TIMEOUT)
+ break; // We got a signal
+
+ // Collect data
+ if ((rc = PdhCollectQueryData(hQuery)) != ERROR_SUCCESS)
+ ReportPdhError(szFName, _T("PdhCollectQueryData"), rc);
+
+ // Get raw values for each counter and compute average value
+ for(i = 0; i < pSet->dwNumCounters; i++)
+ if (pSet->ppCounterList[i]->handle != NULL)
+ {
+ // Get raw value into buffer
+ PdhGetRawCounterValue(pSet->ppCounterList[i]->handle, NULL,
+ &pSet->ppCounterList[i]->pRawValues[pSet->ppCounterList[i]->dwBufferPos]);
+ pSet->ppCounterList[i]->dwBufferPos++;
+ if (pSet->ppCounterList[i]->dwBufferPos == (DWORD)pSet->ppCounterList[i]->wNumSamples)
+ pSet->ppCounterList[i]->dwBufferPos = 0;
+
+ // Calculate mean value
+ if ((rc = PdhComputeCounterStatistics(pSet->ppCounterList[i]->handle,
+ pSet->ppCounterList[i]->dwFormat,
+ pSet->ppCounterList[i]->dwBufferPos,
+ pSet->ppCounterList[i]->wNumSamples,
+ pSet->ppCounterList[i]->pRawValues,
+ &statData)) != ERROR_SUCCESS)
+ {
+ ReportPdhError(szFName, _T("PdhComputeCounterStatistics"), rc);
+ }
+
+ // Update mean value in counter set
+ switch(pSet->ppCounterList[i]->wType)
+ {
+ case COUNTER_TYPE_INT32:
+ pSet->ppCounterList[i]->value.iLong = statData.mean.longValue;
+ break;
+ case COUNTER_TYPE_INT64:
+ pSet->ppCounterList[i]->value.iLarge = statData.mean.largeValue;
+ break;
+ case COUNTER_TYPE_FLOAT:
+ pSet->ppCounterList[i]->value.dFloat = statData.mean.doubleValue;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ // Cleanup
+ PdhCloseQuery(hQuery);
+ NxWriteAgentLog(EVENTLOG_INFORMATION_TYPE, "Collector thread for counter set %c terminated", pSet->cClass);
+
+ return 0;
+}
+
+
+//
+// Start collector threads
+//
+
+void StartCollectorThreads(void)
+{
+ int i;
+
+ for(i = 0; i < 3; i++)
+ ThreadCreate((THREAD_RESULT (THREAD_CALL *)(void *))CollectorThread, 0, &m_cntSet[i]);
+}
#define CFG_BUFFER_SIZE 256000
+#define WPF_ENABLE_DEFAULT_COUNTERS 0x0001
+
+
+//
+// Global variables
+//
+
+HANDLE g_hCondShutdown = NULL;
+
+
+//
+// Static variables
+//
+
+static DWORD m_dwFlags = WPF_ENABLE_DEFAULT_COUNTERS;
+
+
+//
+// List of predefined performance counters
+//
+static struct
+{
+ TCHAR *pszParamName;
+ TCHAR *pszCounterName;
+ int iClass;
+ int iNumSamples;
+ int iDataType;
+} m_counterList[] =
+{
+ { _T("System.CPU.LoadAvg"), _T("\\System\\Processor Queue Length"), 0, 60, COUNTER_TYPE_FLOAT },
+ { _T("System.CPU.LoadAvg5"), _T("\\System\\Processor Queue Length"), 0, 300, COUNTER_TYPE_FLOAT },
+ { _T("System.CPU.LoadAvg15"), _T("\\System\\Processor Queue Length"), 0, 900, COUNTER_TYPE_FLOAT },
+ { _T("System.CPU.Usage"), _T("\\Processor(_Total)\\% Processor Time"), 0, 60, COUNTER_TYPE_INT32 },
+ { _T("System.CPU.Usage5"), _T("\\Processor(_Total)\\% Processor Time"), 0, 300, COUNTER_TYPE_INT32 },
+ { _T("System.CPU.Usage15"), _T("\\Processor(_Total)\\% Processor Time"), 0, 900, COUNTER_TYPE_INT32 },
+ { NULL, NULL, 0, 0, 0 }
+};
+
//
// Value of given performance counter
}
+//
+// Value of given counter collected by one of the collector threads
+//
+
+static LONG H_CollectedCounterData(TCHAR *pszParam, TCHAR *pArg, TCHAR *pValue)
+{
+ switch(((WINPERF_COUNTER *)pArg)->wType)
+ {
+ case COUNTER_TYPE_INT32:
+ ret_int(pValue, ((WINPERF_COUNTER *)pArg)->value.iLong);
+ break;
+ case COUNTER_TYPE_INT64:
+ ret_int64(pValue, ((WINPERF_COUNTER *)pArg)->value.iLarge);
+ break;
+ case COUNTER_TYPE_FLOAT:
+ ret_double(pValue, ((WINPERF_COUNTER *)pArg)->value.dFloat);
+ break;
+ }
+ return SYSINFO_RC_SUCCESS;
+}
+
+
//
// Value of given performance counter
//
PDH_RAW_COUNTER rawData1, rawData2;
PDH_FMT_COUNTERVALUE counterValue;
PDH_STATUS rc;
- PDH_COUNTER_INFO ci;
TCHAR szCounter[MAX_PATH], szBuffer[16];
- static TCHAR szFName[] = _T("H_PdhCounterValue");
- DWORD dwSize;
+ DWORD dwType;
BOOL bUseTwoSamples = FALSE;
+ static TCHAR szFName[] = _T("H_PdhCounterValue");
if ((!NxGetParameterArg(pszParam, 1, szCounter, MAX_PATH)) ||
(!NxGetParameterArg(pszParam, 2, szBuffer, 16)))
PdhCloseQuery(hQuery);
return SYSINFO_RC_ERROR;
}
- PdhGetRawCounterValue(hCounter, NULL, &rawData1);
+ PdhGetRawCounterValue(hCounter, &dwType, &rawData1);
// Get second sample if required
if (bUseTwoSamples)
PdhGetRawCounterValue(hCounter, NULL, &rawData2);
}
- dwSize = sizeof(ci);
- if ((rc = PdhGetCounterInfo(hCounter, FALSE, &dwSize, &ci)) != ERROR_SUCCESS)
- {
- ReportPdhError(szFName, _T("PdhGetCounterInfo"), rc);
- PdhCloseQuery(hQuery);
- return SYSINFO_RC_ERROR;
- }
-
- if (ci.dwType & PERF_SIZE_LARGE)
+ if (dwType & PERF_SIZE_LARGE)
{
if (bUseTwoSamples)
PdhCalculateCounterFromRawValue(hCounter, PDH_FMT_LARGE,
}
+//
+// Handler for subagent unload
+//
+
+static void OnUnload(void)
+{
+ if (g_hCondShutdown != NULL)
+ SetEvent(g_hCondShutdown);
+ Sleep(500);
+}
+
+
//
// Subagent information
//
static NETXMS_SUBAGENT_INFO m_info =
{
- "WinPerf", 0x01000000, NULL,
- sizeof(m_parameters) / sizeof(NETXMS_SUBAGENT_PARAM),
- m_parameters,
+ "WinPerf", 0x01000000, OnUnload,
+ 0, NULL,
sizeof(m_enums) / sizeof(NETXMS_SUBAGENT_ENUM),
m_enums
};
+//
+// Add new parameter to list
+//
+
+static BOOL AddParameter(TCHAR *pszName, LONG (* fpHandler)(TCHAR *, TCHAR *, TCHAR *), TCHAR *pArg)
+{
+ DWORD i;
+
+ for(i = 0; i < m_info.dwNumParameters; i++)
+ if (!_tcsicmp(pszName, m_info.pParamList[i].szName))
+ break;
+
+ if (i == m_info.dwNumParameters)
+ {
+ // Extend list
+ m_info.dwNumParameters++;
+ m_info.pParamList =
+ (NETXMS_SUBAGENT_PARAM *)realloc(m_info.pParamList,
+ sizeof(NETXMS_SUBAGENT_PARAM) * m_info.dwNumParameters);
+ }
+
+ _tcsncpy(m_info.pParamList[i].szName, pszName, MAX_PARAM_NAME);
+ m_info.pParamList[i].fpHandler = fpHandler;
+ m_info.pParamList[i].pArg = pArg;
+
+ return TRUE;
+}
+
+
+//
+// Add predefined counters
+//
+
+static void AddPredefinedCounters(void)
+{
+ int i;
+ WINPERF_COUNTER *pCnt;
+
+ for(i = 0; m_counterList[i].pszParamName != NULL; i++)
+ {
+ pCnt = AddCounter(m_counterList[i].pszCounterName, m_counterList[i].iClass,
+ m_counterList[i].iNumSamples, m_counterList[i].iDataType);
+ if (pCnt != NULL)
+ AddParameter(m_counterList[i].pszParamName, H_CollectedCounterData, (TCHAR *)pCnt);
+ }
+}
+
+
//
// Configuration file template
//
static NX_CFG_TEMPLATE cfgTemplate[] =
{
- { "Counter", CT_STRING_LIST, ',', 0, CFG_BUFFER_SIZE, 0, NULL },
- { "", CT_END_OF_LIST, 0, 0, 0, 0, NULL }
+ { _T("Counter"), CT_STRING_LIST, _T('\n'), 0, CFG_BUFFER_SIZE, 0, NULL },
+ { _T("EnableDefaultCounters"), CT_BOOLEAN, 0, 0, WPF_ENABLE_DEFAULT_COUNTERS, 0, &m_dwFlags },
+ { _T(""), CT_END_OF_LIST, 0, 0, 0, 0, NULL }
};
{
DWORD dwResult;
+ // Init parameters list
+ m_info.dwNumParameters = sizeof(m_parameters) / sizeof(NETXMS_SUBAGENT_PARAM);
+ m_info.pParamList = (NETXMS_SUBAGENT_PARAM *)nx_memdup(m_parameters, sizeof(m_parameters));
+
// Load configuration
cfgTemplate[0].pBuffer = malloc(CFG_BUFFER_SIZE);
dwResult = NxLoadConfig(pszConfigFile, _T("WinPerf"), cfgTemplate, FALSE);
if (dwResult == NXCFG_ERR_OK)
{
+ TCHAR *pItem, *pEnd;
+
+ // Parse counter list
+ for(pItem = (TCHAR *)cfgTemplate[0].pBuffer; *pItem != 0; pItem = pEnd + 1)
+ {
+ pEnd = _tcschr(pItem, _T('\n'));
+ if (pEnd != NULL)
+ *pEnd = 0;
+ StrStrip(pItem);
+ if (!AddCounterFromConfig(pItem))
+ NxWriteAgentLog(EVENTLOG_WARNING_TYPE,
+ _T("Unable to add counter from configuration file. "
+ "Original configuration record: %s",), pItem);
+ }
+
+ if (m_dwFlags & WPF_ENABLE_DEFAULT_COUNTERS)
+ AddPredefinedCounters();
+
+ // Create shitdown condition object
+ g_hCondShutdown = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ StartCollectorThreads();
}
free(cfgTemplate[0].pBuffer);
*ppInfo = &m_info;
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
+SOURCE=.\collect.cpp
+# End Source File
+# Begin Source File
+
SOURCE=.\tools.cpp
# End Source File
# Begin Source File
# End Source File
# Begin Source File
+SOURCE=..\..\..\..\include\nms_threads.h
+# End Source File
+# Begin Source File
+
SOURCE=..\..\..\..\include\nms_util.h
# End Source File
# Begin Source File
#include <windows.h>
#include <nms_common.h>
#include <nms_agent.h>
+#include <nms_threads.h>
#include <pdh.h>
#include <pdhmsg.h>
+//
+// Counter types
+//
+
+#define COUNTER_TYPE_AUTO 0
+#define COUNTER_TYPE_INT32 1
+#define COUNTER_TYPE_INT64 2
+#define COUNTER_TYPE_FLOAT 3
+
+
+//
+// Counter structure
+//
+
+struct WINPERF_COUNTER
+{
+ TCHAR *pszName;
+ WORD wType;
+ WORD wNumSamples;
+ union
+ {
+ LONG iLong;
+ INT64 iLarge;
+ double dFloat;
+ } value;
+ DWORD dwFormat; // Format code for PDH functions
+ PDH_RAW_COUNTER *pRawValues;
+ DWORD dwBufferPos;
+ HCOUNTER handle;
+};
+
+
+//
+// Counter set structure
+//
+
+struct WINPERF_COUNTER_SET
+{
+ DWORD dwInterval; // Interval beetween samples in milliseconds
+ DWORD dwNumCounters;
+ WINPERF_COUNTER **ppCounterList;
+ TCHAR cClass;
+};
+
+
//
// Functions
//
+void StartCollectorThreads(void);
TCHAR *GetPdhErrorText(DWORD dwError, TCHAR *pszBuffer, int iBufferSize);
void ReportPdhError(TCHAR *pszFunction, TCHAR *pszPdhCall, PDH_STATUS dwError);
+WINPERF_COUNTER *AddCounter(TCHAR *pszName, int iClass, int iNumSamples, int iDataType);
+BOOL AddCounterFromConfig(TCHAR *pszStr);
+
+
+//
+// Global variables
+//
+
+extern HANDLE g_hCondShutdown;
+
#endif