v1.3.0: Add multiple vendor parsers and enhanced hardware detection
New parsers: - NVIDIA Field Diagnostics parser with dmidecode output support - NVIDIA Bug Report parser with comprehensive hardware extraction - Supermicro crashdump (CDump.txt) parser - Generic fallback parser for unrecognized text files Enhanced GPU parsing (nvidia-bug-report): - Model and manufacturer detection (NVIDIA H100 80GB HBM3) - UUID, Video BIOS version, IRQ information - Bus location (BDF), DMA size/mask, device minor - PCIe bus type details New hardware detection (nvidia-bug-report): - System Information: server S/N, UUID, manufacturer, product name - CPU: model, S/N, cores, threads, frequencies from dmidecode - Memory: P/N, S/N, manufacturer, speed for all DIMMs - Power Supplies: manufacturer, model, S/N, wattage, status - Network Adapters: Ethernet/InfiniBand controllers with VPD data - Model, P/N, S/N from lspci Vital Product Data - Port count/type detection (QSFP56, OSFP, etc.) - Support for ConnectX-6/7 adapters Archive handling improvements: - Plain .gz file support (not just tar.gz) - Increased size limit for plain gzip files (50MB) - Better error handling for mixed archive formats Web interface enhancements: - Display parser name and filename badges - Improved file info section with visual indicators Co-Authored-By: Claude (qwen3-coder:480b) <noreply@anthropic.com>
This commit is contained in:
140
internal/parser/vendors/nvidia_bug_report/cpu.go
vendored
Normal file
140
internal/parser/vendors/nvidia_bug_report/cpu.go
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
package nvidia_bug_report
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.mchus.pro/mchus/logpile/internal/models"
|
||||
)
|
||||
|
||||
// parseCPUInfo extracts CPU information from dmidecode output
|
||||
func parseCPUInfo(content string, result *models.AnalysisResult) {
|
||||
scanner := bufio.NewScanner(strings.NewReader(content))
|
||||
|
||||
var currentCPU *models.CPU
|
||||
inProcessorInfo := false
|
||||
cpuSocket := 0
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
trimmed := strings.TrimSpace(line)
|
||||
|
||||
// Start of Processor Information section
|
||||
if strings.Contains(trimmed, "Processor Information") {
|
||||
inProcessorInfo = true
|
||||
currentCPU = &models.CPU{
|
||||
Socket: cpuSocket,
|
||||
}
|
||||
cpuSocket++
|
||||
continue
|
||||
}
|
||||
|
||||
// End of current section (empty line or new section with Handle)
|
||||
if inProcessorInfo && (trimmed == "" || strings.HasPrefix(trimmed, "Handle ")) {
|
||||
// Save CPU if it has valid data
|
||||
if currentCPU != nil && currentCPU.Model != "" {
|
||||
result.Hardware.CPUs = append(result.Hardware.CPUs, *currentCPU)
|
||||
}
|
||||
inProcessorInfo = false
|
||||
currentCPU = nil
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse fields within Processor Information section
|
||||
if inProcessorInfo && currentCPU != nil && strings.Contains(line, ":") {
|
||||
parts := strings.SplitN(trimmed, ":", 2)
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
field := strings.TrimSpace(parts[0])
|
||||
value := strings.TrimSpace(parts[1])
|
||||
|
||||
if value == "" || value == "Not Specified" || value == "Unknown" || value == "UNKNOWN" || value == "<OUT OF SPEC>" {
|
||||
continue
|
||||
}
|
||||
|
||||
switch field {
|
||||
case "Version":
|
||||
// CPU model name
|
||||
currentCPU.Model = value
|
||||
case "Serial Number":
|
||||
currentCPU.SerialNumber = value
|
||||
case "Part Number":
|
||||
// Store part number if available
|
||||
// Could be stored in a custom field if needed
|
||||
case "Core Count":
|
||||
if cores, err := strconv.Atoi(value); err == nil {
|
||||
currentCPU.Cores = cores
|
||||
}
|
||||
case "Core Enabled":
|
||||
// Could store this if needed
|
||||
case "Thread Count":
|
||||
if threads, err := strconv.Atoi(value); err == nil {
|
||||
currentCPU.Threads = threads
|
||||
}
|
||||
case "Max Speed":
|
||||
// Parse speed like "3800 MHz"
|
||||
if speed := parseCPUSpeed(value); speed > 0 {
|
||||
currentCPU.MaxFreqMHz = speed
|
||||
}
|
||||
case "Current Speed":
|
||||
// Parse current speed like "2000 MHz"
|
||||
if speed := parseCPUSpeed(value); speed > 0 {
|
||||
currentCPU.FrequencyMHz = speed
|
||||
}
|
||||
case "Voltage":
|
||||
// Could parse voltage if needed (e.g., "1.6 V")
|
||||
case "Status":
|
||||
// Status like "Populated, Enabled"
|
||||
// Check if CPU is enabled
|
||||
if !strings.Contains(value, "Populated") {
|
||||
// Skip unpopulated CPUs
|
||||
currentCPU = nil
|
||||
inProcessorInfo = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save last CPU if exists
|
||||
if currentCPU != nil && currentCPU.Model != "" {
|
||||
result.Hardware.CPUs = append(result.Hardware.CPUs, *currentCPU)
|
||||
}
|
||||
}
|
||||
|
||||
// parseCPUSpeed parses CPU speed strings like "3800 MHz" or "2.0 GHz"
|
||||
func parseCPUSpeed(speedStr string) int {
|
||||
parts := strings.Fields(speedStr)
|
||||
if len(parts) < 2 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Try to parse the number (may be int or float)
|
||||
speedStr = parts[0]
|
||||
var speed float64
|
||||
var err error
|
||||
|
||||
if strings.Contains(speedStr, ".") {
|
||||
speed, err = strconv.ParseFloat(speedStr, 64)
|
||||
} else {
|
||||
var speedInt int
|
||||
speedInt, err = strconv.Atoi(speedStr)
|
||||
speed = float64(speedInt)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
unit := strings.ToUpper(parts[1])
|
||||
switch unit {
|
||||
case "MHZ":
|
||||
return int(speed)
|
||||
case "GHZ":
|
||||
return int(speed * 1000)
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user