Files

100 lines
2.4 KiB
Go

//go:build windows
package loadaverage
import (
"fmt"
"rpi5-health/internal/windows_system"
"sync"
"syscall"
"time"
"unsafe"
)
const (
// Formatting flag for PdhGetFormattedCounterValue to return a float64
PDH_FMT_DOUBLE = 0x00000200
ERROR_SUCCESS = 0
)
// PDH_FMT_COUNTERVALUE_DOUBLE maps the C struct _PDH_FMT_COUNTERVALUE for double values.
// * Note the explicit padding: C++ unions align to 8 bytes on 64-bit systems.
type PDH_FMT_COUNTERVALUE_DOUBLE struct {
CStatus uint32
Pad uint32 // ! Crucial padding for 64-bit alignment before the float64
DoubleValue float64 // Represents the union where the double is stored
}
var (
GlobalLoadAverage LoadAverageInformation
// 1. A RWMutex protects the struct from concurrent reads/writes
loadMu sync.RWMutex
startWorker = sync.OnceFunc(func() {
go loadAverageWorker()
})
)
func loadAverageWorker() {
var hQuery syscall.Handle // PDH_HQUERY
var hCounter syscall.Handle // PDH_HCOUNTER
wintools := windows_system.GetWindowsTools()
// initialize query
ret, _, _ := wintools.ProcPdhOpenQuery.Call(0, 0, uintptr(unsafe.Pointer(&hQuery)))
if ret != ERROR_SUCCESS {
fmt.Printf("PdhOpenQueryW failed: %x\n", ret)
return
}
defer wintools.ProcPdhCloseQuery.Call(uintptr(hQuery))
counterPath, _ := syscall.UTF16PtrFromString(`\Processor(_Total)\% Processor Time`)
ret, _, _ = wintools.ProcPdhAddCounter.Call(
uintptr(hQuery),
uintptr(unsafe.Pointer(counterPath)),
0,
uintptr(unsafe.Pointer(&hCounter)),
)
if ret != ERROR_SUCCESS {
fmt.Printf("PdhAddCounterW failed: %x\n", ret)
return
}
// Collect initial data, "priming the counter"
wintools.ProcPdhCollectQueryData.Call(uintptr(hQuery))
// Infinite collection loop
for {
time.Sleep(SAMPLE_DELAY)
wintools.ProcPdhCollectQueryData.Call(uintptr(hQuery))
var counterType uint32
var value PDH_FMT_COUNTERVALUE_DOUBLE
ret, _, _ = wintools.ProcPdhGetFormattedCounterValue.Call(
uintptr(hCounter),
uintptr(PDH_FMT_DOUBLE),
uintptr(unsafe.Pointer(&counterType)),
uintptr(unsafe.Pointer(&value)),
)
if ret == ERROR_SUCCESS {
loadMu.Lock()
GlobalLoadAverage.Update(value.DoubleValue)
loadMu.Unlock()
} else {
fmt.Printf("PdhGetFormattedCounterValue failed: %x\n", ret)
}
}
}
func GetLoadAverage() (LoadAverageInformation, error) {
startWorker()
loadMu.RLock()
defer loadMu.RUnlock()
return GlobalLoadAverage, nil
}