User-defined counters implemented
[public/netxms.git] / src / agent / subagents / winperf / winperf.cpp
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 //
27 // Constants
28 //
29
30 #define CFG_BUFFER_SIZE 256000
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
48
49 //
50 // List of predefined performance counters
51 //
52 static 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
70
71 //
72 // Value of given performance counter
73 //
74
75 static 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
86 //
87 // Value of given counter collected by one of the collector threads
88 //
89
90 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
108 //
109 // Value of given performance counter
110 //
111
112 static LONG H_PdhCounterValue(TCHAR *pszParam, TCHAR *pArg, TCHAR *pValue)
113 {
114 HQUERY hQuery;
115 HCOUNTER hCounter;
116 PDH_RAW_COUNTER rawData1, rawData2;
117 PDH_FMT_COUNTERVALUE counterValue;
118 PDH_STATUS rc;
119 TCHAR szCounter[MAX_PATH], szBuffer[16];
120 DWORD dwType;
121 BOOL bUseTwoSamples = FALSE;
122 static TCHAR szFName[] = _T("H_PdhCounterValue");
123
124 if ((!NxGetParameterArg(pszParam, 1, szCounter, MAX_PATH)) ||
125 (!NxGetParameterArg(pszParam, 2, szBuffer, 16)))
126 return SYSINFO_RC_UNSUPPORTED;
127
128 bUseTwoSamples = _tcstol(szBuffer, NULL, 0) ? TRUE : FALSE;
129
130 if ((rc = PdhOpenQuery(NULL, 0, &hQuery)) != ERROR_SUCCESS)
131 {
132 ReportPdhError(szFName, _T("PdhOpenQuery"), rc);
133 return SYSINFO_RC_ERROR;
134 }
135
136 if ((rc = PdhAddCounter(hQuery, szCounter, 0, &hCounter)) != ERROR_SUCCESS)
137 {
138 ReportPdhError(szFName, _T("PdhAddCounter"), rc);
139 PdhCloseQuery(hQuery);
140 return SYSINFO_RC_UNSUPPORTED;
141 }
142
143 // Get first sample
144 if ((rc = PdhCollectQueryData(hQuery)) != ERROR_SUCCESS)
145 {
146 ReportPdhError(szFName, _T("PdhCollectQueryData"), rc);
147 PdhCloseQuery(hQuery);
148 return SYSINFO_RC_ERROR;
149 }
150 PdhGetRawCounterValue(hCounter, &dwType, &rawData1);
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 }
164
165 if (dwType & PERF_SIZE_LARGE)
166 {
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);
173 ret_int64(pValue, counterValue.largeValue);
174 }
175 else
176 {
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);
184 }
185 PdhCloseQuery(hQuery);
186 return SYSINFO_RC_SUCCESS;
187 }
188
189
190 //
191 // List of available performance objects
192 //
193
194 static LONG H_PdhObjects(TCHAR *pszParam, TCHAR *pArg, NETXMS_VALUES_LIST *pValue)
195 {
196 TCHAR *pszObject, *pszObjList, szHostName[256];
197 LONG iResult = SYSINFO_RC_ERROR;
198 PDH_STATUS rc;
199 DWORD dwSize;
200
201 dwSize = 256;
202 if (GetComputerName(szHostName, &dwSize))
203 {
204 dwSize = 256000;
205 pszObjList = (TCHAR *)malloc(sizeof(TCHAR) * dwSize);
206 if ((rc = PdhEnumObjects(NULL, szHostName, pszObjList, &dwSize,
207 PERF_DETAIL_WIZARD, TRUE)) == ERROR_SUCCESS)
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
227 static LONG H_PdhObjectItems(TCHAR *pszParam, TCHAR *pArg, NETXMS_VALUES_LIST *pValue)
228 {
229 TCHAR *pszElement, *pszCounterList, *pszInstanceList, szHostName[256], szObject[256];
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;
241 pszCounterList = (TCHAR *)malloc(sizeof(TCHAR) * dwSize1);
242 pszInstanceList = (TCHAR *)malloc(sizeof(TCHAR) * dwSize2);
243 rc = PdhEnumObjectItems(NULL, szHostName, szObject,
244 pszCounterList, &dwSize1, pszInstanceList, &dwSize2,
245 PERF_DETAIL_WIZARD, 0);
246 if ((rc == ERROR_SUCCESS) || (rc == PDH_MORE_DATA))
247 {
248 for(pszElement = (pArg[0] == _T('C')) ? pszCounterList : pszInstanceList;
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 }
257 free(pszCounterList);
258 free(pszInstanceList);
259 }
260 }
261 else
262 {
263 iResult = SYSINFO_RC_UNSUPPORTED;
264 }
265 return iResult;
266 }
267
268
269 //
270 // Value of specific performance parameter, which is mapped one-to-one to
271 // performance counter. Actually, it's an alias for PDH.CounterValue(xxx) parameter.
272 //
273
274 static LONG H_CounterAlias(TCHAR *pszParam, TCHAR *pArg, TCHAR *pValue)
275 {
276 return H_PdhCounterValue(pArg, NULL, pValue);
277 }
278
279
280 //
281 // Handler for subagent unload
282 //
283
284 static void OnUnload(void)
285 {
286 if (g_hCondShutdown != NULL)
287 SetEvent(g_hCondShutdown);
288 Sleep(500);
289 }
290
291
292 //
293 // Subagent information
294 //
295
296 static NETXMS_SUBAGENT_PARAM m_parameters[] =
297 {
298 { _T("PDH.CounterValue(*)"), H_PdhCounterValue, NULL },
299 { _T("PDH.Version"), H_PdhVersion, NULL },
300 { _T("System.Threads"), H_CounterAlias, _T("(\\System\\Threads)") },
301 { _T("System.Uptime"), H_CounterAlias, _T("(\\System\\System Up Time)") }
302 };
303 static NETXMS_SUBAGENT_ENUM m_enums[] =
304 {
305 { _T("PDH.ObjectCounters(*)"), H_PdhObjectItems, _T("C") },
306 { _T("PDH.ObjectInstances(*)"), H_PdhObjectItems, _T("I") },
307 { _T("PDH.Objects"), H_PdhObjects, NULL }
308 };
309
310 static NETXMS_SUBAGENT_INFO m_info =
311 {
312 NETXMS_SUBAGENT_INFO_MAGIC,
313 _T("WinPerf"), _T(NETXMS_VERSION_STRING) _T(DEBUG_SUFFIX), OnUnload,
314 0, NULL,
315 sizeof(m_enums) / sizeof(NETXMS_SUBAGENT_ENUM),
316 m_enums
317 };
318
319
320 //
321 // Add new parameter to list
322 //
323
324 BOOL AddParameter(TCHAR *pszName, LONG (* fpHandler)(TCHAR *, TCHAR *, TCHAR *), TCHAR *pArg)
325 {
326 DWORD i;
327
328 for(i = 0; i < m_info.dwNumParameters; i++)
329 if (!_tcsicmp(pszName, m_info.pParamList[i].szName))
330 break;
331
332 if (i == m_info.dwNumParameters)
333 {
334 // Extend list
335 m_info.dwNumParameters++;
336 m_info.pParamList =
337 (NETXMS_SUBAGENT_PARAM *)realloc(m_info.pParamList,
338 sizeof(NETXMS_SUBAGENT_PARAM) * m_info.dwNumParameters);
339 }
340
341 _tcsncpy(m_info.pParamList[i].szName, pszName, MAX_PARAM_NAME);
342 m_info.pParamList[i].fpHandler = fpHandler;
343 m_info.pParamList[i].pArg = pArg;
344
345 return TRUE;
346 }
347
348
349 //
350 // Add predefined counters
351 //
352
353 static void AddPredefinedCounters(void)
354 {
355 int i;
356 WINPERF_COUNTER *pCnt;
357
358 for(i = 0; m_counterList[i].pszParamName != NULL; i++)
359 {
360 pCnt = AddCounter(m_counterList[i].pszCounterName, m_counterList[i].iClass,
361 m_counterList[i].iNumSamples, m_counterList[i].iDataType);
362 if (pCnt != NULL)
363 AddParameter(m_counterList[i].pszParamName, H_CollectedCounterData, (TCHAR *)pCnt);
364 }
365 }
366
367
368 //
369 // Configuration file template
370 //
371
372 static NX_CFG_TEMPLATE cfgTemplate[] =
373 {
374 { _T("Counter"), CT_STRING_LIST, _T('\n'), 0, CFG_BUFFER_SIZE, 0, NULL },
375 { _T("EnableDefaultCounters"), CT_BOOLEAN, 0, 0, WPF_ENABLE_DEFAULT_COUNTERS, 0, &m_dwFlags },
376 { _T(""), CT_END_OF_LIST, 0, 0, 0, 0, NULL }
377 };
378
379
380 //
381 // Entry point for NetXMS agent
382 //
383
384 extern "C" BOOL __declspec(dllexport) __cdecl
385 NxSubAgentInit(NETXMS_SUBAGENT_INFO **ppInfo, TCHAR *pszConfigFile)
386 {
387 DWORD dwResult;
388
389 // Init parameters list
390 m_info.dwNumParameters = sizeof(m_parameters) / sizeof(NETXMS_SUBAGENT_PARAM);
391 m_info.pParamList = (NETXMS_SUBAGENT_PARAM *)nx_memdup(m_parameters, sizeof(m_parameters));
392
393 // Load configuration
394 cfgTemplate[0].pBuffer = malloc(CFG_BUFFER_SIZE);
395 *((TCHAR *)cfgTemplate[0].pBuffer) = 0;
396 dwResult = NxLoadConfig(pszConfigFile, _T("WinPerf"), cfgTemplate, FALSE);
397 if (dwResult == NXCFG_ERR_OK)
398 {
399 TCHAR *pItem, *pEnd;
400
401 // Parse counter list
402 for(pItem = (TCHAR *)cfgTemplate[0].pBuffer; *pItem != 0; pItem = pEnd + 1)
403 {
404 pEnd = _tcschr(pItem, _T('\n'));
405 if (pEnd != NULL)
406 *pEnd = 0;
407 StrStrip(pItem);
408 if (!AddCounterFromConfig(pItem))
409 NxWriteAgentLog(EVENTLOG_WARNING_TYPE,
410 _T("Unable to add counter from configuration file. "
411 "Original configuration record: %s",), pItem);
412 }
413
414 if (m_dwFlags & WPF_ENABLE_DEFAULT_COUNTERS)
415 AddPredefinedCounters();
416
417 // Create shitdown condition object
418 g_hCondShutdown = CreateEvent(NULL, TRUE, FALSE, NULL);
419
420 StartCollectorThreads();
421 }
422 free(cfgTemplate[0].pBuffer);
423 *ppInfo = &m_info;
424 return dwResult == NXCFG_ERR_OK;
425 }
426
427
428 //
429 // DLL entry point
430 //
431
432 BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
433 {
434 return TRUE;
435 }