metric/cpu/metrics_windows.go (84 lines of code) (raw):
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you 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.
/*
For testing via the win2012 vagrant box:
vagrant winrm -s cmd -e -c "cd C:\\Gopath\src\\github.com\\elastic\\beats\\metricbeat\\module\\system\\cpu; go test -v -tags=integration -run TestFetch" win2012
*/
package cpu
import (
"fmt"
"time"
"github.com/elastic/elastic-agent-libs/helpers/windows/pdh"
"github.com/elastic/elastic-agent-libs/opt"
"github.com/elastic/gosigar/sys/windows"
)
var (
processorInformationCounter = "\\Processor Information(%s)\\%s"
totalKernelTimeCounter = fmt.Sprintf(processorInformationCounter, "*", "% Privileged Time")
totalIdleTimeCounter = fmt.Sprintf(processorInformationCounter, "*", "% Idle Time")
totalUserTimeCounter = fmt.Sprintf(processorInformationCounter, "*", "% User Time")
)
// Get fetches Windows CPU system times
func Get(m *Monitor) (CPUMetrics, error) {
if m.query == nil {
return getUsingSystemTimes()
}
return getUsingPerfCounters(m.query)
}
func getUsingSystemTimes() (CPUMetrics, error) {
idle, kernel, user, err := windows.GetSystemTimes()
if err != nil {
return CPUMetrics{}, fmt.Errorf("call to GetSystemTimes failed: %w", err)
}
globalMetrics := CPUMetrics{}
//convert from duration to ticks
idleMetric := uint64(idle / time.Millisecond)
sysMetric := uint64(kernel / time.Millisecond)
userMetrics := uint64(user / time.Millisecond)
globalMetrics.totals.Idle = opt.UintWith(idleMetric)
globalMetrics.totals.Sys = opt.UintWith(sysMetric)
globalMetrics.totals.User = opt.UintWith(userMetrics)
// get per-cpu data
cpus, err := windows.NtQuerySystemProcessorPerformanceInformation()
if err != nil {
return CPUMetrics{}, fmt.Errorf("catll to NtQuerySystemProcessorPerformanceInformation failed: %w", err)
}
globalMetrics.list = make([]CPU, 0, len(cpus))
for _, cpu := range cpus {
idleMetric := uint64(cpu.IdleTime / time.Millisecond)
sysMetric := uint64(cpu.KernelTime / time.Millisecond)
userMetrics := uint64(cpu.UserTime / time.Millisecond)
globalMetrics.list = append(globalMetrics.list, CPU{
Idle: opt.UintWith(idleMetric),
Sys: opt.UintWith(sysMetric),
User: opt.UintWith(userMetrics),
})
}
return globalMetrics, nil
}
func getUsingPerfCounters(query *pdh.Query) (CPUMetrics, error) {
globalMetrics := CPUMetrics{}
if err := query.CollectData(); err != nil {
return globalMetrics, err
}
kernelRawData, err := query.GetRawCounterArray(totalKernelTimeCounter, true)
if err != nil {
return globalMetrics, fmt.Errorf("error calling GetRawCounterArray for kernel counter: %w", err)
}
idleRawData, err := query.GetRawCounterArray(totalIdleTimeCounter, true)
if err != nil {
return globalMetrics, fmt.Errorf("error calling GetRawCounterArray for idle counter: %w", err)
}
userRawData, err := query.GetRawCounterArray(totalUserTimeCounter, true)
if err != nil {
return globalMetrics, fmt.Errorf("error calling GetRawCounterArray for user counter: %w", err)
}
var idle, kernel, user time.Duration
globalMetrics.list = make([]CPU, len(userRawData))
for i := 0; i < len(globalMetrics.list); i++ {
// The values returned by GetRawCounterArray are of equal length and are sorted by instance names.
// For CPU core {i}, idleRawData[i], kernelRawData[i], and userRawData[i] correspond to the idle time, kernel time, and user time, respectively.
// values returned by counter are in 100-ns intervals. Hence, convert it to millisecond.
idleTime := time.Duration(idleRawData[i].RawValue.FirstValue*100) / time.Millisecond
kernelTime := time.Duration(kernelRawData[i].RawValue.FirstValue*100) / time.Millisecond
userTime := time.Duration(userRawData[i].RawValue.FirstValue*100) / time.Millisecond
globalMetrics.list[i].Idle = opt.UintWith(uint64(idleTime))
globalMetrics.list[i].Sys = opt.UintWith(uint64(kernelTime))
globalMetrics.list[i].User = opt.UintWith(uint64(userTime))
// add the per-cpu time to track the total time spent by system
idle += idleTime
kernel += kernelTime
user += userTime
}
globalMetrics.totals.Idle = opt.UintWith(uint64(idle))
globalMetrics.totals.Sys = opt.UintWith(uint64(kernel))
globalMetrics.totals.User = opt.UintWith(uint64(user))
return globalMetrics, nil
}