88366dc9e833242025c1de0a7c2146524c320e91
[public/netxms.git] / src / agent / subagents / winperf / winperf.cpp
1 /*
2 ** Windows Performance NetXMS subagent
3 ** Copyright (C) 2004-2016 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 * Constants
27 */
28 #define MAX_CPU_COUNT 256
29
30 #define WPF_ENABLE_DEFAULT_COUNTERS 0x0001
31
32 /**
33 * Static variables
34 */
35 static DWORD m_dwFlags = WPF_ENABLE_DEFAULT_COUNTERS;
36 static DWORD m_dwNumCPU = 1;
37 static WINPERF_COUNTER *s_processorCounters0[MAX_CPU_COUNT];
38 static WINPERF_COUNTER *s_processorCounters1[MAX_CPU_COUNT];
39 static WINPERF_COUNTER *s_processorCounters5[MAX_CPU_COUNT];
40 static WINPERF_COUNTER *s_processorCounters15[MAX_CPU_COUNT];
41 static MUTEX s_autoCountersLock = MutexCreate();
42 static StringObjectMap<WINPERF_COUNTER> *s_autoCounters = new StringObjectMap<WINPERF_COUNTER>(false);
43
44 /**
45 * List of predefined performance counters
46 */
47 static struct
48 {
49 TCHAR *pszParamName;
50 TCHAR *pszCounterName;
51 int iClass;
52 int iNumSamples;
53 int iDataType;
54 TCHAR *pszDescription;
55 int iDCIDataType;
56 } m_counterList[] =
57 {
58 { _T("System.CPU.LoadAvg"), _T("\\System\\Processor Queue Length"), 0, 60, COUNTER_TYPE_FLOAT, _T("Average CPU load for last minute"), DCI_DT_FLOAT },
59 { _T("System.CPU.LoadAvg5"), _T("\\System\\Processor Queue Length"), 0, 300, COUNTER_TYPE_FLOAT, _T("Average CPU load for last 5 minutes"), DCI_DT_FLOAT },
60 { _T("System.CPU.LoadAvg15"), _T("\\System\\Processor Queue Length"), 0, 900, COUNTER_TYPE_FLOAT, _T("Average CPU load for last 15 minutes"), DCI_DT_FLOAT },
61 { _T("System.CPU.CurrentUsage"), _T("\\Processor(_Total)\\% Processor Time"), 0, 2, COUNTER_TYPE_INT32, DCIDESC_SYSTEM_CPU_USAGECURR, DCI_DT_INT },
62 { _T("System.CPU.Usage"), _T("\\Processor(_Total)\\% Processor Time"), 0, 60, COUNTER_TYPE_INT32, DCIDESC_SYSTEM_CPU_USAGE, DCI_DT_INT },
63 { _T("System.CPU.Usage5"), _T("\\Processor(_Total)\\% Processor Time"), 0, 300, COUNTER_TYPE_INT32, DCIDESC_SYSTEM_CPU_USAGE5, DCI_DT_INT },
64 { _T("System.CPU.Usage15"), _T("\\Processor(_Total)\\% Processor Time"), 0, 900, COUNTER_TYPE_INT32, DCIDESC_SYSTEM_CPU_USAGE15, DCI_DT_INT },
65 { _T("System.IO.DiskQueue"), _T("\\PhysicalDisk(_Total)\\Avg. Disk Queue Length"), 0, 60, COUNTER_TYPE_FLOAT, DCIDESC_SYSTEM_IO_DISKQUEUE, DCI_DT_FLOAT },
66 { _T("System.IO.DiskTime"), _T("\\PhysicalDisk(_Total)\\% Disk Time"), 0, 60, COUNTER_TYPE_FLOAT, _T("Average disk busy time for last minute"), DCI_DT_FLOAT },
67 { NULL, NULL, 0, 0, 0, NULL, 0 }
68 };
69
70 /**
71 * Handler for PDH.Version parameter
72 */
73 static LONG H_PdhVersion(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *pValue, AbstractCommSession *session)
74 {
75 DWORD dwVersion;
76
77 if (PdhGetDllVersion(&dwVersion) != ERROR_SUCCESS)
78 return SYSINFO_RC_ERROR;
79 ret_uint(pValue, dwVersion);
80 return SYSINFO_RC_SUCCESS;
81 }
82
83 /**
84 * Handler for WinPerf.Features parameter
85 */
86 static LONG H_WinPerfFeatures(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *pValue, AbstractCommSession *session)
87 {
88 ret_uint(pValue, WINPERF_AUTOMATIC_SAMPLE_COUNT | WINPERF_REMOTE_COUNTER_CONFIG);
89 return SYSINFO_RC_SUCCESS;
90 }
91
92 /**
93 * Value of CPU utilization counter
94 */
95 static LONG H_CPUUsage(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *pValue, AbstractCommSession *session)
96 {
97 LONG nProcessor, nRet = SYSINFO_RC_SUCCESS;
98 TCHAR *pEnd, szBuffer[16];
99
100 if (!AgentGetParameterArg(pszParam, 1, szBuffer, 16))
101 return SYSINFO_RC_UNSUPPORTED;
102
103 nProcessor = _tcstol(szBuffer, &pEnd, 0);
104 if ((*pEnd != 0) || (nProcessor < 0) || (nProcessor >= (LONG)m_dwNumCPU))
105 return SYSINFO_RC_UNSUPPORTED;
106
107 switch(pArg[0])
108 {
109 case _T('0'): // System.CPU.Usage(*)
110 if (s_processorCounters0[nProcessor] != NULL)
111 ret_int(pValue, s_processorCounters0[nProcessor]->value.iLong);
112 else
113 nRet = SYSINFO_RC_ERROR;
114 break;
115 case _T('1'): // System.CPU.Usage(*)
116 if (s_processorCounters1[nProcessor] != NULL)
117 ret_int(pValue, s_processorCounters1[nProcessor]->value.iLong);
118 else
119 nRet = SYSINFO_RC_ERROR;
120 break;
121 case _T('2'): // System.CPU.Usage5(*)
122 if (s_processorCounters5[nProcessor] != NULL)
123 ret_int(pValue, s_processorCounters5[nProcessor]->value.iLong);
124 else
125 nRet = SYSINFO_RC_ERROR;
126 break;
127 case _T('3'): // System.CPU.Usage15(*)
128 if (s_processorCounters15[nProcessor] != NULL)
129 ret_int(pValue, s_processorCounters15[nProcessor]->value.iLong);
130 else
131 nRet = SYSINFO_RC_ERROR;
132 break;
133 default:
134 nRet = SYSINFO_RC_UNSUPPORTED;
135 break;
136 }
137
138 return nRet;
139 }
140
141 /**
142 * Value of given counter collected by one of the collector threads
143 */
144 LONG H_CollectedCounterData(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *pValue, AbstractCommSession *session)
145 {
146 switch(((WINPERF_COUNTER *)pArg)->wType)
147 {
148 case COUNTER_TYPE_INT32:
149 ret_int(pValue, ((WINPERF_COUNTER *)pArg)->value.iLong);
150 break;
151 case COUNTER_TYPE_INT64:
152 ret_int64(pValue, ((WINPERF_COUNTER *)pArg)->value.iLarge);
153 break;
154 case COUNTER_TYPE_FLOAT:
155 ret_double(pValue, ((WINPERF_COUNTER *)pArg)->value.dFloat);
156 break;
157 }
158 return SYSINFO_RC_SUCCESS;
159 }
160
161 /**
162 * Value of given performance counter
163 */
164 static LONG H_PdhCounterValue(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *pValue, AbstractCommSession *session)
165 {
166 HQUERY hQuery;
167 HCOUNTER hCounter;
168 PDH_RAW_COUNTER rawData1, rawData2;
169 PDH_FMT_COUNTERVALUE counterValue;
170 PDH_STATUS rc;
171 TCHAR szCounter[MAX_PATH];
172 DWORD dwType;
173 BOOL bUseTwoSamples = FALSE;
174 static TCHAR szFName[] = _T("H_PdhCounterValue");
175
176 if (pArg == NULL) // Normal call
177 {
178 if (!AgentGetParameterArg(pszParam, 1, szCounter, MAX_PATH))
179 return SYSINFO_RC_UNSUPPORTED;
180
181 // required number of samples
182 TCHAR buffer[MAX_PATH] = _T("");
183 AgentGetParameterArg(pszParam, 2, buffer, MAX_PATH);
184 if (buffer[0] != 0) // sample count given
185 {
186 int samples = _tcstol(buffer, NULL, 0);
187 if (samples > 1)
188 {
189 _sntprintf(buffer, MAX_PATH, _T("%d#%s"), samples, szCounter);
190 MutexLock(s_autoCountersLock);
191 WINPERF_COUNTER *counter = s_autoCounters->get(buffer);
192 if (counter == NULL)
193 {
194 counter = AddCounter(szCounter, 0, samples, COUNTER_TYPE_AUTO);
195 if (counter != NULL)
196 {
197 AgentWriteDebugLog(5, _T("WINPERF: new automatic counter created: \"%s\""), buffer);
198 s_autoCounters->set(buffer, counter);
199 }
200 }
201 MutexUnlock(s_autoCountersLock);
202 return (counter != NULL) ? H_CollectedCounterData(pszParam, (const TCHAR *)counter, pValue, session) : SYSINFO_RC_UNSUPPORTED;
203 }
204 }
205 }
206 else // Call from H_CounterAlias
207 {
208 nx_strncpy(szCounter, pArg, MAX_PATH);
209 }
210
211 if ((rc = PdhOpenQuery(NULL, 0, &hQuery)) != ERROR_SUCCESS)
212 {
213 ReportPdhError(szFName, _T("PdhOpenQuery"), rc);
214 return SYSINFO_RC_ERROR;
215 }
216
217 if ((rc = PdhAddCounter(hQuery, szCounter, 0, &hCounter)) != ERROR_SUCCESS)
218 {
219 // Attempt to translate counter name
220 if ((rc == PDH_CSTATUS_NO_COUNTER) || (rc == PDH_CSTATUS_NO_OBJECT))
221 {
222 TCHAR *newName = (TCHAR *)malloc(_tcslen(szCounter) * sizeof(TCHAR) * 4);
223 if (TranslateCounterName(szCounter, newName))
224 {
225 AgentWriteDebugLog(2, _T("WINPERF: Counter translated: %s ==> %s"), szCounter, newName);
226 rc = PdhAddCounter(hQuery, newName, 0, &hCounter);
227 }
228 free(newName);
229 }
230 if (rc != ERROR_SUCCESS)
231 {
232 ReportPdhError(szFName, _T("PdhAddCounter"), rc);
233 PdhCloseQuery(hQuery);
234 return SYSINFO_RC_UNSUPPORTED;
235 }
236 }
237
238 // Get first sample
239 if ((rc = PdhCollectQueryData(hQuery)) != ERROR_SUCCESS)
240 {
241 ReportPdhError(szFName, _T("PdhCollectQueryData"), rc);
242 PdhCloseQuery(hQuery);
243 return SYSINFO_RC_ERROR;
244 }
245 PdhGetRawCounterValue(hCounter, &dwType, &rawData1);
246
247 // Get second sample if required
248 if ((dwType & 0x00000C00) == PERF_TYPE_COUNTER)
249 {
250 DWORD subType = dwType & 0x000F0000;
251 if ((subType == PERF_COUNTER_RATE) || (subType == PERF_COUNTER_PRECISION))
252 {
253 Sleep(1000); // We will take second sample after one second
254 if ((rc = PdhCollectQueryData(hQuery)) != ERROR_SUCCESS)
255 {
256 ReportPdhError(szFName, _T("PdhCollectQueryData"), rc);
257 PdhCloseQuery(hQuery);
258 return SYSINFO_RC_ERROR;
259 }
260 PdhGetRawCounterValue(hCounter, NULL, &rawData2);
261 bUseTwoSamples = TRUE;
262 }
263 }
264
265 if ((dwType & 0x00000300) == PERF_SIZE_LARGE)
266 {
267 if (bUseTwoSamples)
268 PdhCalculateCounterFromRawValue(hCounter, PDH_FMT_LARGE,
269 &rawData2, &rawData1, &counterValue);
270 else
271 PdhCalculateCounterFromRawValue(hCounter, PDH_FMT_LARGE,
272 &rawData1, NULL, &counterValue);
273 ret_int64(pValue, counterValue.largeValue);
274 }
275 else
276 {
277 if (bUseTwoSamples)
278 PdhCalculateCounterFromRawValue(hCounter, PDH_FMT_LONG,
279 &rawData2, &rawData1, &counterValue);
280 else
281 PdhCalculateCounterFromRawValue(hCounter, PDH_FMT_LONG,
282 &rawData1, NULL, &counterValue);
283 ret_int(pValue, counterValue.longValue);
284 }
285 PdhCloseQuery(hQuery);
286 return SYSINFO_RC_SUCCESS;
287 }
288
289 /**
290 * List of available performance objects
291 */
292 static LONG H_PdhObjects(const TCHAR *pszParam, const TCHAR *pArg, StringList *value, AbstractCommSession *session)
293 {
294 TCHAR *pszObject, *pszObjList, szHostName[256];
295 LONG iResult = SYSINFO_RC_ERROR;
296 PDH_STATUS rc;
297 DWORD dwSize;
298
299 dwSize = 256;
300 if (GetComputerName(szHostName, &dwSize))
301 {
302 dwSize = 256000;
303 pszObjList = (TCHAR *)malloc(sizeof(TCHAR) * dwSize);
304 if ((rc = PdhEnumObjects(NULL, szHostName, pszObjList, &dwSize,
305 PERF_DETAIL_WIZARD, TRUE)) == ERROR_SUCCESS)
306 {
307 for(pszObject = pszObjList; *pszObject != 0; pszObject += _tcslen(pszObject) + 1)
308 value->add(pszObject);
309 iResult = SYSINFO_RC_SUCCESS;
310 }
311 else
312 {
313 ReportPdhError(_T("H_PdhObjects"), _T("PdhEnumObjects"), rc);
314 }
315 free(pszObjList);
316 }
317 return iResult;
318 }
319
320 /**
321 * List of available performance items for given object
322 */
323 static LONG H_PdhObjectItems(const TCHAR *pszParam, const TCHAR *pArg, StringList *value, AbstractCommSession *session)
324 {
325 TCHAR *pszElement, *pszCounterList, *pszInstanceList, szHostName[256], szObject[256];
326 LONG iResult = SYSINFO_RC_ERROR;
327 DWORD dwSize1, dwSize2;
328 PDH_STATUS rc;
329
330 AgentGetParameterArg(pszParam, 1, szObject, 256);
331 if (szObject[0] != 0)
332 {
333 dwSize1 = 256;
334 if (GetComputerName(szHostName, &dwSize1))
335 {
336 dwSize1 = dwSize2 = 256000;
337 pszCounterList = (TCHAR *)malloc(sizeof(TCHAR) * dwSize1);
338 pszInstanceList = (TCHAR *)malloc(sizeof(TCHAR) * dwSize2);
339 rc = PdhEnumObjectItems(NULL, szHostName, szObject,
340 pszCounterList, &dwSize1, pszInstanceList, &dwSize2,
341 PERF_DETAIL_WIZARD, 0);
342 if ((rc == ERROR_SUCCESS) || (rc == PDH_MORE_DATA))
343 {
344 for(pszElement = (pArg[0] == _T('C')) ? pszCounterList : pszInstanceList;
345 *pszElement != 0; pszElement += _tcslen(pszElement) + 1)
346 value->add(pszElement);
347 iResult = SYSINFO_RC_SUCCESS;
348 }
349 else
350 {
351 ReportPdhError(_T("H_PdhObjectItems"), _T("PdhEnumObjectItems"), rc);
352 }
353 free(pszCounterList);
354 free(pszInstanceList);
355 }
356 }
357 else
358 {
359 iResult = SYSINFO_RC_UNSUPPORTED;
360 }
361 return iResult;
362 }
363
364 /**
365 * Value of specific performance parameter, which is mapped one-to-one to
366 * performance counter. Actually, it's an alias for PDH.CounterValue(xxx) parameter.
367 */
368 static LONG H_CounterAlias(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *pValue, AbstractCommSession *session)
369 {
370 return H_PdhCounterValue(NULL, pArg, pValue, session);
371 }
372
373 /**
374 * Handler for System.Memory.Physical.FreePerc parameter
375 */
376 static LONG H_FreeMemoryPct(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *pValue, AbstractCommSession *session)
377 {
378 TCHAR buffer[MAX_RESULT_LENGTH];
379 LONG rc = H_PdhCounterValue(NULL, pArg, buffer, session);
380 if (rc != SYSINFO_RC_SUCCESS)
381 return rc;
382
383 UINT64 free = _tcstoull(buffer, NULL, 10);
384
385 MEMORYSTATUSEX mse;
386 mse.dwLength = sizeof(MEMORYSTATUSEX);
387 if (!GlobalMemoryStatusEx(&mse))
388 return SYSINFO_RC_ERROR;
389
390 ret_int(pValue, (int)(free * 100 / mse.ullTotalPhys));
391 return SYSINFO_RC_SUCCESS;
392 }
393
394 /**
395 * Initialize subagent
396 */
397 static BOOL SubAgentInit(Config *config)
398 {
399 StartCollectorThreads();
400 return TRUE;
401 }
402
403 /**
404 * Handler for subagent unload
405 */
406 static void SubAgentShutdown()
407 {
408 JoinCollectorThreads();
409 }
410
411 /**
412 * Supported parameters
413 */
414 static NETXMS_SUBAGENT_PARAM m_parameters[] =
415 {
416 { _T("PDH.CounterValue(*)"), H_PdhCounterValue, NULL, DCI_DT_INT, _T("") },
417 { _T("PDH.Version"), H_PdhVersion, NULL, DCI_DT_UINT, _T("Version of PDH.DLL") },
418 { _T("System.CPU.CurrentUsage(*)"), H_CPUUsage, _T("0"), DCI_DT_INT, DCIDESC_SYSTEM_CPU_USAGECURR_EX },
419 { _T("System.CPU.Usage(*)"), H_CPUUsage, _T("1"), DCI_DT_INT, DCIDESC_SYSTEM_CPU_USAGE_EX },
420 { _T("System.CPU.Usage5(*)"), H_CPUUsage, _T("2"), DCI_DT_INT, DCIDESC_SYSTEM_CPU_USAGE5_EX },
421 { _T("System.CPU.Usage15(*)"), H_CPUUsage, _T("3"), DCI_DT_INT, DCIDESC_SYSTEM_CPU_USAGE15_EX },
422 { _T("System.ThreadCount"), H_CounterAlias, _T("\\System\\Threads"), DCI_DT_UINT, DCIDESC_SYSTEM_THREADCOUNT },
423 { _T("System.Uptime"), H_CounterAlias, _T("\\System\\System Up Time"), DCI_DT_UINT, DCIDESC_SYSTEM_UPTIME },
424 { _T("WinPerf.Features"), H_WinPerfFeatures, NULL, DCI_DT_UINT, _T("Features supported by this WinPerf version") },
425 };
426
427 /**
428 * Supported lists
429 */
430 static NETXMS_SUBAGENT_LIST m_lists[] =
431 {
432 { _T("PDH.ObjectCounters(*)"), H_PdhObjectItems, _T("C") },
433 { _T("PDH.ObjectInstances(*)"), H_PdhObjectItems, _T("I") },
434 { _T("PDH.Objects"), H_PdhObjects, NULL }
435 };
436
437 /**
438 * Subagent information
439 */
440 static NETXMS_SUBAGENT_INFO m_info =
441 {
442 NETXMS_SUBAGENT_INFO_MAGIC,
443 _T("WinPerf"), NETXMS_VERSION_STRING,
444 SubAgentInit, SubAgentShutdown, NULL, // handlers
445 0, NULL, // parameters
446 sizeof(m_lists) / sizeof(NETXMS_SUBAGENT_LIST),
447 m_lists,
448 0, NULL, // tables
449 0, NULL, // actions
450 0, NULL // push parameters
451 };
452
453 /**
454 * Add new parameter to list
455 */
456 BOOL AddParameter(TCHAR *pszName, LONG (* fpHandler)(const TCHAR *, const TCHAR *, TCHAR *, AbstractCommSession *), TCHAR *pArg, int iDataType, TCHAR *pszDescription)
457 {
458 DWORD i;
459
460 for(i = 0; i < m_info.numParameters; i++)
461 if (!_tcsicmp(pszName, m_info.parameters[i].name))
462 break;
463
464 if (i == m_info.numParameters)
465 {
466 // Extend list
467 m_info.numParameters++;
468 m_info.parameters =
469 (NETXMS_SUBAGENT_PARAM *)realloc(m_info.parameters,
470 sizeof(NETXMS_SUBAGENT_PARAM) * m_info.numParameters);
471 }
472
473 nx_strncpy(m_info.parameters[i].name, pszName, MAX_PARAM_NAME);
474 m_info.parameters[i].handler = fpHandler;
475 m_info.parameters[i].arg = pArg;
476 m_info.parameters[i].dataType = iDataType;
477 nx_strncpy(m_info.parameters[i].description, pszDescription, MAX_DB_STRING);
478
479 return TRUE;
480 }
481
482 /**
483 * Add predefined counters
484 */
485 static void AddPredefinedCounters()
486 {
487 DWORD i;
488 WINPERF_COUNTER *pCnt;
489 SYSTEM_INFO sysInfo;
490 TCHAR szBuffer[MAX_PATH];
491
492 for(i = 0; m_counterList[i].pszParamName != NULL; i++)
493 {
494 pCnt = AddCounter(m_counterList[i].pszCounterName, m_counterList[i].iClass,
495 m_counterList[i].iNumSamples, m_counterList[i].iDataType);
496 if (pCnt != NULL)
497 AddParameter(m_counterList[i].pszParamName, H_CollectedCounterData,
498 (TCHAR *)pCnt, m_counterList[i].iDCIDataType,
499 m_counterList[i].pszDescription);
500 }
501
502 // Add CPU utilization counters
503 GetSystemInfo(&sysInfo);
504 m_dwNumCPU = sysInfo.dwNumberOfProcessors;
505 for(i = 0; i < m_dwNumCPU; i++)
506 {
507 _sntprintf(szBuffer, MAX_PATH, _T("\\Processor(%d)\\%% Processor Time"), i);
508 s_processorCounters0[i] = AddCounter(szBuffer, 0, 2, COUNTER_TYPE_INT32);
509 s_processorCounters1[i] = AddCounter(szBuffer, 0, 60, COUNTER_TYPE_INT32);
510 s_processorCounters5[i] = AddCounter(szBuffer, 0, 300, COUNTER_TYPE_INT32);
511 s_processorCounters15[i] = AddCounter(szBuffer, 0, 900, COUNTER_TYPE_INT32);
512 }
513 }
514
515 /**
516 * Configuration file template
517 */
518 static TCHAR *m_pszCounterList = NULL;
519 static NX_CFG_TEMPLATE m_cfgTemplate[] =
520 {
521 { _T("Counter"), CT_STRING_LIST, _T('\n'), 0, 0, 0, &m_pszCounterList },
522 { _T("EnableDefaultCounters"), CT_BOOLEAN, 0, 0, WPF_ENABLE_DEFAULT_COUNTERS, 0, &m_dwFlags },
523 { _T(""), CT_END_OF_LIST, 0, 0, 0, 0, NULL }
524 };
525
526 /**
527 * Entry point for NetXMS agent
528 */
529 DECLARE_SUBAGENT_ENTRY_POINT(WINPERF)
530 {
531 if (m_info.parameters != NULL)
532 return FALSE; // Most likely another instance of WINPERF subagent already loaded
533
534 // Read performance counter indexes
535 TCHAR *counters = NULL;
536 size_t countersBufferSize = 0;
537 DWORD status;
538 do
539 {
540 countersBufferSize += 8192;
541 counters = (TCHAR *)realloc(counters, countersBufferSize);
542 DWORD bytes = (DWORD)countersBufferSize;
543 DWORD type;
544 status = RegQueryValueEx(HKEY_PERFORMANCE_DATA, _T("Counter 009"), NULL, &type, (BYTE *)counters, &bytes);
545 } while(status == ERROR_MORE_DATA);
546 if ((status != ERROR_SUCCESS) || (counters[0] == 0))
547 {
548 AgentWriteDebugLog(1, _T("WinPerf: failed to read counters from HKEY_PERFORMANCE_DATA"));
549
550 HKEY hKey;
551 status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009"), 0, KEY_READ, &hKey);
552 if (status == ERROR_SUCCESS)
553 {
554 DWORD bytes = (DWORD)countersBufferSize;
555 DWORD type;
556 status = RegQueryValueEx(hKey, _T("Counter"), NULL, &type, (BYTE *)counters, &bytes);
557 while(status == ERROR_MORE_DATA)
558 {
559 countersBufferSize += 8192;
560 counters = (TCHAR *)realloc(counters, countersBufferSize);
561 bytes = (DWORD)countersBufferSize;
562 status = RegQueryValueEx(hKey, _T("Counter"), NULL, &type, (BYTE *)counters, &bytes);
563 }
564 RegCloseKey(hKey);
565 }
566 }
567 if (status == ERROR_SUCCESS)
568 {
569 countersBufferSize = 8192;
570 TCHAR *localCounters = (TCHAR *)malloc(countersBufferSize);
571 localCounters[0] = 0;
572
573 HKEY hKey;
574 status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\CurrentLanguage"), 0, KEY_READ, &hKey);
575 if (status == ERROR_SUCCESS)
576 {
577 DWORD bytes = (DWORD)countersBufferSize;
578 DWORD type;
579 status = RegQueryValueEx(hKey, _T("Counter"), NULL, &type, (BYTE *)localCounters, &bytes);
580 while(status == ERROR_MORE_DATA)
581 {
582 countersBufferSize += 8192;
583 localCounters = (TCHAR *)realloc(localCounters, countersBufferSize);
584 bytes = (DWORD)countersBufferSize;
585 status = RegQueryValueEx(hKey, _T("Counter"), NULL, &type, (BYTE *)localCounters, &bytes);
586 }
587 RegCloseKey(hKey);
588 }
589
590 CreateCounterIndex(counters, localCounters);
591 free(localCounters);
592 }
593 free(counters);
594
595 // Init parameters list
596 m_info.numParameters = sizeof(m_parameters) / sizeof(NETXMS_SUBAGENT_PARAM);
597 m_info.parameters = (NETXMS_SUBAGENT_PARAM *)nx_memdup(m_parameters, sizeof(m_parameters));
598
599 // Check counter names for H_CounterAlias
600 TCHAR *newName;
601 for(UINT32 i = 0; i < m_info.numParameters; i++)
602 {
603 if (m_info.parameters[i].handler == H_CounterAlias)
604 {
605 CheckCounter(m_info.parameters[i].arg, &newName);
606 if (newName != NULL)
607 m_info.parameters[i].arg = newName;
608 }
609 }
610
611 // Add System.Memory.Free* handlers if "\Memory\Free & Zero Page List Bytes" counter is available
612 const TCHAR *counter = _T("\\Memory\\Free & Zero Page List Bytes");
613 CheckCounter(counter, &newName);
614 if (newName != NULL)
615 counter = newName;
616 TCHAR value[MAX_RESULT_LENGTH];
617 if (H_PdhCounterValue(NULL, counter, value, NULL) == SYSINFO_RC_SUCCESS)
618 {
619 AgentWriteDebugLog(4, _T("WinPerf: \"\\Memory\\Free & Zero Page List Bytes\" is supported"));
620 AddParameter(_T("System.Memory.Physical.Free"), H_CounterAlias, (TCHAR *)counter, DCI_DT_UINT64, DCIDESC_SYSTEM_MEMORY_PHYSICAL_FREE);
621 AddParameter(_T("System.Memory.Physical.FreePerc"), H_FreeMemoryPct, (TCHAR *)counter, DCI_DT_INT, DCIDESC_SYSTEM_MEMORY_PHYSICAL_FREE_PCT);
622 }
623 else
624 {
625 AgentWriteDebugLog(4, _T("WinPerf: \"\\Memory\\Free & Zero Page List Bytes\" is not supported"));
626 safe_free(newName);
627 }
628
629 // Load configuration
630 bool success = config->parseTemplate(_T("WinPerf"), m_cfgTemplate);
631 if (success)
632 {
633 TCHAR *pItem, *pEnd;
634
635 // Parse counter list
636 if (m_pszCounterList != NULL)
637 {
638 for(pItem = m_pszCounterList; *pItem != 0; pItem = pEnd + 1)
639 {
640 pEnd = _tcschr(pItem, _T('\n'));
641 if (pEnd != NULL)
642 *pEnd = 0;
643 StrStrip(pItem);
644 if (!AddCounterFromConfig(pItem))
645 AgentWriteLog(EVENTLOG_WARNING_TYPE,
646 _T("Unable to add counter from configuration file. ")
647 _T("Original configuration record: %s"), pItem);
648 }
649 free(m_pszCounterList);
650 }
651
652 if (m_dwFlags & WPF_ENABLE_DEFAULT_COUNTERS)
653 AddPredefinedCounters();
654 }
655 else
656 {
657 safe_free(m_pszCounterList);
658 }
659 *ppInfo = &m_info;
660 return success;
661 }
662
663 /**
664 * DLL entry point
665 */
666 BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
667 {
668 if (dwReason == DLL_PROCESS_ATTACH)
669 DisableThreadLibraryCalls(hInstance);
670 return TRUE;
671 }