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>
141 lines
3.4 KiB
Go
141 lines
3.4 KiB
Go
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
|
|
}
|
|
}
|