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