From 83ba6762cc43d9db581b979bb5e3445669e46cc2 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 25 Nov 2024 18:33:56 +0100 Subject: Merging upstream version 2.0.3+dfsg (Closes: #923993, #1042533, #1045145). Signed-off-by: Daniel Baumann --- src/collectors/windows.plugin/GetSystemUptime.c | 68 +- .../integrations/memory_statistics.md | 123 ++ .../integrations/system_statistics.md | 123 ++ .../integrations/system_thermal_zone.md | 121 ++ src/collectors/windows.plugin/metadata.yaml | 276 +++ src/collectors/windows.plugin/metdata.yaml | 92 - src/collectors/windows.plugin/perflib-dump.c | 529 ------ src/collectors/windows.plugin/perflib-hyperv.c | 1793 ++++++++++++++++++++ src/collectors/windows.plugin/perflib-memory.c | 284 +++- src/collectors/windows.plugin/perflib-mssql.c | 1413 +++++++++++++++ src/collectors/windows.plugin/perflib-names.c | 242 --- .../windows.plugin/perflib-netframework.c | 796 +++++++++ src/collectors/windows.plugin/perflib-network.c | 1500 +++++++++++----- src/collectors/windows.plugin/perflib-objects.c | 94 +- src/collectors/windows.plugin/perflib-processes.c | 116 +- src/collectors/windows.plugin/perflib-processor.c | 410 ++--- src/collectors/windows.plugin/perflib-rrd.c | 822 ++++----- src/collectors/windows.plugin/perflib-rrd.h | 23 +- src/collectors/windows.plugin/perflib-storage.c | 949 +++++++---- .../windows.plugin/perflib-thermalzone.c | 103 ++ .../windows.plugin/perflib-web-service.c | 669 ++++++++ src/collectors/windows.plugin/perflib.c | 671 -------- src/collectors/windows.plugin/perflib.h | 72 - src/collectors/windows.plugin/windows-internals.h | 35 +- src/collectors/windows.plugin/windows_plugin.c | 33 +- src/collectors/windows.plugin/windows_plugin.h | 77 +- 26 files changed, 8194 insertions(+), 3240 deletions(-) create mode 100644 src/collectors/windows.plugin/integrations/memory_statistics.md create mode 100644 src/collectors/windows.plugin/integrations/system_statistics.md create mode 100644 src/collectors/windows.plugin/integrations/system_thermal_zone.md create mode 100644 src/collectors/windows.plugin/metadata.yaml delete mode 100644 src/collectors/windows.plugin/metdata.yaml delete mode 100644 src/collectors/windows.plugin/perflib-dump.c create mode 100644 src/collectors/windows.plugin/perflib-hyperv.c create mode 100644 src/collectors/windows.plugin/perflib-mssql.c delete mode 100644 src/collectors/windows.plugin/perflib-names.c create mode 100644 src/collectors/windows.plugin/perflib-netframework.c create mode 100644 src/collectors/windows.plugin/perflib-thermalzone.c create mode 100644 src/collectors/windows.plugin/perflib-web-service.c delete mode 100644 src/collectors/windows.plugin/perflib.c delete mode 100644 src/collectors/windows.plugin/perflib.h (limited to 'src/collectors/windows.plugin') diff --git a/src/collectors/windows.plugin/GetSystemUptime.c b/src/collectors/windows.plugin/GetSystemUptime.c index 9ed939ca0..59bf9d855 100644 --- a/src/collectors/windows.plugin/GetSystemUptime.c +++ b/src/collectors/windows.plugin/GetSystemUptime.c @@ -1,34 +1,34 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "windows_plugin.h" -#include "windows-internals.h" - -int do_GetSystemUptime(int update_every, usec_t dt __maybe_unused) { - ULONGLONG uptime = GetTickCount64(); // in milliseconds - - static RRDSET *st = NULL; - static RRDDIM *rd_uptime = NULL; - if (!st) { - st = rrdset_create_localhost( - "system" - , "uptime" - , NULL - , "uptime" - , "system.uptime" - , "System Uptime" - , "seconds" - , PLUGIN_WINDOWS_NAME - , "GetSystemUptime" - , NETDATA_CHART_PRIO_SYSTEM_UPTIME - , update_every - , RRDSET_TYPE_LINE - ); - - rd_uptime = rrddim_add(st, "uptime", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); - } - - rrddim_set_by_pointer(st, rd_uptime, (collected_number)uptime); - rrdset_done(st); - - return 0; -} +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "windows_plugin.h" +#include "windows-internals.h" + +int do_GetSystemUptime(int update_every, usec_t dt __maybe_unused) { + ULONGLONG uptime = GetTickCount64(); // in milliseconds + + static RRDSET *st = NULL; + static RRDDIM *rd_uptime = NULL; + if (!st) { + st = rrdset_create_localhost( + "system" + , "uptime" + , NULL + , "uptime" + , "system.uptime" + , "System Uptime" + , "seconds" + , PLUGIN_WINDOWS_NAME + , "GetSystemUptime" + , NETDATA_CHART_PRIO_SYSTEM_UPTIME + , update_every + , RRDSET_TYPE_LINE + ); + + rd_uptime = rrddim_add(st, "uptime", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); + } + + rrddim_set_by_pointer(st, rd_uptime, (collected_number)uptime); + rrdset_done(st); + + return 0; +} diff --git a/src/collectors/windows.plugin/integrations/memory_statistics.md b/src/collectors/windows.plugin/integrations/memory_statistics.md new file mode 100644 index 000000000..2f67580a6 --- /dev/null +++ b/src/collectors/windows.plugin/integrations/memory_statistics.md @@ -0,0 +1,123 @@ + + +# Memory statistics + + + + + +Plugin: windows.plugin +Module: PerflibMemory + + + +## Overview + +This collector monitors swap and memory pool statistics on Windows systems. + + +It queries for the 'Memory' object from Perflib in order to gather the metrics. + + +This collector is only supported on the following platforms: + +- windows + +This collector only supports collecting metrics from a single instance of this integration. + + +### Default Behavior + +#### Auto-Detection + +The collector automatically detects all of the metrics, no further configuration is required. + + +#### Limits + +The default configuration for this integration does not impose any limits on data collection. + +#### Performance Impact + +The default configuration for this integration is not expected to impose a significant performance impact on the system. + + +## Metrics + +Metrics grouped by *scope*. + +The scope defines the instance that the metric belongs to. An instance is uniquely identified by a set of labels. + + + +### Per Memory statistics instance + +These metrics refer to the entire monitored instance + +This scope has no labels. + +Metrics: + +| Metric | Dimensions | Unit | +|:------|:----------|:----| +| mem.swap_iops | read, write | operations/s | +| mem.swap_pages_io | read, write | pages/s | +| mem.system_pool_size | paged, pool-paged | bytes | + + + +## Alerts + +There are no alerts configured by default for this integration. + + +## Setup + +### Prerequisites + +No action required. + +### Configuration + +#### File + +The configuration file name for this integration is `netdata.conf`. +Configuration for this specific integration is located in the `[plugin:windows]` section within that file. + +The file format is a modified INI syntax. The general structure is: + +```ini +[section1] + option1 = some value + option2 = some other value + +[section2] + option3 = some third value +``` +You can edit the configuration file using the [`edit-config`](https://github.com/netdata/netdata/blob/master/docs/netdata-agent/configuration/README.md#edit-a-configuration-file-using-edit-config) script from the +Netdata [config directory](https://github.com/netdata/netdata/blob/master/docs/netdata-agent/configuration/README.md#the-netdata-config-directory). + +```bash +cd /etc/netdata 2>/dev/null || cd /opt/netdata/etc/netdata +sudo ./edit-config netdata.conf +``` +#### Options + + + +| Name | Description | Default | Required | +|:----|:-----------|:-------|:--------:| +| PerflibMemory | An option to enable or disable the data collection. | yes | no | + +#### Examples +There are no configuration examples. + + diff --git a/src/collectors/windows.plugin/integrations/system_statistics.md b/src/collectors/windows.plugin/integrations/system_statistics.md new file mode 100644 index 000000000..6df183a7a --- /dev/null +++ b/src/collectors/windows.plugin/integrations/system_statistics.md @@ -0,0 +1,123 @@ + + +# System statistics + + + + + +Plugin: windows.plugin +Module: PerflibProcesses + + + +## Overview + +This collector monitors the current number of processes, threads, and context switches on Windows systems. + + +It queries the 'System' object from Perflib in order to gather the metrics. + + +This collector is only supported on the following platforms: + +- windows + +This collector only supports collecting metrics from a single instance of this integration. + + +### Default Behavior + +#### Auto-Detection + +The collector automatically detects all of the metrics, no further configuration is required. + + +#### Limits + +The default configuration for this integration does not impose any limits on data collection. + +#### Performance Impact + +The default configuration for this integration is not expected to impose a significant performance impact on the system. + + +## Metrics + +Metrics grouped by *scope*. + +The scope defines the instance that the metric belongs to. An instance is uniquely identified by a set of labels. + + + +### Per System statistics instance + +These metrics refer to the entire monitored instance. + +This scope has no labels. + +Metrics: + +| Metric | Dimensions | Unit | +|:------|:----------|:----| +| system.processes | running | processes | +| system.threads | threads | threads | +| system.ctxt | switches | context switches/s | + + + +## Alerts + +There are no alerts configured by default for this integration. + + +## Setup + +### Prerequisites + +No action required. + +### Configuration + +#### File + +The configuration file name for this integration is `netdata.conf`. +Configuration for this specific integration is located in the `[plugin:windows]` section within that file. + +The file format is a modified INI syntax. The general structure is: + +```ini +[section1] + option1 = some value + option2 = some other value + +[section2] + option3 = some third value +``` +You can edit the configuration file using the [`edit-config`](https://github.com/netdata/netdata/blob/master/docs/netdata-agent/configuration/README.md#edit-a-configuration-file-using-edit-config) script from the +Netdata [config directory](https://github.com/netdata/netdata/blob/master/docs/netdata-agent/configuration/README.md#the-netdata-config-directory). + +```bash +cd /etc/netdata 2>/dev/null || cd /opt/netdata/etc/netdata +sudo ./edit-config netdata.conf +``` +#### Options + + + +| Name | Description | Default | Required | +|:----|:-----------|:-------|:--------:| +| PerflibProcesses | An option to enable or disable the data collection. | yes | no | + +#### Examples +There are no configuration examples. + + diff --git a/src/collectors/windows.plugin/integrations/system_thermal_zone.md b/src/collectors/windows.plugin/integrations/system_thermal_zone.md new file mode 100644 index 000000000..6a740b8a0 --- /dev/null +++ b/src/collectors/windows.plugin/integrations/system_thermal_zone.md @@ -0,0 +1,121 @@ + + +# System thermal zone + + + + + +Plugin: windows.plugin +Module: PerflibThermalZone + + + +## Overview + +This collector monitors thermal zone statistics on Windows systems. + + +It queries for the 'Thermal Zone Information' object from Perflib in order to gather the metrics. + + +This collector is only supported on the following platforms: + +- windows + +This collector only supports collecting metrics from a single instance of this integration. + + +### Default Behavior + +#### Auto-Detection + +The collector automatically detects all of the metrics, no further configuration is required. + + +#### Limits + +The default configuration for this integration does not impose any limits on data collection. + +#### Performance Impact + +The default configuration for this integration is not expected to impose a significant performance impact on the system. + + +## Metrics + +Metrics grouped by *scope*. + +The scope defines the instance that the metric belongs to. An instance is uniquely identified by a set of labels. + + + +### Per Thermal zone + +These metrics refer to a Thermal zone + +This scope has no labels. + +Metrics: + +| Metric | Dimensions | Unit | +|:------|:----------|:----| +| system.thermalzone_temperature | temperature | celsius | + + + +## Alerts + +There are no alerts configured by default for this integration. + + +## Setup + +### Prerequisites + +No action required. + +### Configuration + +#### File + +The configuration file name for this integration is `netdata.conf`. +Configuration for this specific integration is located in the `[plugin:windows]` section within that file. + +The file format is a modified INI syntax. The general structure is: + +```ini +[section1] + option1 = some value + option2 = some other value + +[section2] + option3 = some third value +``` +You can edit the configuration file using the [`edit-config`](https://github.com/netdata/netdata/blob/master/docs/netdata-agent/configuration/README.md#edit-a-configuration-file-using-edit-config) script from the +Netdata [config directory](https://github.com/netdata/netdata/blob/master/docs/netdata-agent/configuration/README.md#the-netdata-config-directory). + +```bash +cd /etc/netdata 2>/dev/null || cd /opt/netdata/etc/netdata +sudo ./edit-config netdata.conf +``` +#### Options + + + +| Name | Description | Default | Required | +|:----|:-----------|:-------|:--------:| +| PerflibThermalZone | An option to enable or disable the data collection. | yes | no | + +#### Examples +There are no configuration examples. + + diff --git a/src/collectors/windows.plugin/metadata.yaml b/src/collectors/windows.plugin/metadata.yaml new file mode 100644 index 000000000..52694e03d --- /dev/null +++ b/src/collectors/windows.plugin/metadata.yaml @@ -0,0 +1,276 @@ +plugin_name: windows.plugin +modules: + - meta: + plugin_name: windows.plugin + module_name: PerflibProcesses + monitored_instance: + name: System statistics + link: "https://learn.microsoft.com/en-us/windows/win32/procthread/processes-and-threads" + categories: + - data-collection.windows-systems + icon_filename: "windows.svg" + related_resources: + integrations: + list: [] + info_provided_to_referring_integrations: + description: "" + keywords: + - process counts + - threads + - context switch + most_popular: false + overview: + data_collection: + metrics_description: | + This collector monitors the current number of processes, threads, and context switches on Windows systems. + method_description: | + It queries the 'System' object from Perflib in order to gather the metrics. + supported_platforms: + include: ["windows"] + exclude: [] + multi_instance: false + additional_permissions: + description: "" + default_behavior: + auto_detection: + description: | + The collector automatically detects all of the metrics, no further configuration is required. + limits: + description: "" + performance_impact: + description: "" + setup: + prerequisites: + list: [] + configuration: + file: + name: "netdata.conf" + section_name: "[plugin:windows]" + description: "The Netdata main configuration file" + options: + description: "" + folding: + title: "Config option" + enabled: false + list: + - name: PerflibProcesses + description: An option to enable or disable the data collection. + default_value: yes + required: false + examples: + folding: + enabled: true + title: "" + list: [] + troubleshooting: + problems: + list: [] + alerts: [] + metrics: + folding: + title: Metrics + enabled: false + description: "" + availability: [] + scopes: + - name: global + description: "These metrics refer to the entire monitored instance." + labels: [] + metrics: + - name: system.processes + description: System Processes + unit: "processes" + chart_type: line + dimensions: + - name: running + - name: system.threads + description: System Threads + unit: "threads" + chart_type: line + dimensions: + - name: threads + - name: system.ctxt + description: CPU Context Switches + unit: "context switches/s" + chart_type: line + dimensions: + - name: switches + - meta: + plugin_name: windows.plugin + module_name: PerflibMemory + monitored_instance: + name: Memory statistics + link: "https://learn.microsoft.com/en-us/windows/win32/Memory/memory-management" + categories: + - data-collection.windows-systems + icon_filename: "windows.svg" + related_resources: + integrations: + list: [] + info_provided_to_referring_integrations: + description: "" + keywords: + - memory + - swap + most_popular: false + overview: + data_collection: + metrics_description: | + This collector monitors swap and memory pool statistics on Windows systems. + method_description: | + It queries for the 'Memory' object from Perflib in order to gather the metrics. + supported_platforms: + include: ["windows"] + exclude: [] + multi_instance: false + additional_permissions: + description: "" + default_behavior: + auto_detection: + description: | + The collector automatically detects all of the metrics, no further configuration is required. + limits: + description: "" + performance_impact: + description: "" + setup: + prerequisites: + list: [] + configuration: + file: + name: "netdata.conf" + section_name: "[plugin:windows]" + description: "The Netdata main configuration file" + options: + description: "" + folding: + title: "Config option" + enabled: false + list: + - name: PerflibMemory + description: An option to enable or disable the data collection. + default_value: yes + required: false + examples: + folding: + enabled: true + title: "" + list: [] + troubleshooting: + problems: + list: [] + alerts: [] + metrics: + folding: + title: Metrics + enabled: false + description: "" + availability: [] + scopes: + - name: global + description: "These metrics refer to the entire monitored instance" + labels: [] + metrics: + - name: mem.swap_iops + description: Swap Operations + unit: "operations/s" + chart_type: stacked + dimensions: + - name: read + - name: write + - name: mem.swap_pages_io + description: Swap Pages + unit: "pages/s" + chart_type: stacked + dimensions: + - name: read + - name: write + - name: mem.system_pool_size + description: System Memory Pool + unit: "bytes" + chart_type: stacked + dimensions: + - name: paged + - name: pool-paged + - meta: + plugin_name: windows.plugin + module_name: PerflibThermalZone + monitored_instance: + name: System thermal zone + link: "https://learn.microsoft.com/en-us/windows-hardware/design/device-experiences/design-guide" + categories: + - data-collection.windows-systems + icon_filename: "windows.svg" + related_resources: + integrations: + list: [] + info_provided_to_referring_integrations: + description: "" + keywords: + - thermal + - temperature + most_popular: false + overview: + data_collection: + metrics_description: | + This collector monitors thermal zone statistics on Windows systems. + method_description: | + It queries for the 'Thermal Zone Information' object from Perflib in order to gather the metrics. + supported_platforms: + include: ["windows"] + exclude: [] + multi_instance: false + additional_permissions: + description: "" + default_behavior: + auto_detection: + description: | + The collector automatically detects all of the metrics, no further configuration is required. + limits: + description: "" + performance_impact: + description: "" + setup: + prerequisites: + list: [] + configuration: + file: + section_name: "[plugin:windows]" + name: "netdata.conf" + description: "The Netdata main configuration file." + options: + description: "" + folding: + title: "Config Option" + enabled: false + list: + - name: PerflibThermalZone + description: An option to enable or disable the data collection. + default_value: yes + required: false + examples: + folding: + enabled: false + title: "" + list: [] + troubleshooting: + problems: + list: [] + alerts: [] + metrics: + folding: + title: Metrics + enabled: false + description: "" + availability: [] + scopes: + - name: Thermal zone + description: "These metrics refer to a Thermal zone" + labels: [] + metrics: + - name: system.thermalzone_temperature + description: Thermal zone temperature + unit: celsius + chart_type: line + dimensions: + - name: temperature diff --git a/src/collectors/windows.plugin/metdata.yaml b/src/collectors/windows.plugin/metdata.yaml deleted file mode 100644 index 090a48db5..000000000 --- a/src/collectors/windows.plugin/metdata.yaml +++ /dev/null @@ -1,92 +0,0 @@ -plugin_name: windows.plugin -modules: - - meta: - plugin_name: proc.plugin - module_name: PerflibProcesses - monitored_instance: - name: System statistics - link: "" - categories: - - data-collection.windows-systems - icon_filename: "windows.svg" - related_resources: - integrations: - list: [ ] - info_provided_to_referring_integrations: - description: "" - keywords: - - process counts - - threads - most_popular: false - overview: - data_collection: - metrics_description: | - Perflib provides different statistical methods about Microsoft Windows environment. This collector query for - Object 'System' to show actual number of processes, threads and context switches. - method_description: "" - supported_platforms: - include: [ "windows" ] - exclude: [ ] - multi_instance: false - additional_permissions: - description: "" - default_behavior: - auto_detection: - description: | - The collector auto-detects all metrics. No configuration is needed. - limits: - description: "" - performance_impact: - description: "" - setup: - prerequisites: - list: [ ] - configuration: - file: - section_name: "" - name: "" - description: "" - options: - description: "" - folding: - title: "" - enabled: true - list: [ ] - examples: - folding: - enabled: true - title: "" - list: [ ] - troubleshooting: - problems: - list: [ ] - alerts: - metrics: - folding: - title: Metrics - enabled: false - description: "" - availability: [ ] - scopes: - - name: global - description: "" - labels: [ ] - metrics: - - name: system.processes - description: System Processes - unit: "processes" - chart_type: line - dimensions: - - name: running - - name: system.threads - description: System Threads - unit: "threads" - chart_type: line - dimensions: - - name: threads - - name: system.ctxt - description: CPU Context Switches - unit: "context switches/s" - chart_type: line - dimensions: - - name: switches \ No newline at end of file diff --git a/src/collectors/windows.plugin/perflib-dump.c b/src/collectors/windows.plugin/perflib-dump.c deleted file mode 100644 index e01813a49..000000000 --- a/src/collectors/windows.plugin/perflib-dump.c +++ /dev/null @@ -1,529 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "perflib.h" -#include "windows-internals.h" - -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; -} diff --git a/src/collectors/windows.plugin/perflib-hyperv.c b/src/collectors/windows.plugin/perflib-hyperv.c new file mode 100644 index 000000000..523361995 --- /dev/null +++ b/src/collectors/windows.plugin/perflib-hyperv.c @@ -0,0 +1,1793 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "windows_plugin.h" +#include "windows-internals.h" + +#define _COMMON_PLUGIN_NAME "windows.plugin" +#define _COMMON_PLUGIN_MODULE_NAME "PerflibHyperV" +#include "../common-contexts/common-contexts.h" + +#define HYPERV "hyperv" + +static void get_and_sanitize_instance_value( + PERF_DATA_BLOCK *pDataBlock, + PERF_OBJECT_TYPE *pObjectType, + PERF_INSTANCE_DEFINITION *pi, + char *buffer, + size_t buffer_size) +{ + // char wstr[8192]; + if (!getInstanceName(pDataBlock, pObjectType, pi, buffer, buffer_size)) { + strncpyz(buffer, "[unknown]", buffer_size - 1); + // return; + } + // rrdlabels_sanitize_value(buffer, wstr, buffer_size); +} + +#define DICT_PERF_OPTION (DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE) + +#define DEFINE_RD(counter_name) RRDDIM *rd_##counter_name + +#define GET_INSTANCE_COUNTER(counter) \ + do { \ + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->counter); \ + } while (0) + +#define GET_OBJECT_COUNTER(counter) \ + do { \ + perflibGetObjectCounter(pDataBlock, pObjectType, &p->counter); \ + } while (0) + +#define SETP_DIM_VALUE(st, field) \ + do { \ + rrddim_set_by_pointer(p->st, p->rd_##field, (collected_number)p->field.current.Data); \ + } while (0) + +typedef bool (*perf_func_collect)(PERF_DATA_BLOCK *pDataBlock, int update_every, void *data); + +typedef struct { + const char *registry_name; + perf_func_collect function_collect; + dict_cb_insert_t dict_insert_cb; + size_t dict_size; + DICTIONARY *instance; +} hyperv_perf_item; + +struct hypervisor_memory { + bool collected_metadata; + bool charts_created; + + RRDSET *st_pressure; + RRDSET *st_vm_memory_physical; + RRDSET *st_vm_memory_physical_guest_visible; + + DEFINE_RD(CurrentPressure); + DEFINE_RD(PhysicalMemory); + DEFINE_RD(GuestVisiblePhysicalMemory); + DEFINE_RD(GuestAvailableMemory); + + COUNTER_DATA CurrentPressure; + COUNTER_DATA PhysicalMemory; + COUNTER_DATA GuestVisiblePhysicalMemory; + COUNTER_DATA GuestAvailableMemory; +}; + +void initialize_hyperv_memory_keys(struct hypervisor_memory *p) { + p->CurrentPressure.key = "Current Pressure"; + p->PhysicalMemory.key = "Physical Memory"; + p->GuestVisiblePhysicalMemory.key = "Guest Visible Physical Memory"; + p->GuestAvailableMemory.key = "Guest Available Memory"; +} + + +void dict_hyperv_memory_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { + struct hypervisor_memory *p = value; + initialize_hyperv_memory_keys(p); +} + +struct hypervisor_partition { + bool collected_metadata; + bool charts_created; + + RRDSET *st_vm_vid_physical_pages_allocated; + RRDSET *st_vm_vid_remote_physical_pages; + + DEFINE_RD(PhysicalPagesAllocated); + DEFINE_RD(RemotePhysicalPages); + + COUNTER_DATA PhysicalPagesAllocated; + COUNTER_DATA RemotePhysicalPages; + +}; + +void initialize_hyperv_partition_keys(struct hypervisor_partition *p) +{ + p->PhysicalPagesAllocated.key = "Physical Pages Allocated"; + p->RemotePhysicalPages.key = "Remote Physical Pages"; +} + +void dict_hyperv_partition_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { + struct hypervisor_partition *p = value; + initialize_hyperv_partition_keys(p); +} + +static bool do_hyperv_memory(PERF_DATA_BLOCK *pDataBlock, int update_every, void *data) +{ + hyperv_perf_item *item = data; + + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, item->registry_name); + if (!pObjectType) + return false; + + PERF_INSTANCE_DEFINITION *pi = NULL; + for(LONG i = 0; i < pObjectType->NumInstances ; i++) { + pi = perflibForEachInstance(pDataBlock, pObjectType, pi); + if (!pi) + break; + + get_and_sanitize_instance_value(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer)); + + struct hypervisor_memory *p = dictionary_set(item->instance, windows_shared_buffer, NULL, sizeof(*p)); + + if(!p->collected_metadata) { + p->collected_metadata = true; + } + + GET_INSTANCE_COUNTER(CurrentPressure); + GET_INSTANCE_COUNTER(PhysicalMemory); + GET_INSTANCE_COUNTER(GuestVisiblePhysicalMemory); + GET_INSTANCE_COUNTER(GuestAvailableMemory); + + if (!p->charts_created) { + p->charts_created = true; + if(!p->st_vm_memory_physical) { + p->st_vm_memory_physical = rrdset_create_localhost( + "vm_memory_physical", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV".vm_memory_physical", + "VM assigned memory", + "bytes", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_VM_MEMORY_PHYSICAL, + update_every, + RRDSET_TYPE_LINE); + + p->st_vm_memory_physical_guest_visible = rrdset_create_localhost( + "vm_memory_physical_guest_visible", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV".vm_memory_physical_guest_visible", + "VM guest visible memory", + "bytes", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_VM_MEMORY_PHYSICAL_GUEST_VISIBLE, + update_every, + RRDSET_TYPE_LINE); + + p->st_pressure = rrdset_create_localhost( + "vm_memory_pressure_current", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV".vm_memory_pressure_current", + "VM Memory Pressure", + "percentage", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_VM_MEMORY_PRESSURE_CURRENT, + update_every, + RRDSET_TYPE_LINE); + + p->rd_CurrentPressure = rrddim_add(p->st_pressure, "pressure", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + p->rd_PhysicalMemory = rrddim_add(p->st_vm_memory_physical, "assigned", NULL, 1024 * 1024, 1, RRD_ALGORITHM_ABSOLUTE); + p->rd_GuestVisiblePhysicalMemory = rrddim_add(p->st_vm_memory_physical_guest_visible, "visible", NULL, 1024 * 1024, 1, RRD_ALGORITHM_ABSOLUTE); + p->rd_GuestAvailableMemory = rrddim_add(p->st_vm_memory_physical_guest_visible, "available", NULL, 1024 * 1024, 1, RRD_ALGORITHM_ABSOLUTE); + + rrdlabels_add(p->st_vm_memory_physical->rrdlabels, "vm_name", windows_shared_buffer, RRDLABEL_SRC_AUTO); + rrdlabels_add(p->st_pressure->rrdlabels, "vm_name", windows_shared_buffer, RRDLABEL_SRC_AUTO); + rrdlabels_add(p->st_vm_memory_physical_guest_visible->rrdlabels, "vm_name", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + } + + SETP_DIM_VALUE(st_pressure, CurrentPressure); + SETP_DIM_VALUE(st_vm_memory_physical, PhysicalMemory); + SETP_DIM_VALUE(st_vm_memory_physical_guest_visible, GuestVisiblePhysicalMemory); + SETP_DIM_VALUE(st_vm_memory_physical_guest_visible, GuestAvailableMemory); + + rrdset_done(p->st_pressure); + rrdset_done(p->st_vm_memory_physical); + rrdset_done(p->st_vm_memory_physical_guest_visible); + } + + return true; +} + +static bool do_hyperv_vid_partition(PERF_DATA_BLOCK *pDataBlock, int update_every, void *data) +{ + hyperv_perf_item *item = data; + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, item->registry_name); + if (!pObjectType) + return false; + + PERF_INSTANCE_DEFINITION *pi = NULL; + for(LONG i = 0; i < pObjectType->NumInstances ; i++) { + pi = perflibForEachInstance(pDataBlock, pObjectType, pi); + if (!pi) + break; + + get_and_sanitize_instance_value(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer)); + + struct hypervisor_partition *p = dictionary_set(item->instance, windows_shared_buffer, NULL, sizeof(*p)); + + if(!p->collected_metadata) { + + p->collected_metadata = true; + } + + if(strcasecmp(windows_shared_buffer, "_Total") == 0) + continue; + + GET_INSTANCE_COUNTER(RemotePhysicalPages); + GET_INSTANCE_COUNTER(PhysicalPagesAllocated); + + if (!p->charts_created) { + p->charts_created = true; + + p->st_vm_vid_physical_pages_allocated = rrdset_create_localhost( + "vm_vid_physical_pages_allocated", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".vm_vid_physical_pages_allocated", + "VM physical pages allocated", + "pages", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_VM_VID_PHYSICAL_PAGES_ALLOCATED, + update_every, + RRDSET_TYPE_LINE); + + p->st_vm_vid_remote_physical_pages = rrdset_create_localhost( + "vm_vid_remote_physical_pages", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".vm_vid_remote_physical_pages", + "VM physical pages not allocated from the preferred NUMA node", + "pages", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_VM_VID_REMOTE_PHYSICAL_PAGES, + update_every, + RRDSET_TYPE_LINE); + + p->rd_PhysicalPagesAllocated = rrddim_add(p->st_vm_vid_physical_pages_allocated, "allocated", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + p->rd_RemotePhysicalPages = rrddim_add(p->st_vm_vid_remote_physical_pages, "remote_physical", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + rrdlabels_add(p->st_vm_vid_physical_pages_allocated->rrdlabels, "vm_name", windows_shared_buffer, RRDLABEL_SRC_AUTO); + rrdlabels_add(p->st_vm_vid_remote_physical_pages->rrdlabels, "vm_name", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + SETP_DIM_VALUE(st_vm_vid_remote_physical_pages, RemotePhysicalPages); + SETP_DIM_VALUE(st_vm_vid_physical_pages_allocated, PhysicalPagesAllocated); + + rrdset_done(p->st_vm_vid_physical_pages_allocated); + rrdset_done(p->st_vm_vid_remote_physical_pages); + } + + return true; +} + +// Define structure for Hyper-V Virtual Machine Health Summary +static struct hypervisor_health_summary { + bool collected_metadata; + bool charts_created; + + RRDSET *st_health; + + DEFINE_RD(HealthCritical); + DEFINE_RD(HealthOk); + + COUNTER_DATA HealthCritical; + COUNTER_DATA HealthOk; +} health_summary = { + .collected_metadata = false, + .st_health = NULL, + .HealthCritical.key = "Health Critical", + .HealthOk.key = "Health Ok"}; + +// Function to handle "Hyper-V Virtual Machine Health Summary" +static bool do_hyperv_health_summary(PERF_DATA_BLOCK *pDataBlock, int update_every, void *data) +{ + hyperv_perf_item *item = data; + + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, item->registry_name); + if (!pObjectType) + return false; + + struct hypervisor_health_summary *p = &health_summary; + + GET_OBJECT_COUNTER(HealthCritical); + GET_OBJECT_COUNTER(HealthOk); + + if (!p->charts_created) { + p->charts_created = true; + p->st_health = rrdset_create_localhost( + "vms_health", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".vms_health", + "Virtual machines health status", + "vms", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_VMS_HEALTH, + update_every, + RRDSET_TYPE_STACKED); + + p->rd_HealthCritical = rrddim_add(p->st_health, "critical", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + p->rd_HealthOk = rrddim_add(p->st_health, "ok", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + } + + SETP_DIM_VALUE(st_health, HealthCritical); + SETP_DIM_VALUE(st_health, HealthOk); + + rrdset_done(p->st_health); + return true; +} + +// Define structure for Hyper-V Root Partition Metrics (Device and GPA Space Pages) +struct hypervisor_root_partition { + bool collected_metadata; + bool charts_created; + + RRDSET *st_device_space_pages; + RRDSET *st_gpa_space_pages; + RRDSET *st_gpa_space_modifications; + RRDSET *st_attached_devices; + RRDSET *st_deposited_pages; + + RRDSET *st_DeviceDMAErrors; + RRDSET *st_DeviceInterruptErrors; + RRDSET *st_DeviceInterruptThrottleEvents; + RRDSET *st_IOΤLBFlushesSec; + RRDSET *st_AddressSpaces; + RRDSET *st_VirtualTLBPages; + RRDSET *st_VirtualTLBFlushEntiresSec; + + DEFINE_RD(DeviceSpacePages4K); + DEFINE_RD(DeviceSpacePages2M); + DEFINE_RD(DeviceSpacePages1G); + DEFINE_RD(GPASpacePages4K); + DEFINE_RD(GPASpacePages2M); + DEFINE_RD(GPASpacePages1G); + DEFINE_RD(GPASpaceModifications); + + DEFINE_RD(AttachedDevices); + DEFINE_RD(DepositedPages); + + DEFINE_RD(DeviceDMAErrors); + DEFINE_RD(DeviceInterruptErrors); + DEFINE_RD(DeviceInterruptThrottleEvents); + DEFINE_RD(IOΤLBFlushesSec); + DEFINE_RD(AddressSpaces); + DEFINE_RD(VirtualTLBPages); + DEFINE_RD(VirtualTLBFlushEntiresSec); + + COUNTER_DATA DeviceSpacePages4K; + COUNTER_DATA DeviceSpacePages2M; + COUNTER_DATA DeviceSpacePages1G; + COUNTER_DATA GPASpacePages4K; + COUNTER_DATA GPASpacePages2M; + COUNTER_DATA GPASpacePages1G; + COUNTER_DATA GPASpaceModifications; + COUNTER_DATA AttachedDevices; + COUNTER_DATA DepositedPages; + COUNTER_DATA DeviceDMAErrors; + COUNTER_DATA DeviceInterruptErrors; + COUNTER_DATA DeviceInterruptThrottleEvents; + COUNTER_DATA IOΤLBFlushesSec; + COUNTER_DATA AddressSpaces; + COUNTER_DATA VirtualTLBPages; + COUNTER_DATA VirtualTLBFlushEntiresSec; +}; + +// Initialize the keys for the root partition metrics +void initialize_hyperv_root_partition_keys(struct hypervisor_root_partition *p) { + p->DeviceSpacePages4K.key = "4K device pages"; + p->DeviceSpacePages2M.key = "2M device pages"; + p->DeviceSpacePages1G.key = "1G device pages"; + + p->GPASpacePages4K.key = "4K GPA pages"; + p->GPASpacePages2M.key = "2M GPA pages"; + p->GPASpacePages1G.key = "1G GPA pages"; + + p->GPASpaceModifications.key = "GPA Space Modifications/sec"; + p->AttachedDevices.key = "Attached Devices"; + p->DepositedPages.key = "Deposited Pages"; + + p->DeviceDMAErrors.key = "Device DMA Errors"; + p->DeviceInterruptErrors.key = "Device Interrupt Errors"; + p->DeviceInterruptThrottleEvents.key = "Device Interrupt Throttle Events"; + p->IOΤLBFlushesSec.key = "I/O TLB Flushes/sec"; + p->AddressSpaces.key = "Address Spaces"; + p->VirtualTLBPages.key = "Virtual TLB Pages"; + p->VirtualTLBFlushEntiresSec.key = "Virtual TLB Flush Entires/sec"; +} + +// Callback function for inserting root partition metrics into the dictionary +void dict_hyperv_root_partition_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { + struct hypervisor_root_partition *p = value; + initialize_hyperv_root_partition_keys(p); +} + +// Function to handle "Hyper-V Hypervisor Root Partition" metrics (Device Space and GPA Space) +static bool do_hyperv_root_partition(PERF_DATA_BLOCK *pDataBlock, int update_every, void *data) +{ + hyperv_perf_item *item = data; + + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, item->registry_name); + if (!pObjectType) + return false; + + PERF_INSTANCE_DEFINITION *pi = NULL; + for (LONG i = 0; i < pObjectType->NumInstances; i++) { + pi = perflibForEachInstance(pDataBlock, pObjectType, pi); + if (!pi) + break; + + get_and_sanitize_instance_value(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer)); + + if(strcasecmp(windows_shared_buffer, "_Total") == 0) + continue; + + struct hypervisor_root_partition *p = dictionary_set(item->instance, windows_shared_buffer, NULL, sizeof(*p)); + + if (!p->collected_metadata) { + p->collected_metadata = true; + } + + // Fetch counters + GET_INSTANCE_COUNTER(DeviceSpacePages4K); + GET_INSTANCE_COUNTER(DeviceSpacePages2M); + GET_INSTANCE_COUNTER(DeviceSpacePages1G); + GET_INSTANCE_COUNTER(GPASpacePages4K); + GET_INSTANCE_COUNTER(GPASpacePages2M); + GET_INSTANCE_COUNTER(GPASpacePages1G); + GET_INSTANCE_COUNTER(GPASpaceModifications); + GET_INSTANCE_COUNTER(AttachedDevices); + GET_INSTANCE_COUNTER(DepositedPages); + + GET_INSTANCE_COUNTER(DeviceDMAErrors); + GET_INSTANCE_COUNTER(DeviceInterruptErrors); + GET_INSTANCE_COUNTER(DeviceInterruptThrottleEvents); + GET_INSTANCE_COUNTER(IOΤLBFlushesSec); + GET_INSTANCE_COUNTER(AddressSpaces); + GET_INSTANCE_COUNTER(VirtualTLBPages); + GET_INSTANCE_COUNTER(VirtualTLBFlushEntiresSec); + + + // Create charts + if (!p->charts_created) { + p->charts_created = true; + p->st_device_space_pages = rrdset_create_localhost( + "root_partition_device_space_pages", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".root_partition_device_space_pages", + "Root partition device space pages", + "pages", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_ROOT_PARTITION_DEVICE_SPACE_PAGES, + update_every, + RRDSET_TYPE_LINE); + + p->rd_DeviceSpacePages4K = rrddim_add(p->st_device_space_pages, "4K", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + p->rd_DeviceSpacePages2M = rrddim_add(p->st_device_space_pages, "2M", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + p->rd_DeviceSpacePages1G = rrddim_add(p->st_device_space_pages, "1G", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + p->st_gpa_space_pages = rrdset_create_localhost( + "root_partition_gpa_space_pages", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".root_partition_gpa_space_pages", + "Root partition GPA space pages", + "pages", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_ROOT_PARTITION_GPA_SPACE_PAGES, + update_every, + RRDSET_TYPE_LINE); + + p->rd_GPASpacePages4K = rrddim_add(p->st_gpa_space_pages, "4K", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + p->rd_GPASpacePages2M = rrddim_add(p->st_gpa_space_pages, "2M", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + p->rd_GPASpacePages1G = rrddim_add(p->st_gpa_space_pages, "1G", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + p->st_gpa_space_modifications = rrdset_create_localhost( + "root_partition_gpa_space_modifications", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".root_partition_gpa_space_modifications", + "Root partition GPA space modifications", + "modifications/s", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_ROOT_PARTITION_GPA_SPACE_MODIFICATIONS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_GPASpaceModifications = + rrddim_add(p->st_gpa_space_modifications, "gpa", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + p->st_attached_devices = rrdset_create_localhost( + "root_partition_attached_devices", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".root_partition_attached_devices", + "Root partition attached devices", + "devices", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_ROOT_PARTITION_ATTACHED_DEVICES, + update_every, + RRDSET_TYPE_LINE); + + p->rd_AttachedDevices = rrddim_add(p->st_attached_devices, "attached", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + p->st_deposited_pages = rrdset_create_localhost( + "root_partition_deposited_pages", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".root_partition_deposited_pages", + "Root partition deposited pages", + "pages", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_ROOT_PARTITION_DEPOSITED_PAGES, + update_every, + RRDSET_TYPE_LINE); + + p->rd_DepositedPages = rrddim_add(p->st_deposited_pages, "gpa", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + p->st_DeviceDMAErrors = rrdset_create_localhost( + "root_partition_device_dma_errors", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".root_partition_device_dma_errors", + "Root partition illegal DMA requests", + "requests", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_ROOT_PARTITION_DEVICE_DMA_ERRORS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_DeviceDMAErrors = + rrddim_add(p->st_DeviceDMAErrors, "illegal_dma", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + p->st_DeviceInterruptErrors = rrdset_create_localhost( + "root_partition_device_interrupt_errors", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".root_partition_device_interrupt_errors", + "Root partition illegal interrupt requestss", + "requests", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_ROOT_PARTITION_DEVICE_INTERRUPT_ERRORS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_DeviceInterruptErrors = + rrddim_add(p->st_DeviceInterruptErrors, "illegal_interrupt", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + p->st_DeviceInterruptThrottleEvents = rrdset_create_localhost( + "root_partition_device_interrupt_throttle_events", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".root_partition_device_interrupt_throttle_events", + "Root partition throttled interrupts", + "events", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_ROOT_PARTITION_DEVICE_INTERRUPT_THROTTLE_EVENTS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_DeviceInterruptThrottleEvents = + rrddim_add(p->st_DeviceInterruptThrottleEvents, "throttling", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + p->st_IOΤLBFlushesSec = rrdset_create_localhost( + "root_partition_io_tlb_flush", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".root_partition_io_tlb_flush", + "Root partition flushes of I/O TLBs", + "flushes/s", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_ROOT_PARTITION_IO_TLB_FLUSH, + update_every, + RRDSET_TYPE_LINE); + + p->rd_IOΤLBFlushesSec = rrddim_add(p->st_IOΤLBFlushesSec, "gpa", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + p->st_AddressSpaces = rrdset_create_localhost( + "root_partition_address_space", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".root_partition_address_space", + "Root partition address spaces in the virtual TLB", + "address spaces", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_ROOT_PARTITION_ADDRESS_SPACE, + update_every, + RRDSET_TYPE_LINE); + + p->rd_AddressSpaces = rrddim_add(p->st_AddressSpaces, "address_spaces", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + p->st_VirtualTLBPages = rrdset_create_localhost( + "root_partition_virtual_tlb_pages", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".root_partition_virtual_tlb_pages", + "Root partition pages used by the virtual TLB", + "pages", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_ROOT_PARTITION_VIRTUAL_TLB_PAGES, + update_every, + RRDSET_TYPE_LINE); + + p->rd_VirtualTLBPages = rrddim_add(p->st_VirtualTLBPages, "used", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + p->st_VirtualTLBFlushEntiresSec = rrdset_create_localhost( + "root_partition_virtual_tlb_flush_entries", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".root_partition_virtual_tlb_flush_entries", + "Root partition flushes of the entire virtual TLB", + "flushes/s", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_ROOT_PARTITION_VIRTUAL_TLB_FLUSH_ENTRIES, + update_every, + RRDSET_TYPE_LINE); + + p->rd_VirtualTLBFlushEntiresSec = + rrddim_add(p->st_VirtualTLBFlushEntiresSec, "flushes", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + // Set the data for each dimension + + SETP_DIM_VALUE(st_device_space_pages,DeviceSpacePages4K); + SETP_DIM_VALUE(st_device_space_pages,DeviceSpacePages2M); + SETP_DIM_VALUE(st_device_space_pages,DeviceSpacePages1G); + + SETP_DIM_VALUE(st_gpa_space_pages, GPASpacePages4K); + SETP_DIM_VALUE(st_gpa_space_pages, GPASpacePages2M); + SETP_DIM_VALUE(st_gpa_space_pages, GPASpacePages1G); + + SETP_DIM_VALUE(st_gpa_space_modifications, GPASpaceModifications); + + SETP_DIM_VALUE(st_attached_devices, AttachedDevices); + SETP_DIM_VALUE(st_deposited_pages, DepositedPages); + + SETP_DIM_VALUE(st_DeviceDMAErrors, DeviceDMAErrors); + SETP_DIM_VALUE(st_DeviceInterruptErrors, DeviceInterruptErrors); + SETP_DIM_VALUE(st_DeviceInterruptThrottleEvents, DeviceInterruptThrottleEvents); + SETP_DIM_VALUE(st_IOΤLBFlushesSec, IOΤLBFlushesSec); + SETP_DIM_VALUE(st_AddressSpaces, AddressSpaces); + SETP_DIM_VALUE(st_VirtualTLBPages, VirtualTLBPages); + SETP_DIM_VALUE(st_VirtualTLBFlushEntiresSec, VirtualTLBFlushEntiresSec); + + // Mark the charts as done + rrdset_done(p->st_device_space_pages); + rrdset_done(p->st_gpa_space_pages); + rrdset_done(p->st_gpa_space_modifications); + rrdset_done(p->st_attached_devices); + rrdset_done(p->st_deposited_pages); + rrdset_done(p->st_DeviceInterruptErrors); + rrdset_done(p->st_DeviceInterruptThrottleEvents); + rrdset_done(p->st_IOΤLBFlushesSec); + rrdset_done(p->st_AddressSpaces); + rrdset_done(p->st_DeviceDMAErrors); + rrdset_done(p->st_VirtualTLBPages); + rrdset_done(p->st_VirtualTLBFlushEntiresSec); + } + + return true; +} + +// Storage DEVICE + +struct hypervisor_storage_device { + bool collected_metadata; + bool charts_created; + + RRDSET *st_operations; + DEFINE_RD(ReadOperationsSec); + DEFINE_RD(WriteOperationsSec); + + RRDSET *st_bytes; + DEFINE_RD(ReadBytesSec); + DEFINE_RD(WriteBytesSec); + + RRDSET *st_errors; + DEFINE_RD(ErrorCount); + + COUNTER_DATA ReadOperationsSec; + COUNTER_DATA WriteOperationsSec; + + COUNTER_DATA ReadBytesSec; + COUNTER_DATA WriteBytesSec; + COUNTER_DATA ErrorCount; +}; + + +// Initialize the keys for the root partition metrics +void initialize_hyperv_storage_device_keys(struct hypervisor_storage_device *p) { + p->ReadOperationsSec.key = "Read Operations/Sec"; + p->WriteOperationsSec.key = "Write Operations/Sec"; + + p->ReadBytesSec.key = "Read Bytes/sec"; + p->WriteBytesSec.key = "Write Bytes/sec"; + p->ErrorCount.key = "Error Count"; +} + +// Callback function for inserting root partition metrics into the dictionary +void dict_hyperv_storage_device_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { + struct hypervisor_storage_device *p = value; + initialize_hyperv_storage_device_keys(p); +} + +static bool do_hyperv_storage_device(PERF_DATA_BLOCK *pDataBlock, int update_every, void *data) +{ + hyperv_perf_item *item = data; + + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, item->registry_name); + if (!pObjectType) + return false; + + + PERF_INSTANCE_DEFINITION *pi = NULL; + for (LONG i = 0; i < pObjectType->NumInstances; i++) { + pi = perflibForEachInstance(pDataBlock, pObjectType, pi); + if (!pi) + break; + + get_and_sanitize_instance_value(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer)); + + if(strcasecmp(windows_shared_buffer, "_Total") == 0) + continue; + + struct hypervisor_storage_device *p = dictionary_set(item->instance, windows_shared_buffer, NULL, sizeof(*p)); + + if (!p->collected_metadata) { + p->collected_metadata = true; + } + + // Fetch counters + GET_INSTANCE_COUNTER(ReadOperationsSec); + GET_INSTANCE_COUNTER(WriteOperationsSec); + + GET_INSTANCE_COUNTER(ReadBytesSec); + GET_INSTANCE_COUNTER(WriteBytesSec); + GET_INSTANCE_COUNTER(ErrorCount); + + if (!p->charts_created) { + p->charts_created = true; + if (!p->st_operations) { + p->st_operations = rrdset_create_localhost( + "vm_storage_device_operations", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV".vm_storage_device_operations", + "VM storage device IOPS", + "operations/s", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_VM_STORAGE_DEVICE_OPERATIONS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_ReadOperationsSec = rrddim_add(p->st_operations, "read", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_WriteOperationsSec = rrddim_add(p->st_operations, "write", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(p->st_operations->rrdlabels, "vm_storage_device", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + if (!p->st_bytes) { + p->st_bytes = rrdset_create_localhost( + "vm_storage_device_bytes", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV".vm_storage_device_bytes", + "VM storage device IO", + "bytes/s", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_VM_STORAGE_DEVICE_BYTES, + update_every, + RRDSET_TYPE_AREA); + + p->rd_ReadBytesSec = rrddim_add(p->st_bytes, "read", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_WriteBytesSec = rrddim_add(p->st_bytes, "write", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(p->st_bytes->rrdlabels, "vm_storage_device", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + if (!p->st_errors) { + p->st_errors = rrdset_create_localhost( + "vm_storage_device_errors", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV".vm_storage_device_errors", + "VM storage device errors", + "errors/s", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_VM_STORAGE_DEVICE_ERRORS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_ErrorCount = rrddim_add(p->st_errors, "errors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(p->st_errors->rrdlabels, "vm_storage_device", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + } + + SETP_DIM_VALUE(st_operations,ReadOperationsSec); + SETP_DIM_VALUE(st_operations,WriteOperationsSec); + + SETP_DIM_VALUE(st_bytes,ReadBytesSec); + SETP_DIM_VALUE(st_bytes,WriteBytesSec); + + SETP_DIM_VALUE(st_errors,ErrorCount); + + // Mark the charts as done + rrdset_done(p->st_operations); + rrdset_done(p->st_bytes); + rrdset_done(p->st_errors); + } + + return true; +} + +struct hypervisor_switch { + bool collected_metadata; + bool charts_created; + + RRDSET *st_bytes; + DEFINE_RD(BytesSentSec); + DEFINE_RD(BytesReceivedSec); + + RRDSET *st_packets; + DEFINE_RD(PacketsSentSec); + DEFINE_RD(PacketsReceivedSec); + + RRDSET *st_directed_packets; + DEFINE_RD(DirectedPacketsSentSec); + DEFINE_RD(DirectedPacketsReceivedSec); + + RRDSET *st_broadcast_packets; + DEFINE_RD(BroadcastPacketsSentSec); + DEFINE_RD(BroadcastPacketsReceivedSec); + + RRDSET *st_multicast_packets; + DEFINE_RD(MulticastPacketsSentSec); + DEFINE_RD(MulticastPacketsReceivedSec); + + RRDSET *st_dropped_packets; + DEFINE_RD(DroppedPacketsOutgoingSec); + DEFINE_RD(DroppedPacketsIncomingSec); + + RRDSET *st_ext_dropped_packets; + DEFINE_RD(ExtensionsDroppedPacketsOutgoingSec); + DEFINE_RD(ExtensionsDroppedPacketsIncomingSec); + + RRDSET *st_flooded; + DEFINE_RD(PacketsFlooded); + + RRDSET *st_learned_mac; + DEFINE_RD(LearnedMacAddresses); + + RRDSET *st_purged_mac; + DEFINE_RD(PurgedMacAddresses); + + COUNTER_DATA BytesSentSec; + COUNTER_DATA BytesReceivedSec; + + COUNTER_DATA PacketsSentSec; + COUNTER_DATA PacketsReceivedSec; + + COUNTER_DATA DirectedPacketsSentSec; + COUNTER_DATA DirectedPacketsReceivedSec; + + COUNTER_DATA BroadcastPacketsSentSec; + COUNTER_DATA BroadcastPacketsReceivedSec; + + COUNTER_DATA MulticastPacketsSentSec; + COUNTER_DATA MulticastPacketsReceivedSec; + + COUNTER_DATA DroppedPacketsOutgoingSec; + COUNTER_DATA DroppedPacketsIncomingSec; + + COUNTER_DATA ExtensionsDroppedPacketsOutgoingSec; + COUNTER_DATA ExtensionsDroppedPacketsIncomingSec; + + COUNTER_DATA PacketsFlooded; + + COUNTER_DATA LearnedMacAddresses; + + COUNTER_DATA PurgedMacAddresses; +}; + +// Initialize the keys for the root partition metrics +void initialize_hyperv_switch_keys(struct hypervisor_switch *p) +{ + p->BytesSentSec.key = "Bytes Sent/sec"; + p->BytesReceivedSec.key = "Bytes Received/sec"; + p->PacketsSentSec.key = "Packets Sent/sec"; + p->PacketsReceivedSec.key = "Packets Received/sec"; + + p->DirectedPacketsSentSec.key = "Directed Packets Sent/sec"; + p->DirectedPacketsReceivedSec.key = "Directed Packets Received/sec"; + p->BroadcastPacketsSentSec.key = "Broadcast Packets Sent/sec"; + p->BroadcastPacketsReceivedSec.key = "Broadcast Packets Received/sec"; + p->MulticastPacketsSentSec.key = "Multicast Packets Sent/sec"; + p->MulticastPacketsReceivedSec.key = "Multicast Packets Received/sec"; + p->DroppedPacketsOutgoingSec.key = "Dropped Packets Outgoing/sec"; + p->DroppedPacketsIncomingSec.key = "Dropped Packets Incoming/sec"; + p->ExtensionsDroppedPacketsOutgoingSec.key = "Extensions Dropped Packets Outgoing/sec"; + p->ExtensionsDroppedPacketsIncomingSec.key = "Extensions Dropped Packets Incoming/sec"; + p->PacketsFlooded.key = "Packets Flooded"; + p->LearnedMacAddresses.key = "Learned Mac Addresses"; + p->PurgedMacAddresses.key = "Purged Mac Addresses"; +} + +void dict_hyperv_switch_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) +{ + struct hypervisor_switch *p = value; + initialize_hyperv_switch_keys(p); +} + +static bool do_hyperv_switch(PERF_DATA_BLOCK *pDataBlock, int update_every, void *data) +{ + hyperv_perf_item *item = data; + + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, item->registry_name); + if (!pObjectType) + return false; + + PERF_INSTANCE_DEFINITION *pi = NULL; + for (LONG i = 0; i < pObjectType->NumInstances; i++) { + static bool charts_created = false; + pi = perflibForEachInstance(pDataBlock, pObjectType, pi); + if (!pi) + break; + + get_and_sanitize_instance_value( + pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer)); + + if(strcasecmp(windows_shared_buffer, "_Total") == 0) + continue; + + struct hypervisor_switch *p = dictionary_set(item->instance, windows_shared_buffer, NULL, sizeof(*p)); + + if (!p->collected_metadata) { + p->collected_metadata = true; + } + + GET_INSTANCE_COUNTER(BytesReceivedSec); + GET_INSTANCE_COUNTER(BytesSentSec); + + GET_INSTANCE_COUNTER(PacketsReceivedSec); + GET_INSTANCE_COUNTER(PacketsSentSec); + + GET_INSTANCE_COUNTER(DirectedPacketsSentSec); + GET_INSTANCE_COUNTER(DirectedPacketsReceivedSec); + + GET_INSTANCE_COUNTER(BroadcastPacketsSentSec); + GET_INSTANCE_COUNTER(BroadcastPacketsReceivedSec); + + GET_INSTANCE_COUNTER(MulticastPacketsSentSec); + GET_INSTANCE_COUNTER(MulticastPacketsReceivedSec); + + GET_INSTANCE_COUNTER(DroppedPacketsOutgoingSec); + GET_INSTANCE_COUNTER(DroppedPacketsIncomingSec); + + GET_INSTANCE_COUNTER(ExtensionsDroppedPacketsOutgoingSec); + GET_INSTANCE_COUNTER(ExtensionsDroppedPacketsIncomingSec); + + GET_INSTANCE_COUNTER(PacketsFlooded); + + GET_INSTANCE_COUNTER(LearnedMacAddresses); + + GET_INSTANCE_COUNTER(PurgedMacAddresses); + + if (!p->charts_created) { + p->charts_created = true; + + p->st_bytes = rrdset_create_localhost( + "vswitch_traffic", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".vswitch_traffic", + "Virtual switch traffic", + "kilobits/s", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_VSWITCH_TRAFFIC, + update_every, + RRDSET_TYPE_AREA); + + p->rd_BytesReceivedSec = rrddim_add(p->st_bytes, "received", NULL, 8, 1000, RRD_ALGORITHM_INCREMENTAL); + p->rd_BytesSentSec = rrddim_add(p->st_bytes, "sent", NULL, -8, 1000, RRD_ALGORITHM_INCREMENTAL); + rrdlabels_add(p->st_bytes->rrdlabels, "vswitch", windows_shared_buffer, RRDLABEL_SRC_AUTO); + + p->st_packets = rrdset_create_localhost( + "vswitch_packets", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".vswitch_packets", + "Virtual switch packets", + "packets/s", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_VSWITCH_PACKETS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_PacketsReceivedSec = rrddim_add(p->st_packets, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_PacketsSentSec = rrddim_add(p->st_packets, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrdlabels_add(p->st_packets->rrdlabels, "vswitch", windows_shared_buffer, RRDLABEL_SRC_AUTO); + + p->st_directed_packets = rrdset_create_localhost( + "vswitch_directed_packets", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".vswitch_directed_packets", + "Virtual switch directed packets", + "packets/s", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_VSWITCH_DIRECTED_PACKETS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_DirectedPacketsReceivedSec = + rrddim_add(p->st_directed_packets, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_DirectedPacketsSentSec = + rrddim_add(p->st_directed_packets, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrdlabels_add(p->st_directed_packets->rrdlabels, "vswitch", windows_shared_buffer, RRDLABEL_SRC_AUTO); + + p->st_broadcast_packets = rrdset_create_localhost( + "vswitch_broadcast_packets", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".vswitch_broadcast_packets", + "Virtual switch broadcast packets", + "packets/s", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_VSWITCH_BROADCAST_PACKETS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_BroadcastPacketsReceivedSec = + rrddim_add(p->st_broadcast_packets, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_BroadcastPacketsSentSec = + rrddim_add(p->st_broadcast_packets, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrdlabels_add(p->st_broadcast_packets->rrdlabels, "vswitch", windows_shared_buffer, RRDLABEL_SRC_AUTO); + + p->st_multicast_packets = rrdset_create_localhost( + "vswitch_multicast_packets", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".vswitch_multicast_packets", + "Virtual switch multicast packets", + "packets/s", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_VSWITCH_MULTICAST_PACKETS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_MulticastPacketsReceivedSec = + rrddim_add(p->st_multicast_packets, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_MulticastPacketsSentSec = + rrddim_add(p->st_multicast_packets, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrdlabels_add(p->st_multicast_packets->rrdlabels, "vswitch", windows_shared_buffer, RRDLABEL_SRC_AUTO); + + p->st_dropped_packets = rrdset_create_localhost( + "vswitch_dropped_packets", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".vswitch_dropped_packets", + "Virtual switch dropped packets", + "drops/s", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_VSWITCH_DROPPED_PACKETS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_DroppedPacketsIncomingSec = + rrddim_add(p->st_dropped_packets, "incoming", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_DroppedPacketsOutgoingSec = + rrddim_add(p->st_dropped_packets, "outgoing", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrdlabels_add(p->st_dropped_packets->rrdlabels, "vswitch", windows_shared_buffer, RRDLABEL_SRC_AUTO); + + p->st_ext_dropped_packets = rrdset_create_localhost( + "vswitch_extensions_dropped_packets", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".vswitch_extensions_dropped_packets", + "Virtual switch extensions dropped packets", + "drops/s", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_VSWITCH_EXTENSIONS_DROPPED_PACKETS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_ExtensionsDroppedPacketsIncomingSec = + rrddim_add(p->st_ext_dropped_packets, "incoming", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_ExtensionsDroppedPacketsOutgoingSec = + rrddim_add(p->st_ext_dropped_packets, "outgoing", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrdlabels_add(p->st_ext_dropped_packets->rrdlabels, "vswitch", windows_shared_buffer, RRDLABEL_SRC_AUTO); + + p->st_flooded = rrdset_create_localhost( + "vswitch_packets_flooded", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".vswitch_packets_flooded", + "Virtual switch flooded packets", + "packets/s", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_VSWITCH_PACKETS_FLOODED, + update_every, + RRDSET_TYPE_LINE); + + p->rd_PacketsFlooded = rrddim_add(p->st_flooded, "flooded", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrdlabels_add(p->st_flooded->rrdlabels, "vswitch", windows_shared_buffer, RRDLABEL_SRC_AUTO); + + p->st_learned_mac = rrdset_create_localhost( + "vswitch_learned_mac_addresses", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".vswitch_learned_mac_addresses", + "Virtual switch learned MAC addresses", + "mac addresses/s", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_VSWITCH_LEARNED_MAC_ADDRESSES, + update_every, + RRDSET_TYPE_LINE); + + p->rd_LearnedMacAddresses = rrddim_add(p->st_learned_mac, "learned", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrdlabels_add(p->st_learned_mac->rrdlabels, "vswitch", windows_shared_buffer, RRDLABEL_SRC_AUTO); + + p->st_purged_mac = rrdset_create_localhost( + "vswitch_purged_mac_addresses", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".vswitch_purged_mac_addresses", + "Virtual switch purged MAC addresses", + "mac addresses/s", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_VSWITCH_PURGED_MAC_ADDRESSES, + update_every, + RRDSET_TYPE_LINE); + + p->rd_PurgedMacAddresses = rrddim_add(p->st_purged_mac, "purged", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrdlabels_add(p->st_purged_mac->rrdlabels, "vswitch", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + SETP_DIM_VALUE(st_packets, PacketsReceivedSec); + SETP_DIM_VALUE(st_packets, PacketsSentSec); + + SETP_DIM_VALUE(st_bytes, BytesReceivedSec); + SETP_DIM_VALUE(st_bytes, BytesSentSec); + + SETP_DIM_VALUE(st_directed_packets, DirectedPacketsSentSec); + SETP_DIM_VALUE(st_directed_packets, DirectedPacketsReceivedSec); + + SETP_DIM_VALUE(st_broadcast_packets, BroadcastPacketsSentSec); + SETP_DIM_VALUE(st_broadcast_packets, BroadcastPacketsReceivedSec); + + SETP_DIM_VALUE(st_multicast_packets, MulticastPacketsSentSec); + SETP_DIM_VALUE(st_multicast_packets, MulticastPacketsReceivedSec); + + SETP_DIM_VALUE(st_dropped_packets, DroppedPacketsOutgoingSec); + SETP_DIM_VALUE(st_dropped_packets, DroppedPacketsIncomingSec); + + SETP_DIM_VALUE(st_ext_dropped_packets, ExtensionsDroppedPacketsOutgoingSec); + SETP_DIM_VALUE(st_ext_dropped_packets, ExtensionsDroppedPacketsIncomingSec); + + SETP_DIM_VALUE(st_flooded, PacketsFlooded); + SETP_DIM_VALUE(st_learned_mac, LearnedMacAddresses); + SETP_DIM_VALUE(st_purged_mac, PurgedMacAddresses); + + // Mark the charts as done + rrdset_done(p->st_packets); + rrdset_done(p->st_bytes); + + rrdset_done(p->st_directed_packets); + rrdset_done(p->st_broadcast_packets); + rrdset_done(p->st_multicast_packets); + rrdset_done(p->st_dropped_packets); + rrdset_done(p->st_ext_dropped_packets); + rrdset_done(p->st_flooded); + rrdset_done(p->st_learned_mac); + rrdset_done(p->st_purged_mac); + + } + return true; +} + +struct hypervisor_network_adapter { + bool collected_metadata; + bool charts_created; + + RRDSET *st_dropped_packets; + DEFINE_RD(DroppedPacketsOutgoingSec); + DEFINE_RD(DroppedPacketsIncomingSec); + + RRDSET *st_send_receive_packets; + DEFINE_RD(PacketsSentSec); + DEFINE_RD(PacketsReceivedSec); + + RRDSET *st_send_receive_bytes; + DEFINE_RD(BytesSentSec); + DEFINE_RD(BytesReceivedSec); + + RRDSET *st_IPsecoffloadBytes; + DEFINE_RD(IPsecoffloadBytesReceivedSec); + DEFINE_RD(IPsecoffloadBytesSentSec); + + RRDSET *st_DirectedPackets; + DEFINE_RD(DirectedPacketsSentSec); + DEFINE_RD(DirectedPacketsReceivedSec); + + RRDSET *st_BroadcastPackets; + DEFINE_RD(BroadcastPacketsSentSec); + DEFINE_RD(BroadcastPacketsReceivedSec); + + RRDSET *st_MulticastPackets; + DEFINE_RD(MulticastPacketsSentSec); + DEFINE_RD(MulticastPacketsReceivedSec); + + COUNTER_DATA DroppedPacketsOutgoingSec; + COUNTER_DATA DroppedPacketsIncomingSec; + + COUNTER_DATA PacketsSentSec; + COUNTER_DATA PacketsReceivedSec; + + COUNTER_DATA BytesSentSec; + COUNTER_DATA BytesReceivedSec; + + COUNTER_DATA IPsecoffloadBytesReceivedSec; + COUNTER_DATA IPsecoffloadBytesSentSec; + + COUNTER_DATA DirectedPacketsSentSec; + COUNTER_DATA DirectedPacketsReceivedSec; + + COUNTER_DATA BroadcastPacketsSentSec; + COUNTER_DATA BroadcastPacketsReceivedSec; + + COUNTER_DATA MulticastPacketsSentSec; + COUNTER_DATA MulticastPacketsReceivedSec; +}; + +// Initialize the keys for the root partition metrics +void initialize_hyperv_network_adapter_keys(struct hypervisor_network_adapter *p) +{ + p->DroppedPacketsOutgoingSec.key = "Dropped Packets Outgoing/sec"; + p->DroppedPacketsIncomingSec.key = "Dropped Packets Incoming/sec"; + + p->PacketsSentSec.key = "Packets Sent/sec"; + p->PacketsReceivedSec.key = "Packets Received/sec"; + + p->BytesSentSec.key = "Bytes Sent/sec"; + p->BytesReceivedSec.key = "Bytes Received/sec"; + + p->IPsecoffloadBytesReceivedSec.key = "IPsec offload Bytes Receive/sec"; + p->IPsecoffloadBytesSentSec.key = "IPsec offload Bytes Sent/sec"; + p->DirectedPacketsSentSec.key = "Directed Packets Sent/sec"; + p->DirectedPacketsReceivedSec.key = "Directed Packets Received/sec"; + p->BroadcastPacketsSentSec.key = "Broadcast Packets Sent/sec"; + p->BroadcastPacketsReceivedSec.key = "Broadcast Packets Received/sec"; + p->MulticastPacketsSentSec.key = "Multicast Packets Sent/sec"; + p->MulticastPacketsReceivedSec.key = "Multicast Packets Received/sec"; +} + +void dict_hyperv_network_adapter_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) +{ + struct hypervisor_network_adapter *p = value; + initialize_hyperv_network_adapter_keys(p); +} + +static bool do_hyperv_network_adapter(PERF_DATA_BLOCK *pDataBlock, int update_every, void *data) +{ + hyperv_perf_item *item = data; + + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, item->registry_name); + if (!pObjectType) + return false; + + PERF_INSTANCE_DEFINITION *pi = NULL; + for (LONG i = 0; i < pObjectType->NumInstances; i++) { + pi = perflibForEachInstance(pDataBlock, pObjectType, pi); + if (!pi) + break; + + get_and_sanitize_instance_value(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer)); + + if(strcasecmp(windows_shared_buffer, "_Total") == 0) + continue; + + struct hypervisor_network_adapter *p = dictionary_set(item->instance, windows_shared_buffer, NULL, sizeof(*p)); + + if (!p->collected_metadata) { + p->collected_metadata = true; + } + + GET_INSTANCE_COUNTER(DroppedPacketsIncomingSec); + GET_INSTANCE_COUNTER(DroppedPacketsOutgoingSec); + + GET_INSTANCE_COUNTER(PacketsReceivedSec); + GET_INSTANCE_COUNTER(PacketsSentSec); + + GET_INSTANCE_COUNTER(BytesReceivedSec); + GET_INSTANCE_COUNTER(BytesSentSec); + + GET_INSTANCE_COUNTER(IPsecoffloadBytesReceivedSec); + GET_INSTANCE_COUNTER(IPsecoffloadBytesSentSec); + + GET_INSTANCE_COUNTER(DirectedPacketsSentSec); + GET_INSTANCE_COUNTER(DirectedPacketsReceivedSec); + + GET_INSTANCE_COUNTER(BroadcastPacketsSentSec); + GET_INSTANCE_COUNTER(BroadcastPacketsReceivedSec); + + GET_INSTANCE_COUNTER(MulticastPacketsSentSec); + GET_INSTANCE_COUNTER(MulticastPacketsReceivedSec); + + if (!p->charts_created) { + p->charts_created = true; + p->st_dropped_packets = rrdset_create_localhost( + "vm_net_interface_packets_dropped", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV".vm_net_interface_packets_dropped", + "VM interface packets dropped", + "drops/s", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_VM_NET_INTERFACE_PACKETS_DROPPED, + update_every, + RRDSET_TYPE_LINE); + + p->rd_DroppedPacketsIncomingSec = rrddim_add(p->st_dropped_packets, "incoming", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_DroppedPacketsOutgoingSec = rrddim_add(p->st_dropped_packets, "outgoing", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(p->st_dropped_packets->rrdlabels, "vm_net_interface", windows_shared_buffer, RRDLABEL_SRC_AUTO); + + p->st_send_receive_packets = rrdset_create_localhost( + "vm_net_interface_packets", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".vm_net_interface_packets", + "VM interface packets", + "packets/s", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_VM_NET_INTERFACE_PACKETS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_PacketsReceivedSec = rrddim_add(p->st_send_receive_packets, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_PacketsSentSec = rrddim_add(p->st_send_receive_packets, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(p->st_send_receive_packets->rrdlabels, "vm_net_interface", windows_shared_buffer, RRDLABEL_SRC_AUTO); + + p->st_send_receive_bytes = rrdset_create_localhost( + "vm_net_interface_traffic", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".vm_net_interface_traffic", + "VM interface traffic", + "kilobits/s", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_VM_NET_INTERFACE_TRAFFIC, + update_every, + RRDSET_TYPE_AREA); + + p->rd_BytesReceivedSec = rrddim_add(p->st_send_receive_bytes, "received", NULL, 8, 1000, RRD_ALGORITHM_INCREMENTAL); + p->rd_BytesSentSec = rrddim_add(p->st_send_receive_bytes, "sent", NULL, -8, 1000, RRD_ALGORITHM_INCREMENTAL); + rrdlabels_add(p->st_send_receive_bytes->rrdlabels, "vm_net_interface", windows_shared_buffer, RRDLABEL_SRC_AUTO); + + p->st_IPsecoffloadBytes = rrdset_create_localhost( + "vm_net_interface_ipsec_traffic", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".vm_net_interface_ipsec_traffic", + "VM interface traffic", + "kilobits/s", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_VM_NET_INTERFACE_IPSEC_TRAFFIC, + update_every, + RRDSET_TYPE_AREA); + + p->rd_IPsecoffloadBytesReceivedSec = + rrddim_add(p->st_IPsecoffloadBytes, "received", NULL, 8, 1000, RRD_ALGORITHM_INCREMENTAL); + p->rd_IPsecoffloadBytesSentSec = + rrddim_add(p->st_IPsecoffloadBytes, "sent", NULL, -8, 1000, RRD_ALGORITHM_INCREMENTAL); + rrdlabels_add( + p->st_IPsecoffloadBytes->rrdlabels, "vm_net_interface", windows_shared_buffer, RRDLABEL_SRC_AUTO); + + p->st_DirectedPackets = rrdset_create_localhost( + "vm_net_interface_directed_packets", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".vm_net_interface_directed_packets", + "VM interface traffic", + "packets/s", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_VM_NET_INTERFACE_DIRECTED_PACKETS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_DirectedPacketsReceivedSec = + rrddim_add(p->st_DirectedPackets, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_DirectedPacketsSentSec = + rrddim_add(p->st_DirectedPackets, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrdlabels_add( + p->st_DirectedPackets->rrdlabels, "vm_net_interface", windows_shared_buffer, RRDLABEL_SRC_AUTO); + + p->st_BroadcastPackets = rrdset_create_localhost( + "vm_net_interface_broadcast_packets", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".vm_net_interface_broadcast_packets", + "VM interface broadcast", + "packets/s", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_VM_NET_INTERFACE_PACKETS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_BroadcastPacketsReceivedSec = + rrddim_add(p->st_BroadcastPackets, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_BroadcastPacketsSentSec = + rrddim_add(p->st_BroadcastPackets, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrdlabels_add( + p->st_BroadcastPackets->rrdlabels, "vm_net_interface", windows_shared_buffer, RRDLABEL_SRC_AUTO); + + p->st_MulticastPackets = rrdset_create_localhost( + "vm_net_interface_multicast_packets", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".vm_net_interface_multicast_packets", + "VM interface multicast", + "packets/s", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_VM_NET_INTERFACE_MULTICAST_PACKETS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_MulticastPacketsReceivedSec = + rrddim_add(p->st_MulticastPackets, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_MulticastPacketsSentSec = + rrddim_add(p->st_MulticastPackets, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrdlabels_add( + p->st_MulticastPackets->rrdlabels, "vm_net_interface", windows_shared_buffer, RRDLABEL_SRC_AUTO); + + + } + + SETP_DIM_VALUE(st_dropped_packets, DroppedPacketsIncomingSec); + SETP_DIM_VALUE(st_dropped_packets, DroppedPacketsOutgoingSec); + + SETP_DIM_VALUE(st_send_receive_packets, PacketsReceivedSec); + SETP_DIM_VALUE(st_send_receive_packets, PacketsSentSec); + + SETP_DIM_VALUE(st_send_receive_bytes, BytesReceivedSec); + SETP_DIM_VALUE(st_send_receive_bytes, BytesSentSec); + + SETP_DIM_VALUE(st_IPsecoffloadBytes, IPsecoffloadBytesReceivedSec); + SETP_DIM_VALUE(st_IPsecoffloadBytes, IPsecoffloadBytesSentSec); + + SETP_DIM_VALUE(st_DirectedPackets, DirectedPacketsSentSec); + SETP_DIM_VALUE(st_DirectedPackets, DirectedPacketsReceivedSec); + + SETP_DIM_VALUE(st_BroadcastPackets, BroadcastPacketsSentSec); + SETP_DIM_VALUE(st_BroadcastPackets, BroadcastPacketsReceivedSec); + + SETP_DIM_VALUE(st_MulticastPackets,MulticastPacketsSentSec); + SETP_DIM_VALUE(st_MulticastPackets,MulticastPacketsReceivedSec); + + rrdset_done(p->st_IPsecoffloadBytes); + rrdset_done(p->st_DirectedPackets); + rrdset_done(p->st_BroadcastPackets); + rrdset_done(p->st_MulticastPackets); + rrdset_done(p->st_send_receive_bytes); + rrdset_done(p->st_send_receive_packets); + rrdset_done(p->st_dropped_packets); + } + return true; +} + + +// Hypervisor Virtual Processor +struct hypervisor_processor { + bool collected_metadata; + bool charts_created; + + RRDSET *st_HypervisorProcessor; + + DEFINE_RD(GuestRunTime); + DEFINE_RD(HypervisorRunTime); + DEFINE_RD(RemoteRunTime); + + RRDSET *st_HypervisorProcessorTotal; + DEFINE_RD(TotalRunTime); + + COUNTER_DATA GuestRunTime; + COUNTER_DATA HypervisorRunTime; + COUNTER_DATA RemoteRunTime; + COUNTER_DATA TotalRunTime; + collected_number GuestRunTime_total; + collected_number HypervisorRunTime_total; + collected_number RemoteRunTime_total; + collected_number TotalRunTime_total; +}; + + +void initialize_hyperv_processor_keys(struct hypervisor_processor *p) +{ + p->GuestRunTime.key = "% Guest Run Time"; + p->HypervisorRunTime.key = "% Hypervisor Run Time"; + p->RemoteRunTime.key = "% Remote Run Time"; + p->TotalRunTime.key = "% Total Run Time"; + p->GuestRunTime_total = 0; + p->HypervisorRunTime_total = 0; + p->RemoteRunTime_total = 0; + p->TotalRunTime_total = 0; +} + +void dict_hyperv_processor_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) +{ + struct hypervisor_processor *p = value; + initialize_hyperv_processor_keys(p); +} + +static bool do_hyperv_processor(PERF_DATA_BLOCK *pDataBlock, int update_every, void *data) +{ + hyperv_perf_item *item = data; + + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, item->registry_name); + if (!pObjectType) + return false; + + PERF_INSTANCE_DEFINITION *pi = NULL; + for (LONG i = 0; i < pObjectType->NumInstances; i++) { + pi = perflibForEachInstance(pDataBlock, pObjectType, pi); + if (!pi) + break; + + get_and_sanitize_instance_value(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer)); + + if (strcasecmp(windows_shared_buffer, "_Total") == 0) + continue; + + char *vm = strchr(windows_shared_buffer, ':'); + if (vm) + *vm = '\0'; + + struct hypervisor_processor *p = dictionary_set(item->instance, windows_shared_buffer, NULL, sizeof(*p)); + + if (!p->collected_metadata) { + p->collected_metadata = true; + } + + GET_INSTANCE_COUNTER(GuestRunTime); + GET_INSTANCE_COUNTER(HypervisorRunTime); + GET_INSTANCE_COUNTER(RemoteRunTime); + GET_INSTANCE_COUNTER(TotalRunTime); + + if (!p->charts_created) { + p->charts_created = true; + p->st_HypervisorProcessorTotal = rrdset_create_localhost( + "vm_cpu_usage", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".vm_cpu_usage", + "VM CPU usage", + "percentage", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_VM_CPU_USAGE, + update_every, + RRDSET_TYPE_STACKED); + + p->rd_TotalRunTime = + rrddim_add(p->st_HypervisorProcessorTotal, "usage", NULL, 1, 1000000, RRD_ALGORITHM_INCREMENTAL); + rrdlabels_add(p->st_HypervisorProcessorTotal->rrdlabels, "vm_name", windows_shared_buffer, RRDLABEL_SRC_AUTO); + + p->st_HypervisorProcessor = rrdset_create_localhost( + "vm_cpu_usage_by_run_context", + windows_shared_buffer, + NULL, + HYPERV, + HYPERV ".vm_cpu_usage_by_run_context", + "VM CPU usage by run context", + "percentage", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_WINDOWS_HYPERV_VM_CPU_USAGE_BY_RUN_CONTEXT, + update_every, + RRDSET_TYPE_STACKED); + + p->rd_GuestRunTime = + rrddim_add(p->st_HypervisorProcessor, "guest", NULL, 1, 1000000, RRD_ALGORITHM_INCREMENTAL); + p->rd_HypervisorRunTime = + rrddim_add(p->st_HypervisorProcessor, "hypervisor", NULL, 1, 1000000, RRD_ALGORITHM_INCREMENTAL); + p->rd_RemoteRunTime = + rrddim_add(p->st_HypervisorProcessor, "remote", NULL, 1, 1000000, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(p->st_HypervisorProcessor->rrdlabels, "vm_name", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + p->GuestRunTime_total += (collected_number)p->GuestRunTime.current.Data; + p->HypervisorRunTime_total += (collected_number)p->HypervisorRunTime.current.Data; + p->RemoteRunTime_total += (collected_number)p->RemoteRunTime.current.Data; + p->TotalRunTime_total += (collected_number)p->TotalRunTime.current.Data; + } + + { + struct hypervisor_processor *p; + dfe_start_read(item->instance, p) { + rrddim_set_by_pointer(p->st_HypervisorProcessor, p->rd_HypervisorRunTime, (collected_number) p->HypervisorRunTime_total); + rrddim_set_by_pointer(p->st_HypervisorProcessor, p->rd_GuestRunTime, (collected_number) p->GuestRunTime_total); + rrddim_set_by_pointer(p->st_HypervisorProcessor, p->rd_RemoteRunTime, (collected_number) p->RemoteRunTime_total); + rrdset_done(p->st_HypervisorProcessor); + + rrddim_set_by_pointer(p->st_HypervisorProcessorTotal, p->rd_TotalRunTime, (collected_number) p->TotalRunTime_total); + rrdset_done(p->st_HypervisorProcessorTotal); + + p->GuestRunTime_total = 0; + p->HypervisorRunTime_total = 0; + p->RemoteRunTime_total = 0; + p->TotalRunTime_total = 0; + } + dfe_done(p); + } + + return true; +} + +hyperv_perf_item hyperv_perf_list[] = { + {.registry_name = "Hyper-V Dynamic Memory VM", + .function_collect = do_hyperv_memory, + .dict_insert_cb = dict_hyperv_memory_insert_cb, + .dict_size = sizeof(struct hypervisor_memory)}, + + {.registry_name = "Hyper-V VM Vid Partition", + .function_collect = do_hyperv_vid_partition, + .dict_insert_cb = dict_hyperv_partition_insert_cb, + .dict_size = sizeof(struct hypervisor_partition)}, + + { + .registry_name = "Hyper-V Virtual Machine Health Summary", + .function_collect = do_hyperv_health_summary, + }, + + { + .registry_name = "Hyper-V Hypervisor Root Partition", + .function_collect = do_hyperv_root_partition, + .dict_insert_cb = dict_hyperv_root_partition_insert_cb, + .dict_size = sizeof(struct hypervisor_root_partition), + }, + + {.registry_name = "Hyper-V Virtual Storage Device", + .function_collect = do_hyperv_storage_device, + .dict_insert_cb = dict_hyperv_storage_device_insert_cb, + .dict_size = sizeof(struct hypervisor_storage_device)}, + + {.registry_name = "Hyper-V Virtual Switch", + .function_collect = do_hyperv_switch, + .dict_insert_cb = dict_hyperv_switch_insert_cb, + .dict_size = sizeof(struct hypervisor_switch)}, + + {.registry_name = "Hyper-V Virtual Network Adapter", + .function_collect = do_hyperv_network_adapter, + .dict_insert_cb = dict_hyperv_network_adapter_insert_cb, + .dict_size = sizeof(struct hypervisor_network_adapter)}, + + {.registry_name = "Hyper-V Hypervisor Virtual Processor", + .function_collect = do_hyperv_processor, + .dict_insert_cb = dict_hyperv_processor_insert_cb, + .dict_size = sizeof(struct hypervisor_processor)}, + + {.registry_name = NULL, .function_collect = NULL}}; + +int do_PerflibHyperV(int update_every, usec_t dt __maybe_unused) { + static bool initialized = false; + + if (unlikely(!initialized)) { + for (int i = 0; hyperv_perf_list[i].registry_name != NULL; i++) { + hyperv_perf_item *item = &hyperv_perf_list[i]; + if (item->dict_insert_cb) { + item->instance = dictionary_create_advanced(DICT_PERF_OPTION, NULL, item->dict_size); + dictionary_register_insert_callback(item->instance, item->dict_insert_cb, NULL); + } + } + initialized = true; + } + + for (int i = 0; hyperv_perf_list[i].registry_name != NULL; i++) { + // Find the registry ID using the registry name + DWORD id = RegistryFindIDByName(hyperv_perf_list[i].registry_name); + if (id == PERFLIB_REGISTRY_NAME_NOT_FOUND) + continue; + + // Get the performance data using the registry ID + PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id); + if (!pDataBlock) + continue; + + hyperv_perf_list[i].function_collect(pDataBlock, update_every, &hyperv_perf_list[i]); + } + return 0; +} diff --git a/src/collectors/windows.plugin/perflib-memory.c b/src/collectors/windows.plugin/perflib-memory.c index c876fc68a..e26729cda 100644 --- a/src/collectors/windows.plugin/perflib-memory.c +++ b/src/collectors/windows.plugin/perflib-memory.c @@ -1,65 +1,219 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "windows_plugin.h" -#include "windows-internals.h" - -#define _COMMON_PLUGIN_NAME "windows.plugin" -#define _COMMON_PLUGIN_MODULE_NAME "PerflibMemory" -#include "../common-contexts/common-contexts.h" - -static void initialize(void) { - ; -} - -static bool do_memory(PERF_DATA_BLOCK *pDataBlock, int update_every) { - PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "Memory"); - if (!pObjectType) - return false; - - static COUNTER_DATA pagesPerSec = { .key = "Pages/sec" }; - static COUNTER_DATA pageFaultsPerSec = { .key = "Page Faults/sec" }; - - if(perflibGetObjectCounter(pDataBlock, pObjectType, &pageFaultsPerSec) && - perflibGetObjectCounter(pDataBlock, pObjectType, &pagesPerSec)) { - ULONGLONG total = pageFaultsPerSec.current.Data; - ULONGLONG major = pagesPerSec.current.Data; - ULONGLONG minor = (total > major) ? total - major : 0; - common_mem_pgfaults(minor, major, update_every); - } - - static COUNTER_DATA availableBytes = { .key = "Available Bytes" }; - static COUNTER_DATA availableKBytes = { .key = "Available KBytes" }; - static COUNTER_DATA availableMBytes = { .key = "Available MBytes" }; - ULONGLONG available_bytes = 0; - - if(perflibGetObjectCounter(pDataBlock, pObjectType, &availableBytes)) - available_bytes = availableBytes.current.Data; - else if(perflibGetObjectCounter(pDataBlock, pObjectType, &availableKBytes)) - available_bytes = availableKBytes.current.Data * 1024; - else if(perflibGetObjectCounter(pDataBlock, pObjectType, &availableMBytes)) - available_bytes = availableMBytes.current.Data * 1024 * 1024; - - common_mem_available(available_bytes, update_every); - - return true; -} - -int do_PerflibMemory(int update_every, usec_t dt __maybe_unused) { - static bool initialized = false; - - if(unlikely(!initialized)) { - initialize(); - initialized = true; - } - - DWORD id = RegistryFindIDByName("Memory"); - if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND) - return -1; - - PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id); - if(!pDataBlock) return -1; - - do_memory(pDataBlock, update_every); - - return 0; -} +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "windows_plugin.h" +#include "windows-internals.h" + +#define _COMMON_PLUGIN_NAME "windows.plugin" +#define _COMMON_PLUGIN_MODULE_NAME "PerflibMemory" +#include "../common-contexts/common-contexts.h" + +struct swap { + RRDSET *operations; + RRDDIM *rd_op_read; + RRDDIM *rd_op_write; + + RRDSET *pages; + RRDDIM *rd_page_read; + RRDDIM *rd_page_write; + + COUNTER_DATA pageReadsTotal; + COUNTER_DATA pageWritesTotal; + COUNTER_DATA pageInputTotal; + COUNTER_DATA pageOutputTotal; +}; + +struct system_pool { + RRDSET *pool; + RRDDIM *rd_paged; + RRDDIM *rd_nonpaged; + + COUNTER_DATA pagedData; + COUNTER_DATA nonPagedData; +}; + +struct swap localSwap = { 0 }; +struct system_pool localPool = { 0 }; + +void initialize_swap_keys(struct swap *p) { + // SWAP Operations + p->pageReadsTotal.key = "Page Reads/sec"; + p->pageWritesTotal.key = "Page Writes/s"; + + // Swap Pages + p->pageInputTotal.key = "Pages Input/sec"; + p->pageOutputTotal.key = "Pages Output/s"; +} + +void initialize_pool_keys(struct system_pool *p) { + p->pagedData.key = "Pool Paged Bytes"; + p->nonPagedData.key = "Pool Nonpaged Bytes"; +} + +static void initialize(void) { + initialize_swap_keys(&localSwap); + initialize_pool_keys(&localPool); +} + +static void do_memory_swap(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, int update_every) +{ + perflibGetObjectCounter(pDataBlock, pObjectType, &localSwap.pageReadsTotal); + perflibGetObjectCounter(pDataBlock, pObjectType, &localSwap.pageWritesTotal); + perflibGetObjectCounter(pDataBlock, pObjectType, &localSwap.pageInputTotal); + perflibGetObjectCounter(pDataBlock, pObjectType, &localSwap.pageOutputTotal); + + if (!localSwap.operations) { + localSwap.operations = rrdset_create_localhost( + "mem" + , "swap_operations", NULL + , "swap" + , "mem.swap_iops" + + , "Swap Operations" + , "operations/s" + , PLUGIN_WINDOWS_NAME + , "PerflibMemory" + , NETDATA_CHART_PRIO_MEM_SWAPIO + , update_every + , RRDSET_TYPE_STACKED + ); + + localSwap.rd_op_read = rrddim_add(localSwap.operations, "read", NULL, + 1, 1, RRD_ALGORITHM_INCREMENTAL); + localSwap.rd_op_write = rrddim_add(localSwap.operations, "write", NULL, + 1, -1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(localSwap.operations, + localSwap.rd_op_read, + (collected_number)localSwap.pageReadsTotal.current.Data); + + rrddim_set_by_pointer(localSwap.operations, + localSwap.rd_op_write, + (collected_number)localSwap.pageWritesTotal.current.Data); + rrdset_done(localSwap.operations); + + if (!localSwap.pages) { + localSwap.pages = rrdset_create_localhost( + "mem" + , "swap_pages", NULL + , "swap" + , "mem.swap_pages_io" + + , "Swap Pages" + , "pages/s" + , PLUGIN_WINDOWS_NAME + , "PerflibMemory" + , NETDATA_CHART_PRIO_MEM_SWAP_PAGES + , update_every + , RRDSET_TYPE_STACKED + ); + + localSwap.rd_page_read = rrddim_add(localSwap.pages, "read", NULL, + 1, 1, RRD_ALGORITHM_INCREMENTAL); + localSwap.rd_page_write = rrddim_add(localSwap.pages, "write", NULL, + 1, -1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(localSwap.pages, + localSwap.rd_page_read, + (collected_number)localSwap.pageInputTotal.current.Data); + + rrddim_set_by_pointer(localSwap.pages, + localSwap.rd_page_write, + (collected_number)localSwap.pageOutputTotal.current.Data); + rrdset_done(localSwap.pages); +} + +static void do_memory_system_pool(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, int update_every) +{ + perflibGetObjectCounter(pDataBlock, pObjectType, &localPool.nonPagedData); + perflibGetObjectCounter(pDataBlock, pObjectType, &localPool.pagedData); + + if (!localPool.pool) { + localPool.pool = rrdset_create_localhost( + "mem" + , "system_pool", NULL + , "mem" + , "mem.system_pool_size" + + , "System Memory Pool" + , "bytes" + , PLUGIN_WINDOWS_NAME + , "PerflibMemory" + , NETDATA_CHART_PRIO_MEM_SYSTEM_POOL + , update_every + , RRDSET_TYPE_STACKED + ); + + localPool.rd_paged = rrddim_add(localPool.pool, "paged", NULL, + 1, 1, RRD_ALGORITHM_ABSOLUTE); + localPool.rd_nonpaged = rrddim_add(localPool.pool, "pool-paged", NULL, + 1, 1, RRD_ALGORITHM_ABSOLUTE); + } + + rrddim_set_by_pointer(localPool.pool, + localPool.rd_paged, + (collected_number)localPool.pagedData.current.Data); + + rrddim_set_by_pointer(localPool.pool, + localPool.rd_nonpaged, + (collected_number)localPool.nonPagedData.current.Data); + rrdset_done(localPool.pool); +} + +static bool do_memory(PERF_DATA_BLOCK *pDataBlock, int update_every) { + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "Memory"); + if (!pObjectType) + return false; + + static COUNTER_DATA pagesPerSec = { .key = "Pages/sec" }; + static COUNTER_DATA pageFaultsPerSec = { .key = "Page Faults/sec" }; + + if(perflibGetObjectCounter(pDataBlock, pObjectType, &pageFaultsPerSec) && + perflibGetObjectCounter(pDataBlock, pObjectType, &pagesPerSec)) { + ULONGLONG total = pageFaultsPerSec.current.Data; + ULONGLONG major = pagesPerSec.current.Data; + ULONGLONG minor = (total > major) ? total - major : 0; + common_mem_pgfaults(minor, major, update_every); + } + + static COUNTER_DATA availableBytes = { .key = "Available Bytes" }; + static COUNTER_DATA availableKBytes = { .key = "Available KBytes" }; + static COUNTER_DATA availableMBytes = { .key = "Available MBytes" }; + ULONGLONG available_bytes = 0; + + if(perflibGetObjectCounter(pDataBlock, pObjectType, &availableBytes)) + available_bytes = availableBytes.current.Data; + else if(perflibGetObjectCounter(pDataBlock, pObjectType, &availableKBytes)) + available_bytes = availableKBytes.current.Data * 1024; + else if(perflibGetObjectCounter(pDataBlock, pObjectType, &availableMBytes)) + available_bytes = availableMBytes.current.Data * 1024 * 1024; + + common_mem_available(available_bytes, update_every); + + do_memory_swap(pDataBlock, pObjectType, update_every); + + do_memory_system_pool(pDataBlock, pObjectType, update_every); + + return true; +} + +int do_PerflibMemory(int update_every, usec_t dt __maybe_unused) { + static bool initialized = false; + + if(unlikely(!initialized)) { + initialize(); + initialized = true; + } + + DWORD id = RegistryFindIDByName("Memory"); + if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND) + return -1; + + PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id); + if(!pDataBlock) return -1; + + do_memory(pDataBlock, update_every); + + return 0; +} diff --git a/src/collectors/windows.plugin/perflib-mssql.c b/src/collectors/windows.plugin/perflib-mssql.c new file mode 100644 index 000000000..f6f5c434d --- /dev/null +++ b/src/collectors/windows.plugin/perflib-mssql.c @@ -0,0 +1,1413 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "windows_plugin.h" +#include "windows-internals.h" + +// https://learn.microsoft.com/en-us/sql/sql-server/install/instance-configuration?view=sql-server-ver16 +#define NETDATA_MAX_INSTANCE_NAME 32 +#define NETDATA_MAX_INSTANCE_OBJECT 128 + +BOOL is_sqlexpress = FALSE; + +enum netdata_mssql_metrics { + NETDATA_MSSQL_GENERAL_STATS, + NETDATA_MSSQL_SQL_ERRORS, + NETDATA_MSSQL_DATABASE, + NETDATA_MSSQL_LOCKS, + NETDATA_MSSQL_MEMORY, + NETDATA_MSSQL_BUFFER_MANAGEMENT, + NETDATA_MSSQL_SQL_STATS, + NETDATA_MSSQL_ACCESS_METHODS, + + NETDATA_MSSQL_METRICS_END +}; + +struct mssql_instance { + char *instanceID; + + char *objectName[NETDATA_MSSQL_METRICS_END]; + + RRDSET *st_user_connections; + RRDDIM *rd_user_connections; + + RRDSET *st_process_blocked; + RRDDIM *rd_process_blocked; + + RRDSET *st_stats_auto_param; + RRDDIM *rd_stats_auto_param; + + RRDSET *st_stats_batch_request; + RRDDIM *rd_stats_batch_request; + + RRDSET *st_stats_safe_auto; + RRDDIM *rd_stats_safe_auto; + + RRDSET *st_stats_compilation; + RRDDIM *rd_stats_compilation; + + RRDSET *st_stats_recompiles; + RRDDIM *rd_stats_recompiles; + + RRDSET *st_buff_cache_hits; + RRDDIM *rd_buff_cache_hits; + + RRDSET *st_buff_cache_page_life_expectancy; + RRDDIM *rd_buff_cache_page_life_expectancy; + + RRDSET *st_buff_checkpoint_pages; + RRDDIM *rd_buff_checkpoint_pages; + + RRDSET *st_buff_page_iops; + RRDDIM *rd_buff_page_reads; + RRDDIM *rd_buff_page_writes; + + RRDSET *st_access_method_page_splits; + RRDDIM *rd_access_method_page_splits; + + RRDSET *st_sql_errors; + RRDDIM *rd_sql_errors; + + RRDSET *st_lockWait; + RRDSET *st_deadLocks; + DICTIONARY *locks_instances; + + DICTIONARY *databases; + + RRDSET *st_conn_memory; + RRDDIM *rd_conn_memory; + + RRDSET *st_ext_benefit_mem; + RRDDIM *rd_ext_benefit_mem; + + RRDSET *st_pending_mem_grant; + RRDDIM *rd_pending_mem_grant; + + RRDSET *st_mem_tot_server; + RRDDIM *rd_mem_tot_server; + + COUNTER_DATA MSSQLAccessMethodPageSplits; + COUNTER_DATA MSSQLBufferCacheHits; + COUNTER_DATA MSSQLBufferCheckpointPages; + COUNTER_DATA MSSQLBufferPageLifeExpectancy; + COUNTER_DATA MSSQLBufferPageReads; + COUNTER_DATA MSSQLBufferPageWrites; + COUNTER_DATA MSSQLBlockedProcesses; + COUNTER_DATA MSSQLUserConnections; + COUNTER_DATA MSSQLConnectionMemoryBytes; + COUNTER_DATA MSSQLExternalBenefitOfMemory; + COUNTER_DATA MSSQLPendingMemoryGrants; + COUNTER_DATA MSSQLSQLErrorsTotal; + COUNTER_DATA MSSQLTotalServerMemory; + COUNTER_DATA MSSQLStatsAutoParameterization; + COUNTER_DATA MSSQLStatsBatchRequests; + COUNTER_DATA MSSQLStatSafeAutoParameterization; + COUNTER_DATA MSSQLCompilations; + COUNTER_DATA MSSQLRecompilations; +}; + +enum lock_instance_idx { + NETDATA_MSSQL_ENUM_MLI_IDX_WAIT, + NETDATA_MSSQL_ENUM_MLI_IDX_DEAD_LOCKS, + + NETDATA_MSSQL_ENUM_MLI_IDX_END +}; + +struct mssql_lock_instance { + struct mssql_instance *parent; + + COUNTER_DATA lockWait; + COUNTER_DATA deadLocks; + + RRDDIM *rd_lockWait; + RRDDIM *rd_deadLocks; + + uint32_t updated; +}; + +enum db_instance_idx { + NETDATA_MSSQL_ENUM_MDI_IDX_FILE_SIZE, + NETDATA_MSSQL_ENUM_MDI_IDX_ACTIVE_TRANSACTIONS, + NETDATA_MSSQL_ENUM_MDI_IDX_BACKUP_RESTORE_OP, + NETDATA_MSSQL_ENUM_MDI_IDX_LOG_FLUSHED, + NETDATA_MSSQL_ENUM_MDI_IDX_LOG_FLUSHES, + NETDATA_MSSQL_ENUM_MDI_IDX_TRANSACTIONS, + NETDATA_MSSQL_ENUM_MDI_IDX_WRITE_TRANSACTIONS, + + NETDATA_MSSQL_ENUM_MDI_IDX_END +}; + +struct mssql_db_instance { + struct mssql_instance *parent; + + RRDSET *st_db_data_file_size; + RRDSET *st_db_active_transactions; + RRDSET *st_db_backup_restore_operations; + RRDSET *st_db_log_flushed; + RRDSET *st_db_log_flushes; + RRDSET *st_db_transactions; + RRDSET *st_db_write_transactions; + + RRDDIM *rd_db_data_file_size; + RRDDIM *rd_db_active_transactions; + RRDDIM *rd_db_backup_restore_operations; + RRDDIM *rd_db_log_flushed; + RRDDIM *rd_db_log_flushes; + RRDDIM *rd_db_transactions; + RRDDIM *rd_db_write_transactions; + + COUNTER_DATA MSSQLDatabaseActiveTransactions; + COUNTER_DATA MSSQLDatabaseBackupRestoreOperations; + COUNTER_DATA MSSQLDatabaseDataFileSize; + COUNTER_DATA MSSQLDatabaseLogFlushed; + COUNTER_DATA MSSQLDatabaseLogFlushes; + COUNTER_DATA MSSQLDatabaseTransactions; + COUNTER_DATA MSSQLDatabaseWriteTransactions; + + uint32_t updated; +}; + +static DICTIONARY *mssql_instances = NULL; + +static void initialize_mssql_objects(struct mssql_instance *p, const char *instance) { + char prefix[NETDATA_MAX_INSTANCE_NAME]; + if (!strcmp(instance, "MSSQLSERVER")) { + strncpyz(prefix, "SQLServer:", sizeof(prefix) - 1); + } else if (!strcmp(instance, "SQLEXPRESS")) { + strncpyz(prefix, "MSSQL$SQLEXPRESS:", sizeof(prefix) - 1); + } else { + char *express = (!is_sqlexpress) ? "" : "SQLEXPRESS"; + snprintfz(prefix, sizeof(prefix) - 1, "MSSQL$%s:%s:", express, instance); + } + + size_t length = strlen(prefix); + char name[NETDATA_MAX_INSTANCE_OBJECT]; + snprintfz(name, sizeof(name) - 1, "%s%s", prefix, "General Statistics"); + p->objectName[NETDATA_MSSQL_GENERAL_STATS] = strdup(name); + + strncpyz(&name[length], "SQL Errors", sizeof(name) - length); + p->objectName[NETDATA_MSSQL_SQL_ERRORS] = strdup(name); + + strncpyz(&name[length], "Databases", sizeof(name) - length); + p->objectName[NETDATA_MSSQL_DATABASE] = strdup(name); + + strncpyz(&name[length], "SQL Statistics", sizeof(name) - length); + p->objectName[NETDATA_MSSQL_SQL_STATS] = strdup(name); + + strncpyz(&name[length], "Buffer Manager", sizeof(name) - length); + p->objectName[NETDATA_MSSQL_BUFFER_MANAGEMENT] = strdup(name); + + strncpyz(&name[length], "Memory Manager", sizeof(name) - length); + p->objectName[NETDATA_MSSQL_MEMORY] = strdup(name); + + strncpyz(&name[length], "Locks", sizeof(name) - length); + p->objectName[NETDATA_MSSQL_LOCKS] = strdup(name); + + strncpyz(&name[length], "Access Methods", sizeof(name) - length); + p->objectName[NETDATA_MSSQL_ACCESS_METHODS] = strdup(name); + + p->instanceID = strdup(instance); +} + +static inline void initialize_mssql_keys(struct mssql_instance *p) { + // General Statistics + p->MSSQLUserConnections.key = "User Connections"; + p->MSSQLBlockedProcesses.key = "Processes blocked"; + + // SQL Statistics + p->MSSQLStatsAutoParameterization.key = "Auto-Param Attempts/sec"; + p->MSSQLStatsBatchRequests.key = "Batch Requests/sec"; + p->MSSQLStatSafeAutoParameterization.key = "Safe Auto-Params/sec"; + p->MSSQLCompilations.key = "SQL Compilations/sec"; + p->MSSQLRecompilations.key = "SQL Re-Compilations/sec"; + + // Buffer Management + p->MSSQLBufferCacheHits.key = "Buffer cache hit ratio"; + p->MSSQLBufferPageLifeExpectancy.key = "Page life expectancy"; + p->MSSQLBufferCheckpointPages.key = "Checkpoint pages/sec"; + p->MSSQLBufferPageReads.key = "Page reads/sec"; + p->MSSQLBufferPageWrites.key = "Page writes/sec"; + + // Access Methods + p->MSSQLAccessMethodPageSplits.key = "Page Splits/sec"; + + // Errors + p->MSSQLSQLErrorsTotal.key = "Errors/sec"; + + p->MSSQLConnectionMemoryBytes.key = "Connection Memory (KB)"; + p->MSSQLExternalBenefitOfMemory.key = "External benefit of memory"; + p->MSSQLPendingMemoryGrants.key = "Memory Grants Pending"; + p->MSSQLTotalServerMemory.key = "Total Server Memory (KB)"; +} + +void dict_mssql_insert_locks_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { + struct mssql_lock_instance *ptr = value; + ptr->deadLocks.key = "Number of Deadlocks/sec"; + ptr->lockWait.key = "Lock Waits/sec"; +} + +void dict_mssql_insert_databases_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { + struct mssql_db_instance *ptr = value; + + ptr->MSSQLDatabaseDataFileSize.key = "Data File(s) Size (KB)"; + ptr->MSSQLDatabaseActiveTransactions.key = "Active Transactions"; + ptr->MSSQLDatabaseBackupRestoreOperations.key = "Backup/Restore Throughput/sec"; + ptr->MSSQLDatabaseLogFlushed.key = "Log Bytes Flushed/sec"; + ptr->MSSQLDatabaseLogFlushes.key = "Log Flushes/sec"; + ptr->MSSQLDatabaseTransactions.key = "Transactions/sec"; + ptr->MSSQLDatabaseWriteTransactions.key = "Write Transactions/sec"; +} + +void dict_mssql_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { + struct mssql_instance *p = value; + const char *instance = dictionary_acquired_item_name((DICTIONARY_ITEM *)item); + + if (!p->locks_instances) { + p->locks_instances = dictionary_create_advanced( + DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct mssql_lock_instance)); + dictionary_register_insert_callback(p->locks_instances, dict_mssql_insert_locks_cb, NULL); + } + + if (!p->databases) { + p->databases = dictionary_create_advanced( + DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct mssql_db_instance)); + dictionary_register_insert_callback(p->databases, dict_mssql_insert_databases_cb, NULL); + } + + initialize_mssql_objects(p, instance); + initialize_mssql_keys(p); +} + +static int mssql_fill_dictionary() { + HKEY hKey; + LSTATUS ret = RegOpenKeyExA( + HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Microsoft SQL Server\\Instance Names\\SQL", 0, KEY_READ, &hKey); + if (ret != ERROR_SUCCESS) + return -1; + + DWORD values = 0; + + ret = RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL, &values, NULL, NULL, NULL, NULL); + if (ret != ERROR_SUCCESS) { + goto endMSSQLFillDict; + } + + if (!values) { + ret = ERROR_PATH_NOT_FOUND; + goto endMSSQLFillDict; + } + +// https://learn.microsoft.com/en-us/windows/win32/sysinfo/enumerating-registry-subkeys +#define REGISTRY_MAX_VALUE 16383 + + DWORD i; + char avalue[REGISTRY_MAX_VALUE] = {'\0'}; + DWORD length = REGISTRY_MAX_VALUE; + for (i = 0; i < values; i++) { + avalue[0] = '\0'; + + ret = RegEnumValue(hKey, i, avalue, &length, NULL, NULL, NULL, NULL); + if (ret != ERROR_SUCCESS) + continue; + + if (!strcmp(avalue, "SQLEXPRESS")) { + is_sqlexpress = TRUE; + } + + struct mssql_instance *p = dictionary_set(mssql_instances, avalue, NULL, sizeof(*p)); + } + +endMSSQLFillDict: + RegCloseKey(hKey); + + return (ret == ERROR_SUCCESS) ? 0 : -1; +} + +static int initialize(void) { + mssql_instances = dictionary_create_advanced( + DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct mssql_instance)); + + dictionary_register_insert_callback(mssql_instances, dict_mssql_insert_cb, NULL); + + if (mssql_fill_dictionary()) { + return -1; + } + + return 0; +} + +static void do_mssql_general_stats(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *p, int update_every) { + char id[RRD_ID_LENGTH_MAX + 1]; + + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, p->objectName[NETDATA_MSSQL_GENERAL_STATS]); + if (!pObjectType) + return; + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLUserConnections)) { + if (!p->st_user_connections) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_user_connections", p->instanceID); + netdata_fix_chart_name(id); + p->st_user_connections = rrdset_create_localhost( + "mssql", + id, + NULL, + "connections", + "mssql.instance_user_connections", + "User connections", + "connections", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_USER_CONNECTIONS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_user_connections = rrddim_add(p->st_user_connections, "user", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + rrdlabels_add(p->st_user_connections->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_user_connections, p->rd_user_connections, (collected_number)p->MSSQLUserConnections.current.Data); + rrdset_done(p->st_user_connections); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLBlockedProcesses)) { + if (!p->st_process_blocked) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_blocked_process", p->instanceID); + netdata_fix_chart_name(id); + p->st_process_blocked = rrdset_create_localhost( + "mssql", + id, + NULL, + "processes", + "mssql.instance_blocked_processes", + "Blocked processes", + "process", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_BLOCKED_PROCESSES, + update_every, + RRDSET_TYPE_LINE); + + p->rd_process_blocked = rrddim_add(p->st_process_blocked, "blocked", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + rrdlabels_add(p->st_process_blocked->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_process_blocked, p->rd_process_blocked, (collected_number)p->MSSQLBlockedProcesses.current.Data); + rrdset_done(p->st_process_blocked); + } +} + +static void do_mssql_sql_statistics(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *p, int update_every) { + char id[RRD_ID_LENGTH_MAX + 1]; + + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, p->objectName[NETDATA_MSSQL_SQL_STATS]); + if (!pObjectType) + return; + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLStatsAutoParameterization)) { + if (!p->st_stats_auto_param) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_sqlstats_auto_parameterization_attempts", p->instanceID); + netdata_fix_chart_name(id); + p->st_stats_auto_param = rrdset_create_localhost( + "mssql", + id, + NULL, + "sql activity", + "mssql.instance_sqlstats_auto_parameterization_attempts", + "Failed auto-parameterization attempts", + "attempts/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_STATS_AUTO_PARAMETRIZATION, + update_every, + RRDSET_TYPE_LINE); + + p->rd_stats_auto_param = + rrddim_add(p->st_stats_auto_param, "failed", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(p->st_stats_auto_param->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_stats_auto_param, + p->rd_stats_auto_param, + (collected_number)p->MSSQLStatsAutoParameterization.current.Data); + rrdset_done(p->st_stats_auto_param); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLStatsBatchRequests)) { + if (!p->st_stats_batch_request) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_sqlstats_batch_requests", p->instanceID); + netdata_fix_chart_name(id); + p->st_stats_batch_request = rrdset_create_localhost( + "mssql", + id, + NULL, + "sql activity", + "mssql.instance_sqlstats_batch_requests", + "Total of batches requests", + "requests/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_STATS_BATCH_REQUEST, + update_every, + RRDSET_TYPE_LINE); + + p->rd_stats_batch_request = + rrddim_add(p->st_stats_batch_request, "batch", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(p->st_stats_batch_request->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_stats_batch_request, + p->rd_stats_batch_request, + (collected_number)p->MSSQLStatsBatchRequests.current.Data); + rrdset_done(p->st_stats_batch_request); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLStatSafeAutoParameterization)) { + if (!p->st_stats_safe_auto) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_sqlstats_safe_auto_parameterization_attempts", p->instanceID); + netdata_fix_chart_name(id); + p->st_stats_safe_auto = rrdset_create_localhost( + "mssql", + id, + NULL, + "sql activity", + "mssql.instance_sqlstats_safe_auto_parameterization_attempts", + "Safe auto-parameterization attempts", + "attempts/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_STATS_SAFE_AUTO_PARAMETRIZATION, + update_every, + RRDSET_TYPE_LINE); + + p->rd_stats_safe_auto = rrddim_add(p->st_stats_safe_auto, "safe", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(p->st_stats_safe_auto->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_stats_safe_auto, + p->rd_stats_safe_auto, + (collected_number)p->MSSQLStatSafeAutoParameterization.current.Data); + rrdset_done(p->st_stats_safe_auto); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLCompilations)) { + if (!p->st_stats_compilation) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_sqlstats_sql_compilations", p->instanceID); + netdata_fix_chart_name(id); + p->st_stats_compilation = rrdset_create_localhost( + "mssql", + id, + NULL, + "sql activity", + "mssql.instance_sqlstats_sql_compilations", + "SQL compilations", + "compilations/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_STATS_COMPILATIONS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_stats_compilation = + rrddim_add(p->st_stats_compilation, "compilations", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(p->st_stats_compilation->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_stats_compilation, p->rd_stats_compilation, (collected_number)p->MSSQLCompilations.current.Data); + rrdset_done(p->st_stats_compilation); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLRecompilations)) { + if (!p->st_stats_recompiles) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_sqlstats_sql_recompilations", p->instanceID); + netdata_fix_chart_name(id); + p->st_stats_recompiles = rrdset_create_localhost( + "mssql", + id, + NULL, + "sql activity", + "mssql.instance_sqlstats_sql_recompilations", + "SQL re-compilations", + "recompiles/", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_STATS_RECOMPILATIONS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_stats_recompiles = + rrddim_add(p->st_stats_recompiles, "recompiles", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(p->st_stats_recompiles->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_stats_recompiles, p->rd_stats_recompiles, (collected_number)p->MSSQLRecompilations.current.Data); + rrdset_done(p->st_stats_recompiles); + } +} + +static void do_mssql_buffer_management(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *p, int update_every) { + char id[RRD_ID_LENGTH_MAX + 1]; + + PERF_OBJECT_TYPE *pObjectType = + perflibFindObjectTypeByName(pDataBlock, p->objectName[NETDATA_MSSQL_BUFFER_MANAGEMENT]); + if (!pObjectType) + return; + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLBufferCacheHits)) { + if (!p->st_buff_cache_hits) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_cache_hit_ratio", p->instanceID); + netdata_fix_chart_name(id); + p->st_buff_cache_hits = rrdset_create_localhost( + "mssql", + id, + NULL, + "buffer cache", + "mssql.instance_cache_hit_ratio", + "Buffer Cache hit ratio", + "percentage", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_BUFF_CACHE_HIT_RATIO, + update_every, + RRDSET_TYPE_LINE); + + p->rd_buff_cache_hits = rrddim_add(p->st_buff_cache_hits, "hit_ratio", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + rrdlabels_add(p->st_buff_cache_hits->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_buff_cache_hits, p->rd_buff_cache_hits, (collected_number)p->MSSQLBufferCacheHits.current.Data); + rrdset_done(p->st_buff_cache_hits); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLBufferCheckpointPages)) { + if (!p->st_buff_checkpoint_pages) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_bufman_checkpoint_pages", p->instanceID); + netdata_fix_chart_name(id); + p->st_buff_checkpoint_pages = rrdset_create_localhost( + "mssql", + id, + NULL, + "buffer cache", + "mssql.instance_bufman_checkpoint_pages", + "Flushed pages", + "pages/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_BUFF_CHECKPOINT_PAGES, + update_every, + RRDSET_TYPE_LINE); + + p->rd_buff_checkpoint_pages = + rrddim_add(p->st_buff_checkpoint_pages, "log", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(p->st_buff_checkpoint_pages->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_buff_checkpoint_pages, + p->rd_buff_checkpoint_pages, + (collected_number)p->MSSQLBufferCheckpointPages.current.Data); + rrdset_done(p->st_buff_checkpoint_pages); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLBufferPageLifeExpectancy)) { + if (!p->st_buff_cache_page_life_expectancy) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_bufman_page_life_expectancy", p->instanceID); + netdata_fix_chart_name(id); + p->st_buff_cache_page_life_expectancy = rrdset_create_localhost( + "mssql", + id, + NULL, + "buffer cache", + "mssql.instance_bufman_page_life_expectancy", + "Page life expectancy", + "seconds", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_BUFF_PAGE_LIFE_EXPECTANCY, + update_every, + RRDSET_TYPE_LINE); + + p->rd_buff_cache_page_life_expectancy = rrddim_add( + p->st_buff_cache_page_life_expectancy, "life_expectancy", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + rrdlabels_add( + p->st_buff_cache_page_life_expectancy->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_buff_cache_page_life_expectancy, + p->rd_buff_cache_page_life_expectancy, + (collected_number)p->MSSQLBufferPageLifeExpectancy.current.Data); + rrdset_done(p->st_buff_cache_page_life_expectancy); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLBufferPageReads) && + perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLBufferPageWrites)) { + if (!p->st_buff_page_iops) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_bufman_iops", p->instanceID); + netdata_fix_chart_name(id); + p->st_buff_page_iops = rrdset_create_localhost( + "mssql", + id, + NULL, + "buffer cache", + "mssql.instance_bufman_iops", + "Number of pages input and output", + "pages/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_BUFF_MAN_IOPS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_buff_page_reads = rrddim_add(p->st_buff_page_iops, "read", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_buff_page_writes = + rrddim_add(p->st_buff_page_iops, "written", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(p->st_buff_page_iops->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_buff_page_iops, p->rd_buff_page_reads, (collected_number)p->MSSQLBufferPageReads.current.Data); + rrddim_set_by_pointer( + p->st_buff_page_iops, p->rd_buff_page_writes, (collected_number)p->MSSQLBufferPageWrites.current.Data); + + rrdset_done(p->st_buff_page_iops); + } +} + +static void do_mssql_access_methods(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *p, int update_every) { + char id[RRD_ID_LENGTH_MAX + 1]; + + PERF_OBJECT_TYPE *pObjectType = + perflibFindObjectTypeByName(pDataBlock, p->objectName[NETDATA_MSSQL_ACCESS_METHODS]); + if (!pObjectType) + return; + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLAccessMethodPageSplits)) { + if (!p->st_access_method_page_splits) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_accessmethods_page_splits", p->instanceID); + netdata_fix_chart_name(id); + p->st_access_method_page_splits = rrdset_create_localhost( + "mssql", + id, + NULL, + "buffer cache", + "mssql.instance_accessmethods_page_splits", + "Page splits", + "splits/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_BUFF_METHODS_PAGE_SPLIT, + update_every, + RRDSET_TYPE_LINE); + + p->rd_access_method_page_splits = + rrddim_add(p->st_access_method_page_splits, "page", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add( + p->st_access_method_page_splits->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_access_method_page_splits, + p->rd_access_method_page_splits, + (collected_number)p->MSSQLAccessMethodPageSplits.current.Data); + rrdset_done(p->st_access_method_page_splits); + } +} + +static void do_mssql_errors(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *p, int update_every) { + char id[RRD_ID_LENGTH_MAX + 1]; + + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, p->objectName[NETDATA_MSSQL_SQL_ERRORS]); + if (!pObjectType) + return; + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLSQLErrorsTotal)) { + if (!p->st_sql_errors) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_sql_errors_total", p->instanceID); + netdata_fix_chart_name(id); + p->st_sql_errors = rrdset_create_localhost( + "mssql", + id, + NULL, + "errors", + "mssql.instance_sql_errors", + "Errors", + "errors/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_SQL_ERRORS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_sql_errors = rrddim_add(p->st_sql_errors, "errors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(p->st_sql_errors->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_sql_errors, p->rd_sql_errors, (collected_number)p->MSSQLAccessMethodPageSplits.current.Data); + rrdset_done(p->st_sql_errors); + } +} + +int dict_mssql_locks_charts_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { + char id[RRD_ID_LENGTH_MAX + 1]; + + struct mssql_lock_instance *mli = value; + const char *instance = dictionary_acquired_item_name((DICTIONARY_ITEM *)item); + + int *update_every = data; + + if (!mli->parent->st_lockWait) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_locks_lock_wait", mli->parent->instanceID); + netdata_fix_chart_name(id); + mli->parent->st_lockWait = rrdset_create_localhost( + "mssql", + id, + NULL, + "locks", + "mssql.instance_locks_lock_wait", + "Lock requests that required the caller to wait.", + "locks/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_LOCKS_WAIT, + *update_every, + RRDSET_TYPE_LINE); + + rrdlabels_add( + mli->parent->st_lockWait->rrdlabels, "mssql_instance", mli->parent->instanceID, RRDLABEL_SRC_AUTO); + } + + if (!mli->rd_lockWait) { + mli->rd_lockWait = rrddim_add(mli->parent->st_lockWait, instance, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + if (mli->updated & (1 << NETDATA_MSSQL_ENUM_MLI_IDX_WAIT)) { + rrddim_set_by_pointer( + mli->parent->st_lockWait, mli->rd_lockWait, (collected_number)(mli->lockWait.current.Data)); + } + + if (!mli->parent->st_deadLocks) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_locks_deadlocks", mli->parent->instanceID); + netdata_fix_chart_name(id); + mli->parent->st_deadLocks = rrdset_create_localhost( + "mssql", + id, + NULL, + "locks", + "mssql.instance_locks_deadlocks", + "Lock requests that resulted in deadlock.", + "deadlocks/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_LOCKS_DEADLOCK, + *update_every, + RRDSET_TYPE_LINE); + + rrdlabels_add( + mli->parent->st_deadLocks->rrdlabels, "mssql_instance", mli->parent->instanceID, RRDLABEL_SRC_AUTO); + } + + if (!mli->rd_deadLocks) { + mli->rd_deadLocks = rrddim_add(mli->parent->st_deadLocks, instance, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + if (mli->updated & (1 << NETDATA_MSSQL_ENUM_MLI_IDX_DEAD_LOCKS)) { + rrddim_set_by_pointer( + mli->parent->st_deadLocks, mli->rd_deadLocks, (collected_number)mli->deadLocks.current.Data); + } + + return 1; +} + +static void do_mssql_locks(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *p, int update_every) { + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, p->objectName[NETDATA_MSSQL_LOCKS]); + if (!pObjectType) + return; + + PERF_INSTANCE_DEFINITION *pi = NULL; + for (LONG i = 0; i < pObjectType->NumInstances; i++) { + pi = perflibForEachInstance(pDataBlock, pObjectType, pi); + if (!pi) + break; + + if (!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer))) + strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1); + + if (!strcasecmp(windows_shared_buffer, "_Total")) + continue; + + struct mssql_lock_instance *mli = dictionary_set(p->locks_instances, windows_shared_buffer, NULL, sizeof(*mli)); + if (!mli) + continue; + + if (!mli->parent) { + mli->parent = p; + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &mli->lockWait)) + mli->updated |= (1 << NETDATA_MSSQL_ENUM_MLI_IDX_WAIT); + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &mli->deadLocks)) + mli->updated |= (1 << NETDATA_MSSQL_ENUM_MLI_IDX_DEAD_LOCKS); + } + + dictionary_sorted_walkthrough_read(p->locks_instances, dict_mssql_locks_charts_cb, &update_every); + rrdset_done(p->st_lockWait); + rrdset_done(p->st_deadLocks); +} + +static void mssql_database_backup_restore_chart(struct mssql_db_instance *mli, const char *db, int update_every) { + char id[RRD_ID_LENGTH_MAX + 1]; + + if (!mli->st_db_backup_restore_operations) { + snprintfz(id, RRD_ID_LENGTH_MAX, "db_%s_instance_%s_backup_restore_operations", db, mli->parent->instanceID); + netdata_fix_chart_name(id); + mli->st_db_backup_restore_operations = rrdset_create_localhost( + "mssql", + id, + NULL, + "transactions", + "mssql.database_backup_restore_operations", + "Backup IO per database", + "operations/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_DATABASE_BACKUP_RESTORE_OPERATIONS, + update_every, + RRDSET_TYPE_LINE); + + rrdlabels_add( + mli->st_db_backup_restore_operations->rrdlabels, + "mssql_instance", + mli->parent->instanceID, + RRDLABEL_SRC_AUTO); + rrdlabels_add(mli->st_db_backup_restore_operations->rrdlabels, "database", db, RRDLABEL_SRC_AUTO); + } + + if (!mli->rd_db_backup_restore_operations) { + mli->rd_db_backup_restore_operations = + rrddim_add(mli->st_db_backup_restore_operations, "backup", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + if (mli->updated & (1 << NETDATA_MSSQL_ENUM_MDI_IDX_BACKUP_RESTORE_OP)) { + rrddim_set_by_pointer( + mli->st_db_backup_restore_operations, + mli->rd_db_backup_restore_operations, + (collected_number)mli->MSSQLDatabaseBackupRestoreOperations.current.Data); + } + + rrdset_done(mli->st_db_backup_restore_operations); +} + +static void mssql_database_log_flushes_chart(struct mssql_db_instance *mli, const char *db, int update_every) { + char id[RRD_ID_LENGTH_MAX + 1]; + + if (!mli->st_db_log_flushes) { + snprintfz(id, RRD_ID_LENGTH_MAX, "db_%s_instance_%s_log_flushes", db, mli->parent->instanceID); + netdata_fix_chart_name(id); + mli->st_db_log_flushes = rrdset_create_localhost( + "mssql", + id, + NULL, + "transactions", + "mssql.database_log_flushes", + "Log flushes", + "flushes/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_DATABASE_LOG_FLUSHES, + update_every, + RRDSET_TYPE_LINE); + + rrdlabels_add(mli->st_db_log_flushes->rrdlabels, "mssql_instance", mli->parent->instanceID, RRDLABEL_SRC_AUTO); + rrdlabels_add(mli->st_db_log_flushes->rrdlabels, "database", db, RRDLABEL_SRC_AUTO); + } + + if (!mli->rd_db_log_flushes) { + mli->rd_db_log_flushes = rrddim_add(mli->st_db_log_flushes, "flushes", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + if (mli->updated & (1 << NETDATA_MSSQL_ENUM_MDI_IDX_LOG_FLUSHES)) { + rrddim_set_by_pointer( + mli->st_db_log_flushes, + mli->rd_db_log_flushes, + (collected_number)mli->MSSQLDatabaseLogFlushes.current.Data); + } + + rrdset_done(mli->st_db_log_flushes); +} + +static void mssql_database_log_flushed_chart(struct mssql_db_instance *mli, const char *db, int update_every) { + char id[RRD_ID_LENGTH_MAX + 1]; + + if (!mli->st_db_log_flushed) { + snprintfz(id, RRD_ID_LENGTH_MAX, "db_%s_instance_%s_log_flushed", db, mli->parent->instanceID); + netdata_fix_chart_name(id); + mli->st_db_log_flushed = rrdset_create_localhost( + "mssql", + id, + NULL, + "transactions", + "mssql.database_log_flushed", + "Log flushed", + "bytes/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_DATABASE_LOG_FLUSHED, + update_every, + RRDSET_TYPE_LINE); + + rrdlabels_add(mli->st_db_log_flushed->rrdlabels, "mssql_instance", mli->parent->instanceID, RRDLABEL_SRC_AUTO); + rrdlabels_add(mli->st_db_log_flushed->rrdlabels, "database", db, RRDLABEL_SRC_AUTO); + } + + if (!mli->rd_db_log_flushed) { + mli->rd_db_log_flushed = rrddim_add(mli->st_db_log_flushed, "flushed", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + if (mli->updated & (1 << NETDATA_MSSQL_ENUM_MDI_IDX_LOG_FLUSHED)) { + rrddim_set_by_pointer( + mli->st_db_log_flushed, + mli->rd_db_log_flushed, + (collected_number)mli->MSSQLDatabaseLogFlushed.current.Data); + } + + rrdset_done(mli->st_db_log_flushed); +} + +static void mssql_transactions_chart(struct mssql_db_instance *mli, const char *db, int update_every) { + char id[RRD_ID_LENGTH_MAX + 1]; + + if (!mli->st_db_transactions) { + snprintfz(id, RRD_ID_LENGTH_MAX, "db_%s_instance_%s_transactions", db, mli->parent->instanceID); + netdata_fix_chart_name(id); + mli->st_db_transactions = rrdset_create_localhost( + "mssql", + id, + NULL, + "transactions", + "mssql.database_transactions", + "Transactions", + "transactions/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_DATABASE_TRANSACTIONS, + update_every, + RRDSET_TYPE_LINE); + + rrdlabels_add(mli->st_db_transactions->rrdlabels, "mssql_instance", mli->parent->instanceID, RRDLABEL_SRC_AUTO); + rrdlabels_add(mli->st_db_transactions->rrdlabels, "database", db, RRDLABEL_SRC_AUTO); + } + + if (!mli->rd_db_transactions) { + mli->rd_db_transactions = + rrddim_add(mli->st_db_transactions, "transactions", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + if (mli->updated & (1 << NETDATA_MSSQL_ENUM_MDI_IDX_TRANSACTIONS)) { + rrddim_set_by_pointer( + mli->st_db_transactions, + mli->rd_db_transactions, + (collected_number)mli->MSSQLDatabaseTransactions.current.Data); + } + + rrdset_done(mli->st_db_transactions); +} + +static void mssql_write_transactions_chart(struct mssql_db_instance *mli, const char *db, int update_every) { + char id[RRD_ID_LENGTH_MAX + 1]; + + if (!mli->st_db_write_transactions) { + snprintfz(id, RRD_ID_LENGTH_MAX, "db_%s_instance_%s_write_transactions", db, mli->parent->instanceID); + netdata_fix_chart_name(id); + mli->st_db_write_transactions = rrdset_create_localhost( + "mssql", + id, + NULL, + "transactions", + "mssql.database_write_transactions", + "Write transactions", + "transactions/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_DATABASE_WRITE_TRANSACTIONS, + update_every, + RRDSET_TYPE_LINE); + + rrdlabels_add( + mli->st_db_write_transactions->rrdlabels, "mssql_instance", mli->parent->instanceID, RRDLABEL_SRC_AUTO); + rrdlabels_add(mli->st_db_write_transactions->rrdlabels, "database", db, RRDLABEL_SRC_AUTO); + } + + if (!mli->rd_db_write_transactions) { + mli->rd_db_write_transactions = + rrddim_add(mli->st_db_write_transactions, "write", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + if (mli->updated & (1 << NETDATA_MSSQL_ENUM_MDI_IDX_WRITE_TRANSACTIONS)) { + rrddim_set_by_pointer( + mli->st_db_write_transactions, + mli->rd_db_write_transactions, + (collected_number)mli->MSSQLDatabaseWriteTransactions.current.Data); + } + + rrdset_done(mli->st_db_write_transactions); +} + +static void mssql_active_transactions_chart(struct mssql_db_instance *mli, const char *db, int update_every) { + char id[RRD_ID_LENGTH_MAX + 1]; + + if (!mli->st_db_active_transactions) { + snprintfz(id, RRD_ID_LENGTH_MAX, "db_%s_instance_%s_active_transactions", db, mli->parent->instanceID); + netdata_fix_chart_name(id); + mli->st_db_active_transactions = rrdset_create_localhost( + "mssql", + id, + NULL, + "transactions", + "mssql.database_active_transactions", + "Active transactions per database", + "transactions", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_DATABASE_ACTIVE_TRANSACTIONS, + update_every, + RRDSET_TYPE_LINE); + + rrdlabels_add( + mli->st_db_active_transactions->rrdlabels, "mssql_instance", mli->parent->instanceID, RRDLABEL_SRC_AUTO); + rrdlabels_add(mli->st_db_active_transactions->rrdlabels, "database", db, RRDLABEL_SRC_AUTO); + } + + if (!mli->rd_db_active_transactions) { + mli->rd_db_active_transactions = + rrddim_add(mli->st_db_active_transactions, "active", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + } + + if (mli->updated & (1 << NETDATA_MSSQL_ENUM_MDI_IDX_ACTIVE_TRANSACTIONS)) { + rrddim_set_by_pointer( + mli->st_db_active_transactions, + mli->rd_db_active_transactions, + (collected_number)mli->MSSQLDatabaseActiveTransactions.current.Data); + } + + rrdset_done(mli->st_db_active_transactions); +} + +static void mssql_data_file_size_chart(struct mssql_db_instance *mli, const char *db, int update_every) { + char id[RRD_ID_LENGTH_MAX + 1]; + + if (!mli->st_db_data_file_size) { + snprintfz(id, RRD_ID_LENGTH_MAX, "db_%s_instance_%s_data_files_size", db, mli->parent->instanceID); + netdata_fix_chart_name(id); + mli->st_db_data_file_size = rrdset_create_localhost( + "mssql", + id, + NULL, + "size", + "mssql.database_data_files_size", + "Current database size.", + "bytes", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_DATABASE_DATA_FILE_SIZE, + update_every, + RRDSET_TYPE_LINE); + + rrdlabels_add( + mli->st_db_data_file_size->rrdlabels, "mssql_instance", mli->parent->instanceID, RRDLABEL_SRC_AUTO); + rrdlabels_add(mli->st_db_data_file_size->rrdlabels, "database", db, RRDLABEL_SRC_AUTO); + } + + if (!mli->rd_db_data_file_size) { + mli->rd_db_data_file_size = rrddim_add(mli->st_db_data_file_size, "size", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + } + + // FIXME: If the value cannot be retrieved, remove the chart instead of displaying a 0 value. + collected_number data = + (mli->updated & (1 << NETDATA_MSSQL_ENUM_MDI_IDX_FILE_SIZE)) ? mli->MSSQLDatabaseDataFileSize.current.Data : 0; + rrddim_set_by_pointer(mli->st_db_data_file_size, mli->rd_db_data_file_size, data); + + rrdset_done(mli->st_db_data_file_size); +} + +int dict_mssql_databases_charts_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { + struct mssql_db_instance *mli = value; + const char *db = dictionary_acquired_item_name((DICTIONARY_ITEM *)item); + + int *update_every = data; + + void (*transaction_chart[])(struct mssql_db_instance *, const char *, int) = { + // FIXME: allegedly Netdata collects negative values (MSSQLDatabaseDataFileSize). + // something is wrong, perflibdump shows correct values. + // mssql_data_file_size_chart, + mssql_transactions_chart, + mssql_database_backup_restore_chart, + mssql_database_log_flushed_chart, + mssql_database_log_flushes_chart, + mssql_active_transactions_chart, + mssql_write_transactions_chart, + + // Last function pointer must be NULL + NULL}; + + int i; + for (i = 0; transaction_chart[i]; i++) { + transaction_chart[i](mli, db, *update_every); + } + + return 1; +} + +static void do_mssql_databases(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *p, int update_every) { + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, p->objectName[NETDATA_MSSQL_DATABASE]); + if (!pObjectType) + return; + + PERF_INSTANCE_DEFINITION *pi = NULL; + for (LONG i = 0; i < pObjectType->NumInstances; i++) { + pi = perflibForEachInstance(pDataBlock, pObjectType, pi); + if (!pi) + break; + + if (!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer))) + strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1); + + if (!strcasecmp(windows_shared_buffer, "_Total")) + continue; + + struct mssql_db_instance *mdi = dictionary_set(p->databases, windows_shared_buffer, NULL, sizeof(*mdi)); + if (!mdi) + continue; + + mdi->updated = 0; + if (!mdi->parent) { + mdi->parent = p; + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &mdi->MSSQLDatabaseDataFileSize)) { + LONGLONG value = (LONGLONG)mdi->MSSQLDatabaseDataFileSize.current.Data; + if (value > 0) + mdi->updated |= (1 << NETDATA_MSSQL_ENUM_MDI_IDX_FILE_SIZE); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &mdi->MSSQLDatabaseActiveTransactions)) + mdi->updated |= (1 << NETDATA_MSSQL_ENUM_MDI_IDX_ACTIVE_TRANSACTIONS); + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &mdi->MSSQLDatabaseBackupRestoreOperations)) + mdi->updated |= (1 << NETDATA_MSSQL_ENUM_MDI_IDX_BACKUP_RESTORE_OP); + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &mdi->MSSQLDatabaseLogFlushed)) + mdi->updated |= (1 << NETDATA_MSSQL_ENUM_MDI_IDX_LOG_FLUSHED); + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &mdi->MSSQLDatabaseLogFlushes)) + mdi->updated |= (1 << NETDATA_MSSQL_ENUM_MDI_IDX_LOG_FLUSHES); + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &mdi->MSSQLDatabaseTransactions)) + mdi->updated |= (1 << NETDATA_MSSQL_ENUM_MDI_IDX_TRANSACTIONS); + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &mdi->MSSQLDatabaseWriteTransactions)) + mdi->updated |= (1 << NETDATA_MSSQL_ENUM_MDI_IDX_WRITE_TRANSACTIONS); + } + + dictionary_sorted_walkthrough_read(p->databases, dict_mssql_databases_charts_cb, &update_every); +} + +static void do_mssql_memory_mgr(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *p, int update_every) { + char id[RRD_ID_LENGTH_MAX + 1]; + + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, p->objectName[NETDATA_MSSQL_MEMORY]); + if (!pObjectType) + return; + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLConnectionMemoryBytes)) { + if (!p->st_conn_memory) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_memmgr_connection_memory_bytes", p->instanceID); + netdata_fix_chart_name(id); + p->st_conn_memory = rrdset_create_localhost( + "mssql", + id, + NULL, + "memory", + "mssql.instance_memmgr_connection_memory_bytes", + "Amount of dynamic memory to maintain connections", + "bytes", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_MEMMGR_CONNECTION_MEMORY_BYTES, + update_every, + RRDSET_TYPE_LINE); + + p->rd_conn_memory = rrddim_add(p->st_conn_memory, "memory", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + rrdlabels_add(p->st_conn_memory->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_conn_memory, + p->rd_conn_memory, + (collected_number)(p->MSSQLConnectionMemoryBytes.current.Data * 1024)); + rrdset_done(p->st_conn_memory); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLExternalBenefitOfMemory)) { + if (!p->st_ext_benefit_mem) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_memmgr_external_benefit_of_memory", p->instanceID); + netdata_fix_chart_name(id); + p->st_ext_benefit_mem = rrdset_create_localhost( + "mssql", + id, + NULL, + "memory", + "mssql.instance_memmgr_external_benefit_of_memory", + "Performance benefit from adding memory to a specific cache", + "bytes", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_MEMMGR_EXTERNAL_BENEFIT_OF_MEMORY, + update_every, + RRDSET_TYPE_LINE); + + p->rd_ext_benefit_mem = rrddim_add(p->st_ext_benefit_mem, "benefit", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + rrdlabels_add(p->st_ext_benefit_mem->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_ext_benefit_mem, + p->rd_ext_benefit_mem, + (collected_number)p->MSSQLExternalBenefitOfMemory.current.Data); + rrdset_done(p->st_ext_benefit_mem); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLPendingMemoryGrants)) { + if (!p->st_pending_mem_grant) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_memmgr_pending_memory_grants", p->instanceID); + netdata_fix_chart_name(id); + p->st_pending_mem_grant = rrdset_create_localhost( + "mssql", + id, + NULL, + "memory", + "mssql.instance_memmgr_pending_memory_grants", + "Process waiting for memory grant", + "processes", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_MEMMGR_PENDING_MEMORY_GRANTS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_pending_mem_grant = + rrddim_add(p->st_pending_mem_grant, "pending", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + rrdlabels_add(p->st_pending_mem_grant->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_pending_mem_grant, + p->rd_pending_mem_grant, + (collected_number)p->MSSQLPendingMemoryGrants.current.Data); + + rrdset_done(p->st_pending_mem_grant); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLTotalServerMemory)) { + if (!p->st_mem_tot_server) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_memmgr_server_memory", p->instanceID); + netdata_fix_chart_name(id); + p->st_mem_tot_server = rrdset_create_localhost( + "mssql", + id, + NULL, + "memory", + "mssql.instance_memmgr_server_memory", + "Memory committed", + "bytes", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_MEMMGR_TOTAL_SERVER, + update_every, + RRDSET_TYPE_LINE); + + p->rd_mem_tot_server = rrddim_add(p->st_mem_tot_server, "memory", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + rrdlabels_add(p->st_mem_tot_server->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_mem_tot_server, + p->rd_mem_tot_server, + (collected_number)(p->MSSQLTotalServerMemory.current.Data * 1024)); + + rrdset_done(p->st_mem_tot_server); + } +} + +int dict_mssql_charts_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { + struct mssql_instance *p = value; + int *update_every = data; + + static void (*doMSSQL[])(PERF_DATA_BLOCK *, struct mssql_instance *, int) = { + do_mssql_general_stats, + do_mssql_errors, + do_mssql_databases, + do_mssql_locks, + do_mssql_memory_mgr, + do_mssql_buffer_management, + do_mssql_sql_statistics, + do_mssql_access_methods}; + + DWORD i; + for (i = 0; i < NETDATA_MSSQL_METRICS_END; i++) { + if (!doMSSQL[i]) + continue; + + DWORD id = RegistryFindIDByName(p->objectName[i]); + if (id == PERFLIB_REGISTRY_NAME_NOT_FOUND) + return -1; + + PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id); + if (!pDataBlock) + return -1; + + doMSSQL[i](pDataBlock, p, *update_every); + } + + return 1; +} + +int do_PerflibMSSQL(int update_every, usec_t dt __maybe_unused) { + static bool initialized = false; + + if (unlikely(!initialized)) { + if (initialize()) + return -1; + + initialized = true; + } + + dictionary_sorted_walkthrough_read(mssql_instances, dict_mssql_charts_cb, &update_every); + + return 0; +} diff --git a/src/collectors/windows.plugin/perflib-names.c b/src/collectors/windows.plugin/perflib-names.c deleted file mode 100644 index 5b47cbce7..000000000 --- a/src/collectors/windows.plugin/perflib-names.c +++ /dev/null @@ -1,242 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "perflib.h" - -#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.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); - } -} diff --git a/src/collectors/windows.plugin/perflib-netframework.c b/src/collectors/windows.plugin/perflib-netframework.c new file mode 100644 index 000000000..28d58cae8 --- /dev/null +++ b/src/collectors/windows.plugin/perflib-netframework.c @@ -0,0 +1,796 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "windows_plugin.h" +#include "windows-internals.h" + +enum netdata_netframework_metrics { + NETDATA_NETFRAMEWORK_EXCEPTIONS, + NETDATA_NETFRAMEWORK_INTEROP, + NETDATA_NETFRAMEWORK_JIT, + NETDATA_NETFRAMEWORK_LOADING, + + NETDATA_NETFRAMEWORK_END +}; + +struct net_framework_instances { + RRDSET *st_clrexception_thrown; + RRDDIM *rd_clrexception_thrown; + + RRDSET *st_clrexception_filters; + RRDDIM *rd_clrexception_filters; + + RRDSET *st_clrexception_finallys; + RRDDIM *rd_clrexception_finallys; + + RRDSET *st_clrexception_total_catch_depth; + RRDDIM *rd_clrexception_total_catch_depth; + + RRDSET *st_clrinterop_com_callable_wrappers; + RRDDIM *rd_clrinterop_com_callable_wrappers; + + RRDSET *st_clrinterop_marshalling; + RRDDIM *rd_clrinterop_marshalling; + + RRDSET *st_clrinterop_interop_stubs_created; + RRDDIM *rd_clrinterop_interop_stubs_created; + + RRDSET *st_clrjit_methods; + RRDDIM *rd_clrjit_methods; + + RRDSET *st_clrjit_time; + RRDDIM *rd_clrjit_time; + + RRDSET *st_clrjit_standard_failures; + RRDDIM *rd_clrjit_standard_failures; + + RRDSET *st_clrjit_il_bytes; + RRDDIM *rd_clrjit_il_bytes; + + RRDSET *st_clrloading_heap_size; + RRDDIM *rd_clrloading_heap_size; + + RRDSET *st_clrloading_app_domains_loaded; + RRDDIM *rd_clrloading_app_domains_loaded; + + RRDSET *st_clrloading_app_domains_unloaded; + RRDDIM *rd_clrloading_app_domains_unloaded; + + RRDSET *st_clrloading_assemblies_loaded; + RRDDIM *rd_clrloading_assemblies_loaded; + + RRDSET *st_clrloading_classes_loaded; + RRDDIM *rd_clrloading_classes_loaded; + + RRDSET *st_clrloading_class_load_failure; + RRDDIM *rd_clrloading_class_load_failure; + + COUNTER_DATA NETFrameworkCLRExceptionThrown; + COUNTER_DATA NETFrameworkCLRExceptionFilters; + COUNTER_DATA NETFrameworkCLRExceptionFinallys; + COUNTER_DATA NETFrameworkCLRExceptionTotalCatchDepth; + + COUNTER_DATA NETFrameworkCLRInteropCOMCallableWrappers; + COUNTER_DATA NETFrameworkCLRInteropMarshalling; + COUNTER_DATA NETFrameworkCLRInteropStubsCreated; + + COUNTER_DATA NETFrameworkCLRJITMethods; + COUNTER_DATA NETFrameworkCLRJITPercentTime; + COUNTER_DATA NETFrameworkCLRJITFrequencyTime; + COUNTER_DATA NETFrameworkCLRJITStandardFailures; + COUNTER_DATA NETFrameworkCLRJITIlBytes; + + COUNTER_DATA NETFrameworkCLRLoadingHeapSize; + COUNTER_DATA NETFrameworkCLRLoadingAppDomainsLoaded; + COUNTER_DATA NETFrameworkCLRLoadingAppDomainsUnloaded; + COUNTER_DATA NETFrameworkCLRLoadingAssembliesLoaded; + COUNTER_DATA NETFrameworkCLRLoadingClassesLoaded; + COUNTER_DATA NETFrameworkCLRLoadingClassLoadFailure; +}; + +static inline void initialize_net_framework_processes_keys(struct net_framework_instances *p) { + p->NETFrameworkCLRExceptionFilters.key = "# of Filters / sec"; + p->NETFrameworkCLRExceptionFinallys.key = "# of Finallys / sec"; + p->NETFrameworkCLRExceptionThrown.key = "# of Exceps Thrown / sec"; + p->NETFrameworkCLRExceptionTotalCatchDepth.key = "Throw To Catch Depth / sec"; + + p->NETFrameworkCLRInteropCOMCallableWrappers.key = "# of CCWs"; + p->NETFrameworkCLRInteropMarshalling.key = "# of Stubs"; + p->NETFrameworkCLRInteropStubsCreated.key = "# of marshalling"; + + p->NETFrameworkCLRJITMethods.key = "# of Methods Jitted"; + p->NETFrameworkCLRJITPercentTime.key = "% Time in Jit"; + p->NETFrameworkCLRJITFrequencyTime.key = "IL Bytes Jitted / sec"; + p->NETFrameworkCLRJITStandardFailures.key = "Standard Jit Failures"; + p->NETFrameworkCLRJITIlBytes.key = "# of IL Bytes Jitted"; + + p->NETFrameworkCLRLoadingHeapSize.key = "Bytes in Loader Heap"; + p->NETFrameworkCLRLoadingAppDomainsLoaded.key = "Rate of appdomains"; + p->NETFrameworkCLRLoadingAppDomainsUnloaded.key = "Total appdomains unloaded"; + p->NETFrameworkCLRLoadingAssembliesLoaded.key = "Total Assemblies"; + p->NETFrameworkCLRLoadingClassesLoaded.key = "Total Classes Loaded"; + p->NETFrameworkCLRLoadingClassLoadFailure.key = "Total # of Load Failures"; +} + +void dict_net_framework_processes_insert_cb( + const DICTIONARY_ITEM *item __maybe_unused, + void *value, + void *data __maybe_unused) { + struct net_framework_instances *p = value; + initialize_net_framework_processes_keys(p); +} + +static DICTIONARY *processes = NULL; + +static void initialize(void) { + processes = dictionary_create_advanced( + DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct net_framework_instances)); + + dictionary_register_insert_callback(processes, dict_net_framework_processes_insert_cb, NULL); +} + +static void netdata_framework_clr_exceptions(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, int update_every) { + PERF_INSTANCE_DEFINITION *pi = NULL; + char id[RRD_ID_LENGTH_MAX + 1]; + for (LONG i = 0; i < pObjectType->NumInstances; i++) { + pi = perflibForEachInstance(pDataBlock, pObjectType, pi); + if (!pi) + break; + + if (!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer))) + strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1); + + if (strcasecmp(windows_shared_buffer, "_Global_") == 0) + continue; + + struct net_framework_instances *p = dictionary_set(processes, windows_shared_buffer, NULL, sizeof(*p)); + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRExceptionThrown)) { + if (!p->st_clrexception_thrown) { + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrexception_thrown", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st_clrexception_thrown = rrdset_create_localhost( + "netframework", + id, + NULL, + "exceptions", + "netframework.clrexception_thrown", + "Thrown exceptions", + "exceptions/s", + PLUGIN_WINDOWS_NAME, + "PerflibNetFramework", + PRIO_NETFRAMEWORK_CLR_EXCEPTION_THROWN, + update_every, + RRDSET_TYPE_LINE); + + p->rd_clrexception_thrown = + rrddim_add(p->st_clrexception_thrown, "exceptions", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add( + p->st_clrexception_thrown->rrdlabels, "process", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_clrexception_thrown, + p->rd_clrexception_thrown, + (collected_number)p->NETFrameworkCLRExceptionThrown.current.Data); + rrdset_done(p->st_clrexception_thrown); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRExceptionFilters)) { + if (!p->st_clrexception_filters) { + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrexception_filters", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st_clrexception_filters = rrdset_create_localhost( + "netframework", + id, + NULL, + "exceptions", + "netframework.clrexception_filters", + "Thrown exceptions filters", + "filters/s", + PLUGIN_WINDOWS_NAME, + "PerflibNetFramework", + PRIO_NETFRAMEWORK_CLR_EXCEPTION_FILTERS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_clrexception_filters = + rrddim_add(p->st_clrexception_filters, "filters", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add( + p->st_clrexception_filters->rrdlabels, "process", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_clrexception_filters, + p->rd_clrexception_filters, + (collected_number)p->NETFrameworkCLRExceptionFilters.current.Data); + rrdset_done(p->st_clrexception_filters); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRExceptionFinallys)) { + if (!p->st_clrexception_finallys) { + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrexception_finallys", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st_clrexception_finallys = rrdset_create_localhost( + "netframework", + id, + NULL, + "exceptions", + "netframework.clrexception_finallys", + "Executed finally blocks", + "finallys/s", + PLUGIN_WINDOWS_NAME, + "PerflibNetFramework", + PRIO_NETFRAMEWORK_CLR_EXCEPTION_FINALLYS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_clrexception_finallys = + rrddim_add(p->st_clrexception_finallys, "finallys", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add( + p->st_clrexception_finallys->rrdlabels, "process", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_clrexception_finallys, + p->rd_clrexception_finallys, + (collected_number)p->NETFrameworkCLRExceptionFinallys.current.Data); + rrdset_done(p->st_clrexception_finallys); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRExceptionTotalCatchDepth)) { + if (!p->st_clrexception_total_catch_depth) { + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrexception_throw_to_catch_depth", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st_clrexception_total_catch_depth = rrdset_create_localhost( + "netframework", + id, + NULL, + "exceptions", + "netframework.clrexception_throw_to_catch_depth", + "Traversed stack frames", + "stack_frames/s", + PLUGIN_WINDOWS_NAME, + "PerflibNetFramework", + PRIO_NETFRAMEWORK_CLR_EXCEPTION_THROW_TO_CATCH_DEPTH, + update_every, + RRDSET_TYPE_LINE); + + p->rd_clrexception_total_catch_depth = rrddim_add( + p->st_clrexception_total_catch_depth, "traversed", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add( + p->st_clrexception_total_catch_depth->rrdlabels, + "process", + windows_shared_buffer, + RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_clrexception_total_catch_depth, + p->rd_clrexception_total_catch_depth, + (collected_number)p->NETFrameworkCLRExceptionTotalCatchDepth.current.Data); + rrdset_done(p->st_clrexception_total_catch_depth); + } + } +} + +static void netdata_framework_clr_interop(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, int update_every) { + PERF_INSTANCE_DEFINITION *pi = NULL; + char id[RRD_ID_LENGTH_MAX + 1]; + for (LONG i = 0; i < pObjectType->NumInstances; i++) { + pi = perflibForEachInstance(pDataBlock, pObjectType, pi); + if (!pi) + break; + + if (!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer))) + strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1); + + if (strcasecmp(windows_shared_buffer, "_Global_") == 0) + continue; + + struct net_framework_instances *p = dictionary_set(processes, windows_shared_buffer, NULL, sizeof(*p)); + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRInteropCOMCallableWrappers)) { + if (!p->st_clrinterop_com_callable_wrappers) { + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrinterop_com_callable_wrappers", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st_clrinterop_com_callable_wrappers = rrdset_create_localhost( + "netframework", + id, + NULL, + "interop", + "netframework.clrinterop_com_callable_wrappers", + "COM callable wrappers (CCW)", + "ccw/s", + PLUGIN_WINDOWS_NAME, + "PerflibNetFramework", + PRIO_NETFRAMEWORK_CLR_INTEROP_CCW, + update_every, + RRDSET_TYPE_LINE); + + p->rd_clrinterop_com_callable_wrappers = rrddim_add( + p->st_clrinterop_com_callable_wrappers, + "com_callable_wrappers", + NULL, + 1, + 1, + RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add( + p->st_clrinterop_com_callable_wrappers->rrdlabels, + "process", + windows_shared_buffer, + RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_clrinterop_com_callable_wrappers, + p->rd_clrinterop_com_callable_wrappers, + (collected_number)p->NETFrameworkCLRInteropCOMCallableWrappers.current.Data); + rrdset_done(p->st_clrinterop_com_callable_wrappers); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRInteropMarshalling)) { + if (!p->st_clrinterop_marshalling) { + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrinterop_interop_marshalling", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st_clrinterop_marshalling = rrdset_create_localhost( + "netframework", + id, + NULL, + "interop", + "netframework.clrinterop_interop_marshallings", + "Arguments and return values marshallings", + "marshalling/s", + PLUGIN_WINDOWS_NAME, + "PerflibNetFramework", + PRIO_NETFRAMEWORK_CLR_INTEROP_MARSHALLING, + update_every, + RRDSET_TYPE_LINE); + + p->rd_clrinterop_marshalling = + rrddim_add(p->st_clrinterop_marshalling, "marshallings", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add( + p->st_clrinterop_marshalling->rrdlabels, "process", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_clrinterop_marshalling, + p->rd_clrinterop_marshalling, + (collected_number)p->NETFrameworkCLRInteropMarshalling.current.Data); + rrdset_done(p->st_clrinterop_marshalling); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRInteropStubsCreated)) { + if (!p->st_clrinterop_interop_stubs_created) { + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrinterop_interop_stubs_created", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st_clrinterop_interop_stubs_created = rrdset_create_localhost( + "netframework", + id, + NULL, + "interop", + "netframework.clrinterop_interop_stubs_created", + "Created stubs", + "stubs/s", + PLUGIN_WINDOWS_NAME, + "PerflibNetFramework", + PRIO_NETFRAMEWORK_CLR_INTEROP_STUBS_CREATED, + update_every, + RRDSET_TYPE_LINE); + + p->rd_clrinterop_interop_stubs_created = rrddim_add( + p->st_clrinterop_interop_stubs_created, "created", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add( + p->st_clrinterop_interop_stubs_created->rrdlabels, + "process", + windows_shared_buffer, + RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_clrinterop_interop_stubs_created, + p->rd_clrinterop_interop_stubs_created, + (collected_number)p->NETFrameworkCLRInteropStubsCreated.current.Data); + rrdset_done(p->st_clrinterop_interop_stubs_created); + } + } +} + +static void netdata_framework_clr_jit(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, int update_every) { + PERF_INSTANCE_DEFINITION *pi = NULL; + char id[RRD_ID_LENGTH_MAX + 1]; + for (LONG i = 0; i < pObjectType->NumInstances; i++) { + pi = perflibForEachInstance(pDataBlock, pObjectType, pi); + if (!pi) + break; + + if (!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer))) + strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1); + + if (strcasecmp(windows_shared_buffer, "_Global_") == 0) + continue; + + struct net_framework_instances *p = dictionary_set(processes, windows_shared_buffer, NULL, sizeof(*p)); + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRJITMethods)) { + if (!p->st_clrjit_methods) { + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrjit_methods", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st_clrjit_methods = rrdset_create_localhost( + "netframework", + id, + NULL, + "jit", + "netframework.clrjit_methods", + "JIT-compiled methods", + "methods/s", + PLUGIN_WINDOWS_NAME, + "PerflibNetFramework", + PRIO_NETFRAMEWORK_CLR_JIT_METHODS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_clrjit_methods = + rrddim_add(p->st_clrjit_methods, "jit-compiled", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(p->st_clrjit_methods->rrdlabels, "process", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_clrjit_methods, + p->rd_clrjit_methods, + (collected_number)p->NETFrameworkCLRJITMethods.current.Data); + rrdset_done(p->st_clrjit_methods); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRJITFrequencyTime) && + perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRJITPercentTime)) { + if (!p->st_clrjit_time) { + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrjit_time", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st_clrjit_time = rrdset_create_localhost( + "netframework", + id, + NULL, + "jit", + "netframework.clrjit_time", + "Time spent in JIT compilation", + "percentage", + PLUGIN_WINDOWS_NAME, + "PerflibNetFramework", + PRIO_NETFRAMEWORK_CLR_JIT_TIME, + update_every, + RRDSET_TYPE_LINE); + + p->rd_clrjit_time = rrddim_add(p->st_clrjit_time, "time", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE); + + rrdlabels_add(p->st_clrjit_time->rrdlabels, "process", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + double percTime = (double)p->NETFrameworkCLRJITPercentTime.current.Data; + percTime /= (double)p->NETFrameworkCLRJITFrequencyTime.current.Data; + percTime *= 100; + rrddim_set_by_pointer(p->st_clrjit_time, p->rd_clrjit_time, (collected_number)percTime); + rrdset_done(p->st_clrjit_time); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRJITStandardFailures)) { + if (!p->st_clrjit_standard_failures) { + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrjit_standard_failures", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st_clrjit_standard_failures = rrdset_create_localhost( + "netframework", + id, + NULL, + "jit", + "netframework.clrjit_standard_failures", + "JIT compiler failures", + "failures/s", + PLUGIN_WINDOWS_NAME, + "PerflibNetFramework", + PRIO_NETFRAMEWORK_CLR_JIT_STANDARD_FAILURES, + update_every, + RRDSET_TYPE_LINE); + + p->rd_clrjit_standard_failures = + rrddim_add(p->st_clrjit_standard_failures, "failures", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add( + p->st_clrjit_standard_failures->rrdlabels, "process", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_clrjit_standard_failures, + p->rd_clrjit_standard_failures, + (collected_number)p->NETFrameworkCLRJITStandardFailures.current.Data); + rrdset_done(p->st_clrjit_standard_failures); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRJITIlBytes)) { + if (!p->st_clrjit_il_bytes) { + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrjit_il_bytes", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st_clrjit_il_bytes = rrdset_create_localhost( + "netframework", + id, + NULL, + "jit", + "netframework.clrjit_il_bytes", + "Compiled Microsoft intermediate language (MSIL) bytes", + "bytes/s", + PLUGIN_WINDOWS_NAME, + "PerflibNetFramework", + PRIO_NETFRAMEWORK_CLR_JIT_IL_BYTES, + update_every, + RRDSET_TYPE_LINE); + + p->rd_clrjit_il_bytes = + rrddim_add(p->st_clrjit_il_bytes, "compiled_msil", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(p->st_clrjit_il_bytes->rrdlabels, "process", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_clrjit_il_bytes, + p->rd_clrjit_il_bytes, + (collected_number)p->NETFrameworkCLRJITIlBytes.current.Data); + rrdset_done(p->st_clrjit_il_bytes); + } + } +} + +static void netdata_framework_clr_loading(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, int update_every) { + char id[RRD_ID_LENGTH_MAX + 1]; + PERF_INSTANCE_DEFINITION *pi = NULL; + for (LONG i = 0; i < pObjectType->NumInstances; i++) { + pi = perflibForEachInstance(pDataBlock, pObjectType, pi); + if (!pi) + break; + + if (!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer))) + strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1); + + if (strcasecmp(windows_shared_buffer, "_Global_") == 0) + continue; + + struct net_framework_instances *p = dictionary_set(processes, windows_shared_buffer, NULL, sizeof(*p)); + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRLoadingHeapSize)) { + if (!p->st_clrloading_heap_size) { + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrloading_loader_heap_size", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st_clrloading_heap_size = rrdset_create_localhost( + "netframework", + id, + NULL, + "loading", + "netframework.clrloading_loader_heap_size", + "Memory committed by class loader", + "bytes", + PLUGIN_WINDOWS_NAME, + "PerflibNetFramework", + PRIO_NETFRAMEWORK_CLR_LOADING_HEAP_SIZE, + update_every, + RRDSET_TYPE_LINE); + + p->rd_clrloading_heap_size = + rrddim_add(p->st_clrloading_heap_size, "committed", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + rrdlabels_add( + p->st_clrloading_heap_size->rrdlabels, "process", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_clrloading_heap_size, + p->rd_clrloading_heap_size, + (collected_number)p->NETFrameworkCLRLoadingHeapSize.current.Data); + rrdset_done(p->st_clrloading_heap_size); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRLoadingAppDomainsLoaded)) { + if (!p->st_clrloading_app_domains_loaded) { + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrloading_appdomains_loaded", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st_clrloading_app_domains_loaded = rrdset_create_localhost( + "netframework", + id, + NULL, + "loading", + "netframework.clrloading_appdomains_loaded", + "Loaded application domains", + "domain/s", + PLUGIN_WINDOWS_NAME, + "PerflibNetFramework", + PRIO_NETFRAMEWORK_CLR_LOADING_APP_DOMAINS_LOADED, + update_every, + RRDSET_TYPE_LINE); + + p->rd_clrloading_app_domains_loaded = + rrddim_add(p->st_clrloading_app_domains_loaded, "loaded", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add( + p->st_clrloading_app_domains_loaded->rrdlabels, "process", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_clrloading_app_domains_loaded, + p->rd_clrloading_app_domains_loaded, + (collected_number)p->NETFrameworkCLRLoadingAppDomainsLoaded.current.Data); + rrdset_done(p->st_clrloading_app_domains_loaded); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRLoadingAppDomainsUnloaded)) { + if (!p->st_clrloading_app_domains_unloaded) { + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrloading_appdomains_unloaded", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st_clrloading_app_domains_unloaded = rrdset_create_localhost( + "netframework", + id, + NULL, + "loading", + "netframework.clrloading_appdomains_unloaded", + "Unloaded application domains", + "domain/s", + PLUGIN_WINDOWS_NAME, + "PerflibNetFramework", + PRIO_NETFRAMEWORK_CLR_LOADING_APP_DOMAINS_UNLOADED, + update_every, + RRDSET_TYPE_LINE); + + p->rd_clrloading_app_domains_unloaded = rrddim_add( + p->st_clrloading_app_domains_unloaded, "unloaded", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add( + p->st_clrloading_app_domains_unloaded->rrdlabels, + "process", + windows_shared_buffer, + RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_clrloading_app_domains_unloaded, + p->rd_clrloading_app_domains_unloaded, + (collected_number)p->NETFrameworkCLRLoadingAppDomainsUnloaded.current.Data); + rrdset_done(p->st_clrloading_app_domains_unloaded); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRLoadingAssembliesLoaded)) { + if (!p->st_clrloading_assemblies_loaded) { + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrloading_assemblies_loaded", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st_clrloading_assemblies_loaded = rrdset_create_localhost( + "netframework", + id, + NULL, + "loading", + "netframework.clrloading_assemblies_loaded", + "Loaded assemblies", + "assemblies/s", + PLUGIN_WINDOWS_NAME, + "PerflibNetFramework", + PRIO_NETFRAMEWORK_CLR_LOADING_ASSEMBLIES_LOADED, + update_every, + RRDSET_TYPE_LINE); + + p->rd_clrloading_assemblies_loaded = + rrddim_add(p->st_clrloading_assemblies_loaded, "loaded", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add( + p->st_clrloading_assemblies_loaded->rrdlabels, "process", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_clrloading_assemblies_loaded, + p->rd_clrloading_assemblies_loaded, + (collected_number)p->NETFrameworkCLRLoadingAssembliesLoaded.current.Data); + rrdset_done(p->st_clrloading_assemblies_loaded); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRLoadingClassesLoaded)) { + if (!p->st_clrloading_classes_loaded) { + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrloading_classes_loaded", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st_clrloading_classes_loaded = rrdset_create_localhost( + "netframework", + id, + NULL, + "loading", + "netframework.clrloading_classes_loaded", + "Loaded classes in all assemblies", + "classes/s", + PLUGIN_WINDOWS_NAME, + "PerflibNetFramework", + PRIO_NETFRAMEWORK_CLR_LOADING_CLASSES_LOADED, + update_every, + RRDSET_TYPE_LINE); + + p->rd_clrloading_classes_loaded = + rrddim_add(p->st_clrloading_classes_loaded, "loaded", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add( + p->st_clrloading_classes_loaded->rrdlabels, "process", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_clrloading_classes_loaded, + p->rd_clrloading_classes_loaded, + (collected_number)p->NETFrameworkCLRLoadingClassesLoaded.current.Data); + rrdset_done(p->st_clrloading_classes_loaded); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRLoadingClassLoadFailure)) { + if (!p->st_clrloading_class_load_failure) { + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrloading_class_load_failure", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st_clrloading_class_load_failure = rrdset_create_localhost( + "netframework", + id, + NULL, + "loading", + "netframework.clrloading_class_load_failures", + "Class load failures", + "failures/s", + PLUGIN_WINDOWS_NAME, + "PerflibNetFramework", + PRIO_NETFRAMEWORK_CLR_LOADING_CLASS_LOAD_FAILURE, + update_every, + RRDSET_TYPE_LINE); + + p->rd_clrloading_class_load_failure = rrddim_add( + p->st_clrloading_class_load_failure, "class_load", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add( + p->st_clrloading_class_load_failure->rrdlabels, "process", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_clrloading_class_load_failure, + p->rd_clrloading_class_load_failure, + (collected_number)p->NETFrameworkCLRLoadingClassLoadFailure.current.Data); + rrdset_done(p->st_clrloading_class_load_failure); + } + } +} + +struct netdata_netframework_objects { + char *object; + void (*fnct)(PERF_DATA_BLOCK *, PERF_OBJECT_TYPE *, int); +} netframewrk_obj[NETDATA_NETFRAMEWORK_END] = { + {.fnct = netdata_framework_clr_exceptions, .object = ".NET CLR Exceptions"}, + {.fnct = netdata_framework_clr_interop, .object = ".NET CLR Interop"}, + {.fnct = netdata_framework_clr_jit, .object = ".NET CLR Jit"}, + {.fnct = netdata_framework_clr_loading, .object = ".NET CLR Loading"}}; + +int do_PerflibNetFramework(int update_every, usec_t dt __maybe_unused) { + static bool initialized = false; + + if (unlikely(!initialized)) { + initialize(); + initialized = true; + } + + int i; + for (i = 0; i < NETDATA_NETFRAMEWORK_END; i++) { + DWORD id = RegistryFindIDByName(netframewrk_obj[i].object); + if (id == PERFLIB_REGISTRY_NAME_NOT_FOUND) + continue; + + PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id); + if (!pDataBlock) + continue; + + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, netframewrk_obj[i].object); + if (!pObjectType) + continue; + + netframewrk_obj[i].fnct(pDataBlock, pObjectType, update_every); + } + + return 0; +} diff --git a/src/collectors/windows.plugin/perflib-network.c b/src/collectors/windows.plugin/perflib-network.c index ecadd1e87..55d873b6f 100644 --- a/src/collectors/windows.plugin/perflib-network.c +++ b/src/collectors/windows.plugin/perflib-network.c @@ -1,453 +1,1047 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "windows_plugin.h" -#include "windows-internals.h" - -// -------------------------------------------------------------------------------------------------------------------- -// network protocols - -struct network_protocol { - const char *protocol; - - struct { - COUNTER_DATA received; - COUNTER_DATA sent; - COUNTER_DATA delivered; - COUNTER_DATA forwarded; - RRDSET *st; - RRDDIM *rd_received; - RRDDIM *rd_sent; - RRDDIM *rd_forwarded; - RRDDIM *rd_delivered; - const char *type; - const char *id; - const char *family; - const char *context; - const char *title; - long priority; - } packets; - -} networks[] = { - { - .protocol = "IPv4", - .packets = { - .received = { .key = "Datagrams Received/sec" }, - .sent = { .key = "Datagrams Sent/sec" }, - .delivered = { .key = "Datagrams Received Delivered/sec" }, - .forwarded = { .key = "Datagrams Forwarded/sec" }, - .type = "ipv4", - .id = "packets", - .family = "packets", - .context = "ipv4.packets", - .title = "IPv4 Packets", - .priority = NETDATA_CHART_PRIO_IPV4_PACKETS, - }, - }, - { - .protocol = "IPv6", - .packets = { - .received = { .key = "Datagrams Received/sec" }, - .sent = { .key = "Datagrams Sent/sec" }, - .delivered = { .key = "Datagrams Received Delivered/sec" }, - .forwarded = { .key = "Datagrams Forwarded/sec" }, - .type = "ipv6", - .id = "packets", - .family = "packets", - .context = "ip6.packets", - .title = "IPv6 Packets", - .priority = NETDATA_CHART_PRIO_IPV6_PACKETS, - }, - }, - { - .protocol = "TCPv4", - .packets = { - .received = { .key = "Segments Received/sec" }, - .sent = { .key = "Segments Sent/sec" }, - .type = "ipv4", - .id = "tcppackets", - .family = "tcp", - .context = "ipv4.tcppackets", - .title = "IPv4 TCP Packets", - .priority = NETDATA_CHART_PRIO_IPV4_TCP_PACKETS, - }, - }, - { - .protocol = "TCPv6", - .packets = { - .received = { .key = "Segments Received/sec" }, - .sent = { .key = "Segments Sent/sec" }, - .type = "ipv6", - .id = "tcppackets", - .family = "tcp6", - .context = "ipv6.tcppackets", - .title = "IPv6 TCP Packets", - .priority = NETDATA_CHART_PRIO_IPV6_TCP_PACKETS, - }, - }, - { - .protocol = "UDPv4", - .packets = { - .received = { .key = "Datagrams Received/sec" }, - .sent = { .key = "Datagrams Sent/sec" }, - .type = "ipv4", - .id = "udppackets", - .family = "udp", - .context = "ipv4.udppackets", - .title = "IPv4 UDP Packets", - .priority = NETDATA_CHART_PRIO_IPV4_UDP_PACKETS, - }, - }, - { - .protocol = "UDPv6", - .packets = { - .received = { .key = "Datagrams Received/sec" }, - .sent = { .key = "Datagrams Sent/sec" }, - .type = "ipv6", - .id = "udppackets", - .family = "udp6", - .context = "ipv6.udppackets", - .title = "IPv6 UDP Packets", - .priority = NETDATA_CHART_PRIO_IPV6_UDP_PACKETS, - }, - }, - { - .protocol = "ICMP", - .packets = { - .received = { .key = "Messages Received/sec" }, - .sent = { .key = "Messages Sent/sec" }, - .type = "ipv4", - .id = "icmp", - .family = "icmp", - .context = "ipv4.icmp", - .title = "IPv4 ICMP Packets", - .priority = NETDATA_CHART_PRIO_IPV4_ICMP_PACKETS, - }, - }, - { - .protocol = "ICMPv6", - .packets = { - .received = { .key = "Messages Received/sec" }, - .sent = { .key = "Messages Sent/sec" }, - .type = "ipv6", - .id = "icmp", - .family = "icmp6", - .context = "ipv6.icmp", - .title = "IPv6 ICMP Packets", - .priority = NETDATA_CHART_PRIO_IPV6_ICMP_PACKETS, - }, - }, - - // terminator - { - .protocol = NULL, - } -}; - -struct network_protocol tcp46 = { - .packets = { - .type = "ip", - .id = "tcppackets", - .family = "tcp", - .context = "ip.tcppackets", - .title = "TCP Packets", - .priority = NETDATA_CHART_PRIO_IP_TCP_PACKETS, - } -}; - -static void protocol_packets_chart_update(struct network_protocol *p, int update_every) { - if(!p->packets.st) { - p->packets.st = rrdset_create_localhost( - p->packets.type - , p->packets.id - , NULL - , p->packets.family - , NULL - , p->packets.title - , "packets/s" - , PLUGIN_WINDOWS_NAME - , "PerflibNetwork" - , p->packets.priority - , update_every - , RRDSET_TYPE_AREA - ); - - p->packets.rd_received = rrddim_add(p->packets.st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - p->packets.rd_sent = rrddim_add(p->packets.st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - - if(p->packets.forwarded.key) - p->packets.rd_forwarded = rrddim_add(p->packets.st, "forwarded", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - - if(p->packets.delivered.key) - p->packets.rd_delivered = rrddim_add(p->packets.st, "delivered", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - } - - if(p->packets.received.updated) - rrddim_set_by_pointer(p->packets.st, p->packets.rd_received, (collected_number)p->packets.received.current.Data); - - if(p->packets.sent.updated) - rrddim_set_by_pointer(p->packets.st, p->packets.rd_sent, (collected_number)p->packets.sent.current.Data); - - if(p->packets.forwarded.key && p->packets.forwarded.updated) - rrddim_set_by_pointer(p->packets.st, p->packets.rd_forwarded, (collected_number)p->packets.forwarded.current.Data); - - if(p->packets.delivered.key && p->packets.delivered.updated) - rrddim_set_by_pointer(p->packets.st, p->packets.rd_delivered, (collected_number)p->packets.delivered.current.Data); - - rrdset_done(p->packets.st); -} - -static bool do_network_protocol(PERF_DATA_BLOCK *pDataBlock, int update_every, struct network_protocol *p) { - if(!p || !p->protocol) return false; - - PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, p->protocol); - if(!pObjectType) return false; - - size_t packets = 0; - if(p->packets.received.key) - packets += perflibGetObjectCounter(pDataBlock, pObjectType, &p->packets.received) ? 1 : 0; - - if(p->packets.sent.key) - packets += perflibGetObjectCounter(pDataBlock, pObjectType, &p->packets.sent) ? 1 : 0; - - if(p->packets.delivered.key) - packets += perflibGetObjectCounter(pDataBlock, pObjectType, &p->packets.delivered) ? 1 :0; - - if(p->packets.forwarded.key) - packets += perflibGetObjectCounter(pDataBlock, pObjectType, &p->packets.forwarded) ? 1 : 0; - - if(packets) - protocol_packets_chart_update(p, update_every); - - return true; -} - -// -------------------------------------------------------------------------------------------------------------------- -// network interfaces - -struct network_interface { - bool collected_metadata; - - struct { - COUNTER_DATA received; - COUNTER_DATA sent; - - RRDSET *st; - RRDDIM *rd_received; - RRDDIM *rd_sent; - } packets; - - struct { - COUNTER_DATA received; - COUNTER_DATA sent; - - RRDSET *st; - RRDDIM *rd_received; - RRDDIM *rd_sent; - } traffic; -}; - -static DICTIONARY *physical_interfaces = NULL, *virtual_interfaces = NULL; - -static void network_interface_init(struct network_interface *ni) { - ni->packets.received.key = "Packets Received/sec"; - ni->packets.sent.key = "Packets Sent/sec"; - - ni->traffic.received.key = "Bytes Received/sec"; - ni->traffic.sent.key = "Bytes Sent/sec"; -} - -void dict_interface_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { - struct network_interface *ni = value; - network_interface_init(ni); -} - -static void initialize(void) { - physical_interfaces = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | - DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct network_interface)); - - virtual_interfaces = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | - DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct network_interface)); - - dictionary_register_insert_callback(physical_interfaces, dict_interface_insert_cb, NULL); - dictionary_register_insert_callback(virtual_interfaces, dict_interface_insert_cb, NULL); -} - -static void add_interface_labels(RRDSET *st, const char *name, bool physical) { - rrdlabels_add(st->rrdlabels, "device", name, RRDLABEL_SRC_AUTO); - rrdlabels_add(st->rrdlabels, "interface_type", physical ? "real" : "virtual", RRDLABEL_SRC_AUTO); -} - -static bool is_physical_interface(const char *name) { - void *d = dictionary_get(physical_interfaces, name); - return d ? true : false; -} - -static bool do_network_interface(PERF_DATA_BLOCK *pDataBlock, int update_every, bool physical) { - DICTIONARY *dict = physical_interfaces; - - PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, physical ? "Network Interface" : "Network Adapter"); - if(!pObjectType) return false; - - uint64_t total_received = 0, total_sent = 0; - - PERF_INSTANCE_DEFINITION *pi = NULL; - for(LONG i = 0; i < pObjectType->NumInstances ; i++) { - pi = perflibForEachInstance(pDataBlock, pObjectType, pi); - if(!pi) break; - - if(!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer))) - strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1); - - if(strcasecmp(windows_shared_buffer, "_Total") == 0) - continue; - - if(!physical && is_physical_interface(windows_shared_buffer)) - // this virtual interface is already reported as physical interface - continue; - - struct network_interface *d = dictionary_set(dict, windows_shared_buffer, NULL, sizeof(*d)); - - if(!d->collected_metadata) { - // TODO - get metadata about the network interface - d->collected_metadata = true; - } - - if(perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->traffic.received) && - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->traffic.sent)) { - - if(d->traffic.received.current.Data == 0 && d->traffic.sent.current.Data == 0) - // this interface has not received or sent any traffic - continue; - - if (unlikely(!d->traffic.st)) { - d->traffic.st = rrdset_create_localhost( - "net", - windows_shared_buffer, - NULL, - windows_shared_buffer, - "net.net", - "Bandwidth", - "kilobits/s", - PLUGIN_WINDOWS_NAME, - "PerflibNetwork", - NETDATA_CHART_PRIO_FIRST_NET_IFACE, - update_every, - RRDSET_TYPE_AREA); - - rrdset_flag_set(d->traffic.st, RRDSET_FLAG_DETAIL); - - add_interface_labels(d->traffic.st, windows_shared_buffer, physical); - - d->traffic.rd_received = rrddim_add(d->traffic.st, "received", NULL, 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); - d->traffic.rd_sent = rrddim_add(d->traffic.st, "sent", NULL, -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); - } - - total_received += d->traffic.received.current.Data; - total_sent += d->traffic.sent.current.Data; - - rrddim_set_by_pointer(d->traffic.st, d->traffic.rd_received, (collected_number)d->traffic.received.current.Data); - rrddim_set_by_pointer(d->traffic.st, d->traffic.rd_sent, (collected_number)d->traffic.sent.current.Data); - rrdset_done(d->traffic.st); - } - - if(perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->packets.received) && - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->packets.sent)) { - - if (unlikely(!d->packets.st)) { - d->packets.st = rrdset_create_localhost( - "net_packets", - windows_shared_buffer, - NULL, - windows_shared_buffer, - "net.packets", - "Packets", - "packets/s", - PLUGIN_WINDOWS_NAME, - "PerflibNetwork", - NETDATA_CHART_PRIO_FIRST_NET_IFACE + 1, - update_every, - RRDSET_TYPE_LINE); - - rrdset_flag_set(d->packets.st, RRDSET_FLAG_DETAIL); - - add_interface_labels(d->traffic.st, windows_shared_buffer, physical); - - d->packets.rd_received = rrddim_add(d->packets.st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - d->packets.rd_sent = rrddim_add(d->packets.st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } - - rrddim_set_by_pointer(d->packets.st, d->packets.rd_received, (collected_number)d->packets.received.current.Data); - rrddim_set_by_pointer(d->packets.st, d->packets.rd_sent, (collected_number)d->packets.sent.current.Data); - rrdset_done(d->packets.st); - } - } - - if(physical) { - static RRDSET *st = NULL; - static RRDDIM *rd_received = NULL, *rd_sent = NULL; - - if (unlikely(!st)) { - st = rrdset_create_localhost( - "system", - "net", - NULL, - "network", - "system.net", - "Physical Network Interfaces Aggregated Bandwidth", - "kilobits/s", - PLUGIN_WINDOWS_NAME, - "PerflibNetwork", - NETDATA_CHART_PRIO_SYSTEM_NET, - update_every, - RRDSET_TYPE_AREA); - - rd_received = rrddim_add(st, "received", NULL, 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); - rd_sent = rrddim_add(st, "sent", NULL, -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); - } - - rrddim_set_by_pointer(st, rd_received, (collected_number)total_received); - rrddim_set_by_pointer(st, rd_sent, (collected_number)total_sent); - rrdset_done(st); - } - - return true; -} - -int do_PerflibNetwork(int update_every, usec_t dt __maybe_unused) { - static bool initialized = false; - - if(unlikely(!initialized)) { - initialize(); - initialized = true; - } - - DWORD id = RegistryFindIDByName("Network Interface"); - if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND) - return -1; - - PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id); - if(!pDataBlock) return -1; - - do_network_interface(pDataBlock, update_every, true); - do_network_interface(pDataBlock, update_every, false); - - struct network_protocol *tcp4 = NULL, *tcp6 = NULL; - for(size_t i = 0; networks[i].protocol ;i++) { - do_network_protocol(pDataBlock, update_every, &networks[i]); - - if(!tcp4 && strcmp(networks[i].protocol, "TCPv4") == 0) - tcp4 = &networks[i]; - if(!tcp6 && strcmp(networks[i].protocol, "TCPv6") == 0) - tcp6 = &networks[i]; - } - - if(tcp4 && tcp6) { - tcp46.packets.received = tcp4->packets.received; - tcp46.packets.sent = tcp4->packets.sent; - tcp46.packets.received.current.Data += tcp6->packets.received.current.Data; - tcp46.packets.sent.current.Data += tcp6->packets.sent.current.Data; - protocol_packets_chart_update(&tcp46, update_every); - } - - return 0; -} +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "windows_plugin.h" +#include "windows-internals.h" + +#define ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, counter) \ + do { \ + if ((p)->packets.counter.key) { \ + packets += perflibGetObjectCounter((pDataBlock), (pObjectType), &(p)->packets.counter) ? 1 : 0; \ + } \ + } while (0) + +#define SET_DIM_IF_KEY_AND_UPDATED(p, field) \ + do { \ + if ((p)->packets.field.key && (p)->packets.field.updated) { \ + rrddim_set_by_pointer( \ + (p)->packets.st, (p)->packets.rd_##field, (collected_number)(p)->packets.field.current.Data); \ + } \ + } while (0) + +#define ADD_RRD_DIM_IF_KEY(packet_field, id, name, multiplier, algorithm) \ + do { \ + if (p->packets.packet_field.key) \ + p->packets.rd_##packet_field = rrddim_add(st, id, name, multiplier, 1, algorithm); \ + } while (0) + +// -------------------------------------------------------------------------------------------------------------------- +// network protocols + +struct network_protocol { + const char *protocol; + + struct { + COUNTER_DATA received; + COUNTER_DATA sent; + COUNTER_DATA delivered; + COUNTER_DATA forwarded; + + COUNTER_DATA InDiscards; + COUNTER_DATA OutDiscards; + COUNTER_DATA InHdrErrors; + COUNTER_DATA InAddrErrors; + COUNTER_DATA InUnknownProtos; + COUNTER_DATA InTooBigErrors; + COUNTER_DATA InTruncatedPkts; + COUNTER_DATA InNoRoutes; + COUNTER_DATA OutNoRoutes; + + COUNTER_DATA InEchoReps; + COUNTER_DATA OutEchoReps; + COUNTER_DATA InDestUnreachs; + COUNTER_DATA OutDestUnreachs; + COUNTER_DATA InRedirects; + COUNTER_DATA OutRedirects; + COUNTER_DATA InEchos; + COUNTER_DATA OutEchos; + COUNTER_DATA InRouterAdvert; + COUNTER_DATA OutRouterAdvert; + COUNTER_DATA InRouterSelect; + COUNTER_DATA OutRouterSelect; + COUNTER_DATA InTimeExcds; + COUNTER_DATA OutTimeExcds; + COUNTER_DATA InParmProbs; + COUNTER_DATA OutParmProbs; + COUNTER_DATA InTimestamps; + COUNTER_DATA OutTimestamps; + COUNTER_DATA InTimestampReps; + COUNTER_DATA OutTimestampReps; + + RRDSET *st; + RRDDIM *rd_received; + RRDDIM *rd_sent; + RRDDIM *rd_forwarded; + RRDDIM *rd_delivered; + + RRDDIM *rd_InDiscards; + RRDDIM *rd_OutDiscards; + RRDDIM *rd_InHdrErrors; + RRDDIM *rd_InAddrErrors; + RRDDIM *rd_InUnknownProtos; + RRDDIM *rd_InTooBigErrors; + RRDDIM *rd_InTruncatedPkts; + RRDDIM *rd_InNoRoutes; + RRDDIM *rd_OutNoRoutes; + + RRDDIM *rd_InEchoReps; + RRDDIM *rd_OutEchoReps; + RRDDIM *rd_InDestUnreachs; + RRDDIM *rd_OutDestUnreachs; + RRDDIM *rd_InRedirects; + RRDDIM *rd_OutRedirects; + RRDDIM *rd_InEchos; + RRDDIM *rd_OutEchos; + RRDDIM *rd_InRouterAdvert; + RRDDIM *rd_OutRouterAdvert; + RRDDIM *rd_InRouterSelect; + RRDDIM *rd_OutRouterSelect; + RRDDIM *rd_InTimeExcds; + RRDDIM *rd_OutTimeExcds; + RRDDIM *rd_InParmProbs; + RRDDIM *rd_OutParmProbs; + RRDDIM *rd_InTimestamps; + RRDDIM *rd_OutTimestamps; + RRDDIM *rd_InTimestampReps; + RRDDIM *rd_OutTimestampReps; + + const char *type; + const char *id; + const char *family; + const char *context; + const char *title; + long priority; + } packets; + +} networks[] = { + { + .protocol = "IPv4", + .packets = { + .received = { .key = "Datagrams Received/sec" }, + .sent = { .key = "Datagrams Sent/sec" }, + .delivered = { .key = "Datagrams Received Delivered/sec" }, + .forwarded = { .key = "Datagrams Forwarded/sec" }, + .type = "ipv4", + .id = "packets", + .family = "packets", + .context = "ipv4.packets", + .title = "IPv4 Packets", + .priority = NETDATA_CHART_PRIO_IPV4_PACKETS, + }, + }, + { + .protocol = "IPv6", + .packets = { + .received = { .key = "Datagrams Received/sec" }, + .sent = { .key = "Datagrams Sent/sec" }, + .delivered = { .key = "Datagrams Received Delivered/sec" }, + .forwarded = { .key = "Datagrams Forwarded/sec" }, + .type = "ipv6", + .id = "packets", + .family = "packets", + .context = "ip6.packets", + .title = "IPv6 Packets", + .priority = NETDATA_CHART_PRIO_IPV6_PACKETS, + }, + }, + { + .protocol = "TCPv4", + .packets = { + .received = { .key = "Segments Received/sec" }, + .sent = { .key = "Segments Sent/sec" }, + .type = "ipv4", + .id = "tcppackets", + .family = "tcp", + .context = "ipv4.tcppackets", + .title = "IPv4 TCP Packets", + .priority = NETDATA_CHART_PRIO_IPV4_TCP_PACKETS, + }, + }, + { + .protocol = "TCPv6", + .packets = { + .received = { .key = "Segments Received/sec" }, + .sent = { .key = "Segments Sent/sec" }, + .type = "ipv6", + .id = "tcppackets", + .family = "tcp6", + .context = "ipv6.tcppackets", + .title = "IPv6 TCP Packets", + .priority = NETDATA_CHART_PRIO_IPV6_TCP_PACKETS, + }, + }, + { + .protocol = "UDPv4", + .packets = { + .received = { .key = "Datagrams Received/sec" }, + .sent = { .key = "Datagrams Sent/sec" }, + .type = "ipv4", + .id = "udppackets", + .family = "udp", + .context = "ipv4.udppackets", + .title = "IPv4 UDP Packets", + .priority = NETDATA_CHART_PRIO_IPV4_UDP_PACKETS, + }, + }, + { + .protocol = "UDPv6", + .packets = { + .received = { .key = "Datagrams Received/sec" }, + .sent = { .key = "Datagrams Sent/sec" }, + .type = "ipv6", + .id = "udppackets", + .family = "udp6", + .context = "ipv6.udppackets", + .title = "IPv6 UDP Packets", + .priority = NETDATA_CHART_PRIO_IPV6_UDP_PACKETS, + }, + }, + { + .protocol = "ICMP", + .packets = { + .received = { .key = "Messages Received/sec" }, + .sent = { .key = "Messages Sent/sec" }, + .type = "ipv4", + .id = "icmp", + .family = "icmp", + .context = "ipv4.icmp", + .title = "IPv4 ICMP Packets", + .priority = NETDATA_CHART_PRIO_IPV4_ICMP_PACKETS, + }, + }, + { + .protocol = "ICMPv6", + .packets = { + .received = { .key = "Messages Received/sec" }, + .sent = { .key = "Messages Sent/sec" }, + .type = "ipv6", + .id = "icmp", + .family = "icmp6", + .context = "ipv6.icmp", + .title = "IPv6 ICMP Packets", + .priority = NETDATA_CHART_PRIO_IPV6_ICMP_PACKETS, + }, + }, + + { + .protocol = "IPv4", + .packets = { + .InDiscards = { .key = "Datagrams Received Discarded" }, + .OutDiscards = { .key = "Datagrams Outbound Discarded" }, + .OutNoRoutes = { .key = "Datagrams Outbound No Route" }, + .InAddrErrors = { .key = "Datagrams Received Address Errors" }, + .InHdrErrors = { .key = "Datagrams Received Header Errors" }, + .InUnknownProtos = { .key = "Datagrams Received Unknown Protocol" }, + .type = "ipv4", + .id = "errors", + .family = "errors", + .context = "ipv4.errors", + .title = "IPv4 errors", + .priority = NETDATA_CHART_PRIO_IPV4_ERRORS, + }, + }, + { + .protocol = "IPv6", + .packets = { + .InDiscards = { .key = "Datagrams Received Discarded" }, + .OutDiscards = { .key = "Datagrams Outbound Discarded" }, + .OutNoRoutes = { .key = "Datagrams Outbound No Route" }, + .InAddrErrors = { .key = "Datagrams Received Address Errors" }, + .InHdrErrors = { .key = "Datagrams Received Header Errors" }, + .InUnknownProtos = { .key = "Datagrams Received Unknown Protocol" }, + .type = "ipv6", + .id = "errors", + .family = "errors", + .context = "ipv6.errors", + .title = "IPv6 errors", + .priority = NETDATA_CHART_PRIO_IPV6_ERRORS, + }, + }, + { + .protocol = "ICMP", + .packets = + { + .InEchoReps = {.key = "Received Echo Reply/sec"}, + .OutEchoReps = {.key = "Received Echo Reply/sec"}, + .InDestUnreachs = {.key = "Received Dest. Unreachable"}, + .OutDestUnreachs = {.key = "Sent Destination Unreachable"}, + .InRedirects = {.key = "Received Redirect/sec"}, + .OutRedirects = {.key = "Sent Redirect/sec"}, + .InEchos = {.key = "Received Echo/sec"}, + .OutEchos = {.key = "Sent Echo/sec"}, + .InRouterAdvert = {.key = NULL}, + .OutRouterAdvert = {.key = NULL}, + .InRouterSelect = {.key = NULL}, + .OutRouterSelect = {.key = NULL}, + .InTimeExcds = {.key = "Received Time Exceeded"}, + .OutTimeExcds = {.key = "Sent Time Exceeded"}, + .InParmProbs = {.key = "Received Parameter Problem"}, + .OutParmProbs = {.key = "Sent Parameter Problem"}, + .InTimestamps = {.key = "Received Timestamp/sec"}, + .OutTimestamps = {.key = "Sent Timestamp/sec"}, + .InTimestampReps = {.key = "Received Timestamp Reply/sec"}, + .OutTimestampReps = {.key = "Sent Timestamp Reply/sec"}, + + .type = "ipv4", + .id = "icmpmsg", + .family = "icmp", + .context = "ipv4.icmpmsg", + .title = "IPv4 ICMP Packets", + .priority = NETDATA_CHART_PRIO_IPV4_ICMP_MESSAGES, + }, + }, + { + .protocol = "ICMPv6", + .packets = + { + .InEchoReps = {.key = "Received Echo Reply/sec"}, + .OutEchoReps = {.key = "Received Echo Reply/sec"}, + .InDestUnreachs = {.key = "Received Dest. Unreachable"}, + .OutDestUnreachs = {.key = "Sent Destination Unreachable"}, + .InRedirects = {.key = "Received Redirect/sec"}, + .OutRedirects = {.key = "Sent Redirect/sec"}, + .InEchos = {.key = "Received Echo/sec"}, + .OutEchos = {.key = "Sent Echo/sec"}, + .InRouterAdvert = {.key = NULL}, + .OutRouterAdvert = {.key = NULL}, + .InRouterSelect = {.key = NULL}, + .OutRouterSelect = {.key = NULL}, + .InTimeExcds = {.key = "Received Time Exceeded"}, + .OutTimeExcds = {.key = "Sent Time Exceeded"}, + .InParmProbs = {.key = "Received Parameter Problem"}, + .OutParmProbs = {.key = "Sent Parameter Problem"}, + .InTimestamps = {.key = "Received Timestamp/sec"}, + .OutTimestamps = {.key = "Sent Timestamp/sec"}, + .InTimestampReps = {.key = "Received Timestamp Reply/sec"}, + .OutTimestampReps = {.key = "Sent Timestamp Reply/sec"}, + + .type = "ipv6", + .id = "icmpmsg", + .family = "icmp", + .context = "ipv6.icmpmsg", + .title = "IPv6 ICMP Packets", + .priority = NETDATA_CHART_PRIO_IPV6_ICMP_MESSAGES, + }, + }, + + // terminator + { + .protocol = NULL, + } +}; + +struct network_protocol tcp46 = { + .packets = { + .type = "ip", + .id = "tcppackets", + .family = "tcp", + .context = "ip.tcppackets", + .title = "TCP Packets", + .priority = NETDATA_CHART_PRIO_IP_TCP_PACKETS, + } +}; + +static void protocol_packets_chart_update(struct network_protocol *p, int update_every) { + if(!p->packets.st) { + p->packets.st = rrdset_create_localhost( + p->packets.type + , p->packets.id + , NULL + , p->packets.family + , NULL + , p->packets.title + , "packets/s" + , PLUGIN_WINDOWS_NAME + , "PerflibNetwork" + , p->packets.priority + , update_every + , RRDSET_TYPE_AREA + ); + + RRDSET *st = p->packets.st; + + ADD_RRD_DIM_IF_KEY(received, "received", NULL, 1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(sent, "sent", NULL, -1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(forwarded, "forwarded", NULL, -1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(delivered, "delivered", NULL, 1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(InDiscards, "InDiscards", NULL, 1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(OutDiscards, "OutDiscards", NULL, -1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(InHdrErrors, "InHdrErrors", NULL, 1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(InAddrErrors, "InAddrErrors", NULL, 1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(InUnknownProtos, "InUnknownProtos", NULL, 1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(InTooBigErrors, "InTooBigErrors", NULL, 1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(InTruncatedPkts, "InTruncatedPkts", NULL, 1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(InNoRoutes, "InNoRoutes", NULL, 1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(OutNoRoutes, "OutNoRoutes", NULL, -1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(InEchoReps, "InType0", "InEchoReps", 1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(OutEchoReps, "OutType0", "OutEchoReps", -1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(InDestUnreachs, "InType3", "InDestUnreachs", 1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(OutDestUnreachs, "OutType3", "OutDestUnreachs", -1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(InRedirects, "InType5", "InRedirects", 1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(OutRedirects, "OutType5", "OutRedirects", -1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(InEchos, "InType8", "InEchos", 1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(OutEchos, "OutType8", "OutEchos", -1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(InRouterAdvert, "InType9", "InRouterAdvert", 1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(OutRouterAdvert, "OutType9", "OutRouterAdvert", -1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(InRouterSelect, "InType10", "InRouterSelect", 1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(OutRouterSelect, "OutType10", "OutRouterSelect", -1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(InTimeExcds, "InType11", "InTimeExcds", 1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(OutTimeExcds, "OutType11", "OutTimeExcds", -1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(InParmProbs, "InType12", "InParmProbs", 1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(OutParmProbs, "OutType12", "OutParmProbs", -1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(InTimestamps, "InType13", "InTimestamps", 1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(OutTimestamps, "OutType13", "OutTimestamps", -1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(InTimestampReps, "InType14", "InTimestampReps", 1, RRD_ALGORITHM_INCREMENTAL); + ADD_RRD_DIM_IF_KEY(OutTimestampReps, "OutType14", "OutTimestampReps", -1, RRD_ALGORITHM_INCREMENTAL); + + } + + SET_DIM_IF_KEY_AND_UPDATED(p, received); + SET_DIM_IF_KEY_AND_UPDATED(p, sent); + + SET_DIM_IF_KEY_AND_UPDATED(p, forwarded); + SET_DIM_IF_KEY_AND_UPDATED(p, delivered); + SET_DIM_IF_KEY_AND_UPDATED(p, InDiscards); + SET_DIM_IF_KEY_AND_UPDATED(p, OutDiscards); + SET_DIM_IF_KEY_AND_UPDATED(p, InHdrErrors); + SET_DIM_IF_KEY_AND_UPDATED(p, InAddrErrors); + SET_DIM_IF_KEY_AND_UPDATED(p, InUnknownProtos); + SET_DIM_IF_KEY_AND_UPDATED(p, InTooBigErrors); + SET_DIM_IF_KEY_AND_UPDATED(p, InTruncatedPkts); + SET_DIM_IF_KEY_AND_UPDATED(p, InNoRoutes); + SET_DIM_IF_KEY_AND_UPDATED(p, OutNoRoutes); + SET_DIM_IF_KEY_AND_UPDATED(p, InEchoReps); + SET_DIM_IF_KEY_AND_UPDATED(p, OutEchoReps); + SET_DIM_IF_KEY_AND_UPDATED(p, InDestUnreachs); + SET_DIM_IF_KEY_AND_UPDATED(p, OutDestUnreachs); + SET_DIM_IF_KEY_AND_UPDATED(p, InRedirects); + SET_DIM_IF_KEY_AND_UPDATED(p, OutRedirects); + SET_DIM_IF_KEY_AND_UPDATED(p, InEchos); + SET_DIM_IF_KEY_AND_UPDATED(p, OutEchos); + SET_DIM_IF_KEY_AND_UPDATED(p, InRouterAdvert); + SET_DIM_IF_KEY_AND_UPDATED(p, OutRouterAdvert); + SET_DIM_IF_KEY_AND_UPDATED(p, InRouterSelect); + SET_DIM_IF_KEY_AND_UPDATED(p, OutRouterSelect); + SET_DIM_IF_KEY_AND_UPDATED(p, InTimeExcds); + SET_DIM_IF_KEY_AND_UPDATED(p, OutTimeExcds); + SET_DIM_IF_KEY_AND_UPDATED(p, InParmProbs); + SET_DIM_IF_KEY_AND_UPDATED(p, OutParmProbs); + SET_DIM_IF_KEY_AND_UPDATED(p, InTimestamps); + SET_DIM_IF_KEY_AND_UPDATED(p, OutTimestamps); + SET_DIM_IF_KEY_AND_UPDATED(p, InTimestampReps); + SET_DIM_IF_KEY_AND_UPDATED(p, OutTimestampReps); + + rrdset_done(p->packets.st); +} + +static bool do_network_protocol(PERF_DATA_BLOCK *pDataBlock, int update_every, struct network_protocol *p) { + if(!p || !p->protocol) return false; + + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, p->protocol); + if(!pObjectType) return false; + + size_t packets = 0; + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, received); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, sent); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, delivered); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, forwarded); + + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InDiscards); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutDiscards); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InHdrErrors); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InAddrErrors); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InUnknownProtos); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InTooBigErrors); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InTruncatedPkts); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InNoRoutes); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutNoRoutes); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InEchoReps); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutEchoReps); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InDestUnreachs); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutDestUnreachs); + + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InRedirects); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutRedirects); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InEchos); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutEchos); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InRouterAdvert); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutRouterAdvert); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InRouterSelect); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutRouterSelect); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InTimeExcds); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutTimeExcds); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InParmProbs); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutParmProbs); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InTimestamps); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutTimestamps); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InTimestampReps); + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutTimestampReps); + + if(packets) + protocol_packets_chart_update(p, update_every); + + return true; +} + +// -------------------------------------------------------------------------------------------------------------------- +// network interfaces + +struct network_interface { + usec_t last_collected; + bool collected_metadata; + + struct { + COUNTER_DATA received; + COUNTER_DATA sent; + + RRDSET *st; + RRDDIM *rd_received; + RRDDIM *rd_sent; + } packets; + + struct { + const RRDVAR_ACQUIRED *chart_var_speed; + + COUNTER_DATA received; + COUNTER_DATA sent; + + RRDSET *st; + RRDDIM *rd_received; + RRDDIM *rd_sent; + } traffic; + + struct { + COUNTER_DATA current_bandwidth; + RRDSET *st; + RRDDIM *rd; + } speed; + + struct { + COUNTER_DATA received; + COUNTER_DATA outbound; + + RRDSET *st; + RRDDIM *rd_received; + RRDDIM *rd_outbound; + } discards; + + struct { + COUNTER_DATA received; + COUNTER_DATA outbound; + + RRDSET *st; + RRDDIM *rd_received; + RRDDIM *rd_outbound; + } errors; + + struct { + COUNTER_DATA length; + RRDSET *st; + RRDDIM *rd; + } queue; + + struct { + COUNTER_DATA connections; + RRDSET *st; + RRDDIM *rd; + } chimney; + + struct { + COUNTER_DATA connections; + COUNTER_DATA packets; + COUNTER_DATA exceptions; + COUNTER_DATA average_packet_size; + + RRDSET *st_connections; + RRDDIM *rd_connections; + + RRDSET *st_packets; + RRDDIM *rd_packets; + + RRDSET *st_exceptions; + RRDDIM *rd_exceptions; + + RRDSET *st_average_packet_size; + RRDDIM *rd_average_packet_size; + } rsc; +}; + +static DICTIONARY *physical_interfaces = NULL, *virtual_interfaces = NULL; + +static void network_interface_init(struct network_interface *d) { + d->packets.received.key = "Packets Received/sec"; + d->packets.sent.key = "Packets Sent/sec"; + d->traffic.received.key = "Bytes Received/sec"; + d->traffic.sent.key = "Bytes Sent/sec"; + d->speed.current_bandwidth.key = "Current Bandwidth"; + d->discards.received.key = "Packets Received Discarded"; + d->discards.outbound.key = "Packets Outbound Discarded"; + d->errors.received.key = "Packets Received Errors"; + d->errors.outbound.key = "Packets Outbound Errors"; + d->queue.length.key = "Output Queue Length"; + d->chimney.connections.key = "Offloaded Connections"; + d->rsc.connections.key = "TCP Active RSC Connections"; + d->rsc.packets.key = "TCP RSC Coalesced Packets/sec"; + d->rsc.exceptions.key = "TCP RSC Exceptions/sec"; + d->rsc.average_packet_size.key = "TCP RSC Average Packet Size"; +} + +static void network_interface_cleanup(struct network_interface *d) { + rrdvar_chart_variable_release(d->traffic.st, d->traffic.chart_var_speed); + rrdset_is_obsolete___safe_from_collector_thread(d->packets.st); + rrdset_is_obsolete___safe_from_collector_thread(d->traffic.st); + rrdset_is_obsolete___safe_from_collector_thread(d->speed.st); + rrdset_is_obsolete___safe_from_collector_thread(d->discards.st); + rrdset_is_obsolete___safe_from_collector_thread(d->errors.st); + rrdset_is_obsolete___safe_from_collector_thread(d->queue.st); + rrdset_is_obsolete___safe_from_collector_thread(d->chimney.st); + rrdset_is_obsolete___safe_from_collector_thread(d->rsc.st_connections); + rrdset_is_obsolete___safe_from_collector_thread(d->rsc.st_packets); + rrdset_is_obsolete___safe_from_collector_thread(d->rsc.st_exceptions); + rrdset_is_obsolete___safe_from_collector_thread(d->rsc.st_average_packet_size); +} + +void dict_interface_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { + struct network_interface *ni = value; + network_interface_init(ni); +} + +static void initialize(void) { + physical_interfaces = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | + DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct network_interface)); + + virtual_interfaces = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | + DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct network_interface)); + + dictionary_register_insert_callback(physical_interfaces, dict_interface_insert_cb, NULL); + dictionary_register_insert_callback(virtual_interfaces, dict_interface_insert_cb, NULL); +} + +static void add_interface_labels(RRDSET *st, const char *name, bool physical) { + rrdlabels_add(st->rrdlabels, "device", name, RRDLABEL_SRC_AUTO); + rrdlabels_add(st->rrdlabels, "interface_type", physical ? "real" : "virtual", RRDLABEL_SRC_AUTO); +} + +static bool is_physical_interface(const char *name) { + void *d = dictionary_get(physical_interfaces, name); + return d ? true : false; +} + +static bool do_network_interface(PERF_DATA_BLOCK *pDataBlock, int update_every, bool physical, usec_t now_ut) { + DICTIONARY *dict = physical ? physical_interfaces : virtual_interfaces; + + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, physical ? "Network Interface" : "Network Adapter"); + if(!pObjectType) return false; + + uint64_t total_received = 0, total_sent = 0; + + PERF_INSTANCE_DEFINITION *pi = NULL; + for(LONG i = 0; i < pObjectType->NumInstances ; i++) { + pi = perflibForEachInstance(pDataBlock, pObjectType, pi); + if(!pi) break; + + if(!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer))) + strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1); + + if(strcasecmp(windows_shared_buffer, "_Total") == 0) + continue; + + if(!physical && is_physical_interface(windows_shared_buffer)) + // this virtual interface is already reported as physical interface + continue; + + struct network_interface *d = dictionary_set(dict, windows_shared_buffer, NULL, sizeof(*d)); + d->last_collected = now_ut; + + if(!d->collected_metadata) { + // TODO - get metadata about the network interface + d->collected_metadata = true; + } + + if(perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->traffic.received) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->traffic.sent)) { + + if(d->traffic.received.current.Data == 0 && d->traffic.sent.current.Data == 0) + // this interface has not received or sent any traffic yet + continue; + + if (unlikely(!d->traffic.st)) { + d->traffic.st = rrdset_create_localhost( + "net", + windows_shared_buffer, + NULL, + windows_shared_buffer, + "net.net", + "Bandwidth", + "kilobits/s", + PLUGIN_WINDOWS_NAME, + "PerflibNetwork", + NETDATA_CHART_PRIO_FIRST_NET_IFACE, + update_every, + RRDSET_TYPE_AREA); + + add_interface_labels(d->traffic.st, windows_shared_buffer, physical); + + d->traffic.rd_received = rrddim_add(d->traffic.st, "received", NULL, 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); + d->traffic.rd_sent = rrddim_add(d->traffic.st, "sent", NULL, -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); + + d->traffic.chart_var_speed = rrdvar_chart_variable_add_and_acquire(d->traffic.st, "nic_speed_max"); + rrdvar_chart_variable_set(d->traffic.st, d->traffic.chart_var_speed, NAN); + } + + total_received += d->traffic.received.current.Data; + total_sent += d->traffic.sent.current.Data; + + rrddim_set_by_pointer(d->traffic.st, d->traffic.rd_received, (collected_number)d->traffic.received.current.Data); + rrddim_set_by_pointer(d->traffic.st, d->traffic.rd_sent, (collected_number)d->traffic.sent.current.Data); + rrdset_done(d->traffic.st); + } + + if(perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->packets.received) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->packets.sent)) { + + if (unlikely(!d->packets.st)) { + d->packets.st = rrdset_create_localhost( + "net_packets", + windows_shared_buffer, + NULL, + windows_shared_buffer, + "net.packets", + "Packets", + "packets/s", + PLUGIN_WINDOWS_NAME, + "PerflibNetwork", + NETDATA_CHART_PRIO_FIRST_NET_IFACE + 1, + update_every, + RRDSET_TYPE_LINE); + + add_interface_labels(d->packets.st, windows_shared_buffer, physical); + + d->packets.rd_received = rrddim_add(d->packets.st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + d->packets.rd_sent = rrddim_add(d->packets.st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(d->packets.st, d->packets.rd_received, (collected_number)d->packets.received.current.Data); + rrddim_set_by_pointer(d->packets.st, d->packets.rd_sent, (collected_number)d->packets.sent.current.Data); + rrdset_done(d->packets.st); + } + + if(perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->speed.current_bandwidth)) { + if(unlikely(!d->speed.st)) { + d->speed.st = rrdset_create_localhost( + "net_speed" + , windows_shared_buffer + , NULL + , windows_shared_buffer + , "net.speed" + , "Interface Speed" + , "kilobits/s" + , PLUGIN_WINDOWS_NAME + , "PerflibNetwork" + , NETDATA_CHART_PRIO_FIRST_NET_IFACE + 10 + , update_every + , RRDSET_TYPE_LINE + ); + + add_interface_labels(d->speed.st, windows_shared_buffer, physical); + + d->speed.rd = rrddim_add(d->speed.st, "speed", NULL, 1, BITS_IN_A_KILOBIT, RRD_ALGORITHM_ABSOLUTE); + } + + rrddim_set_by_pointer(d->speed.st, d->speed.rd, (collected_number)d->speed.current_bandwidth.current.Data); + rrdset_done(d->speed.st); + + rrdvar_chart_variable_set(d->traffic.st, d->traffic.chart_var_speed, + (NETDATA_DOUBLE)d->speed.current_bandwidth.current.Data / BITS_IN_A_KILOBIT); + } + + if(perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->errors.received) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->errors.outbound)) { + + if (unlikely(!d->errors.st)) { + d->errors.st = rrdset_create_localhost( + "net_errors", + windows_shared_buffer, + NULL, + windows_shared_buffer, + "net.errors", + "Interface Errors", + "errors/s", + PLUGIN_WINDOWS_NAME, + "PerflibNetwork", + NETDATA_CHART_PRIO_FIRST_NET_IFACE + 3, + update_every, + RRDSET_TYPE_LINE); + + add_interface_labels(d->errors.st, windows_shared_buffer, physical); + + d->errors.rd_received = rrddim_add(d->errors.st, "inbound", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + d->errors.rd_outbound = rrddim_add(d->errors.st, "outbound", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(d->errors.st, d->errors.rd_received, (collected_number)d->errors.received.current.Data); + rrddim_set_by_pointer(d->errors.st, d->errors.rd_outbound, (collected_number)d->errors.outbound.current.Data); + rrdset_done(d->errors.st); + } + + if(perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->discards.received) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->discards.outbound)) { + + if (unlikely(!d->discards.st)) { + d->discards.st = rrdset_create_localhost( + "net_drops", + windows_shared_buffer, + NULL, + windows_shared_buffer, + "net.drops", + "Interface Drops", + "drops/s", + PLUGIN_WINDOWS_NAME, + "PerflibNetwork", + NETDATA_CHART_PRIO_FIRST_NET_IFACE + 4, + update_every, + RRDSET_TYPE_LINE); + + add_interface_labels(d->discards.st, windows_shared_buffer, physical); + + d->discards.rd_received = rrddim_add(d->discards.st, "inbound", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + d->discards.rd_outbound = rrddim_add(d->discards.st, "outbound", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(d->discards.st, d->discards.rd_received, (collected_number)d->discards.received.current.Data); + rrddim_set_by_pointer(d->discards.st, d->discards.rd_outbound, (collected_number)d->discards.outbound.current.Data); + rrdset_done(d->discards.st); + } + + if(perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->queue.length)) { + if (unlikely(!d->queue.st)) { + d->queue.st = rrdset_create_localhost( + "net_queue_length", + windows_shared_buffer, + NULL, + windows_shared_buffer, + "net.queue_length", + "Interface Output Queue Length", + "packets", + PLUGIN_WINDOWS_NAME, + "PerflibNetwork", + NETDATA_CHART_PRIO_FIRST_NET_IFACE + 5, + update_every, + RRDSET_TYPE_LINE); + + add_interface_labels(d->queue.st, windows_shared_buffer, physical); + + d->queue.rd = rrddim_add(d->queue.st, "length", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + } + + rrddim_set_by_pointer(d->queue.st, d->queue.rd, (collected_number)d->queue.length.current.Data); + rrdset_done(d->queue.st); + } + + if(perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->rsc.connections)) { + if (unlikely(!d->rsc.st_connections)) { + d->rsc.st_connections = rrdset_create_localhost( + "net_rsc_connections", + windows_shared_buffer, + NULL, + windows_shared_buffer, + "net.rsc_connections", + "Active TCP Connections Offloaded by RSC", + "connections", + PLUGIN_WINDOWS_NAME, + "PerflibNetwork", + NETDATA_CHART_PRIO_FIRST_NET_IFACE + 6, + update_every, + RRDSET_TYPE_LINE); + + add_interface_labels(d->rsc.st_connections, windows_shared_buffer, physical); + + d->rsc.rd_connections = rrddim_add(d->rsc.st_connections, "connections", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + } + + rrddim_set_by_pointer(d->rsc.st_connections, d->rsc.rd_connections, (collected_number)d->rsc.connections.current.Data); + rrdset_done(d->rsc.st_connections); + } + + if(perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->rsc.packets)) { + if (unlikely(!d->rsc.st_packets)) { + d->rsc.st_packets = rrdset_create_localhost( + "net_rsc_packets", + windows_shared_buffer, + NULL, + windows_shared_buffer, + "net.rsc_packets", + "TCP RSC Coalesced Packets", + "packets/s", + PLUGIN_WINDOWS_NAME, + "PerflibNetwork", + NETDATA_CHART_PRIO_FIRST_NET_IFACE + 7, + update_every, + RRDSET_TYPE_LINE); + + add_interface_labels(d->rsc.st_packets, windows_shared_buffer, physical); + + d->rsc.rd_packets = rrddim_add(d->rsc.st_packets, "packets", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(d->rsc.st_packets, d->rsc.rd_packets, (collected_number)d->rsc.packets.current.Data); + rrdset_done(d->rsc.st_packets); + } + + if(perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->rsc.exceptions)) { + if (unlikely(!d->rsc.st_exceptions)) { + d->rsc.st_exceptions = rrdset_create_localhost( + "net_rsc_exceptions", + windows_shared_buffer, + NULL, + windows_shared_buffer, + "net.rsc_exceptions", + "TCP RSC Exceptions", + "exceptions/s", + PLUGIN_WINDOWS_NAME, + "PerflibNetwork", + NETDATA_CHART_PRIO_FIRST_NET_IFACE + 8, + update_every, + RRDSET_TYPE_LINE); + + add_interface_labels(d->rsc.st_exceptions, windows_shared_buffer, physical); + + d->rsc.rd_exceptions = rrddim_add(d->rsc.st_exceptions, "exceptions", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(d->rsc.st_exceptions, d->rsc.rd_exceptions, (collected_number)d->rsc.exceptions.current.Data); + rrdset_done(d->rsc.st_exceptions); + } + + if(perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->rsc.average_packet_size)) { + if (unlikely(!d->rsc.st_average_packet_size)) { + d->rsc.st_average_packet_size = rrdset_create_localhost( + "net_rsc_average_packet_size", + windows_shared_buffer, + NULL, + windows_shared_buffer, + "net.rsc_average_packet_size", + "TCP RSC Average Packet Size", + "bytes", + PLUGIN_WINDOWS_NAME, + "PerflibNetwork", + NETDATA_CHART_PRIO_FIRST_NET_IFACE + 9, + update_every, + RRDSET_TYPE_LINE); + + add_interface_labels(d->rsc.st_average_packet_size, windows_shared_buffer, physical); + + d->rsc.rd_average_packet_size = rrddim_add(d->rsc.st_average_packet_size, "average", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + } + + rrddim_set_by_pointer(d->rsc.st_average_packet_size, d->rsc.rd_average_packet_size, (collected_number)d->rsc.average_packet_size.current.Data); + rrdset_done(d->rsc.st_average_packet_size); + } + + if(perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->chimney.connections)) { + if (unlikely(!d->chimney.st)) { + d->chimney.st = rrdset_create_localhost( + "net_chimney_connections", + windows_shared_buffer, + NULL, + windows_shared_buffer, + "net.chimney_connections", + "Active TCP Connections Offloaded with Chimney", + "connections", + PLUGIN_WINDOWS_NAME, + "PerflibNetwork", + NETDATA_CHART_PRIO_FIRST_NET_IFACE + 10, + update_every, + RRDSET_TYPE_LINE); + + add_interface_labels(d->chimney.st, windows_shared_buffer, physical); + + d->chimney.rd = rrddim_add(d->chimney.st, "connections", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + } + + rrddim_set_by_pointer(d->chimney.st, d->chimney.rd, (collected_number)d->chimney.connections.current.Data); + rrdset_done(d->chimney.st); + } + } + + if(physical) { + static RRDSET *st = NULL; + static RRDDIM *rd_received = NULL, *rd_sent = NULL; + + if (unlikely(!st)) { + st = rrdset_create_localhost( + "system", + "net", + NULL, + "network", + "system.net", + "Physical Network Interfaces Aggregated Bandwidth", + "kilobits/s", + PLUGIN_WINDOWS_NAME, + "PerflibNetwork", + NETDATA_CHART_PRIO_SYSTEM_NET, + update_every, + RRDSET_TYPE_AREA); + + rd_received = rrddim_add(st, "received", NULL, 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); + rd_sent = rrddim_add(st, "sent", NULL, -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_received, (collected_number)total_received); + rrddim_set_by_pointer(st, rd_sent, (collected_number)total_sent); + rrdset_done(st); + } + + // cleanup + { + struct network_interface *d; + dfe_start_write(dict, d) { + if(d->last_collected < now_ut) { + network_interface_cleanup(d); + dictionary_del(dict, d_dfe.name); + } + } + dfe_done(d); + dictionary_garbage_collect(dict); + } + + return true; +} + +int do_PerflibNetwork(int update_every, usec_t dt __maybe_unused) { + static bool initialized = false; + + if(unlikely(!initialized)) { + initialize(); + initialized = true; + } + + DWORD id = RegistryFindIDByName("Network Interface"); + if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND) + return -1; + + PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id); + if(!pDataBlock) return -1; + + usec_t now_ut = now_monotonic_usec(); + do_network_interface(pDataBlock, update_every, true, now_ut); + do_network_interface(pDataBlock, update_every, false, now_ut); + + struct network_protocol *tcp4 = NULL, *tcp6 = NULL; + for(size_t i = 0; networks[i].protocol ;i++) { + do_network_protocol(pDataBlock, update_every, &networks[i]); + + if(!tcp4 && strcmp(networks[i].protocol, "TCPv4") == 0) + tcp4 = &networks[i]; + if(!tcp6 && strcmp(networks[i].protocol, "TCPv6") == 0) + tcp6 = &networks[i]; + } + + if(tcp4 && tcp6) { + tcp46.packets.received = tcp4->packets.received; + tcp46.packets.sent = tcp4->packets.sent; + tcp46.packets.received.current.Data += tcp6->packets.received.current.Data; + tcp46.packets.sent.current.Data += tcp6->packets.sent.current.Data; + protocol_packets_chart_update(&tcp46, update_every); + } + return 0; +} diff --git a/src/collectors/windows.plugin/perflib-objects.c b/src/collectors/windows.plugin/perflib-objects.c index 6628ff864..cb1bc8d22 100644 --- a/src/collectors/windows.plugin/perflib-objects.c +++ b/src/collectors/windows.plugin/perflib-objects.c @@ -1,47 +1,47 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "windows_plugin.h" -#include "windows-internals.h" - -#define _COMMON_PLUGIN_NAME "windows.plugin" -#define _COMMON_PLUGIN_MODULE_NAME "PerflibObjects" -#include "../common-contexts/common-contexts.h" - -static void initialize(void) { - ; -} - -static bool do_objects(PERF_DATA_BLOCK *pDataBlock, int update_every) { - PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "Objects"); - if (!pObjectType) - return false; - - static COUNTER_DATA semaphores = { .key = "Semaphores" }; - - if(perflibGetObjectCounter(pDataBlock, pObjectType, &semaphores)) { - ULONGLONG sem = semaphores.current.Data; - common_semaphore_ipc(sem, WINDOWS_MAX_KERNEL_OBJECT, _COMMON_PLUGIN_MODULE_NAME, update_every); - } - - return true; -} - -int do_PerflibObjects(int update_every, usec_t dt __maybe_unused) { - static bool initialized = false; - - if(unlikely(!initialized)) { - initialize(); - initialized = true; - } - - DWORD id = RegistryFindIDByName("Objects"); - if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND) - return -1; - - PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id); - if(!pDataBlock) return -1; - - do_objects(pDataBlock, update_every); - - return 0; -} +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "windows_plugin.h" +#include "windows-internals.h" + +#define _COMMON_PLUGIN_NAME "windows.plugin" +#define _COMMON_PLUGIN_MODULE_NAME "PerflibObjects" +#include "../common-contexts/common-contexts.h" + +static void initialize(void) { + ; +} + +static bool do_objects(PERF_DATA_BLOCK *pDataBlock, int update_every) { + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "Objects"); + if (!pObjectType) + return false; + + static COUNTER_DATA semaphores = { .key = "Semaphores" }; + + if(perflibGetObjectCounter(pDataBlock, pObjectType, &semaphores)) { + ULONGLONG sem = semaphores.current.Data; + common_semaphore_ipc(sem, WINDOWS_MAX_KERNEL_OBJECT, _COMMON_PLUGIN_MODULE_NAME, update_every); + } + + return true; +} + +int do_PerflibObjects(int update_every, usec_t dt __maybe_unused) { + static bool initialized = false; + + if(unlikely(!initialized)) { + initialize(); + initialized = true; + } + + DWORD id = RegistryFindIDByName("Objects"); + if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND) + return -1; + + PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id); + if(!pDataBlock) return -1; + + do_objects(pDataBlock, update_every); + + return 0; +} diff --git a/src/collectors/windows.plugin/perflib-processes.c b/src/collectors/windows.plugin/perflib-processes.c index 92aa243b9..70e388eed 100644 --- a/src/collectors/windows.plugin/perflib-processes.c +++ b/src/collectors/windows.plugin/perflib-processes.c @@ -1,58 +1,58 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "windows_plugin.h" -#include "windows-internals.h" - -#define _COMMON_PLUGIN_NAME "windows.plugin" -#define _COMMON_PLUGIN_MODULE_NAME "PerflibProcesses" -#include "../common-contexts/common-contexts.h" - -static void initialize(void) { - ; -} - -static bool do_processes(PERF_DATA_BLOCK *pDataBlock, int update_every) { - PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "System"); - if (!pObjectType) - return false; - - static COUNTER_DATA processesRunning = { .key = "Processes" }; - static COUNTER_DATA contextSwitchPerSec = { .key = "Context Switches/sec" }; - static COUNTER_DATA threads = { .key = "Threads" }; - - if(perflibGetObjectCounter(pDataBlock, pObjectType, &processesRunning)) { - ULONGLONG running = processesRunning.current.Data; - common_system_processes(running, update_every); - } - - if(perflibGetObjectCounter(pDataBlock, pObjectType, &contextSwitchPerSec)) { - ULONGLONG contexts = contextSwitchPerSec.current.Data; - common_system_context_switch(contexts, update_every); - } - - if(perflibGetObjectCounter(pDataBlock, pObjectType, &threads)) { - ULONGLONG totalThreads = threads.current.Data; - common_system_threads(totalThreads, update_every); - } - return true; -} - -int do_PerflibProcesses(int update_every, usec_t dt __maybe_unused) { - static bool initialized = false; - - if(unlikely(!initialized)) { - initialize(); - initialized = true; - } - - DWORD id = RegistryFindIDByName("System"); - if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND) - return -1; - - PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id); - if(!pDataBlock) return -1; - - do_processes(pDataBlock, update_every); - - return 0; -} +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "windows_plugin.h" +#include "windows-internals.h" + +#define _COMMON_PLUGIN_NAME "windows.plugin" +#define _COMMON_PLUGIN_MODULE_NAME "PerflibProcesses" +#include "../common-contexts/common-contexts.h" + +static void initialize(void) { + ; +} + +static bool do_processes(PERF_DATA_BLOCK *pDataBlock, int update_every) { + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "System"); + if (!pObjectType) + return false; + + static COUNTER_DATA processesRunning = { .key = "Processes" }; + static COUNTER_DATA contextSwitchPerSec = { .key = "Context Switches/sec" }; + static COUNTER_DATA threads = { .key = "Threads" }; + + if(perflibGetObjectCounter(pDataBlock, pObjectType, &processesRunning)) { + ULONGLONG running = processesRunning.current.Data; + common_system_processes(running, update_every); + } + + if(perflibGetObjectCounter(pDataBlock, pObjectType, &contextSwitchPerSec)) { + ULONGLONG contexts = contextSwitchPerSec.current.Data; + common_system_context_switch(contexts, update_every); + } + + if(perflibGetObjectCounter(pDataBlock, pObjectType, &threads)) { + ULONGLONG totalThreads = threads.current.Data; + common_system_threads(totalThreads, update_every); + } + return true; +} + +int do_PerflibProcesses(int update_every, usec_t dt __maybe_unused) { + static bool initialized = false; + + if(unlikely(!initialized)) { + initialize(); + initialized = true; + } + + DWORD id = RegistryFindIDByName("System"); + if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND) + return -1; + + PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id); + if(!pDataBlock) return -1; + + do_processes(pDataBlock, update_every); + + return 0; +} diff --git a/src/collectors/windows.plugin/perflib-processor.c b/src/collectors/windows.plugin/perflib-processor.c index 4c7d86c90..a3df0fced 100644 --- a/src/collectors/windows.plugin/perflib-processor.c +++ b/src/collectors/windows.plugin/perflib-processor.c @@ -1,205 +1,205 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "windows_plugin.h" -#include "windows-internals.h" - -#define _COMMON_PLUGIN_NAME "windows.plugin" -#define _COMMON_PLUGIN_MODULE_NAME "PerflibProcesses" -#include "../common-contexts/common-contexts.h" - -struct processor { - bool collected_metadata; - - RRDSET *st; - RRDDIM *rd_user; - RRDDIM *rd_system; - RRDDIM *rd_irq; - RRDDIM *rd_dpc; - RRDDIM *rd_idle; - -// RRDSET *st2; -// RRDDIM *rd2_busy; - - COUNTER_DATA percentProcessorTime; - COUNTER_DATA percentUserTime; - COUNTER_DATA percentPrivilegedTime; - COUNTER_DATA percentDPCTime; - COUNTER_DATA percentInterruptTime; - COUNTER_DATA percentIdleTime; - - COUNTER_DATA interruptsPerSec; -}; - -struct processor total = { 0 }; - -void initialize_processor_keys(struct processor *p) { - p->percentProcessorTime.key = "% Processor Time"; - p->percentUserTime.key = "% User Time"; - p->percentPrivilegedTime.key = "% Privileged Time"; - p->percentDPCTime.key = "% DPC Time"; - p->percentInterruptTime.key = "% Interrupt Time"; - p->percentIdleTime.key = "% Idle Time"; - p->interruptsPerSec.key = "Interrupts/sec"; -} - -void dict_processor_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { - struct processor *p = value; - initialize_processor_keys(p); -} - -static DICTIONARY *processors = NULL; - -static void initialize(void) { - initialize_processor_keys(&total); - - processors = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | - DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct processor)); - - dictionary_register_insert_callback(processors, dict_processor_insert_cb, NULL); -} - -static bool do_processors(PERF_DATA_BLOCK *pDataBlock, int update_every) { - PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "Processor"); - if(!pObjectType) return false; - - static const RRDVAR_ACQUIRED *cpus_var = NULL; - int cores_found = 0; - uint64_t totalIPC = 0; - - PERF_INSTANCE_DEFINITION *pi = NULL; - for(LONG i = 0; i < pObjectType->NumInstances ; i++) { - pi = perflibForEachInstance(pDataBlock, pObjectType, pi); - if(!pi) break; - - if(!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer))) - strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1); - - bool is_total = false; - struct processor *p; - int cpu = -1; - if(strcasecmp(windows_shared_buffer, "_Total") == 0) { - p = &total; - is_total = true; - cpu = -1; - } - else { - p = dictionary_set(processors, windows_shared_buffer, NULL, sizeof(*p)); - is_total = false; - cpu = str2i(windows_shared_buffer); - snprintfz(windows_shared_buffer, sizeof(windows_shared_buffer), "cpu%d", cpu); - - if(cpu + 1 > cores_found) - cores_found = cpu + 1; - } - - if(!is_total && !p->collected_metadata) { - // TODO collect processor metadata - p->collected_metadata = true; - } - - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentProcessorTime); - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentUserTime); - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentPrivilegedTime); - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentDPCTime); - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentInterruptTime); - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentIdleTime); - - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->interruptsPerSec); - - if(!p->st) { - p->st = rrdset_create_localhost( - is_total ? "system" : "cpu" - , is_total ? "cpu" : windows_shared_buffer, NULL - , is_total ? "cpu" : "utilization" - , is_total ? "system.cpu" : "cpu.cpu" - , is_total ? "Total CPU Utilization" : "Core Utilization" - , "percentage" - , PLUGIN_WINDOWS_NAME - , "PerflibProcessor" - , is_total ? NETDATA_CHART_PRIO_SYSTEM_CPU : NETDATA_CHART_PRIO_CPU_PER_CORE - , update_every - , RRDSET_TYPE_STACKED - ); - - p->rd_irq = rrddim_add(p->st, "interrupts", "irq", 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); - p->rd_user = rrddim_add(p->st, "user", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); - p->rd_system = rrddim_add(p->st, "privileged", "system", 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); - p->rd_dpc = rrddim_add(p->st, "dpc", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); - p->rd_idle = rrddim_add(p->st, "idle", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); - rrddim_hide(p->st, "idle"); - - if(!is_total) - rrdlabels_add(p->st->rrdlabels, "cpu", windows_shared_buffer, RRDLABEL_SRC_AUTO); - else - cpus_var = rrdvar_host_variable_add_and_acquire(localhost, "active_processors"); - } - - uint64_t user = p->percentUserTime.current.Data; - uint64_t system = p->percentPrivilegedTime.current.Data; - uint64_t dpc = p->percentDPCTime.current.Data; - uint64_t irq = p->percentInterruptTime.current.Data; - uint64_t idle = p->percentIdleTime.current.Data; - - totalIPC += p->interruptsPerSec.current.Data; - - rrddim_set_by_pointer(p->st, p->rd_user, (collected_number)user); - rrddim_set_by_pointer(p->st, p->rd_system, (collected_number)system); - rrddim_set_by_pointer(p->st, p->rd_irq, (collected_number)irq); - rrddim_set_by_pointer(p->st, p->rd_dpc, (collected_number)dpc); - rrddim_set_by_pointer(p->st, p->rd_idle, (collected_number)idle); - rrdset_done(p->st); - -// if(!p->st2) { -// p->st2 = rrdset_create_localhost( -// is_total ? "system" : "cpu2" -// , is_total ? "cpu3" : buffer -// , NULL -// , is_total ? "utilization" : buffer -// , is_total ? "system.cpu3" : "cpu2.cpu" -// , is_total ? "Total CPU Utilization" : "Core Utilization" -// , "percentage" -// , PLUGIN_WINDOWS_NAME -// , "PerflibProcessor" -// , is_total ? NETDATA_CHART_PRIO_SYSTEM_CPU : NETDATA_CHART_PRIO_CPU_PER_CORE -// , update_every -// , RRDSET_TYPE_STACKED -// ); -// -// p->rd2_busy = perflib_rrddim_add(p->st2, "busy", NULL, 1, 1, &p->percentProcessorTime); -// rrddim_hide(p->st2, "idle"); -// -// if(!is_total) -// rrdlabels_add(p->st->rrdlabels, "cpu", buffer, RRDLABEL_SRC_AUTO); -// } -// -// perflib_rrddim_set_by_pointer(p->st2, p->rd2_busy, &p->percentProcessorTime); -// rrdset_done(p->st2); - } - - if(cpus_var) - rrdvar_host_variable_set(localhost, cpus_var, cores_found); - - common_interrupts(totalIPC, update_every, NULL); - - return true; -} - -int do_PerflibProcessor(int update_every, usec_t dt __maybe_unused) { - static bool initialized = false; - - if(unlikely(!initialized)) { - initialize(); - initialized = true; - } - - DWORD id = RegistryFindIDByName("Processor"); - if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND) - return -1; - - PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id); - if(!pDataBlock) return -1; - - do_processors(pDataBlock, update_every); - - return 0; -} +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "windows_plugin.h" +#include "windows-internals.h" + +#define _COMMON_PLUGIN_NAME "windows.plugin" +#define _COMMON_PLUGIN_MODULE_NAME "PerflibProcesses" +#include "../common-contexts/common-contexts.h" + +struct processor { + bool collected_metadata; + + RRDSET *st; + RRDDIM *rd_user; + RRDDIM *rd_system; + RRDDIM *rd_irq; + RRDDIM *rd_dpc; + RRDDIM *rd_idle; + +// RRDSET *st2; +// RRDDIM *rd2_busy; + + COUNTER_DATA percentProcessorTime; + COUNTER_DATA percentUserTime; + COUNTER_DATA percentPrivilegedTime; + COUNTER_DATA percentDPCTime; + COUNTER_DATA percentInterruptTime; + COUNTER_DATA percentIdleTime; + + COUNTER_DATA interruptsPerSec; +}; + +struct processor total = { 0 }; + +void initialize_processor_keys(struct processor *p) { + p->percentProcessorTime.key = "% Processor Time"; + p->percentUserTime.key = "% User Time"; + p->percentPrivilegedTime.key = "% Privileged Time"; + p->percentDPCTime.key = "% DPC Time"; + p->percentInterruptTime.key = "% Interrupt Time"; + p->percentIdleTime.key = "% Idle Time"; + p->interruptsPerSec.key = "Interrupts/sec"; +} + +void dict_processor_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { + struct processor *p = value; + initialize_processor_keys(p); +} + +static DICTIONARY *processors = NULL; + +static void initialize(void) { + initialize_processor_keys(&total); + + processors = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | + DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct processor)); + + dictionary_register_insert_callback(processors, dict_processor_insert_cb, NULL); +} + +static bool do_processors(PERF_DATA_BLOCK *pDataBlock, int update_every) { + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "Processor"); + if(!pObjectType) return false; + + static const RRDVAR_ACQUIRED *cpus_var = NULL; + int cores_found = 0; + uint64_t totalIPC = 0; + + PERF_INSTANCE_DEFINITION *pi = NULL; + for(LONG i = 0; i < pObjectType->NumInstances ; i++) { + pi = perflibForEachInstance(pDataBlock, pObjectType, pi); + if(!pi) break; + + if(!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer))) + strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1); + + bool is_total = false; + struct processor *p; + int cpu = -1; + if(strcasecmp(windows_shared_buffer, "_Total") == 0) { + p = &total; + is_total = true; + cpu = -1; + } + else { + p = dictionary_set(processors, windows_shared_buffer, NULL, sizeof(*p)); + is_total = false; + cpu = str2i(windows_shared_buffer); + snprintfz(windows_shared_buffer, sizeof(windows_shared_buffer), "cpu%d", cpu); + + if(cpu + 1 > cores_found) + cores_found = cpu + 1; + } + + if(!is_total && !p->collected_metadata) { + // TODO collect processor metadata + p->collected_metadata = true; + } + + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentProcessorTime); + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentUserTime); + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentPrivilegedTime); + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentDPCTime); + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentInterruptTime); + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentIdleTime); + + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->interruptsPerSec); + + if(!p->st) { + p->st = rrdset_create_localhost( + is_total ? "system" : "cpu" + , is_total ? "cpu" : windows_shared_buffer, NULL + , is_total ? "cpu" : "utilization" + , is_total ? "system.cpu" : "cpu.cpu" + , is_total ? "Total CPU Utilization" : "Core Utilization" + , "percentage" + , PLUGIN_WINDOWS_NAME + , "PerflibProcessor" + , is_total ? NETDATA_CHART_PRIO_SYSTEM_CPU : NETDATA_CHART_PRIO_CPU_PER_CORE + , update_every + , RRDSET_TYPE_STACKED + ); + + p->rd_irq = rrddim_add(p->st, "interrupts", "irq", 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + p->rd_user = rrddim_add(p->st, "user", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + p->rd_system = rrddim_add(p->st, "privileged", "system", 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + p->rd_dpc = rrddim_add(p->st, "dpc", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + p->rd_idle = rrddim_add(p->st, "idle", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rrddim_hide(p->st, "idle"); + + if(!is_total) + rrdlabels_add(p->st->rrdlabels, "cpu", windows_shared_buffer, RRDLABEL_SRC_AUTO); + else + cpus_var = rrdvar_host_variable_add_and_acquire(localhost, "active_processors"); + } + + uint64_t user = p->percentUserTime.current.Data; + uint64_t system = p->percentPrivilegedTime.current.Data; + uint64_t dpc = p->percentDPCTime.current.Data; + uint64_t irq = p->percentInterruptTime.current.Data; + uint64_t idle = p->percentIdleTime.current.Data; + + totalIPC += p->interruptsPerSec.current.Data; + + rrddim_set_by_pointer(p->st, p->rd_user, (collected_number)user); + rrddim_set_by_pointer(p->st, p->rd_system, (collected_number)system); + rrddim_set_by_pointer(p->st, p->rd_irq, (collected_number)irq); + rrddim_set_by_pointer(p->st, p->rd_dpc, (collected_number)dpc); + rrddim_set_by_pointer(p->st, p->rd_idle, (collected_number)idle); + rrdset_done(p->st); + +// if(!p->st2) { +// p->st2 = rrdset_create_localhost( +// is_total ? "system" : "cpu2" +// , is_total ? "cpu3" : buffer +// , NULL +// , is_total ? "utilization" : buffer +// , is_total ? "system.cpu3" : "cpu2.cpu" +// , is_total ? "Total CPU Utilization" : "Core Utilization" +// , "percentage" +// , PLUGIN_WINDOWS_NAME +// , "PerflibProcessor" +// , is_total ? NETDATA_CHART_PRIO_SYSTEM_CPU : NETDATA_CHART_PRIO_CPU_PER_CORE +// , update_every +// , RRDSET_TYPE_STACKED +// ); +// +// p->rd2_busy = perflib_rrddim_add(p->st2, "busy", NULL, 1, 1, &p->percentProcessorTime); +// rrddim_hide(p->st2, "idle"); +// +// if(!is_total) +// rrdlabels_add(p->st->rrdlabels, "cpu", buffer, RRDLABEL_SRC_AUTO); +// } +// +// perflib_rrddim_set_by_pointer(p->st2, p->rd2_busy, &p->percentProcessorTime); +// rrdset_done(p->st2); + } + + if(cpus_var) + rrdvar_host_variable_set(localhost, cpus_var, cores_found); + + common_interrupts(totalIPC, update_every, NULL); + + return true; +} + +int do_PerflibProcessor(int update_every, usec_t dt __maybe_unused) { + static bool initialized = false; + + if(unlikely(!initialized)) { + initialize(); + initialized = true; + } + + DWORD id = RegistryFindIDByName("Processor"); + if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND) + return -1; + + PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id); + if(!pDataBlock) return -1; + + do_processors(pDataBlock, update_every); + + return 0; +} diff --git a/src/collectors/windows.plugin/perflib-rrd.c b/src/collectors/windows.plugin/perflib-rrd.c index d425307ee..5af36ae35 100644 --- a/src/collectors/windows.plugin/perflib-rrd.c +++ b/src/collectors/windows.plugin/perflib-rrd.c @@ -1,411 +1,411 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "perflib-rrd.h" - -#define COLLECTED_NUMBER_PRECISION 10000 - -RRDDIM *perflib_rrddim_add(RRDSET *st, const char *id, const char *name, collected_number multiplier, collected_number divider, COUNTER_DATA *cd) { - RRD_ALGORITHM algorithm = RRD_ALGORITHM_ABSOLUTE; - - switch (cd->current.CounterType) { - case PERF_COUNTER_COUNTER: - case PERF_SAMPLE_COUNTER: - case PERF_COUNTER_BULK_COUNT: - // (N1 - N0) / ((D1 - D0) / F) - // multiplier *= cd->current.Frequency / 10000000; - // tested, the frequency is not that useful for netdata - // we get right results without it. - algorithm = RRD_ALGORITHM_INCREMENTAL; - break; - - 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 - // (N1 - N0) / (D1 - D0) - algorithm = RRD_ALGORITHM_INCREMENTAL; - break; - - 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: - // 100 * (N1 - N0) / (D1 - D0) - multiplier *= 100; - algorithm = RRD_ALGORITHM_INCREMENTAL; - break; - - case PERF_COUNTER_TIMER_INV: - case PERF_100NSEC_TIMER_INV: - // 100 * (1 - ((N1 - N0) / (D1 - D0))) - divider *= COLLECTED_NUMBER_PRECISION; - algorithm = RRD_ALGORITHM_ABSOLUTE; - break; - - case PERF_COUNTER_MULTI_TIMER: - // 100 * ((N1 - N0) / ((D1 - D0) / TB)) / B1 - divider *= COLLECTED_NUMBER_PRECISION; - algorithm = RRD_ALGORITHM_ABSOLUTE; - break; - - case PERF_100NSEC_MULTI_TIMER: - // 100 * ((N1 - N0) / (D1 - D0)) / B1 - divider *= COLLECTED_NUMBER_PRECISION; - algorithm = RRD_ALGORITHM_ABSOLUTE; - break; - - case PERF_COUNTER_MULTI_TIMER_INV: - case PERF_100NSEC_MULTI_TIMER_INV: - // 100 * (B1 - ((N1 - N0) / (D1 - D0))) - divider *= COLLECTED_NUMBER_PRECISION; - algorithm = RRD_ALGORITHM_ABSOLUTE; - break; - - case PERF_COUNTER_RAWCOUNT: - case PERF_COUNTER_LARGE_RAWCOUNT: - // N as decimal - algorithm = RRD_ALGORITHM_ABSOLUTE; - break; - - case PERF_COUNTER_RAWCOUNT_HEX: - case PERF_COUNTER_LARGE_RAWCOUNT_HEX: - // N as hexadecimal - algorithm = RRD_ALGORITHM_ABSOLUTE; - break; - - case PERF_COUNTER_DELTA: - case PERF_COUNTER_LARGE_DELTA: - // N1 - N0 - algorithm = RRD_ALGORITHM_ABSOLUTE; - break; - - case PERF_RAW_FRACTION: - case PERF_LARGE_RAW_FRACTION: - // 100 * N / B - algorithm = RRD_ALGORITHM_ABSOLUTE; - divider *= COLLECTED_NUMBER_PRECISION; - break; - - case PERF_AVERAGE_TIMER: - // ((N1 - N0) / TB) / (B1 - B0) - // divider *= cd->current.Frequency / 10000000; - algorithm = RRD_ALGORITHM_INCREMENTAL; - break; - - case PERF_ELAPSED_TIME: - // (D0 - N0) / F - algorithm = RRD_ALGORITHM_ABSOLUTE; - break; - - 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: - break; - } - - return rrddim_add(st, id, name, multiplier, divider, algorithm); -} - -#define VALID_DELTA(cd) \ - ((cd)->previous.Time > 0 && (cd)->current.Data >= (cd)->previous.Data && (cd)->current.Time > (cd)->previous.Time) - -collected_number perflib_rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, COUNTER_DATA *cd) { - ULONGLONG numerator = 0; - LONGLONG denominator = 0; - double doubleValue = 0.0; - collected_number value; - - switch(cd->current.CounterType) { - case PERF_COUNTER_COUNTER: - case PERF_SAMPLE_COUNTER: - case PERF_COUNTER_BULK_COUNT: - // (N1 - N0) / ((D1 - D0) / F) - value = (collected_number)cd->current.Data; - break; - - 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 - // (N1 - N0) / (D1 - D0) - value = (collected_number)cd->current.Data; - break; - - 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: - // 100 * (N1 - N0) / (D1 - D0) - value = (collected_number)cd->current.Data; - break; - - case PERF_COUNTER_TIMER_INV: - case PERF_100NSEC_TIMER_INV: - // 100 * (1 - ((N1 - N0) / (D1 - D0))) - if(!VALID_DELTA(cd)) return 0; - numerator = cd->current.Data - cd->previous.Data; - denominator = cd->current.Time - cd->previous.Time; - doubleValue = 100.0 * (1.0 - ((double)numerator / (double)denominator)); - // printf("Display value is (timer-inv): %f%%\n", doubleValue); - value = (collected_number)(doubleValue * COLLECTED_NUMBER_PRECISION); - break; - - case PERF_COUNTER_MULTI_TIMER: - // 100 * ((N1 - N0) / ((D1 - D0) / TB)) / B1 - if(!VALID_DELTA(cd)) return 0; - numerator = cd->current.Data - cd->previous.Data; - denominator = cd->current.Time - cd->previous.Time; - denominator /= cd->current.Frequency; - doubleValue = 100.0 * ((double)numerator / (double)denominator) / cd->current.MultiCounterData; - // printf("Display value is (multi-timer): %f%%\n", doubleValue); - value = (collected_number)(doubleValue * COLLECTED_NUMBER_PRECISION); - break; - - case PERF_100NSEC_MULTI_TIMER: - // 100 * ((N1 - N0) / (D1 - D0)) / B1 - if(!VALID_DELTA(cd)) return 0; - numerator = cd->current.Data - cd->previous.Data; - denominator = cd->current.Time - cd->previous.Time; - doubleValue = 100.0 * ((double)numerator / (double)denominator) / (double)cd->current.MultiCounterData; - // printf("Display value is (100ns multi-timer): %f%%\n", doubleValue); - value = (collected_number)(doubleValue * COLLECTED_NUMBER_PRECISION); - break; - - case PERF_COUNTER_MULTI_TIMER_INV: - case PERF_100NSEC_MULTI_TIMER_INV: - // 100 * (B1 - ((N1 - N0) / (D1 - D0))) - if(!VALID_DELTA(cd)) return 0; - numerator = cd->current.Data - cd->previous.Data; - denominator = cd->current.Time - cd->previous.Time; - doubleValue = 100.0 * ((double)cd->current.MultiCounterData - ((double)numerator / (double)denominator)); - // printf("Display value is (multi-timer-inv): %f%%\n", doubleValue); - value = (collected_number)(doubleValue * COLLECTED_NUMBER_PRECISION); - break; - - case PERF_COUNTER_RAWCOUNT: - case PERF_COUNTER_LARGE_RAWCOUNT: - // N as decimal - value = (collected_number)cd->current.Data; - break; - - case PERF_COUNTER_RAWCOUNT_HEX: - case PERF_COUNTER_LARGE_RAWCOUNT_HEX: - // N as hexadecimal - value = (collected_number)cd->current.Data; - break; - - case PERF_COUNTER_DELTA: - case PERF_COUNTER_LARGE_DELTA: - if(!VALID_DELTA(cd)) return 0; - value = (collected_number)(cd->current.Data - cd->previous.Data); - break; - - case PERF_RAW_FRACTION: - case PERF_LARGE_RAW_FRACTION: - // 100 * N / B - if(!cd->current.Time) return 0; - doubleValue = 100.0 * (double)cd->current.Data / (double)cd->current.Time; - // printf("Display value is (fraction): %f%%\n", doubleValue); - value = (collected_number)(doubleValue * COLLECTED_NUMBER_PRECISION); - break; - - default: - return 0; - } - - return rrddim_set_by_pointer(st, rd, value); -} - -/* -double perflibCalculateValue(RAW_DATA *current, RAW_DATA *previous) { - ULONGLONG numerator = 0; - LONGLONG denominator = 0; - double doubleValue = 0.0; - DWORD dwordValue = 0; - - if (NULL == previous) { - // Return error if the counter type requires two samples to calculate the value. - switch (current->CounterType) { - default: - if (PERF_DELTA_COUNTER != (current->CounterType & PERF_DELTA_COUNTER)) - break; - __fallthrough; - // fallthrough - - case PERF_AVERAGE_TIMER: // Special case. - case PERF_AVERAGE_BULK: // Special case. - // printf(" > The counter type requires two samples but only one sample was provided.\n"); - return NAN; - } - } - else { - if (current->CounterType != previous->CounterType) { - // printf(" > The samples have inconsistent counter types.\n"); - return NAN; - } - - // Check for integer overflow or bad data from provider (the data from - // sample 2 must be greater than the data from sample 1). - if (current->Data < previous->Data) - { - // Can happen for various reasons. Commonly occurs with the Process counterset when - // multiple processes have the same name and one of them starts or stops. - // Normally you'll just drop the older sample and continue. - // printf("> current (%llu) is smaller than previous (%llu).\n", current->Data, previous->Data); - return NAN; - } - } - - switch (current->CounterType) { - case PERF_COUNTER_COUNTER: - case PERF_SAMPLE_COUNTER: - case PERF_COUNTER_BULK_COUNT: - // (N1 - N0) / ((D1 - D0) / F) - numerator = current->Data - previous->Data; - denominator = current->Time - previous->Time; - dwordValue = (DWORD)(numerator / ((double)denominator / current->Frequency)); - //printf("Display value is (counter): %lu%s\n", (unsigned long)dwordValue, - // (previous->CounterType == PERF_SAMPLE_COUNTER) ? "" : "/sec"); - return (double)dwordValue; - - 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 - // (N1 - N0) / (D1 - D0) - numerator = current->Data - previous->Data; - denominator = current->Time - previous->Time; - doubleValue = (double)numerator / denominator; - if (previous->CounterType != PERF_AVERAGE_BULK) { - // printf("Display value is (queuelen): %f\n", doubleValue); - return doubleValue; - } - return NAN; - - 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: - // 100 * (N1 - N0) / (D1 - D0) - numerator = current->Data - previous->Data; - denominator = current->Time - previous->Time; - doubleValue = (double)(100 * numerator) / denominator; - // printf("Display value is (timer): %f%%\n", doubleValue); - return doubleValue; - - case PERF_COUNTER_TIMER_INV: - // 100 * (1 - ((N1 - N0) / (D1 - D0))) - numerator = current->Data - previous->Data; - denominator = current->Time - previous->Time; - doubleValue = 100 * (1 - ((double)numerator / denominator)); - // printf("Display value is (timer-inv): %f%%\n", doubleValue); - return doubleValue; - - case PERF_100NSEC_TIMER_INV: - // 100 * (1- (N1 - N0) / (D1 - D0)) - numerator = current->Data - previous->Data; - denominator = current->Time - previous->Time; - doubleValue = 100 * (1 - (double)numerator / denominator); - // printf("Display value is (100ns-timer-inv): %f%%\n", doubleValue); - return doubleValue; - - case PERF_COUNTER_MULTI_TIMER: - // 100 * ((N1 - N0) / ((D1 - D0) / TB)) / B1 - numerator = current->Data - previous->Data; - denominator = current->Time - previous->Time; - denominator /= current->Frequency; - doubleValue = 100 * ((double)numerator / denominator) / current->MultiCounterData; - // printf("Display value is (multi-timer): %f%%\n", doubleValue); - return doubleValue; - - case PERF_100NSEC_MULTI_TIMER: - // 100 * ((N1 - N0) / (D1 - D0)) / B1 - numerator = current->Data - previous->Data; - denominator = current->Time - previous->Time; - doubleValue = 100 * ((double)numerator / (double)denominator) / (double)current->MultiCounterData; - // printf("Display value is (100ns multi-timer): %f%%\n", doubleValue); - return doubleValue; - - case PERF_COUNTER_MULTI_TIMER_INV: - case PERF_100NSEC_MULTI_TIMER_INV: - // 100 * (B1 - ((N1 - N0) / (D1 - D0))) - numerator = current->Data - previous->Data; - denominator = current->Time - previous->Time; - doubleValue = 100.0 * ((double)current->MultiCounterData - ((double)numerator / (double)denominator)); - // printf("Display value is (multi-timer-inv): %f%%\n", doubleValue); - return doubleValue; - - case PERF_COUNTER_RAWCOUNT: - case PERF_COUNTER_LARGE_RAWCOUNT: - // N as decimal - // printf("Display value is (rawcount): %llu\n", current->Data); - return (double)current->Data; - - case PERF_COUNTER_RAWCOUNT_HEX: - case PERF_COUNTER_LARGE_RAWCOUNT_HEX: - // N as hexadecimal - // printf("Display value is (hex): 0x%llx\n", current->Data); - return (double)current->Data; - - case PERF_COUNTER_DELTA: - case PERF_COUNTER_LARGE_DELTA: - // N1 - N0 - // printf("Display value is (delta): %llu\n", current->Data - previous->Data); - return (double)(current->Data - previous->Data); - - case PERF_RAW_FRACTION: - case PERF_LARGE_RAW_FRACTION: - // 100 * N / B - doubleValue = 100.0 * (double)current->Data / (double)current->Time; - // printf("Display value is (fraction): %f%%\n", doubleValue); - return doubleValue; - - case PERF_AVERAGE_TIMER: - // ((N1 - N0) / TB) / (B1 - B0) - numerator = current->Data - previous->Data; - denominator = current->Time - previous->Time; - doubleValue = (double)numerator / (double)current->Frequency / (double)denominator; - // printf("Display value is (average timer): %f seconds\n", doubleValue); - return doubleValue; - - case PERF_ELAPSED_TIME: - // (D0 - N0) / F - doubleValue = (double)(current->Time - current->Data) / (double)current->Frequency; - // printf("Display value is (elapsed time): %f seconds\n", doubleValue); - return doubleValue; - - 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: - // printf(" > Non-printing counter type: 0x%08x\n", current->CounterType); - return NAN; - break; - - default: - // printf(" > Unrecognized counter type: 0x%08x\n", current->CounterType); - return NAN; - break; - } -} -*/ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "perflib-rrd.h" + +#define COLLECTED_NUMBER_PRECISION 10000 + +RRDDIM *perflib_rrddim_add(RRDSET *st, const char *id, const char *name, collected_number multiplier, collected_number divider, COUNTER_DATA *cd) { + RRD_ALGORITHM algorithm = RRD_ALGORITHM_ABSOLUTE; + + switch (cd->current.CounterType) { + case PERF_COUNTER_COUNTER: + case PERF_SAMPLE_COUNTER: + case PERF_COUNTER_BULK_COUNT: + // (N1 - N0) / ((D1 - D0) / F) + // multiplier *= cd->current.Frequency / 10000000; + // tested, the frequency is not that useful for netdata + // we get right results without it. + algorithm = RRD_ALGORITHM_INCREMENTAL; + break; + + 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 + // (N1 - N0) / (D1 - D0) + algorithm = RRD_ALGORITHM_INCREMENTAL; + break; + + 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: + // 100 * (N1 - N0) / (D1 - D0) + multiplier *= 100; + algorithm = RRD_ALGORITHM_INCREMENTAL; + break; + + case PERF_COUNTER_TIMER_INV: + case PERF_100NSEC_TIMER_INV: + // 100 * (1 - ((N1 - N0) / (D1 - D0))) + divider *= COLLECTED_NUMBER_PRECISION; + algorithm = RRD_ALGORITHM_ABSOLUTE; + break; + + case PERF_COUNTER_MULTI_TIMER: + // 100 * ((N1 - N0) / ((D1 - D0) / TB)) / B1 + divider *= COLLECTED_NUMBER_PRECISION; + algorithm = RRD_ALGORITHM_ABSOLUTE; + break; + + case PERF_100NSEC_MULTI_TIMER: + // 100 * ((N1 - N0) / (D1 - D0)) / B1 + divider *= COLLECTED_NUMBER_PRECISION; + algorithm = RRD_ALGORITHM_ABSOLUTE; + break; + + case PERF_COUNTER_MULTI_TIMER_INV: + case PERF_100NSEC_MULTI_TIMER_INV: + // 100 * (B1 - ((N1 - N0) / (D1 - D0))) + divider *= COLLECTED_NUMBER_PRECISION; + algorithm = RRD_ALGORITHM_ABSOLUTE; + break; + + case PERF_COUNTER_RAWCOUNT: + case PERF_COUNTER_LARGE_RAWCOUNT: + // N as decimal + algorithm = RRD_ALGORITHM_ABSOLUTE; + break; + + case PERF_COUNTER_RAWCOUNT_HEX: + case PERF_COUNTER_LARGE_RAWCOUNT_HEX: + // N as hexadecimal + algorithm = RRD_ALGORITHM_ABSOLUTE; + break; + + case PERF_COUNTER_DELTA: + case PERF_COUNTER_LARGE_DELTA: + // N1 - N0 + algorithm = RRD_ALGORITHM_ABSOLUTE; + break; + + case PERF_RAW_FRACTION: + case PERF_LARGE_RAW_FRACTION: + // 100 * N / B + algorithm = RRD_ALGORITHM_ABSOLUTE; + divider *= COLLECTED_NUMBER_PRECISION; + break; + + case PERF_AVERAGE_TIMER: + // ((N1 - N0) / TB) / (B1 - B0) + // divider *= cd->current.Frequency / 10000000; + algorithm = RRD_ALGORITHM_INCREMENTAL; + break; + + case PERF_ELAPSED_TIME: + // (D0 - N0) / F + algorithm = RRD_ALGORITHM_ABSOLUTE; + break; + + 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: + break; + } + + return rrddim_add(st, id, name, multiplier, divider, algorithm); +} + +#define VALID_DELTA(cd) \ + ((cd)->previous.Time > 0 && (cd)->current.Data >= (cd)->previous.Data && (cd)->current.Time > (cd)->previous.Time) + +collected_number perflib_rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, COUNTER_DATA *cd) { + ULONGLONG numerator = 0; + LONGLONG denominator = 0; + double doubleValue = 0.0; + collected_number value; + + switch(cd->current.CounterType) { + case PERF_COUNTER_COUNTER: + case PERF_SAMPLE_COUNTER: + case PERF_COUNTER_BULK_COUNT: + // (N1 - N0) / ((D1 - D0) / F) + value = (collected_number)cd->current.Data; + break; + + 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 + // (N1 - N0) / (D1 - D0) + value = (collected_number)cd->current.Data; + break; + + 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: + // 100 * (N1 - N0) / (D1 - D0) + value = (collected_number)cd->current.Data; + break; + + case PERF_COUNTER_TIMER_INV: + case PERF_100NSEC_TIMER_INV: + // 100 * (1 - ((N1 - N0) / (D1 - D0))) + if(!VALID_DELTA(cd)) return 0; + numerator = cd->current.Data - cd->previous.Data; + denominator = cd->current.Time - cd->previous.Time; + doubleValue = 100.0 * (1.0 - ((double)numerator / (double)denominator)); + // printf("Display value is (timer-inv): %f%%\n", doubleValue); + value = (collected_number)(doubleValue * COLLECTED_NUMBER_PRECISION); + break; + + case PERF_COUNTER_MULTI_TIMER: + // 100 * ((N1 - N0) / ((D1 - D0) / TB)) / B1 + if(!VALID_DELTA(cd)) return 0; + numerator = cd->current.Data - cd->previous.Data; + denominator = cd->current.Time - cd->previous.Time; + denominator /= cd->current.Frequency; + doubleValue = 100.0 * ((double)numerator / (double)denominator) / cd->current.MultiCounterData; + // printf("Display value is (multi-timer): %f%%\n", doubleValue); + value = (collected_number)(doubleValue * COLLECTED_NUMBER_PRECISION); + break; + + case PERF_100NSEC_MULTI_TIMER: + // 100 * ((N1 - N0) / (D1 - D0)) / B1 + if(!VALID_DELTA(cd)) return 0; + numerator = cd->current.Data - cd->previous.Data; + denominator = cd->current.Time - cd->previous.Time; + doubleValue = 100.0 * ((double)numerator / (double)denominator) / (double)cd->current.MultiCounterData; + // printf("Display value is (100ns multi-timer): %f%%\n", doubleValue); + value = (collected_number)(doubleValue * COLLECTED_NUMBER_PRECISION); + break; + + case PERF_COUNTER_MULTI_TIMER_INV: + case PERF_100NSEC_MULTI_TIMER_INV: + // 100 * (B1 - ((N1 - N0) / (D1 - D0))) + if(!VALID_DELTA(cd)) return 0; + numerator = cd->current.Data - cd->previous.Data; + denominator = cd->current.Time - cd->previous.Time; + doubleValue = 100.0 * ((double)cd->current.MultiCounterData - ((double)numerator / (double)denominator)); + // printf("Display value is (multi-timer-inv): %f%%\n", doubleValue); + value = (collected_number)(doubleValue * COLLECTED_NUMBER_PRECISION); + break; + + case PERF_COUNTER_RAWCOUNT: + case PERF_COUNTER_LARGE_RAWCOUNT: + // N as decimal + value = (collected_number)cd->current.Data; + break; + + case PERF_COUNTER_RAWCOUNT_HEX: + case PERF_COUNTER_LARGE_RAWCOUNT_HEX: + // N as hexadecimal + value = (collected_number)cd->current.Data; + break; + + case PERF_COUNTER_DELTA: + case PERF_COUNTER_LARGE_DELTA: + if(!VALID_DELTA(cd)) return 0; + value = (collected_number)(cd->current.Data - cd->previous.Data); + break; + + case PERF_RAW_FRACTION: + case PERF_LARGE_RAW_FRACTION: + // 100 * N / B + if(!cd->current.Time) return 0; + doubleValue = 100.0 * (double)cd->current.Data / (double)cd->current.Time; + // printf("Display value is (fraction): %f%%\n", doubleValue); + value = (collected_number)(doubleValue * COLLECTED_NUMBER_PRECISION); + break; + + default: + return 0; + } + + return rrddim_set_by_pointer(st, rd, value); +} + +/* +double perflibCalculateValue(RAW_DATA *current, RAW_DATA *previous) { + ULONGLONG numerator = 0; + LONGLONG denominator = 0; + double doubleValue = 0.0; + DWORD dwordValue = 0; + + if (NULL == previous) { + // Return error if the counter type requires two samples to calculate the value. + switch (current->CounterType) { + default: + if (PERF_DELTA_COUNTER != (current->CounterType & PERF_DELTA_COUNTER)) + break; + __fallthrough; + // fallthrough + + case PERF_AVERAGE_TIMER: // Special case. + case PERF_AVERAGE_BULK: // Special case. + // printf(" > The counter type requires two samples but only one sample was provided.\n"); + return NAN; + } + } + else { + if (current->CounterType != previous->CounterType) { + // printf(" > The samples have inconsistent counter types.\n"); + return NAN; + } + + // Check for integer overflow or bad data from provider (the data from + // sample 2 must be greater than the data from sample 1). + if (current->Data < previous->Data) + { + // Can happen for various reasons. Commonly occurs with the Process counterset when + // multiple processes have the same name and one of them starts or stops. + // Normally you'll just drop the older sample and continue. + // printf("> current (%llu) is smaller than previous (%llu).\n", current->Data, previous->Data); + return NAN; + } + } + + switch (current->CounterType) { + case PERF_COUNTER_COUNTER: + case PERF_SAMPLE_COUNTER: + case PERF_COUNTER_BULK_COUNT: + // (N1 - N0) / ((D1 - D0) / F) + numerator = current->Data - previous->Data; + denominator = current->Time - previous->Time; + dwordValue = (DWORD)(numerator / ((double)denominator / current->Frequency)); + //printf("Display value is (counter): %lu%s\n", (unsigned long)dwordValue, + // (previous->CounterType == PERF_SAMPLE_COUNTER) ? "" : "/sec"); + return (double)dwordValue; + + 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 + // (N1 - N0) / (D1 - D0) + numerator = current->Data - previous->Data; + denominator = current->Time - previous->Time; + doubleValue = (double)numerator / denominator; + if (previous->CounterType != PERF_AVERAGE_BULK) { + // printf("Display value is (queuelen): %f\n", doubleValue); + return doubleValue; + } + return NAN; + + 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: + // 100 * (N1 - N0) / (D1 - D0) + numerator = current->Data - previous->Data; + denominator = current->Time - previous->Time; + doubleValue = (double)(100 * numerator) / denominator; + // printf("Display value is (timer): %f%%\n", doubleValue); + return doubleValue; + + case PERF_COUNTER_TIMER_INV: + // 100 * (1 - ((N1 - N0) / (D1 - D0))) + numerator = current->Data - previous->Data; + denominator = current->Time - previous->Time; + doubleValue = 100 * (1 - ((double)numerator / denominator)); + // printf("Display value is (timer-inv): %f%%\n", doubleValue); + return doubleValue; + + case PERF_100NSEC_TIMER_INV: + // 100 * (1- (N1 - N0) / (D1 - D0)) + numerator = current->Data - previous->Data; + denominator = current->Time - previous->Time; + doubleValue = 100 * (1 - (double)numerator / denominator); + // printf("Display value is (100ns-timer-inv): %f%%\n", doubleValue); + return doubleValue; + + case PERF_COUNTER_MULTI_TIMER: + // 100 * ((N1 - N0) / ((D1 - D0) / TB)) / B1 + numerator = current->Data - previous->Data; + denominator = current->Time - previous->Time; + denominator /= current->Frequency; + doubleValue = 100 * ((double)numerator / denominator) / current->MultiCounterData; + // printf("Display value is (multi-timer): %f%%\n", doubleValue); + return doubleValue; + + case PERF_100NSEC_MULTI_TIMER: + // 100 * ((N1 - N0) / (D1 - D0)) / B1 + numerator = current->Data - previous->Data; + denominator = current->Time - previous->Time; + doubleValue = 100 * ((double)numerator / (double)denominator) / (double)current->MultiCounterData; + // printf("Display value is (100ns multi-timer): %f%%\n", doubleValue); + return doubleValue; + + case PERF_COUNTER_MULTI_TIMER_INV: + case PERF_100NSEC_MULTI_TIMER_INV: + // 100 * (B1 - ((N1 - N0) / (D1 - D0))) + numerator = current->Data - previous->Data; + denominator = current->Time - previous->Time; + doubleValue = 100.0 * ((double)current->MultiCounterData - ((double)numerator / (double)denominator)); + // printf("Display value is (multi-timer-inv): %f%%\n", doubleValue); + return doubleValue; + + case PERF_COUNTER_RAWCOUNT: + case PERF_COUNTER_LARGE_RAWCOUNT: + // N as decimal + // printf("Display value is (rawcount): %llu\n", current->Data); + return (double)current->Data; + + case PERF_COUNTER_RAWCOUNT_HEX: + case PERF_COUNTER_LARGE_RAWCOUNT_HEX: + // N as hexadecimal + // printf("Display value is (hex): 0x%llx\n", current->Data); + return (double)current->Data; + + case PERF_COUNTER_DELTA: + case PERF_COUNTER_LARGE_DELTA: + // N1 - N0 + // printf("Display value is (delta): %llu\n", current->Data - previous->Data); + return (double)(current->Data - previous->Data); + + case PERF_RAW_FRACTION: + case PERF_LARGE_RAW_FRACTION: + // 100 * N / B + doubleValue = 100.0 * (double)current->Data / (double)current->Time; + // printf("Display value is (fraction): %f%%\n", doubleValue); + return doubleValue; + + case PERF_AVERAGE_TIMER: + // ((N1 - N0) / TB) / (B1 - B0) + numerator = current->Data - previous->Data; + denominator = current->Time - previous->Time; + doubleValue = (double)numerator / (double)current->Frequency / (double)denominator; + // printf("Display value is (average timer): %f seconds\n", doubleValue); + return doubleValue; + + case PERF_ELAPSED_TIME: + // (D0 - N0) / F + doubleValue = (double)(current->Time - current->Data) / (double)current->Frequency; + // printf("Display value is (elapsed time): %f seconds\n", doubleValue); + return doubleValue; + + 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: + // printf(" > Non-printing counter type: 0x%08x\n", current->CounterType); + return NAN; + break; + + default: + // printf(" > Unrecognized counter type: 0x%08x\n", current->CounterType); + return NAN; + break; + } +} +*/ diff --git a/src/collectors/windows.plugin/perflib-rrd.h b/src/collectors/windows.plugin/perflib-rrd.h index 0b91de2ec..2347c5b1d 100644 --- a/src/collectors/windows.plugin/perflib-rrd.h +++ b/src/collectors/windows.plugin/perflib-rrd.h @@ -1,12 +1,11 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef NETDATA_PERFLIB_RRD_H -#define NETDATA_PERFLIB_RRD_H - -#include "perflib.h" -#include "database/rrd.h" - -RRDDIM *perflib_rrddim_add(RRDSET *st, const char *id, const char *name, collected_number multiplier, collected_number divider, COUNTER_DATA *cd); -collected_number perflib_rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, COUNTER_DATA *cd); - -#endif //NETDATA_PERFLIB_RRD_H +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef NETDATA_PERFLIB_RRD_H +#define NETDATA_PERFLIB_RRD_H + +#include "database/rrd.h" + +RRDDIM *perflib_rrddim_add(RRDSET *st, const char *id, const char *name, collected_number multiplier, collected_number divider, COUNTER_DATA *cd); +collected_number perflib_rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, COUNTER_DATA *cd); + +#endif //NETDATA_PERFLIB_RRD_H diff --git a/src/collectors/windows.plugin/perflib-storage.c b/src/collectors/windows.plugin/perflib-storage.c index d3b80052f..823ba2c04 100644 --- a/src/collectors/windows.plugin/perflib-storage.c +++ b/src/collectors/windows.plugin/perflib-storage.c @@ -1,317 +1,632 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "windows_plugin.h" -#include "windows-internals.h" - -#define _COMMON_PLUGIN_NAME PLUGIN_WINDOWS_NAME -#define _COMMON_PLUGIN_MODULE_NAME "PerflibStorage" -#include "../common-contexts/common-contexts.h" - -struct logical_disk { - bool collected_metadata; - - STRING *filesystem; - - RRDSET *st_disk_space; - RRDDIM *rd_disk_space_used; - RRDDIM *rd_disk_space_free; - - COUNTER_DATA percentDiskFree; - // COUNTER_DATA freeMegabytes; -}; - -struct physical_disk { - bool collected_metadata; - - STRING *device; - STRING *mount_point; - - ND_DISK_IO disk_io; - COUNTER_DATA diskReadBytesPerSec; - COUNTER_DATA diskWriteBytesPerSec; - - COUNTER_DATA percentIdleTime; - COUNTER_DATA percentDiskTime; - COUNTER_DATA percentDiskReadTime; - COUNTER_DATA percentDiskWriteTime; - COUNTER_DATA currentDiskQueueLength; - COUNTER_DATA averageDiskQueueLength; - COUNTER_DATA averageDiskReadQueueLength; - COUNTER_DATA averageDiskWriteQueueLength; - COUNTER_DATA averageDiskSecondsPerTransfer; - COUNTER_DATA averageDiskSecondsPerRead; - COUNTER_DATA averageDiskSecondsPerWrite; - COUNTER_DATA diskTransfersPerSec; - COUNTER_DATA diskReadsPerSec; - COUNTER_DATA diskWritesPerSec; - COUNTER_DATA diskBytesPerSec; - COUNTER_DATA averageDiskBytesPerTransfer; - COUNTER_DATA averageDiskBytesPerRead; - COUNTER_DATA averageDiskBytesPerWrite; - COUNTER_DATA splitIoPerSec; -}; - -struct physical_disk system_physical_total = { - .collected_metadata = true, -}; - -void dict_logical_disk_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { - struct logical_disk *ld = value; - - ld->percentDiskFree.key = "% Free Space"; - // ld->freeMegabytes.key = "Free Megabytes"; -} - -void initialize_physical_disk(struct physical_disk *pd) { - pd->percentIdleTime.key = "% Idle Time"; - pd->percentDiskTime.key = "% Disk Time"; - pd->percentDiskReadTime.key = "% Disk Read Time"; - pd->percentDiskWriteTime.key = "% Disk Write Time"; - pd->currentDiskQueueLength.key = "Current Disk Queue Length"; - pd->averageDiskQueueLength.key = "Avg. Disk Queue Length"; - pd->averageDiskReadQueueLength.key = "Avg. Disk Read Queue Length"; - pd->averageDiskWriteQueueLength.key = "Avg. Disk Write Queue Length"; - pd->averageDiskSecondsPerTransfer.key = "Avg. Disk sec/Transfer"; - pd->averageDiskSecondsPerRead.key = "Avg. Disk sec/Read"; - pd->averageDiskSecondsPerWrite.key = "Avg. Disk sec/Write"; - pd->diskTransfersPerSec.key = "Disk Transfers/sec"; - pd->diskReadsPerSec.key = "Disk Reads/sec"; - pd->diskWritesPerSec.key = "Disk Writes/sec"; - pd->diskBytesPerSec.key = "Disk Bytes/sec"; - pd->diskReadBytesPerSec.key = "Disk Read Bytes/sec"; - pd->diskWriteBytesPerSec.key = "Disk Write Bytes/sec"; - pd->averageDiskBytesPerTransfer.key = "Avg. Disk Bytes/Transfer"; - pd->averageDiskBytesPerRead.key = "Avg. Disk Bytes/Read"; - pd->averageDiskBytesPerWrite.key = "Avg. Disk Bytes/Write"; - pd->splitIoPerSec.key = "Split IO/Sec"; -} - -void dict_physical_disk_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { - struct physical_disk *pd = value; - initialize_physical_disk(pd); -} - -static DICTIONARY *logicalDisks = NULL, *physicalDisks = NULL; -static void initialize(void) { - initialize_physical_disk(&system_physical_total); - - logicalDisks = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | - DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct logical_disk)); - - dictionary_register_insert_callback(logicalDisks, dict_logical_disk_insert_cb, NULL); - - physicalDisks = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | - DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct physical_disk)); - - dictionary_register_insert_callback(physicalDisks, dict_physical_disk_insert_cb, NULL); -} - -static STRING *getFileSystemType(const char* diskName) { - if (!diskName || !*diskName) return NULL; - - char fileSystemNameBuffer[128] = {0}; // Buffer for file system name - char pathBuffer[256] = {0}; // Path buffer to accommodate different formats - DWORD serialNumber = 0; - DWORD maxComponentLength = 0; - DWORD fileSystemFlags = 0; - BOOL success; - - // Check if the input is likely a drive letter (e.g., "C:") - if (isalpha((uint8_t)diskName[0]) && diskName[1] == ':' && diskName[2] == '\0') - snprintf(pathBuffer, sizeof(pathBuffer), "%s\\", diskName); // Format as "C:\\" - else - // Assume it's a Volume GUID path or a device path - snprintf(pathBuffer, sizeof(pathBuffer), "\\\\.\\%s", diskName); // Format as "\\.\HarddiskVolume1" - - // Attempt to get the volume information - success = GetVolumeInformation( - pathBuffer, // Path to the disk - NULL, // We don't need the volume name - 0, // Size of volume name buffer is 0 - &serialNumber, // Volume serial number - &maxComponentLength, // Maximum component length - &fileSystemFlags, // File system flags - fileSystemNameBuffer, // File system name buffer - sizeof(fileSystemNameBuffer) // Size of file system name buffer - ); - - if (success && fileSystemNameBuffer[0]) { - char *s = fileSystemNameBuffer; - while(*s) { *s = tolower((uint8_t)*s); s++; } - return string_strdupz(fileSystemNameBuffer); // Duplicate the file system name - } - else - return NULL; -} - -static bool do_logical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every) { - DICTIONARY *dict = logicalDisks; - - PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "LogicalDisk"); - if(!pObjectType) return false; - - PERF_INSTANCE_DEFINITION *pi = NULL; - for(LONG i = 0; i < pObjectType->NumInstances ; i++) { - pi = perflibForEachInstance(pDataBlock, pObjectType, pi); - if(!pi) break; - - if(!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer))) - strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1); - - if(strcasecmp(windows_shared_buffer, "_Total") == 0) - continue; - - struct logical_disk *d = dictionary_set(dict, windows_shared_buffer, NULL, sizeof(*d)); - - if(!d->collected_metadata) { - d->filesystem = getFileSystemType(windows_shared_buffer); - d->collected_metadata = true; - } - - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentDiskFree); - // perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->freeMegabytes); - - if(!d->st_disk_space) { - d->st_disk_space = rrdset_create_localhost( - "disk_space" - , windows_shared_buffer, NULL - , windows_shared_buffer, "disk.space" - , "Disk Space Usage" - , "GiB" - , PLUGIN_WINDOWS_NAME - , "PerflibStorage" - , NETDATA_CHART_PRIO_DISKSPACE_SPACE - , update_every - , RRDSET_TYPE_STACKED - ); - - rrdlabels_add(d->st_disk_space->rrdlabels, "mount_point", windows_shared_buffer, RRDLABEL_SRC_AUTO); - // rrdlabels_add(d->st->rrdlabels, "mount_root", name, RRDLABEL_SRC_AUTO); - - if(d->filesystem) - rrdlabels_add(d->st_disk_space->rrdlabels, "filesystem", string2str(d->filesystem), RRDLABEL_SRC_AUTO); - - d->rd_disk_space_free = rrddim_add(d->st_disk_space, "avail", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); - d->rd_disk_space_used = rrddim_add(d->st_disk_space, "used", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); - } - - // percentDiskFree has the free space in Data and the size of the disk in Time, in MiB. - rrddim_set_by_pointer(d->st_disk_space, d->rd_disk_space_free, (collected_number)d->percentDiskFree.current.Data); - rrddim_set_by_pointer(d->st_disk_space, d->rd_disk_space_used, (collected_number)(d->percentDiskFree.current.Time - d->percentDiskFree.current.Data)); - rrdset_done(d->st_disk_space); - } - - return true; -} - -static void physical_disk_labels(RRDSET *st, void *data) { - struct physical_disk *d = data; - - if(d->device) - rrdlabels_add(st->rrdlabels, "device", string2str(d->device), RRDLABEL_SRC_AUTO); - - if (d->mount_point) - rrdlabels_add(st->rrdlabels, "mount_point", string2str(d->mount_point), RRDLABEL_SRC_AUTO); -} - -static bool do_physical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every) { - DICTIONARY *dict = physicalDisks; - - PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "PhysicalDisk"); - if(!pObjectType) return false; - - PERF_INSTANCE_DEFINITION *pi = NULL; - for (LONG i = 0; i < pObjectType->NumInstances; i++) { - pi = perflibForEachInstance(pDataBlock, pObjectType, pi); - if (!pi) - break; - - if (!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer))) - strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1); - - char *device = windows_shared_buffer; - char *mount_point = NULL; - - if((mount_point = strchr(device, ' '))) { - *mount_point = '\0'; - mount_point++; - } - - struct physical_disk *d; - bool is_system; - if (strcasecmp(windows_shared_buffer, "_Total") == 0) { - d = &system_physical_total; - is_system = true; - } - else { - d = dictionary_set(dict, device, NULL, sizeof(*d)); - is_system = false; - } - - if (!d->collected_metadata) { - // TODO collect metadata - device_type, serial, id - d->device = string_strdupz(device); - d->mount_point = string_strdupz(mount_point); - d->collected_metadata = true; - } - - if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskReadBytesPerSec) && - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskWriteBytesPerSec)) { - if(is_system) - common_system_io(d->diskReadBytesPerSec.current.Data, d->diskWriteBytesPerSec.current.Data, update_every); - else - common_disk_io( - &d->disk_io, - device, - NULL, - d->diskReadBytesPerSec.current.Data, - d->diskWriteBytesPerSec.current.Data, - update_every, - physical_disk_labels, - d); - } - - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentIdleTime); - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentDiskTime); - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentDiskReadTime); - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentDiskWriteTime); - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->currentDiskQueueLength); - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskQueueLength); - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskReadQueueLength); - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskWriteQueueLength); - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskSecondsPerTransfer); - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskSecondsPerRead); - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskSecondsPerWrite); - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskTransfersPerSec); - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskReadsPerSec); - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskWritesPerSec); - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskBytesPerSec); - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskBytesPerTransfer); - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskBytesPerRead); - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskBytesPerWrite); - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->splitIoPerSec); - } - - return true; -} - -int do_PerflibStorage(int update_every, usec_t dt __maybe_unused) { - static bool initialized = false; - - if(unlikely(!initialized)) { - initialize(); - initialized = true; - } - - DWORD id = RegistryFindIDByName("LogicalDisk"); - if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND) - return -1; - - PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id); - if(!pDataBlock) return -1; - - do_logical_disk(pDataBlock, update_every); - do_physical_disk(pDataBlock, update_every); - - return 0; -} +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "windows_plugin.h" +#include "windows-internals.h" + +#define _COMMON_PLUGIN_NAME PLUGIN_WINDOWS_NAME +#define _COMMON_PLUGIN_MODULE_NAME "PerflibStorage" +#include "../common-contexts/common-contexts.h" +#include "libnetdata/os/windows-wmi/windows-wmi.h" + +struct logical_disk { + usec_t last_collected; + bool collected_metadata; + + UINT DriveType; + DWORD SerialNumber; + bool readonly; + + STRING *filesystem; + + RRDSET *st_disk_space; + RRDDIM *rd_disk_space_used; + RRDDIM *rd_disk_space_free; + + COUNTER_DATA percentDiskFree; + // COUNTER_DATA freeMegabytes; +}; + +struct physical_disk { + usec_t last_collected; + bool collected_metadata; + + STRING *device; + STRING *mount_point; + STRING *manufacturer; + STRING *model; + STRING *media_type; + STRING *name; + STRING *device_id; + + ND_DISK_IO disk_io; + // COUNTER_DATA diskBytesPerSec; + COUNTER_DATA diskReadBytesPerSec; + COUNTER_DATA diskWriteBytesPerSec; + + ND_DISK_OPS disk_ops; + // COUNTER_DATA diskTransfersPerSec; + COUNTER_DATA diskReadsPerSec; + COUNTER_DATA diskWritesPerSec; + + ND_DISK_UTIL disk_util; + COUNTER_DATA percentIdleTime; + + ND_DISK_BUSY disk_busy; + COUNTER_DATA percentDiskTime; + + ND_DISK_IOTIME disk_iotime; + COUNTER_DATA percentDiskReadTime; + COUNTER_DATA percentDiskWriteTime; + + ND_DISK_QOPS disk_qops; + COUNTER_DATA currentDiskQueueLength; + // COUNTER_DATA averageDiskQueueLength; + // COUNTER_DATA averageDiskReadQueueLength; + // COUNTER_DATA averageDiskWriteQueueLength; + + ND_DISK_AWAIT disk_await; + COUNTER_DATA averageDiskSecondsPerRead; + COUNTER_DATA averageDiskSecondsPerWrite; + + ND_DISK_SVCTM disk_svctm; + COUNTER_DATA averageDiskSecondsPerTransfer; + + ND_DISK_AVGSZ disk_avgsz; + //COUNTER_DATA averageDiskBytesPerTransfer; + COUNTER_DATA averageDiskBytesPerRead; + COUNTER_DATA averageDiskBytesPerWrite; + + COUNTER_DATA splitIoPerSec; + RRDSET *st_split; + RRDDIM *rd_split; +}; + +struct physical_disk system_physical_total = { + .collected_metadata = true, +}; + +static void dict_logical_disk_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { + struct logical_disk *d = value; + + d->percentDiskFree.key = "% Free Space"; + // d->freeMegabytes.key = "Free Megabytes"; +} + +static void logical_disk_cleanup(struct logical_disk *d) { + rrdset_is_obsolete___safe_from_collector_thread(d->st_disk_space); +} + +static void physical_disk_initialize(struct physical_disk *d) { + d->percentIdleTime.key = "% Idle Time"; + d->percentDiskTime.key = "% Disk Time"; + d->percentDiskReadTime.key = "% Disk Read Time"; + d->percentDiskWriteTime.key = "% Disk Write Time"; + d->currentDiskQueueLength.key = "Current Disk Queue Length"; + // d->averageDiskQueueLength.key = "Avg. Disk Queue Length"; + // d->averageDiskReadQueueLength.key = "Avg. Disk Read Queue Length"; + // d->averageDiskWriteQueueLength.key = "Avg. Disk Write Queue Length"; + d->averageDiskSecondsPerTransfer.key = "Avg. Disk sec/Transfer"; + d->averageDiskSecondsPerRead.key = "Avg. Disk sec/Read"; + d->averageDiskSecondsPerWrite.key = "Avg. Disk sec/Write"; + // d->diskTransfersPerSec.key = "Disk Transfers/sec"; + d->diskReadsPerSec.key = "Disk Reads/sec"; + d->diskWritesPerSec.key = "Disk Writes/sec"; + // d->diskBytesPerSec.key = "Disk Bytes/sec"; + d->diskReadBytesPerSec.key = "Disk Read Bytes/sec"; + d->diskWriteBytesPerSec.key = "Disk Write Bytes/sec"; + // d->averageDiskBytesPerTransfer.key = "Avg. Disk Bytes/Transfer"; + d->averageDiskBytesPerRead.key = "Avg. Disk Bytes/Read"; + d->averageDiskBytesPerWrite.key = "Avg. Disk Bytes/Write"; + d->splitIoPerSec.key = "Split IO/Sec"; +} + +static void physical_disk_cleanup(struct physical_disk *d) { + string_freez(d->device); + string_freez(d->mount_point); + string_freez(d->manufacturer); + string_freez(d->model); + string_freez(d->media_type); + string_freez(d->name); + string_freez(d->device_id); + + rrdset_is_obsolete___safe_from_collector_thread(d->disk_io.st_io); + rrdset_is_obsolete___safe_from_collector_thread(d->disk_ops.st_ops); + rrdset_is_obsolete___safe_from_collector_thread(d->disk_util.st_util); + rrdset_is_obsolete___safe_from_collector_thread(d->disk_busy.st_busy); + rrdset_is_obsolete___safe_from_collector_thread(d->disk_iotime.st_iotime); + rrdset_is_obsolete___safe_from_collector_thread(d->disk_qops.st_qops); + rrdset_is_obsolete___safe_from_collector_thread(d->disk_await.st_await); + rrdset_is_obsolete___safe_from_collector_thread(d->disk_svctm.st_svctm); + rrdset_is_obsolete___safe_from_collector_thread(d->disk_avgsz.st_avgsz); + rrdset_is_obsolete___safe_from_collector_thread(d->st_split); +} + +void dict_physical_disk_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { + struct physical_disk *pd = value; + physical_disk_initialize(pd); +} + +static DICTIONARY *logicalDisks = NULL, *physicalDisks = NULL; +static void initialize(void) { + physical_disk_initialize(&system_physical_total); + + logicalDisks = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | + DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct logical_disk)); + + dictionary_register_insert_callback(logicalDisks, dict_logical_disk_insert_cb, NULL); + + physicalDisks = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | + DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct physical_disk)); + + dictionary_register_insert_callback(physicalDisks, dict_physical_disk_insert_cb, NULL); +} + +static STRING *getFileSystemType(struct logical_disk *d, const char* diskName) { + if (!diskName || !*diskName) return NULL; + + char fileSystemNameBuffer[128] = {0}; // Buffer for file system name + char pathBuffer[256] = {0}; // Path buffer to accommodate different formats + DWORD serialNumber = 0; + DWORD maxComponentLength = 0; + DWORD fileSystemFlags = 0; + BOOL success; + + // Check if the input is likely a drive letter (e.g., "C:") + if (isalpha((uint8_t)diskName[0]) && diskName[1] == ':' && diskName[2] == '\0') + snprintf(pathBuffer, sizeof(pathBuffer), "%s\\", diskName); // Format as "C:\" + else + // Assume it's a Volume GUID path or a device path + snprintf(pathBuffer, sizeof(pathBuffer), "\\\\.\\%s\\", diskName); // Format as "\\.\HarddiskVolume1\" + + d->DriveType = GetDriveTypeA(pathBuffer); + + // Attempt to get the volume information + success = GetVolumeInformationA( + pathBuffer, // Path to the disk + NULL, // We don't need the volume name + 0, // Size of volume name buffer is 0 + &serialNumber, // Volume serial number + &maxComponentLength, // Maximum component length + &fileSystemFlags, // File system flags + fileSystemNameBuffer, // File system name buffer + sizeof(fileSystemNameBuffer) // Size of file system name buffer + ); + + if(success) { + d->readonly = fileSystemFlags & FILE_READ_ONLY_VOLUME; + d->SerialNumber = serialNumber; + + if (fileSystemNameBuffer[0]) { + char *s = fileSystemNameBuffer; + while (*s) { + *s = tolower((uint8_t) *s); + s++; + } + return string_strdupz(fileSystemNameBuffer); // Duplicate the file system name + } + } + return NULL; +} + +static const char *drive_type_to_str(UINT type) { + switch(type) { + default: + case 0: return "unknown"; + case 1: return "norootdir"; + case 2: return "removable"; + case 3: return "fixed"; + case 4: return "remote"; + case 5: return "cdrom"; + case 6: return "ramdisk"; + } +} + +static bool do_logical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every, usec_t now_ut) { + DICTIONARY *dict = logicalDisks; + + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "LogicalDisk"); + if(!pObjectType) return false; + + PERF_INSTANCE_DEFINITION *pi = NULL; + for(LONG i = 0; i < pObjectType->NumInstances ; i++) { + pi = perflibForEachInstance(pDataBlock, pObjectType, pi); + if(!pi) break; + + if(!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer))) + strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1); + + if(strcasecmp(windows_shared_buffer, "_Total") == 0) + continue; + + struct logical_disk *d = dictionary_set(dict, windows_shared_buffer, NULL, sizeof(*d)); + d->last_collected = now_ut; + + if(!d->collected_metadata) { + d->filesystem = getFileSystemType(d, windows_shared_buffer); + d->collected_metadata = true; + } + + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentDiskFree); + // perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->freeMegabytes); + + if(!d->st_disk_space) { + d->st_disk_space = rrdset_create_localhost( + "disk_space" + , windows_shared_buffer + , NULL + , windows_shared_buffer + , "disk.space" + , "Disk Space Usage" + , "GiB" + , PLUGIN_WINDOWS_NAME + , "PerflibStorage" + , NETDATA_CHART_PRIO_DISKSPACE_SPACE + , update_every + , RRDSET_TYPE_STACKED + ); + + rrdlabels_add(d->st_disk_space->rrdlabels, "mount_point", windows_shared_buffer, RRDLABEL_SRC_AUTO); + rrdlabels_add(d->st_disk_space->rrdlabels, "drive_type", drive_type_to_str(d->DriveType), RRDLABEL_SRC_AUTO); + rrdlabels_add(d->st_disk_space->rrdlabels, "filesystem", d->filesystem ? string2str(d->filesystem) : "unknown", RRDLABEL_SRC_AUTO); + rrdlabels_add(d->st_disk_space->rrdlabels, "rw_mode", d->readonly ? "ro" : "rw", RRDLABEL_SRC_AUTO); + + { + char buf[UINT64_HEX_MAX_LENGTH]; + print_uint64_hex(buf, d->SerialNumber); + rrdlabels_add(d->st_disk_space->rrdlabels, "serial_number", buf, RRDLABEL_SRC_AUTO); + } + + d->rd_disk_space_free = rrddim_add(d->st_disk_space, "avail", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); + d->rd_disk_space_used = rrddim_add(d->st_disk_space, "used", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); + } + + // percentDiskFree has the free space in Data and the size of the disk in Time, in MiB. + rrddim_set_by_pointer(d->st_disk_space, d->rd_disk_space_free, (collected_number)d->percentDiskFree.current.Data); + rrddim_set_by_pointer(d->st_disk_space, d->rd_disk_space_used, (collected_number)(d->percentDiskFree.current.Time - d->percentDiskFree.current.Data)); + rrdset_done(d->st_disk_space); + } + + // cleanup + { + struct logical_disk *d; + dfe_start_write(dict, d) { + if(d->last_collected < now_ut) { + logical_disk_cleanup(d); + dictionary_del(dict, d_dfe.name); + } + } + dfe_done(d); + dictionary_garbage_collect(dict); + } + + return true; +} + +static void physical_disk_labels(RRDSET *st, void *data) { + struct physical_disk *d = data; + + if(d->device) + rrdlabels_add(st->rrdlabels, "device", string2str(d->device), RRDLABEL_SRC_AUTO); + + if (d->mount_point) + rrdlabels_add(st->rrdlabels, "mount_point", string2str(d->mount_point), RRDLABEL_SRC_AUTO); + +// if (d->manufacturer) +// rrdlabels_add(st->rrdlabels, "manufacturer", string2str(d->manufacturer), RRDLABEL_SRC_AUTO); + + if (d->model) + rrdlabels_add(st->rrdlabels, "model", string2str(d->model), RRDLABEL_SRC_AUTO); + +// if (d->media_type) +// rrdlabels_add(st->rrdlabels, "media_type", string2str(d->media_type), RRDLABEL_SRC_AUTO); + +// if (d->name) +// rrdlabels_add(st->rrdlabels, "name", string2str(d->name), RRDLABEL_SRC_AUTO); + + if (d->device_id) + rrdlabels_add(st->rrdlabels, "device_id", string2str(d->device_id), RRDLABEL_SRC_AUTO); +} + +static bool str_is_numeric(const char *s) { + while(*s) if(!isdigit((uint8_t)*s++)) return false; + return true; +} + +static inline double perflib_average_timer_ms(COUNTER_DATA *d) { + if(!d->updated) return 0.0; + + ULONGLONG data1 = d->current.Data; + ULONGLONG data0 = d->previous.Data; + LONGLONG time1 = d->current.Time; + LONGLONG time0 = d->previous.Time; + LONGLONG freq1 = d->current.Frequency; + + if(data1 >= data0 && time1 > time0 && time0 && freq1) + return ((double)(data1 - data0) / (double)(freq1 / MSEC_PER_SEC)) / (double)(time1 - time0); + + return 0; +} + +static inline uint64_t perflib_average_bulk(COUNTER_DATA *d) { + if(!d->updated) return 0; + + ULONGLONG data1 = d->current.Data; + ULONGLONG data0 = d->previous.Data; + LONGLONG time1 = d->current.Time; + LONGLONG time0 = d->previous.Time; + + if(data1 >= data0 && time1 > time0 && time0) + return (data1 - data0) / (time1 - time0); + + return 0; +} + +static inline uint64_t perflib_idle_time_percent(COUNTER_DATA *d) { + if(!d->updated) return 0.0; + + ULONGLONG data1 = d->current.Data; + ULONGLONG data0 = d->previous.Data; + LONGLONG time1 = d->current.Time; + LONGLONG time0 = d->previous.Time; + + if(data1 >= data0 && time1 > time0 && time0) { + uint64_t pcent = 100 * (data1 - data0) / (time1 - time0); + return pcent > 100 ? 100 : pcent; + } + + return 0; +} + +#define MAX_WMI_DRIVES 100 +static DiskDriveInfoWMI infos[MAX_WMI_DRIVES]; + +static bool do_physical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every, usec_t now_ut) { + DICTIONARY *dict = physicalDisks; + + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "PhysicalDisk"); + if(!pObjectType) return false; + + PERF_INSTANCE_DEFINITION *pi = NULL; + for (LONG i = 0; i < pObjectType->NumInstances; i++) { + pi = perflibForEachInstance(pDataBlock, pObjectType, pi); + if (!pi) + break; + + if (!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer))) + strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1); + + int device_index = -1; + char *device = windows_shared_buffer; + char mount_point[128]; mount_point[0] = '\0'; + + struct physical_disk *d; + bool is_system; + if (strcasecmp(windows_shared_buffer, "_Total") == 0) { + d = &system_physical_total; + is_system = true; + } + else { + char *space; + if((space = strchr(windows_shared_buffer, ' '))) { + *space++ = '\0'; + strncpyz(mount_point, space, sizeof(mount_point) - 1); + } + + if(str_is_numeric(windows_shared_buffer)) { + device_index = str2ull(device, NULL); + snprintfz(windows_shared_buffer, sizeof(windows_shared_buffer), "Disk %d", device_index); + device = windows_shared_buffer; + } + + d = dictionary_set(dict, device, NULL, sizeof(*d)); + is_system = false; + } + d->last_collected = now_ut; + + if (!d->collected_metadata) { + if(!is_system && device_index != -1) { + size_t infoCount = GetDiskDriveInfo(infos, _countof(infos)); + for(size_t k = 0; k < infoCount ; k++) { + if(infos[k].Index != device_index) + continue; + + d->manufacturer = string_strdupz(infos[k].Manufacturer); + d->model = string_strdupz(infos[k].Model); + d->media_type = string_strdupz(infos[k].MediaType); + d->name = string_strdupz(infos[k].Name); + d->device_id = string_strdupz(infos[k].DeviceID); + + break; + } + } + + d->device = string_strdupz(device); + d->mount_point = string_strdupz(mount_point); + d->collected_metadata = true; + } + + if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskReadBytesPerSec) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskWriteBytesPerSec)) { + if(is_system) + common_system_io( + d->diskReadBytesPerSec.current.Data, + d->diskWriteBytesPerSec.current.Data, + update_every); + else + common_disk_io( + &d->disk_io, + device, + NULL, + d->diskReadBytesPerSec.current.Data, + d->diskWriteBytesPerSec.current.Data, + update_every, + physical_disk_labels, + d); + } + + if(is_system) continue; + + if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskReadsPerSec) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskWritesPerSec)) { + + common_disk_ops( + &d->disk_ops, + device, + NULL, + d->diskReadsPerSec.current.Data, + d->diskWritesPerSec.current.Data, + update_every, + physical_disk_labels, + d); + } + + if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentIdleTime)) { + common_disk_util( + &d->disk_util, + device, + NULL, + 100 - perflib_idle_time_percent(&d->percentIdleTime), + update_every, + physical_disk_labels, + d); + } + + if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentDiskTime)) { + common_disk_busy( + &d->disk_busy, + device, + NULL, + d->percentDiskTime.current.Data / NS100_PER_MS, + update_every, + physical_disk_labels, + d); + } + + if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentDiskReadTime) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentDiskWriteTime)) { + + common_disk_iotime( + &d->disk_iotime, + device, + NULL, + d->percentDiskReadTime.current.Data / NS100_PER_MS, + d->percentDiskWriteTime.current.Data / NS100_PER_MS, + update_every, + physical_disk_labels, + d); + } + + if(perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->currentDiskQueueLength)) { + common_disk_qops( + &d->disk_qops, + device, + NULL, + d->currentDiskQueueLength.current.Data, + update_every, + physical_disk_labels, + d); + } + + if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskSecondsPerRead) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskSecondsPerWrite)) { + + common_disk_await( + &d->disk_await, + device, + NULL, + perflib_average_timer_ms(&d->averageDiskSecondsPerRead), + perflib_average_timer_ms(&d->averageDiskSecondsPerWrite), + update_every, + physical_disk_labels, + d); + } + + if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskSecondsPerTransfer)) { + common_disk_svctm( + &d->disk_svctm, + device, + NULL, + perflib_average_timer_ms(&d->averageDiskSecondsPerTransfer), + update_every, + physical_disk_labels, + d); + } + + if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskBytesPerRead) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskBytesPerWrite)) { + + common_disk_avgsz( + &d->disk_avgsz, + device, + NULL, + perflib_average_bulk(&d->averageDiskBytesPerRead), + perflib_average_bulk(&d->averageDiskBytesPerWrite), + update_every, + physical_disk_labels, + d); + } + + if(perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->splitIoPerSec)) { + if (!d->st_split) { + d->st_split = rrdset_create_localhost( + "disk_split", + device, + NULL, + "iops", + "disk.split", + "Split I/O Operations", + "operations/s", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_DISK_SPLIT, + update_every, + RRDSET_TYPE_LINE + ); + + d->rd_split = rrddim_add(d->st_split, "discards", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + physical_disk_labels(d->st_split, d); + } + + rrddim_set_by_pointer(d->st_split, d->rd_split, d->splitIoPerSec.current.Data); + rrdset_done(d->st_split); + } + } + + // cleanup + { + struct physical_disk *d; + dfe_start_write(dict, d) { + if(d->last_collected < now_ut) { + physical_disk_cleanup(d); + dictionary_del(dict, d_dfe.name); + } + } + dfe_done(d); + dictionary_garbage_collect(dict); + } + + return true; +} + +int do_PerflibStorage(int update_every, usec_t dt __maybe_unused) { + static bool initialized = false; + + if(unlikely(!initialized)) { + initialize(); + initialized = true; + } + + DWORD id = RegistryFindIDByName("LogicalDisk"); + if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND) + return -1; + + PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id); + if(!pDataBlock) return -1; + + usec_t now_ut = now_monotonic_usec(); + do_logical_disk(pDataBlock, update_every, now_ut); + do_physical_disk(pDataBlock, update_every, now_ut); + + return 0; +} diff --git a/src/collectors/windows.plugin/perflib-thermalzone.c b/src/collectors/windows.plugin/perflib-thermalzone.c new file mode 100644 index 000000000..f85ba019f --- /dev/null +++ b/src/collectors/windows.plugin/perflib-thermalzone.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "windows_plugin.h" +#include "windows-internals.h" + +struct thermal_zone { + RRDSET *st; + RRDDIM *rd; + + COUNTER_DATA thermalZoneTemperature; +}; + +static inline void initialize_thermal_zone_keys(struct thermal_zone *p) { + p->thermalZoneTemperature.key = "Temperature"; +} + +void dict_thermal_zone_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { + struct thermal_zone *p = value; + initialize_thermal_zone_keys(p); +} + +static DICTIONARY *thermal_zones = NULL; + +static void initialize(void) { + thermal_zones = dictionary_create_advanced( + DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct thermal_zone)); + + dictionary_register_insert_callback(thermal_zones, dict_thermal_zone_insert_cb, NULL); +} + +static bool do_thermal_zones(PERF_DATA_BLOCK *pDataBlock, int update_every) { + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "Thermal Zone Information"); + if (!pObjectType) + return false; + + PERF_INSTANCE_DEFINITION *pi = NULL; + for (LONG i = 0; i < pObjectType->NumInstances; i++) { + pi = perflibForEachInstance(pDataBlock, pObjectType, pi); + if (!pi) + break; + + if (!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer))) + strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1); + + struct thermal_zone *p = dictionary_set(thermal_zones, windows_shared_buffer, NULL, sizeof(*p)); + + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->thermalZoneTemperature); + + // https://learn.microsoft.com/en-us/windows-hardware/design/device-experiences/design-guide + if (!p->st) { + char id[RRD_ID_LENGTH_MAX + 1]; + snprintfz(id, RRD_ID_LENGTH_MAX, "thermalzone_%s_temperature", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st = rrdset_create_localhost( + "system", + id, + NULL, + "thermalzone", + "system.thermalzone_temperature", + "Thermal zone temperature", + "Celsius", + PLUGIN_WINDOWS_NAME, + "ThermalZone", + NETDATA_CHART_PRIO_WINDOWS_THERMAL_ZONES, + update_every, + RRDSET_TYPE_LINE); + + p->rd = rrddim_add(p->st, id, "temperature", 1, 1, RRD_ALGORITHM_ABSOLUTE); + + rrdlabels_add(p->st->rrdlabels, "thermalzone", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + // Convert to Celsius before to plot + NETDATA_DOUBLE kTemperature = (NETDATA_DOUBLE)p->thermalZoneTemperature.current.Data; + kTemperature -= 273.15; + + rrddim_set_by_pointer(p->st, p->rd, (collected_number)kTemperature); + rrdset_done(p->st); + } + + return true; +} + +int do_PerflibThermalZone(int update_every, usec_t dt __maybe_unused) { + static bool initialized = false; + + if (unlikely(!initialized)) { + initialize(); + initialized = true; + } + + DWORD id = RegistryFindIDByName("Thermal Zone Information"); + if (id == PERFLIB_REGISTRY_NAME_NOT_FOUND) + return -1; + + PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id); + if (!pDataBlock) + return -1; + + do_thermal_zones(pDataBlock, update_every); + + return 0; +} diff --git a/src/collectors/windows.plugin/perflib-web-service.c b/src/collectors/windows.plugin/perflib-web-service.c new file mode 100644 index 000000000..159f6e0ee --- /dev/null +++ b/src/collectors/windows.plugin/perflib-web-service.c @@ -0,0 +1,669 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "windows_plugin.h" +#include "windows-internals.h" + +struct web_service { + RRDSET *st_request_rate; + RRDDIM *rd_request_rate; + + RRDSET *st_request_by_type_rate; + RRDDIM *rd_request_options_rate; + RRDDIM *rd_request_get_rate; + RRDDIM *rd_request_post_rate; + RRDDIM *rd_request_head_rate; + RRDDIM *rd_request_put_rate; + RRDDIM *rd_request_delete_rate; + RRDDIM *rd_request_trace_rate; + RRDDIM *rd_request_move_rate; + RRDDIM *rd_request_copy_rate; + RRDDIM *rd_request_mkcol_rate; + RRDDIM *rd_request_propfind_rate; + RRDDIM *rd_request_proppatch_rate; + RRDDIM *rd_request_search_rate; + RRDDIM *rd_request_lock_rate; + RRDDIM *rd_request_unlock_rate; + RRDDIM *rd_request_other_rate; + + RRDSET *st_traffic; + RRDDIM *rd_traffic_received; + RRDDIM *rd_traffic_sent; + + RRDSET *st_file_transfer; + RRDDIM *rd_files_received; + RRDDIM *rd_files_sent; + + RRDSET *st_curr_connections; + RRDDIM *rd_curr_connections; + + RRDSET *st_connections_attemps; + RRDDIM *rd_connections_attemps; + + RRDSET *st_user_count; + RRDDIM *rd_user_anonymous; + RRDDIM *rd_user_nonanonymous; + + RRDSET *st_isapi_extension_request_count; + RRDDIM *rd_isapi_extension_request_count; + + RRDSET *st_isapi_extension_request_rate; + RRDDIM *rd_isapi_extension_request_rate; + + RRDSET *st_error_rate; + RRDDIM *rd_error_rate_locked; + RRDDIM *rd_error_rate_not_found; + + RRDSET *st_logon_attemps; + RRDDIM *rd_logon_attemps; + + RRDSET *st_service_uptime; + RRDDIM *rd_service_uptime; + + COUNTER_DATA IISCurrentAnonymousUser; + COUNTER_DATA IISCurrentNonAnonymousUsers; + COUNTER_DATA IISCurrentConnections; + COUNTER_DATA IISCurrentISAPIExtRequests; + COUNTER_DATA IISUptime; + + COUNTER_DATA IISReceivedBytesTotal; + COUNTER_DATA IISSentBytesTotal; + COUNTER_DATA IISIPAPIExtRequestsTotal; + COUNTER_DATA IISConnAttemptsAllInstancesTotal; + COUNTER_DATA IISFilesReceivedTotal; + COUNTER_DATA IISFilesSentTotal; + COUNTER_DATA IISLogonAttemptsTotal; + COUNTER_DATA IISLockedErrorsTotal; + COUNTER_DATA IISNotFoundErrorsTotal; + + COUNTER_DATA IISRequestsOptions; + COUNTER_DATA IISRequestsGet; + COUNTER_DATA IISRequestsPost; + COUNTER_DATA IISRequestsHead; + COUNTER_DATA IISRequestsPut; + COUNTER_DATA IISRequestsDelete; + COUNTER_DATA IISRequestsTrace; + COUNTER_DATA IISRequestsMove; + COUNTER_DATA IISRequestsCopy; + COUNTER_DATA IISRequestsMkcol; + COUNTER_DATA IISRequestsPropfind; + COUNTER_DATA IISRequestsProppatch; + COUNTER_DATA IISRequestsSearch; + COUNTER_DATA IISRequestsLock; + COUNTER_DATA IISRequestsUnlock; + COUNTER_DATA IISRequestsOther; +}; + +static inline void initialize_web_service_keys(struct web_service *p) { + p->IISCurrentAnonymousUser.key = "Current Anonymous Users"; + p->IISCurrentNonAnonymousUsers.key = "Current NonAnonymous Users"; + p->IISCurrentConnections.key = "Current Connections"; + p->IISCurrentISAPIExtRequests.key = "Current ISAPI Extension Requests"; + p->IISUptime.key = "Service Uptime"; + + p->IISReceivedBytesTotal.key = "Total Bytes Received"; + p->IISSentBytesTotal.key = "Total Bytes Sent"; + p->IISIPAPIExtRequestsTotal.key = "Total ISAPI Extension Requests"; + p->IISConnAttemptsAllInstancesTotal.key = "Total Connection Attempts (all instances)"; + p->IISFilesReceivedTotal.key = "Total Files Received"; + p->IISFilesSentTotal.key = "Total Files Sent"; + p->IISLogonAttemptsTotal.key = "Total Logon Attempts"; + p->IISLockedErrorsTotal.key = "Total Locked Errors"; + p->IISNotFoundErrorsTotal.key = "Total Not Found Errors"; + + p->IISRequestsOptions.key = "Options Requests/sec"; + p->IISRequestsGet.key = "Get Requests/sec"; + p->IISRequestsPost.key = "Post Requests/sec"; + p->IISRequestsHead.key = "Head Requests/sec"; + p->IISRequestsPut.key = "Put Requests/sec"; + p->IISRequestsDelete.key = "Delete Requests/sec"; + p->IISRequestsTrace.key = "Trace Requests/sec"; + p->IISRequestsMove.key = "Move Requests/sec"; + p->IISRequestsCopy.key = "Copy Requests/sec"; + p->IISRequestsMkcol.key = "Mkcol Requests/sec"; + p->IISRequestsPropfind.key = "Propfind Requests/sec"; + p->IISRequestsProppatch.key = "Proppatch Requests/sec"; + p->IISRequestsSearch.key = "Search Requests/sec"; + p->IISRequestsLock.key = "Lock Requests/sec"; + p->IISRequestsUnlock.key = "Unlock Requests/sec"; + p->IISRequestsOther.key = "Other Request Methods/sec"; +} + +void dict_web_service_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { + struct web_service *p = value; + initialize_web_service_keys(p); +} + +static DICTIONARY *web_services = NULL; + +static void initialize(void) { + web_services = dictionary_create_advanced( + DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct web_service)); + + dictionary_register_insert_callback(web_services, dict_web_service_insert_cb, NULL); +} + +static bool do_web_services(PERF_DATA_BLOCK *pDataBlock, int update_every) { + char id[RRD_ID_LENGTH_MAX + 1]; + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "Web Service"); + if (!pObjectType) + return false; + + PERF_INSTANCE_DEFINITION *pi = NULL; + for (LONG i = 0; i < pObjectType->NumInstances; i++) { + pi = perflibForEachInstance(pDataBlock, pObjectType, pi); + if (!pi) + break; + + if (!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer))) + strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1); + + // We are not ploting _Total here, because cloud will group the sites + if (strcasecmp(windows_shared_buffer, "_Total") == 0) { + continue; + } + + struct web_service *p = dictionary_set(web_services, windows_shared_buffer, NULL, sizeof(*p)); + + if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISReceivedBytesTotal) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISSentBytesTotal)) { + if (!p->st_traffic) { + snprintfz(id, RRD_ID_LENGTH_MAX, "website_%s_traffic", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st_traffic = rrdset_create_localhost( + "iis", + id, + NULL, + "traffic", + "iis.website_traffic", + "Website traffic", + "bytes/s", + PLUGIN_WINDOWS_NAME, + "PerflibWebService", + PRIO_WEBSITE_IIS_TRAFFIC, + update_every, + RRDSET_TYPE_AREA); + + p->rd_traffic_received = rrddim_add(p->st_traffic, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_traffic_sent = rrddim_add(p->st_traffic, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrdlabels_add(p->st_traffic->rrdlabels, "website", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_traffic, p->rd_traffic_received, (collected_number)p->IISReceivedBytesTotal.current.Data); + rrddim_set_by_pointer( + p->st_traffic, p->rd_traffic_sent, (collected_number)p->IISSentBytesTotal.current.Data); + + rrdset_done(p->st_traffic); + } + + if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISFilesReceivedTotal) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISFilesSentTotal)) { + if (!p->st_file_transfer) { + snprintfz(id, RRD_ID_LENGTH_MAX, "website_%s_ftp_file_transfer_rate", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st_file_transfer = rrdset_create_localhost( + "iis", + id, + NULL, + "traffic", + "iis.website_ftp_file_transfer_rate", + "Website FTP file transfer rate", + "files/s", + PLUGIN_WINDOWS_NAME, + "PerflibWebService", + PRIO_WEBSITE_IIS_FTP_FILE_TRANSFER_RATE, + update_every, + RRDSET_TYPE_LINE); + + p->rd_files_received = rrddim_add(p->st_file_transfer, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_files_sent = rrddim_add(p->st_file_transfer, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrdlabels_add(p->st_file_transfer->rrdlabels, "website", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_file_transfer, p->rd_files_received, (collected_number)p->IISFilesReceivedTotal.current.Data); + rrddim_set_by_pointer( + p->st_file_transfer, p->rd_files_sent, (collected_number)p->IISFilesSentTotal.current.Data); + + rrdset_done(p->st_file_transfer); + } + + if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISCurrentConnections)) { + if (!p->st_curr_connections) { + snprintfz(id, RRD_ID_LENGTH_MAX, "website_%s_active_connections_count", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st_curr_connections = rrdset_create_localhost( + "iis", + id, + NULL, + "connections", + "iis.website_active_connections_count", + "Website active connections", + "connections", + PLUGIN_WINDOWS_NAME, + "PerflibWebService1", + PRIO_WEBSITE_IIS_ACTIVE_CONNECTIONS_COUNT, + update_every, + RRDSET_TYPE_LINE); + + p->rd_curr_connections = rrddim_add(p->st_curr_connections, "active", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rrdlabels_add(p->st_curr_connections->rrdlabels, "website", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_curr_connections, + p->rd_curr_connections, + (collected_number)p->IISCurrentConnections.current.Data); + + rrdset_done(p->st_curr_connections); + } + + if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISConnAttemptsAllInstancesTotal)) { + if (!p->st_connections_attemps) { + snprintfz(id, RRD_ID_LENGTH_MAX, "website_%s_connection_attempts_rate", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st_connections_attemps = rrdset_create_localhost( + "iis", + id, + NULL, + "connections", + "iis.website_connection_attempts_rate", + "Website connections attempts", + "attempts/s", + PLUGIN_WINDOWS_NAME, + "PerflibWebService", + PRIO_WEBSITE_IIS_CONNECTIONS_ATTEMP, + update_every, + RRDSET_TYPE_LINE); + + p->rd_connections_attemps = + rrddim_add(p->st_connections_attemps, "connection", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add( + p->st_connections_attemps->rrdlabels, "website", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_connections_attemps, + p->rd_connections_attemps, + (collected_number)p->IISCurrentConnections.current.Data); + + rrdset_done(p->st_connections_attemps); + } + + if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISCurrentAnonymousUser) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISCurrentNonAnonymousUsers)) { + if (!p->st_user_count) { + snprintfz(id, RRD_ID_LENGTH_MAX, "website_%s_users_count", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st_user_count = rrdset_create_localhost( + "iis", + id, + NULL, + "requests", + "iis.website_users_count", + "Website users with pending requests", + "users", + PLUGIN_WINDOWS_NAME, + "PerflibWebService", + PRIO_WEBSITE_IIS_USERS, + update_every, + RRDSET_TYPE_STACKED); + + p->rd_user_anonymous = rrddim_add(p->st_user_count, "anonymous", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + p->rd_user_nonanonymous = + rrddim_add(p->st_user_count, "non_anonymous", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + rrdlabels_add(p->st_user_count->rrdlabels, "website", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_user_count, p->rd_user_anonymous, (collected_number)p->IISCurrentAnonymousUser.current.Data); + + rrddim_set_by_pointer( + p->st_user_count, + p->rd_user_nonanonymous, + (collected_number)p->IISCurrentNonAnonymousUsers.current.Data); + + rrdset_done(p->st_user_count); + } + + if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISCurrentISAPIExtRequests)) { + if (!p->st_isapi_extension_request_count) { + snprintfz(id, RRD_ID_LENGTH_MAX, "website_%s_isapi_extension_requests_count", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st_isapi_extension_request_count = rrdset_create_localhost( + "iis", + id, + NULL, + "requests", + "iis.website_isapi_extension_requests_count", + "ISAPI extension requests", + "requests", + PLUGIN_WINDOWS_NAME, + "PerflibWebService", + PRIO_WEBSITE_IIS_ISAPI_EXT_REQUEST_COUNT, + update_every, + RRDSET_TYPE_LINE); + + p->rd_isapi_extension_request_count = + rrddim_add(p->st_isapi_extension_request_count, "isapi", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + rrdlabels_add( + p->st_isapi_extension_request_count->rrdlabels, "website", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_isapi_extension_request_count, + p->rd_isapi_extension_request_count, + (collected_number)p->IISCurrentISAPIExtRequests.current.Data); + + rrdset_done(p->st_isapi_extension_request_count); + } + + if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISIPAPIExtRequestsTotal)) { + if (!p->st_isapi_extension_request_rate) { + snprintfz(id, RRD_ID_LENGTH_MAX, "website_%s_isapi_extension_requests_rate", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st_isapi_extension_request_rate = rrdset_create_localhost( + "iis", + id, + NULL, + "requests", + "iis.website_isapi_extension_requests_rate", + "Website extensions request", + "requests/s", + PLUGIN_WINDOWS_NAME, + "PerflibWebService", + PRIO_WEBSITE_IIS_ISAPI_EXT_REQUEST_RATE, + update_every, + RRDSET_TYPE_LINE); + + p->rd_isapi_extension_request_rate = + rrddim_add(p->st_isapi_extension_request_rate, "isapi", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add( + p->st_isapi_extension_request_rate->rrdlabels, "website", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_isapi_extension_request_rate, + p->rd_isapi_extension_request_rate, + (collected_number)p->IISIPAPIExtRequestsTotal.current.Data); + + rrdset_done(p->st_isapi_extension_request_rate); + } + + if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISLockedErrorsTotal) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISNotFoundErrorsTotal)) { + if (!p->st_error_rate) { + snprintfz(id, RRD_ID_LENGTH_MAX, "website_%s_errors_rate", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st_error_rate = rrdset_create_localhost( + "iis", + id, + NULL, + "requests", + "iis.website_errors_rate", + "Website errors", + "errors/s", + PLUGIN_WINDOWS_NAME, + "PerflibWebService", + PRIO_WEBSITE_IIS_USERS, + update_every, + RRDSET_TYPE_STACKED); + + p->rd_error_rate_locked = + rrddim_add(p->st_error_rate, "document_locked", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_error_rate_not_found = + rrddim_add(p->st_error_rate, "document_not_found", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(p->st_error_rate->rrdlabels, "website", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_error_rate, p->rd_error_rate_locked, (collected_number)p->IISLockedErrorsTotal.current.Data); + + rrddim_set_by_pointer( + p->st_error_rate, p->rd_error_rate_not_found, (collected_number)p->IISNotFoundErrorsTotal.current.Data); + + rrdset_done(p->st_error_rate); + } + + if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISLogonAttemptsTotal)) { + if (!p->st_logon_attemps) { + snprintfz(id, RRD_ID_LENGTH_MAX, "website_%s_logon_attempts_rate", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st_logon_attemps = rrdset_create_localhost( + "iis", + id, + NULL, + "logon", + "iis.website_logon_attempts_rate", + "Website logon attempts", + "attempts/s", + PLUGIN_WINDOWS_NAME, + "PerflibWebService", + PRIO_WEBSITE_IIS_LOGON_ATTEMPTS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_logon_attemps = rrddim_add(p->st_logon_attemps, "logon", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(p->st_logon_attemps->rrdlabels, "website", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_logon_attemps, p->rd_logon_attemps, (collected_number)p->IISLogonAttemptsTotal.current.Data); + + rrdset_done(p->st_logon_attemps); + } + + if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISUptime)) { + if (!p->st_service_uptime) { + snprintfz(id, RRD_ID_LENGTH_MAX, "website_%s_uptime", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st_service_uptime = rrdset_create_localhost( + "iis", + id, + NULL, + "uptime", + "iis.website_uptime", + "Website uptime", + "seconds", + PLUGIN_WINDOWS_NAME, + "PerflibWebService", + PRIO_WEBSITE_IIS_UPTIME, + update_every, + RRDSET_TYPE_LINE); + + p->rd_service_uptime = rrddim_add(p->st_service_uptime, "uptime", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + rrdlabels_add(p->st_service_uptime->rrdlabels, "website", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_service_uptime, p->rd_service_uptime, (collected_number)p->IISUptime.current.Data); + + rrdset_done(p->st_service_uptime); + } + + if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISRequestsOptions) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISRequestsGet) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISRequestsPost) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISRequestsHead) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISRequestsPut) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISRequestsDelete) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISRequestsTrace) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISRequestsMove) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISRequestsCopy) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISRequestsMkcol) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISRequestsPropfind) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISRequestsProppatch) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISRequestsSearch) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISRequestsLock) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISRequestsUnlock) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->IISRequestsOther)) { + if (!p->st_request_rate) { + snprintfz(id, RRD_ID_LENGTH_MAX, "website_%s_requests_rate", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st_request_rate = rrdset_create_localhost( + "iis", + id, + NULL, + "requests", + "iis.website_requests_rate", + "Website requests rate", + "requests/s", + PLUGIN_WINDOWS_NAME, + "PerflibWebService", + PRIO_WEBSITE_IIS_REQUESTS_RATE, + update_every, + RRDSET_TYPE_LINE); + + p->rd_request_rate = rrddim_add(p->st_request_rate, "requests", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(p->st_request_rate->rrdlabels, "website", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + uint64_t requests = + p->IISRequestsOptions.current.Data + p->IISRequestsGet.current.Data + p->IISRequestsPost.current.Data + + p->IISRequestsHead.current.Data + p->IISRequestsPut.current.Data + p->IISRequestsDelete.current.Data + + p->IISRequestsTrace.current.Data + p->IISRequestsMove.current.Data + p->IISRequestsCopy.current.Data + + p->IISRequestsMkcol.current.Data + p->IISRequestsPropfind.current.Data + + p->IISRequestsProppatch.current.Data + p->IISRequestsSearch.current.Data + + p->IISRequestsLock.current.Data + p->IISRequestsUnlock.current.Data + p->IISRequestsOther.current.Data; + + rrddim_set_by_pointer(p->st_request_rate, p->rd_request_rate, (collected_number)requests); + + rrdset_done(p->st_request_rate); + + if (!p->st_request_by_type_rate) { + snprintfz(id, RRD_ID_LENGTH_MAX, "website_%s_requests_by_type_rate", windows_shared_buffer); + netdata_fix_chart_name(id); + p->st_request_by_type_rate = rrdset_create_localhost( + "iis", + id, + NULL, + "requests", + "iis.website_requests_by_type_rate", + "Website requests rate", + "requests/s", + PLUGIN_WINDOWS_NAME, + "PerflibWebService", + PRIO_WEBSITE_IIS_REQUESTS_BY_TYPE_RATE, + update_every, + RRDSET_TYPE_STACKED); + + p->rd_request_options_rate = rrddim_add(p->st_request_by_type_rate, "options", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_request_get_rate = rrddim_add(p->st_request_by_type_rate, "get", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_request_post_rate = rrddim_add(p->st_request_by_type_rate, "post", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_request_head_rate = rrddim_add(p->st_request_by_type_rate, "head", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_request_put_rate = rrddim_add(p->st_request_by_type_rate, "put", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_request_delete_rate = rrddim_add(p->st_request_by_type_rate, "delete", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_request_trace_rate = rrddim_add(p->st_request_by_type_rate, "trace", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_request_move_rate = rrddim_add(p->st_request_by_type_rate, "move", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_request_copy_rate = rrddim_add(p->st_request_by_type_rate, "copy", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_request_mkcol_rate = rrddim_add(p->st_request_by_type_rate, "mkcol", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_request_propfind_rate = rrddim_add(p->st_request_by_type_rate, "propfind", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_request_proppatch_rate = rrddim_add(p->st_request_by_type_rate, "proppatch", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_request_search_rate = rrddim_add(p->st_request_by_type_rate, "search", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_request_lock_rate = rrddim_add(p->st_request_by_type_rate, "lock", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_request_unlock_rate = rrddim_add(p->st_request_by_type_rate, "unlock", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_request_other_rate = rrddim_add(p->st_request_by_type_rate, "other", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(p->st_request_by_type_rate->rrdlabels, "website", windows_shared_buffer, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_request_by_type_rate, + p->rd_request_options_rate, + (collected_number)p->IISRequestsOptions.current.Data); + rrddim_set_by_pointer( + p->st_request_by_type_rate, + p->rd_request_get_rate, + (collected_number)p->IISRequestsGet.current.Data); + rrddim_set_by_pointer( + p->st_request_by_type_rate, + p->rd_request_post_rate, + (collected_number)p->IISRequestsPost.current.Data); + rrddim_set_by_pointer( + p->st_request_by_type_rate, + p->rd_request_head_rate, + (collected_number)p->IISRequestsHead.current.Data); + rrddim_set_by_pointer( + p->st_request_by_type_rate, + p->rd_request_put_rate, + (collected_number)p->IISRequestsPut.current.Data); + rrddim_set_by_pointer( + p->st_request_by_type_rate, + p->rd_request_delete_rate, + (collected_number)p->IISRequestsDelete.current.Data); + rrddim_set_by_pointer( + p->st_request_by_type_rate, + p->rd_request_trace_rate, + (collected_number)p->IISRequestsTrace.current.Data); + rrddim_set_by_pointer( + p->st_request_by_type_rate, + p->rd_request_move_rate, + (collected_number)p->IISRequestsMove.current.Data); + rrddim_set_by_pointer( + p->st_request_by_type_rate, + p->rd_request_copy_rate, + (collected_number)p->IISRequestsCopy.current.Data); + rrddim_set_by_pointer( + p->st_request_by_type_rate, + p->rd_request_mkcol_rate, + (collected_number)p->IISRequestsMkcol.current.Data); + rrddim_set_by_pointer( + p->st_request_by_type_rate, + p->rd_request_propfind_rate, + (collected_number)p->IISRequestsPropfind.current.Data); + rrddim_set_by_pointer( + p->st_request_by_type_rate, + p->rd_request_proppatch_rate, + (collected_number)p->IISRequestsProppatch.current.Data); + rrddim_set_by_pointer( + p->st_request_by_type_rate, + p->rd_request_search_rate, + (collected_number)p->IISRequestsSearch.current.Data); + rrddim_set_by_pointer( + p->st_request_by_type_rate, + p->rd_request_lock_rate, + (collected_number)p->IISRequestsLock.current.Data); + rrddim_set_by_pointer( + p->st_request_by_type_rate, + p->rd_request_unlock_rate, + (collected_number)p->IISRequestsUnlock.current.Data); + rrddim_set_by_pointer( + p->st_request_by_type_rate, + p->rd_request_other_rate, + (collected_number)p->IISRequestsOther.current.Data); + + rrdset_done(p->st_request_by_type_rate); + } + } + + return true; +} + +int do_PerflibWebService(int update_every, usec_t dt __maybe_unused) { + static bool initialized = false; + + if (unlikely(!initialized)) { + initialize(); + initialized = true; + } + + DWORD id = RegistryFindIDByName("Web Service"); + if (id == PERFLIB_REGISTRY_NAME_NOT_FOUND) + return -1; + + PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id); + if (!pDataBlock) + return -1; + + do_web_services(pDataBlock, update_every); + + return 0; +} diff --git a/src/collectors/windows.plugin/perflib.c b/src/collectors/windows.plugin/perflib.c deleted file mode 100644 index 4df48acfb..000000000 --- a/src/collectors/windows.plugin/perflib.c +++ /dev/null @@ -1,671 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "perflib.h" - -// -------------------------------------------------------------------------------- - -// 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) { - WCHAR *tempBuffer; // Temporary buffer for Unicode data - DWORD charsCopied = 0; - BOOL free_tempBuffer; - - if (CodePage == 0) { - // Input is already Unicode (UTF-16) - tempBuffer = (WCHAR *)start; - charsCopied = length / sizeof(WCHAR); // Convert byte length to number of WCHARs - free_tempBuffer = FALSE; - } - else { - // Convert the multi-byte instance name to Unicode (UTF-16) - // Calculate maximum possible characters in UTF-16 - - int charCount = MultiByteToWideChar(CodePage, 0, start, (int)length, NULL, 0); - tempBuffer = (WCHAR *)malloc(charCount * sizeof(WCHAR)); - if (!tempBuffer) return FALSE; - - charsCopied = MultiByteToWideChar(CodePage, 0, start, (int)length, tempBuffer, charCount); - if (charsCopied == 0) { - free(tempBuffer); - dst[0] = '\0'; - return FALSE; - } - - free_tempBuffer = TRUE; - } - - // 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) { - if (free_tempBuffer) free(tempBuffer); - dst[0] = '\0'; // Ensure the buffer is null-terminated even on failure - return FALSE; - } - - dst[bytesCopied] = '\0'; // Ensure buffer is null-terminated - if (free_tempBuffer) free(tempBuffer); // Free temporary buffer if used - 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) { - 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 = getInstanceCounterBlock(pDataBlock, pObjectType, pInstance); - - 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; -} - -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; -} diff --git a/src/collectors/windows.plugin/perflib.h b/src/collectors/windows.plugin/perflib.h deleted file mode 100644 index deba4e9a3..000000000 --- a/src/collectors/windows.plugin/perflib.h +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef NETDATA_PERFLIB_H -#define NETDATA_PERFLIB_H - -#include "libnetdata/libnetdata.h" -#include - -const char *RegistryFindNameByID(DWORD id); -const char *RegistryFindHelpByID(DWORD id); -DWORD RegistryFindIDByName(const char *name); -#define PERFLIB_REGISTRY_NAME_NOT_FOUND (DWORD)-1 - -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; - const char *key; - DWORD OverwriteCounterType; // if set, the counter type will be overwritten once read - RAW_DATA current; - RAW_DATA previous; -} COUNTER_DATA; - -#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 //NETDATA_PERFLIB_H diff --git a/src/collectors/windows.plugin/windows-internals.h b/src/collectors/windows.plugin/windows-internals.h index 1b7cccc73..70d44b902 100644 --- a/src/collectors/windows.plugin/windows-internals.h +++ b/src/collectors/windows.plugin/windows-internals.h @@ -1,18 +1,17 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef NETDATA_WINDOWS_INTERNALS_H -#define NETDATA_WINDOWS_INTERNALS_H - -#include - -static inline ULONGLONG FileTimeToULL(FILETIME ft) { - ULARGE_INTEGER ul; - ul.LowPart = ft.dwLowDateTime; - ul.HighPart = ft.dwHighDateTime; - return ul.QuadPart; -} - -#include "perflib.h" -#include "perflib-rrd.h" - -#endif //NETDATA_WINDOWS_INTERNALS_H +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef NETDATA_WINDOWS_INTERNALS_H +#define NETDATA_WINDOWS_INTERNALS_H + +#include "libnetdata/libnetdata.h" + +static inline ULONGLONG FileTimeToULL(FILETIME ft) { + ULARGE_INTEGER ul; + ul.LowPart = ft.dwLowDateTime; + ul.HighPart = ft.dwHighDateTime; + return ul.QuadPart; +} + +#include "perflib-rrd.h" + +#endif //NETDATA_WINDOWS_INTERNALS_H diff --git a/src/collectors/windows.plugin/windows_plugin.c b/src/collectors/windows.plugin/windows_plugin.c index 35ef857be..74b72e0ce 100644 --- a/src/collectors/windows.plugin/windows_plugin.c +++ b/src/collectors/windows.plugin/windows_plugin.c @@ -13,18 +13,26 @@ static struct proc_module { } win_modules[] = { // system metrics - {.name = "GetSystemUptime", .dim = "GetSystemUptime", .func = do_GetSystemUptime}, - {.name = "GetSystemRAM", .dim = "GetSystemRAM", .func = do_GetSystemRAM}, + {.name = "GetSystemUptime", .dim = "GetSystemUptime", .enabled = CONFIG_BOOLEAN_YES, .func = do_GetSystemUptime}, + {.name = "GetSystemRAM", .dim = "GetSystemRAM", .enabled = CONFIG_BOOLEAN_YES, .func = do_GetSystemRAM}, // the same is provided by PerflibProcessor, with more detailed analysis - //{.name = "GetSystemCPU", .dim = "GetSystemCPU", .func = do_GetSystemCPU}, + //{.name = "GetSystemCPU", .dim = "GetSystemCPU", .enabled = CONFIG_BOOLEAN_YES, .func = do_GetSystemCPU}, - {.name = "PerflibProcesses", .dim = "PerflibProcesses", .func = do_PerflibProcesses}, - {.name = "PerflibProcessor", .dim = "PerflibProcessor", .func = do_PerflibProcessor}, - {.name = "PerflibMemory", .dim = "PerflibMemory", .func = do_PerflibMemory}, - {.name = "PerflibStorage", .dim = "PerflibStorage", .func = do_PerflibStorage}, - {.name = "PerflibNetwork", .dim = "PerflibNetwork", .func = do_PerflibNetwork}, - {.name = "PerflibObjects", .dim = "PerflibObjects", .func = do_PerflibObjects}, + {.name = "PerflibProcesses", .dim = "PerflibProcesses", .enabled = CONFIG_BOOLEAN_YES, .func = do_PerflibProcesses}, + {.name = "PerflibProcessor", .dim = "PerflibProcessor", .enabled = CONFIG_BOOLEAN_YES, .func = do_PerflibProcessor}, + {.name = "PerflibMemory", .dim = "PerflibMemory", .enabled = CONFIG_BOOLEAN_YES, .func = do_PerflibMemory}, + {.name = "PerflibStorage", .dim = "PerflibStorage", .enabled = CONFIG_BOOLEAN_YES, .func = do_PerflibStorage}, + {.name = "PerflibNetwork", .dim = "PerflibNetwork", .enabled = CONFIG_BOOLEAN_YES, .func = do_PerflibNetwork}, + {.name = "PerflibObjects", .dim = "PerflibObjects", .enabled = CONFIG_BOOLEAN_YES, .func = do_PerflibObjects}, + {.name = "PerflibHyperV", .dim = "PerflibHyperV", .enabled = CONFIG_BOOLEAN_YES, .func = do_PerflibHyperV}, + + {.name = "PerflibThermalZone", .dim = "PerflibThermalZone", .enabled = CONFIG_BOOLEAN_NO, .func = do_PerflibThermalZone}, + + {.name = "PerflibWebService", .dim = "PerflibWebService", .enabled = CONFIG_BOOLEAN_YES, .func = do_PerflibWebService}, + {.name = "PerflibMSSQL", .dim = "PerflibMSSQL", .enabled = CONFIG_BOOLEAN_YES, .func = do_PerflibMSSQL}, + + {.name = "PerflibNetFramework", .dim = "PerflibNetFramework", .enabled = CONFIG_BOOLEAN_YES, .func = do_PerflibNetFramework}, // the terminator of this array {.name = NULL, .dim = NULL, .func = NULL} @@ -66,15 +74,14 @@ void *win_plugin_main(void *ptr) { for(i = 0; win_modules[i].name; i++) { struct proc_module *pm = &win_modules[i]; - pm->enabled = config_get_boolean("plugin:windows", pm->name, CONFIG_BOOLEAN_YES); + pm->enabled = config_get_boolean("plugin:windows", pm->name, pm->enabled); pm->rd = NULL; worker_register_job_name(i, win_modules[i].dim); } - usec_t step = localhost->rrd_update_every * USEC_PER_SEC; heartbeat_t hb; - heartbeat_init(&hb); + heartbeat_init(&hb, localhost->rrd_update_every * USEC_PER_SEC); #define LGS_MODULE_ID 0 @@ -86,7 +93,7 @@ void *win_plugin_main(void *ptr) { while(service_running(SERVICE_COLLECTORS)) { worker_is_idle(); - usec_t hb_dt = heartbeat_next(&hb, step); + usec_t hb_dt = heartbeat_next(&hb); if(unlikely(!service_running(SERVICE_COLLECTORS))) break; diff --git a/src/collectors/windows.plugin/windows_plugin.h b/src/collectors/windows.plugin/windows_plugin.h index 73c1ecda1..3852653ed 100644 --- a/src/collectors/windows.plugin/windows_plugin.h +++ b/src/collectors/windows.plugin/windows_plugin.h @@ -24,7 +24,82 @@ int do_PerflibProcesses(int update_every, usec_t dt); int do_PerflibProcessor(int update_every, usec_t dt); int do_PerflibMemory(int update_every, usec_t dt); int do_PerflibObjects(int update_every, usec_t dt); +int do_PerflibThermalZone(int update_every, usec_t dt); +int do_PerflibWebService(int update_every, usec_t dt); +int do_PerflibMSSQL(int update_every, usec_t dt); +int do_PerflibNetFramework(int update_every, usec_t dt); -#include "perflib.h" +enum PERFLIB_PRIO { + PRIO_WEBSITE_IIS_REQUESTS_RATE = 21000, // PRIO selected, because APPS is using 20YYY + PRIO_WEBSITE_IIS_REQUESTS_BY_TYPE_RATE, + PRIO_WEBSITE_IIS_TRAFFIC, + PRIO_WEBSITE_IIS_FTP_FILE_TRANSFER_RATE, + PRIO_WEBSITE_IIS_ACTIVE_CONNECTIONS_COUNT, + PRIO_WEBSITE_IIS_CONNECTIONS_ATTEMP, + PRIO_WEBSITE_IIS_USERS, + PRIO_WEBSITE_IIS_ISAPI_EXT_REQUEST_COUNT, + PRIO_WEBSITE_IIS_ISAPI_EXT_REQUEST_RATE, + PRIO_WEBSITE_IIS_ERRORS_RATE, + PRIO_WEBSITE_IIS_LOGON_ATTEMPTS, + PRIO_WEBSITE_IIS_UPTIME, + + PRIO_MSSQL_USER_CONNECTIONS, + + PRIO_MSSQL_DATABASE_TRANSACTIONS, + PRIO_MSSQL_DATABASE_ACTIVE_TRANSACTIONS, + PRIO_MSSQL_DATABASE_WRITE_TRANSACTIONS, + PRIO_MSSQL_DATABASE_BACKUP_RESTORE_OPERATIONS, + PRIO_MSSQL_DATABASE_LOG_FLUSHES, + PRIO_MSSQL_DATABASE_LOG_FLUSHED, + + PRIO_MSSQL_DATABASE_DATA_FILE_SIZE, + + PRIO_MSSQL_STATS_BATCH_REQUEST, + PRIO_MSSQL_STATS_COMPILATIONS, + PRIO_MSSQL_STATS_RECOMPILATIONS, + PRIO_MSSQL_STATS_AUTO_PARAMETRIZATION, + PRIO_MSSQL_STATS_SAFE_AUTO_PARAMETRIZATION, + + PRIO_MSSQL_BLOCKED_PROCESSES, + + PRIO_MSSQL_BUFF_CACHE_HIT_RATIO, + PRIO_MSSQL_BUFF_MAN_IOPS, + PRIO_MSSQL_BUFF_CHECKPOINT_PAGES, + PRIO_MSSQL_BUFF_METHODS_PAGE_SPLIT, + PRIO_MSSQL_BUFF_PAGE_LIFE_EXPECTANCY, + + PRIO_MSSQL_MEMMGR_CONNECTION_MEMORY_BYTES, + PRIO_MSSQL_MEMMGR_TOTAL_SERVER, + PRIO_MSSQL_MEMMGR_EXTERNAL_BENEFIT_OF_MEMORY, + PRIO_MSSQL_MEMMGR_PENDING_MEMORY_GRANTS, + + PRIO_MSSQL_LOCKS_WAIT, + PRIO_MSSQL_LOCKS_DEADLOCK, + + PRIO_MSSQL_SQL_ERRORS, + + PRIO_NETFRAMEWORK_CLR_EXCEPTION_THROWN, + PRIO_NETFRAMEWORK_CLR_EXCEPTION_FILTERS, + PRIO_NETFRAMEWORK_CLR_EXCEPTION_FINALLYS, + PRIO_NETFRAMEWORK_CLR_EXCEPTION_THROW_TO_CATCH_DEPTH, + + PRIO_NETFRAMEWORK_CLR_INTEROP_CCW, + PRIO_NETFRAMEWORK_CLR_INTEROP_MARSHALLING, + PRIO_NETFRAMEWORK_CLR_INTEROP_STUBS_CREATED, + + PRIO_NETFRAMEWORK_CLR_JIT_METHODS, + PRIO_NETFRAMEWORK_CLR_JIT_TIME, + PRIO_NETFRAMEWORK_CLR_JIT_STANDARD_FAILURES, + PRIO_NETFRAMEWORK_CLR_JIT_IL_BYTES, + + PRIO_NETFRAMEWORK_CLR_LOADING_HEAP_SIZE, + PRIO_NETFRAMEWORK_CLR_LOADING_APP_DOMAINS_LOADED, + PRIO_NETFRAMEWORK_CLR_LOADING_APP_DOMAINS_UNLOADED, + PRIO_NETFRAMEWORK_CLR_LOADING_ASSEMBLIES_LOADED, + PRIO_NETFRAMEWORK_CLR_LOADING_CLASSES_LOADED, + PRIO_NETFRAMEWORK_CLR_LOADING_CLASS_LOAD_FAILURE +}; + +int do_PerflibHyperV(int update_every, usec_t dt); #endif //NETDATA_WINDOWS_PLUGIN_H -- cgit v1.2.3