From 55275e7db1663de3a43d5b6f1a07d2371c7bb5e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Thu, 15 Oct 2026 19:58:31 +0200 Subject: [PATCH] hyperv: fix misnamed root partition metrics, counter types and units (#2720) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- UPGRADE.md | 11 + docs/collector.hyperv.md | 46 +++-- internal/collector/hyperv/AGENTS.md | 24 ++- .../hyperv/hyperv_dynamic_memory_balancer.go | 2 +- .../hyperv_hypervisor_root_partition.go | 54 ++--- ...yperv_hypervisor_root_virtual_processor.go | 50 ++++- .../hyperv_hypervisor_virtual_processor.go | 49 ++++- .../hyperv_metric_types_internal_test.go | 189 ++++++++++++++++++ .../hyperv/hyperv_virtual_storage_device.go | 30 ++- 9 files changed, 368 insertions(+), 87 deletions(-) create mode 100644 internal/collector/hyperv/hyperv_metric_types_internal_test.go diff --git a/UPGRADE.md b/UPGRADE.md index 83f9d9c4..468f2e3f 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -213,6 +213,17 @@ Renamed: | 0.31 | 0.32 | Note | |------------------------------------------------------|----------------------------------------------------------------|------------------------------------| | `windows_hyperv_virtual_storage_device_throughput` | `windows_hyperv_virtual_storage_device_throughput_total` | Now a counter, use `rate()` | +| `windows_hyperv_virtual_storage_device_normalized_throughput` | `windows_hyperv_virtual_storage_device_normalized_throughput_total` | Now a counter, use `rate()` | +| `windows_hyperv_io_quota_replenishment_rate` | `windows_hyperv_virtual_storage_device_io_quota_replenishment_total` | Now a counter, use `rate()` | +| `windows_hyperv_root_partition_preferred_numa_node_index` | `windows_hyperv_root_partition_gpa_pages` | Same value; the old name was wrong | +| `windows_hyperv_root_partition_physical_pages_allocated` | `windows_hyperv_root_partition_skipped_timer_ticks_total` | Same value; the old name was wrong, now a counter | +| `windows_hyperv_root_partition_device_interrupt_throttle_events` | `windows_hyperv_root_partition_device_interrupt_throttle_events_total` | Now a counter | +| `windows_hyperv_root_partition_io_tlb_flush_cost` | `windows_hyperv_root_partition_io_tlb_flush_seconds_total` | Same value, cumulative seconds, now a counter. Divide its rate by `rate(windows_hyperv_root_partition_io_tlb_flushes_total)` for the average time per flush | +| `windows_hyperv_root_partition_io_tlb_flush` | `windows_hyperv_root_partition_io_tlb_flushes_total` | Same value | +| `windows_hyperv_root_partition_gpa_space_modifications` | `windows_hyperv_root_partition_gpa_space_modifications_total` | Same value | +| `windows_hyperv_root_partition_virtual_tlb_flush_entries` | `windows_hyperv_root_partition_virtual_tlb_flush_entries_total` | Same value | +| `windows_hyperv_hypervisor_virtual_processor_cpu_wait_time_per_dispatch_total` | `windows_hyperv_hypervisor_virtual_processor_cpu_wait_seconds_total` | Same value, cumulative seconds. Divide its rate by `rate(windows_hyperv_hypervisor_virtual_processor_dispatches_total)` for the average wait per dispatch | +| `windows_hyperv_hypervisor_root_virtual_cpu_wait_time_per_dispatch_total` | `windows_hyperv_hypervisor_root_virtual_processor_cpu_wait_seconds_total` | Same value, cumulative seconds. Divide its rate by `rate(windows_hyperv_hypervisor_root_virtual_processor_dispatches_total)` for the average wait per dispatch | | `windows_filetime_mtime_timestamp_seconds` | `windows_file_mtime_timestamp_seconds` | See [filetime](#filetime-collector-removed) | | `windows_memory_commit_limit` | `windows_memory_commit_limit_bytes` | Unit suffix added, same value | | `windows_memory_pool_paged_allocs_total` | `windows_memory_pool_paged_allocs` | Now a gauge, query it without `rate()` | diff --git a/docs/collector.hyperv.md b/docs/collector.hyperv.md index 82762d7c..222690aa 100644 --- a/docs/collector.hyperv.md +++ b/docs/collector.hyperv.md @@ -157,20 +157,20 @@ Multiply by the number of threads per core to get the ratio per **physical** cor | `windows_hyperv_root_partition_device_dma_errors` | An indicator of illegal DMA requests generated by all devices assigned to the partition | gauge | None | | `windows_hyperv_root_partition_device_interrupt_errors` | An indicator of illegal interrupt requests generated by all devices assigned to the partition | gauge | None | | `windows_hyperv_root_partition_device_interrupt_mappings` | The number of device interrupt mappings used by the partition | gauge | None | -| `windows_hyperv_root_partition_device_interrupt_throttle_events` | The number of times an interrupt from a device assigned to the partition was temporarily throttled because the device was generating too many interrupts | gauge | None | -| `windows_hyperv_root_partition_preferred_numa_node_index` | The number of pages present in the GPA space of the partition (zero for root partition) | gauge | None | -| `windows_hyperv_root_partition_gpa_space_modifications` | The rate of modifications to the GPA space of the partition | counter | None | -| `windows_hyperv_root_partition_io_tlb_flush_cost` | The average time (in nanoseconds) spent processing an I/O TLB flush | gauge | None | -| `windows_hyperv_root_partition_io_tlb_flush` | The rate of flushes of I/O TLBs of the partition | counter | None | +| `windows_hyperv_root_partition_device_interrupt_throttle_events_total` | The number of times an interrupt from a device assigned to the partition was temporarily throttled because the device was generating too many interrupts | counter | None | +| `windows_hyperv_root_partition_gpa_pages` | The number of pages present in the GPA space of the partition (zero for root partition) | gauge | None | +| `windows_hyperv_root_partition_gpa_space_modifications_total` | The number of modifications to the GPA space of the partition | counter | None | +| `windows_hyperv_root_partition_io_tlb_flush_seconds_total` | The time spent processing I/O TLB flushes. Divide its rate by the rate of `windows_hyperv_root_partition_io_tlb_flushes_total` for the average time per flush | counter | None | +| `windows_hyperv_root_partition_io_tlb_flushes_total` | The number of flushes of I/O TLBs of the partition | counter | None | | `windows_hyperv_root_partition_recommended_virtual_tlb_size` | The recommended number of pages to be deposited for the virtual TLB | gauge | None | -| `windows_hyperv_root_partition_physical_pages_allocated` | The number of timer interrupts skipped for the partition | gauge | None | +| `windows_hyperv_root_partition_skipped_timer_ticks_total` | The number of timer interrupts skipped for the partition | counter | None | | `windows_hyperv_root_partition_1G_device_pages` | The number of 1G pages present in the device space of the partition | gauge | None | | `windows_hyperv_root_partition_1G_gpa_pages` | The number of 1G pages present in the GPA space of the partition | gauge | None | | `windows_hyperv_root_partition_2M_device_pages` | The number of 2M pages present in the device space of the partition | gauge | None | | `windows_hyperv_root_partition_2M_gpa_pages` | The number of 2M pages present in the GPA space of the partition | gauge | None | | `windows_hyperv_root_partition_4K_device_pages` | The number of 4K pages present in the device space of the partition | gauge | None | | `windows_hyperv_root_partition_4K_gpa_pages` | The number of 4K pages present in the GPA space of the partition | gauge | None | -| `windows_hyperv_root_partition_virtual_tlb_flush_entries` | The rate of flushes of the entire virtual TLB | counter | None | +| `windows_hyperv_root_partition_virtual_tlb_flush_entries_total` | The number of flushes of the entire virtual TLB | counter | None | | `windows_hyperv_root_partition_virtual_tlb_pages` | The number of pages used by the virtual TLB of the partition | gauge | None | @@ -178,9 +178,10 @@ Multiply by the number of threads per core to get the ratio per **physical** cor | Name | Description | Type | Labels | |---------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------|---------|----------------| -| `windows_hyperv_hypervisor_root_virtual_processor_time_total` | Time that processor spent in different modes (hypervisor, guest_run, guest_idle, remote) | counter | `core`,`state` | -| `windows_hyperv_hypervisor_root_virtual_processor_total_run_time_total` | Time that processor spent | counter | `core` | -| `windows_hyperv_hypervisor_root_virtual_cpu_wait_time_per_dispatch_total` | The average time (in nanoseconds) spent waiting for a virtual processor to be dispatched onto a logical processor | counter | `core` | +| `windows_hyperv_hypervisor_root_virtual_processor_time_total` | Time in seconds that processor spent in different modes (`state`: `guest_run`, `hypervisor`, `remote`) | counter | `core`,`state` | +| `windows_hyperv_hypervisor_root_virtual_processor_total_run_time_total` | Time in seconds that processor spent running | counter | `core` | +| `windows_hyperv_hypervisor_root_virtual_processor_cpu_wait_seconds_total` | The time spent waiting for a virtual processor to be dispatched onto a logical processor. Divide its rate by the rate of `windows_hyperv_hypervisor_root_virtual_processor_dispatches_total` for the average wait per dispatch | counter | `core` | +| `windows_hyperv_hypervisor_root_virtual_processor_dispatches_total` | The number of times the virtual processor was dispatched onto a logical processor | counter | `core` | ### Hyper-V Legacy Network Adapter @@ -271,11 +272,12 @@ Example alert: | Name | Description | Type | Labels | |--------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------|---------|-----------------------| -| `windows_hyperv_hypervisor_virtual_processor_mode_time_total` | Time that processor spent in different modes (hypervisor, guest_run, guest_idle, remote) | counter | `vm`, `core`, `state` | -| `windows_hyperv_hypervisor_virtual_processor_run_time_total` | Time that processor spent | counter | `vm`, `core` | -| `windows_hyperv_hypervisor_virtual_processor_cpu_wait_time_per_dispatch_total` | The average time (in nanoseconds) spent waiting for a virtual processor to be dispatched onto a logical processor. | counter | `vm`, `core` | -| `windows_hyperv_hypervisor_virtual_processor_time_total` | **Deprecated:** use `windows_hyperv_hypervisor_virtual_processor_mode_time_total`. Time that processor spent in different modes (hypervisor, guest_run, guest_idle, remote) | counter | `vm`, `core`, `state` | -| `windows_hyperv_hypervisor_virtual_processor_total_run_time_total` | **Deprecated:** use `windows_hyperv_hypervisor_virtual_processor_run_time_total`. Time that processor spent | counter | `vm`, `core` | +| `windows_hyperv_hypervisor_virtual_processor_mode_time_total` | Time in seconds that processor spent in different modes (`state`: `hypervisor`, `guest`, `remote`) | counter | `vm`, `core`, `state` | +| `windows_hyperv_hypervisor_virtual_processor_run_time_total` | Time in seconds that processor spent running | counter | `vm`, `core` | +| `windows_hyperv_hypervisor_virtual_processor_cpu_wait_seconds_total` | The time spent waiting for a virtual processor to be dispatched onto a logical processor. Divide its rate by the rate of `windows_hyperv_hypervisor_virtual_processor_dispatches_total` for the average wait per dispatch | counter | `vm`, `core` | +| `windows_hyperv_hypervisor_virtual_processor_dispatches_total` | The number of times the virtual processor was dispatched onto a logical processor | counter | `vm`, `core` | +| `windows_hyperv_hypervisor_virtual_processor_time_total` | **Deprecated:** use `windows_hyperv_hypervisor_virtual_processor_mode_time_total`. Time in seconds that processor spent in different modes (`state`: `hypervisor`, `guest`, `remote`) | counter | `vm`, `core`, `state` | +| `windows_hyperv_hypervisor_virtual_processor_total_run_time_total` | **Deprecated:** use `windows_hyperv_hypervisor_virtual_processor_run_time_total`. Time in seconds that processor spent running | counter | `vm`, `core` | ### Hyper-V Virtual Network Adapter @@ -355,12 +357,12 @@ Example alert: | `windows_hyperv_virtual_storage_device_operations_written_total` | Represents the total number of write operations that have occurred on this virtual device. | counter | `device` | | `windows_hyperv_virtual_storage_device_io_latency_seconds_total` | Represents the total IO transfer latency for this virtual device. | counter | `device` | | `windows_hyperv_virtual_storage_device_latency_seconds` | **Deprecated:** use `windows_hyperv_virtual_storage_device_io_latency_seconds_total`. Cumulative IO transfer latency in 100ns ticks, see below. | gauge | `device` | -| `windows_hyperv_virtual_storage_device_throughput_total` | Represents the total number of 8KB IO transfers completed by this virtual device. | counter | `device` | -| `windows_hyperv_virtual_storage_device_normalized_throughput` | Represents the average number of IO transfers completed by this virtual device. | gauge | `device` | +| `windows_hyperv_virtual_storage_device_throughput_total` | Represents the total number of IO transfers completed by this virtual device. | counter | `device` | +| `windows_hyperv_virtual_storage_device_normalized_throughput_total` | Represents the total number of IO transfers completed by this virtual device, normalized to 8KB transfers. | counter | `device` | | `windows_hyperv_virtual_storage_device_lower_io_latency_seconds_total` | Represents the total IO transfer latency on the underlying storage subsystem for this virtual device. | counter | `device` | | `windows_hyperv_virtual_storage_device_lower_queue_length` | **Deprecated:** use `rate(windows_hyperv_virtual_storage_device_lower_io_latency_seconds_total)`. Cumulative queue length in 100ns ticks, see below. | gauge | `device` | | `windows_hyperv_virtual_storage_device_lower_latency_seconds` | **Deprecated:** use `windows_hyperv_virtual_storage_device_lower_io_latency_seconds_total`. Cumulative IO transfer latency in 100ns ticks, see below. | gauge | `device` | -| `windows_hyperv_io_quota_replenishment_rate` | Represents the IO quota replenishment rate for this virtual device. | gauge | `device` | +| `windows_hyperv_virtual_storage_device_io_quota_replenishment_total` | Represents the IO quota replenishment counter for this virtual device. Use `rate()` for the replenishment rate. | counter | `device` | #### Deprecated latency and queue length metrics @@ -493,19 +495,19 @@ _This collector does not yet have explained examples, we would appreciate your h ## Useful queries Percent of physical CPU resources used per VM (on instance "localhost") ``` -(sum (rate(windows_hyperv_hypervisor_virtual_processor_mode_time_total{state="hypervisor",instance="localhost"}[1m]))) / ignoring(state,vm) group_left max (windows_cpu_logical_processor{instance="localhost"}) / 100000 +sum by (vm) (rate(windows_hyperv_hypervisor_virtual_processor_run_time_total{instance="localhost"}[1m])) / ignoring(vm) group_left max (windows_cpu_logical_processor{instance="localhost"}) * 100 ``` Percent of physical CPU resources used by all VMs (on all monitored hosts) ``` -(sum by (instance)(rate(windows_hyperv_hypervisor_virtual_processor_run_time_total{}[1m]))) / max by (instance)(windows_cpu_logical_processor{}) / 100000 +(sum by (instance)(rate(windows_hyperv_hypervisor_virtual_processor_run_time_total{}[1m]))) / max by (instance)(windows_cpu_logical_processor{}) * 100 ``` Percent of physical CPU resources by the hosts themselves (on all monitored hosts) ``` -(sum by (instance)(rate(windows_hyperv_hypervisor_root_virtual_processor_total_run_time_total{}[1m]))) / sum by (instance)(windows_cpu_logical_processor{}) / 100000 +(sum by (instance)(rate(windows_hyperv_hypervisor_root_virtual_processor_total_run_time_total{}[1m]))) / sum by (instance)(windows_cpu_logical_processor{}) * 100 ``` Percent of physical CPU resources by the hypervisor (on all monitored hosts) ``` -(sum by (instance)(rate(windows_hyperv_hypervisor_logical_processor_time_total{}[1m]))) / sum by (instance)(windows_cpu_logical_processor{}) / 100000 +(sum by (instance)(rate(windows_hyperv_hypervisor_logical_processor_time_total{state="hypervisor"}[1m]))) / sum by (instance)(windows_cpu_logical_processor{}) * 100 ``` ## Alerting examples diff --git a/internal/collector/hyperv/AGENTS.md b/internal/collector/hyperv/AGENTS.md index 1fcb83f2..5336b9d9 100644 --- a/internal/collector/hyperv/AGENTS.md +++ b/internal/collector/hyperv/AGENTS.md @@ -19,11 +19,17 @@ memory) on a host with 12 logical processors. `% Total Run Time` is about `% Guest Run Time` + `% Hypervisor Run Time`. The help text of `CPU Wait Time Per Dispatch` and `I/O TLB Flush Cost` says "average time (in nanoseconds)", but the raw value is cumulative 100 ns - ticks. + ticks. `internal/pdh` already scales `PERF_PRECISION_100NS_TIMER` to + seconds, so do not scale again. Publish both as `_seconds_total` counters + next to their operation count (`Logical Processor Dispatches/sec`, + `I/O TLB Flushes/sec`), so the average is a ratio of the two rates. +- The virtual processor `state` label is `hypervisor`, `guest`, `remote` for + VMs and `guest_run`, `hypervisor`, `remote` for root VPs. There is no idle + state. - `Hyper-V Hypervisor Root Partition` is read for the instance `Root` only. All its non-`/sec` counters are `PERF_COUNTER_LARGE_RAWCOUNT`. Page counts and sizes are gauges; by the help text `Skipped Timer Ticks` and - `Device Interrupt Throttle Events` count events. + `Device Interrupt Throttle Events` count events, so they are counters. - Dynamic Memory: memory counters are in MB (`Physical Memory` and `Guest Visible Physical Memory` are 2048 for the 2 GiB VM). `Added Memory` and `Removed Memory` are cumulative MB, `Memory Add/Remove Operations` are @@ -52,7 +58,9 @@ memory) on a host with 12 logical processors. `Read Count` + `Write Count` (6 + 4). - `Throughput` (`PERF_COUNTER_COUNTER`) counts IO transfers. `Normalized Throughput` (`PERF_COUNTER_BULK_COUNT`) counts 8 KB IO - transfers by its help text. Both raw values are cumulative. + transfers by its help text. Both raw values are cumulative, as is + `Io Quota Replenishment Rate` (`PERF_COUNTER_BULK_COUNT`); publish them + as counters. - `Read Count`, `Write Count` and `Error Count` are `PERF_COUNTER_RAWCOUNT` but cumulative ("total number of ..."). - `Hyper-V Virtual Switch` and `Hyper-V Virtual Network Adapter`: all @@ -81,6 +89,10 @@ memory) on a host with 12 logical processors. the one VM. - The `wmi_health`, `virtual_hard_disk` and host VM processor metrics do not come from PDH, so the table below does not cover them. +- To test emitted metrics without Hyper-V, build the descriptors with the + `build...Descriptors` helpers, fill the `perfDataObject...` slice (or a + `pdhtypes.Collector` fixture) and gather through a registry, as in + `hyperv_metric_types_internal_test.go`. ## Counter reference @@ -252,10 +264,11 @@ Counters the collector reads: | `% Remote Run Time` | `PERF_PRECISION_100NS_TIMER` | 0 | | | `% Total Run Time` | `PERF_PRECISION_100NS_TIMER` | 10411471300 | | | `CPU Wait Time Per Dispatch` | `PERF_PRECISION_100NS_TIMER` | 290865977 | | +| `Logical Processor Dispatches/sec` | `PERF_COUNTER_BULK_COUNT` | 3171435 | | Not read by the collector: -- `PERF_COUNTER_BULK_COUNT`: `Address Domain Flushes/sec`, `Address Space Evictions/sec`, `Address Space Flushes/sec`, `Address Space Switches/sec`, `APIC EOI Accesses/sec`, `APIC IPIs Sent/sec`, `APIC MMIO Accesses/sec`, `APIC Self IPIs Sent/sec`, `APIC TPR Accesses/sec`, `Bus Lock Acquisitions/sec`, `Control Register Accesses Forwarded/sec`, `Control Register Accesses/sec`, `CPPC Request Context Switches/sec`, `CPU Group Hypercalls/sec`, `CPUID Instructions Forwarded/sec`, `CPUID Instructions/sec`, `Debug Register Accesses Forwarded/sec`, `Debug Register Accesses/sec`, `Deposit Hypercalls/sec`, `Device Domain Hypercalls/sec`, `Emulated Instructions Forwarded/sec`, `Emulated Instructions/sec`, `Event Log Hypercalls/sec`, `Extended Hypercall Intercept Messages/sec`, `Extended Hypercalls/sec`, `External Interrupts Forwarded/sec`, `External Interrupts/sec`, `Flush Physical Address List Hypercalls/sec`, `Flush Physical Address Space Hypercalls/sec`, `GIF Instruction Emulation Intercepts/sec`, `Global GVA Range Flushes/sec`, `Global I/O TLB Flushes/sec`, `GPA Space Hypercalls/sec`, `Guest Page Table Maps/sec`, `Hardware Interrupts/sec`, `HLT Instructions Forwarded/sec`, `HLT Instructions/sec`, `Hypercalls Forwarded/sec`, `Hypercalls/sec`, `InvEpt All Context Emulation Intercepts/sec`, `InvEpt Single Context Emulation Intercepts/sec`, `InvVpid All Context Emulation Intercepts/sec`, `InvVpid Single Address Emulation Intercepts/sec`, `InvVpid Single Context Emulation Intercepts/sec`, `IO Instructions Forwarded/sec`, `IO Instructions/sec`, `IO Intercept Messages/sec`, `IOMMU Hypercalls/sec`, `Large Page TLB Fills/sec`, `Local Flushed GVA Ranges/sec`, `Local I/O TLB Flushes/sec`, `Logical Processor Dispatches/sec`, `Logical Processor Hypercalls/sec`, `Logical Processor Migrations/sec`, `Long Spin Wait Hypercalls/sec`, `MBEC Nested Page Table Switches/sec`, `Memory Intercept Messages/sec`, `MSR Accesses Forwarded/sec`, `MSR Accesses/sec`, `MWAIT Instructions Forwarded/sec`, `MWAIT Instructions/sec`, `Nested Page Fault Intercepts/sec`, `Nested SLAT Hard Page Faults/sec`, `Nested SLAT Soft Page Faults/sec`, `Nested TLB Page Table Evictions/sec`, `Nested TLB Page Table Reclamations/sec`, `Nested VM Entries/sec`, `Other Hypercalls/sec`, `Other Intercepts Forwarded/sec`, `Other Intercepts/sec`, `Other Messages/sec`, `Other Reflected Guest Exceptions/sec`, `Page Fault Intercepts Forwarded/sec`, `Page Fault Intercepts/sec`, `Page Invalidations Forwarded/sec`, `Page Invalidations/sec`, `Page Scans/sec`, `Page Table Allocations/sec`, `Page Table Evictions/sec`, `Page Table Reclamations/sec`, `Page Table Resets/sec`, `Page Table Validations/sec`, `Page Table Write Intercepts/sec`, `Pending Interrupts Forwarded/sec`, `Pending Interrupts/sec`, `Performance Monitoring Interrupts/sec`, `Performance Monitoring IPT MSR Accesses/sec`, `Performance Monitoring LBR MSR Accesses/sec`, `Performance Monitoring vPMU MSR Accesses/sec`, `Posted Interrupt Notifications/sec`, `Posted Interrupt Scans/sec`, `RDPMC Instructions/sec`, `Reflected Guest Page Faults/sec`, `Small Page TLB Fills/sec`, `SVM Hypercalls/sec`, `Synthetic Interrupt Hypercalls/sec`, `Synthetic Interrupts/sec`, `Total Intercepts/sec`, `Total Messages/sec`, `Total Virtualization Instructions Emulated/sec`, `Virtual Interrupt Hypercalls/sec`, `Virtual Interrupts/sec`, `Virtual MMU Hypercalls/sec`, `Virtual Processor Hypercalls/sec`, `VMCLEAR Emulation Intercepts/sec`, `VMLOAD Emulation Intercepts/sec`, `VMPTRLD Emulation Intercepts/sec`, `VMPTRST Emulation Intercepts/sec`, `VMREAD Emulation Intercepts/sec`, `VMSAVE Emulation Intercepts/sec`, `VMWRITE Emulation Intercepts/sec`, `VMXOFF Emulation Intercepts/sec`, `VMXON Emulation Intercepts/sec`, `VSM Hypercalls/sec`, `VTL1 Dispatches/sec`, `VTL2 Dispatches/sec` +- `PERF_COUNTER_BULK_COUNT`: `Address Domain Flushes/sec`, `Address Space Evictions/sec`, `Address Space Flushes/sec`, `Address Space Switches/sec`, `APIC EOI Accesses/sec`, `APIC IPIs Sent/sec`, `APIC MMIO Accesses/sec`, `APIC Self IPIs Sent/sec`, `APIC TPR Accesses/sec`, `Bus Lock Acquisitions/sec`, `Control Register Accesses Forwarded/sec`, `Control Register Accesses/sec`, `CPPC Request Context Switches/sec`, `CPU Group Hypercalls/sec`, `CPUID Instructions Forwarded/sec`, `CPUID Instructions/sec`, `Debug Register Accesses Forwarded/sec`, `Debug Register Accesses/sec`, `Deposit Hypercalls/sec`, `Device Domain Hypercalls/sec`, `Emulated Instructions Forwarded/sec`, `Emulated Instructions/sec`, `Event Log Hypercalls/sec`, `Extended Hypercall Intercept Messages/sec`, `Extended Hypercalls/sec`, `External Interrupts Forwarded/sec`, `External Interrupts/sec`, `Flush Physical Address List Hypercalls/sec`, `Flush Physical Address Space Hypercalls/sec`, `GIF Instruction Emulation Intercepts/sec`, `Global GVA Range Flushes/sec`, `Global I/O TLB Flushes/sec`, `GPA Space Hypercalls/sec`, `Guest Page Table Maps/sec`, `Hardware Interrupts/sec`, `HLT Instructions Forwarded/sec`, `HLT Instructions/sec`, `Hypercalls Forwarded/sec`, `Hypercalls/sec`, `InvEpt All Context Emulation Intercepts/sec`, `InvEpt Single Context Emulation Intercepts/sec`, `InvVpid All Context Emulation Intercepts/sec`, `InvVpid Single Address Emulation Intercepts/sec`, `InvVpid Single Context Emulation Intercepts/sec`, `IO Instructions Forwarded/sec`, `IO Instructions/sec`, `IO Intercept Messages/sec`, `IOMMU Hypercalls/sec`, `Large Page TLB Fills/sec`, `Local Flushed GVA Ranges/sec`, `Local I/O TLB Flushes/sec`, `Logical Processor Hypercalls/sec`, `Logical Processor Migrations/sec`, `Long Spin Wait Hypercalls/sec`, `MBEC Nested Page Table Switches/sec`, `Memory Intercept Messages/sec`, `MSR Accesses Forwarded/sec`, `MSR Accesses/sec`, `MWAIT Instructions Forwarded/sec`, `MWAIT Instructions/sec`, `Nested Page Fault Intercepts/sec`, `Nested SLAT Hard Page Faults/sec`, `Nested SLAT Soft Page Faults/sec`, `Nested TLB Page Table Evictions/sec`, `Nested TLB Page Table Reclamations/sec`, `Nested VM Entries/sec`, `Other Hypercalls/sec`, `Other Intercepts Forwarded/sec`, `Other Intercepts/sec`, `Other Messages/sec`, `Other Reflected Guest Exceptions/sec`, `Page Fault Intercepts Forwarded/sec`, `Page Fault Intercepts/sec`, `Page Invalidations Forwarded/sec`, `Page Invalidations/sec`, `Page Scans/sec`, `Page Table Allocations/sec`, `Page Table Evictions/sec`, `Page Table Reclamations/sec`, `Page Table Resets/sec`, `Page Table Validations/sec`, `Page Table Write Intercepts/sec`, `Pending Interrupts Forwarded/sec`, `Pending Interrupts/sec`, `Performance Monitoring Interrupts/sec`, `Performance Monitoring IPT MSR Accesses/sec`, `Performance Monitoring LBR MSR Accesses/sec`, `Performance Monitoring vPMU MSR Accesses/sec`, `Posted Interrupt Notifications/sec`, `Posted Interrupt Scans/sec`, `RDPMC Instructions/sec`, `Reflected Guest Page Faults/sec`, `Small Page TLB Fills/sec`, `SVM Hypercalls/sec`, `Synthetic Interrupt Hypercalls/sec`, `Synthetic Interrupts/sec`, `Total Intercepts/sec`, `Total Messages/sec`, `Total Virtualization Instructions Emulated/sec`, `Virtual Interrupt Hypercalls/sec`, `Virtual Interrupts/sec`, `Virtual MMU Hypercalls/sec`, `Virtual Processor Hypercalls/sec`, `VMCLEAR Emulation Intercepts/sec`, `VMLOAD Emulation Intercepts/sec`, `VMPTRLD Emulation Intercepts/sec`, `VMPTRST Emulation Intercepts/sec`, `VMREAD Emulation Intercepts/sec`, `VMSAVE Emulation Intercepts/sec`, `VMWRITE Emulation Intercepts/sec`, `VMXOFF Emulation Intercepts/sec`, `VMXON Emulation Intercepts/sec`, `VSM Hypercalls/sec`, `VTL1 Dispatches/sec`, `VTL2 Dispatches/sec` - `PERF_COUNTER_LARGE_RAWCOUNT`: `Scheduling Priority` - `PERF_PRECISION_100NS_TIMER`: `% Guest-Relative Utilization`, `% Total Core Run Time`, `% VTL1 Run Time`, `% VTL2 Run Time`, `Control Register Accesses Cost`, `Control Register Accesses Forwarding Cost`, `CPU Contention Time Per Dispatch`, `CPU Wake Up Time Per Dispatch`, `CPUID Instructions Cost`, `CPUID Instructions Forwarding Cost`, `Debug Register Accesses Cost`, `Debug Register Accesses Forwarding Cost`, `Emulated Instructions Cost`, `Emulated Instructions Forwarding Cost`, `External Interrupts Cost`, `GIF Instruction Emulation Cost`, `Global I/O TLB Flush Cost`, `HLT Instructions Cost`, `HLT Instructions Forwarding Cost`, `Hypercalls Cost`, `Hypercalls Forwarding Cost`, `InvEpt All Context Instruction Emulation Cost`, `InvEpt Single Context Instruction Emulation Cost`, `InvVpid All Context Instruction Emulation Cost`, `InvVpid Single Address Instruction Emulation Cost`, `InvVpid Single Context Instruction Emulation Cost`, `IO Instructions Cost`, `IO Instructions Forwarding Cost`, `Local I/O TLB Flush Cost`, `MSR Accesses Cost`, `MSR Accesses Forwarding Cost`, `MWAIT Instructions Cost`, `MWAIT Instructions Forwarding Cost`, `Nested Page Fault Intercepts Cost`, `Nested SLAT Hard Page Faults Cost`, `Nested SLAT Soft Page Faults Cost`, `Nested VM Entries Cost`, `Other Intercepts Cost`, `Other Intercepts Forwarding Cost`, `Page Fault Intercepts Cost`, `Page Fault Intercepts Forwarding Cost`, `Page Invalidations Cost`, `Page Invalidations Forwarding Cost`, `Pending Interrupts Cost`, `Pending Interrupts Forwarding Cost`, `RDPMC Instructions Cost`, `Total Intercepts Cost`, `Total Virtualization Instructions Emulation Cost`, `VMCLEAR Instruction Emulation Cost`, `VMLOAD Instruction Emulation Cost`, `VMPTRLD Instruction Emulation Cost`, `VMPTRST Instruction Emulation Cost`, `VMREAD Instruction Emulation Cost`, `VMSAVE Instruction Emulation Cost`, `VMWRITE Instruction Emulation Cost`, `VMXOFF Instruction Emulation Cost`, `VMXON Instruction Emulation Cost`, `VTL1 Average Run Time`, `VTL2 Average Run Time` @@ -272,10 +285,11 @@ Counters the collector reads: | `% Remote Run Time` | `PERF_PRECISION_100NS_TIMER` | 0 | | | `% Total Run Time` | `PERF_PRECISION_100NS_TIMER` | 3180787877 | | | `CPU Wait Time Per Dispatch` | `PERF_PRECISION_100NS_TIMER` | 1217187193 | | +| `Logical Processor Dispatches/sec` | `PERF_COUNTER_BULK_COUNT` | 2007559 | | Not read by the collector: -- `PERF_COUNTER_BULK_COUNT`: `Address Domain Flushes/sec`, `Address Space Evictions/sec`, `Address Space Flushes/sec`, `Address Space Switches/sec`, `APIC EOI Accesses/sec`, `APIC IPIs Sent/sec`, `APIC MMIO Accesses/sec`, `APIC Self IPIs Sent/sec`, `APIC TPR Accesses/sec`, `Bus Lock Acquisitions/sec`, `Control Register Accesses Forwarded/sec`, `Control Register Accesses/sec`, `CPPC Request Context Switches/sec`, `CPU Group Hypercalls/sec`, `CPUID Instructions Forwarded/sec`, `CPUID Instructions/sec`, `Debug Register Accesses Forwarded/sec`, `Debug Register Accesses/sec`, `Deposit Hypercalls/sec`, `Device Domain Hypercalls/sec`, `Emulated Instructions Forwarded/sec`, `Emulated Instructions/sec`, `Event Log Hypercalls/sec`, `Extended Hypercall Intercept Messages/sec`, `Extended Hypercalls/sec`, `External Interrupts Forwarded/sec`, `External Interrupts/sec`, `Flush Physical Address List Hypercalls/sec`, `Flush Physical Address Space Hypercalls/sec`, `GIF Instruction Emulation Intercepts/sec`, `Global GVA Range Flushes/sec`, `Global I/O TLB Flushes/sec`, `GPA Space Hypercalls/sec`, `Guest Page Table Maps/sec`, `Hardware Interrupts/sec`, `HLT Instructions Forwarded/sec`, `HLT Instructions/sec`, `Hypercalls Forwarded/sec`, `Hypercalls/sec`, `InvEpt All Context Emulation Intercepts/sec`, `InvEpt Single Context Emulation Intercepts/sec`, `InvVpid All Context Emulation Intercepts/sec`, `InvVpid Single Address Emulation Intercepts/sec`, `InvVpid Single Context Emulation Intercepts/sec`, `IO Instructions Forwarded/sec`, `IO Instructions/sec`, `IO Intercept Messages/sec`, `IOMMU Hypercalls/sec`, `Large Page TLB Fills/sec`, `Local Flushed GVA Ranges/sec`, `Local I/O TLB Flushes/sec`, `Logical Processor Dispatches/sec`, `Logical Processor Hypercalls/sec`, `Logical Processor Migrations/sec`, `Long Spin Wait Hypercalls/sec`, `MBEC Nested Page Table Switches/sec`, `Memory Intercept Messages/sec`, `MSR Accesses Forwarded/sec`, `MSR Accesses/sec`, `MWAIT Instructions Forwarded/sec`, `MWAIT Instructions/sec`, `Nested Page Fault Intercepts/sec`, `Nested SLAT Hard Page Faults/sec`, `Nested SLAT Soft Page Faults/sec`, `Nested TLB Page Table Evictions/sec`, `Nested TLB Page Table Reclamations/sec`, `Nested VM Entries/sec`, `Other Hypercalls/sec`, `Other Intercepts Forwarded/sec`, `Other Intercepts/sec`, `Other Messages/sec`, `Other Reflected Guest Exceptions/sec`, `Page Fault Intercepts Forwarded/sec`, `Page Fault Intercepts/sec`, `Page Invalidations Forwarded/sec`, `Page Invalidations/sec`, `Page Scans/sec`, `Page Table Allocations/sec`, `Page Table Evictions/sec`, `Page Table Reclamations/sec`, `Page Table Resets/sec`, `Page Table Validations/sec`, `Page Table Write Intercepts/sec`, `Pending Interrupts Forwarded/sec`, `Pending Interrupts/sec`, `Performance Monitoring Interrupts/sec`, `Performance Monitoring IPT MSR Accesses/sec`, `Performance Monitoring LBR MSR Accesses/sec`, `Performance Monitoring vPMU MSR Accesses/sec`, `Posted Interrupt Notifications/sec`, `Posted Interrupt Scans/sec`, `RDPMC Instructions/sec`, `Reflected Guest Page Faults/sec`, `Small Page TLB Fills/sec`, `SVM Hypercalls/sec`, `Synthetic Interrupt Hypercalls/sec`, `Synthetic Interrupts/sec`, `Total Intercepts/sec`, `Total Messages/sec`, `Total Virtualization Instructions Emulated/sec`, `Virtual Interrupt Hypercalls/sec`, `Virtual Interrupts/sec`, `Virtual MMU Hypercalls/sec`, `Virtual Processor Hypercalls/sec`, `VMCLEAR Emulation Intercepts/sec`, `VMLOAD Emulation Intercepts/sec`, `VMPTRLD Emulation Intercepts/sec`, `VMPTRST Emulation Intercepts/sec`, `VMREAD Emulation Intercepts/sec`, `VMSAVE Emulation Intercepts/sec`, `VMWRITE Emulation Intercepts/sec`, `VMXOFF Emulation Intercepts/sec`, `VMXON Emulation Intercepts/sec`, `VSM Hypercalls/sec`, `VTL1 Dispatches/sec`, `VTL2 Dispatches/sec` +- `PERF_COUNTER_BULK_COUNT`: `Address Domain Flushes/sec`, `Address Space Evictions/sec`, `Address Space Flushes/sec`, `Address Space Switches/sec`, `APIC EOI Accesses/sec`, `APIC IPIs Sent/sec`, `APIC MMIO Accesses/sec`, `APIC Self IPIs Sent/sec`, `APIC TPR Accesses/sec`, `Bus Lock Acquisitions/sec`, `Control Register Accesses Forwarded/sec`, `Control Register Accesses/sec`, `CPPC Request Context Switches/sec`, `CPU Group Hypercalls/sec`, `CPUID Instructions Forwarded/sec`, `CPUID Instructions/sec`, `Debug Register Accesses Forwarded/sec`, `Debug Register Accesses/sec`, `Deposit Hypercalls/sec`, `Device Domain Hypercalls/sec`, `Emulated Instructions Forwarded/sec`, `Emulated Instructions/sec`, `Event Log Hypercalls/sec`, `Extended Hypercall Intercept Messages/sec`, `Extended Hypercalls/sec`, `External Interrupts Forwarded/sec`, `External Interrupts/sec`, `Flush Physical Address List Hypercalls/sec`, `Flush Physical Address Space Hypercalls/sec`, `GIF Instruction Emulation Intercepts/sec`, `Global GVA Range Flushes/sec`, `Global I/O TLB Flushes/sec`, `GPA Space Hypercalls/sec`, `Guest Page Table Maps/sec`, `Hardware Interrupts/sec`, `HLT Instructions Forwarded/sec`, `HLT Instructions/sec`, `Hypercalls Forwarded/sec`, `Hypercalls/sec`, `InvEpt All Context Emulation Intercepts/sec`, `InvEpt Single Context Emulation Intercepts/sec`, `InvVpid All Context Emulation Intercepts/sec`, `InvVpid Single Address Emulation Intercepts/sec`, `InvVpid Single Context Emulation Intercepts/sec`, `IO Instructions Forwarded/sec`, `IO Instructions/sec`, `IO Intercept Messages/sec`, `IOMMU Hypercalls/sec`, `Large Page TLB Fills/sec`, `Local Flushed GVA Ranges/sec`, `Local I/O TLB Flushes/sec`, `Logical Processor Hypercalls/sec`, `Logical Processor Migrations/sec`, `Long Spin Wait Hypercalls/sec`, `MBEC Nested Page Table Switches/sec`, `Memory Intercept Messages/sec`, `MSR Accesses Forwarded/sec`, `MSR Accesses/sec`, `MWAIT Instructions Forwarded/sec`, `MWAIT Instructions/sec`, `Nested Page Fault Intercepts/sec`, `Nested SLAT Hard Page Faults/sec`, `Nested SLAT Soft Page Faults/sec`, `Nested TLB Page Table Evictions/sec`, `Nested TLB Page Table Reclamations/sec`, `Nested VM Entries/sec`, `Other Hypercalls/sec`, `Other Intercepts Forwarded/sec`, `Other Intercepts/sec`, `Other Messages/sec`, `Other Reflected Guest Exceptions/sec`, `Page Fault Intercepts Forwarded/sec`, `Page Fault Intercepts/sec`, `Page Invalidations Forwarded/sec`, `Page Invalidations/sec`, `Page Scans/sec`, `Page Table Allocations/sec`, `Page Table Evictions/sec`, `Page Table Reclamations/sec`, `Page Table Resets/sec`, `Page Table Validations/sec`, `Page Table Write Intercepts/sec`, `Pending Interrupts Forwarded/sec`, `Pending Interrupts/sec`, `Performance Monitoring Interrupts/sec`, `Performance Monitoring IPT MSR Accesses/sec`, `Performance Monitoring LBR MSR Accesses/sec`, `Performance Monitoring vPMU MSR Accesses/sec`, `Posted Interrupt Notifications/sec`, `Posted Interrupt Scans/sec`, `RDPMC Instructions/sec`, `Reflected Guest Page Faults/sec`, `Small Page TLB Fills/sec`, `SVM Hypercalls/sec`, `Synthetic Interrupt Hypercalls/sec`, `Synthetic Interrupts/sec`, `Total Intercepts/sec`, `Total Messages/sec`, `Total Virtualization Instructions Emulated/sec`, `Virtual Interrupt Hypercalls/sec`, `Virtual Interrupts/sec`, `Virtual MMU Hypercalls/sec`, `Virtual Processor Hypercalls/sec`, `VMCLEAR Emulation Intercepts/sec`, `VMLOAD Emulation Intercepts/sec`, `VMPTRLD Emulation Intercepts/sec`, `VMPTRST Emulation Intercepts/sec`, `VMREAD Emulation Intercepts/sec`, `VMSAVE Emulation Intercepts/sec`, `VMWRITE Emulation Intercepts/sec`, `VMXOFF Emulation Intercepts/sec`, `VMXON Emulation Intercepts/sec`, `VSM Hypercalls/sec`, `VTL1 Dispatches/sec`, `VTL2 Dispatches/sec` - `PERF_COUNTER_LARGE_RAWCOUNT`: `Scheduling Priority` - `PERF_PRECISION_100NS_TIMER`: `% Guest-Relative Utilization`, `% Total Core Run Time`, `% VTL1 Run Time`, `% VTL2 Run Time`, `Control Register Accesses Cost`, `Control Register Accesses Forwarding Cost`, `CPU Contention Time Per Dispatch`, `CPU Wake Up Time Per Dispatch`, `CPUID Instructions Cost`, `CPUID Instructions Forwarding Cost`, `Debug Register Accesses Cost`, `Debug Register Accesses Forwarding Cost`, `Emulated Instructions Cost`, `Emulated Instructions Forwarding Cost`, `External Interrupts Cost`, `GIF Instruction Emulation Cost`, `Global I/O TLB Flush Cost`, `HLT Instructions Cost`, `HLT Instructions Forwarding Cost`, `Hypercalls Cost`, `Hypercalls Forwarding Cost`, `InvEpt All Context Instruction Emulation Cost`, `InvEpt Single Context Instruction Emulation Cost`, `InvVpid All Context Instruction Emulation Cost`, `InvVpid Single Address Instruction Emulation Cost`, `InvVpid Single Context Instruction Emulation Cost`, `IO Instructions Cost`, `IO Instructions Forwarding Cost`, `Local I/O TLB Flush Cost`, `MSR Accesses Cost`, `MSR Accesses Forwarding Cost`, `MWAIT Instructions Cost`, `MWAIT Instructions Forwarding Cost`, `Nested Page Fault Intercepts Cost`, `Nested SLAT Hard Page Faults Cost`, `Nested SLAT Soft Page Faults Cost`, `Nested VM Entries Cost`, `Other Intercepts Cost`, `Other Intercepts Forwarding Cost`, `Page Fault Intercepts Cost`, `Page Fault Intercepts Forwarding Cost`, `Page Invalidations Cost`, `Page Invalidations Forwarding Cost`, `Pending Interrupts Cost`, `Pending Interrupts Forwarding Cost`, `RDPMC Instructions Cost`, `Total Intercepts Cost`, `Total Virtualization Instructions Emulation Cost`, `VMCLEAR Instruction Emulation Cost`, `VMLOAD Instruction Emulation Cost`, `VMPTRLD Instruction Emulation Cost`, `VMPTRST Instruction Emulation Cost`, `VMREAD Instruction Emulation Cost`, `VMSAVE Instruction Emulation Cost`, `VMWRITE Instruction Emulation Cost`, `VMXOFF Instruction Emulation Cost`, `VMXON Instruction Emulation Cost`, `VTL1 Average Run Time`, `VTL2 Average Run Time` diff --git a/internal/collector/hyperv/hyperv_dynamic_memory_balancer.go b/internal/collector/hyperv/hyperv_dynamic_memory_balancer.go index b2283f5d..f8d728e5 100644 --- a/internal/collector/hyperv/hyperv_dynamic_memory_balancer.go +++ b/internal/collector/hyperv/hyperv_dynamic_memory_balancer.go @@ -55,7 +55,7 @@ func (c *Collector) buildDynamicMemoryBalancer() error { // https://learn.microsoft.com/en-us/archive/blogs/chrisavis/monitoring-dynamic-memory-in-windows-server-hyper-v-2012 c.perfDataCollectorDynamicMemoryBalancer, err = pdh.NewCollector[perfDataCounterValuesDynamicMemoryBalancer](c.logger, pdh.CounterTypeRaw, "Hyper-V Dynamic Memory Balancer", pdh.InstancesAll) if err != nil { - return fmt.Errorf("failed to create Hyper-V Virtual Machine Health Summary collector: %w", err) + return fmt.Errorf("failed to create Hyper-V Dynamic Memory Balancer collector: %w", err) } c.vmDynamicMemoryBalancerAvailableMemory = prometheus.NewDesc( diff --git a/internal/collector/hyperv/hyperv_hypervisor_root_partition.go b/internal/collector/hyperv/hyperv_hypervisor_root_partition.go index 615cfc14..b2cc5b16 100644 --- a/internal/collector/hyperv/hyperv_hypervisor_root_partition.go +++ b/internal/collector/hyperv/hyperv_hypervisor_root_partition.go @@ -65,18 +65,20 @@ type perfDataCounterValuesHypervisorRootPartition struct { HypervisorRootPartitionDeviceInterruptThrottleEvents float64 `perfdata:"Device Interrupt Throttle Events"` HypervisorRootPartitionGPAPages float64 `perfdata:"GPA Pages"` HypervisorRootPartitionGPASpaceModifications float64 `perfdata:"GPA Space Modifications/sec"` - HypervisorRootPartitionIOTLBFlushCost float64 `perfdata:"I/O TLB Flush Cost"` - HypervisorRootPartitionIOTLBFlushes float64 `perfdata:"I/O TLB Flushes/sec"` - HypervisorRootPartitionRecommendedVirtualTLBSize float64 `perfdata:"Recommended Virtual TLB Size"` - HypervisorRootPartitionSkippedTimerTicks float64 `perfdata:"Skipped Timer Ticks"` - HypervisorRootPartition1GDevicePages float64 `perfdata:"1G device pages"` - HypervisorRootPartition1GGPAPages float64 `perfdata:"1G GPA pages"` - HypervisorRootPartition2MDevicePages float64 `perfdata:"2M device pages"` - HypervisorRootPartition2MGPAPages float64 `perfdata:"2M GPA pages"` - HypervisorRootPartition4KDevicePages float64 `perfdata:"4K device pages"` - HypervisorRootPartition4KGPAPages float64 `perfdata:"4K GPA pages"` - HypervisorRootPartitionVirtualTLBFlushEntries float64 `perfdata:"Virtual TLB Flush Entires/sec"` - HypervisorRootPartitionVirtualTLBPages float64 `perfdata:"Virtual TLB Pages"` + // PERF_PRECISION_100NS_TIMER: the raw value is the cumulative flush time, which pdh + // scales to seconds. The Windows help text calls it an average in nanoseconds. + HypervisorRootPartitionIOTLBFlushCost float64 `perfdata:"I/O TLB Flush Cost"` + HypervisorRootPartitionIOTLBFlushes float64 `perfdata:"I/O TLB Flushes/sec"` + HypervisorRootPartitionRecommendedVirtualTLBSize float64 `perfdata:"Recommended Virtual TLB Size"` + HypervisorRootPartitionSkippedTimerTicks float64 `perfdata:"Skipped Timer Ticks"` + HypervisorRootPartition1GDevicePages float64 `perfdata:"1G device pages"` + HypervisorRootPartition1GGPAPages float64 `perfdata:"1G GPA pages"` + HypervisorRootPartition2MDevicePages float64 `perfdata:"2M device pages"` + HypervisorRootPartition2MGPAPages float64 `perfdata:"2M GPA pages"` + HypervisorRootPartition4KDevicePages float64 `perfdata:"4K device pages"` + HypervisorRootPartition4KGPAPages float64 `perfdata:"4K GPA pages"` + HypervisorRootPartitionVirtualTLBFlushEntries float64 `perfdata:"Virtual TLB Flush Entires/sec"` + HypervisorRootPartitionVirtualTLBPages float64 `perfdata:"Virtual TLB Pages"` } func (c *Collector) buildHypervisorRootPartition() error { @@ -130,32 +132,32 @@ func (c *Collector) buildHypervisorRootPartitionDescriptors() { nil, ) c.hypervisorRootPartitionDeviceInterruptThrottleEvents = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "root_partition_device_interrupt_throttle_events"), + prometheus.BuildFQName(types.Namespace, Name, "root_partition_device_interrupt_throttle_events_total"), "The number of times an interrupt from a device assigned to the partition was temporarily throttled because the device was generating too many interrupts", nil, nil, ) c.hypervisorRootPartitionGPAPages = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "root_partition_preferred_numa_node_index"), + prometheus.BuildFQName(types.Namespace, Name, "root_partition_gpa_pages"), "The number of pages present in the GPA space of the partition (zero for root partition)", nil, nil, ) c.hypervisorRootPartitionGPASpaceModifications = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "root_partition_gpa_space_modifications"), - "The rate of modifications to the GPA space of the partition", + prometheus.BuildFQName(types.Namespace, Name, "root_partition_gpa_space_modifications_total"), + "The number of modifications to the GPA space of the partition", nil, nil, ) c.hypervisorRootPartitionIOTLBFlushCost = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "root_partition_io_tlb_flush_cost"), - "The average time (in nanoseconds) spent processing an I/O TLB flush", + prometheus.BuildFQName(types.Namespace, Name, "root_partition_io_tlb_flush_seconds_total"), + "The time spent processing I/O TLB flushes. Divide its rate by the rate of windows_hyperv_root_partition_io_tlb_flushes_total for the average time per flush", nil, nil, ) c.hypervisorRootPartitionIOTLBFlushes = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "root_partition_io_tlb_flush"), - "The rate of flushes of I/O TLBs of the partition", + prometheus.BuildFQName(types.Namespace, Name, "root_partition_io_tlb_flushes_total"), + "The number of flushes of I/O TLBs of the partition", nil, nil, ) @@ -166,7 +168,7 @@ func (c *Collector) buildHypervisorRootPartitionDescriptors() { nil, ) c.hypervisorRootPartitionSkippedTimerTicks = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "root_partition_physical_pages_allocated"), + prometheus.BuildFQName(types.Namespace, Name, "root_partition_skipped_timer_ticks_total"), "The number of timer interrupts skipped for the partition", nil, nil, @@ -208,8 +210,8 @@ func (c *Collector) buildHypervisorRootPartitionDescriptors() { nil, ) c.hypervisorRootPartitionVirtualTLBFlushEntries = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "root_partition_virtual_tlb_flush_entries"), - "The rate of flushes of the entire virtual TLB", + prometheus.BuildFQName(types.Namespace, Name, "root_partition_virtual_tlb_flush_entries_total"), + "The number of flushes of the entire virtual TLB", nil, nil, ) @@ -265,7 +267,7 @@ func (c *Collector) collectHypervisorRootPartition(ch chan<- prometheus.Metric, ch <- scrape.MustNewConstMetric( c.hypervisorRootPartitionDeviceInterruptThrottleEvents, - prometheus.GaugeValue, + prometheus.CounterValue, c.perfDataObjectHypervisorRootPartition[0].HypervisorRootPartitionDeviceInterruptThrottleEvents, ) @@ -283,7 +285,7 @@ func (c *Collector) collectHypervisorRootPartition(ch chan<- prometheus.Metric, ch <- scrape.MustNewConstMetric( c.hypervisorRootPartitionIOTLBFlushCost, - prometheus.GaugeValue, + prometheus.CounterValue, c.perfDataObjectHypervisorRootPartition[0].HypervisorRootPartitionIOTLBFlushCost, ) @@ -301,7 +303,7 @@ func (c *Collector) collectHypervisorRootPartition(ch chan<- prometheus.Metric, ch <- scrape.MustNewConstMetric( c.hypervisorRootPartitionSkippedTimerTicks, - prometheus.GaugeValue, + prometheus.CounterValue, c.perfDataObjectHypervisorRootPartition[0].HypervisorRootPartitionSkippedTimerTicks, ) diff --git a/internal/collector/hyperv/hyperv_hypervisor_root_virtual_processor.go b/internal/collector/hyperv/hyperv_hypervisor_root_virtual_processor.go index c4d83a5d..de64628f 100644 --- a/internal/collector/hyperv/hyperv_hypervisor_root_virtual_processor.go +++ b/internal/collector/hyperv/hyperv_hypervisor_root_virtual_processor.go @@ -36,19 +36,24 @@ type collectorHypervisorRootVirtualProcessor struct { // \Hyper-V Hypervisor Root Virtual Processor(*)\% Hypervisor Run Time // \Hyper-V Hypervisor Root Virtual Processor(*)\% Remote Run Time // \Hyper-V Hypervisor Root Virtual Processor(*)\% Total Run Time - hypervisorRootVirtualProcessorTimeTotal *prometheus.Desc - hypervisorRootVirtualProcessorTotalRunTimeTotal *prometheus.Desc - hypervisorRootVirtualProcessorCPUWaitTimePerDispatch *prometheus.Desc // \Hyper-V Hypervisor Root Virtual Processor(*)\CPU Wait Time Per Dispatch + hypervisorRootVirtualProcessorTimeTotal *prometheus.Desc + hypervisorRootVirtualProcessorTotalRunTimeTotal *prometheus.Desc + hypervisorRootVirtualProcessorCPUWaitSeconds *prometheus.Desc // \Hyper-V Hypervisor Root Virtual Processor(*)\CPU Wait Time Per Dispatch + hypervisorRootVirtualProcessorDispatches *prometheus.Desc // \Hyper-V Hypervisor Root Virtual Processor(*)\Logical Processor Dispatches/sec } type perfDataCounterValuesHypervisorRootVirtualProcessor struct { Name string + // The run time counters and CPU Wait Time Per Dispatch are PERF_PRECISION_100NS_TIMER. + // Their raw values are cumulative times, which pdh scales to seconds. Despite its name + // and help text, CPU Wait Time Per Dispatch is the total wait time, not an average. HypervisorRootVirtualProcessorGuestRunTimePercent float64 `perfdata:"% Guest Run Time"` HypervisorRootVirtualProcessorHypervisorRunTimePercent float64 `perfdata:"% Hypervisor Run Time"` HypervisorRootVirtualProcessorTotalRunTimePercent float64 `perfdata:"% Total Run Time"` HypervisorRootVirtualProcessorRemoteRunTimePercent float64 `perfdata:"% Remote Run Time"` HypervisorRootVirtualProcessorCPUWaitTimePerDispatch float64 `perfdata:"CPU Wait Time Per Dispatch"` + HypervisorRootVirtualProcessorDispatches float64 `perfdata:"Logical Processor Dispatches/sec"` } func (c *Collector) buildHypervisorRootVirtualProcessor() error { @@ -59,28 +64,40 @@ func (c *Collector) buildHypervisorRootVirtualProcessor() error { return fmt.Errorf("failed to create Hyper-V Hypervisor Root Virtual Processor collector: %w", err) } + c.buildHypervisorRootVirtualProcessorDescriptors() + + return nil +} + +func (c *Collector) buildHypervisorRootVirtualProcessorDescriptors() { c.hypervisorRootVirtualProcessorTimeTotal = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "hypervisor_root_virtual_processor_time_total"), - "Time that processor spent in different modes (hypervisor, guest_run, guest_idle, remote)", + "Time in seconds that processor spent in different modes (guest_run, hypervisor, remote)", []string{"core", "state"}, nil, ) c.hypervisorRootVirtualProcessorTotalRunTimeTotal = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "hypervisor_root_virtual_processor_total_run_time_total"), - "Time that processor spent", + "Time in seconds that processor spent running", []string{"core"}, nil, ) - c.hypervisorRootVirtualProcessorCPUWaitTimePerDispatch = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "hypervisor_root_virtual_cpu_wait_time_per_dispatch_total"), - "The average time (in nanoseconds) spent waiting for a virtual processor to be dispatched onto a logical processor.", + c.hypervisorRootVirtualProcessorCPUWaitSeconds = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "hypervisor_root_virtual_processor_cpu_wait_seconds_total"), + "The time spent waiting for a virtual processor to be dispatched onto a logical processor. "+ + "Divide its rate by the rate of windows_hyperv_hypervisor_root_virtual_processor_dispatches_total for the average wait per dispatch.", []string{"core"}, nil, ) - return nil + c.hypervisorRootVirtualProcessorDispatches = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "hypervisor_root_virtual_processor_dispatches_total"), + "The number of times the virtual processor was dispatched onto a logical processor.", + []string{"core"}, + nil, + ) } func (c *Collector) collectHypervisorRootVirtualProcessor(ch chan<- prometheus.Metric, scrape *labelcache.Scrape) error { @@ -89,8 +106,12 @@ func (c *Collector) collectHypervisorRootVirtualProcessor(ch chan<- prometheus.M return fmt.Errorf("failed to collect Hyper-V Hypervisor Root Virtual Processor metrics: %w", err) } + return c.publishHypervisorRootVirtualProcessor(ch, scrape) +} + +func (c *Collector) publishHypervisorRootVirtualProcessor(ch chan<- prometheus.Metric, scrape *labelcache.Scrape) error { for _, data := range c.perfDataObjectHypervisorRootVirtualProcessor { - // The name format is Hv LP + // The name format is Root VP parts := strings.Split(data.Name, " ") if len(parts) != 3 { return fmt.Errorf("unexpected Hyper-V Hypervisor Root Virtual Processor name format: %s", data.Name) @@ -127,11 +148,18 @@ func (c *Collector) collectHypervisorRootVirtualProcessor(ch chan<- prometheus.M ) ch <- scrape.MustNewConstMetric( - c.hypervisorRootVirtualProcessorCPUWaitTimePerDispatch, + c.hypervisorRootVirtualProcessorCPUWaitSeconds, prometheus.CounterValue, data.HypervisorRootVirtualProcessorCPUWaitTimePerDispatch, coreID, ) + + ch <- scrape.MustNewConstMetric( + c.hypervisorRootVirtualProcessorDispatches, + prometheus.CounterValue, + data.HypervisorRootVirtualProcessorDispatches, + coreID, + ) } return nil diff --git a/internal/collector/hyperv/hyperv_hypervisor_virtual_processor.go b/internal/collector/hyperv/hyperv_hypervisor_virtual_processor.go index 04d32be7..32fbae0e 100644 --- a/internal/collector/hyperv/hyperv_hypervisor_virtual_processor.go +++ b/internal/collector/hyperv/hyperv_hypervisor_virtual_processor.go @@ -39,17 +39,22 @@ type collectorHypervisorVirtualProcessor struct { hypervisorVirtualProcessorModeTimeTotal *prometheus.Desc // New name for better clarity hypervisorVirtualProcessorTotalRunTimeTotal *prometheus.Desc // \Hyper-V Hypervisor Virtual Processor(*)\% Total Run Time hypervisorVirtualProcessorRunTimeTotal *prometheus.Desc // New name for better clarity - hypervisorVirtualProcessorContextSwitches *prometheus.Desc // \Hyper-V Hypervisor Virtual Processor(*)\CPU Wait Time Per Dispatch + hypervisorVirtualProcessorCPUWaitSeconds *prometheus.Desc // \Hyper-V Hypervisor Virtual Processor(*)\CPU Wait Time Per Dispatch + hypervisorVirtualProcessorDispatches *prometheus.Desc // \Hyper-V Hypervisor Virtual Processor(*)\Logical Processor Dispatches/sec } type perfDataCounterValuesHypervisorVirtualProcessor struct { Name string + // The run time counters and CPU Wait Time Per Dispatch are PERF_PRECISION_100NS_TIMER. + // Their raw values are cumulative times, which pdh scales to seconds. Despite its name + // and help text, CPU Wait Time Per Dispatch is the total wait time, not an average. HypervisorVirtualProcessorGuestRunTimePercent float64 `perfdata:"% Guest Run Time"` HypervisorVirtualProcessorHypervisorRunTimePercent float64 `perfdata:"% Hypervisor Run Time"` HypervisorVirtualProcessorTotalRunTimePercent float64 `perfdata:"% Total Run Time"` HypervisorVirtualProcessorRemoteRunTimePercent float64 `perfdata:"% Remote Run Time"` HypervisorVirtualProcessorCPUWaitTimePerDispatch float64 `perfdata:"CPU Wait Time Per Dispatch"` + HypervisorVirtualProcessorDispatches float64 `perfdata:"Logical Processor Dispatches/sec"` } func (c *Collector) buildHypervisorVirtualProcessor() error { @@ -60,42 +65,53 @@ func (c *Collector) buildHypervisorVirtualProcessor() error { return fmt.Errorf("failed to create Hyper-V Hypervisor Virtual Processor collector: %w", err) } + c.buildHypervisorVirtualProcessorDescriptors() + + return nil +} + +func (c *Collector) buildHypervisorVirtualProcessorDescriptors() { c.hypervisorVirtualProcessorTimeTotal = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "hypervisor_virtual_processor_time_total"), - "DEPRECATED: use hypervisor_virtual_processor_mode_time_total. Time that processor spent in different modes (hypervisor, guest_run, guest_idle, remote)", + "DEPRECATED: use hypervisor_virtual_processor_mode_time_total. Time in seconds that processor spent in different modes (hypervisor, guest, remote)", []string{"vm", "core", "state"}, nil, ) // New metric with better name for clarity, old one is kept for backward compatibility c.hypervisorVirtualProcessorModeTimeTotal = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "hypervisor_virtual_processor_mode_time_total"), - "Time that processor spent in different modes (hypervisor, guest_run, guest_idle, remote)", + "Time in seconds that processor spent in different modes (hypervisor, guest, remote)", []string{"vm", "core", "state"}, nil, ) // end same metric c.hypervisorVirtualProcessorTotalRunTimeTotal = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "hypervisor_virtual_processor_total_run_time_total"), - "DEPRECATED: use hypervisor_virtual_processor_run_time_total. Time that processor spent", + "DEPRECATED: use hypervisor_virtual_processor_run_time_total. Time in seconds that processor spent running", []string{"vm", "core"}, nil, ) // New metric with better name for clarity, old one is kept for backward compatibility c.hypervisorVirtualProcessorRunTimeTotal = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "hypervisor_virtual_processor_run_time_total"), - "Time that processor spent", + "Time in seconds that processor spent running", []string{"vm", "core"}, nil, ) // end same metric - c.hypervisorVirtualProcessorContextSwitches = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "hypervisor_virtual_processor_cpu_wait_time_per_dispatch_total"), - "The average time (in nanoseconds) spent waiting for a virtual processor to be dispatched onto a logical processor.", + c.hypervisorVirtualProcessorCPUWaitSeconds = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "hypervisor_virtual_processor_cpu_wait_seconds_total"), + "The time spent waiting for a virtual processor to be dispatched onto a logical processor. "+ + "Divide its rate by the rate of windows_hyperv_hypervisor_virtual_processor_dispatches_total for the average wait per dispatch.", + []string{"vm", "core"}, + nil, + ) + c.hypervisorVirtualProcessorDispatches = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "hypervisor_virtual_processor_dispatches_total"), + "The number of times the virtual processor was dispatched onto a logical processor.", []string{"vm", "core"}, nil, ) - - return nil } func (c *Collector) collectHypervisorVirtualProcessor(ch chan<- prometheus.Metric, scrape *labelcache.Scrape) error { @@ -104,6 +120,10 @@ func (c *Collector) collectHypervisorVirtualProcessor(ch chan<- prometheus.Metri return fmt.Errorf("failed to collect Hyper-V Hypervisor Virtual Processor metrics: %w", err) } + return c.publishHypervisorVirtualProcessor(ch, scrape) +} + +func (c *Collector) publishHypervisorVirtualProcessor(ch chan<- prometheus.Metric, scrape *labelcache.Scrape) error { for _, data := range c.perfDataObjectHypervisorVirtualProcessor { // The name format is :Hv VP parts := strings.Split(data.Name, ":") @@ -176,11 +196,18 @@ func (c *Collector) collectHypervisorVirtualProcessor(ch chan<- prometheus.Metri ) // end same metric ch <- scrape.MustNewConstMetric( - c.hypervisorVirtualProcessorContextSwitches, + c.hypervisorVirtualProcessorCPUWaitSeconds, prometheus.CounterValue, data.HypervisorVirtualProcessorCPUWaitTimePerDispatch, vmName, coreID, ) + + ch <- scrape.MustNewConstMetric( + c.hypervisorVirtualProcessorDispatches, + prometheus.CounterValue, + data.HypervisorVirtualProcessorDispatches, + vmName, coreID, + ) } return nil diff --git a/internal/collector/hyperv/hyperv_metric_types_internal_test.go b/internal/collector/hyperv/hyperv_metric_types_internal_test.go new file mode 100644 index 00000000..bc093a36 --- /dev/null +++ b/internal/collector/hyperv/hyperv_metric_types_internal_test.go @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build windows + +package hyperv + +import ( + "testing" + + "github.com/prometheus/client_golang/prometheus" + dto "github.com/prometheus/client_model/go" + "github.com/stretchr/testify/require" +) + +// publishCollector adapts a publish function to prometheus.Collector, so the test +// checks the metrics as a registry gathers them. +type publishCollector func(ch chan<- prometheus.Metric) error + +func (publishCollector) Describe(chan<- *prometheus.Desc) {} + +func (p publishCollector) Collect(ch chan<- prometheus.Metric) { + if err := p(ch); err != nil { + panic(err) + } +} + +type gatheredSample struct { + metricType dto.MetricType + value float64 +} + +func gatherSamples(t *testing.T, publish publishCollector) map[string]gatheredSample { + t.Helper() + + registry := prometheus.NewPedanticRegistry() + require.NoError(t, registry.Register(publish)) + + families, err := registry.Gather() + require.NoError(t, err) + + samples := make(map[string]gatheredSample, len(families)) + + for _, family := range families { + metric := family.GetMetric()[0] + + sample := gatheredSample{metricType: family.GetType()} + + switch family.GetType() { + case dto.MetricType_COUNTER: + sample.value = metric.GetCounter().GetValue() + case dto.MetricType_GAUGE: + sample.value = metric.GetGauge().GetValue() + default: + t.Fatalf("unexpected metric type %s for %s", family.GetType(), family.GetName()) + } + + samples[family.GetName()] = sample + } + + return samples +} + +func requireSample(t *testing.T, samples map[string]gatheredSample, name string, metricType dto.MetricType, value float64) { + t.Helper() + + sample, ok := samples[name] + require.Truef(t, ok, "metric %s not published", name) + require.Equalf(t, metricType, sample.metricType, "type of %s", name) + require.InDeltaf(t, value, sample.value, 1e-9, "value of %s", name) +} + +type rootPartitionFixture struct{} + +func (rootPartitionFixture) Collect(dst *[]perfDataCounterValuesHypervisorRootPartition) error { + *dst = []perfDataCounterValuesHypervisorRootPartition{{ + HypervisorRootPartitionGPAPages: 11, + HypervisorRootPartitionSkippedTimerTicks: 22, + HypervisorRootPartitionDeviceInterruptThrottleEvents: 33, + HypervisorRootPartitionIOTLBFlushCost: 0.5, + HypervisorRootPartitionIOTLBFlushes: 44, + HypervisorRootPartitionGPASpaceModifications: 55, + HypervisorRootPartitionVirtualTLBFlushEntries: 66, + }} + + return nil +} + +func (rootPartitionFixture) Close() {} + +func TestRootPartitionMetricNamesAndTypes(t *testing.T) { + t.Parallel() + + c := New(nil) + c.buildHypervisorRootPartitionDescriptors() + c.perfDataCollectorHypervisorRootPartition = rootPartitionFixture{} + + samples := gatherSamples(t, func(ch chan<- prometheus.Metric) error { + return c.collectHypervisorRootPartition(ch, nil) + }) + + requireSample(t, samples, "windows_hyperv_root_partition_gpa_pages", dto.MetricType_GAUGE, 11) + requireSample(t, samples, "windows_hyperv_root_partition_skipped_timer_ticks_total", dto.MetricType_COUNTER, 22) + requireSample(t, samples, "windows_hyperv_root_partition_device_interrupt_throttle_events_total", dto.MetricType_COUNTER, 33) + requireSample(t, samples, "windows_hyperv_root_partition_io_tlb_flush_seconds_total", dto.MetricType_COUNTER, 0.5) + requireSample(t, samples, "windows_hyperv_root_partition_io_tlb_flushes_total", dto.MetricType_COUNTER, 44) + requireSample(t, samples, "windows_hyperv_root_partition_gpa_space_modifications_total", dto.MetricType_COUNTER, 55) + requireSample(t, samples, "windows_hyperv_root_partition_virtual_tlb_flush_entries_total", dto.MetricType_COUNTER, 66) + + for _, removed := range []string{ + "windows_hyperv_root_partition_preferred_numa_node_index", + "windows_hyperv_root_partition_physical_pages_allocated", + "windows_hyperv_root_partition_io_tlb_flush_cost", + } { + require.NotContains(t, samples, removed) + } +} + +func TestVirtualProcessorCPUWaitMetrics(t *testing.T) { + t.Parallel() + + c := New(nil) + c.buildHypervisorVirtualProcessorDescriptors() + c.buildHypervisorRootVirtualProcessorDescriptors() + + // pdh has already scaled the 100ns timers to seconds. + c.perfDataObjectHypervisorVirtualProcessor = []perfDataCounterValuesHypervisorVirtualProcessor{{ + Name: "vm1:Hv VP 0", + HypervisorVirtualProcessorCPUWaitTimePerDispatch: 39.13, + HypervisorVirtualProcessorDispatches: 1086776, + }} + c.perfDataObjectHypervisorRootVirtualProcessor = []perfDataCounterValuesHypervisorRootVirtualProcessor{{ + Name: "Root VP 0", + HypervisorRootVirtualProcessorCPUWaitTimePerDispatch: 9.98, + HypervisorRootVirtualProcessorDispatches: 1082841, + }} + + samples := gatherSamples(t, func(ch chan<- prometheus.Metric) error { + if err := c.publishHypervisorVirtualProcessor(ch, nil); err != nil { + return err + } + + return c.publishHypervisorRootVirtualProcessor(ch, nil) + }) + + requireSample(t, samples, "windows_hyperv_hypervisor_virtual_processor_cpu_wait_seconds_total", dto.MetricType_COUNTER, 39.13) + requireSample(t, samples, "windows_hyperv_hypervisor_virtual_processor_dispatches_total", dto.MetricType_COUNTER, 1086776) + requireSample(t, samples, "windows_hyperv_hypervisor_root_virtual_processor_cpu_wait_seconds_total", dto.MetricType_COUNTER, 9.98) + requireSample(t, samples, "windows_hyperv_hypervisor_root_virtual_processor_dispatches_total", dto.MetricType_COUNTER, 1082841) + require.NotContains(t, samples, "windows_hyperv_hypervisor_virtual_processor_cpu_wait_time_per_dispatch_total") + require.NotContains(t, samples, "windows_hyperv_hypervisor_root_virtual_cpu_wait_time_per_dispatch_total") +} + +func TestVirtualStorageDeviceCounterTypes(t *testing.T) { + t.Parallel() + + c := New(nil) + c.buildVirtualStorageDeviceDescriptors() + c.perfDataObjectVirtualStorageDevice = []perfDataCounterValuesVirtualStorageDevice{{ + Name: "ubuntu.vhdx", + VirtualStorageDeviceThroughput: 22336, + VirtualStorageDeviceNormalizedThroughput: 36413, + VirtualStorageDeviceIOQuotaReplenishmentRate: 7, + }} + + samples := gatherSamples(t, func(ch chan<- prometheus.Metric) error { + c.publishVirtualStorageDevice(ch, nil) + + return nil + }) + + requireSample(t, samples, "windows_hyperv_virtual_storage_device_throughput_total", dto.MetricType_COUNTER, 22336) + requireSample(t, samples, "windows_hyperv_virtual_storage_device_normalized_throughput_total", dto.MetricType_COUNTER, 36413) + requireSample(t, samples, "windows_hyperv_virtual_storage_device_io_quota_replenishment_total", dto.MetricType_COUNTER, 7) + require.NotContains(t, samples, "windows_hyperv_virtual_storage_device_normalized_throughput") + require.NotContains(t, samples, "windows_hyperv_io_quota_replenishment_rate") +} diff --git a/internal/collector/hyperv/hyperv_virtual_storage_device.go b/internal/collector/hyperv/hyperv_virtual_storage_device.go index 3e1fdd01..9248ec0f 100644 --- a/internal/collector/hyperv/hyperv_virtual_storage_device.go +++ b/internal/collector/hyperv/hyperv_virtual_storage_device.go @@ -72,6 +72,12 @@ func (c *Collector) buildVirtualStorageDevice() error { return fmt.Errorf("failed to create Hyper-V Virtual Storage Device collector: %w", err) } + c.buildVirtualStorageDeviceDescriptors() + + return nil +} + +func (c *Collector) buildVirtualStorageDeviceDescriptors() { c.virtualStorageDeviceErrorCount = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "virtual_storage_device_error_count_total"), "Represents the total number of errors that have occurred on this virtual device.", @@ -118,13 +124,13 @@ func (c *Collector) buildVirtualStorageDevice() error { ) c.virtualStorageDeviceThroughput = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "virtual_storage_device_throughput_total"), - "Represents the total number of 8KB IO transfers completed by this virtual device.", + "Represents the total number of IO transfers completed by this virtual device.", []string{"device"}, nil, ) c.virtualStorageDeviceNormalizedThroughput = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "virtual_storage_device_normalized_throughput"), - "Represents the average number of IO transfers completed by this virtual device.", + prometheus.BuildFQName(types.Namespace, Name, "virtual_storage_device_normalized_throughput_total"), + "Represents the total number of IO transfers completed by this virtual device, normalized to 8KB transfers.", []string{"device"}, nil, ) @@ -157,13 +163,11 @@ func (c *Collector) buildVirtualStorageDevice() error { nil, ) c.virtualStorageDeviceIOQuotaReplenishmentRate = prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, Name, "io_quota_replenishment_rate"), - "Represents the IO quota replenishment rate for this virtual device.", + prometheus.BuildFQName(types.Namespace, Name, "virtual_storage_device_io_quota_replenishment_total"), + "Represents the IO quota replenishment counter for this virtual device. Use rate() for the replenishment rate.", []string{"device"}, nil, ) - - return nil } func (c *Collector) collectVirtualStorageDevice(ch chan<- prometheus.Metric, scrape *labelcache.Scrape) error { @@ -172,6 +176,12 @@ func (c *Collector) collectVirtualStorageDevice(ch chan<- prometheus.Metric, scr return fmt.Errorf("failed to collect Hyper-V Virtual Storage Device metrics: %w", err) } + c.publishVirtualStorageDevice(ch, scrape) + + return nil +} + +func (c *Collector) publishVirtualStorageDevice(ch chan<- prometheus.Metric, scrape *labelcache.Scrape) { for _, data := range c.perfDataObjectVirtualStorageDevice { ch <- scrape.MustNewConstMetric( c.virtualStorageDeviceErrorCount, @@ -231,7 +241,7 @@ func (c *Collector) collectVirtualStorageDevice(ch chan<- prometheus.Metric, scr ch <- scrape.MustNewConstMetric( c.virtualStorageDeviceNormalizedThroughput, - prometheus.GaugeValue, + prometheus.CounterValue, data.VirtualStorageDeviceNormalizedThroughput, data.Name, ) @@ -252,7 +262,7 @@ func (c *Collector) collectVirtualStorageDevice(ch chan<- prometheus.Metric, scr ch <- scrape.MustNewConstMetric( c.virtualStorageDeviceIOQuotaReplenishmentRate, - prometheus.GaugeValue, + prometheus.CounterValue, data.VirtualStorageDeviceIOQuotaReplenishmentRate, data.Name, ) @@ -273,6 +283,4 @@ func (c *Collector) collectVirtualStorageDevice(ch chan<- prometheus.Metric, scr data.Name, ) } - - return nil }