- Implemented collector thread
[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 static 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 // Handler for subagent unload
271 //
272
273 static void OnUnload(void)
274 {
275 if (g_hCondShutdown != NULL)
276 SetEvent(g_hCondShutdown);
277 Sleep(500);
278 }
279
280
281 //
282 // Subagent information
283 //
284
285 static NETXMS_SUBAGENT_PARAM m_parameters[] =
286 {
287 { _T("PDH.CounterValue(*)"), H_PdhCounterValue, NULL },
288 { _T("PDH.Version"), H_PdhVersion, NULL }
289 };
290 static NETXMS_SUBAGENT_ENUM m_enums[] =
291 {
292 { _T("PDH.ObjectCounters(*)"), H_PdhObjectItems, _T("C") },
293 { _T("PDH.ObjectInstances(*)"), H_PdhObjectItems, _T("I") },
294 { _T("PDH.Objects"), H_PdhObjects, NULL }
295 };
296
297 static NETXMS_SUBAGENT_INFO m_info =
298 {
299 "WinPerf", 0x01000000, OnUnload,
300 0, NULL,
301 sizeof(m_enums) / sizeof(NETXMS_SUBAGENT_ENUM),
302 m_enums
303 };
304
305
306 //
307 // Add new parameter to list
308 //
309
310 static BOOL AddParameter(TCHAR *pszName, LONG (* fpHandler)(TCHAR *, TCHAR *, TCHAR *), TCHAR *pArg)
311 {
312 DWORD i;
313
314 for(i = 0; i < m_info.dwNumParameters; i++)
315 if (!_tcsicmp(pszName, m_info.pParamList[i].szName))
316 break;
317
318 if (i == m_info.dwNumParameters)
319 {
320 // Extend list
321 m_info.dwNumParameters++;
322 m_info.pParamList =
323 (NETXMS_SUBAGENT_PARAM *)realloc(m_info.pParamList,
324 sizeof(NETXMS_SUBAGENT_PARAM) * m_info.dwNumParameters);
325 }
326
327 _tcsncpy(m_info.pParamList[i].szName, pszName, MAX_PARAM_NAME);
328 m_info.pParamList[i].fpHandler = fpHandler;
329 m_info.pParamList[i].pArg = pArg;
330
331 return TRUE;
332 }
333
334
335 //
336 // Add predefined counters
337 //
338
339 static void AddPredefinedCounters(void)
340 {
341 int i;
342 WINPERF_COUNTER *pCnt;
343
344 for(i = 0; m_counterList[i].pszParamName != NULL; i++)
345 {
346 pCnt = AddCounter(m_counterList[i].pszCounterName, m_counterList[i].iClass,
347 m_counterList[i].iNumSamples, m_counterList[i].iDataType);
348 if (pCnt != NULL)
349 AddParameter(m_counterList[i].pszParamName, H_CollectedCounterData, (TCHAR *)pCnt);
350 }
351 }
352
353
354 //
355 // Configuration file template
356 //
357
358 static NX_CFG_TEMPLATE cfgTemplate[] =
359 {
360 { _T("Counter"), CT_STRING_LIST, _T('\n'), 0, CFG_BUFFER_SIZE, 0, NULL },
361 { _T("EnableDefaultCounters"), CT_BOOLEAN, 0, 0, WPF_ENABLE_DEFAULT_COUNTERS, 0, &m_dwFlags },
362 { _T(""), CT_END_OF_LIST, 0, 0, 0, 0, NULL }
363 };
364
365
366 //
367 // Entry point for NetXMS agent
368 //
369
370 extern "C" BOOL __declspec(dllexport) __cdecl
371 NxSubAgentInit(NETXMS_SUBAGENT_INFO **ppInfo, TCHAR *pszConfigFile)
372 {
373 DWORD dwResult;
374
375 // Init parameters list
376 m_info.dwNumParameters = sizeof(m_parameters) / sizeof(NETXMS_SUBAGENT_PARAM);
377 m_info.pParamList = (NETXMS_SUBAGENT_PARAM *)nx_memdup(m_parameters, sizeof(m_parameters));
378
379 // Load configuration
380 cfgTemplate[0].pBuffer = malloc(CFG_BUFFER_SIZE);
381 dwResult = NxLoadConfig(pszConfigFile, _T("WinPerf"), cfgTemplate, FALSE);
382 if (dwResult == NXCFG_ERR_OK)
383 {
384 TCHAR *pItem, *pEnd;
385
386 // Parse counter list
387 for(pItem = (TCHAR *)cfgTemplate[0].pBuffer; *pItem != 0; pItem = pEnd + 1)
388 {
389 pEnd = _tcschr(pItem, _T('\n'));
390 if (pEnd != NULL)
391 *pEnd = 0;
392 StrStrip(pItem);
393 if (!AddCounterFromConfig(pItem))
394 NxWriteAgentLog(EVENTLOG_WARNING_TYPE,
395 _T("Unable to add counter from configuration file. "
396 "Original configuration record: %s",), pItem);
397 }
398
399 if (m_dwFlags & WPF_ENABLE_DEFAULT_COUNTERS)
400 AddPredefinedCounters();
401
402 // Create shitdown condition object
403 g_hCondShutdown = CreateEvent(NULL, TRUE, FALSE, NULL);
404
405 StartCollectorThreads();
406 }
407 free(cfgTemplate[0].pBuffer);
408 *ppInfo = &m_info;
409 return dwResult == NXCFG_ERR_OK;
410 }
411
412
413 //
414 // DLL entry point
415 //
416
417 BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
418 {
419 return TRUE;
420 }