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