master
c 715 lines 26.8 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "perflib.h"
4
5 #if defined(OS_WINDOWS)
6 // --------------------------------------------------------------------------------
7
8 // Retrieve a buffer that contains the specified performance data.
9 // The pwszSource parameter determines the data that GetRegistryBuffer returns.
10 //
11 // Typically, when calling RegQueryValueEx, you can specify zero for the size of the buffer
12 // and the RegQueryValueEx will set your size variable to the required buffer size. However,
13 // if the source is "Global" or one or more object index values, you will need to increment
14 // the buffer size in a loop until RegQueryValueEx does not return ERROR_MORE_DATA.
15 static LPBYTE getPerformanceData(const char *pwszSource) {
16 static __thread DWORD size = 0;
17 static __thread LPBYTE buffer = NULL;
18
19 if(pwszSource == (const char *)0x01) {
20 freez(buffer);
21 buffer = NULL;
22 size = 0;
23 return NULL;
24 }
25
26 if(!size) {
27 size = 32 * 1024;
28 buffer = mallocz(size);
29 }
30
31 LONG status = ERROR_SUCCESS;
32 while ((status = RegQueryValueEx(HKEY_PERFORMANCE_DATA, pwszSource,
33 NULL, NULL, buffer, &size)) == ERROR_MORE_DATA) {
34 size *= 2;
35 buffer = reallocz(buffer, size);
36 }
37
38 if (status != ERROR_SUCCESS) {
39 nd_log(NDLS_COLLECTORS, NDLP_ERR, "RegQueryValueEx failed with 0x%x.\n", status);
40 return NULL;
41 }
42
43 return buffer;
44 }
45
46 void perflibFreePerformanceData(void) {
47 getPerformanceData((const char *)0x01);
48 }
49
50 // --------------------------------------------------------------------------------------------------------------------
51
52 // Retrieve the raw counter value and any supporting data needed to calculate
53 // a displayable counter value. Use the counter type to determine the information
54 // needed to calculate the value.
55
56 static BOOL getCounterData(
57 PERF_DATA_BLOCK *pDataBlock,
58 PERF_OBJECT_TYPE* pObject,
59 PERF_COUNTER_DEFINITION* pCounter,
60 PERF_COUNTER_BLOCK* pCounterDataBlock,
61 PRAW_DATA pRawData)
62 {
63 PVOID pData = NULL;
64 UNALIGNED ULONGLONG* pullData = NULL;
65 PERF_COUNTER_DEFINITION* pBaseCounter = NULL;
66 BOOL fSuccess = TRUE;
67
68 if(!pCounterDataBlock)
69 return FALSE;
70
71 //Point to the raw counter data.
72 pData = (PVOID)((LPBYTE)pCounterDataBlock + pCounter->CounterOffset);
73
74 //Now use the PERF_COUNTER_DEFINITION.CounterType value to figure out what
75 //other information you need to calculate a displayable value.
76 switch (pCounter->CounterType) {
77
78 case PERF_COUNTER_COUNTER:
79 case PERF_COUNTER_QUEUELEN_TYPE:
80 case PERF_SAMPLE_COUNTER:
81 pRawData->Data = (ULONGLONG)(*(DWORD*)pData);
82 pRawData->Time = pDataBlock->PerfTime.QuadPart;
83 if (PERF_COUNTER_COUNTER == pCounter->CounterType || PERF_SAMPLE_COUNTER == pCounter->CounterType)
84 pRawData->Frequency = pDataBlock->PerfFreq.QuadPart;
85 break;
86
87 case PERF_OBJ_TIME_TIMER:
88 pRawData->Data = (ULONGLONG)(*(DWORD*)pData);
89 pRawData->Time = pObject->PerfTime.QuadPart;
90 break;
91
92 case PERF_COUNTER_100NS_QUEUELEN_TYPE:
93 pRawData->Data = *(UNALIGNED ULONGLONG *)pData;
94 pRawData->Time = pDataBlock->PerfTime100nSec.QuadPart;
95 break;
96
97 case PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE:
98 pRawData->Data = *(UNALIGNED ULONGLONG *)pData;
99 pRawData->Time = pObject->PerfTime.QuadPart;
100 break;
101
102 case PERF_COUNTER_TIMER:
103 case PERF_COUNTER_TIMER_INV:
104 case PERF_COUNTER_BULK_COUNT:
105 case PERF_COUNTER_LARGE_QUEUELEN_TYPE:
106 pullData = (UNALIGNED ULONGLONG *)pData;
107 pRawData->Data = *pullData;
108 pRawData->Time = pDataBlock->PerfTime.QuadPart;
109 if (pCounter->CounterType == PERF_COUNTER_BULK_COUNT)
110 pRawData->Frequency = pDataBlock->PerfFreq.QuadPart;
111 break;
112
113 case PERF_COUNTER_MULTI_TIMER:
114 case PERF_COUNTER_MULTI_TIMER_INV:
115 pullData = (UNALIGNED ULONGLONG *)pData;
116 pRawData->Data = *pullData;
117 pRawData->Frequency = pDataBlock->PerfFreq.QuadPart;
118 pRawData->Time = pDataBlock->PerfTime.QuadPart;
119
120 //These counter types have a second counter value that is adjacent to
121 //this counter value in the counter data block. The value is needed for
122 //the calculation.
123 if ((pCounter->CounterType & PERF_MULTI_COUNTER) == PERF_MULTI_COUNTER) {
124 ++pullData;
125 pRawData->MultiCounterData = *(DWORD*)pullData;
126 }
127 break;
128
129 //These counters do not use any time reference.
130 case PERF_COUNTER_RAWCOUNT:
131 case PERF_COUNTER_RAWCOUNT_HEX:
132 case PERF_COUNTER_DELTA:
133 // some counters in these categories, have CounterSize = sizeof(ULONGLONG)
134 // but the official documentation always uses them as sizeof(DWORD)
135 pRawData->Data = (ULONGLONG)(*(DWORD*)pData);
136 pRawData->Time = 0;
137 break;
138
139 case PERF_COUNTER_LARGE_RAWCOUNT:
140 case PERF_COUNTER_LARGE_RAWCOUNT_HEX:
141 case PERF_COUNTER_LARGE_DELTA:
142 pRawData->Data = *(UNALIGNED ULONGLONG*)pData;
143 pRawData->Time = 0;
144 break;
145
146 //These counters use the 100ns time base in their calculation.
147 case PERF_100NSEC_TIMER:
148 case PERF_100NSEC_TIMER_INV:
149 case PERF_100NSEC_MULTI_TIMER:
150 case PERF_100NSEC_MULTI_TIMER_INV:
151 pullData = (UNALIGNED ULONGLONG*)pData;
152 pRawData->Data = *pullData;
153 pRawData->Time = pDataBlock->PerfTime100nSec.QuadPart;
154
155 //These counter types have a second counter value that is adjacent to
156 //this counter value in the counter data block. The value is needed for
157 //the calculation.
158 if ((pCounter->CounterType & PERF_MULTI_COUNTER) == PERF_MULTI_COUNTER) {
159 ++pullData;
160 pRawData->MultiCounterData = *(DWORD*)pullData;
161 }
162 break;
163
164 //These counters use two data points, this value and one from this counter's
165 //base counter. The base counter should be the next counter in the object's
166 //list of counters.
167 case PERF_SAMPLE_FRACTION:
168 case PERF_RAW_FRACTION:
169 pRawData->Data = (ULONGLONG)(*(DWORD*)pData);
170 pBaseCounter = pCounter + 1; //Get base counter
171 if ((pBaseCounter->CounterType & PERF_COUNTER_BASE) == PERF_COUNTER_BASE) {
172 pData = (PVOID)((LPBYTE)pCounterDataBlock + pBaseCounter->CounterOffset);
173 pRawData->Time = (LONGLONG)(*(DWORD*)pData);
174 }
175 else
176 fSuccess = FALSE;
177 break;
178
179 case PERF_LARGE_RAW_FRACTION:
180 case PERF_PRECISION_SYSTEM_TIMER:
181 case PERF_PRECISION_100NS_TIMER:
182 case PERF_PRECISION_OBJECT_TIMER:
183 pRawData->Data = *(UNALIGNED ULONGLONG*)pData;
184 pBaseCounter = pCounter + 1;
185 if ((pBaseCounter->CounterType & PERF_COUNTER_BASE) == PERF_COUNTER_BASE) {
186 pData = (PVOID)((LPBYTE)pCounterDataBlock + pBaseCounter->CounterOffset);
187 pRawData->Time = *(LONGLONG*)pData;
188 }
189 else
190 fSuccess = FALSE;
191 break;
192
193 case PERF_AVERAGE_TIMER:
194 case PERF_AVERAGE_BULK:
195 pRawData->Data = *(UNALIGNED ULONGLONG*)pData;
196 pBaseCounter = pCounter+1;
197 if ((pBaseCounter->CounterType & PERF_COUNTER_BASE) == PERF_COUNTER_BASE) {
198 pData = (PVOID)((LPBYTE)pCounterDataBlock + pBaseCounter->CounterOffset);
199 pRawData->Time = *(DWORD*)pData;
200 }
201 else
202 fSuccess = FALSE;
203
204 if (pCounter->CounterType == PERF_AVERAGE_TIMER)
205 pRawData->Frequency = pDataBlock->PerfFreq.QuadPart;
206 break;
207
208 //These are base counters and are used in calculations for other counters.
209 //This case should never be entered.
210 case PERF_SAMPLE_BASE:
211 case PERF_AVERAGE_BASE:
212 case PERF_COUNTER_MULTI_BASE:
213 case PERF_RAW_BASE:
214 case PERF_LARGE_RAW_BASE:
215 pRawData->Data = 0;
216 pRawData->Time = 0;
217 fSuccess = FALSE;
218 break;
219
220 case PERF_ELAPSED_TIME:
221 pRawData->Data = *(UNALIGNED ULONGLONG*)pData;
222 pRawData->Time = pObject->PerfTime.QuadPart;
223 pRawData->Frequency = pObject->PerfFreq.QuadPart;
224 break;
225
226 //These counters are currently not supported.
227 case PERF_COUNTER_TEXT:
228 case PERF_COUNTER_NODATA:
229 case PERF_COUNTER_HISTOGRAM_TYPE:
230 default: // unknown counter types
231 pRawData->Data = 0;
232 pRawData->Time = 0;
233 fSuccess = FALSE;
234 break;
235 }
236
237 return fSuccess;
238 }
239
240 // --------------------------------------------------------------------------------------------------------------------
241
242 ALWAYS_INLINE
243 static BOOL isValidPointer(PERF_DATA_BLOCK *pDataBlock, void *ptr) {
244 return
245 !pDataBlock ||
246 (PBYTE)ptr < (PBYTE)pDataBlock ||
247 (PBYTE)ptr >= (PBYTE)pDataBlock + pDataBlock->TotalByteLength ? FALSE : TRUE;
248 }
249
250 ALWAYS_INLINE
251 static BOOL isValidStructure(PERF_DATA_BLOCK *pDataBlock, void *ptr, size_t length) {
252 return
253 !pDataBlock ||
254 !length || length > pDataBlock->TotalByteLength ||
255 (PBYTE)ptr < (PBYTE)pDataBlock ||
256 (PBYTE)ptr > (PBYTE)ptr + length || // Check for pointer arithmetic overflow
257 (PBYTE)ptr + length > (PBYTE)pDataBlock + pDataBlock->TotalByteLength ? FALSE : TRUE;
258 }
259
260 static inline PERF_DATA_BLOCK *getDataBlock(BYTE *pBuffer) {
261 PERF_DATA_BLOCK *pDataBlock = (PERF_DATA_BLOCK *)pBuffer;
262
263 static WCHAR signature[] = { 'P', 'E', 'R', 'F' };
264
265 if(memcmp(pDataBlock->Signature, signature, sizeof(signature)) != 0) {
266 nd_log(NDLS_COLLECTORS, NDLP_ERR,
267 "WINDOWS: PERFLIB: Invalid data block signature.");
268 return NULL;
269 }
270
271 if(!isValidPointer(pDataBlock, (PBYTE)pDataBlock + pDataBlock->SystemNameOffset) ||
272 !isValidStructure(pDataBlock, (PBYTE)pDataBlock + pDataBlock->SystemNameOffset, pDataBlock->SystemNameLength)) {
273 nd_log(NDLS_COLLECTORS, NDLP_ERR,
274 "WINDOWS: PERFLIB: Invalid system name array.");
275 return NULL;
276 }
277
278 return pDataBlock;
279 }
280
281 ALWAYS_INLINE
282 static PERF_OBJECT_TYPE *getObjectType(PERF_DATA_BLOCK* pDataBlock, PERF_OBJECT_TYPE *lastObjectType) {
283 PERF_OBJECT_TYPE* pObjectType = NULL;
284
285 if(!lastObjectType)
286 pObjectType = (PERF_OBJECT_TYPE *)((PBYTE)pDataBlock + pDataBlock->HeaderLength);
287 else if (lastObjectType->TotalByteLength != 0)
288 pObjectType = (PERF_OBJECT_TYPE *)((PBYTE)lastObjectType + lastObjectType->TotalByteLength);
289
290 if(pObjectType && (!isValidPointer(pDataBlock, pObjectType) || !isValidStructure(pDataBlock, pObjectType, pObjectType->TotalByteLength))) {
291 nd_log(NDLS_COLLECTORS, NDLP_ERR, "WINDOWS: PERFLIB: %s(): Invalid ObjectType!", __FUNCTION__);
292 pObjectType = NULL;
293 }
294
295 return pObjectType;
296 }
297
298 ALWAYS_INLINE
299 PERF_OBJECT_TYPE *getObjectTypeByIndex(PERF_DATA_BLOCK *pDataBlock, DWORD ObjectNameTitleIndex) {
300 PERF_OBJECT_TYPE *po = NULL;
301 for(DWORD o = 0; o < pDataBlock->NumObjectTypes ; o++) {
302 po = getObjectType(pDataBlock, po);
303 if(!po) break;
304
305 if(po->ObjectNameTitleIndex == ObjectNameTitleIndex)
306 return po;
307 }
308
309 return NULL;
310 }
311
312 ALWAYS_INLINE
313 static PERF_INSTANCE_DEFINITION *getInstance(
314 PERF_DATA_BLOCK *pDataBlock,
315 PERF_OBJECT_TYPE *pObjectType,
316 PERF_COUNTER_BLOCK *lastCounterBlock
317 ) {
318 if(unlikely(!pObjectType))
319 return NULL;
320
321 PERF_INSTANCE_DEFINITION *pInstance;
322 if(!lastCounterBlock)
323 pInstance = (PERF_INSTANCE_DEFINITION *)((PBYTE)pObjectType + pObjectType->DefinitionLength);
324 else
325 pInstance = (PERF_INSTANCE_DEFINITION *)((PBYTE)lastCounterBlock + lastCounterBlock->ByteLength);
326
327 if(pInstance && (!isValidPointer(pDataBlock, pInstance) || !isValidStructure(pDataBlock, pInstance, pInstance->ByteLength))) {
328 nd_log(NDLS_COLLECTORS, NDLP_ERR, "WINDOWS: PERFLIB: %s(): Invalid Instance Definition!", __FUNCTION__);
329 pInstance = NULL;
330 }
331
332 return pInstance;
333 }
334
335 ALWAYS_INLINE
336 static PERF_COUNTER_BLOCK *getObjectTypeCounterBlock(
337 PERF_DATA_BLOCK *pDataBlock,
338 PERF_OBJECT_TYPE *pObjectType
339 ) {
340 if(unlikely(!pObjectType))
341 return NULL;
342
343 PERF_COUNTER_BLOCK *pCounterBlock = (PERF_COUNTER_BLOCK *)((PBYTE)pObjectType + pObjectType->DefinitionLength);
344
345 if(pCounterBlock && (!isValidPointer(pDataBlock, pCounterBlock) || !isValidStructure(pDataBlock, pCounterBlock, pCounterBlock->ByteLength))) {
346 nd_log(NDLS_COLLECTORS, NDLP_ERR, "WINDOWS: PERFLIB: %s(): Invalid ObjectType CounterBlock!", __FUNCTION__);
347 pCounterBlock = NULL;
348 }
349
350 return pCounterBlock;
351 }
352
353 ALWAYS_INLINE
354 static PERF_COUNTER_BLOCK *getInstanceCounterBlock(
355 PERF_DATA_BLOCK *pDataBlock,
356 PERF_OBJECT_TYPE *pObjectType __maybe_unused,
357 PERF_INSTANCE_DEFINITION *pInstance
358 ) {
359 if(unlikely(!pInstance))
360 return NULL;
361
362 PERF_COUNTER_BLOCK *pCounterBlock = (PERF_COUNTER_BLOCK *)((PBYTE)pInstance + pInstance->ByteLength);
363
364 if(pCounterBlock && (!isValidPointer(pDataBlock, pCounterBlock) || !isValidStructure(pDataBlock, pCounterBlock, pCounterBlock->ByteLength))) {
365 nd_log(NDLS_COLLECTORS, NDLP_ERR, "WINDOWS: PERFLIB: %s(): Invalid Instance CounterBlock!", __FUNCTION__);
366 pCounterBlock = NULL;
367 }
368
369 return pCounterBlock;
370 }
371
372 ALWAYS_INLINE
373 PERF_INSTANCE_DEFINITION *getInstanceByPosition(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, DWORD instancePosition) {
374 PERF_INSTANCE_DEFINITION *pi = NULL;
375 PERF_COUNTER_BLOCK *pc = NULL;
376 for(DWORD i = 0; i <= instancePosition ;i++) {
377 pi = getInstance(pDataBlock, pObjectType, pc);
378 pc = getInstanceCounterBlock(pDataBlock, pObjectType, pi);
379 }
380 return pi;
381 }
382
383 ALWAYS_INLINE
384 static PERF_COUNTER_DEFINITION *getCounterDefinition(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_COUNTER_DEFINITION *lastCounterDefinition) {
385 if(unlikely(!pObjectType))
386 return NULL;
387
388 PERF_COUNTER_DEFINITION *pCounterDefinition = NULL;
389 if(!lastCounterDefinition)
390 pCounterDefinition = (PERF_COUNTER_DEFINITION *)((PBYTE)pObjectType + pObjectType->HeaderLength);
391 else
392 pCounterDefinition = (PERF_COUNTER_DEFINITION *)((PBYTE)lastCounterDefinition + lastCounterDefinition->ByteLength);
393
394 if(pCounterDefinition && (!isValidPointer(pDataBlock, pCounterDefinition) || !isValidStructure(pDataBlock, pCounterDefinition, pCounterDefinition->ByteLength))) {
395 nd_log(NDLS_COLLECTORS, NDLP_ERR, "WINDOWS: PERFLIB: %s(): Invalid Counter Definition!", __FUNCTION__);
396 pCounterDefinition = NULL;
397 }
398
399 return pCounterDefinition;
400 }
401
402 // --------------------------------------------------------------------------------------------------------------------
403
404 ALWAYS_INLINE
405 static BOOL getEncodedStringToUTF8(char *dst, size_t dst_len, DWORD CodePage, char *start, DWORD length) {
406 static __thread wchar_t unicode[PERFLIB_MAX_NAME_LENGTH];
407
408 WCHAR *tempBuffer; // Temporary buffer for Unicode data
409 DWORD charsCopied = 0;
410
411 if (CodePage == 0) {
412 // Input is already Unicode (UTF-16)
413 tempBuffer = (WCHAR *)start;
414 charsCopied = length / sizeof(WCHAR); // Convert byte length to number of WCHARs
415 }
416 else {
417 tempBuffer = unicode;
418 charsCopied = any_to_utf16(CodePage, unicode, _countof(unicode), start, (int)length, NULL);
419 if(!charsCopied) return FALSE;
420 }
421
422 // Now convert from Unicode (UTF-16) to UTF-8
423 int bytesCopied = WideCharToMultiByte(CP_UTF8, 0, tempBuffer, (int)charsCopied, dst, (int)dst_len, NULL, NULL);
424 if (bytesCopied == 0) {
425 dst[0] = '\0'; // Ensure the buffer is null-terminated even on failure
426 return FALSE;
427 }
428
429 dst[bytesCopied - 1] = '\0'; // Ensure buffer is null-terminated
430 return TRUE;
431 }
432
433 ALWAYS_INLINE
434 BOOL getInstanceName(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *pInstance,
435 char *buffer, size_t bufferLen) {
436 (void)pDataBlock;
437 if (!pObjectType || !pInstance || !buffer || !bufferLen)
438 return FALSE;
439
440 return getEncodedStringToUTF8(buffer, bufferLen, pObjectType->CodePage,
441 ((char *)pInstance + pInstance->NameOffset), pInstance->NameLength);
442 }
443
444 ALWAYS_INLINE
445 BOOL getSystemName(PERF_DATA_BLOCK *pDataBlock, char *buffer, size_t bufferLen) {
446 return getEncodedStringToUTF8(buffer, bufferLen, 0,
447 ((char *)pDataBlock + pDataBlock->SystemNameOffset), pDataBlock->SystemNameLength);
448 }
449
450 ALWAYS_INLINE
451 BOOL ObjectTypeHasInstances(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType) {
452 (void)pDataBlock;
453 return pObjectType && pObjectType->NumInstances != PERF_NO_INSTANCES && pObjectType->NumInstances > 0 ? TRUE : FALSE;
454 }
455
456 PERF_OBJECT_TYPE *perflibFindObjectTypeByName(PERF_DATA_BLOCK *pDataBlock, const char *name) {
457 PERF_OBJECT_TYPE* pObjectType = NULL;
458 for(DWORD o = 0; o < pDataBlock->NumObjectTypes; o++) {
459 pObjectType = getObjectType(pDataBlock, pObjectType);
460 if(!pObjectType) break;
461 if(strcmp(name, RegistryFindNameByID(pObjectType->ObjectNameTitleIndex)) == 0)
462 return pObjectType;
463 }
464
465 return NULL;
466 }
467
468 PERF_INSTANCE_DEFINITION *perflibForEachInstance(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *lastInstance) {
469 if(!ObjectTypeHasInstances(pDataBlock, pObjectType))
470 return NULL;
471
472 return getInstance(pDataBlock, pObjectType,
473 lastInstance ?
474 getInstanceCounterBlock(pDataBlock, pObjectType, lastInstance) :
475 NULL );
476 }
477
478 bool perflibGetInstanceCounter(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *pInstance, COUNTER_DATA *cd) {
479 DWORD id = cd->id;
480 const char *key = cd->key;
481 internal_fatal(key == NULL, "You have to set a key for this call.");
482
483 if(unlikely(cd->failures >= PERFLIB_MAX_FAILURES_TO_FIND_METRIC)) {
484 // we don't want to lookup and compare strings all the time
485 // when a metric is not there, so we try to find it for
486 // XX times, and then we give up.
487
488 if(cd->failures == PERFLIB_MAX_FAILURES_TO_FIND_METRIC) {
489 nd_log(NDLS_COLLECTORS, NDLP_ERR,
490 "WINDOWS: PERFLIB: Giving up on metric '%s' (tried to find it %u times).",
491 cd->key, cd->failures);
492
493 cd->failures++; // increment it once, so that we will not log this again
494 }
495
496 goto failed;
497 }
498
499 PERF_COUNTER_DEFINITION *pCounterDefinition = NULL;
500 for(DWORD c = 0; c < pObjectType->NumCounters ;c++) {
501 pCounterDefinition = getCounterDefinition(pDataBlock, pObjectType, pCounterDefinition);
502 if(!pCounterDefinition) {
503 nd_log(NDLS_COLLECTORS, NDLP_ERR,
504 "WINDOWS: PERFLIB: Cannot read counter definition No %u (out of %u)",
505 c, pObjectType->NumCounters);
506 break;
507 }
508
509 if(id) {
510 if(id != pCounterDefinition->CounterNameTitleIndex)
511 continue;
512 }
513 else {
514 const char *name = RegistryFindNameByID(pCounterDefinition->CounterNameTitleIndex);
515 if(strcmp(name, key) != 0)
516 continue;
517
518 cd->id = pCounterDefinition->CounterNameTitleIndex;
519 }
520
521 cd->current.CounterType = cd->OverwriteCounterType ? cd->OverwriteCounterType : pCounterDefinition->CounterType;
522 PERF_COUNTER_BLOCK *pCounterBlock = getInstanceCounterBlock(pDataBlock, pObjectType, pInstance);
523
524 cd->previous = cd->current;
525 if(likely(getCounterData(pDataBlock, pObjectType, pCounterDefinition, pCounterBlock, &cd->current))) {
526 cd->updated = true;
527 cd->failures = 0;
528 return true;
529 }
530 }
531
532 cd->failures++;
533
534 failed:
535 cd->previous = cd->current;
536 cd->current = RAW_DATA_EMPTY;
537 cd->updated = false;
538 return false;
539 }
540
541 bool perflibGetObjectCounter(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, COUNTER_DATA *cd) {
542 if (unlikely(!pObjectType))
543 goto cleanup;
544
545 PERF_COUNTER_DEFINITION *pCounterDefinition = NULL;
546 for(DWORD c = 0; c < pObjectType->NumCounters ;c++) {
547 pCounterDefinition = getCounterDefinition(pDataBlock, pObjectType, pCounterDefinition);
548 if(!pCounterDefinition) {
549 nd_log(NDLS_COLLECTORS, NDLP_ERR,
550 "WINDOWS: PERFLIB: Cannot read counter definition No %u (out of %u)",
551 c, pObjectType->NumCounters);
552 break;
553 }
554
555 if(cd->id) {
556 if(cd->id != pCounterDefinition->CounterNameTitleIndex)
557 continue;
558 }
559 else {
560 if(strcmp(RegistryFindNameByID(pCounterDefinition->CounterNameTitleIndex), cd->key) != 0)
561 continue;
562
563 cd->id = pCounterDefinition->CounterNameTitleIndex;
564 }
565
566 cd->current.CounterType = cd->OverwriteCounterType ? cd->OverwriteCounterType : pCounterDefinition->CounterType;
567 PERF_COUNTER_BLOCK *pCounterBlock = getObjectTypeCounterBlock(pDataBlock, pObjectType);
568
569 cd->previous = cd->current;
570 cd->updated = getCounterData(pDataBlock, pObjectType, pCounterDefinition, pCounterBlock, &cd->current);
571 return cd->updated;
572 }
573
574 cleanup:
575 cd->previous = cd->current;
576 cd->current = RAW_DATA_EMPTY;
577 cd->updated = false;
578 return false;
579 }
580
581 PERF_DATA_BLOCK *perflibGetPerformanceData(DWORD id) {
582 char source[24];
583 snprintfz(source, sizeof(source), "%u", id);
584
585 LPBYTE pData = (LPBYTE)getPerformanceData((id > 0) ? source : NULL);
586 if (!pData) return NULL;
587
588 PERF_DATA_BLOCK *pDataBlock = getDataBlock(pData);
589 if(!pDataBlock) return NULL;
590
591 return pDataBlock;
592 }
593
594 int perflibQueryAndTraverse(DWORD id,
595 perflib_data_cb dataCb,
596 perflib_object_cb objectCb,
597 perflib_instance_cb instanceCb,
598 perflib_instance_counter_cb instanceCounterCb,
599 perflib_counter_cb counterCb,
600 void *data) {
601 int counters = -1;
602
603 PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id);
604 if(!pDataBlock) goto cleanup;
605
606 bool do_data = true;
607 if(dataCb)
608 do_data = dataCb(pDataBlock, data);
609
610 PERF_OBJECT_TYPE* pObjectType = NULL;
611 for(DWORD o = 0; do_data && o < pDataBlock->NumObjectTypes; o++) {
612 pObjectType = getObjectType(pDataBlock, pObjectType);
613 if(!pObjectType) {
614 nd_log(NDLS_COLLECTORS, NDLP_ERR,
615 "WINDOWS: PERFLIB: Cannot read object type No %d (out of %d)",
616 o, pDataBlock->NumObjectTypes);
617 break;
618 }
619
620 bool do_object = true;
621 if(objectCb)
622 do_object = objectCb(pDataBlock, pObjectType, data);
623
624 if(!do_object)
625 continue;
626
627 if(ObjectTypeHasInstances(pDataBlock, pObjectType)) {
628 PERF_INSTANCE_DEFINITION *pInstance = NULL;
629 PERF_COUNTER_BLOCK *pCounterBlock = NULL;
630 for(LONG i = 0; i < pObjectType->NumInstances ;i++) {
631 pInstance = getInstance(pDataBlock, pObjectType, pCounterBlock);
632 if(!pInstance) {
633 nd_log(NDLS_COLLECTORS, NDLP_ERR,
634 "WINDOWS: PERFLIB: Cannot read Instance No %d (out of %d)",
635 i, pObjectType->NumInstances);
636 break;
637 }
638
639 pCounterBlock = getInstanceCounterBlock(pDataBlock, pObjectType, pInstance);
640 if(!pCounterBlock) {
641 nd_log(NDLS_COLLECTORS, NDLP_ERR,
642 "WINDOWS: PERFLIB: Cannot read CounterBlock of instance No %d (out of %d)",
643 i, pObjectType->NumInstances);
644 break;
645 }
646
647 bool do_instance = true;
648 if(instanceCb)
649 do_instance = instanceCb(pDataBlock, pObjectType, pInstance, data);
650
651 if(!do_instance)
652 continue;
653
654 PERF_COUNTER_DEFINITION *pCounterDefinition = NULL;
655 for(DWORD c = 0; c < pObjectType->NumCounters ;c++) {
656 pCounterDefinition = getCounterDefinition(pDataBlock, pObjectType, pCounterDefinition);
657 if(!pCounterDefinition) {
658 nd_log(NDLS_COLLECTORS, NDLP_ERR,
659 "WINDOWS: PERFLIB: Cannot read counter definition No %u (out of %u)",
660 c, pObjectType->NumCounters);
661 break;
662 }
663
664 RAW_DATA sample = {
665 .CounterType = pCounterDefinition->CounterType,
666 };
667 if(getCounterData(pDataBlock, pObjectType, pCounterDefinition, pCounterBlock, &sample)) {
668 // DisplayCalculatedValue(&sample, &sample);
669
670 if(instanceCounterCb) {
671 instanceCounterCb(pDataBlock, pObjectType, pInstance, pCounterDefinition, &sample, data);
672 counters++;
673 }
674 }
675 }
676
677 if(instanceCb)
678 instanceCb(pDataBlock, pObjectType, NULL, data);
679 }
680 }
681 else {
682 PERF_COUNTER_BLOCK *pCounterBlock = getObjectTypeCounterBlock(pDataBlock, pObjectType);
683 PERF_COUNTER_DEFINITION *pCounterDefinition = NULL;
684 for(DWORD c = 0; c < pObjectType->NumCounters ;c++) {
685 pCounterDefinition = getCounterDefinition(pDataBlock, pObjectType, pCounterDefinition);
686 if(!pCounterDefinition) {
687 nd_log(NDLS_COLLECTORS, NDLP_ERR,
688 "WINDOWS: PERFLIB: Cannot read counter definition No %u (out of %u)",
689 c, pObjectType->NumCounters);
690 break;
691 }
692
693 RAW_DATA sample = {
694 .CounterType = pCounterDefinition->CounterType,
695 };
696 if(getCounterData(pDataBlock, pObjectType, pCounterDefinition, pCounterBlock, &sample)) {
697 // DisplayCalculatedValue(&sample, &sample);
698
699 if(counterCb) {
700 counterCb(pDataBlock, pObjectType, pCounterDefinition, &sample, data);
701 counters++;
702 }
703 }
704 }
705 }
706
707 if(objectCb)
708 objectCb(pDataBlock, NULL, data);
709 }
710
711 cleanup:
712 return counters;
713 }
714
715 #endif // OS_WINDOWS