102 lines
2.6 KiB
Go
102 lines
2.6 KiB
Go
//go:build windows
|
|
|
|
package runtime
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"syscall"
|
|
"time"
|
|
"unsafe"
|
|
)
|
|
|
|
const cpuSampleInterval = 100 * time.Millisecond
|
|
|
|
var (
|
|
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
|
getSystemTimesProc = kernel32.NewProc("GetSystemTimes")
|
|
globalMemoryStatusExProc = kernel32.NewProc("GlobalMemoryStatusEx")
|
|
)
|
|
|
|
type windowsFileTime struct {
|
|
lowDateTime uint32
|
|
highDateTime uint32
|
|
}
|
|
|
|
type windowsMemoryStatusEx struct {
|
|
dwLength uint32
|
|
dwMemoryLoad uint32
|
|
ullTotalPhys uint64
|
|
ullAvailPhys uint64
|
|
ullTotalPageFile uint64
|
|
ullAvailPageFile uint64
|
|
ullTotalVirtual uint64
|
|
ullAvailVirtual uint64
|
|
ullAvailExtendedVirtual uint64
|
|
}
|
|
|
|
type windowsCPUStat struct {
|
|
idle uint64
|
|
total uint64
|
|
}
|
|
|
|
func hostCPUPercent(ctx context.Context) (float64, error) {
|
|
first, err := readWindowsCPUStat()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
timer := time.NewTimer(cpuSampleInterval)
|
|
defer timer.Stop()
|
|
select {
|
|
case <-ctx.Done():
|
|
return 0, ctx.Err()
|
|
case <-timer.C:
|
|
}
|
|
second, err := readWindowsCPUStat()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
totalDelta := second.total - first.total
|
|
idleDelta := second.idle - first.idle
|
|
if totalDelta == 0 || idleDelta > totalDelta {
|
|
return 0, fmt.Errorf("Windows CPU counters did not advance")
|
|
}
|
|
return clampMetricPercent(100 * float64(totalDelta-idleDelta) / float64(totalDelta)), nil
|
|
}
|
|
|
|
func readWindowsCPUStat() (windowsCPUStat, error) {
|
|
var idle, kernel, user windowsFileTime
|
|
result, _, callErr := getSystemTimesProc.Call(uintptr(unsafe.Pointer(&idle)), uintptr(unsafe.Pointer(&kernel)), uintptr(unsafe.Pointer(&user)))
|
|
if result == 0 {
|
|
return windowsCPUStat{}, callErr
|
|
}
|
|
idleTicks := windowsFileTimeValue(idle)
|
|
return windowsCPUStat{idle: idleTicks, total: idleTicks + windowsFileTimeValue(kernel) + windowsFileTimeValue(user)}, nil
|
|
}
|
|
|
|
func windowsFileTimeValue(value windowsFileTime) uint64 {
|
|
return uint64(value.highDateTime)<<32 | uint64(value.lowDateTime)
|
|
}
|
|
|
|
func hostMemoryPercent() (float64, error) {
|
|
status := windowsMemoryStatusEx{dwLength: uint32(unsafe.Sizeof(windowsMemoryStatusEx{}))}
|
|
result, _, callErr := globalMemoryStatusExProc.Call(uintptr(unsafe.Pointer(&status)))
|
|
if result == 0 {
|
|
return 0, callErr
|
|
}
|
|
if status.ullTotalPhys == 0 || status.ullAvailPhys > status.ullTotalPhys {
|
|
return 0, fmt.Errorf("Windows memory counters are invalid")
|
|
}
|
|
return clampMetricPercent(100 * float64(status.ullTotalPhys-status.ullAvailPhys) / float64(status.ullTotalPhys)), nil
|
|
}
|
|
|
|
func clampMetricPercent(value float64) float64 {
|
|
if value < 0 {
|
|
return 0
|
|
}
|
|
if value > 100 {
|
|
return 100
|
|
}
|
|
return value
|
|
}
|