|
|
package xsysinfo |
|
|
|
|
|
import ( |
|
|
"bytes" |
|
|
"encoding/json" |
|
|
"os/exec" |
|
|
"strconv" |
|
|
"strings" |
|
|
"sync" |
|
|
|
|
|
"github.com/jaypipes/ghw" |
|
|
"github.com/jaypipes/ghw/pkg/gpu" |
|
|
"github.com/mudler/xlog" |
|
|
) |
|
|
|
|
|
|
|
|
const ( |
|
|
VendorNVIDIA = "nvidia" |
|
|
VendorAMD = "amd" |
|
|
VendorIntel = "intel" |
|
|
VendorVulkan = "vulkan" |
|
|
VendorUnknown = "unknown" |
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var UnifiedMemoryDevices = []string{ |
|
|
"NVIDIA GB10", |
|
|
"GB10", |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
type GPUMemoryInfo struct { |
|
|
Index int `json:"index"` |
|
|
Name string `json:"name"` |
|
|
Vendor string `json:"vendor"` |
|
|
TotalVRAM uint64 `json:"total_vram"` |
|
|
UsedVRAM uint64 `json:"used_vram"` |
|
|
FreeVRAM uint64 `json:"free_vram"` |
|
|
UsagePercent float64 `json:"usage_percent"` |
|
|
} |
|
|
|
|
|
|
|
|
type GPUAggregateInfo struct { |
|
|
TotalVRAM uint64 `json:"total_vram"` |
|
|
UsedVRAM uint64 `json:"used_vram"` |
|
|
FreeVRAM uint64 `json:"free_vram"` |
|
|
UsagePercent float64 `json:"usage_percent"` |
|
|
GPUCount int `json:"gpu_count"` |
|
|
} |
|
|
|
|
|
|
|
|
type AggregateMemoryInfo struct { |
|
|
TotalMemory uint64 `json:"total_memory"` |
|
|
UsedMemory uint64 `json:"used_memory"` |
|
|
FreeMemory uint64 `json:"free_memory"` |
|
|
UsagePercent float64 `json:"usage_percent"` |
|
|
GPUCount int `json:"gpu_count"` |
|
|
} |
|
|
|
|
|
|
|
|
type ResourceInfo struct { |
|
|
Type string `json:"type"` |
|
|
Available bool `json:"available"` |
|
|
GPUs []GPUMemoryInfo `json:"gpus,omitempty"` |
|
|
RAM *SystemRAMInfo `json:"ram,omitempty"` |
|
|
Aggregate AggregateMemoryInfo `json:"aggregate"` |
|
|
} |
|
|
|
|
|
var ( |
|
|
gpuCache []*gpu.GraphicsCard |
|
|
gpuCacheOnce sync.Once |
|
|
gpuCacheErr error |
|
|
) |
|
|
|
|
|
func GPUs() ([]*gpu.GraphicsCard, error) { |
|
|
gpuCacheOnce.Do(func() { |
|
|
gpu, err := ghw.GPU() |
|
|
if err != nil { |
|
|
gpuCacheErr = err |
|
|
return |
|
|
} |
|
|
gpuCache = gpu.GraphicsCards |
|
|
}) |
|
|
|
|
|
return gpuCache, gpuCacheErr |
|
|
} |
|
|
|
|
|
func TotalAvailableVRAM() (uint64, error) { |
|
|
|
|
|
gpus, err := GPUs() |
|
|
if err == nil { |
|
|
var totalVRAM uint64 |
|
|
for _, gpu := range gpus { |
|
|
if gpu != nil && gpu.Node != nil && gpu.Node.Memory != nil { |
|
|
if gpu.Node.Memory.TotalUsableBytes > 0 { |
|
|
totalVRAM += uint64(gpu.Node.Memory.TotalUsableBytes) |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
if totalVRAM > 0 { |
|
|
return totalVRAM, nil |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
gpuMemoryInfo := GetGPUMemoryUsage() |
|
|
if len(gpuMemoryInfo) > 0 { |
|
|
var totalVRAM uint64 |
|
|
for _, gpu := range gpuMemoryInfo { |
|
|
totalVRAM += gpu.TotalVRAM |
|
|
} |
|
|
if totalVRAM > 0 { |
|
|
xlog.Debug("VRAM detected via binary tools", "total_vram", totalVRAM) |
|
|
return totalVRAM, nil |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return 0, nil |
|
|
} |
|
|
|
|
|
func HasGPU(vendor string) bool { |
|
|
gpus, err := GPUs() |
|
|
if err != nil { |
|
|
return false |
|
|
} |
|
|
if vendor == "" { |
|
|
return len(gpus) > 0 |
|
|
} |
|
|
for _, gpu := range gpus { |
|
|
if strings.Contains(gpu.String(), vendor) { |
|
|
return true |
|
|
} |
|
|
} |
|
|
return false |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func DetectGPUVendor() (string, error) { |
|
|
|
|
|
gpus, err := GPUs() |
|
|
if err == nil && len(gpus) > 0 { |
|
|
for _, gpu := range gpus { |
|
|
if gpu.DeviceInfo != nil && gpu.DeviceInfo.Vendor != nil { |
|
|
vendorName := strings.ToUpper(gpu.DeviceInfo.Vendor.Name) |
|
|
if strings.Contains(vendorName, strings.ToUpper(VendorNVIDIA)) { |
|
|
xlog.Debug("GPU vendor detected via ghw", "vendor", VendorNVIDIA) |
|
|
return VendorNVIDIA, nil |
|
|
} |
|
|
if strings.Contains(vendorName, strings.ToUpper(VendorAMD)) { |
|
|
xlog.Debug("GPU vendor detected via ghw", "vendor", VendorAMD) |
|
|
return VendorAMD, nil |
|
|
} |
|
|
if strings.Contains(vendorName, strings.ToUpper(VendorIntel)) { |
|
|
xlog.Debug("GPU vendor detected via ghw", "vendor", VendorIntel) |
|
|
return VendorIntel, nil |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if _, err := exec.LookPath("nvidia-smi"); err == nil { |
|
|
xlog.Debug("GPU vendor detected via binary", "vendor", VendorNVIDIA, "binary", "nvidia-smi") |
|
|
return VendorNVIDIA, nil |
|
|
} |
|
|
|
|
|
|
|
|
if _, err := exec.LookPath("rocm-smi"); err == nil { |
|
|
xlog.Debug("GPU vendor detected via binary", "vendor", VendorAMD, "binary", "rocm-smi") |
|
|
return VendorAMD, nil |
|
|
} |
|
|
|
|
|
|
|
|
if _, err := exec.LookPath("xpu-smi"); err == nil { |
|
|
xlog.Debug("GPU vendor detected via binary", "vendor", VendorIntel, "binary", "xpu-smi") |
|
|
return VendorIntel, nil |
|
|
} |
|
|
if _, err := exec.LookPath("intel_gpu_top"); err == nil { |
|
|
xlog.Debug("GPU vendor detected via binary", "vendor", VendorIntel, "binary", "intel_gpu_top") |
|
|
return VendorIntel, nil |
|
|
} |
|
|
|
|
|
|
|
|
if _, err := exec.LookPath("vulkaninfo"); err == nil { |
|
|
xlog.Debug("GPU vendor detected via binary", "vendor", VendorVulkan, "binary", "vulkaninfo") |
|
|
return VendorVulkan, nil |
|
|
} |
|
|
|
|
|
|
|
|
return "", nil |
|
|
} |
|
|
|
|
|
|
|
|
func isUnifiedMemoryDevice(gpuName string) bool { |
|
|
gpuNameUpper := strings.ToUpper(gpuName) |
|
|
for _, pattern := range UnifiedMemoryDevices { |
|
|
if strings.Contains(gpuNameUpper, strings.ToUpper(pattern)) { |
|
|
return true |
|
|
} |
|
|
} |
|
|
return false |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func GetGPUMemoryUsage() []GPUMemoryInfo { |
|
|
var gpus []GPUMemoryInfo |
|
|
|
|
|
|
|
|
nvidiaGPUs := getNVIDIAGPUMemory() |
|
|
if len(nvidiaGPUs) > 0 { |
|
|
gpus = append(gpus, nvidiaGPUs...) |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
amdGPUs := getAMDGPUMemory() |
|
|
if len(amdGPUs) > 0 { |
|
|
|
|
|
startIdx := len(gpus) |
|
|
for i := range amdGPUs { |
|
|
amdGPUs[i].Index = startIdx + i |
|
|
} |
|
|
gpus = append(gpus, amdGPUs...) |
|
|
} |
|
|
|
|
|
|
|
|
intelGPUs := getIntelGPUMemory() |
|
|
if len(intelGPUs) > 0 { |
|
|
startIdx := len(gpus) |
|
|
for i := range intelGPUs { |
|
|
intelGPUs[i].Index = startIdx + i |
|
|
} |
|
|
gpus = append(gpus, intelGPUs...) |
|
|
} |
|
|
|
|
|
|
|
|
if len(gpus) == 0 { |
|
|
vulkanGPUs := getVulkanGPUMemory() |
|
|
gpus = append(gpus, vulkanGPUs...) |
|
|
} |
|
|
|
|
|
return gpus |
|
|
} |
|
|
|
|
|
|
|
|
func GetGPUAggregateInfo() GPUAggregateInfo { |
|
|
gpus := GetGPUMemoryUsage() |
|
|
|
|
|
var aggregate GPUAggregateInfo |
|
|
aggregate.GPUCount = len(gpus) |
|
|
|
|
|
for _, gpu := range gpus { |
|
|
aggregate.TotalVRAM += gpu.TotalVRAM |
|
|
aggregate.UsedVRAM += gpu.UsedVRAM |
|
|
aggregate.FreeVRAM += gpu.FreeVRAM |
|
|
} |
|
|
|
|
|
if aggregate.TotalVRAM > 0 { |
|
|
aggregate.UsagePercent = float64(aggregate.UsedVRAM) / float64(aggregate.TotalVRAM) * 100 |
|
|
} |
|
|
|
|
|
return aggregate |
|
|
} |
|
|
|
|
|
|
|
|
func getNVIDIAGPUMemory() []GPUMemoryInfo { |
|
|
|
|
|
if _, err := exec.LookPath("nvidia-smi"); err != nil { |
|
|
return nil |
|
|
} |
|
|
|
|
|
cmd := exec.Command("nvidia-smi", |
|
|
"--query-gpu=index,name,memory.total,memory.used,memory.free", |
|
|
"--format=csv,noheader,nounits") |
|
|
|
|
|
var stdout, stderr bytes.Buffer |
|
|
cmd.Stdout = &stdout |
|
|
cmd.Stderr = &stderr |
|
|
|
|
|
if err := cmd.Run(); err != nil { |
|
|
xlog.Debug("nvidia-smi failed", "error", err, "stderr", stderr.String()) |
|
|
return nil |
|
|
} |
|
|
|
|
|
var gpus []GPUMemoryInfo |
|
|
lines := strings.Split(strings.TrimSpace(stdout.String()), "\n") |
|
|
|
|
|
for _, line := range lines { |
|
|
if line == "" { |
|
|
continue |
|
|
} |
|
|
|
|
|
parts := strings.Split(line, ", ") |
|
|
if len(parts) < 5 { |
|
|
continue |
|
|
} |
|
|
|
|
|
idx, _ := strconv.Atoi(strings.TrimSpace(parts[0])) |
|
|
name := strings.TrimSpace(parts[1]) |
|
|
totalStr := strings.TrimSpace(parts[2]) |
|
|
usedStr := strings.TrimSpace(parts[3]) |
|
|
freeStr := strings.TrimSpace(parts[4]) |
|
|
|
|
|
var totalBytes, usedBytes, freeBytes uint64 |
|
|
var usagePercent float64 |
|
|
|
|
|
|
|
|
isNA := totalStr == "[N/A]" || usedStr == "[N/A]" || freeStr == "[N/A]" |
|
|
|
|
|
if isNA && isUnifiedMemoryDevice(name) { |
|
|
|
|
|
sysInfo, err := GetSystemRAMInfo() |
|
|
if err != nil { |
|
|
xlog.Debug("failed to get system RAM for unified memory device", "error", err, "device", name) |
|
|
|
|
|
gpus = append(gpus, GPUMemoryInfo{ |
|
|
Index: idx, |
|
|
Name: name, |
|
|
Vendor: VendorNVIDIA, |
|
|
TotalVRAM: 0, |
|
|
UsedVRAM: 0, |
|
|
FreeVRAM: 0, |
|
|
UsagePercent: 0, |
|
|
}) |
|
|
continue |
|
|
} |
|
|
|
|
|
totalBytes = sysInfo.Total |
|
|
usedBytes = sysInfo.Used |
|
|
freeBytes = sysInfo.Free |
|
|
if totalBytes > 0 { |
|
|
usagePercent = float64(usedBytes) / float64(totalBytes) * 100 |
|
|
} |
|
|
|
|
|
xlog.Debug("using system RAM for unified memory GPU", "device", name, "system_ram_bytes", totalBytes) |
|
|
} else if isNA { |
|
|
|
|
|
xlog.Debug("nvidia-smi returned N/A for unknown device", "device", name) |
|
|
gpus = append(gpus, GPUMemoryInfo{ |
|
|
Index: idx, |
|
|
Name: name, |
|
|
Vendor: VendorNVIDIA, |
|
|
TotalVRAM: 0, |
|
|
UsedVRAM: 0, |
|
|
FreeVRAM: 0, |
|
|
UsagePercent: 0, |
|
|
}) |
|
|
continue |
|
|
} else { |
|
|
|
|
|
totalMB, _ := strconv.ParseFloat(totalStr, 64) |
|
|
usedMB, _ := strconv.ParseFloat(usedStr, 64) |
|
|
freeMB, _ := strconv.ParseFloat(freeStr, 64) |
|
|
|
|
|
|
|
|
totalBytes = uint64(totalMB * 1024 * 1024) |
|
|
usedBytes = uint64(usedMB * 1024 * 1024) |
|
|
freeBytes = uint64(freeMB * 1024 * 1024) |
|
|
|
|
|
if totalBytes > 0 { |
|
|
usagePercent = float64(usedBytes) / float64(totalBytes) * 100 |
|
|
} |
|
|
} |
|
|
|
|
|
gpus = append(gpus, GPUMemoryInfo{ |
|
|
Index: idx, |
|
|
Name: name, |
|
|
Vendor: VendorNVIDIA, |
|
|
TotalVRAM: totalBytes, |
|
|
UsedVRAM: usedBytes, |
|
|
FreeVRAM: freeBytes, |
|
|
UsagePercent: usagePercent, |
|
|
}) |
|
|
} |
|
|
|
|
|
return gpus |
|
|
} |
|
|
|
|
|
|
|
|
func getAMDGPUMemory() []GPUMemoryInfo { |
|
|
|
|
|
if _, err := exec.LookPath("rocm-smi"); err != nil { |
|
|
return nil |
|
|
} |
|
|
|
|
|
|
|
|
cmd := exec.Command("rocm-smi", "--showmeminfo", "vram", "--csv") |
|
|
|
|
|
var stdout, stderr bytes.Buffer |
|
|
cmd.Stdout = &stdout |
|
|
cmd.Stderr = &stderr |
|
|
|
|
|
if err := cmd.Run(); err != nil { |
|
|
xlog.Debug("rocm-smi failed", "error", err, "stderr", stderr.String()) |
|
|
return nil |
|
|
} |
|
|
|
|
|
var gpus []GPUMemoryInfo |
|
|
lines := strings.Split(strings.TrimSpace(stdout.String()), "\n") |
|
|
|
|
|
|
|
|
for i, line := range lines { |
|
|
if i == 0 || line == "" { |
|
|
continue |
|
|
} |
|
|
|
|
|
parts := strings.Split(line, ",") |
|
|
if len(parts) < 3 { |
|
|
continue |
|
|
} |
|
|
|
|
|
|
|
|
idxStr := strings.TrimSpace(parts[0]) |
|
|
idx := 0 |
|
|
if strings.HasPrefix(idxStr, "GPU[") { |
|
|
idxStr = strings.TrimPrefix(idxStr, "GPU[") |
|
|
idxStr = strings.TrimSuffix(idxStr, "]") |
|
|
idx, _ = strconv.Atoi(idxStr) |
|
|
} |
|
|
|
|
|
|
|
|
usedBytes, _ := strconv.ParseUint(strings.TrimSpace(parts[2]), 10, 64) |
|
|
totalBytes, _ := strconv.ParseUint(strings.TrimSpace(parts[1]), 10, 64) |
|
|
|
|
|
|
|
|
if totalBytes < 1000000 { |
|
|
usedBytes *= 1024 * 1024 |
|
|
totalBytes *= 1024 * 1024 |
|
|
} |
|
|
|
|
|
freeBytes := uint64(0) |
|
|
if totalBytes > usedBytes { |
|
|
freeBytes = totalBytes - usedBytes |
|
|
} |
|
|
|
|
|
usagePercent := 0.0 |
|
|
if totalBytes > 0 { |
|
|
usagePercent = float64(usedBytes) / float64(totalBytes) * 100 |
|
|
} |
|
|
|
|
|
gpus = append(gpus, GPUMemoryInfo{ |
|
|
Index: idx, |
|
|
Name: "AMD GPU", |
|
|
Vendor: VendorAMD, |
|
|
TotalVRAM: totalBytes, |
|
|
UsedVRAM: usedBytes, |
|
|
FreeVRAM: freeBytes, |
|
|
UsagePercent: usagePercent, |
|
|
}) |
|
|
} |
|
|
|
|
|
return gpus |
|
|
} |
|
|
|
|
|
|
|
|
func getIntelGPUMemory() []GPUMemoryInfo { |
|
|
|
|
|
gpus := getIntelXPUSMI() |
|
|
if len(gpus) > 0 { |
|
|
return gpus |
|
|
} |
|
|
|
|
|
|
|
|
return getIntelGPUTop() |
|
|
} |
|
|
|
|
|
|
|
|
func getIntelXPUSMI() []GPUMemoryInfo { |
|
|
if _, err := exec.LookPath("xpu-smi"); err != nil { |
|
|
return nil |
|
|
} |
|
|
|
|
|
|
|
|
cmd := exec.Command("xpu-smi", "discovery", "--json") |
|
|
|
|
|
var stdout, stderr bytes.Buffer |
|
|
cmd.Stdout = &stdout |
|
|
cmd.Stderr = &stderr |
|
|
|
|
|
if err := cmd.Run(); err != nil { |
|
|
xlog.Debug("xpu-smi discovery failed", "error", err, "stderr", stderr.String()) |
|
|
return nil |
|
|
} |
|
|
|
|
|
|
|
|
var result struct { |
|
|
DeviceList []struct { |
|
|
DeviceID int `json:"device_id"` |
|
|
DeviceName string `json:"device_name"` |
|
|
VendorName string `json:"vendor_name"` |
|
|
MemoryPhysicalSizeBytes uint64 `json:"memory_physical_size_byte"` |
|
|
} `json:"device_list"` |
|
|
} |
|
|
|
|
|
if err := json.Unmarshal(stdout.Bytes(), &result); err != nil { |
|
|
xlog.Debug("failed to parse xpu-smi discovery output", "error", err) |
|
|
return nil |
|
|
} |
|
|
|
|
|
var gpus []GPUMemoryInfo |
|
|
|
|
|
for _, device := range result.DeviceList { |
|
|
|
|
|
statsCmd := exec.Command("xpu-smi", "stats", "-d", strconv.Itoa(device.DeviceID), "--json") |
|
|
|
|
|
var statsStdout bytes.Buffer |
|
|
statsCmd.Stdout = &statsStdout |
|
|
|
|
|
usedBytes := uint64(0) |
|
|
if err := statsCmd.Run(); err == nil { |
|
|
var stats struct { |
|
|
DeviceID int `json:"device_id"` |
|
|
MemoryUsed uint64 `json:"memory_used"` |
|
|
} |
|
|
if err := json.Unmarshal(statsStdout.Bytes(), &stats); err == nil { |
|
|
usedBytes = stats.MemoryUsed |
|
|
} |
|
|
} |
|
|
|
|
|
totalBytes := device.MemoryPhysicalSizeBytes |
|
|
freeBytes := uint64(0) |
|
|
if totalBytes > usedBytes { |
|
|
freeBytes = totalBytes - usedBytes |
|
|
} |
|
|
|
|
|
usagePercent := 0.0 |
|
|
if totalBytes > 0 { |
|
|
usagePercent = float64(usedBytes) / float64(totalBytes) * 100 |
|
|
} |
|
|
|
|
|
gpus = append(gpus, GPUMemoryInfo{ |
|
|
Index: device.DeviceID, |
|
|
Name: device.DeviceName, |
|
|
Vendor: VendorIntel, |
|
|
TotalVRAM: totalBytes, |
|
|
UsedVRAM: usedBytes, |
|
|
FreeVRAM: freeBytes, |
|
|
UsagePercent: usagePercent, |
|
|
}) |
|
|
} |
|
|
|
|
|
return gpus |
|
|
} |
|
|
|
|
|
|
|
|
func getIntelGPUTop() []GPUMemoryInfo { |
|
|
if _, err := exec.LookPath("intel_gpu_top"); err != nil { |
|
|
return nil |
|
|
} |
|
|
|
|
|
|
|
|
cmd := exec.Command("intel_gpu_top", "-J", "-s", "1") |
|
|
|
|
|
var stdout, stderr bytes.Buffer |
|
|
cmd.Stdout = &stdout |
|
|
cmd.Stderr = &stderr |
|
|
|
|
|
if err := cmd.Run(); err != nil { |
|
|
xlog.Debug("intel_gpu_top failed", "error", err, "stderr", stderr.String()) |
|
|
return nil |
|
|
} |
|
|
|
|
|
|
|
|
lines := strings.Split(strings.TrimSpace(stdout.String()), "\n") |
|
|
if len(lines) == 0 { |
|
|
return nil |
|
|
} |
|
|
|
|
|
|
|
|
var lastJSON string |
|
|
for i := len(lines) - 1; i >= 0; i-- { |
|
|
if strings.HasPrefix(strings.TrimSpace(lines[i]), "{") { |
|
|
lastJSON = lines[i] |
|
|
break |
|
|
} |
|
|
} |
|
|
|
|
|
if lastJSON == "" { |
|
|
return nil |
|
|
} |
|
|
|
|
|
var result struct { |
|
|
Engines map[string]interface{} `json:"engines"` |
|
|
|
|
|
} |
|
|
|
|
|
if err := json.Unmarshal([]byte(lastJSON), &result); err != nil { |
|
|
xlog.Debug("failed to parse intel_gpu_top output", "error", err) |
|
|
return nil |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return nil |
|
|
} |
|
|
|
|
|
|
|
|
func GetResourceInfo() ResourceInfo { |
|
|
gpus := GetGPUMemoryUsage() |
|
|
|
|
|
if len(gpus) > 0 { |
|
|
|
|
|
aggregate := GetGPUAggregateInfo() |
|
|
return ResourceInfo{ |
|
|
Type: "gpu", |
|
|
Available: true, |
|
|
GPUs: gpus, |
|
|
RAM: nil, |
|
|
Aggregate: AggregateMemoryInfo{ |
|
|
TotalMemory: aggregate.TotalVRAM, |
|
|
UsedMemory: aggregate.UsedVRAM, |
|
|
FreeMemory: aggregate.FreeVRAM, |
|
|
UsagePercent: aggregate.UsagePercent, |
|
|
GPUCount: aggregate.GPUCount, |
|
|
}, |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
ramInfo, err := GetSystemRAMInfo() |
|
|
if err != nil { |
|
|
xlog.Debug("failed to get system RAM info", "error", err) |
|
|
return ResourceInfo{ |
|
|
Type: "ram", |
|
|
Available: false, |
|
|
Aggregate: AggregateMemoryInfo{}, |
|
|
} |
|
|
} |
|
|
|
|
|
return ResourceInfo{ |
|
|
Type: "ram", |
|
|
Available: true, |
|
|
GPUs: nil, |
|
|
RAM: ramInfo, |
|
|
Aggregate: AggregateMemoryInfo{ |
|
|
TotalMemory: ramInfo.Total, |
|
|
UsedMemory: ramInfo.Used, |
|
|
FreeMemory: ramInfo.Free, |
|
|
UsagePercent: ramInfo.UsagePercent, |
|
|
GPUCount: 0, |
|
|
}, |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func GetResourceAggregateInfo() AggregateMemoryInfo { |
|
|
resourceInfo := GetResourceInfo() |
|
|
return resourceInfo.Aggregate |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func getVulkanGPUMemory() []GPUMemoryInfo { |
|
|
if _, err := exec.LookPath("vulkaninfo"); err != nil { |
|
|
return nil |
|
|
} |
|
|
|
|
|
cmd := exec.Command("vulkaninfo", "--json") |
|
|
|
|
|
var stdout, stderr bytes.Buffer |
|
|
cmd.Stdout = &stdout |
|
|
cmd.Stderr = &stderr |
|
|
|
|
|
if err := cmd.Run(); err != nil { |
|
|
xlog.Debug("vulkaninfo failed", "error", err, "stderr", stderr.String()) |
|
|
return nil |
|
|
} |
|
|
|
|
|
|
|
|
var result struct { |
|
|
VkPhysicalDevices []struct { |
|
|
DeviceName string `json:"deviceName"` |
|
|
DeviceType string `json:"deviceType"` |
|
|
VkPhysicalDeviceMemoryProperties struct { |
|
|
MemoryHeaps []struct { |
|
|
Flags int `json:"flags"` |
|
|
Size uint64 `json:"size"` |
|
|
} `json:"memoryHeaps"` |
|
|
} `json:"VkPhysicalDeviceMemoryProperties"` |
|
|
} `json:"VkPhysicalDevices"` |
|
|
} |
|
|
|
|
|
if err := json.Unmarshal(stdout.Bytes(), &result); err != nil { |
|
|
xlog.Debug("failed to parse vulkaninfo output", "error", err) |
|
|
return nil |
|
|
} |
|
|
|
|
|
var gpus []GPUMemoryInfo |
|
|
|
|
|
for i, device := range result.VkPhysicalDevices { |
|
|
|
|
|
if device.DeviceType == "VK_PHYSICAL_DEVICE_TYPE_CPU" { |
|
|
continue |
|
|
} |
|
|
|
|
|
|
|
|
var totalVRAM uint64 |
|
|
for _, heap := range device.VkPhysicalDeviceMemoryProperties.MemoryHeaps { |
|
|
|
|
|
if heap.Flags&1 != 0 { |
|
|
totalVRAM += heap.Size |
|
|
} |
|
|
} |
|
|
|
|
|
if totalVRAM == 0 { |
|
|
continue |
|
|
} |
|
|
|
|
|
gpus = append(gpus, GPUMemoryInfo{ |
|
|
Index: i, |
|
|
Name: device.DeviceName, |
|
|
Vendor: VendorVulkan, |
|
|
TotalVRAM: totalVRAM, |
|
|
UsedVRAM: 0, |
|
|
FreeVRAM: totalVRAM, |
|
|
UsagePercent: 0, |
|
|
}) |
|
|
} |
|
|
|
|
|
return gpus |
|
|
} |
|
|
|