From 6c4d375cd30489c3a14ae4ddd1970bacd457b293 Mon Sep 17 00:00:00 2001 From: SuperQ Date: Sun, 8 Mar 2026 11:39:08 +0100 Subject: [PATCH] Refactor class thermal error handling Include any read errors when parsing /sys/class/thermal/thermal_zone data. This avoids premature error returns if there are platform specific problems like `temp` being unreadable, even tho it's a required file. Related to: https://github.com/prometheus/node_exporter/issues/2980 Signed-off-by: SuperQ --- sysfs/class_thermal.go | 44 +++++++++++++++++-------------------- sysfs/class_thermal_test.go | 26 ++++++++++++---------- 2 files changed, 34 insertions(+), 36 deletions(-) diff --git a/sysfs/class_thermal.go b/sysfs/class_thermal.go index db832867..8372f137 100644 --- a/sysfs/class_thermal.go +++ b/sysfs/class_thermal.go @@ -17,12 +17,10 @@ package sysfs import ( "errors" + "fmt" "os" "path/filepath" "strings" - "syscall" - - fsp "io/fs" "github.com/prometheus/procfs/internal/util" ) @@ -37,6 +35,9 @@ type ClassThermalZoneStats struct { Policy string // One of the various thermal governors used for a particular zone. Mode *bool // Optional: One of the predefined values in [enabled, disabled]. Passive *uint64 // Optional: millidegrees Celsius. (0 for disabled, > 1000 for enabled+value) + + // ReadError contains any errors returned when gathering data. + ReadErrors error } // ClassThermalZoneStats returns Thermal Zone metrics for all zones. @@ -48,39 +49,33 @@ func (fs FS) ClassThermalZoneStats() ([]ClassThermalZoneStats, error) { stats := make([]ClassThermalZoneStats, 0, len(zones)) for _, zone := range zones { - zoneStats, err := parseClassThermalZone(zone) - if err != nil { - if errors.Is(err, syscall.ENODATA) || errors.As(err, new(*fsp.PathError)) || errors.Is(err, syscall.EAGAIN) || - errors.Is(err, syscall.EINVAL) { - continue - } - return nil, err - } + zoneStats := parseClassThermalZone(zone) zoneStats.Name = strings.TrimPrefix(filepath.Base(zone), "thermal_zone") stats = append(stats, zoneStats) } return stats, nil } -func parseClassThermalZone(zone string) (ClassThermalZoneStats, error) { +func parseClassThermalZone(zone string) ClassThermalZoneStats { + var errs []error // Required attributes. zoneType, err := util.SysReadFile(filepath.Join(zone, "type")) if err != nil { - return ClassThermalZoneStats{}, err + errs = append(errs, fmt.Errorf("error reading type: %w", err)) } zonePolicy, err := util.SysReadFile(filepath.Join(zone, "policy")) if err != nil { - return ClassThermalZoneStats{}, err + errs = append(errs, fmt.Errorf("error reading policy: %w", err)) } zoneTemp, err := util.SysReadIntFromFile(filepath.Join(zone, "temp")) - if err != nil { - return ClassThermalZoneStats{}, err + if err != nil && !errors.Is(err, os.ErrInvalid) { + errs = append(errs, fmt.Errorf("error reading temp: %w", err)) } // Optional attributes. mode, err := util.SysReadFile(filepath.Join(zone, "mode")) if err != nil && !os.IsNotExist(err) && !os.IsPermission(err) { - return ClassThermalZoneStats{}, err + errs = append(errs, fmt.Errorf("error reading mode: %w", err)) } zoneMode := util.ParseBool(mode) @@ -90,16 +85,17 @@ func parseClassThermalZone(zone string) (ClassThermalZoneStats, error) { case os.IsNotExist(err), os.IsPermission(err): zonePassive = nil case err != nil: - return ClassThermalZoneStats{}, err + errs = append(errs, fmt.Errorf("error reading passive: %w", err)) default: zonePassive = &passive } return ClassThermalZoneStats{ - Type: zoneType, - Policy: zonePolicy, - Temp: zoneTemp, - Mode: zoneMode, - Passive: zonePassive, - }, nil + Type: zoneType, + Policy: zonePolicy, + Temp: zoneTemp, + Mode: zoneMode, + Passive: zonePassive, + ReadErrors: errors.Join(errs...), + } } diff --git a/sysfs/class_thermal_test.go b/sysfs/class_thermal_test.go index 7c2a45c8..0e5feeb0 100644 --- a/sysfs/class_thermal_test.go +++ b/sysfs/class_thermal_test.go @@ -39,20 +39,22 @@ func TestClassThermalZoneStats(t *testing.T) { classThermalZoneStats := []ClassThermalZoneStats{ { - Name: "0", - Type: "bcm2835_thermal", - Policy: "step_wise", - Temp: 49925, - Mode: nil, - Passive: nil, + Name: "0", + Type: "bcm2835_thermal", + Policy: "step_wise", + Temp: 49925, + Mode: nil, + Passive: nil, + ReadErrors: nil, }, { - Name: "1", - Type: "acpitz", - Policy: "step_wise", - Temp: -44000, - Mode: enabled, - Passive: &passive, + Name: "1", + Type: "acpitz", + Policy: "step_wise", + Temp: -44000, + Mode: enabled, + Passive: &passive, + ReadErrors: nil, }, }