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