- Implemented collector thread
authorVictor Kirhenshtein <victor@netxms.org>
Tue, 5 Oct 2004 09:55:04 +0000 (09:55 +0000)
committerVictor Kirhenshtein <victor@netxms.org>
Tue, 5 Oct 2004 09:55:04 +0000 (09:55 +0000)
- Added predefined parameters System.CPU.Usage and System.CPU.LoadAvg
- Handler for PDH.CounterValue(*) optimized

.gitattributes
src/agent/subagents/winperf/collect.cpp [new file with mode: 0644]
src/agent/subagents/winperf/winperf.cpp
src/agent/subagents/winperf/winperf.dsp
src/agent/subagents/winperf/winperf.h

index 08a1f7b..04eb34f 100644 (file)
@@ -108,6 +108,7 @@ src/agent/subagents/skeleton/Makefile.nw -text
 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
diff --git a/src/agent/subagents/winperf/collect.cpp b/src/agent/subagents/winperf/collect.cpp
new file mode 100644 (file)
index 0000000..5d9ac7b
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+** 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]);
+}
index b09b612..312d778 100644 (file)
 
 #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
@@ -45,6 +83,28 @@ static LONG H_PdhVersion(TCHAR *pszParam, TCHAR *pArg, TCHAR *pValue)
 }
 
 
+//
+// 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
 //
@@ -56,11 +116,10 @@ static LONG H_PdhCounterValue(TCHAR *pszParam, TCHAR *pArg, TCHAR *pValue)
    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)))
@@ -88,7 +147,7 @@ static LONG H_PdhCounterValue(TCHAR *pszParam, TCHAR *pArg, TCHAR *pValue)
       PdhCloseQuery(hQuery);
       return SYSINFO_RC_ERROR;
    }
-   PdhGetRawCounterValue(hCounter, NULL, &rawData1);
+   PdhGetRawCounterValue(hCounter, &dwType, &rawData1);
 
    // Get second sample if required
    if (bUseTwoSamples)
@@ -103,15 +162,7 @@ static LONG H_PdhCounterValue(TCHAR *pszParam, TCHAR *pArg, TCHAR *pValue)
       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,
@@ -215,6 +266,18 @@ static LONG H_PdhObjectItems(TCHAR *pszParam, TCHAR *pArg, NETXMS_VALUES_LIST *p
 }
 
 
+//
+// Handler for subagent unload
+//
+
+static void OnUnload(void)
+{
+   if (g_hCondShutdown != NULL)
+      SetEvent(g_hCondShutdown);
+   Sleep(500);
+}
+
+
 //
 // Subagent information
 //
@@ -233,22 +296,70 @@ static NETXMS_SUBAGENT_ENUM m_enums[] =
 
 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 }
 };
 
 
@@ -261,11 +372,37 @@ extern "C" BOOL __declspec(dllexport) __cdecl
 {
    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;
index 66541bb..16c6d03 100644 (file)
@@ -97,6 +97,10 @@ PostBuild_Cmds=copy Debug\winperf.nsm ..\..\..\..\bin
 # 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
@@ -113,6 +117,10 @@ SOURCE=..\..\..\..\include\nms_agent.h
 # 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
index 45776fe..e8bb914 100644 (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