XML configs implemented
[public/netxms.git] / src / agent / subagents / winperf / winperf.cpp
1 /*
2 ** Windows Performance NetXMS subagent
3 ** Copyright (C) 2004, 2005, 2006, 2007 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 ** File: winperf.cpp
20 **
21 **/
22
23 #include "winperf.h"
24
25
26 //
27 // Constants
28 //
29
30 #define MAX_CPU_COUNT 64
31
32 #define WPF_ENABLE_DEFAULT_COUNTERS 0x0001
33
34
35 //
36 // Global variables
37 //
38
39 HANDLE g_hCondShutdown = NULL;
40
41
42 //
43 // Static variables
44 //
45
46 static DWORD m_dwFlags = WPF_ENABLE_DEFAULT_COUNTERS;
47 static DWORD m_dwNumCPU = 1;
48 static WINPERF_COUNTER *m_pProcessorCounters[MAX_CPU_COUNT];
49 static WINPERF_COUNTER *m_pProcessorCounters5[MAX_CPU_COUNT];
50 static WINPERF_COUNTER *m_pProcessorCounters15[MAX_CPU_COUNT];
51
52
53 //
54 // List of predefined performance counters
55 //
56 static struct
57 {
58 TCHAR *pszParamName;
59 TCHAR *pszCounterName;
60 int iClass;
61 int iNumSamples;
62 int iDataType;
63 TCHAR *pszDescription;
64 int iDCIDataType;
65 } m_counterList[] =
66 {
67 { _T("System.CPU.LoadAvg"), _T("\\System\\Processor Queue Length"), 0,
68 60, COUNTER_TYPE_FLOAT, _T("Average CPU load for last minute"), DCI_DT_FLOAT },
69 { _T("System.CPU.LoadAvg5"), _T("\\System\\Processor Queue Length"), 0,
70 300, COUNTER_TYPE_FLOAT, _T("Average CPU load for last 5 minutes"), DCI_DT_FLOAT },
71 { _T("System.CPU.LoadAvg15"), _T("\\System\\Processor Queue Length"), 0,
72 900, COUNTER_TYPE_FLOAT, _T("Average CPU load for last 15 minutes"), DCI_DT_FLOAT },
73 { _T("System.CPU.Usage"), _T("\\Processor(_Total)\\% Processor Time"), 0,
74 60, COUNTER_TYPE_INT32, _T("Average CPU utilization for last minute"), DCI_DT_INT },
75 { _T("System.CPU.Usage5"), _T("\\Processor(_Total)\\% Processor Time"), 0,
76 300, COUNTER_TYPE_INT32, _T("Average CPU utilization for last 5 minutes"), DCI_DT_INT },
77 { _T("System.CPU.Usage15"), _T("\\Processor(_Total)\\% Processor Time"), 0,
78 900, COUNTER_TYPE_INT32, _T("Average CPU utilization for last 15 minutes"), DCI_DT_INT },
79 { _T("System.IO.DiskQueue"), _T("\\PhysicalDisk(_Total)\\Avg. Disk Queue Length"), 0,
80 60, COUNTER_TYPE_FLOAT, _T("Average disk queue length for last minute"), DCI_DT_FLOAT },
81 { _T("System.IO.DiskTime"), _T("\\PhysicalDisk(_Total)\\% Disk Time"), 0,
82 60, COUNTER_TYPE_FLOAT, _T("Average disk busy time for last minute"), DCI_DT_FLOAT },
83 { NULL, NULL, 0, 0, 0, NULL, 0 }
84 };
85
86
87 //
88 // Value of given performance counter
89 //
90
91 static LONG H_PdhVersion(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *pValue)
92 {
93 DWORD dwVersion;
94
95 if (PdhGetDllVersion(&dwVersion) != ERROR_SUCCESS)
96 return SYSINFO_RC_ERROR;
97 ret_uint(pValue, dwVersion);
98 return SYSINFO_RC_SUCCESS;
99 }
100
101
102 //
103 // Value of CPU utilization counter
104 //
105
106 static LONG H_CPUUsage(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *pValue)
107 {
108 LONG nProcessor, nRet = SYSINFO_RC_SUCCESS;
109 TCHAR *pEnd, szBuffer[16];
110
111 if (!NxGetParameterArg(pszParam, 1, szBuffer, 16))
112 return SYSINFO_RC_UNSUPPORTED;
113
114 nProcessor = _tcstol(szBuffer, &pEnd, 0);
115 if ((*pEnd != 0) || (nProcessor < 0) || (nProcessor >= (LONG)m_dwNumCPU))
116 return SYSINFO_RC_UNSUPPORTED;
117
118 switch(pArg[0])
119 {
120 case _T('1'): // System.CPU.Usage(*)
121 if (m_pProcessorCounters[nProcessor] != NULL)
122 ret_int(pValue, m_pProcessorCounters[nProcessor]->value.iLong);
123 else
124 nRet = SYSINFO_RC_ERROR;
125 break;
126 case _T('2'): // System.CPU.Usage5(*)
127 if (m_pProcessorCounters5[nProcessor] != NULL)
128 ret_int(pValue, m_pProcessorCounters5[nProcessor]->value.iLong);
129 else
130 nRet = SYSINFO_RC_ERROR;
131 break;
132 case _T('3'): // System.CPU.Usage15(*)
133 if (m_pProcessorCounters15[nProcessor] != NULL)
134 ret_int(pValue, m_pProcessorCounters15[nProcessor]->value.iLong);
135 else
136 nRet = SYSINFO_RC_ERROR;
137 break;
138 default:
139 nRet = SYSINFO_RC_UNSUPPORTED;
140 break;
141 }
142
143 return nRet;
144 }
145
146
147 //
148 // Value of given counter collected by one of the collector threads
149 //
150
151 LONG H_CollectedCounterData(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *pValue)
152 {
153 switch(((WINPERF_COUNTER *)pArg)->wType)
154 {
155 case COUNTER_TYPE_INT32:
156 ret_int(pValue, ((WINPERF_COUNTER *)pArg)->value.iLong);
157 break;
158 case COUNTER_TYPE_INT64:
159 ret_int64(pValue, ((WINPERF_COUNTER *)pArg)->value.iLarge);
160 break;
161 case COUNTER_TYPE_FLOAT:
162 ret_double(pValue, ((WINPERF_COUNTER *)pArg)->value.dFloat);
163 break;
164 }
165 return SYSINFO_RC_SUCCESS;
166 }
167
168
169 //
170 // Value of given performance counter
171 //
172
173 static LONG H_PdhCounterValue(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *pValue)
174 {
175 HQUERY hQuery;
176 HCOUNTER hCounter;
177 PDH_RAW_COUNTER rawData1, rawData2;
178 PDH_FMT_COUNTERVALUE counterValue;
179 PDH_STATUS rc;
180 TCHAR szCounter[MAX_PATH], szBuffer[16];
181 DWORD dwType;
182 BOOL bUseTwoSamples = FALSE;
183 static TCHAR szFName[] = _T("H_PdhCounterValue");
184
185 if (pArg == NULL) // Normal call
186 {
187 if ((!NxGetParameterArg(pszParam, 1, szCounter, MAX_PATH)) ||
188 (!NxGetParameterArg(pszParam, 2, szBuffer, 16)))
189 return SYSINFO_RC_UNSUPPORTED;
190
191 bUseTwoSamples = _tcstol(szBuffer, NULL, 0) ? TRUE : FALSE;
192 }
193 else // Call from H_CounterAlias
194 {
195 nx_strncpy(szCounter, pArg, MAX_PATH);
196 }
197
198 if ((rc = PdhOpenQuery(NULL, 0, &hQuery)) != ERROR_SUCCESS)
199 {
200 ReportPdhError(szFName, _T("PdhOpenQuery"), rc);
201 return SYSINFO_RC_ERROR;
202 }
203
204 if ((rc = PdhAddCounter(hQuery, szCounter, 0, &hCounter)) != ERROR_SUCCESS)
205 {
206 ReportPdhError(szFName, _T("PdhAddCounter"), rc);
207 PdhCloseQuery(hQuery);
208 return SYSINFO_RC_UNSUPPORTED;
209 }
210
211 // Get first sample
212 if ((rc = PdhCollectQueryData(hQuery)) != ERROR_SUCCESS)
213 {
214 ReportPdhError(szFName, _T("PdhCollectQueryData"), rc);
215 PdhCloseQuery(hQuery);
216 return SYSINFO_RC_ERROR;
217 }
218 PdhGetRawCounterValue(hCounter, &dwType, &rawData1);
219
220 // Get second sample if required
221 if (bUseTwoSamples)
222 {
223 Sleep(1000); // We will take second sample after one second
224 if ((rc = PdhCollectQueryData(hQuery)) != ERROR_SUCCESS)
225 {
226 ReportPdhError(szFName, _T("PdhCollectQueryData"), rc);
227 PdhCloseQuery(hQuery);
228 return SYSINFO_RC_ERROR;
229 }
230 PdhGetRawCounterValue(hCounter, NULL, &rawData2);
231 }
232
233 if (dwType & PERF_SIZE_LARGE)
234 {
235 if (bUseTwoSamples)
236 PdhCalculateCounterFromRawValue(hCounter, PDH_FMT_LARGE,
237 &rawData2, &rawData1, &counterValue);
238 else
239 PdhCalculateCounterFromRawValue(hCounter, PDH_FMT_LARGE,
240 &rawData1, NULL, &counterValue);
241 ret_int64(pValue, counterValue.largeValue);
242 }
243 else
244 {
245 if (bUseTwoSamples)
246 PdhCalculateCounterFromRawValue(hCounter, PDH_FMT_LONG,
247 &rawData2, &rawData1, &counterValue);
248 else
249 PdhCalculateCounterFromRawValue(hCounter, PDH_FMT_LONG,
250 &rawData1, NULL, &counterValue);
251 ret_int(pValue, counterValue.longValue);
252 }
253 PdhCloseQuery(hQuery);
254 return SYSINFO_RC_SUCCESS;
255 }
256
257
258 //
259 // List of available performance objects
260 //
261
262 static LONG H_PdhObjects(const TCHAR *pszParam, const TCHAR *pArg, NETXMS_VALUES_LIST *pValue)
263 {
264 TCHAR *pszObject, *pszObjList, szHostName[256];
265 LONG iResult = SYSINFO_RC_ERROR;
266 PDH_STATUS rc;
267 DWORD dwSize;
268
269 dwSize = 256;
270 if (GetComputerName(szHostName, &dwSize))
271 {
272 dwSize = 256000;
273 pszObjList = (TCHAR *)malloc(sizeof(TCHAR) * dwSize);
274 if ((rc = PdhEnumObjects(NULL, szHostName, pszObjList, &dwSize,
275 PERF_DETAIL_WIZARD, TRUE)) == ERROR_SUCCESS)
276 {
277 for(pszObject = pszObjList; *pszObject != 0; pszObject += _tcslen(pszObject) + 1)
278 NxAddResultString(pValue, pszObject);
279 iResult = SYSINFO_RC_SUCCESS;
280 }
281 else
282 {
283 ReportPdhError(_T("H_PdhObjects"), _T("PdhEnumObjects"), rc);
284 }
285 free(pszObjList);
286 }
287 return iResult;
288 }
289
290
291 //
292 // List of available performance items for given object
293 //
294
295 static LONG H_PdhObjectItems(const TCHAR *pszParam, const TCHAR *pArg, NETXMS_VALUES_LIST *pValue)
296 {
297 TCHAR *pszElement, *pszCounterList, *pszInstanceList, szHostName[256], szObject[256];
298 LONG iResult = SYSINFO_RC_ERROR;
299 DWORD dwSize1, dwSize2;
300 PDH_STATUS rc;
301
302 NxGetParameterArg(pszParam, 1, szObject, 256);
303 if (szObject[0] != 0)
304 {
305 dwSize1 = 256;
306 if (GetComputerName(szHostName, &dwSize1))
307 {
308 dwSize1 = dwSize2 = 256000;
309 pszCounterList = (TCHAR *)malloc(sizeof(TCHAR) * dwSize1);
310 pszInstanceList = (TCHAR *)malloc(sizeof(TCHAR) * dwSize2);
311 rc = PdhEnumObjectItems(NULL, szHostName, szObject,
312 pszCounterList, &dwSize1, pszInstanceList, &dwSize2,
313 PERF_DETAIL_WIZARD, 0);
314 if ((rc == ERROR_SUCCESS) || (rc == PDH_MORE_DATA))
315 {
316 for(pszElement = (pArg[0] == _T('C')) ? pszCounterList : pszInstanceList;
317 *pszElement != 0; pszElement += _tcslen(pszElement) + 1)
318 NxAddResultString(pValue, pszElement);
319 iResult = SYSINFO_RC_SUCCESS;
320 }
321 else
322 {
323 ReportPdhError(_T("H_PdhObjectItems"), _T("PdhEnumObjectItems"), rc);
324 }
325 free(pszCounterList);
326 free(pszInstanceList);
327 }
328 }
329 else
330 {
331 iResult = SYSINFO_RC_UNSUPPORTED;
332 }
333 return iResult;
334 }
335
336
337 //
338 // Value of specific performance parameter, which is mapped one-to-one to
339 // performance counter. Actually, it's an alias for PDH.CounterValue(xxx) parameter.
340 //
341
342 static LONG H_CounterAlias(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *pValue)
343 {
344 return H_PdhCounterValue(NULL, pArg, pValue);
345 }
346
347
348 //
349 // Initialize subagent
350 //
351
352 static BOOL SubAgentInit(Config *config)
353 {
354 // Create shutdown condition object
355 g_hCondShutdown = CreateEvent(NULL, TRUE, FALSE, NULL);
356 StartCollectorThreads();
357 return TRUE;
358 }
359
360
361 //
362 // Handler for subagent unload
363 //
364
365 static void SubAgentShutdown(void)
366 {
367 if (g_hCondShutdown != NULL)
368 SetEvent(g_hCondShutdown);
369 Sleep(500);
370 }
371
372
373 //
374 // Subagent information
375 //
376
377 static NETXMS_SUBAGENT_PARAM m_parameters[] =
378 {
379 { _T("PDH.CounterValue(*)"), H_PdhCounterValue, NULL, DCI_DT_INT, _T("") },
380 { _T("PDH.Version"), H_PdhVersion, NULL, DCI_DT_UINT, _T("Version of PDH.DLL") },
381 { _T("System.CPU.Usage(*)"), H_CPUUsage, "1", DCI_DT_INT, "Average CPU {instance} utilization for last minute" },
382 { _T("System.CPU.Usage5(*)"), H_CPUUsage, "2", DCI_DT_INT, "Average CPU {instance} utilization for last 5 minutes" },
383 { _T("System.CPU.Usage15(*)"), H_CPUUsage, "3", DCI_DT_INT, "Average CPU {instance} utilization for last 15 minutes" },
384 { _T("System.ThreadCount"), H_CounterAlias, _T("\\System\\Threads"), DCI_DT_UINT, _T("Total number of threads") },
385 { _T("System.Uptime"), H_CounterAlias, _T("\\System\\System Up Time"), DCI_DT_UINT, _T("System uptime") }
386 };
387 static NETXMS_SUBAGENT_ENUM m_enums[] =
388 {
389 { _T("PDH.ObjectCounters(*)"), H_PdhObjectItems, _T("C") },
390 { _T("PDH.ObjectInstances(*)"), H_PdhObjectItems, _T("I") },
391 { _T("PDH.Objects"), H_PdhObjects, NULL }
392 };
393
394 static NETXMS_SUBAGENT_INFO m_info =
395 {
396 NETXMS_SUBAGENT_INFO_MAGIC,
397 _T("WinPerf"), _T(NETXMS_VERSION_STRING) _T(DEBUG_SUFFIX),
398 SubAgentInit, SubAgentShutdown, NULL, // handlers
399 0, NULL, // parameters
400 sizeof(m_enums) / sizeof(NETXMS_SUBAGENT_ENUM),
401 m_enums,
402 0, NULL // actions
403 };
404
405
406 //
407 // Add new parameter to list
408 //
409
410 BOOL AddParameter(TCHAR *pszName, LONG (* fpHandler)(const TCHAR *, const TCHAR *, TCHAR *),
411 TCHAR *pArg, int iDataType, TCHAR *pszDescription)
412 {
413 DWORD i;
414
415 for(i = 0; i < m_info.dwNumParameters; i++)
416 if (!_tcsicmp(pszName, m_info.pParamList[i].szName))
417 break;
418
419 if (i == m_info.dwNumParameters)
420 {
421 // Extend list
422 m_info.dwNumParameters++;
423 m_info.pParamList =
424 (NETXMS_SUBAGENT_PARAM *)realloc(m_info.pParamList,
425 sizeof(NETXMS_SUBAGENT_PARAM) * m_info.dwNumParameters);
426 }
427
428 nx_strncpy(m_info.pParamList[i].szName, pszName, MAX_PARAM_NAME);
429 m_info.pParamList[i].fpHandler = fpHandler;
430 m_info.pParamList[i].pArg = pArg;
431 m_info.pParamList[i].iDataType = iDataType;
432 nx_strncpy(m_info.pParamList[i].szDescription, pszDescription, MAX_DB_STRING);
433
434 return TRUE;
435 }
436
437
438 //
439 // Add predefined counters
440 //
441
442 static void AddPredefinedCounters(void)
443 {
444 DWORD i;
445 WINPERF_COUNTER *pCnt;
446 SYSTEM_INFO sysInfo;
447 TCHAR szBuffer[MAX_PATH];
448
449 for(i = 0; m_counterList[i].pszParamName != NULL; i++)
450 {
451 pCnt = AddCounter(m_counterList[i].pszCounterName, m_counterList[i].iClass,
452 m_counterList[i].iNumSamples, m_counterList[i].iDataType);
453 if (pCnt != NULL)
454 AddParameter(m_counterList[i].pszParamName, H_CollectedCounterData,
455 (TCHAR *)pCnt, m_counterList[i].iDCIDataType,
456 m_counterList[i].pszDescription);
457 }
458
459 // Add CPU utilization counters
460 GetSystemInfo(&sysInfo);
461 m_dwNumCPU = sysInfo.dwNumberOfProcessors;
462 for(i = 0; i < m_dwNumCPU; i++)
463 {
464 _sntprintf(szBuffer, MAX_PATH, _T("\\Processor(%d)\\%% Processor Time"), i);
465 m_pProcessorCounters[i] = AddCounter(szBuffer, 0, 60, COUNTER_TYPE_INT32);
466 m_pProcessorCounters5[i] = AddCounter(szBuffer, 0, 300, COUNTER_TYPE_INT32);
467 m_pProcessorCounters15[i] = AddCounter(szBuffer, 0, 900, COUNTER_TYPE_INT32);
468 }
469 }
470
471
472 //
473 // Configuration file template
474 //
475
476 static TCHAR *m_pszCounterList = NULL;
477 static NX_CFG_TEMPLATE m_cfgTemplate[] =
478 {
479 { _T("Counter"), CT_STRING_LIST, _T('\n'), 0, 0, 0, &m_pszCounterList },
480 { _T("EnableDefaultCounters"), CT_BOOLEAN, 0, 0, WPF_ENABLE_DEFAULT_COUNTERS, 0, &m_dwFlags },
481 { _T(""), CT_END_OF_LIST, 0, 0, 0, 0, NULL }
482 };
483
484
485 //
486 // Entry point for NetXMS agent
487 //
488
489 extern "C" BOOL __declspec(dllexport) __cdecl
490 NxSubAgentRegister(NETXMS_SUBAGENT_INFO **ppInfo, Config *config)
491 {
492 DWORD i, dwBufferSize, dwBytes, dwType, dwStatus;
493 TCHAR *pBuffer, *newName;
494
495 if (m_info.pParamList != NULL)
496 return FALSE; // Most likely another instance of WINPERF subagent already loaded
497
498 // Read performance counter indexes
499 pBuffer = NULL;
500 dwBufferSize = 0;
501 do
502 {
503 dwBufferSize += 8192;
504 pBuffer = (TCHAR *)realloc(pBuffer, dwBufferSize);
505 dwBytes = dwBufferSize;
506 dwStatus = RegQueryValueEx(HKEY_PERFORMANCE_DATA, _T("Counter 009"), NULL, &dwType, (BYTE *)pBuffer, &dwBytes);
507 } while(dwStatus == ERROR_MORE_DATA);
508 if (dwStatus == ERROR_SUCCESS)
509 {
510 CreateCounterIndex(pBuffer);
511 }
512 safe_free(pBuffer);
513
514 // Init parameters list
515 m_info.dwNumParameters = sizeof(m_parameters) / sizeof(NETXMS_SUBAGENT_PARAM);
516 m_info.pParamList = (NETXMS_SUBAGENT_PARAM *)nx_memdup(m_parameters, sizeof(m_parameters));
517
518 // Check counter names for H_CounterAlias
519 for(i = 0; i < m_info.dwNumParameters; i++)
520 {
521 if (m_info.pParamList[i].fpHandler == H_CounterAlias)
522 {
523 CheckCounter(m_info.pParamList[i].pArg, &newName);
524 if (newName != NULL)
525 m_info.pParamList[i].pArg = newName;
526 }
527 }
528
529 // Load configuration
530 bool success = config->bindParameters(_T("WinPerf"), m_cfgTemplate);
531 if (success)
532 {
533 TCHAR *pItem, *pEnd;
534
535 // Parse counter list
536 if (m_pszCounterList != NULL)
537 {
538 for(pItem = m_pszCounterList; *pItem != 0; pItem = pEnd + 1)
539 {
540 pEnd = _tcschr(pItem, _T('\n'));
541 if (pEnd != NULL)
542 *pEnd = 0;
543 StrStrip(pItem);
544 if (!AddCounterFromConfig(pItem))
545 NxWriteAgentLog(EVENTLOG_WARNING_TYPE,
546 _T("Unable to add counter from configuration file. ")
547 _T("Original configuration record: %s"), pItem);
548 }
549 free(m_pszCounterList);
550 }
551
552 if (m_dwFlags & WPF_ENABLE_DEFAULT_COUNTERS)
553 AddPredefinedCounters();
554 }
555 else
556 {
557 safe_free(m_pszCounterList);
558 }
559 *ppInfo = &m_info;
560 return success;
561 }
562
563
564 //
565 // DLL entry point
566 //
567
568 BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
569 {
570 if (dwReason == DLL_PROCESS_ATTACH)
571 DisableThreadLibraryCalls(hInstance);
572 return TRUE;
573 }