- Changes in module support
[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 WPF_ENABLE_DEFAULT_COUNTERS 0x0001
31
32
33 //
34 // Global variables
35 //
36
37 HANDLE g_hCondShutdown = NULL;
38
39
40 //
41 // Static variables
42 //
43
44 static DWORD m_dwFlags = WPF_ENABLE_DEFAULT_COUNTERS;
45
46
47 //
48 // List of predefined performance counters
49 //
50 static struct
51 {
52 TCHAR *pszParamName;
53 TCHAR *pszCounterName;
54 int iClass;
55 int iNumSamples;
56 int iDataType;
57 } m_counterList[] =
58 {
59 { _T("System.CPU.LoadAvg"), _T("\\System\\Processor Queue Length"), 0, 60, COUNTER_TYPE_FLOAT },
60 { _T("System.CPU.LoadAvg5"), _T("\\System\\Processor Queue Length"), 0, 300, COUNTER_TYPE_FLOAT },
61 { _T("System.CPU.LoadAvg15"), _T("\\System\\Processor Queue Length"), 0, 900, COUNTER_TYPE_FLOAT },
62 { _T("System.CPU.Usage"), _T("\\Processor(_Total)\\% Processor Time"), 0, 60, COUNTER_TYPE_INT32 },
63 { _T("System.CPU.Usage5"), _T("\\Processor(_Total)\\% Processor Time"), 0, 300, COUNTER_TYPE_INT32 },
64 { _T("System.CPU.Usage15"), _T("\\Processor(_Total)\\% Processor Time"), 0, 900, COUNTER_TYPE_INT32 },
65 { NULL, NULL, 0, 0, 0 }
66 };
67
68
69 //
70 // Value of given performance counter
71 //
72
73 static LONG H_PdhVersion(TCHAR *pszParam, TCHAR *pArg, TCHAR *pValue)
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 //
85 // Value of given counter collected by one of the collector threads
86 //
87
88 LONG H_CollectedCounterData(TCHAR *pszParam, TCHAR *pArg, TCHAR *pValue)
89 {
90 switch(((WINPERF_COUNTER *)pArg)->wType)
91 {
92 case COUNTER_TYPE_INT32:
93 ret_int(pValue, ((WINPERF_COUNTER *)pArg)->value.iLong);
94 break;
95 case COUNTER_TYPE_INT64:
96 ret_int64(pValue, ((WINPERF_COUNTER *)pArg)->value.iLarge);
97 break;
98 case COUNTER_TYPE_FLOAT:
99 ret_double(pValue, ((WINPERF_COUNTER *)pArg)->value.dFloat);
100 break;
101 }
102 return SYSINFO_RC_SUCCESS;
103 }
104
105
106 //
107 // Value of given performance counter
108 //
109
110 static LONG H_PdhCounterValue(TCHAR *pszParam, TCHAR *pArg, TCHAR *pValue)
111 {
112 HQUERY hQuery;
113 HCOUNTER hCounter;
114 PDH_RAW_COUNTER rawData1, rawData2;
115 PDH_FMT_COUNTERVALUE counterValue;
116 PDH_STATUS rc;
117 TCHAR szCounter[MAX_PATH], szBuffer[16];
118 DWORD dwType;
119 BOOL bUseTwoSamples = FALSE;
120 static TCHAR szFName[] = _T("H_PdhCounterValue");
121
122 if ((!NxGetParameterArg(pszParam, 1, szCounter, MAX_PATH)) ||
123 (!NxGetParameterArg(pszParam, 2, szBuffer, 16)))
124 return SYSINFO_RC_UNSUPPORTED;
125
126 bUseTwoSamples = _tcstol(szBuffer, NULL, 0) ? TRUE : FALSE;
127
128 if ((rc = PdhOpenQuery(NULL, 0, &hQuery)) != ERROR_SUCCESS)
129 {
130 ReportPdhError(szFName, _T("PdhOpenQuery"), rc);
131 return SYSINFO_RC_ERROR;
132 }
133
134 if ((rc = PdhAddCounter(hQuery, szCounter, 0, &hCounter)) != ERROR_SUCCESS)
135 {
136 ReportPdhError(szFName, _T("PdhAddCounter"), rc);
137 PdhCloseQuery(hQuery);
138 return SYSINFO_RC_UNSUPPORTED;
139 }
140
141 // Get first sample
142 if ((rc = PdhCollectQueryData(hQuery)) != ERROR_SUCCESS)
143 {
144 ReportPdhError(szFName, _T("PdhCollectQueryData"), rc);
145 PdhCloseQuery(hQuery);
146 return SYSINFO_RC_ERROR;
147 }
148 PdhGetRawCounterValue(hCounter, &dwType, &rawData1);
149
150 // Get second sample if required
151 if (bUseTwoSamples)
152 {
153 Sleep(1000); // We will take second sample after one second
154 if ((rc = PdhCollectQueryData(hQuery)) != ERROR_SUCCESS)
155 {
156 ReportPdhError(szFName, _T("PdhCollectQueryData"), rc);
157 PdhCloseQuery(hQuery);
158 return SYSINFO_RC_ERROR;
159 }
160 PdhGetRawCounterValue(hCounter, NULL, &rawData2);
161 }
162
163 if (dwType & PERF_SIZE_LARGE)
164 {
165 if (bUseTwoSamples)
166 PdhCalculateCounterFromRawValue(hCounter, PDH_FMT_LARGE,
167 &rawData2, &rawData1, &counterValue);
168 else
169 PdhCalculateCounterFromRawValue(hCounter, PDH_FMT_LARGE,
170 &rawData1, NULL, &counterValue);
171 ret_int64(pValue, counterValue.largeValue);
172 }
173 else
174 {
175 if (bUseTwoSamples)
176 PdhCalculateCounterFromRawValue(hCounter, PDH_FMT_LONG,
177 &rawData2, &rawData1, &counterValue);
178 else
179 PdhCalculateCounterFromRawValue(hCounter, PDH_FMT_LONG,
180 &rawData1, NULL, &counterValue);
181 ret_int(pValue, counterValue.longValue);
182 }
183 PdhCloseQuery(hQuery);
184 return SYSINFO_RC_SUCCESS;
185 }
186
187
188 //
189 // List of available performance objects
190 //
191
192 static LONG H_PdhObjects(TCHAR *pszParam, TCHAR *pArg, NETXMS_VALUES_LIST *pValue)
193 {
194 TCHAR *pszObject, *pszObjList, szHostName[256];
195 LONG iResult = SYSINFO_RC_ERROR;
196 PDH_STATUS rc;
197 DWORD dwSize;
198
199 dwSize = 256;
200 if (GetComputerName(szHostName, &dwSize))
201 {
202 dwSize = 256000;
203 pszObjList = (TCHAR *)malloc(sizeof(TCHAR) * dwSize);
204 if ((rc = PdhEnumObjects(NULL, szHostName, pszObjList, &dwSize,
205 PERF_DETAIL_WIZARD, TRUE)) == ERROR_SUCCESS)
206 {
207 for(pszObject = pszObjList; *pszObject != 0; pszObject += _tcslen(pszObject) + 1)
208 NxAddResultString(pValue, pszObject);
209 iResult = SYSINFO_RC_SUCCESS;
210 }
211 else
212 {
213 ReportPdhError(_T("H_PdhObjects"), _T("PdhEnumObjects"), rc);
214 }
215 free(pszObjList);
216 }
217 return iResult;
218 }
219
220
221 //
222 // List of available performance items for given object
223 //
224
225 static LONG H_PdhObjectItems(TCHAR *pszParam, TCHAR *pArg, NETXMS_VALUES_LIST *pValue)
226 {
227 TCHAR *pszElement, *pszCounterList, *pszInstanceList, szHostName[256], szObject[256];
228 LONG iResult = SYSINFO_RC_ERROR;
229 DWORD dwSize1, dwSize2;
230 PDH_STATUS rc;
231
232 NxGetParameterArg(pszParam, 1, szObject, 256);
233 if (szObject[0] != 0)
234 {
235 dwSize1 = 256;
236 if (GetComputerName(szHostName, &dwSize1))
237 {
238 dwSize1 = dwSize2 = 256000;
239 pszCounterList = (TCHAR *)malloc(sizeof(TCHAR) * dwSize1);
240 pszInstanceList = (TCHAR *)malloc(sizeof(TCHAR) * dwSize2);
241 rc = PdhEnumObjectItems(NULL, szHostName, szObject,
242 pszCounterList, &dwSize1, pszInstanceList, &dwSize2,
243 PERF_DETAIL_WIZARD, 0);
244 if ((rc == ERROR_SUCCESS) || (rc == PDH_MORE_DATA))
245 {
246 for(pszElement = (pArg[0] == _T('C')) ? pszCounterList : pszInstanceList;
247 *pszElement != 0; pszElement += _tcslen(pszElement) + 1)
248 NxAddResultString(pValue, pszElement);
249 iResult = SYSINFO_RC_SUCCESS;
250 }
251 else
252 {
253 ReportPdhError(_T("H_PdhObjectItems"), _T("PdhEnumObjectItems"), rc);
254 }
255 free(pszCounterList);
256 free(pszInstanceList);
257 }
258 }
259 else
260 {
261 iResult = SYSINFO_RC_UNSUPPORTED;
262 }
263 return iResult;
264 }
265
266
267 //
268 // Value of specific performance parameter, which is mapped one-to-one to
269 // performance counter. Actually, it's an alias for PDH.CounterValue(xxx) parameter.
270 //
271
272 static LONG H_CounterAlias(TCHAR *pszParam, TCHAR *pArg, TCHAR *pValue)
273 {
274 return H_PdhCounterValue(pArg, NULL, pValue);
275 }
276
277
278 //
279 // Handler for subagent unload
280 //
281
282 static void OnUnload(void)
283 {
284 if (g_hCondShutdown != NULL)
285 SetEvent(g_hCondShutdown);
286 Sleep(500);
287 }
288
289
290 //
291 // Subagent information
292 //
293
294 static NETXMS_SUBAGENT_PARAM m_parameters[] =
295 {
296 { _T("PDH.CounterValue(*)"), H_PdhCounterValue, NULL },
297 { _T("PDH.Version"), H_PdhVersion, NULL },
298 { _T("System.Threads"), H_CounterAlias, _T("(\\System\\Threads)") },
299 { _T("System.Uptime"), H_CounterAlias, _T("(\\System\\System Up Time)") }
300 };
301 static NETXMS_SUBAGENT_ENUM m_enums[] =
302 {
303 { _T("PDH.ObjectCounters(*)"), H_PdhObjectItems, _T("C") },
304 { _T("PDH.ObjectInstances(*)"), H_PdhObjectItems, _T("I") },
305 { _T("PDH.Objects"), H_PdhObjects, NULL }
306 };
307
308 static NETXMS_SUBAGENT_INFO m_info =
309 {
310 NETXMS_SUBAGENT_INFO_MAGIC,
311 _T("WinPerf"), _T(NETXMS_VERSION_STRING) _T(DEBUG_SUFFIX), OnUnload,
312 0, NULL, NULL,
313 sizeof(m_enums) / sizeof(NETXMS_SUBAGENT_ENUM),
314 m_enums
315 };
316
317
318 //
319 // Add new parameter to list
320 //
321
322 BOOL AddParameter(TCHAR *pszName, LONG (* fpHandler)(TCHAR *, TCHAR *, TCHAR *), TCHAR *pArg)
323 {
324 DWORD i;
325
326 for(i = 0; i < m_info.dwNumParameters; i++)
327 if (!_tcsicmp(pszName, m_info.pParamList[i].szName))
328 break;
329
330 if (i == m_info.dwNumParameters)
331 {
332 // Extend list
333 m_info.dwNumParameters++;
334 m_info.pParamList =
335 (NETXMS_SUBAGENT_PARAM *)realloc(m_info.pParamList,
336 sizeof(NETXMS_SUBAGENT_PARAM) * m_info.dwNumParameters);
337 }
338
339 _tcsncpy(m_info.pParamList[i].szName, pszName, MAX_PARAM_NAME);
340 m_info.pParamList[i].fpHandler = fpHandler;
341 m_info.pParamList[i].pArg = pArg;
342
343 return TRUE;
344 }
345
346
347 //
348 // Add predefined counters
349 //
350
351 static void AddPredefinedCounters(void)
352 {
353 int i;
354 WINPERF_COUNTER *pCnt;
355
356 for(i = 0; m_counterList[i].pszParamName != NULL; i++)
357 {
358 pCnt = AddCounter(m_counterList[i].pszCounterName, m_counterList[i].iClass,
359 m_counterList[i].iNumSamples, m_counterList[i].iDataType);
360 if (pCnt != NULL)
361 AddParameter(m_counterList[i].pszParamName, H_CollectedCounterData, (TCHAR *)pCnt);
362 }
363 }
364
365
366 //
367 // Configuration file template
368 //
369
370 static TCHAR *m_pszCounterList = NULL;
371 static NX_CFG_TEMPLATE cfgTemplate[] =
372 {
373 { _T("Counter"), CT_STRING_LIST, _T('\n'), 0, 0, 0, &m_pszCounterList },
374 { _T("EnableDefaultCounters"), CT_BOOLEAN, 0, 0, WPF_ENABLE_DEFAULT_COUNTERS, 0, &m_dwFlags },
375 { _T(""), CT_END_OF_LIST, 0, 0, 0, 0, NULL }
376 };
377
378
379 //
380 // Entry point for NetXMS agent
381 //
382
383 extern "C" BOOL __declspec(dllexport) __cdecl
384 NxSubAgentInit(NETXMS_SUBAGENT_INFO **ppInfo, TCHAR *pszConfigFile)
385 {
386 DWORD dwResult;
387
388 // Init parameters list
389 m_info.dwNumParameters = sizeof(m_parameters) / sizeof(NETXMS_SUBAGENT_PARAM);
390 m_info.pParamList = (NETXMS_SUBAGENT_PARAM *)nx_memdup(m_parameters, sizeof(m_parameters));
391
392 // Load configuration
393 dwResult = NxLoadConfig(pszConfigFile, _T("WinPerf"), cfgTemplate, FALSE);
394 if (dwResult == NXCFG_ERR_OK)
395 {
396 TCHAR *pItem, *pEnd;
397
398 // Parse counter list
399 if (m_pszCounterList != NULL)
400 {
401 for(pItem = m_pszCounterList; *pItem != 0; pItem = pEnd + 1)
402 {
403 pEnd = _tcschr(pItem, _T('\n'));
404 if (pEnd != NULL)
405 *pEnd = 0;
406 StrStrip(pItem);
407 if (!AddCounterFromConfig(pItem))
408 NxWriteAgentLog(EVENTLOG_WARNING_TYPE,
409 _T("Unable to add counter from configuration file. "
410 "Original configuration record: %s",), pItem);
411 }
412 free(m_pszCounterList);
413 }
414
415 if (m_dwFlags & WPF_ENABLE_DEFAULT_COUNTERS)
416 AddPredefinedCounters();
417
418 // Create shitdown condition object
419 g_hCondShutdown = CreateEvent(NULL, TRUE, FALSE, NULL);
420
421 StartCollectorThreads();
422 }
423 else
424 {
425 safe_free(m_pszCounterList);
426 }
427 *ppInfo = &m_info;
428 return dwResult == NXCFG_ERR_OK;
429 }
430
431
432 //
433 // DLL entry point
434 //
435
436 BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
437 {
438 return TRUE;
439 }