88 lines
2.0 KiB
Go
88 lines
2.0 KiB
Go
//go:build linux
|
|
|
|
package hoststats
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
func readAMD(ctx context.Context, device string) (AMD, error) {
|
|
if err := ctx.Err(); err != nil {
|
|
return AMD{}, err
|
|
}
|
|
if strings.TrimSpace(device) == "" {
|
|
var err error
|
|
device, err = findAMDDevice("/sys/class/drm")
|
|
if err != nil {
|
|
return AMD{}, err
|
|
}
|
|
}
|
|
return readAMDDevice(ctx, device)
|
|
}
|
|
|
|
func findAMDDevice(root string) (string, error) {
|
|
cards, err := filepath.Glob(filepath.Join(root, "card*", "device"))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
for _, device := range cards {
|
|
b, err := os.ReadFile(filepath.Join(device, "vendor"))
|
|
if err != nil {
|
|
continue
|
|
}
|
|
if strings.EqualFold(strings.TrimSpace(string(b)), "0x1002") {
|
|
return device, nil
|
|
}
|
|
}
|
|
return "", fmt.Errorf("no AMDGPU sysfs device found")
|
|
}
|
|
|
|
func readAMDDevice(ctx context.Context, device string) (AMD, error) {
|
|
if err := ctx.Err(); err != nil {
|
|
return AMD{}, err
|
|
}
|
|
var out AMD
|
|
var found bool
|
|
if v, ok := readInt64(filepath.Join(device, "mem_info_vram_total")); ok {
|
|
out.MemoryTotalBytes = v
|
|
found = true
|
|
}
|
|
if v, ok := readInt64(filepath.Join(device, "mem_info_vram_used")); ok {
|
|
out.MemoryUsedBytes = v
|
|
found = true
|
|
}
|
|
if v, ok := readInt64(filepath.Join(device, "gpu_busy_percent")); ok {
|
|
out.UtilizationPercent = float64(v)
|
|
found = true
|
|
}
|
|
hwmons, _ := filepath.Glob(filepath.Join(device, "hwmon", "hwmon*"))
|
|
for _, hw := range hwmons {
|
|
if v, ok := readInt64(filepath.Join(hw, "temp1_input")); ok && out.TemperatureC == 0 {
|
|
out.TemperatureC = float64(v) / 1000
|
|
found = true
|
|
}
|
|
if v, ok := readInt64(filepath.Join(hw, "power1_average")); ok && out.PowerWatts == 0 {
|
|
out.PowerWatts = float64(v) / 1_000_000
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
return AMD{}, fmt.Errorf("no readable AMDGPU telemetry in %s", device)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func readInt64(path string) (int64, bool) {
|
|
b, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return 0, false
|
|
}
|
|
v, err := strconv.ParseInt(strings.TrimSpace(string(b)), 10, 64)
|
|
return v, err == nil
|
|
}
|