Files
logpile/internal/parser/vendors/nvidia_bug_report/psu.go
T
Mikhail ChusavitinandClaude Sonnet 5 a430a8c46f feat(nvidia_bug_report): extract Xid/SXid GPU error events
Parse Xid and SXid entries from the kernel log into GPU error events with
severity and the decoded failure reason.

See ADL-055.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 17:05:52 +03:00

119 lines
3.1 KiB
Go

package nvidia_bug_report
import (
"bufio"
"strconv"
"strings"
"git.mchus.pro/mchus/logpile/internal/models"
)
// parsePSUInfo extracts Power Supply information from dmidecode output
func parsePSUInfo(content string, result *models.AnalysisResult) {
scanner := bufio.NewScanner(strings.NewReader(content))
var currentPSU *models.PSU
inPowerSupply := false
for scanner.Scan() {
line := scanner.Text()
trimmed := strings.TrimSpace(line)
// Start of System Power Supply section
if strings.Contains(trimmed, "System Power Supply") {
inPowerSupply = true
currentPSU = &models.PSU{}
continue
}
// End of current section (empty line or new section with Handle)
if inPowerSupply && (trimmed == "" || strings.HasPrefix(trimmed, "Handle ")) {
// Save PSU if it has valid data
if currentPSU != nil && currentPSU.Slot != "" {
// Only add if PSU is present
if strings.Contains(strings.ToLower(currentPSU.Status), "present") {
currentPSU.Present = true
result.Hardware.PowerSupply = append(result.Hardware.PowerSupply, *currentPSU)
}
}
inPowerSupply = false
currentPSU = nil
continue
}
// Parse fields within System Power Supply section
if inPowerSupply && currentPSU != 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" {
continue
}
switch field {
case "Location":
currentPSU.Slot = value
case "Name":
// Use Name as Model if Model is not set later
if currentPSU.Model == "" {
currentPSU.Model = value
}
case "Manufacturer":
currentPSU.Vendor = value
case "Serial Number":
currentPSU.SerialNumber = value
case "Model Part Number":
// Use Model Part Number as the primary model identifier
currentPSU.Model = value
case "Revision":
currentPSU.Firmware = value
case "Max Power Capacity":
// Parse wattage like "2700 W"
if wattage := parsePowerWattage(value); wattage > 0 {
currentPSU.WattageW = wattage
}
case "Status":
currentPSU.Status = value
case "Type":
// Could store PSU type if needed (e.g., "Switching")
case "Plugged":
// Could track if PSU is plugged
case "Hot Replaceable":
// Could track if hot-swappable
}
}
}
// Save last PSU if exists
if currentPSU != nil && currentPSU.Slot != "" {
if strings.Contains(strings.ToLower(currentPSU.Status), "present") {
currentPSU.Present = true
result.Hardware.PowerSupply = append(result.Hardware.PowerSupply, *currentPSU)
}
}
}
// parsePowerWattage parses power capacity strings like "2700 W" or "1200 Watts"
func parsePowerWattage(powerStr string) int {
parts := strings.Fields(powerStr)
if len(parts) < 1 {
return 0
}
// Try to parse the number
wattageStr := parts[0]
wattage, err := strconv.Atoi(wattageStr)
if err != nil {
return 0
}
// Check if unit is specified (W, Watts, etc.) and convert if needed
// For now, assume it's always in Watts
return wattage
}