mirror of
https://github.com/fankes/komari-agent.git
synced 2026-02-04 12:17:30 +08:00
feat: 优化 GPU 识别,支持ARM开发板和手机
This commit is contained in:
@@ -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 ""
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user