208
package main
import (
"fmt"
"os"
"path/filepath"
"strings"
"git.sophuwu.com/amdgpu-qstat/config"
)
func fatal(msg string) {
if config.ShowErr {
fmt.Fprintf(os.Stderr, "fatal error: %s\n", msg)
}
os.Exit(1)
}
var HwmonPath string
func getHwmonPath() {
if HwmonPath != "" {
return
}
hwmonPath := "/sys/class/hwmon"
dr, err := os.ReadDir(hwmonPath)
if err != nil || len(dr) == 0 {
fatal("unable to read hwmon")
}
var b []byte
for _, entry := range dr {
b, err = os.ReadFile(filepath.Join(hwmonPath, entry.Name(), "name"))
if err != nil {
continue
}
if strings.TrimSpace(string(b)) == "amdgpu" {
HwmonPath = filepath.Join(hwmonPath, entry.Name())
return
}
}
fatal("amdgpu hwmon not found")
}
func ERR(msg string) {
if config.ShowErr {
fmt.Fprintf(os.Stderr, "error: %s\n", msg)
}
}
func main() {
getHwmonPath()
var f Freq
if config.Freq || config.MemFreq {
err := f.Get()
if err != nil {
ERR("unable to read frequency")
config.Freq = false
config.MemFreq = false
}
}
var m Mem
if config.Mem || config.MemPerc {
err := m.Get()
if err != nil {
ERR("unable to read memory usage")
config.Mem = false
config.MemPerc = false
}
}
var val int
var err error
var s string
if config.Use || config.Temp || config.Freq || config.Power {
s += "GPU: "
if config.Use {
val, err = readNum("device/gpu_busy_percent")
if err != nil {
ERR("unable to read GPU usage")
} else {
s += fmt.Sprintf("%3d%% ", val)
}
}
if config.Freq {
if f.Core != "" {
s += fmt.Sprintf("%s ", f.Core)
}
}
if config.Power {
val, err = readNum("power1_input")
if err != nil {
ERR("unable to read power consumption")
} else {
s += fmt.Sprintf("%3.0f W ", float64(val)/1000000.0)
}
}
if config.Temp {
val, err = readNum("temp1_input")
if err != nil {
ERR("unable to read temperature")
} else {
s += fmt.Sprintf("%3d C", val/1000)
}
}
if config.Fan || config.MemFreq || config.Mem || config.MemPerc {
s += " | "
}
}
if config.Mem || config.MemPerc || config.MemFreq {
s += "MEM: "
if config.Mem && config.MemPerc {
s += fmt.Sprintf("%s (%s) ", m.Gb, m.Perc)
} else if config.Mem {
s += fmt.Sprintf("%s ", m.Gb)
} else if config.MemPerc {
s += fmt.Sprintf("%s ", m.Perc)
}
if config.MemFreq {
s += fmt.Sprintf("%s", f.Mem)
}
if config.Fan {
s += " | "
}
}
if config.Fan {
val, err = readNum("fan1_input")
if err != nil {
ERR("unable to read fan speed")
} else {
s += fmt.Sprintf("FAN: %4d RPM", val)
}
}
fmt.Println(s)
}
type Mem struct {
Perc string
Gb string
}
func (m *Mem) Get() error {
used, err := readNum("device/mem_info_vram_used")
if err != nil {
return fmt.Errorf("unable to get memory usage")
}
total, err := readNum("device/mem_info_vram_total")
if err != nil {
return fmt.Errorf("unable to get memory usage")
}
Perc := float64(used) / float64(total) * 100
m.Perc = fmt.Sprintf("%3.0f%%", Perc)
m.Gb = fmt.Sprintf("%4.2f GB / %4.2f GB", float64(used)/1073741824.0, float64(total)/1073741824.0)
return nil
}
func readNum(path string) (int, error) {
b, err := os.ReadFile(filepath.Join(HwmonPath, path))
if err != nil {
return 0, err
}
var val int
for _, c := range b {
if c < '0' || c > '9' {
break
}
val = val*10 + int(c-'0')
}
return int(val), nil
}
type Freq struct {
Core string
Mem string
}
func (f *Freq) Get() error {
dr, err := os.ReadDir(filepath.Join(HwmonPath))
if err != nil {
return fmt.Errorf("unable to get frequency")
}
var b []byte
var val int
var s string
for _, entry := range dr {
if strings.HasPrefix(entry.Name(), "freq") && strings.HasSuffix(entry.Name(), "_label") {
b, err = os.ReadFile(filepath.Join(HwmonPath, entry.Name()))
if err != nil {
return fmt.Errorf("unable to get frequency")
}
val, err = readNum(strings.Replace(entry.Name(), "_label", "_input", 1))
if err != nil {
return fmt.Errorf("unable to get frequency")
}
s = fmt.Sprintf("%4.0f MHz", float64(val)/1000000.0)
if strings.TrimSpace(string(b)) == "mclk" {
f.Mem = s
} else if strings.TrimSpace(string(b)) == "sclk" {
f.Core = s
}
}
}
return nil
}