diff --git a/collector/cpu.go b/collector/cpu.go index 0a5bc59b..57b8f38d 100644 --- a/collector/cpu.go +++ b/collector/cpu.go @@ -3,23 +3,54 @@ package collector import ( + "strconv" "strings" "github.com/StackExchange/wmi" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/log" + "golang.org/x/sys/windows/registry" ) func init() { Factories["cpu"] = NewCPUCollector } +//A function to get windows version from registry + +func getWindowsVersion() float64 { + k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE) + if err != nil { + log.Warn("Couldn't open registry", err) + return 0 + } + defer func() { + err = k.Close() + if err != nil { + log.Warnf("Failed to close registry key: %v", err) + } + }() + + currentv, _, err := k.GetStringValue("CurrentVersion") + if err != nil { + log.Warn("Couldn't open registry to determine current Windows version:", err) + return 0 + } + + currentv_flt, err := strconv.ParseFloat(currentv, 64) + + log.Debugf("Detected Windows version %f\n", currentv_flt) + + return currentv_flt +} + // A CPUCollector is a Prometheus collector for WMI Win32_PerfRawData_PerfOS_Processor metrics type CPUCollector struct { CStateSecondsTotal *prometheus.Desc TimeTotal *prometheus.Desc InterruptsTotal *prometheus.Desc DPCsTotal *prometheus.Desc + ProcessorFrequency *prometheus.Desc } // NewCPUCollector constructs a new CPUCollector @@ -51,6 +82,12 @@ func NewCPUCollector() (Collector, error) { []string{"core"}, nil, ), + ProcessorFrequency: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, subsystem, "core_frequence_mhz"), + "Core frequency in megahertz", + []string{"core"}, + nil, + ), }, nil } @@ -85,7 +122,6 @@ type Win32_PerfRawData_PerfOS_Processor struct { PercentUserTime uint64 } -/* NOTE: This is an alternative class, but it is not as widely available. Decide which to use type Win32_PerfRawData_Counters_ProcessorInformation struct { Name string AverageIdleTime uint64 @@ -116,36 +152,110 @@ type Win32_PerfRawData_Counters_ProcessorInformation struct { PerformanceLimitFlags uint64 ProcessorFrequency uint64 ProcessorStateFlags uint64 -}*/ +} func (c *CPUCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) { + version := getWindowsVersion() + // Windows version by number https://docs.microsoft.com/en-us/windows/desktop/sysinfo/operating-system-version + // For Windows 2008 or earlier Windows version is 6.0 or lower, so we use Win32_PerfRawData_PerfOS_Processor class + // For Windows 2008 R2 or later Windows version is 6.1 or higher, so we use Win32_PerfRawData_Counters_ProcessorInformation + // Value 6.05 was selected just to split between WIndows versions + if version > 6.05 { + var dst []Win32_PerfRawData_Counters_ProcessorInformation + q := queryAll(&dst) + if err := wmi.Query(q, &dst); err != nil { + return nil, err + } + + for _, data := range dst { + if strings.Contains(data.Name, "_Total") { + continue + } + core := data.Name + + ch <- prometheus.MustNewConstMetric( + c.ProcessorFrequency, + prometheus.GaugeValue, + float64(data.ProcessorFrequency), + core, + ) + ch <- prometheus.MustNewConstMetric( + c.CStateSecondsTotal, + prometheus.CounterValue, + float64(data.PercentC1Time)*ticksToSecondsScaleFactor, + core, "c1", + ) + ch <- prometheus.MustNewConstMetric( + c.CStateSecondsTotal, + prometheus.CounterValue, + float64(data.PercentC2Time)*ticksToSecondsScaleFactor, + core, "c2", + ) + ch <- prometheus.MustNewConstMetric( + c.CStateSecondsTotal, + prometheus.CounterValue, + float64(data.PercentC3Time)*ticksToSecondsScaleFactor, + core, "c3", + ) + + ch <- prometheus.MustNewConstMetric( + c.TimeTotal, + prometheus.CounterValue, + float64(data.PercentIdleTime)*ticksToSecondsScaleFactor, + core, "idle", + ) + ch <- prometheus.MustNewConstMetric( + c.TimeTotal, + prometheus.CounterValue, + float64(data.PercentInterruptTime)*ticksToSecondsScaleFactor, + core, "interrupt", + ) + ch <- prometheus.MustNewConstMetric( + c.TimeTotal, + prometheus.CounterValue, + float64(data.PercentDPCTime)*ticksToSecondsScaleFactor, + core, "dpc", + ) + ch <- prometheus.MustNewConstMetric( + c.TimeTotal, + prometheus.CounterValue, + float64(data.PercentPrivilegedTime)*ticksToSecondsScaleFactor, + core, "privileged", + ) + ch <- prometheus.MustNewConstMetric( + c.TimeTotal, + prometheus.CounterValue, + float64(data.PercentUserTime)*ticksToSecondsScaleFactor, + core, "user", + ) + + ch <- prometheus.MustNewConstMetric( + c.InterruptsTotal, + prometheus.CounterValue, + float64(data.InterruptsPersec), + core, + ) + ch <- prometheus.MustNewConstMetric( + c.DPCsTotal, + prometheus.CounterValue, + float64(data.DPCsQueuedPersec), + core, + ) + } + + return nil, nil + + } var dst []Win32_PerfRawData_PerfOS_Processor q := queryAll(&dst) if err := wmi.Query(q, &dst); err != nil { return nil, err } - for _, data := range dst { if strings.Contains(data.Name, "_Total") { continue } - core := data.Name - - // These are only available from Win32_PerfRawData_Counters_ProcessorInformation, which is only available from Win2008R2+ - /*ch <- prometheus.MustNewConstMetric( - c.ProcessorFrequency, - prometheus.GaugeValue, - float64(data.ProcessorFrequency), - socket, core, - ) - ch <- prometheus.MustNewConstMetric( - c.MaximumFrequency, - prometheus.GaugeValue, - float64(data.PercentofMaximumFrequency)/100*float64(data.ProcessorFrequency), - socket, core, - )*/ - ch <- prometheus.MustNewConstMetric( c.CStateSecondsTotal, prometheus.CounterValue, @@ -164,7 +274,6 @@ func (c *CPUCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, e float64(data.PercentC3Time)*ticksToSecondsScaleFactor, core, "c3", ) - ch <- prometheus.MustNewConstMetric( c.TimeTotal, prometheus.CounterValue, @@ -195,7 +304,6 @@ func (c *CPUCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, e float64(data.PercentUserTime)*ticksToSecondsScaleFactor, core, "user", ) - ch <- prometheus.MustNewConstMetric( c.InterruptsTotal, prometheus.CounterValue,