Files

139 lines
3.0 KiB
Go

//go:build linux && openwrt
package agent
import (
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"time"
"github.com/henrygd/beszel/internal/entities/system"
)
const intelGpuStatsCmd = "sh"
type intelGpuStats struct {
PowerGPU float64
PowerPkg float64
Engines map[string]float64
}
var (
gpuStateMu sync.Mutex
lastRc6 uint64
lastGpuEnergy uint64
lastPkgEnergy uint64
lastTime time.Time
)
func getEnergyPaths() (gpuPath string, pkgPath string) {
pkgBase := "/sys/class/powercap/intel-rapl/intel-rapl:0"
gpuBase := "/sys/class/powercap/intel-rapl/intel-rapl:0/intel-rapl:0:1"
if _, err := os.Stat(pkgBase + "/energy_uj"); err == nil {
pkgPath = pkgBase + "/energy_uj"
}
if _, err := os.Stat(gpuBase + "/energy_uj"); err == nil {
gpuPath = gpuBase + "/energy_uj"
}
return gpuPath, pkgPath
}
func readUint64(path string) uint64 {
if path == "" {
return 0
}
data, err := os.ReadFile(path)
if err != nil {
return 0
}
val, _ := strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64)
return val
}
func (gm *GPUManager) updateIntelFromStats(sample *intelGpuStats) bool {
gm.Lock()
defer gm.Unlock()
id := "0"
gpuData, ok := gm.GpuDataMap[id]
if !ok {
gpuData = &system.GPUData{Name: "GPU", Engines: make(map[string]float64)}
gm.GpuDataMap[id] = gpuData
}
gpuData.Power += sample.PowerGPU
gpuData.PowerPkg += sample.PowerPkg
if gpuData.Engines == nil {
gpuData.Engines = make(map[string]float64, len(sample.Engines))
}
for name, engine := range sample.Engines {
gpuData.Engines[name] += engine
}
gpuData.Count++
return true
}
func (gm *GPUManager) collectIntelStats() (err error) {
gpuStateMu.Lock()
defer gpuStateMu.Unlock()
device := os.Getenv("INTEL_GPU_DEVICE")
if device == "" {
device = "card0"
}
device = filepath.Base(device)
rc6Path := "/sys/class/drm/" + device + "/power/rc6_residency_ms"
gpuEnergyPath, pkgEnergyPath := getEnergyPaths()
currRc6 := readUint64(rc6Path)
currGpuEnergy := readUint64(gpuEnergyPath)
currPkgEnergy := readUint64(pkgEnergyPath)
now := time.Now()
if !lastTime.IsZero() {
timeDelta := uint64(now.Sub(lastTime).Milliseconds())
if timeDelta > 500 {
rc6Delta := currRc6 - lastRc6
usage := 100.0 - (float64(rc6Delta) / float64(timeDelta) * 100.0)
if usage < 0 { usage = 0 }
var powerGPU float64
if currGpuEnergy > 0 && currGpuEnergy >= lastGpuEnergy {
energyDelta := currGpuEnergy - lastGpuEnergy
powerGPU = float64(energyDelta) / float64(timeDelta) / 1000.0
}
var powerPkg float64
if currPkgEnergy > 0 && currPkgEnergy >= lastPkgEnergy {
energyDelta := currPkgEnergy - lastPkgEnergy
powerPkg = float64(energyDelta) / float64(timeDelta) / 1000.0
}
sample := intelGpuStats{
PowerGPU: powerGPU,
PowerPkg: powerPkg,
Engines: map[string]float64{
"Render/3D": usage,
},
}
gm.updateIntelFromStats(&sample)
}
}
lastRc6 = currRc6
lastGpuEnergy = currGpuEnergy
lastPkgEnergy = currPkgEnergy
lastTime = now
time.Sleep(2 * time.Second)
return nil
}