908b20ab9bfd4772a6ee738b4c9f664f5976721c
[public/netxms.git] / src / agent / subagents / winperf / collect.cpp
1 /*
2 ** Windows Performance NetXMS subagent
3 ** Copyright (C) 2004-2012 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 ** File: collect.cpp
20 **
21 **/
22
23 #include "winperf.h"
24
25
26 //
27 // Static data
28 //
29
30 WINPERF_COUNTER_SET m_cntSet[3] =
31 {
32 { 1000, 0, NULL, _T('A') },
33 { 5000, 0, NULL, _T('B') },
34 { 60000, 0, NULL, _T('C') }
35 };
36
37
38 //
39 // Check that counter's name is valid and determine counter's type
40 //
41
42 int CheckCounter(const TCHAR *pszName, TCHAR **ppszNewName)
43 {
44 HQUERY hQuery;
45 HCOUNTER hCounter;
46 PDH_STATUS rc;
47 PDH_COUNTER_INFO ci;
48 DWORD dwSize;
49 static TCHAR szFName[] = _T("CheckCounter");
50
51 *ppszNewName = NULL;
52
53 if ((rc = PdhOpenQuery(NULL, 0, &hQuery)) != ERROR_SUCCESS)
54 {
55 ReportPdhError(szFName, _T("PdhOpenQuery"), rc);
56 return -1;
57 }
58
59 rc = PdhAddCounter(hQuery, pszName, 0, &hCounter);
60 if (rc != ERROR_SUCCESS)
61 {
62 // Attempt to translate counter name
63 if ((rc == PDH_CSTATUS_NO_COUNTER) || (rc == PDH_CSTATUS_NO_OBJECT))
64 {
65 *ppszNewName = (TCHAR *)malloc(_tcslen(pszName) * sizeof(TCHAR) * 4);
66 if (TranslateCounterName(pszName, *ppszNewName))
67 {
68 AgentWriteDebugLog(2, _T("WINPERF: Counter translated: %s ==> %s"), pszName, *ppszNewName);
69 rc = PdhAddCounter(hQuery, *ppszNewName, 0, &hCounter);
70 if (rc != ERROR_SUCCESS)
71 {
72 free(*ppszNewName);
73 *ppszNewName = NULL;
74 }
75 }
76 }
77 if (rc != ERROR_SUCCESS)
78 {
79 ReportPdhError(szFName, _T("PdhAddCounter"), rc);
80 PdhCloseQuery(hQuery);
81 return -1;
82 }
83 }
84
85 dwSize = sizeof(ci);
86 if ((rc = PdhGetCounterInfo(hCounter, FALSE, &dwSize, &ci)) != ERROR_SUCCESS)
87 {
88 ReportPdhError(szFName, _T("PdhGetCounterInfo"), rc);
89 PdhCloseQuery(hQuery);
90 return -1;
91 }
92
93 PdhCloseQuery(hQuery);
94 return (ci.dwType & PERF_SIZE_LARGE) ? COUNTER_TYPE_INT64 : COUNTER_TYPE_INT32;
95 }
96
97
98 //
99 // Add counter to set
100 //
101
102 WINPERF_COUNTER *AddCounter(TCHAR *pszName, int iClass, int iNumSamples, int iDataType)
103 {
104 WINPERF_COUNTER *pCnt;
105 TCHAR *pszNewName;
106 int iType;
107
108 // Check for valid class
109 if ((iClass < 0) || (iClass > 2))
110 return NULL;
111
112 // Check counter's name and get it's type
113 iType = CheckCounter(pszName, &pszNewName);
114 if (iType == -1)
115 {
116 safe_free(pszNewName);
117 return NULL;
118 }
119
120 // Create new counter
121 pCnt = (WINPERF_COUNTER *)malloc(sizeof(WINPERF_COUNTER));
122 memset(pCnt, 0, sizeof(WINPERF_COUNTER));
123 if (pszNewName != NULL)
124 {
125 pCnt->pszName = (TCHAR *)realloc(pszNewName, (_tcslen(pszNewName) + 1) * sizeof(TCHAR));
126 }
127 else
128 {
129 pCnt->pszName = _tcsdup(pszName);
130 }
131 pCnt->wType = (iDataType == COUNTER_TYPE_AUTO) ? iType : iDataType;
132 pCnt->wNumSamples = iNumSamples;
133 pCnt->pRawValues = (PDH_RAW_COUNTER *)malloc(sizeof(PDH_RAW_COUNTER) * iNumSamples);
134 memset(pCnt->pRawValues, 0, sizeof(PDH_RAW_COUNTER) * iNumSamples);
135 switch(pCnt->wType)
136 {
137 case COUNTER_TYPE_INT32:
138 pCnt->dwFormat = PDH_FMT_LONG;
139 break;
140 case COUNTER_TYPE_INT64:
141 pCnt->dwFormat = PDH_FMT_LARGE;
142 break;
143 case COUNTER_TYPE_FLOAT:
144 pCnt->dwFormat = PDH_FMT_DOUBLE;
145 break;
146 }
147
148 // Add counter to set
149 m_cntSet[iClass].dwNumCounters++;
150 m_cntSet[iClass].ppCounterList =
151 (WINPERF_COUNTER **)realloc(m_cntSet[iClass].ppCounterList, sizeof(WINPERF_COUNTER *) * m_cntSet[iClass].dwNumCounters);
152 m_cntSet[iClass].ppCounterList[m_cntSet[iClass].dwNumCounters - 1] = pCnt;
153
154 return pCnt;
155 }
156
157
158 //
159 // Add custom counter from configuration file
160 // Should be in form
161 // <parameter name>:<counter path>:<number of samples>:<class>:<data type>:<description>
162 // Class can be A (poll every second), B (poll every 5 seconds) or C (poll every 30 seconds)
163 //
164
165 BOOL AddCounterFromConfig(TCHAR *pszStr)
166 {
167 TCHAR *ptr, *eptr, *pszCurrField;
168 TCHAR szParamName[MAX_PARAM_NAME], szCounterPath[MAX_PATH];
169 TCHAR szDescription[MAX_DB_STRING] = _T("");
170 int iState, iField, iNumSamples, iClass, iPos, iDataType = DCI_DT_INT;
171 WINPERF_COUNTER *pCnt;
172
173 // Parse line
174 pszCurrField = (TCHAR *)malloc(sizeof(TCHAR) * (_tcslen(pszStr) + 1));
175 for(ptr = pszStr, iState = 0, iField = 0, iPos = 0; (iState != -1) && (iState != 255); ptr++)
176 {
177 switch(iState)
178 {
179 case 0: // Normal text
180 switch(*ptr)
181 {
182 case '\'':
183 iState = 1;
184 break;
185 case '"':
186 iState = 2;
187 break;
188 case ':': // New field
189 case 0:
190 pszCurrField[iPos] = 0;
191 switch(iField)
192 {
193 case 0: // Parameter name
194 nx_strncpy(szParamName, pszCurrField, MAX_PARAM_NAME);
195 break;
196 case 1: // Counter path
197 nx_strncpy(szCounterPath, pszCurrField, MAX_PATH);
198 break;
199 case 2: // Number of samples
200 iNumSamples = _tcstol(pszCurrField, &eptr, 0);
201 if (*eptr != 0)
202 iState = 255; // Error
203 break;
204 case 3: // Class
205 _tcsupr(pszCurrField);
206 iClass = *pszCurrField - _T('A');
207 if ((iClass < 0) || (iClass > 2))
208 iState = 255; // Error
209 break;
210 case 4: // Data type
211 iDataType = NxDCIDataTypeFromText(pszCurrField);
212 if (iDataType == -1) // Invalid data type specified
213 iState = 255;
214 break;
215 case 5: // Description
216 nx_strncpy(szDescription, pszCurrField, MAX_DB_STRING);
217 break;
218 default:
219 iState = 255; // Error
220 break;
221 }
222 iField++;
223 iPos = 0;
224 if ((iState != 255) && (*ptr == 0))
225 iState = -1; // Finish
226 break;
227 default:
228 pszCurrField[iPos++] = *ptr;
229 break;
230 }
231 break;
232 case 1: // Single quotes
233 switch(*ptr)
234 {
235 case '\'':
236 iState = 0;
237 break;
238 case 0: // Unexpected end of line
239 iState = 255;
240 break;
241 default:
242 pszCurrField[iPos++] = *ptr;
243 break;
244 }
245 break;
246 case 2: // Double quotes
247 switch(*ptr)
248 {
249 case '"':
250 iState = 0;
251 break;
252 case 0: // Unexpected end of line
253 iState = 255;
254 break;
255 default:
256 pszCurrField[iPos++] = *ptr;
257 break;
258 }
259 break;
260 default:
261 break;
262 }
263 }
264 free(pszCurrField);
265
266 // Add new counter if parsing was successful
267 if ((iState == -1) && (iField >= 4) && (iField <= 6))
268 {
269 pCnt = AddCounter(szCounterPath, iClass, iNumSamples, (iDataType == DCI_DT_FLOAT) ? COUNTER_TYPE_FLOAT : COUNTER_TYPE_AUTO);
270 if (pCnt != NULL)
271 AddParameter(szParamName, H_CollectedCounterData, (TCHAR *)pCnt,
272 iDataType, szDescription);
273 }
274
275 return ((iState == -1) && (iField >= 4) && (iField <= 6));
276 }
277
278
279 //
280 // Collector thread
281 //
282
283 static THREAD_RESULT THREAD_CALL CollectorThread(WINPERF_COUNTER_SET *pSet)
284 {
285 HQUERY hQuery;
286 PDH_STATUS rc;
287 PDH_STATISTICS statData;
288 DWORD i, dwOKCounters = 0;
289 TCHAR szFName[] = _T("CollectorThread_X");
290
291 szFName[16] = pSet->cClass;
292 if (pSet->dwNumCounters == 0)
293 {
294 AgentWriteLog(EVENTLOG_INFORMATION_TYPE, _T("Counter set %c is empty, collector thread for that set will not start"),
295 pSet->cClass);
296 return 0;
297 }
298
299 if ((rc = PdhOpenQuery(NULL, 0, &hQuery)) != ERROR_SUCCESS)
300 {
301 ReportPdhError(szFName, _T("PdhOpenQuery"), rc);
302 return -1;
303 }
304
305 // Add counters to query
306 for(i = 0; i < pSet->dwNumCounters; i++)
307 if ((rc = PdhAddCounter(hQuery, pSet->ppCounterList[i]->pszName,
308 0, &pSet->ppCounterList[i]->handle)) != ERROR_SUCCESS)
309 {
310 TCHAR szBuffer[1024];
311
312 AgentWriteLog(EVENTLOG_WARNING_TYPE, _T("%s: Unable to add counter \"%s\" to query (%s)"),
313 szFName, pSet->ppCounterList[i]->pszName, GetPdhErrorText(rc, szBuffer, 1024));
314 pSet->ppCounterList[i]->handle = NULL;
315 }
316 else
317 {
318 dwOKCounters++;
319 }
320
321 // Check if we was able to add at least one counter
322 if (dwOKCounters == 0)
323 {
324 PdhCloseQuery(hQuery);
325 AgentWriteLog(EVENTLOG_WARNING_TYPE, _T("Failed to add any counter to query for counter set %c, ")
326 _T("collector thread for that set will not start"),
327 pSet->cClass);
328 return 0;
329 }
330
331 // Main collection loop
332 while(1)
333 {
334 if (WaitForSingleObject(g_hCondShutdown, pSet->dwInterval) != WAIT_TIMEOUT)
335 break; // We got a signal
336
337 // Collect data
338 if ((rc = PdhCollectQueryData(hQuery)) != ERROR_SUCCESS)
339 ReportPdhError(szFName, _T("PdhCollectQueryData"), rc);
340
341 // Get raw values for each counter and compute average value
342 for(i = 0; i < pSet->dwNumCounters; i++)
343 if (pSet->ppCounterList[i]->handle != NULL)
344 {
345 // Get raw value into buffer
346 PdhGetRawCounterValue(pSet->ppCounterList[i]->handle, NULL,
347 &pSet->ppCounterList[i]->pRawValues[pSet->ppCounterList[i]->dwBufferPos]);
348 pSet->ppCounterList[i]->dwBufferPos++;
349 if (pSet->ppCounterList[i]->dwBufferPos == (DWORD)pSet->ppCounterList[i]->wNumSamples)
350 pSet->ppCounterList[i]->dwBufferPos = 0;
351
352 // Calculate mean value
353 if ((rc = PdhComputeCounterStatistics(pSet->ppCounterList[i]->handle,
354 pSet->ppCounterList[i]->dwFormat,
355 pSet->ppCounterList[i]->dwBufferPos,
356 pSet->ppCounterList[i]->wNumSamples,
357 pSet->ppCounterList[i]->pRawValues,
358 &statData)) != ERROR_SUCCESS)
359 {
360 ReportPdhError(szFName, _T("PdhComputeCounterStatistics"), rc);
361 }
362
363 // Update mean value in counter set
364 switch(pSet->ppCounterList[i]->wType)
365 {
366 case COUNTER_TYPE_INT32:
367 pSet->ppCounterList[i]->value.iLong = statData.mean.longValue;
368 break;
369 case COUNTER_TYPE_INT64:
370 pSet->ppCounterList[i]->value.iLarge = statData.mean.largeValue;
371 break;
372 case COUNTER_TYPE_FLOAT:
373 pSet->ppCounterList[i]->value.dFloat = statData.mean.doubleValue;
374 break;
375 default:
376 break;
377 }
378 }
379 }
380
381 // Cleanup
382 PdhCloseQuery(hQuery);
383 AgentWriteLog(EVENTLOG_INFORMATION_TYPE, _T("Collector thread for counter set %c terminated"), pSet->cClass);
384
385 return 0;
386 }
387
388
389 //
390 // Start collector threads
391 //
392
393 void StartCollectorThreads(void)
394 {
395 int i;
396
397 for(i = 0; i < 3; i++)
398 ThreadCreate((THREAD_RESULT (THREAD_CALL *)(void *))CollectorThread, 0, &m_cntSet[i]);
399 }