Commit | Line | Data |
---|---|---|
5039dede AK |
1 | /* |
2 | ** Windows Performance NetXMS subagent | |
d02f6b92 | 3 | ** Copyright (C) 2004-2013 Victor Kirhenshtein |
5039dede AK |
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 | ||
2658158a VK |
25 | /** |
26 | * Constants | |
27 | */ | |
28 | #define MAX_CPU_COUNT 256 | |
5039dede AK |
29 | |
30 | #define WPF_ENABLE_DEFAULT_COUNTERS 0x0001 | |
31 | ||
d02f6b92 VK |
32 | /** |
33 | * Static variables | |
34 | */ | |
5039dede AK |
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]; | |
d02f6b92 VK |
40 | static MUTEX s_autoCountersLock = MutexCreate(); |
41 | static StringObjectMap<WINPERF_COUNTER> *s_autoCounters = new StringObjectMap<WINPERF_COUNTER>(false); | |
5039dede | 42 | |
d02f6b92 VK |
43 | /** |
44 | * List of predefined performance counters | |
45 | */ | |
5039dede AK |
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 | { | |
2658158a VK |
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 }, | |
5039dede AK |
65 | { NULL, NULL, 0, 0, 0, NULL, 0 } |
66 | }; | |
67 | ||
d02f6b92 VK |
68 | /** |
69 | * Handler for PDH.Version parameter | |
70 | */ | |
060c5a11 | 71 | static LONG H_PdhVersion(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *pValue, AbstractCommSession *session) |
5039dede AK |
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 | ||
d02f6b92 VK |
81 | /** |
82 | * Handler for WinPerf.Features parameter | |
83 | */ | |
060c5a11 | 84 | static LONG H_WinPerfFeatures(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *pValue, AbstractCommSession *session) |
d02f6b92 VK |
85 | { |
86 | ret_uint(pValue, WINPERF_AUTOMATIC_SAMPLE_COUNT | WINPERF_REMOTE_COUNTER_CONFIG); | |
87 | return SYSINFO_RC_SUCCESS; | |
88 | } | |
5039dede | 89 | |
d02f6b92 VK |
90 | /** |
91 | * Value of CPU utilization counter | |
92 | */ | |
060c5a11 | 93 | static LONG H_CPUUsage(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *pValue, AbstractCommSession *session) |
5039dede AK |
94 | { |
95 | LONG nProcessor, nRet = SYSINFO_RC_SUCCESS; | |
96 | TCHAR *pEnd, szBuffer[16]; | |
060c5a11 | 97 | |
6173bea8 | 98 | if (!AgentGetParameterArg(pszParam, 1, szBuffer, 16)) |
5039dede AK |
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 | ||
46ee6286 VK |
133 | /** |
134 | * Value of given counter collected by one of the collector threads | |
135 | */ | |
060c5a11 | 136 | LONG H_CollectedCounterData(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *pValue, AbstractCommSession *session) |
5039dede AK |
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 | ||
46ee6286 VK |
153 | /** |
154 | * Value of given performance counter | |
155 | */ | |
060c5a11 | 156 | static LONG H_PdhCounterValue(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *pValue, AbstractCommSession *session) |
5039dede AK |
157 | { |
158 | HQUERY hQuery; | |
159 | HCOUNTER hCounter; | |
160 | PDH_RAW_COUNTER rawData1, rawData2; | |
161 | PDH_FMT_COUNTERVALUE counterValue; | |
162 | PDH_STATUS rc; | |
46ee6286 | 163 | TCHAR szCounter[MAX_PATH]; |
5039dede AK |
164 | DWORD dwType; |
165 | BOOL bUseTwoSamples = FALSE; | |
166 | static TCHAR szFName[] = _T("H_PdhCounterValue"); | |
167 | ||
168 | if (pArg == NULL) // Normal call | |
169 | { | |
46ee6286 | 170 | if (!AgentGetParameterArg(pszParam, 1, szCounter, MAX_PATH)) |
5039dede | 171 | return SYSINFO_RC_UNSUPPORTED; |
d02f6b92 VK |
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); | |
e9cee163 | 194 | return (counter != NULL) ? H_CollectedCounterData(pszParam, (const TCHAR *)counter, pValue, session) : SYSINFO_RC_UNSUPPORTED; |
d02f6b92 VK |
195 | } |
196 | } | |
5039dede AK |
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 | { | |
aed55395 VK |
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 | } | |
5039dede AK |
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 | |
46ee6286 | 240 | if ((dwType & 0x00000C00) == PERF_TYPE_COUNTER) |
5039dede | 241 | { |
46ee6286 VK |
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 | } | |
5039dede AK |
255 | } |
256 | ||
46ee6286 | 257 | if ((dwType & 0x00000300) == PERF_SIZE_LARGE) |
5039dede AK |
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 | ||
46ee6286 VK |
281 | /** |
282 | * List of available performance objects | |
283 | */ | |
060c5a11 | 284 | static LONG H_PdhObjects(const TCHAR *pszParam, const TCHAR *pArg, StringList *value, AbstractCommSession *session) |
5039dede AK |
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); | |
060c5a11 | 296 | if ((rc = PdhEnumObjects(NULL, szHostName, pszObjList, &dwSize, |
5039dede AK |
297 | PERF_DETAIL_WIZARD, TRUE)) == ERROR_SUCCESS) |
298 | { | |
299 | for(pszObject = pszObjList; *pszObject != 0; pszObject += _tcslen(pszObject) + 1) | |
6173bea8 | 300 | value->add(pszObject); |
5039dede AK |
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 | ||
46ee6286 VK |
312 | /** |
313 | * List of available performance items for given object | |
314 | */ | |
060c5a11 | 315 | static LONG H_PdhObjectItems(const TCHAR *pszParam, const TCHAR *pArg, StringList *value, AbstractCommSession *session) |
5039dede AK |
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 | ||
6173bea8 | 322 | AgentGetParameterArg(pszParam, 1, szObject, 256); |
5039dede AK |
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, | |
060c5a11 | 332 | pszCounterList, &dwSize1, pszInstanceList, &dwSize2, |
5039dede AK |
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) | |
6173bea8 | 338 | value->add(pszElement); |
5039dede AK |
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 | ||
d02f6b92 VK |
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 | */ | |
060c5a11 | 360 | static LONG H_CounterAlias(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *pValue, AbstractCommSession *session) |
5039dede | 361 | { |
e9cee163 | 362 | return H_PdhCounterValue(NULL, pArg, pValue, session); |
5039dede AK |
363 | } |
364 | ||
2658158a VK |
365 | /** |
366 | * Handler for System.Memory.Physical.FreePerc parameter | |
367 | */ | |
060c5a11 | 368 | static LONG H_FreeMemoryPct(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *pValue, AbstractCommSession *session) |
2658158a VK |
369 | { |
370 | TCHAR buffer[MAX_RESULT_LENGTH]; | |
e9cee163 | 371 | LONG rc = H_PdhCounterValue(NULL, pArg, buffer, session); |
2658158a VK |
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 | ||
d02f6b92 VK |
386 | /** |
387 | * Initialize subagent | |
388 | */ | |
ff175e91 | 389 | static BOOL SubAgentInit(Config *config) |
5039dede | 390 | { |
5039dede AK |
391 | StartCollectorThreads(); |
392 | return TRUE; | |
393 | } | |
394 | ||
0f506caa VK |
395 | /** |
396 | * Handler for subagent unload | |
397 | */ | |
398 | static void SubAgentShutdown() | |
5039dede | 399 | { |
53720bbf | 400 | JoinCollectorThreads(); |
5039dede AK |
401 | } |
402 | ||
0f506caa | 403 | /** |
2658158a | 404 | * Supported parameters |
0f506caa | 405 | */ |
5039dede AK |
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") }, | |
7ee7f8bc VK |
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 }, | |
d02f6b92 VK |
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") }, | |
5039dede | 416 | }; |
2658158a VK |
417 | |
418 | /** | |
419 | * Supported lists | |
420 | */ | |
421 | static NETXMS_SUBAGENT_LIST m_lists[] = | |
5039dede AK |
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 | ||
2658158a VK |
428 | /** |
429 | * Subagent information | |
430 | */ | |
5039dede AK |
431 | static NETXMS_SUBAGENT_INFO m_info = |
432 | { | |
433 | NETXMS_SUBAGENT_INFO_MAGIC, | |
7ee7f8bc | 434 | _T("WinPerf"), NETXMS_VERSION_STRING, |
5039dede AK |
435 | SubAgentInit, SubAgentShutdown, NULL, // handlers |
436 | 0, NULL, // parameters | |
2658158a VK |
437 | sizeof(m_lists) / sizeof(NETXMS_SUBAGENT_LIST), |
438 | m_lists, | |
f1a20e8d VK |
439 | 0, NULL, // tables |
440 | 0, NULL, // actions | |
441 | 0, NULL // push parameters | |
5039dede AK |
442 | }; |
443 | ||
d02f6b92 VK |
444 | /** |
445 | * Add new parameter to list | |
446 | */ | |
060c5a11 | 447 | BOOL AddParameter(TCHAR *pszName, LONG (* fpHandler)(const TCHAR *, const TCHAR *, TCHAR *, AbstractCommSession *), TCHAR *pArg, int iDataType, TCHAR *pszDescription) |
5039dede AK |
448 | { |
449 | DWORD i; | |
450 | ||
f1a20e8d VK |
451 | for(i = 0; i < m_info.numParameters; i++) |
452 | if (!_tcsicmp(pszName, m_info.parameters[i].name)) | |
5039dede AK |
453 | break; |
454 | ||
f1a20e8d | 455 | if (i == m_info.numParameters) |
5039dede AK |
456 | { |
457 | // Extend list | |
f1a20e8d | 458 | m_info.numParameters++; |
060c5a11 | 459 | m_info.parameters = |
f1a20e8d VK |
460 | (NETXMS_SUBAGENT_PARAM *)realloc(m_info.parameters, |
461 | sizeof(NETXMS_SUBAGENT_PARAM) * m_info.numParameters); | |
5039dede AK |
462 | } |
463 | ||
f1a20e8d VK |
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); | |
060c5a11 | 469 | |
5039dede AK |
470 | return TRUE; |
471 | } | |
472 | ||
0f506caa VK |
473 | /** |
474 | * Add predefined counters | |
475 | */ | |
f1a20e8d | 476 | static void AddPredefinedCounters() |
5039dede AK |
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) | |
060c5a11 | 488 | AddParameter(m_counterList[i].pszParamName, H_CollectedCounterData, |
489 | (TCHAR *)pCnt, m_counterList[i].iDCIDataType, | |
5039dede AK |
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 | ||
0f506caa VK |
505 | /** |
506 | * Configuration file template | |
507 | */ | |
5039dede | 508 | static TCHAR *m_pszCounterList = NULL; |
ff175e91 | 509 | static NX_CFG_TEMPLATE m_cfgTemplate[] = |
5039dede AK |
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 | ||
0f506caa VK |
516 | /** |
517 | * Entry point for NetXMS agent | |
518 | */ | |
519 | DECLARE_SUBAGENT_ENTRY_POINT(WINPERF) | |
5039dede | 520 | { |
f1a20e8d | 521 | if (m_info.parameters != NULL) |
5039dede AK |
522 | return FALSE; // Most likely another instance of WINPERF subagent already loaded |
523 | ||
524 | // Read performance counter indexes | |
7fabd388 VK |
525 | TCHAR *counters = NULL; |
526 | size_t countersBufferSize = 0; | |
527 | DWORD status; | |
5039dede AK |
528 | do |
529 | { | |
7fabd388 VK |
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_PERFORMANCE_DATA, _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_PERFORMANCE_DATA, _T("Counter"), NULL, &type, (BYTE *)counters, &bytes); | |
553 | } | |
554 | RegCloseKey(hKey); | |
555 | } | |
556 | } | |
557 | if (status == ERROR_SUCCESS) | |
5039dede | 558 | { |
7fabd388 | 559 | CreateCounterIndex(counters); |
5039dede | 560 | } |
7fabd388 | 561 | safe_free(counters); |
5039dede AK |
562 | |
563 | // Init parameters list | |
f1a20e8d VK |
564 | m_info.numParameters = sizeof(m_parameters) / sizeof(NETXMS_SUBAGENT_PARAM); |
565 | m_info.parameters = (NETXMS_SUBAGENT_PARAM *)nx_memdup(m_parameters, sizeof(m_parameters)); | |
5039dede AK |
566 | |
567 | // Check counter names for H_CounterAlias | |
7fabd388 VK |
568 | TCHAR *newName; |
569 | for(UINT32 i = 0; i < m_info.numParameters; i++) | |
5039dede | 570 | { |
f1a20e8d | 571 | if (m_info.parameters[i].handler == H_CounterAlias) |
5039dede | 572 | { |
f1a20e8d | 573 | CheckCounter(m_info.parameters[i].arg, &newName); |
5039dede | 574 | if (newName != NULL) |
f1a20e8d | 575 | m_info.parameters[i].arg = newName; |
5039dede AK |
576 | } |
577 | } | |
578 | ||
2658158a VK |
579 | // Add System.Memory.Free* handlers if "\Memory\Free & Zero Page List Bytes" counter is available |
580 | const TCHAR *counter = _T("\\Memory\\Free & Zero Page List Bytes"); | |
581 | CheckCounter(counter, &newName); | |
582 | if (newName != NULL) | |
583 | counter = newName; | |
584 | TCHAR value[MAX_RESULT_LENGTH]; | |
e9cee163 | 585 | if (H_PdhCounterValue(NULL, counter, value, NULL) == SYSINFO_RC_SUCCESS) |
2658158a VK |
586 | { |
587 | AgentWriteDebugLog(4, _T("WinPerf: \"\\Memory\\Free & Zero Page List Bytes\" is supported")); | |
588 | AddParameter(_T("System.Memory.Physical.Free"), H_CounterAlias, (TCHAR *)counter, DCI_DT_UINT64, DCIDESC_SYSTEM_MEMORY_PHYSICAL_FREE); | |
589 | AddParameter(_T("System.Memory.Physical.FreePerc"), H_FreeMemoryPct, (TCHAR *)counter, DCI_DT_UINT, DCIDESC_SYSTEM_MEMORY_PHYSICAL_FREE_PCT); | |
590 | } | |
591 | else | |
592 | { | |
593 | AgentWriteDebugLog(4, _T("WinPerf: \"\\Memory\\Free & Zero Page List Bytes\" is not supported")); | |
594 | safe_free(newName); | |
595 | } | |
596 | ||
5039dede | 597 | // Load configuration |
97a92859 | 598 | bool success = config->parseTemplate(_T("WinPerf"), m_cfgTemplate); |
ff175e91 | 599 | if (success) |
5039dede AK |
600 | { |
601 | TCHAR *pItem, *pEnd; | |
602 | ||
603 | // Parse counter list | |
604 | if (m_pszCounterList != NULL) | |
605 | { | |
606 | for(pItem = m_pszCounterList; *pItem != 0; pItem = pEnd + 1) | |
607 | { | |
608 | pEnd = _tcschr(pItem, _T('\n')); | |
609 | if (pEnd != NULL) | |
610 | *pEnd = 0; | |
611 | StrStrip(pItem); | |
612 | if (!AddCounterFromConfig(pItem)) | |
060c5a11 | 613 | AgentWriteLog(EVENTLOG_WARNING_TYPE, |
5039dede AK |
614 | _T("Unable to add counter from configuration file. ") |
615 | _T("Original configuration record: %s"), pItem); | |
616 | } | |
617 | free(m_pszCounterList); | |
618 | } | |
619 | ||
620 | if (m_dwFlags & WPF_ENABLE_DEFAULT_COUNTERS) | |
621 | AddPredefinedCounters(); | |
622 | } | |
623 | else | |
624 | { | |
625 | safe_free(m_pszCounterList); | |
626 | } | |
627 | *ppInfo = &m_info; | |
ff175e91 | 628 | return success; |
5039dede AK |
629 | } |
630 | ||
0f506caa VK |
631 | /** |
632 | * DLL entry point | |
633 | */ | |
5039dede AK |
634 | BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) |
635 | { | |
636 | if (dwReason == DLL_PROCESS_ATTACH) | |
637 | DisableThreadLibraryCalls(hInstance); | |
638 | return TRUE; | |
639 | } |