hyperv: fix misnamed root partition metrics, counter types and units (#2720)

Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
This commit is contained in:
Jan-Otto Kröpke
2026-10-15 17:58:31 +00:00
committed by GitHub
parent 8fe85c143b
commit 55275e7db1
9 changed files with 368 additions and 87 deletions
+19 -5
View File
@@ -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`
@@ -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(
@@ -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,
)
@@ -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 <core id>
// The name format is Root VP <core id>
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
@@ -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 <VM Name>:Hv VP <vcore id>
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
@@ -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")
}
@@ -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
}