Files
run/runtime/metrics_host_windows.go
2026-08-26 09:56:43 +08:00

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
}