summaryrefslogtreecommitdiffstats
path: root/src/libnetdata/os/windows-perflib
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-11-25 17:33:56 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-11-25 17:34:10 +0000
commit83ba6762cc43d9db581b979bb5e3445669e46cc2 (patch)
tree2e69833b43f791ed253a7a20318b767ebe56cdb8 /src/libnetdata/os/windows-perflib
parentReleasing debian version 1.47.5-1. (diff)
downloadnetdata-83ba6762cc43d9db581b979bb5e3445669e46cc2.tar.xz
netdata-83ba6762cc43d9db581b979bb5e3445669e46cc2.zip
Merging upstream version 2.0.3+dfsg (Closes: #923993, #1042533, #1045145).
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/libnetdata/os/windows-perflib')
-rw-r--r--src/libnetdata/os/windows-perflib/perflib-dump.c531
-rw-r--r--src/libnetdata/os/windows-perflib/perflib-names.c245
-rw-r--r--src/libnetdata/os/windows-perflib/perflib.c687
-rw-r--r--src/libnetdata/os/windows-perflib/perflib.h92
4 files changed, 1555 insertions, 0 deletions
diff --git a/src/libnetdata/os/windows-perflib/perflib-dump.c b/src/libnetdata/os/windows-perflib/perflib-dump.c
new file mode 100644
index 000000000..eaccb7827
--- /dev/null
+++ b/src/libnetdata/os/windows-perflib/perflib-dump.c
@@ -0,0 +1,531 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "perflib.h"
+
+#if defined(OS_WINDOWS)
+static const char *getCounterType(DWORD CounterType) {
+ switch (CounterType) {
+ case PERF_COUNTER_COUNTER:
+ return "PERF_COUNTER_COUNTER";
+
+ case PERF_COUNTER_TIMER:
+ return "PERF_COUNTER_TIMER";
+
+ case PERF_COUNTER_QUEUELEN_TYPE:
+ return "PERF_COUNTER_QUEUELEN_TYPE";
+
+ case PERF_COUNTER_LARGE_QUEUELEN_TYPE:
+ return "PERF_COUNTER_LARGE_QUEUELEN_TYPE";
+
+ case PERF_COUNTER_100NS_QUEUELEN_TYPE:
+ return "PERF_COUNTER_100NS_QUEUELEN_TYPE";
+
+ case PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE:
+ return "PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE";
+
+ case PERF_COUNTER_BULK_COUNT:
+ return "PERF_COUNTER_BULK_COUNT";
+
+ case PERF_COUNTER_TEXT:
+ return "PERF_COUNTER_TEXT";
+
+ case PERF_COUNTER_RAWCOUNT:
+ return "PERF_COUNTER_RAWCOUNT";
+
+ case PERF_COUNTER_LARGE_RAWCOUNT:
+ return "PERF_COUNTER_LARGE_RAWCOUNT";
+
+ case PERF_COUNTER_RAWCOUNT_HEX:
+ return "PERF_COUNTER_RAWCOUNT_HEX";
+
+ case PERF_COUNTER_LARGE_RAWCOUNT_HEX:
+ return "PERF_COUNTER_LARGE_RAWCOUNT_HEX";
+
+ case PERF_SAMPLE_FRACTION:
+ return "PERF_SAMPLE_FRACTION";
+
+ case PERF_SAMPLE_COUNTER:
+ return "PERF_SAMPLE_COUNTER";
+
+ case PERF_COUNTER_NODATA:
+ return "PERF_COUNTER_NODATA";
+
+ case PERF_COUNTER_TIMER_INV:
+ return "PERF_COUNTER_TIMER_INV";
+
+ case PERF_SAMPLE_BASE:
+ return "PERF_SAMPLE_BASE";
+
+ case PERF_AVERAGE_TIMER:
+ return "PERF_AVERAGE_TIMER";
+
+ case PERF_AVERAGE_BASE:
+ return "PERF_AVERAGE_BASE";
+
+ case PERF_AVERAGE_BULK:
+ return "PERF_AVERAGE_BULK";
+
+ case PERF_OBJ_TIME_TIMER:
+ return "PERF_OBJ_TIME_TIMER";
+
+ case PERF_100NSEC_TIMER:
+ return "PERF_100NSEC_TIMER";
+
+ case PERF_100NSEC_TIMER_INV:
+ return "PERF_100NSEC_TIMER_INV";
+
+ case PERF_COUNTER_MULTI_TIMER:
+ return "PERF_COUNTER_MULTI_TIMER";
+
+ case PERF_COUNTER_MULTI_TIMER_INV:
+ return "PERF_COUNTER_MULTI_TIMER_INV";
+
+ case PERF_COUNTER_MULTI_BASE:
+ return "PERF_COUNTER_MULTI_BASE";
+
+ case PERF_100NSEC_MULTI_TIMER:
+ return "PERF_100NSEC_MULTI_TIMER";
+
+ case PERF_100NSEC_MULTI_TIMER_INV:
+ return "PERF_100NSEC_MULTI_TIMER_INV";
+
+ case PERF_RAW_FRACTION:
+ return "PERF_RAW_FRACTION";
+
+ case PERF_LARGE_RAW_FRACTION:
+ return "PERF_LARGE_RAW_FRACTION";
+
+ case PERF_RAW_BASE:
+ return "PERF_RAW_BASE";
+
+ case PERF_LARGE_RAW_BASE:
+ return "PERF_LARGE_RAW_BASE";
+
+ case PERF_ELAPSED_TIME:
+ return "PERF_ELAPSED_TIME";
+
+ case PERF_COUNTER_HISTOGRAM_TYPE:
+ return "PERF_COUNTER_HISTOGRAM_TYPE";
+
+ case PERF_COUNTER_DELTA:
+ return "PERF_COUNTER_DELTA";
+
+ case PERF_COUNTER_LARGE_DELTA:
+ return "PERF_COUNTER_LARGE_DELTA";
+
+ case PERF_PRECISION_SYSTEM_TIMER:
+ return "PERF_PRECISION_SYSTEM_TIMER";
+
+ case PERF_PRECISION_100NS_TIMER:
+ return "PERF_PRECISION_100NS_TIMER";
+
+ case PERF_PRECISION_OBJECT_TIMER:
+ return "PERF_PRECISION_OBJECT_TIMER";
+
+ default:
+ return "UNKNOWN_COUNTER_TYPE";
+ }
+}
+
+static const char *getCounterDescription(DWORD CounterType) {
+ switch (CounterType) {
+ case PERF_COUNTER_COUNTER:
+ return "32-bit Counter. Divide delta by delta time. Display suffix: \"/sec\"";
+
+ case PERF_COUNTER_TIMER:
+ return "64-bit Timer. Divide delta by delta time. Display suffix: \"%\"";
+
+ case PERF_COUNTER_QUEUELEN_TYPE:
+ case PERF_COUNTER_LARGE_QUEUELEN_TYPE:
+ return "Queue Length Space-Time Product. Divide delta by delta time. No Display Suffix";
+
+ case PERF_COUNTER_100NS_QUEUELEN_TYPE:
+ return "Queue Length Space-Time Product using 100 Ns timebase. Divide delta by delta time. No Display Suffix";
+
+ case PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE:
+ return "Queue Length Space-Time Product using Object specific timebase. Divide delta by delta time. No Display Suffix.";
+
+ case PERF_COUNTER_BULK_COUNT:
+ return "64-bit Counter. Divide delta by delta time. Display Suffix: \"/sec\"";
+
+ case PERF_COUNTER_TEXT:
+ return "Unicode text Display as text.";
+
+ case PERF_COUNTER_RAWCOUNT:
+ case PERF_COUNTER_LARGE_RAWCOUNT:
+ return "A counter which should not be time averaged on display (such as an error counter on a serial line). Display as is. No Display Suffix.";
+
+ case PERF_COUNTER_RAWCOUNT_HEX:
+ case PERF_COUNTER_LARGE_RAWCOUNT_HEX:
+ return "Special case for RAWCOUNT which should be displayed in hex. A counter which should not be time averaged on display (such as an error counter on a serial line). Display as is. No Display Suffix.";
+
+ case PERF_SAMPLE_FRACTION:
+ return "A count which is either 1 or 0 on each sampling interrupt (% busy). Divide delta by delta base. Display Suffix: \"%\"";
+
+ case PERF_SAMPLE_COUNTER:
+ return "A count which is sampled on each sampling interrupt (queue length). Divide delta by delta time. No Display Suffix.";
+
+ case PERF_COUNTER_NODATA:
+ return "A label: no data is associated with this counter (it has 0 length). Do not display.";
+
+ case PERF_COUNTER_TIMER_INV:
+ return "64-bit Timer inverse (e.g., idle is measured, but display busy %). Display 100 - delta divided by delta time. Display suffix: \"%\"";
+
+ case PERF_SAMPLE_BASE:
+ return "The divisor for a sample, used with the previous counter to form a sampled %. You must check for >0 before dividing by this! This counter will directly follow the numerator counter. It should not be displayed to the user.";
+
+ case PERF_AVERAGE_TIMER:
+ return "A timer which, when divided by an average base, produces a time in seconds which is the average time of some operation. This timer times total operations, and the base is the number of operations. Display Suffix: \"sec\"";
+
+ case PERF_AVERAGE_BASE:
+ return "Used as the denominator in the computation of time or count averages. Must directly follow the numerator counter. Not displayed to the user.";
+
+ case PERF_AVERAGE_BULK:
+ return "A bulk count which, when divided (typically) by the number of operations, gives (typically) the number of bytes per operation. No Display Suffix.";
+
+ case PERF_OBJ_TIME_TIMER:
+ return "64-bit Timer in object specific units. Display delta divided by delta time as returned in the object type header structure. Display suffix: \"%\"";
+
+ case PERF_100NSEC_TIMER:
+ return "64-bit Timer in 100 nsec units. Display delta divided by delta time. Display suffix: \"%\"";
+
+ case PERF_100NSEC_TIMER_INV:
+ return "64-bit Timer inverse (e.g., idle is measured, but display busy %). Display 100 - delta divided by delta time. Display suffix: \"%\"";
+
+ case PERF_COUNTER_MULTI_TIMER:
+ return "64-bit Timer. Divide delta by delta time. Display suffix: \"%\". Timer for multiple instances, so result can exceed 100%.";
+
+ case PERF_COUNTER_MULTI_TIMER_INV:
+ return "64-bit Timer inverse (e.g., idle is measured, but display busy %). Display 100 * _MULTI_BASE - delta divided by delta time. Display suffix: \"%\" Timer for multiple instances, so result can exceed 100%. Followed by a counter of type _MULTI_BASE.";
+
+ case PERF_COUNTER_MULTI_BASE:
+ return "Number of instances to which the preceding _MULTI_..._INV counter applies. Used as a factor to get the percentage.";
+
+ case PERF_100NSEC_MULTI_TIMER:
+ return "64-bit Timer in 100 nsec units. Display delta divided by delta time. Display suffix: \"%\" Timer for multiple instances, so result can exceed 100%.";
+
+ case PERF_100NSEC_MULTI_TIMER_INV:
+ return "64-bit Timer inverse (e.g., idle is measured, but display busy %). Display 100 * _MULTI_BASE - delta divided by delta time. Display suffix: \"%\" Timer for multiple instances, so result can exceed 100%. Followed by a counter of type _MULTI_BASE.";
+
+ case PERF_LARGE_RAW_FRACTION:
+ case PERF_RAW_FRACTION:
+ return "Indicates the data is a fraction of the following counter which should not be time averaged on display (such as free space over total space.) Display as is. Display the quotient as \"%\"";
+
+ case PERF_RAW_BASE:
+ case PERF_LARGE_RAW_BASE:
+ return "Indicates the data is a base for the preceding counter which should not be time averaged on display (such as free space over total space.)";
+
+ case PERF_ELAPSED_TIME:
+ return "The data collected in this counter is actually the start time of the item being measured. For display, this data is subtracted from the sample time to yield the elapsed time as the difference between the two. In the definition below, the PerfTime field of the Object contains the sample time as indicated by the PERF_OBJECT_TIMER bit and the difference is scaled by the PerfFreq of the Object to convert the time units into seconds.";
+
+ case PERF_COUNTER_HISTOGRAM_TYPE:
+ return "Counter type can be used with the preceding types to define a range of values to be displayed in a histogram.";
+
+ case PERF_COUNTER_DELTA:
+ case PERF_COUNTER_LARGE_DELTA:
+ return "This counter is used to display the difference from one sample to the next. The counter value is a constantly increasing number and the value displayed is the difference between the current value and the previous value. Negative numbers are not allowed which shouldn't be a problem as long as the counter value is increasing or unchanged.";
+
+ case PERF_PRECISION_SYSTEM_TIMER:
+ return "The precision counters are timers that consist of two counter values:\r\n\t1) the count of elapsed time of the event being monitored\r\n\t2) the \"clock\" time in the same units\r\nthe precision timers are used where the standard system timers are not precise enough for accurate readings. It's assumed that the service providing the data is also providing a timestamp at the same time which will eliminate any error that may occur since some small and variable time elapses between the time the system timestamp is captured and when the data is collected from the performance DLL. Only in extreme cases has this been observed to be problematic.\r\nwhen using this type of timer, the definition of the PERF_PRECISION_TIMESTAMP counter must immediately follow the definition of the PERF_PRECISION_*_TIMER in the Object header\r\nThe timer used has the same frequency as the System Performance Timer";
+
+ case PERF_PRECISION_100NS_TIMER:
+ return "The precision counters are timers that consist of two counter values:\r\n\t1) the count of elapsed time of the event being monitored\r\n\t2) the \"clock\" time in the same units\r\nthe precision timers are used where the standard system timers are not precise enough for accurate readings. It's assumed that the service providing the data is also providing a timestamp at the same time which will eliminate any error that may occur since some small and variable time elapses between the time the system timestamp is captured and when the data is collected from the performance DLL. Only in extreme cases has this been observed to be problematic.\r\nwhen using this type of timer, the definition of the PERF_PRECISION_TIMESTAMP counter must immediately follow the definition of the PERF_PRECISION_*_TIMER in the Object header\r\nThe timer used has the same frequency as the 100 NanoSecond Timer";
+
+ case PERF_PRECISION_OBJECT_TIMER:
+ return "The precision counters are timers that consist of two counter values:\r\n\t1) the count of elapsed time of the event being monitored\r\n\t2) the \"clock\" time in the same units\r\nthe precision timers are used where the standard system timers are not precise enough for accurate readings. It's assumed that the service providing the data is also providing a timestamp at the same time which will eliminate any error that may occur since some small and variable time elapses between the time the system timestamp is captured and when the data is collected from the performance DLL. Only in extreme cases has this been observed to be problematic.\r\nwhen using this type of timer, the definition of the PERF_PRECISION_TIMESTAMP counter must immediately follow the definition of the PERF_PRECISION_*_TIMER in the Object header\r\nThe timer used is of the frequency specified in the Object header's. PerfFreq field (PerfTime is ignored)";
+
+ default:
+ return "";
+ }
+}
+
+static const char *getCounterAlgorithm(DWORD CounterType) {
+ switch (CounterType)
+ {
+ case PERF_COUNTER_COUNTER:
+ case PERF_SAMPLE_COUNTER:
+ case PERF_COUNTER_BULK_COUNT:
+ return "(data1 - data0) / ((time1 - time0) / frequency)";
+
+ case PERF_COUNTER_QUEUELEN_TYPE:
+ case PERF_COUNTER_100NS_QUEUELEN_TYPE:
+ case PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE:
+ case PERF_COUNTER_LARGE_QUEUELEN_TYPE:
+ case PERF_AVERAGE_BULK: // normally not displayed
+ return "(data1 - data0) / (time1 - time0)";
+
+ case PERF_OBJ_TIME_TIMER:
+ case PERF_COUNTER_TIMER:
+ case PERF_100NSEC_TIMER:
+ case PERF_PRECISION_SYSTEM_TIMER:
+ case PERF_PRECISION_100NS_TIMER:
+ case PERF_PRECISION_OBJECT_TIMER:
+ case PERF_SAMPLE_FRACTION:
+ return "100 * (data1 - data0) / (time1 - time0)";
+
+ case PERF_COUNTER_TIMER_INV:
+ return "100 * (1 - ((data1 - data0) / (time1 - time0)))";
+
+ case PERF_100NSEC_TIMER_INV:
+ return "100 * (1- (data1 - data0) / (time1 - time0))";
+
+ case PERF_COUNTER_MULTI_TIMER:
+ return "100 * ((data1 - data0) / ((time1 - time0) / frequency1)) / multi1";
+
+ case PERF_100NSEC_MULTI_TIMER:
+ return "100 * ((data1 - data0) / (time1 - time0)) / multi1";
+
+ case PERF_COUNTER_MULTI_TIMER_INV:
+ case PERF_100NSEC_MULTI_TIMER_INV:
+ return "100 * (multi1 - ((data1 - data0) / (time1 - time0)))";
+
+ case PERF_COUNTER_RAWCOUNT:
+ case PERF_COUNTER_LARGE_RAWCOUNT:
+ return "data0";
+
+ case PERF_COUNTER_RAWCOUNT_HEX:
+ case PERF_COUNTER_LARGE_RAWCOUNT_HEX:
+ return "hex(data0)";
+
+ case PERF_COUNTER_DELTA:
+ case PERF_COUNTER_LARGE_DELTA:
+ return "data1 - data0";
+
+ case PERF_RAW_FRACTION:
+ case PERF_LARGE_RAW_FRACTION:
+ return "100 * data0 / time0";
+
+ case PERF_AVERAGE_TIMER:
+ return "((data1 - data0) / frequency1) / (time1 - time0)";
+
+ case PERF_ELAPSED_TIME:
+ return "(time0 - data0) / frequency0";
+
+ case PERF_COUNTER_TEXT:
+ case PERF_SAMPLE_BASE:
+ case PERF_AVERAGE_BASE:
+ case PERF_COUNTER_MULTI_BASE:
+ case PERF_RAW_BASE:
+ case PERF_COUNTER_NODATA:
+ case PERF_PRECISION_TIMESTAMP:
+ default:
+ return "";
+ }
+}
+
+void dumpSystemTime(BUFFER *wb, SYSTEMTIME *st) {
+ buffer_json_member_add_uint64(wb, "Year", st->wYear);
+ buffer_json_member_add_uint64(wb, "Month", st->wMonth);
+ buffer_json_member_add_uint64(wb, "DayOfWeek", st->wDayOfWeek);
+ buffer_json_member_add_uint64(wb, "Day", st->wDay);
+ buffer_json_member_add_uint64(wb, "Hour", st->wHour);
+ buffer_json_member_add_uint64(wb, "Minute", st->wMinute);
+ buffer_json_member_add_uint64(wb, "Second", st->wSecond);
+ buffer_json_member_add_uint64(wb, "Milliseconds", st->wMilliseconds);
+}
+
+bool dumpDataCb(PERF_DATA_BLOCK *pDataBlock, void *data) {
+ char name[4096];
+ if(!getSystemName(pDataBlock, name, sizeof(name)))
+ strncpyz(name, "[failed]", sizeof(name) - 1);
+
+ BUFFER *wb = data;
+ buffer_json_member_add_string(wb, "SystemName", name);
+
+ // Number of types of objects being reported
+ // Type: DWORD
+ buffer_json_member_add_int64(wb, "NumObjectTypes", pDataBlock->NumObjectTypes);
+
+ buffer_json_member_add_int64(wb, "LittleEndian", pDataBlock->LittleEndian);
+
+ // Version and Revision of these data structures.
+ // Version starts at 1.
+ // Revision starts at 0 for each Version.
+ // Type: DWORD
+ buffer_json_member_add_int64(wb, "Version", pDataBlock->Version);
+ buffer_json_member_add_int64(wb, "Revision", pDataBlock->Revision);
+
+ // Object Title Index of default object to display when data from this system is retrieved
+ // (-1 = none, but this is not expected to be used)
+ // Type: LONG
+ buffer_json_member_add_int64(wb, "DefaultObject", pDataBlock->DefaultObject);
+
+ // Performance counter frequency at the system under measurement
+ // Type: LARGE_INTEGER
+ buffer_json_member_add_int64(wb, "PerfFreq", pDataBlock->PerfFreq.QuadPart);
+
+ // Performance counter value at the system under measurement
+ // Type: LARGE_INTEGER
+ buffer_json_member_add_int64(wb, "PerfTime", pDataBlock->PerfTime.QuadPart);
+
+ // Performance counter time in 100 nsec units at the system under measurement
+ // Type: LARGE_INTEGER
+ buffer_json_member_add_int64(wb, "PerfTime100nSec", pDataBlock->PerfTime100nSec.QuadPart);
+
+ // Time at the system under measurement in UTC
+ // Type: SYSTEMTIME
+ buffer_json_member_add_object(wb, "SystemTime");
+ dumpSystemTime(wb, &pDataBlock->SystemTime);
+ buffer_json_object_close(wb);
+
+ if(pDataBlock->NumObjectTypes)
+ buffer_json_member_add_array(wb, "Objects");
+
+ return true;
+}
+
+static const char *GetDetailLevel(DWORD num) {
+ switch (num) {
+ case 100:
+ return "Novice (100)";
+ case 200:
+ return "Advanced (200)";
+ case 300:
+ return "Expert (300)";
+ case 400:
+ return "Wizard (400)";
+
+ default:
+ return "Unknown";
+ }
+}
+
+bool dumpObjectCb(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, void *data) {
+ (void)pDataBlock;
+ BUFFER *wb = data;
+ if(!pObjectType) {
+ buffer_json_array_close(wb); // instances or counters
+ buffer_json_object_close(wb); // objectType
+ return true;
+ }
+
+ buffer_json_add_array_item_object(wb); // objectType
+ buffer_json_member_add_int64(wb, "NameId", pObjectType->ObjectNameTitleIndex);
+ buffer_json_member_add_string(wb, "Name", RegistryFindNameByID(pObjectType->ObjectNameTitleIndex));
+ buffer_json_member_add_int64(wb, "HelpId", pObjectType->ObjectHelpTitleIndex);
+ buffer_json_member_add_string(wb, "Help", RegistryFindHelpByID(pObjectType->ObjectHelpTitleIndex));
+ buffer_json_member_add_int64(wb, "NumInstances", pObjectType->NumInstances);
+ buffer_json_member_add_int64(wb, "NumCounters", pObjectType->NumCounters);
+ buffer_json_member_add_int64(wb, "PerfTime", pObjectType->PerfTime.QuadPart);
+ buffer_json_member_add_int64(wb, "PerfFreq", pObjectType->PerfFreq.QuadPart);
+ buffer_json_member_add_int64(wb, "CodePage", pObjectType->CodePage);
+ buffer_json_member_add_int64(wb, "DefaultCounter", pObjectType->DefaultCounter);
+ buffer_json_member_add_string(wb, "DetailLevel", GetDetailLevel(pObjectType->DetailLevel));
+
+ if(ObjectTypeHasInstances(pDataBlock, pObjectType))
+ buffer_json_member_add_array(wb, "Instances");
+ else
+ buffer_json_member_add_array(wb, "Counters");
+
+ return true;
+}
+
+bool dumpInstanceCb(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *pInstance, void *data) {
+ (void)pDataBlock;
+ BUFFER *wb = data;
+ if(!pInstance) {
+ buffer_json_array_close(wb); // counters
+ buffer_json_object_close(wb); // instance
+ return true;
+ }
+
+ char name[4096];
+ if(!getInstanceName(pDataBlock, pObjectType, pInstance, name, sizeof(name)))
+ strncpyz(name, "[failed]", sizeof(name) - 1);
+
+ buffer_json_add_array_item_object(wb);
+ buffer_json_member_add_string(wb, "Instance", name);
+ buffer_json_member_add_int64(wb, "UniqueID", pInstance->UniqueID);
+ buffer_json_member_add_array(wb, "Labels");
+ {
+ buffer_json_add_array_item_object(wb);
+ {
+ buffer_json_member_add_string(wb, "key", RegistryFindNameByID(pObjectType->ObjectNameTitleIndex));
+ buffer_json_member_add_string(wb, "value", name);
+ }
+ buffer_json_object_close(wb);
+
+ if(pInstance->ParentObjectTitleIndex) {
+ PERF_INSTANCE_DEFINITION *pi = pInstance;
+ while(pi->ParentObjectTitleIndex) {
+ PERF_OBJECT_TYPE *po = getObjectTypeByIndex(pDataBlock, pInstance->ParentObjectTitleIndex);
+ pi = getInstanceByPosition(pDataBlock, po, pi->ParentObjectInstance);
+
+ if(!getInstanceName(pDataBlock, po, pi, name, sizeof(name)))
+ strncpyz(name, "[failed]", sizeof(name) - 1);
+
+ buffer_json_add_array_item_object(wb);
+ {
+ buffer_json_member_add_string(wb, "key", RegistryFindNameByID(po->ObjectNameTitleIndex));
+ buffer_json_member_add_string(wb, "value", name);
+ }
+ buffer_json_object_close(wb);
+ }
+ }
+ }
+ buffer_json_array_close(wb); // rrdlabels
+
+ buffer_json_member_add_array(wb, "Counters");
+ return true;
+}
+
+void dumpSample(BUFFER *wb, RAW_DATA *d) {
+ buffer_json_member_add_object(wb, "Value");
+ buffer_json_member_add_uint64(wb, "data", d->Data);
+ buffer_json_member_add_int64(wb, "time", d->Time);
+ buffer_json_member_add_uint64(wb, "type", d->CounterType);
+ buffer_json_member_add_int64(wb, "multi", d->MultiCounterData);
+ buffer_json_member_add_int64(wb, "frequency", d->Frequency);
+ buffer_json_object_close(wb);
+}
+
+bool dumpCounterCb(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_COUNTER_DEFINITION *pCounter, RAW_DATA *sample, void *data) {
+ (void)pDataBlock;
+ (void)pObjectType;
+ BUFFER *wb = data;
+ buffer_json_add_array_item_object(wb);
+ buffer_json_member_add_string(wb, "Counter", RegistryFindNameByID(pCounter->CounterNameTitleIndex));
+ dumpSample(wb, sample);
+ buffer_json_member_add_string(wb, "Help", RegistryFindHelpByID(pCounter->CounterHelpTitleIndex));
+ buffer_json_member_add_string(wb, "Type", getCounterType(pCounter->CounterType));
+ buffer_json_member_add_string(wb, "Algorithm", getCounterAlgorithm(pCounter->CounterType));
+ buffer_json_member_add_string(wb, "Description", getCounterDescription(pCounter->CounterType));
+ buffer_json_object_close(wb);
+ return true;
+}
+
+bool dumpInstanceCounterCb(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *pInstance, PERF_COUNTER_DEFINITION *pCounter, RAW_DATA *sample, void *data) {
+ (void)pInstance;
+ return dumpCounterCb(pDataBlock, pObjectType, pCounter, sample, data);
+}
+
+
+int windows_perflib_dump(const char *key) {
+ if(key && !*key)
+ key = NULL;
+
+ PerflibNamesRegistryInitialize();
+
+ DWORD id = 0;
+ if(key) {
+ id = RegistryFindIDByName(key);
+ if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND) {
+ fprintf(stderr, "Cannot find key '%s' in Windows Performance Counters Registry.\n", key);
+ exit(1);
+ }
+ }
+
+ CLEAN_BUFFER *wb = buffer_create(0, NULL);
+ buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_MINIFY);
+
+ perflibQueryAndTraverse(id, dumpDataCb, dumpObjectCb, dumpInstanceCb, dumpInstanceCounterCb, dumpCounterCb, wb);
+
+ buffer_json_finalize(wb);
+ printf("\n%s\n", buffer_tostring(wb));
+
+ perflibFreePerformanceData();
+
+ return 0;
+}
+
+#endif // OS_WINDOWS \ No newline at end of file
diff --git a/src/libnetdata/os/windows-perflib/perflib-names.c b/src/libnetdata/os/windows-perflib/perflib-names.c
new file mode 100644
index 000000000..18ff2af65
--- /dev/null
+++ b/src/libnetdata/os/windows-perflib/perflib-names.c
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "perflib.h"
+
+#if defined(OS_WINDOWS)
+#define REGISTRY_KEY "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009"
+
+typedef struct perflib_registry {
+ DWORD id;
+ char *key;
+ char *help;
+} perfLibRegistryEntry;
+
+static inline bool compare_perfLibRegistryEntry(const char *k1, const char *k2) {
+ return strcmp(k1, k2) == 0;
+}
+
+static inline const char *value2key_perfLibRegistryEntry(perfLibRegistryEntry *entry) {
+ return entry->key;
+}
+
+#define SIMPLE_HASHTABLE_COMPARE_KEYS_FUNCTION compare_perfLibRegistryEntry
+#define SIMPLE_HASHTABLE_VALUE2KEY_FUNCTION value2key_perfLibRegistryEntry
+#define SIMPLE_HASHTABLE_KEY_TYPE const char
+#define SIMPLE_HASHTABLE_VALUE_TYPE perfLibRegistryEntry
+#define SIMPLE_HASHTABLE_NAME _PERFLIB
+#include "libnetdata/simple_hashtable/simple_hashtable.h"
+
+static struct {
+ SPINLOCK spinlock;
+ size_t size;
+ perfLibRegistryEntry **array;
+ struct simple_hashtable_PERFLIB hashtable;
+ FILETIME lastWriteTime;
+} names_globals = {
+ .spinlock = NETDATA_SPINLOCK_INITIALIZER,
+ .size = 0,
+ .array = NULL,
+};
+
+DWORD RegistryFindIDByName(const char *name) {
+ DWORD rc = PERFLIB_REGISTRY_NAME_NOT_FOUND;
+
+ spinlock_lock(&names_globals.spinlock);
+ XXH64_hash_t hash = XXH3_64bits((void *)name, strlen(name));
+ SIMPLE_HASHTABLE_SLOT_PERFLIB *sl = simple_hashtable_get_slot_PERFLIB(&names_globals.hashtable, hash, name, false);
+ perfLibRegistryEntry *e = SIMPLE_HASHTABLE_SLOT_DATA(sl);
+ if(e) rc = e->id;
+ spinlock_unlock(&names_globals.spinlock);
+
+ return rc;
+}
+
+static inline void RegistryAddToHashTable_unsafe(perfLibRegistryEntry *entry) {
+ XXH64_hash_t hash = XXH3_64bits((void *)entry->key, strlen(entry->key));
+ SIMPLE_HASHTABLE_SLOT_PERFLIB *sl = simple_hashtable_get_slot_PERFLIB(&names_globals.hashtable, hash, entry->key, true);
+ perfLibRegistryEntry *e = SIMPLE_HASHTABLE_SLOT_DATA(sl);
+ if(!e || e->id > entry->id)
+ simple_hashtable_set_slot_PERFLIB(&names_globals.hashtable, sl, hash, entry);
+}
+
+static void RegistrySetData_unsafe(DWORD id, const char *key, const char *help) {
+ if(id >= names_globals.size) {
+ // increase the size of the array
+
+ size_t old_size = names_globals.size;
+
+ if(!names_globals.size)
+ names_globals.size = 20000;
+ else
+ names_globals.size *= 2;
+
+ names_globals.array = reallocz(names_globals.array, names_globals.size * sizeof(perfLibRegistryEntry *));
+
+ memset(names_globals.array + old_size, 0, (names_globals.size - old_size) * sizeof(perfLibRegistryEntry *));
+ }
+
+ perfLibRegistryEntry *entry = names_globals.array[id];
+ if(!entry)
+ entry = names_globals.array[id] = (perfLibRegistryEntry *)calloc(1, sizeof(perfLibRegistryEntry));
+
+ bool add_to_hash = false;
+ if(key && !entry->key) {
+ entry->key = strdup(key);
+ add_to_hash = true;
+ }
+
+ if(help && !entry->help)
+ entry->help = strdup(help);
+
+ entry->id = id;
+
+ if(add_to_hash)
+ RegistryAddToHashTable_unsafe(entry);
+}
+
+const char *RegistryFindNameByID(DWORD id) {
+ const char *s = "";
+ spinlock_lock(&names_globals.spinlock);
+
+ if(id < names_globals.size) {
+ perfLibRegistryEntry *titleEntry = names_globals.array[id];
+ if(titleEntry && titleEntry->key)
+ s = titleEntry->key;
+ }
+
+ spinlock_unlock(&names_globals.spinlock);
+ return s;
+}
+
+const char *RegistryFindHelpByID(DWORD id) {
+ const char *s = "";
+ spinlock_lock(&names_globals.spinlock);
+
+ if(id < names_globals.size) {
+ perfLibRegistryEntry *titleEntry = names_globals.array[id];
+ if(titleEntry && titleEntry->help)
+ s = titleEntry->help;
+ }
+
+ spinlock_unlock(&names_globals.spinlock);
+ return s;
+}
+
+// ----------------------------------------------------------
+
+static inline void readRegistryKeys_unsafe(BOOL helps) {
+ TCHAR *pData = NULL;
+
+ HKEY hKey;
+ DWORD dwType;
+ DWORD dwSize = 0;
+ LONG lStatus;
+
+ LPCSTR valueName;
+ if(helps)
+ valueName = TEXT("help");
+ else
+ valueName = TEXT("CounterDefinition");
+
+ // Open the key for the English counters
+ lStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT(REGISTRY_KEY), 0, KEY_READ, &hKey);
+ if (lStatus != ERROR_SUCCESS) {
+ nd_log(NDLS_COLLECTORS, NDLP_ERR,
+ "Failed to open registry key HKEY_LOCAL_MACHINE, subkey '%s', error %ld\n", REGISTRY_KEY, (long)lStatus);
+ return;
+ }
+
+ // Get the size of the 'Counters' data
+ lStatus = RegQueryValueEx(hKey, valueName, NULL, &dwType, NULL, &dwSize);
+ if (lStatus != ERROR_SUCCESS) {
+ nd_log(NDLS_COLLECTORS, NDLP_ERR,
+ "Failed to get registry key HKEY_LOCAL_MACHINE, subkey '%s', value '%s', size of data, error %ld\n",
+ REGISTRY_KEY, (const char *)valueName, (long)lStatus);
+ goto cleanup;
+ }
+
+ // Allocate memory for the data
+ pData = mallocz(dwSize);
+
+ // Read the 'Counters' data
+ lStatus = RegQueryValueEx(hKey, valueName, NULL, &dwType, (LPBYTE)pData, &dwSize);
+ if (lStatus != ERROR_SUCCESS || dwType != REG_MULTI_SZ) {
+ nd_log(NDLS_COLLECTORS, NDLP_ERR,
+ "Failed to get registry key HKEY_LOCAL_MACHINE, subkey '%s', value '%s', data, error %ld\n",
+ REGISTRY_KEY, (const char *)valueName, (long)lStatus);
+ goto cleanup;
+ }
+
+ // Process the counter data
+ TCHAR *ptr = pData;
+ while (*ptr) {
+ TCHAR *sid = ptr; // First string is the ID
+ ptr += lstrlen(ptr) + 1; // Move to the next string
+ TCHAR *name = ptr; // Second string is the name
+ ptr += lstrlen(ptr) + 1; // Move to the next pair
+
+ DWORD id = strtoul(sid, NULL, 10);
+
+ if(helps)
+ RegistrySetData_unsafe(id, NULL, name);
+ else
+ RegistrySetData_unsafe(id, name, NULL);
+ }
+
+cleanup:
+ if(pData) freez(pData);
+ RegCloseKey(hKey);
+}
+
+static BOOL RegistryKeyModification(FILETIME *lastWriteTime) {
+ HKEY hKey;
+ LONG lResult;
+ BOOL ret = FALSE;
+
+ // Open the registry key
+ lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT(REGISTRY_KEY), 0, KEY_READ, &hKey);
+ if (lResult != ERROR_SUCCESS) {
+ nd_log(NDLS_COLLECTORS, NDLP_ERR,
+ "Failed to open registry key HKEY_LOCAL_MACHINE, subkey '%s', error %ld\n", REGISTRY_KEY, (long)lResult);
+ return FALSE;
+ }
+
+ // Get the last write time
+ lResult = RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, lastWriteTime);
+ if (lResult != ERROR_SUCCESS) {
+ nd_log(NDLS_COLLECTORS, NDLP_ERR,
+ "Failed to query registry key HKEY_LOCAL_MACHINE, subkey '%s', last write time, error %ld\n", REGISTRY_KEY, (long)lResult);
+ ret = FALSE;
+ }
+ else
+ ret = TRUE;
+
+ RegCloseKey(hKey);
+ return ret;
+}
+
+static inline void RegistryFetchAll_unsafe(void) {
+ readRegistryKeys_unsafe(FALSE);
+ readRegistryKeys_unsafe(TRUE);
+}
+
+void PerflibNamesRegistryInitialize(void) {
+ spinlock_lock(&names_globals.spinlock);
+ simple_hashtable_init_PERFLIB(&names_globals.hashtable, 20000);
+ RegistryKeyModification(&names_globals.lastWriteTime);
+ RegistryFetchAll_unsafe();
+ spinlock_unlock(&names_globals.spinlock);
+}
+
+void PerflibNamesRegistryUpdate(void) {
+ FILETIME lastWriteTime = { 0 };
+ RegistryKeyModification(&lastWriteTime);
+
+ if(CompareFileTime(&lastWriteTime, &names_globals.lastWriteTime) > 0) {
+ spinlock_lock(&names_globals.spinlock);
+ if(CompareFileTime(&lastWriteTime, &names_globals.lastWriteTime) > 0) {
+ names_globals.lastWriteTime = lastWriteTime;
+ RegistryFetchAll_unsafe();
+ }
+ spinlock_unlock(&names_globals.spinlock);
+ }
+}
+
+#endif // OS_WINDOWS
diff --git a/src/libnetdata/os/windows-perflib/perflib.c b/src/libnetdata/os/windows-perflib/perflib.c
new file mode 100644
index 000000000..413a202fa
--- /dev/null
+++ b/src/libnetdata/os/windows-perflib/perflib.c
@@ -0,0 +1,687 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "perflib.h"
+
+#if defined(OS_WINDOWS)
+// --------------------------------------------------------------------------------
+
+// Retrieve a buffer that contains the specified performance data.
+// The pwszSource parameter determines the data that GetRegistryBuffer returns.
+//
+// Typically, when calling RegQueryValueEx, you can specify zero for the size of the buffer
+// and the RegQueryValueEx will set your size variable to the required buffer size. However,
+// if the source is "Global" or one or more object index values, you will need to increment
+// the buffer size in a loop until RegQueryValueEx does not return ERROR_MORE_DATA.
+static LPBYTE getPerformanceData(const char *pwszSource) {
+ static __thread DWORD size = 0;
+ static __thread LPBYTE buffer = NULL;
+
+ if(pwszSource == (const char *)0x01) {
+ freez(buffer);
+ buffer = NULL;
+ size = 0;
+ return NULL;
+ }
+
+ if(!size) {
+ size = 32 * 1024;
+ buffer = mallocz(size);
+ }
+
+ LONG status = ERROR_SUCCESS;
+ while ((status = RegQueryValueEx(HKEY_PERFORMANCE_DATA, pwszSource,
+ NULL, NULL, buffer, &size)) == ERROR_MORE_DATA) {
+ size *= 2;
+ buffer = reallocz(buffer, size);
+ }
+
+ if (status != ERROR_SUCCESS) {
+ nd_log(NDLS_COLLECTORS, NDLP_ERR, "RegQueryValueEx failed with 0x%x.\n", status);
+ return NULL;
+ }
+
+ return buffer;
+}
+
+void perflibFreePerformanceData(void) {
+ getPerformanceData((const char *)0x01);
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+
+// Retrieve the raw counter value and any supporting data needed to calculate
+// a displayable counter value. Use the counter type to determine the information
+// needed to calculate the value.
+
+static BOOL getCounterData(
+ PERF_DATA_BLOCK *pDataBlock,
+ PERF_OBJECT_TYPE* pObject,
+ PERF_COUNTER_DEFINITION* pCounter,
+ PERF_COUNTER_BLOCK* pCounterDataBlock,
+ PRAW_DATA pRawData)
+{
+ PVOID pData = NULL;
+ UNALIGNED ULONGLONG* pullData = NULL;
+ PERF_COUNTER_DEFINITION* pBaseCounter = NULL;
+ BOOL fSuccess = TRUE;
+
+ //Point to the raw counter data.
+ pData = (PVOID)((LPBYTE)pCounterDataBlock + pCounter->CounterOffset);
+
+ //Now use the PERF_COUNTER_DEFINITION.CounterType value to figure out what
+ //other information you need to calculate a displayable value.
+ switch (pCounter->CounterType) {
+
+ case PERF_COUNTER_COUNTER:
+ case PERF_COUNTER_QUEUELEN_TYPE:
+ case PERF_SAMPLE_COUNTER:
+ pRawData->Data = (ULONGLONG)(*(DWORD*)pData);
+ pRawData->Time = pDataBlock->PerfTime.QuadPart;
+ if (PERF_COUNTER_COUNTER == pCounter->CounterType || PERF_SAMPLE_COUNTER == pCounter->CounterType)
+ pRawData->Frequency = pDataBlock->PerfFreq.QuadPart;
+ break;
+
+ case PERF_OBJ_TIME_TIMER:
+ pRawData->Data = (ULONGLONG)(*(DWORD*)pData);
+ pRawData->Time = pObject->PerfTime.QuadPart;
+ break;
+
+ case PERF_COUNTER_100NS_QUEUELEN_TYPE:
+ pRawData->Data = *(UNALIGNED ULONGLONG *)pData;
+ pRawData->Time = pDataBlock->PerfTime100nSec.QuadPart;
+ break;
+
+ case PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE:
+ pRawData->Data = *(UNALIGNED ULONGLONG *)pData;
+ pRawData->Time = pObject->PerfTime.QuadPart;
+ break;
+
+ case PERF_COUNTER_TIMER:
+ case PERF_COUNTER_TIMER_INV:
+ case PERF_COUNTER_BULK_COUNT:
+ case PERF_COUNTER_LARGE_QUEUELEN_TYPE:
+ pullData = (UNALIGNED ULONGLONG *)pData;
+ pRawData->Data = *pullData;
+ pRawData->Time = pDataBlock->PerfTime.QuadPart;
+ if (pCounter->CounterType == PERF_COUNTER_BULK_COUNT)
+ pRawData->Frequency = pDataBlock->PerfFreq.QuadPart;
+ break;
+
+ case PERF_COUNTER_MULTI_TIMER:
+ case PERF_COUNTER_MULTI_TIMER_INV:
+ pullData = (UNALIGNED ULONGLONG *)pData;
+ pRawData->Data = *pullData;
+ pRawData->Frequency = pDataBlock->PerfFreq.QuadPart;
+ pRawData->Time = pDataBlock->PerfTime.QuadPart;
+
+ //These counter types have a second counter value that is adjacent to
+ //this counter value in the counter data block. The value is needed for
+ //the calculation.
+ if ((pCounter->CounterType & PERF_MULTI_COUNTER) == PERF_MULTI_COUNTER) {
+ ++pullData;
+ pRawData->MultiCounterData = *(DWORD*)pullData;
+ }
+ break;
+
+ //These counters do not use any time reference.
+ case PERF_COUNTER_RAWCOUNT:
+ case PERF_COUNTER_RAWCOUNT_HEX:
+ case PERF_COUNTER_DELTA:
+ // some counters in these categories, have CounterSize = sizeof(ULONGLONG)
+ // but the official documentation always uses them as sizeof(DWORD)
+ pRawData->Data = (ULONGLONG)(*(DWORD*)pData);
+ pRawData->Time = 0;
+ break;
+
+ case PERF_COUNTER_LARGE_RAWCOUNT:
+ case PERF_COUNTER_LARGE_RAWCOUNT_HEX:
+ case PERF_COUNTER_LARGE_DELTA:
+ pRawData->Data = *(UNALIGNED ULONGLONG*)pData;
+ pRawData->Time = 0;
+ break;
+
+ //These counters use the 100ns time base in their calculation.
+ case PERF_100NSEC_TIMER:
+ case PERF_100NSEC_TIMER_INV:
+ case PERF_100NSEC_MULTI_TIMER:
+ case PERF_100NSEC_MULTI_TIMER_INV:
+ pullData = (UNALIGNED ULONGLONG*)pData;
+ pRawData->Data = *pullData;
+ pRawData->Time = pDataBlock->PerfTime100nSec.QuadPart;
+
+ //These counter types have a second counter value that is adjacent to
+ //this counter value in the counter data block. The value is needed for
+ //the calculation.
+ if ((pCounter->CounterType & PERF_MULTI_COUNTER) == PERF_MULTI_COUNTER) {
+ ++pullData;
+ pRawData->MultiCounterData = *(DWORD*)pullData;
+ }
+ break;
+
+ //These counters use two data points, this value and one from this counter's
+ //base counter. The base counter should be the next counter in the object's
+ //list of counters.
+ case PERF_SAMPLE_FRACTION:
+ case PERF_RAW_FRACTION:
+ pRawData->Data = (ULONGLONG)(*(DWORD*)pData);
+ pBaseCounter = pCounter + 1; //Get base counter
+ if ((pBaseCounter->CounterType & PERF_COUNTER_BASE) == PERF_COUNTER_BASE) {
+ pData = (PVOID)((LPBYTE)pCounterDataBlock + pBaseCounter->CounterOffset);
+ pRawData->Time = (LONGLONG)(*(DWORD*)pData);
+ }
+ else
+ fSuccess = FALSE;
+ break;
+
+ case PERF_LARGE_RAW_FRACTION:
+ case PERF_PRECISION_SYSTEM_TIMER:
+ case PERF_PRECISION_100NS_TIMER:
+ case PERF_PRECISION_OBJECT_TIMER:
+ pRawData->Data = *(UNALIGNED ULONGLONG*)pData;
+ pBaseCounter = pCounter + 1;
+ if ((pBaseCounter->CounterType & PERF_COUNTER_BASE) == PERF_COUNTER_BASE) {
+ pData = (PVOID)((LPBYTE)pCounterDataBlock + pBaseCounter->CounterOffset);
+ pRawData->Time = *(LONGLONG*)pData;
+ }
+ else
+ fSuccess = FALSE;
+ break;
+
+ case PERF_AVERAGE_TIMER:
+ case PERF_AVERAGE_BULK:
+ pRawData->Data = *(UNALIGNED ULONGLONG*)pData;
+ pBaseCounter = pCounter+1;
+ if ((pBaseCounter->CounterType & PERF_COUNTER_BASE) == PERF_COUNTER_BASE) {
+ pData = (PVOID)((LPBYTE)pCounterDataBlock + pBaseCounter->CounterOffset);
+ pRawData->Time = *(DWORD*)pData;
+ }
+ else
+ fSuccess = FALSE;
+
+ if (pCounter->CounterType == PERF_AVERAGE_TIMER)
+ pRawData->Frequency = pDataBlock->PerfFreq.QuadPart;
+ break;
+
+ //These are base counters and are used in calculations for other counters.
+ //This case should never be entered.
+ case PERF_SAMPLE_BASE:
+ case PERF_AVERAGE_BASE:
+ case PERF_COUNTER_MULTI_BASE:
+ case PERF_RAW_BASE:
+ case PERF_LARGE_RAW_BASE:
+ pRawData->Data = 0;
+ pRawData->Time = 0;
+ fSuccess = FALSE;
+ break;
+
+ case PERF_ELAPSED_TIME:
+ pRawData->Data = *(UNALIGNED ULONGLONG*)pData;
+ pRawData->Time = pObject->PerfTime.QuadPart;
+ pRawData->Frequency = pObject->PerfFreq.QuadPart;
+ break;
+
+ //These counters are currently not supported.
+ case PERF_COUNTER_TEXT:
+ case PERF_COUNTER_NODATA:
+ case PERF_COUNTER_HISTOGRAM_TYPE:
+ default: // unknown counter types
+ pRawData->Data = 0;
+ pRawData->Time = 0;
+ fSuccess = FALSE;
+ break;
+ }
+
+ return fSuccess;
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+
+static inline BOOL isValidPointer(PERF_DATA_BLOCK *pDataBlock __maybe_unused, void *ptr __maybe_unused) {
+#ifdef NETDATA_INTERNAL_CHECKS
+ return (PBYTE)ptr >= (PBYTE)pDataBlock + pDataBlock->TotalByteLength ? FALSE : TRUE;
+#else
+ return TRUE;
+#endif
+}
+
+static inline BOOL isValidStructure(PERF_DATA_BLOCK *pDataBlock __maybe_unused, void *ptr __maybe_unused, size_t length __maybe_unused) {
+#ifdef NETDATA_INTERNAL_CHECKS
+ return (PBYTE)ptr + length > (PBYTE)pDataBlock + pDataBlock->TotalByteLength ? FALSE : TRUE;
+#else
+ return TRUE;
+#endif
+}
+
+static inline PERF_DATA_BLOCK *getDataBlock(BYTE *pBuffer) {
+ PERF_DATA_BLOCK *pDataBlock = (PERF_DATA_BLOCK *)pBuffer;
+
+ static WCHAR signature[] = { 'P', 'E', 'R', 'F' };
+
+ if(memcmp(pDataBlock->Signature, signature, sizeof(signature)) != 0) {
+ nd_log(NDLS_COLLECTORS, NDLP_ERR,
+ "WINDOWS: PERFLIB: Invalid data block signature.");
+ return NULL;
+ }
+
+ if(!isValidPointer(pDataBlock, (PBYTE)pDataBlock + pDataBlock->SystemNameOffset) ||
+ !isValidStructure(pDataBlock, (PBYTE)pDataBlock + pDataBlock->SystemNameOffset, pDataBlock->SystemNameLength)) {
+ nd_log(NDLS_COLLECTORS, NDLP_ERR,
+ "WINDOWS: PERFLIB: Invalid system name array.");
+ return NULL;
+ }
+
+ return pDataBlock;
+}
+
+static inline PERF_OBJECT_TYPE *getObjectType(PERF_DATA_BLOCK* pDataBlock, PERF_OBJECT_TYPE *lastObjectType) {
+ PERF_OBJECT_TYPE* pObjectType = NULL;
+
+ if(!lastObjectType)
+ pObjectType = (PERF_OBJECT_TYPE *)((PBYTE)pDataBlock + pDataBlock->HeaderLength);
+ else if (lastObjectType->TotalByteLength != 0)
+ pObjectType = (PERF_OBJECT_TYPE *)((PBYTE)lastObjectType + lastObjectType->TotalByteLength);
+
+ if(pObjectType && (!isValidPointer(pDataBlock, pObjectType) || !isValidStructure(pDataBlock, pObjectType, pObjectType->TotalByteLength))) {
+ nd_log(NDLS_COLLECTORS, NDLP_ERR,
+ "WINDOWS: PERFLIB: Invalid ObjectType!");
+ pObjectType = NULL;
+ }
+
+ return pObjectType;
+}
+
+inline PERF_OBJECT_TYPE *getObjectTypeByIndex(PERF_DATA_BLOCK *pDataBlock, DWORD ObjectNameTitleIndex) {
+ PERF_OBJECT_TYPE *po = NULL;
+ for(DWORD o = 0; o < pDataBlock->NumObjectTypes ; o++) {
+ po = getObjectType(pDataBlock, po);
+ if(po->ObjectNameTitleIndex == ObjectNameTitleIndex)
+ return po;
+ }
+
+ return NULL;
+}
+
+static inline PERF_INSTANCE_DEFINITION *getInstance(
+ PERF_DATA_BLOCK *pDataBlock,
+ PERF_OBJECT_TYPE *pObjectType,
+ PERF_COUNTER_BLOCK *lastCounterBlock
+) {
+ PERF_INSTANCE_DEFINITION *pInstance;
+
+ if(!lastCounterBlock)
+ pInstance = (PERF_INSTANCE_DEFINITION *)((PBYTE)pObjectType + pObjectType->DefinitionLength);
+ else
+ pInstance = (PERF_INSTANCE_DEFINITION *)((PBYTE)lastCounterBlock + lastCounterBlock->ByteLength);
+
+ if(pInstance && (!isValidPointer(pDataBlock, pInstance) || !isValidStructure(pDataBlock, pInstance, pInstance->ByteLength))) {
+ nd_log(NDLS_COLLECTORS, NDLP_ERR,
+ "WINDOWS: PERFLIB: Invalid Instance Definition!");
+ pInstance = NULL;
+ }
+
+ return pInstance;
+}
+
+static inline PERF_COUNTER_BLOCK *getObjectTypeCounterBlock(
+ PERF_DATA_BLOCK *pDataBlock,
+ PERF_OBJECT_TYPE *pObjectType
+) {
+ PERF_COUNTER_BLOCK *pCounterBlock = (PERF_COUNTER_BLOCK *)((PBYTE)pObjectType + pObjectType->DefinitionLength);
+
+ if(pCounterBlock && (!isValidPointer(pDataBlock, pCounterBlock) || !isValidStructure(pDataBlock, pCounterBlock, pCounterBlock->ByteLength))) {
+ nd_log(NDLS_COLLECTORS, NDLP_ERR,
+ "WINDOWS: PERFLIB: Invalid ObjectType CounterBlock!");
+ pCounterBlock = NULL;
+ }
+
+ return pCounterBlock;
+}
+
+static inline PERF_COUNTER_BLOCK *getInstanceCounterBlock(
+ PERF_DATA_BLOCK *pDataBlock,
+ PERF_OBJECT_TYPE *pObjectType,
+ PERF_INSTANCE_DEFINITION *pInstance
+) {
+ (void)pObjectType;
+ PERF_COUNTER_BLOCK *pCounterBlock = (PERF_COUNTER_BLOCK *)((PBYTE)pInstance + pInstance->ByteLength);
+
+ if(pCounterBlock && (!isValidPointer(pDataBlock, pCounterBlock) || !isValidStructure(pDataBlock, pCounterBlock, pCounterBlock->ByteLength))) {
+ nd_log(NDLS_COLLECTORS, NDLP_ERR,
+ "WINDOWS: PERFLIB: Invalid Instance CounterBlock!");
+ pCounterBlock = NULL;
+ }
+
+ return pCounterBlock;
+}
+
+inline PERF_INSTANCE_DEFINITION *getInstanceByPosition(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, DWORD instancePosition) {
+ PERF_INSTANCE_DEFINITION *pi = NULL;
+ PERF_COUNTER_BLOCK *pc = NULL;
+ for(DWORD i = 0; i <= instancePosition ;i++) {
+ pi = getInstance(pDataBlock, pObjectType, pc);
+ pc = getInstanceCounterBlock(pDataBlock, pObjectType, pi);
+ }
+ return pi;
+}
+
+static inline PERF_COUNTER_DEFINITION *getCounterDefinition(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_COUNTER_DEFINITION *lastCounterDefinition) {
+ PERF_COUNTER_DEFINITION *pCounterDefinition = NULL;
+
+ if(!lastCounterDefinition)
+ pCounterDefinition = (PERF_COUNTER_DEFINITION *)((PBYTE)pObjectType + pObjectType->HeaderLength);
+ else
+ pCounterDefinition = (PERF_COUNTER_DEFINITION *)((PBYTE)lastCounterDefinition + lastCounterDefinition->ByteLength);
+
+ if(pCounterDefinition && (!isValidPointer(pDataBlock, pCounterDefinition) || !isValidStructure(pDataBlock, pCounterDefinition, pCounterDefinition->ByteLength))) {
+ nd_log(NDLS_COLLECTORS, NDLP_ERR,
+ "WINDOWS: PERFLIB: Invalid Counter Definition!");
+ pCounterDefinition = NULL;
+ }
+
+ return pCounterDefinition;
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+
+static inline BOOL getEncodedStringToUTF8(char *dst, size_t dst_len, DWORD CodePage, char *start, DWORD length) {
+ static __thread wchar_t unicode[PERFLIB_MAX_NAME_LENGTH];
+
+ WCHAR *tempBuffer; // Temporary buffer for Unicode data
+ DWORD charsCopied = 0;
+
+ if (CodePage == 0) {
+ // Input is already Unicode (UTF-16)
+ tempBuffer = (WCHAR *)start;
+ charsCopied = length / sizeof(WCHAR); // Convert byte length to number of WCHARs
+ }
+ else {
+ tempBuffer = unicode;
+ charsCopied = any_to_utf16(CodePage, unicode, _countof(unicode), start, (int)length, NULL);
+ if(!charsCopied) return FALSE;
+ }
+
+ // Now convert from Unicode (UTF-16) to UTF-8
+ int bytesCopied = WideCharToMultiByte(CP_UTF8, 0, tempBuffer, (int)charsCopied, dst, (int)dst_len, NULL, NULL);
+ if (bytesCopied == 0) {
+ dst[0] = '\0'; // Ensure the buffer is null-terminated even on failure
+ return FALSE;
+ }
+
+ dst[bytesCopied - 1] = '\0'; // Ensure buffer is null-terminated
+ return TRUE;
+}
+
+inline BOOL getInstanceName(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *pInstance,
+ char *buffer, size_t bufferLen) {
+ (void)pDataBlock;
+ if (!pInstance || !buffer || !bufferLen) return FALSE;
+
+ return getEncodedStringToUTF8(buffer, bufferLen, pObjectType->CodePage,
+ ((char *)pInstance + pInstance->NameOffset), pInstance->NameLength);
+}
+
+inline BOOL getSystemName(PERF_DATA_BLOCK *pDataBlock, char *buffer, size_t bufferLen) {
+ return getEncodedStringToUTF8(buffer, bufferLen, 0,
+ ((char *)pDataBlock + pDataBlock->SystemNameOffset), pDataBlock->SystemNameLength);
+}
+
+inline bool ObjectTypeHasInstances(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType) {
+ (void)pDataBlock;
+ return pObjectType->NumInstances != PERF_NO_INSTANCES && pObjectType->NumInstances > 0;
+}
+
+PERF_OBJECT_TYPE *perflibFindObjectTypeByName(PERF_DATA_BLOCK *pDataBlock, const char *name) {
+ PERF_OBJECT_TYPE* pObjectType = NULL;
+ for(DWORD o = 0; o < pDataBlock->NumObjectTypes; o++) {
+ pObjectType = getObjectType(pDataBlock, pObjectType);
+ if(strcmp(name, RegistryFindNameByID(pObjectType->ObjectNameTitleIndex)) == 0)
+ return pObjectType;
+ }
+
+ return NULL;
+}
+
+PERF_INSTANCE_DEFINITION *perflibForEachInstance(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *lastInstance) {
+ if(!ObjectTypeHasInstances(pDataBlock, pObjectType))
+ return NULL;
+
+ return getInstance(pDataBlock, pObjectType,
+ lastInstance ?
+ getInstanceCounterBlock(pDataBlock, pObjectType, lastInstance) :
+ NULL );
+}
+
+bool perflibGetInstanceCounter(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *pInstance, COUNTER_DATA *cd) {
+ DWORD id = cd->id;
+ const char *key = cd->key;
+ internal_fatal(key == NULL, "You have to set a key for this call.");
+
+ if(unlikely(cd->failures >= PERFLIB_MAX_FAILURES_TO_FIND_METRIC)) {
+ // we don't want to lookup and compare strings all the time
+ // when a metric is not there, so we try to find it for
+ // XX times, and then we give up.
+
+ if(cd->failures == PERFLIB_MAX_FAILURES_TO_FIND_METRIC) {
+ nd_log(NDLS_COLLECTORS, NDLP_ERR,
+ "WINDOWS: PERFLIB: Giving up on metric '%s' (tried to find it %u times).",
+ cd->key, cd->failures);
+
+ cd->failures++; // increment it once, so that we will not log this again
+ }
+
+ goto failed;
+ }
+
+ PERF_COUNTER_DEFINITION *pCounterDefinition = NULL;
+ for(DWORD c = 0; c < pObjectType->NumCounters ;c++) {
+ pCounterDefinition = getCounterDefinition(pDataBlock, pObjectType, pCounterDefinition);
+ if(!pCounterDefinition) {
+ nd_log(NDLS_COLLECTORS, NDLP_ERR,
+ "WINDOWS: PERFLIB: Cannot read counter definition No %u (out of %u)",
+ c, pObjectType->NumCounters);
+ break;
+ }
+
+ if(id) {
+ if(id != pCounterDefinition->CounterNameTitleIndex)
+ continue;
+ }
+ else {
+ const char *name = RegistryFindNameByID(pCounterDefinition->CounterNameTitleIndex);
+ if(strcmp(name, key) != 0)
+ continue;
+
+ cd->id = pCounterDefinition->CounterNameTitleIndex;
+ }
+
+ cd->current.CounterType = cd->OverwriteCounterType ? cd->OverwriteCounterType : pCounterDefinition->CounterType;
+ PERF_COUNTER_BLOCK *pCounterBlock = getInstanceCounterBlock(pDataBlock, pObjectType, pInstance);
+
+ cd->previous = cd->current;
+ if(likely(getCounterData(pDataBlock, pObjectType, pCounterDefinition, pCounterBlock, &cd->current))) {
+ cd->updated = true;
+ cd->failures = 0;
+ return true;
+ }
+ }
+
+ cd->failures++;
+
+failed:
+ cd->previous = cd->current;
+ cd->current = RAW_DATA_EMPTY;
+ cd->updated = false;
+ return false;
+}
+
+bool perflibGetObjectCounter(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, COUNTER_DATA *cd) {
+ PERF_COUNTER_DEFINITION *pCounterDefinition = NULL;
+ for(DWORD c = 0; c < pObjectType->NumCounters ;c++) {
+ pCounterDefinition = getCounterDefinition(pDataBlock, pObjectType, pCounterDefinition);
+ if(!pCounterDefinition) {
+ nd_log(NDLS_COLLECTORS, NDLP_ERR,
+ "WINDOWS: PERFLIB: Cannot read counter definition No %u (out of %u)",
+ c, pObjectType->NumCounters);
+ break;
+ }
+
+ if(cd->id) {
+ if(cd->id != pCounterDefinition->CounterNameTitleIndex)
+ continue;
+ }
+ else {
+ if(strcmp(RegistryFindNameByID(pCounterDefinition->CounterNameTitleIndex), cd->key) != 0)
+ continue;
+
+ cd->id = pCounterDefinition->CounterNameTitleIndex;
+ }
+
+ cd->current.CounterType = cd->OverwriteCounterType ? cd->OverwriteCounterType : pCounterDefinition->CounterType;
+ PERF_COUNTER_BLOCK *pCounterBlock = getObjectTypeCounterBlock(pDataBlock, pObjectType);
+
+ cd->previous = cd->current;
+ cd->updated = getCounterData(pDataBlock, pObjectType, pCounterDefinition, pCounterBlock, &cd->current);
+ return cd->updated;
+ }
+
+ cd->previous = cd->current;
+ cd->current = RAW_DATA_EMPTY;
+ cd->updated = false;
+ return false;
+}
+
+PERF_DATA_BLOCK *perflibGetPerformanceData(DWORD id) {
+ char source[24];
+ snprintfz(source, sizeof(source), "%u", id);
+
+ LPBYTE pData = (LPBYTE)getPerformanceData((id > 0) ? source : NULL);
+ if (!pData) return NULL;
+
+ PERF_DATA_BLOCK *pDataBlock = getDataBlock(pData);
+ if(!pDataBlock) return NULL;
+
+ return pDataBlock;
+}
+
+int perflibQueryAndTraverse(DWORD id,
+ perflib_data_cb dataCb,
+ perflib_object_cb objectCb,
+ perflib_instance_cb instanceCb,
+ perflib_instance_counter_cb instanceCounterCb,
+ perflib_counter_cb counterCb,
+ void *data) {
+ int counters = -1;
+
+ PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id);
+ if(!pDataBlock) goto cleanup;
+
+ bool do_data = true;
+ if(dataCb)
+ do_data = dataCb(pDataBlock, data);
+
+ PERF_OBJECT_TYPE* pObjectType = NULL;
+ for(DWORD o = 0; do_data && o < pDataBlock->NumObjectTypes; o++) {
+ pObjectType = getObjectType(pDataBlock, pObjectType);
+ if(!pObjectType) {
+ nd_log(NDLS_COLLECTORS, NDLP_ERR,
+ "WINDOWS: PERFLIB: Cannot read object type No %d (out of %d)",
+ o, pDataBlock->NumObjectTypes);
+ break;
+ }
+
+ bool do_object = true;
+ if(objectCb)
+ do_object = objectCb(pDataBlock, pObjectType, data);
+
+ if(!do_object)
+ continue;
+
+ if(ObjectTypeHasInstances(pDataBlock, pObjectType)) {
+ PERF_INSTANCE_DEFINITION *pInstance = NULL;
+ PERF_COUNTER_BLOCK *pCounterBlock = NULL;
+ for(LONG i = 0; i < pObjectType->NumInstances ;i++) {
+ pInstance = getInstance(pDataBlock, pObjectType, pCounterBlock);
+ if(!pInstance) {
+ nd_log(NDLS_COLLECTORS, NDLP_ERR,
+ "WINDOWS: PERFLIB: Cannot read Instance No %d (out of %d)",
+ i, pObjectType->NumInstances);
+ break;
+ }
+
+ pCounterBlock = getInstanceCounterBlock(pDataBlock, pObjectType, pInstance);
+ if(!pCounterBlock) {
+ nd_log(NDLS_COLLECTORS, NDLP_ERR,
+ "WINDOWS: PERFLIB: Cannot read CounterBlock of instance No %d (out of %d)",
+ i, pObjectType->NumInstances);
+ break;
+ }
+
+ bool do_instance = true;
+ if(instanceCb)
+ do_instance = instanceCb(pDataBlock, pObjectType, pInstance, data);
+
+ if(!do_instance)
+ continue;
+
+ PERF_COUNTER_DEFINITION *pCounterDefinition = NULL;
+ for(DWORD c = 0; c < pObjectType->NumCounters ;c++) {
+ pCounterDefinition = getCounterDefinition(pDataBlock, pObjectType, pCounterDefinition);
+ if(!pCounterDefinition) {
+ nd_log(NDLS_COLLECTORS, NDLP_ERR,
+ "WINDOWS: PERFLIB: Cannot read counter definition No %u (out of %u)",
+ c, pObjectType->NumCounters);
+ break;
+ }
+
+ RAW_DATA sample = {
+ .CounterType = pCounterDefinition->CounterType,
+ };
+ if(getCounterData(pDataBlock, pObjectType, pCounterDefinition, pCounterBlock, &sample)) {
+ // DisplayCalculatedValue(&sample, &sample);
+
+ if(instanceCounterCb) {
+ instanceCounterCb(pDataBlock, pObjectType, pInstance, pCounterDefinition, &sample, data);
+ counters++;
+ }
+ }
+ }
+
+ if(instanceCb)
+ instanceCb(pDataBlock, pObjectType, NULL, data);
+ }
+ }
+ else {
+ PERF_COUNTER_BLOCK *pCounterBlock = getObjectTypeCounterBlock(pDataBlock, pObjectType);
+ PERF_COUNTER_DEFINITION *pCounterDefinition = NULL;
+ for(DWORD c = 0; c < pObjectType->NumCounters ;c++) {
+ pCounterDefinition = getCounterDefinition(pDataBlock, pObjectType, pCounterDefinition);
+ if(!pCounterDefinition) {
+ nd_log(NDLS_COLLECTORS, NDLP_ERR,
+ "WINDOWS: PERFLIB: Cannot read counter definition No %u (out of %u)",
+ c, pObjectType->NumCounters);
+ break;
+ }
+
+ RAW_DATA sample = {
+ .CounterType = pCounterDefinition->CounterType,
+ };
+ if(getCounterData(pDataBlock, pObjectType, pCounterDefinition, pCounterBlock, &sample)) {
+ // DisplayCalculatedValue(&sample, &sample);
+
+ if(counterCb) {
+ counterCb(pDataBlock, pObjectType, pCounterDefinition, &sample, data);
+ counters++;
+ }
+ }
+ }
+ }
+
+ if(objectCb)
+ objectCb(pDataBlock, NULL, data);
+ }
+
+cleanup:
+ return counters;
+}
+
+#endif // OS_WINDOWS \ No newline at end of file
diff --git a/src/libnetdata/os/windows-perflib/perflib.h b/src/libnetdata/os/windows-perflib/perflib.h
new file mode 100644
index 000000000..650e5503b
--- /dev/null
+++ b/src/libnetdata/os/windows-perflib/perflib.h
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef NETDATA_PERFLIB_H
+#define NETDATA_PERFLIB_H
+
+#include "libnetdata/libnetdata.h"
+
+#if defined(OS_WINDOWS)
+
+typedef uint32_t DWORD;
+typedef long long LONGLONG;
+typedef unsigned long long ULONGLONG;
+typedef int BOOL;
+
+struct _PERF_DATA_BLOCK;
+typedef struct _PERF_DATA_BLOCK PERF_DATA_BLOCK;
+struct _PERF_OBJECT_TYPE;
+typedef struct _PERF_OBJECT_TYPE PERF_OBJECT_TYPE;
+struct _PERF_INSTANCE_DEFINITION;
+typedef struct _PERF_INSTANCE_DEFINITION PERF_INSTANCE_DEFINITION;
+struct _PERF_COUNTER_DEFINITION;
+typedef struct _PERF_COUNTER_DEFINITION PERF_COUNTER_DEFINITION;
+
+const char *RegistryFindNameByID(DWORD id);
+const char *RegistryFindHelpByID(DWORD id);
+DWORD RegistryFindIDByName(const char *name);
+#define PERFLIB_REGISTRY_NAME_NOT_FOUND (DWORD)-1
+#define PERFLIB_MAX_NAME_LENGTH 1024
+
+PERF_DATA_BLOCK *perflibGetPerformanceData(DWORD id);
+void perflibFreePerformanceData(void);
+PERF_OBJECT_TYPE *perflibFindObjectTypeByName(PERF_DATA_BLOCK *pDataBlock, const char *name);
+PERF_INSTANCE_DEFINITION *perflibForEachInstance(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *lastInstance);
+
+typedef struct _rawdata {
+ DWORD CounterType;
+ DWORD MultiCounterData; // Second raw counter value for multi-valued counters
+ ULONGLONG Data; // Raw counter data
+ LONGLONG Time; // Is a time value or a base value
+ LONGLONG Frequency;
+} RAW_DATA, *PRAW_DATA;
+
+typedef struct _counterdata {
+ DWORD id;
+ bool updated;
+ uint8_t failures; // counts the number of failures to find this key
+ const char *key;
+ DWORD OverwriteCounterType; // if set, the counter type will be overwritten once read
+ RAW_DATA current;
+ RAW_DATA previous;
+} COUNTER_DATA;
+
+#define PERFLIB_MAX_FAILURES_TO_FIND_METRIC 10
+
+#define RAW_DATA_EMPTY (RAW_DATA){ 0 }
+
+bool perflibGetInstanceCounter(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *pInstance, COUNTER_DATA *cd);
+bool perflibGetObjectCounter(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, COUNTER_DATA *cd);
+
+typedef bool (*perflib_data_cb)(PERF_DATA_BLOCK *pDataBlock, void *data);
+typedef bool (*perflib_object_cb)(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, void *data);
+typedef bool (*perflib_instance_cb)(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *pInstance, void *data);
+typedef bool (*perflib_instance_counter_cb)(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *pInstance, PERF_COUNTER_DEFINITION *pCounter, RAW_DATA *sample, void *data);
+typedef bool (*perflib_counter_cb)(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_COUNTER_DEFINITION *pCounter, RAW_DATA *sample, void *data);
+
+int perflibQueryAndTraverse(DWORD id,
+ perflib_data_cb dataCb,
+ perflib_object_cb objectCb,
+ perflib_instance_cb instanceCb,
+ perflib_instance_counter_cb instanceCounterCb,
+ perflib_counter_cb counterCb,
+ void *data);
+
+bool ObjectTypeHasInstances(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType);
+
+BOOL getInstanceName(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *pInstance,
+ char *buffer, size_t bufferLen);
+
+BOOL getSystemName(PERF_DATA_BLOCK *pDataBlock, char *buffer, size_t bufferLen);
+
+PERF_OBJECT_TYPE *getObjectTypeByIndex(PERF_DATA_BLOCK *pDataBlock, DWORD ObjectNameTitleIndex);
+
+PERF_INSTANCE_DEFINITION *getInstanceByPosition(
+ PERF_DATA_BLOCK *pDataBlock,
+ PERF_OBJECT_TYPE *pObjectType,
+ DWORD instancePosition);
+
+void PerflibNamesRegistryInitialize(void);
+void PerflibNamesRegistryUpdate(void);
+
+#endif // OS_WINDOWS
+#endif //NETDATA_PERFLIB_H