feat: 优化 GPU 识别,支持ARM开发板和手机

This commit is contained in:
Akizon77
2025-11-30 22:07:12 +08:00
parent 7071fa746b
commit a9d191d6c3

View File

@@ -4,52 +4,227 @@
package monitoring package monitoring
import ( import (
"bytes"
"os"
"os/exec" "os/exec"
"path/filepath"
"regexp"
"strings" "strings"
) )
func GpuName() string { func GpuName() string {
// 调整优先级:专用显卡厂商优先,避免只识别集成显卡 if name := getFromLspci(); name != "None" {
accept := []string{"nvidia", "amd", "radeon", "vga", "3d"} return name
}
if name := getFromSysfsDRM(); name != "None" {
return name
}
return "None"
}
func getFromLspci() string {
out, err := exec.Command("lspci").Output() out, err := exec.Command("lspci").Output()
if err == nil { if err != nil {
lines := strings.Split(string(out), "\n") return "None"
}
// 首先尝试找专用显卡 excludePatterns := []string{
for _, line := range lines { "^1111", // 1111 (rev 02)
lower := strings.ToLower(line) `(?i)^cirrus logic (cl[-\s]?)?gd 5`, // CL-GD 系列 1990 年代中期的产物, 现常用于虚拟机
"(?i)virtio",
// 跳过集成显卡和管理控制器 "(?i)vmware",
if strings.Contains(lower, "aspeed") || `(?i)qxl`, // SPICE 虚拟显卡
strings.Contains(lower, "matrox") || `(?i)hyper-v`,
strings.Contains(lower, "management") { }
continue
} lines := strings.Split(string(out), "\n")
// 优先匹配专用显卡厂商 priorityVendors := []string{"nvidia", "amd", "radeon", "intel", "arc", "snap", "qualcomm", "snapdragon"}
for _, a := range accept {
if strings.Contains(lower, a) { isExcluded := func(name string) bool {
parts := strings.SplitN(line, ":", 4) for _, pattern := range excludePatterns {
if len(parts) >= 4 { if matched, _ := regexp.MatchString(pattern, name); matched {
return strings.TrimSpace(parts[3]) return true
} else if len(parts) == 3 {
return strings.TrimSpace(parts[2])
} else if len(parts) == 2 {
return strings.TrimSpace(parts[1])
}
}
} }
} }
return false
// 如果没有找到专用显卡返回第一个VGA设备作为兜底 }
for _, line := range lines {
if strings.Contains(strings.ToLower(line), "vga") { extractName := func(line string) string {
parts := strings.SplitN(line, ":", 4) // 取最后一个冒号之后的内容
if len(parts) >= 3 { idx := strings.LastIndex(line, ":")
return strings.TrimSpace(parts[2]) if idx == -1 || idx == len(line)-1 {
return ""
}
name := strings.TrimSpace(line[idx+1:])
// 去除末尾的 (rev xx)
if parenIdx := strings.LastIndex(name, "("); parenIdx != -1 {
name = strings.TrimSpace(name[:parenIdx])
}
return name
}
// 寻找 priorityVendors
for _, line := range lines {
lower := strings.ToLower(line)
// 必须确认是显示设备,防止匹配到 Intel 网卡或 Qualcomm 蓝牙
if !strings.Contains(lower, "vga") && !strings.Contains(lower, "3d") && !strings.Contains(lower, "display") {
continue
}
for _, vendor := range priorityVendors {
if strings.Contains(lower, vendor) {
name := extractName(line)
if name != "" && !isExcluded(name) {
// 找到独显立刻返回
return name
} }
} }
} }
} }
// 任意非黑名单的 VGA 设备
for _, line := range lines {
lower := strings.ToLower(line)
if strings.Contains(lower, "vga") || strings.Contains(lower, "3d") || strings.Contains(lower, "display") {
name := extractName(line)
if name != "" && !isExcluded(name) {
return name
}
}
}
return "None"
}
func getFromSysfsDRM() string {
matches, _ := filepath.Glob("/sys/class/drm/card*")
for _, path := range matches {
// 驱动名称
driverLink, err := os.Readlink(filepath.Join(path, "device", "driver"))
if err != nil {
continue
}
driverName := filepath.Base(driverLink)
// 设备树 compatible 提取具体型号
// /sys/class/drm/card0/device/of_node/compatible
// "qcom,adreno-750.1\0qcom,adreno"
exactModel := ""
compatibleBytes, err := os.ReadFile(filepath.Join(path, "device", "of_node", "compatible"))
if err == nil {
exactModel = parseSocModel(driverName, compatibleBytes)
}
// 有具体型号则直接返回
if exactModel != "" {
return exactModel
}
// 通用的驱动名称映射
switch driverName {
case "vc4":
return "Broadcom VideoCore IV/VI (Raspberry Pi)"
case "v3d":
return "Broadcom V3D (Raspberry Pi 4/5)"
case "msm", "msm_drm":
return "Qualcomm Adreno (Unknown Model)"
case "panfrost":
return "ARM Mali (Panfrost)"
case "lima":
return "ARM Mali (Lima)"
case "sun4i-drm", "sunxi-drm":
return "Allwinner Display Engine"
case "tegra":
return "NVIDIA Tegra"
}
if driverName != "" {
return "SoC GPU (" + driverName + ")"
}
}
// 开发板 Model
modelData, err := os.ReadFile("/sys/firmware/devicetree/base/model")
if err == nil {
model := string(modelData)
if strings.Contains(model, "Raspberry Pi") {
return "Broadcom VideoCore (Integrated)"
}
}
return "None" return "None"
} }
// parseSocModel 解析设备树 compatible 字符串,提取人性化名称
func parseSocModel(driver string, rawBytes []byte) string {
// compatible 文件包含多个以 \0 分隔的字符串
content := string(bytes.ReplaceAll(rawBytes, []byte{0}, []byte(" ")))
lower := strings.ToLower(content)
// 高通 Adreno (Qualcomm)
if driver == "msm" || strings.Contains(lower, "adreno") {
// "adreno-750", "adreno-660"
re := regexp.MustCompile(`adreno[-_](\d+)`)
matches := re.FindStringSubmatch(lower)
if len(matches) > 1 {
return "Qualcomm Adreno " + matches[1]
}
return "Qualcomm Adreno"
}
// ARM Mali (Rockchip/MediaTek/AmLogic)
if driver == "panfrost" || driver == "lima" || strings.Contains(lower, "mali") {
// "mali-g610", "mali-t860"
re := regexp.MustCompile(`mali[-_]([a-z]\d+)`)
matches := re.FindStringSubmatch(lower)
if len(matches) > 1 {
return "ARM Mali " + strings.ToUpper(matches[1]) // Mali G610
}
return "ARM Mali" // 泛指
}
// 树莓派 VideoCore
if driver == "vc4" || driver == "v3d" {
if strings.Contains(lower, "bcm2712") {
return "Broadcom VideoCore VII (Pi 5)"
}
if strings.Contains(lower, "bcm2711") {
return "Broadcom VideoCore VI (Pi 4)"
}
if strings.Contains(lower, "bcm2837") || strings.Contains(lower, "bcm2835") {
return "Broadcom VideoCore IV"
}
}
// Allwinner (全志)
// "allwinner,sun50i-h6-display-engine"
if strings.Contains(lower, "allwinner") || strings.Contains(lower, "sun50i") || strings.Contains(lower, "sun8i") {
re := regexp.MustCompile(`sun\d+i-([a-z0-9]+)`)
matches := re.FindStringSubmatch(lower)
if len(matches) > 1 {
model := strings.ToUpper(matches[1])
return "Allwinner " + model
}
return "Allwinner Display Engine"
}
// NVIDIA Tegra
if driver == "tegra" {
if strings.Contains(lower, "tegra194") {
return "NVIDIA Tegra Xavier"
}
if strings.Contains(lower, "tegra234") {
return "NVIDIA Orin"
}
if strings.Contains(lower, "tegra210") {
return "NVIDIA Tegra X1"
}
}
return ""
}