//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 }