added load average calculations and moved the testing from main to their own cli commands
This commit is contained in:
1 parent
8c931aa3d8
commit
b1d4f3565d
7 files changed
+271
-26
No files matched your search
@@ -0,0 +1,55 @@
|
||||
//go:build linux
|
||||
|
||||
package loadaverage
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func getLinuxLoadAverage() (LoadAverageInformation, error) {
|
||||
loadFile, err := os.Open("/proc/loadavg")
|
||||
if err != nil {
|
||||
return LoadAverageInformation{}, fmt.Errorf("Could not find /proc/loadavg. Are you sure you are on linux?")
|
||||
}
|
||||
defer loadFile.Close()
|
||||
|
||||
fileReader := bufio.NewScanner(loadFile)
|
||||
|
||||
var one_m float64
|
||||
var five_m float64
|
||||
var fifteen_m float64
|
||||
|
||||
fileReader.Scan()
|
||||
if fileReader.Err() != nil {
|
||||
return LoadAverageInformation{}, fmt.Errorf("Could not read /proc/uptime.")
|
||||
}
|
||||
|
||||
data := fileReader.Text()
|
||||
|
||||
/*
|
||||
* fields[0] One minute load average
|
||||
* fields[1] Five minute load average
|
||||
* fields[2] Fifteen minute load average
|
||||
* fields[3] Currently executing/total scheduled
|
||||
* fields[4] Most recent process PID
|
||||
*/
|
||||
fields := strings.Split(data, " ")
|
||||
|
||||
one_m, _ = strconv.ParseFloat(fields[0], 64)
|
||||
five_m, _ = strconv.ParseFloat(fields[1], 64)
|
||||
fifteen_m, _ = strconv.ParseFloat(fields[2], 64)
|
||||
|
||||
return LoadAverageInformation{
|
||||
Load1: one_m,
|
||||
Load5: five_m,
|
||||
Load15: fifteen_m,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func GetLoadAverage() (LoadAverageInformation, error) {
|
||||
return getLinuxLoadAverage()
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package loadaverage
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Constants for calculating EMA
|
||||
|
||||
const SAMPLE_DELAY time.Duration = time.Second * 5
|
||||
|
||||
var (
|
||||
ONE_MIN_ALPHA float64 = math.Exp(-SAMPLE_DELAY.Seconds() / 60.0) // e^(-dt/W), where dt is the sample time and W
|
||||
FIVE_MIN_ALPHA float64 = math.Exp(-SAMPLE_DELAY.Seconds() / 300.0) // is the target timeframe. eg.: 60 for one min
|
||||
FIFTEEN_MIN_ALPHA float64 = math.Exp(-SAMPLE_DELAY.Seconds() / 900.0)
|
||||
)
|
||||
|
||||
type LoadAverageInformation struct {
|
||||
/*
|
||||
* Load average in the past minute
|
||||
*/
|
||||
Load1 float64
|
||||
/*
|
||||
* Load average in the past 5 minutes
|
||||
*/
|
||||
Load5 float64
|
||||
/*
|
||||
* Load average in the past 15 minutes
|
||||
*/
|
||||
Load15 float64
|
||||
|
||||
initialized bool // track whether this is the first measurement or not
|
||||
}
|
||||
|
||||
func (loadAvg *LoadAverageInformation) Update(currentSample float64) {
|
||||
if !loadAvg.initialized {
|
||||
// Seed the initial averages with the first sample to avoid a slow ramp-up from zero
|
||||
loadAvg.Load1 = currentSample
|
||||
loadAvg.Load5 = currentSample
|
||||
loadAvg.Load15 = currentSample
|
||||
loadAvg.initialized = true
|
||||
return
|
||||
}
|
||||
|
||||
loadAvg.Load1 = loadAvg.Load1*ONE_MIN_ALPHA + currentSample*(1.0-ONE_MIN_ALPHA)
|
||||
loadAvg.Load5 = loadAvg.Load5*FIVE_MIN_ALPHA + currentSample*(1.0-FIVE_MIN_ALPHA)
|
||||
loadAvg.Load15 = loadAvg.Load15*FIFTEEN_MIN_ALPHA + currentSample*(1.0-FIFTEEN_MIN_ALPHA)
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
//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
|
||||
}
|
||||
Reference in new issue
Block a user