- Implemented collector thread
[public/netxms.git] / src / agent / subagents / winperf / winperf.cpp
CommitLineData
e49f205f
VK
1/*
2** Windows Performance NetXMS subagent
3** Copyright (C) 2004 Victor Kirhenshtein
4**
5** This program is free software; you can redistribute it and/or modify
6** it under the terms of the GNU General Public License as published by
7** the Free Software Foundation; either version 2 of the License, or
8** (at your option) any later version.
9**
10** This program is distributed in the hope that it will be useful,
11** but WITHOUT ANY WARRANTY; without even the implied warranty of
12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13** GNU General Public License for more details.
14**
15** You should have received a copy of the GNU General Public License
16** along with this program; if not, write to the Free Software
17** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18**
19** $module: winperf.cpp
20**
21**/
22
23#include "winperf.h"
24
25
26//
5556a0d4 27// Constants
e49f205f
VK
28//
29
5556a0d4
VK
30#define CFG_BUFFER_SIZE 256000
31
18f25f90
VK
32#define WPF_ENABLE_DEFAULT_COUNTERS 0x0001
33
34
35//
36// Global variables
37//
38
39HANDLE g_hCondShutdown = NULL;
40
41
42//
43// Static variables
44//
45
46static DWORD m_dwFlags = WPF_ENABLE_DEFAULT_COUNTERS;
47
48
49//
50// List of predefined performance counters
51//
52static struct
53{
54 TCHAR *pszParamName;
55 TCHAR *pszCounterName;
56 int iClass;
57 int iNumSamples;
58 int iDataType;
59} m_counterList[] =
60{
61 { _T("System.CPU.LoadAvg"), _T("\\System\\Processor Queue Length"), 0, 60, COUNTER_TYPE_FLOAT },
62 { _T("System.CPU.LoadAvg5"), _T("\\System\\Processor Queue Length"), 0, 300, COUNTER_TYPE_FLOAT },
63 { _T("System.CPU.LoadAvg15"), _T("\\System\\Processor Queue Length"), 0, 900, COUNTER_TYPE_FLOAT },
64 { _T("System.CPU.Usage"), _T("\\Processor(_Total)\\% Processor Time"), 0, 60, COUNTER_TYPE_INT32 },
65 { _T("System.CPU.Usage5"), _T("\\Processor(_Total)\\% Processor Time"), 0, 300, COUNTER_TYPE_INT32 },
66 { _T("System.CPU.Usage15"), _T("\\Processor(_Total)\\% Processor Time"), 0, 900, COUNTER_TYPE_INT32 },
67 { NULL, NULL, 0, 0, 0 }
68};
69
5556a0d4
VK
70
71//
72// Value of given performance counter
73//
74
75static LONG H_PdhVersion(TCHAR *pszParam, TCHAR *pArg, TCHAR *pValue)
76{
77 DWORD dwVersion;
78
79 if (PdhGetDllVersion(&dwVersion) != ERROR_SUCCESS)
80 return SYSINFO_RC_ERROR;
81 ret_uint(pValue, dwVersion);
82 return SYSINFO_RC_SUCCESS;
83}
84
85
18f25f90
VK
86//
87// Value of given counter collected by one of the collector threads
88//
89
90static LONG H_CollectedCounterData(TCHAR *pszParam, TCHAR *pArg, TCHAR *pValue)
91{
92 switch(((WINPERF_COUNTER *)pArg)->wType)
93 {
94 case COUNTER_TYPE_INT32:
95 ret_int(pValue, ((WINPERF_COUNTER *)pArg)->value.iLong);
96 break;
97 case COUNTER_TYPE_INT64:
98 ret_int64(pValue, ((WINPERF_COUNTER *)pArg)->value.iLarge);
99 break;
100 case COUNTER_TYPE_FLOAT:
101 ret_double(pValue, ((WINPERF_COUNTER *)pArg)->value.dFloat);
102 break;
103 }
104 return SYSINFO_RC_SUCCESS;
105}
106
107
5556a0d4
VK
108//
109// Value of given performance counter
110//
111
112static LONG H_PdhCounterValue(TCHAR *pszParam, TCHAR *pArg, TCHAR *pValue)
e49f205f
VK
113{
114 HQUERY hQuery;
6fe3e32b 115 HCOUNTER hCounter;
c7939cfa 116 PDH_RAW_COUNTER rawData1, rawData2;
6fe3e32b 117 PDH_FMT_COUNTERVALUE counterValue;
e49f205f 118 PDH_STATUS rc;
c7939cfa 119 TCHAR szCounter[MAX_PATH], szBuffer[16];
18f25f90 120 DWORD dwType;
c7939cfa 121 BOOL bUseTwoSamples = FALSE;
18f25f90 122 static TCHAR szFName[] = _T("H_PdhCounterValue");
e49f205f 123
c7939cfa
VK
124 if ((!NxGetParameterArg(pszParam, 1, szCounter, MAX_PATH)) ||
125 (!NxGetParameterArg(pszParam, 2, szBuffer, 16)))
ff5e8be5 126 return SYSINFO_RC_UNSUPPORTED;
e49f205f 127
c7939cfa
VK
128 bUseTwoSamples = _tcstol(szBuffer, NULL, 0) ? TRUE : FALSE;
129
e49f205f
VK
130 if ((rc = PdhOpenQuery(NULL, 0, &hQuery)) != ERROR_SUCCESS)
131 {
132 ReportPdhError(szFName, _T("PdhOpenQuery"), rc);
133 return SYSINFO_RC_ERROR;
134 }
135
6fe3e32b 136 if ((rc = PdhAddCounter(hQuery, szCounter, 0, &hCounter)) != ERROR_SUCCESS)
e49f205f 137 {
6fe3e32b 138 ReportPdhError(szFName, _T("PdhAddCounter"), rc);
e49f205f
VK
139 PdhCloseQuery(hQuery);
140 return SYSINFO_RC_UNSUPPORTED;
141 }
142
c7939cfa 143 // Get first sample
6fe3e32b 144 if ((rc = PdhCollectQueryData(hQuery)) != ERROR_SUCCESS)
e49f205f 145 {
6fe3e32b 146 ReportPdhError(szFName, _T("PdhCollectQueryData"), rc);
e49f205f
VK
147 PdhCloseQuery(hQuery);
148 return SYSINFO_RC_ERROR;
149 }
18f25f90 150 PdhGetRawCounterValue(hCounter, &dwType, &rawData1);
c7939cfa
VK
151
152 // Get second sample if required
153 if (bUseTwoSamples)
154 {
155 Sleep(1000); // We will take second sample after one second
156 if ((rc = PdhCollectQueryData(hQuery)) != ERROR_SUCCESS)
157 {
158 ReportPdhError(szFName, _T("PdhCollectQueryData"), rc);
159 PdhCloseQuery(hQuery);
160 return SYSINFO_RC_ERROR;
161 }
162 PdhGetRawCounterValue(hCounter, NULL, &rawData2);
163 }
e49f205f 164
18f25f90 165 if (dwType & PERF_SIZE_LARGE)
6fe3e32b 166 {
c7939cfa
VK
167 if (bUseTwoSamples)
168 PdhCalculateCounterFromRawValue(hCounter, PDH_FMT_LARGE,
169 &rawData2, &rawData1, &counterValue);
170 else
171 PdhCalculateCounterFromRawValue(hCounter, PDH_FMT_LARGE,
172 &rawData1, NULL, &counterValue);
6fe3e32b
VK
173 ret_int64(pValue, counterValue.largeValue);
174 }
175 else
176 {
c7939cfa
VK
177 if (bUseTwoSamples)
178 PdhCalculateCounterFromRawValue(hCounter, PDH_FMT_LONG,
179 &rawData2, &rawData1, &counterValue);
180 else
181 PdhCalculateCounterFromRawValue(hCounter, PDH_FMT_LONG,
182 &rawData1, NULL, &counterValue);
183 ret_int(pValue, counterValue.longValue);
6fe3e32b 184 }
e49f205f
VK
185 PdhCloseQuery(hQuery);
186 return SYSINFO_RC_SUCCESS;
187}
188
189
190//
191// List of available performance objects
192//
193
5556a0d4 194static LONG H_PdhObjects(TCHAR *pszParam, TCHAR *pArg, NETXMS_VALUES_LIST *pValue)
e49f205f
VK
195{
196 TCHAR *pszObject, *pszObjList, szHostName[256];
197 LONG iResult = SYSINFO_RC_ERROR;
6fe3e32b 198 PDH_STATUS rc;
e49f205f
VK
199 DWORD dwSize;
200
201 dwSize = 256;
202 if (GetComputerName(szHostName, &dwSize))
203 {
204 dwSize = 256000;
205 pszObjList = (TCHAR *)malloc(sizeof(TCHAR) * dwSize);
6fe3e32b
VK
206 if ((rc = PdhEnumObjects(NULL, szHostName, pszObjList, &dwSize,
207 PERF_DETAIL_WIZARD, TRUE)) == ERROR_SUCCESS)
e49f205f
VK
208 {
209 for(pszObject = pszObjList; *pszObject != 0; pszObject += _tcslen(pszObject) + 1)
210 NxAddResultString(pValue, pszObject);
211 iResult = SYSINFO_RC_SUCCESS;
212 }
213 else
214 {
215 ReportPdhError(_T("H_PdhObjects"), _T("PdhEnumObjects"), rc);
216 }
217 free(pszObjList);
218 }
219 return iResult;
220}
221
222
223//
224// List of available performance items for given object
225//
226
5556a0d4 227static LONG H_PdhObjectItems(TCHAR *pszParam, TCHAR *pArg, NETXMS_VALUES_LIST *pValue)
e49f205f 228{
6fe3e32b 229 TCHAR *pszElement, *pszCounterList, *pszInstanceList, szHostName[256], szObject[256];
e49f205f
VK
230 LONG iResult = SYSINFO_RC_ERROR;
231 DWORD dwSize1, dwSize2;
232 PDH_STATUS rc;
233
234 NxGetParameterArg(pszParam, 1, szObject, 256);
235 if (szObject[0] != 0)
236 {
237 dwSize1 = 256;
238 if (GetComputerName(szHostName, &dwSize1))
239 {
240 dwSize1 = dwSize2 = 256000;
6fe3e32b 241 pszCounterList = (TCHAR *)malloc(sizeof(TCHAR) * dwSize1);
e49f205f
VK
242 pszInstanceList = (TCHAR *)malloc(sizeof(TCHAR) * dwSize2);
243 rc = PdhEnumObjectItems(NULL, szHostName, szObject,
6fe3e32b 244 pszCounterList, &dwSize1, pszInstanceList, &dwSize2,
e49f205f
VK
245 PERF_DETAIL_WIZARD, 0);
246 if ((rc == ERROR_SUCCESS) || (rc == PDH_MORE_DATA))
247 {
6fe3e32b 248 for(pszElement = (pArg[0] == _T('C')) ? pszCounterList : pszInstanceList;
e49f205f
VK
249 *pszElement != 0; pszElement += _tcslen(pszElement) + 1)
250 NxAddResultString(pValue, pszElement);
251 iResult = SYSINFO_RC_SUCCESS;
252 }
253 else
254 {
255 ReportPdhError(_T("H_PdhObjectItems"), _T("PdhEnumObjectItems"), rc);
256 }
6fe3e32b
VK
257 free(pszCounterList);
258 free(pszInstanceList);
e49f205f
VK
259 }
260 }
261 else
262 {
263 iResult = SYSINFO_RC_UNSUPPORTED;
264 }
265 return iResult;
266}
267
268
18f25f90
VK
269//
270// Handler for subagent unload
271//
272
273static void OnUnload(void)
274{
275 if (g_hCondShutdown != NULL)
276 SetEvent(g_hCondShutdown);
277 Sleep(500);
278}
279
280
e49f205f
VK
281//
282// Subagent information
283//
284
285static NETXMS_SUBAGENT_PARAM m_parameters[] =
286{
5556a0d4
VK
287 { _T("PDH.CounterValue(*)"), H_PdhCounterValue, NULL },
288 { _T("PDH.Version"), H_PdhVersion, NULL }
e49f205f
VK
289};
290static NETXMS_SUBAGENT_ENUM m_enums[] =
291{
6fe3e32b
VK
292 { _T("PDH.ObjectCounters(*)"), H_PdhObjectItems, _T("C") },
293 { _T("PDH.ObjectInstances(*)"), H_PdhObjectItems, _T("I") },
294 { _T("PDH.Objects"), H_PdhObjects, NULL }
e49f205f
VK
295};
296
297static NETXMS_SUBAGENT_INFO m_info =
298{
18f25f90
VK
299 "WinPerf", 0x01000000, OnUnload,
300 0, NULL,
e49f205f
VK
301 sizeof(m_enums) / sizeof(NETXMS_SUBAGENT_ENUM),
302 m_enums
303};
304
305
18f25f90
VK
306//
307// Add new parameter to list
308//
309
310static BOOL AddParameter(TCHAR *pszName, LONG (* fpHandler)(TCHAR *, TCHAR *, TCHAR *), TCHAR *pArg)
311{
312 DWORD i;
313
314 for(i = 0; i < m_info.dwNumParameters; i++)
315 if (!_tcsicmp(pszName, m_info.pParamList[i].szName))
316 break;
317
318 if (i == m_info.dwNumParameters)
319 {
320 // Extend list
321 m_info.dwNumParameters++;
322 m_info.pParamList =
323 (NETXMS_SUBAGENT_PARAM *)realloc(m_info.pParamList,
324 sizeof(NETXMS_SUBAGENT_PARAM) * m_info.dwNumParameters);
325 }
326
327 _tcsncpy(m_info.pParamList[i].szName, pszName, MAX_PARAM_NAME);
328 m_info.pParamList[i].fpHandler = fpHandler;
329 m_info.pParamList[i].pArg = pArg;
330
331 return TRUE;
332}
333
334
335//
336// Add predefined counters
337//
338
339static void AddPredefinedCounters(void)
340{
341 int i;
342 WINPERF_COUNTER *pCnt;
343
344 for(i = 0; m_counterList[i].pszParamName != NULL; i++)
345 {
346 pCnt = AddCounter(m_counterList[i].pszCounterName, m_counterList[i].iClass,
347 m_counterList[i].iNumSamples, m_counterList[i].iDataType);
348 if (pCnt != NULL)
349 AddParameter(m_counterList[i].pszParamName, H_CollectedCounterData, (TCHAR *)pCnt);
350 }
351}
352
353
5556a0d4
VK
354//
355// Configuration file template
356//
357
358static NX_CFG_TEMPLATE cfgTemplate[] =
359{
18f25f90
VK
360 { _T("Counter"), CT_STRING_LIST, _T('\n'), 0, CFG_BUFFER_SIZE, 0, NULL },
361 { _T("EnableDefaultCounters"), CT_BOOLEAN, 0, 0, WPF_ENABLE_DEFAULT_COUNTERS, 0, &m_dwFlags },
362 { _T(""), CT_END_OF_LIST, 0, 0, 0, 0, NULL }
5556a0d4
VK
363};
364
365
e49f205f
VK
366//
367// Entry point for NetXMS agent
368//
369
370extern "C" BOOL __declspec(dllexport) __cdecl
371 NxSubAgentInit(NETXMS_SUBAGENT_INFO **ppInfo, TCHAR *pszConfigFile)
372{
5556a0d4
VK
373 DWORD dwResult;
374
18f25f90
VK
375 // Init parameters list
376 m_info.dwNumParameters = sizeof(m_parameters) / sizeof(NETXMS_SUBAGENT_PARAM);
377 m_info.pParamList = (NETXMS_SUBAGENT_PARAM *)nx_memdup(m_parameters, sizeof(m_parameters));
378
5556a0d4
VK
379 // Load configuration
380 cfgTemplate[0].pBuffer = malloc(CFG_BUFFER_SIZE);
381 dwResult = NxLoadConfig(pszConfigFile, _T("WinPerf"), cfgTemplate, FALSE);
382 if (dwResult == NXCFG_ERR_OK)
383 {
18f25f90
VK
384 TCHAR *pItem, *pEnd;
385
386 // Parse counter list
387 for(pItem = (TCHAR *)cfgTemplate[0].pBuffer; *pItem != 0; pItem = pEnd + 1)
388 {
389 pEnd = _tcschr(pItem, _T('\n'));
390 if (pEnd != NULL)
391 *pEnd = 0;
392 StrStrip(pItem);
393 if (!AddCounterFromConfig(pItem))
394 NxWriteAgentLog(EVENTLOG_WARNING_TYPE,
395 _T("Unable to add counter from configuration file. "
396 "Original configuration record: %s",), pItem);
397 }
398
399 if (m_dwFlags & WPF_ENABLE_DEFAULT_COUNTERS)
400 AddPredefinedCounters();
401
402 // Create shitdown condition object
403 g_hCondShutdown = CreateEvent(NULL, TRUE, FALSE, NULL);
404
405 StartCollectorThreads();
5556a0d4
VK
406 }
407 free(cfgTemplate[0].pBuffer);
e49f205f 408 *ppInfo = &m_info;
5556a0d4 409 return dwResult == NXCFG_ERR_OK;
e49f205f
VK
410}
411
412
413//
414// DLL entry point
415//
416
417BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
418{
419 return TRUE;
420}