From 4a94bddd928a3a528cb98e888f7b32410175df5d Mon Sep 17 00:00:00 2001 From: alu Date: Tue, 4 Aug 2026 15:27:33 -0700 Subject: [PATCH] add cpu frequency driver --- app/cpu/cpu.go | 14 ++- app/cpu/drivers/drivers.go | 31 ++++++ app/cpu/drivers/hwmon.go | 76 +++++++++++++ app/cpu/drivers/rapl.go | 143 +++++++++++++++++++++++++ app/cpu/drivers/sys.go | 67 ++++++++++++ app/cpu/proc/proc.go | 22 +--- app/cpu/proc/vendor_amd.go | 202 +++++------------------------------ app/cpu/proc/vendor_intel.go | 196 ++++----------------------------- 8 files changed, 381 insertions(+), 370 deletions(-) create mode 100644 app/cpu/drivers/drivers.go create mode 100644 app/cpu/drivers/hwmon.go create mode 100644 app/cpu/drivers/rapl.go create mode 100644 app/cpu/drivers/sys.go diff --git a/app/cpu/cpu.go b/app/cpu/cpu.go index f0dd741..7e200e0 100644 --- a/app/cpu/cpu.go +++ b/app/cpu/cpu.go @@ -13,6 +13,7 @@ func RenderCPU(console *strings.Builder, proc *proc.Proc) { RenderTemperatureSensors(console, proc) RenderPowerSensors(console, proc) + RenderClockSensors(console, proc) } func RenderTemperatureSensors(console *strings.Builder, proc *proc.Proc) { @@ -22,7 +23,7 @@ func RenderTemperatureSensors(console *strings.Builder, proc *proc.Proc) { tempC := sensor.TempC tempF := (tempC * 9 / 5) + 32 // \033[K clears from cursor to end of line (prevents lingering chars) - fmt.Fprintf(console, "Sensor: %-25s | %5.1f°C (%5.1f°F)\033[K\n", sensor.Name, tempC, tempF) + fmt.Fprintf(console, "Sensor: %-25s | %5.1f °C (%5.1f °F)\033[K\n", sensor.Name, tempC, tempF) } } @@ -31,6 +32,15 @@ func RenderPowerSensors(console *strings.Builder, proc *proc.Proc) { power, _ := proc.SensorReader.ReadPowerSensors() for _, sensor := range power { // \033[K clears from cursor to end of line (prevents lingering chars) - fmt.Fprintf(console, "Sensor: %-25s | %5.1fW\033[K\n", sensor.Name, sensor.Watts) + fmt.Fprintf(console, "Sensor: %-25s | %5.1f W\033[K\n", sensor.Name, sensor.Watts) + } +} + +func RenderClockSensors(console *strings.Builder, proc *proc.Proc) { + fmt.Fprint(console, "--- Clock Frequency ---\n") + power, _ := proc.SensorReader.ReadClockSensors() + for _, sensor := range power { + // \033[K clears from cursor to end of line (prevents lingering chars) + fmt.Fprintf(console, "Sensor: %-25d | %5.1f GHz\033[K\n", sensor.Name, sensor.FrequencyGHz) } } diff --git a/app/cpu/drivers/drivers.go b/app/cpu/drivers/drivers.go new file mode 100644 index 0000000..3efc113 --- /dev/null +++ b/app/cpu/drivers/drivers.go @@ -0,0 +1,31 @@ +package drivers + +type TemperatureSensor struct { + path string + Name string + TempC float64 +} + +type TemperatureDriver interface { + ReadTemperatureSensors() ([]TemperatureSensor, error) +} + +type PowerSensor struct { + path string + Name string + Watts float64 +} + +type PowerDriver interface { + ReadPowerSensors() ([]PowerSensor, error) +} + +type ClockSensor struct { + path string + Name int + FrequencyGHz float64 +} + +type ClockDriver interface { + ReadClockSensors() ([]ClockSensor, error) +} diff --git a/app/cpu/drivers/hwmon.go b/app/cpu/drivers/hwmon.go new file mode 100644 index 0000000..56a3946 --- /dev/null +++ b/app/cpu/drivers/hwmon.go @@ -0,0 +1,76 @@ +package drivers + +import ( + "fmt" + "os" + "path/filepath" + "strconv" + "strings" +) + +type HwmonDriver struct { + hwmonDir string + hwmonDriver string +} + +func NewHwmonDriver(expectedDrivers []string) *HwmonDriver { + // Locate hwmon path + dirs, err := filepath.Glob("/sys/class/hwmon/hwmon*") + if err != nil { + return nil + } + + for _, dir := range dirs { + nameRaw, err := os.ReadFile(filepath.Join(dir, "name")) + name := strings.TrimSpace(string(nameRaw)) + if err == nil { + for _, driver := range expectedDrivers { + if driver == name { + return &HwmonDriver{ + hwmonDir: dir, + hwmonDriver: driver, + } + } + } + } + } + + return nil +} + +func (r *HwmonDriver) ReadTemperatureSensors() ([]TemperatureSensor, error) { + inputs, err := filepath.Glob(filepath.Join(r.hwmonDir, "temp*_input")) + if err != nil || len(inputs) == 0 { + return nil, fmt.Errorf("no temperature inputs found in %s", r.hwmonDir) + } + + temps := make([]TemperatureSensor, 0, len(inputs)) + + for _, inputPath := range inputs { + valBytes, err := os.ReadFile(inputPath) + if err != nil { + continue + } + + milliC, err := strconv.ParseFloat(strings.TrimSpace(string(valBytes)), 64) + if err != nil { + continue + } + + labelPath := strings.TrimSuffix(inputPath, "_input") + "_label" + label := "" + if lBytes, err := os.ReadFile(labelPath); err == nil { + label = strings.TrimSpace(string(lBytes)) + } else { + label = filepath.Base(strings.TrimSuffix(inputPath, "_input")) + } + + temps = append(temps, TemperatureSensor{ + path: inputPath, + Name: label, + TempC: milliC / 1000.0, + }) + } + + return temps, nil +} diff --git a/app/cpu/drivers/rapl.go b/app/cpu/drivers/rapl.go new file mode 100644 index 0000000..8845238 --- /dev/null +++ b/app/cpu/drivers/rapl.go @@ -0,0 +1,143 @@ +package drivers + +import ( + "fmt" + "math" + "os" + "path/filepath" + "strconv" + "strings" + "time" +) + +type EnergyState struct { + lastEnergy uint64 + lastTime time.Time +} + +type RaplDriver struct { + energyState map[string]EnergyState +} + +func NewRaplDriver() *RaplDriver { + return &RaplDriver{ + energyState: map[string]EnergyState{}, + } + +} + +func (r *RaplDriver) ReadPowerSensors() ([]PowerSensor, error) { + powerSensors := []PowerSensor{} + + patterns := []string{ + "/sys/class/powercap/intel-rapl*/energy_uj", + "/sys/class/powercap/intel-rapl/intel-rapl*/energy_uj", + "/sys/class/powercap/intel-rapl/intel-rapl*/*/energy_uj", + "/sys/class/powercap/intel-rapl:*/energy_uj", + "/sys/class/powercap/intel-rapl:*/*/energy_uj", + } + + energyInputs := []string{} + unique := make(map[string]bool) + + for _, pattern := range patterns { + matches, _ := filepath.Glob(pattern) + + for _, match := range matches { + realPath, err := filepath.EvalSymlinks(match) + if err != nil { + realPath = match + } + if !unique[realPath] { + unique[realPath] = true + energyInputs = append(energyInputs, realPath) + } + } + } + + if len(energyInputs) == 0 { + return powerSensors, fmt.Errorf("found no compatible rapl drivers") + } + + r.calculateEnergyDelta(energyInputs, &powerSensors) + return powerSensors, nil +} + +func getRaplLabel(inputPath string) string { + dir := filepath.Dir(inputPath) + if nameBytes, err := os.ReadFile(filepath.Join(dir, "name")); err == nil { + return strings.TrimSpace(string(nameBytes)) + } + return filepath.Base(dir) +} + +func (r *RaplDriver) calculateEnergyDelta(inputs []string, out *[]PowerSensor) { + now := time.Now() + + if r.energyState == nil { + r.energyState = make(map[string]EnergyState) + } + + for _, rawPath := range inputs { + key, err := filepath.EvalSymlinks(rawPath) + if err != nil { + key = filepath.Clean(rawPath) + } + + valBytes, err := os.ReadFile(key) + if err != nil { + continue + } + + microJoules, err := strconv.ParseUint(strings.TrimSpace(string(valBytes)), 10, 64) + if err != nil { + continue + } + + prevState, exists := r.energyState[key] + + if !exists { + r.energyState[key] = EnergyState{ + lastEnergy: microJoules, + lastTime: now, + } + *out = append(*out, PowerSensor{ + Name: getRaplLabel(key), + Watts: 0.0, + }) + continue + } + + timeDiff := now.Sub(prevState.lastTime).Seconds() + if timeDiff < 0.05 { + continue + } + + maxRange := uint64(math.MaxUint64) + rangeBytes, err := os.ReadFile(filepath.Join(filepath.Dir(key), "max_energy_range_uj")) + if err == nil { + if parsedMax, err := strconv.ParseUint(strings.TrimSpace(string(rangeBytes)), 10, 64); err == nil && parsedMax > 0 { + maxRange = parsedMax + } + } + + var energyDiff uint64 + if microJoules >= prevState.lastEnergy { + energyDiff = microJoules - prevState.lastEnergy + } else { + energyDiff = (maxRange - prevState.lastEnergy) + microJoules + } + + watts := (float64(energyDiff) / 1_000_000.0) / timeDiff + + r.energyState[key] = EnergyState{ + lastEnergy: microJoules, + lastTime: now, + } + + *out = append(*out, PowerSensor{ + Name: getRaplLabel(key), + Watts: watts, + }) + } +} diff --git a/app/cpu/drivers/sys.go b/app/cpu/drivers/sys.go new file mode 100644 index 0000000..4c1415a --- /dev/null +++ b/app/cpu/drivers/sys.go @@ -0,0 +1,67 @@ +package drivers + +import ( + "fmt" + "os" + "path/filepath" + "sort" + "strconv" + "strings" +) + +type SysDriver struct{} + +func NewSysDriver() *SysDriver { + return &SysDriver{} +} + +func (r *SysDriver) ReadClockSensors() ([]ClockSensor, error) { + // Glob directly targets scaling_cur_freq for all numerical CPU core paths + freqFiles, err := filepath.Glob("/sys/devices/system/cpu/cpu[0-9]*/cpufreq/scaling_cur_freq") + if err != nil { + return nil, fmt.Errorf("failed to glob cpufreq files: %w", err) + } + + clockSensors := []ClockSensor{} + + for _, path := range freqFiles { + // Example path: /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq + // Extract core ID from path segments + parts := strings.Split(path, "/") + if len(parts) < 6 { + continue + } + + // Parse "cpu0" -> 0 + cpuDir := parts[5] + coreIDStr := strings.TrimPrefix(cpuDir, "cpu") + coreID, err := strconv.Atoi(coreIDStr) + if err != nil { + continue + } + + // Read frequency in kHz directly from the matched file path + data, err := os.ReadFile(path) + if err != nil { + continue + } + + khz, err := strconv.ParseFloat(strings.TrimSpace(string(data)), 64) + if err != nil { + continue + } + + clockSensors = append(clockSensors, ClockSensor{ + path: path, + Name: coreID, + FrequencyGHz: khz / 1000000.0, // Convert KHz to GHz + }) + } + + // Guarantee numerical order by core ID + sort.Slice(clockSensors, func(i, j int) bool { + return clockSensors[i].Name < clockSensors[j].Name + }) + + return clockSensors, nil +} diff --git a/app/cpu/proc/proc.go b/app/cpu/proc/proc.go index b926eae..255765a 100644 --- a/app/cpu/proc/proc.go +++ b/app/cpu/proc/proc.go @@ -2,10 +2,10 @@ package proc import ( "bufio" + driver "finally-a-monolithic-linux-hw-monitor/app/cpu/drivers" "fmt" "os" "strings" - "time" ) type Proc struct { @@ -14,23 +14,9 @@ type Proc struct { } type CPUSensorReader interface { - ReadTemperatureSensors() ([]TempSensor, error) - ReadPowerSensors() ([]PowerSensor, error) -} - -type TempSensor struct { - Name string - TempC float64 -} - -type PowerSensor struct { - Name string - Watts float64 -} - -type EnergyState struct { - lastEnergy uint64 - lastTime time.Time + ReadTemperatureSensors() ([]driver.TemperatureSensor, error) + ReadPowerSensors() ([]driver.PowerSensor, error) + ReadClockSensors() ([]driver.ClockSensor, error) } const ( diff --git a/app/cpu/proc/vendor_amd.go b/app/cpu/proc/vendor_amd.go index a826b8a..351e8a3 100644 --- a/app/cpu/proc/vendor_amd.go +++ b/app/cpu/proc/vendor_amd.go @@ -1,197 +1,47 @@ package proc import ( + driver "finally-a-monolithic-linux-hw-monitor/app/cpu/drivers" "fmt" - "math" - "os" - "path/filepath" - "strconv" - "strings" - "time" ) type AMDReader struct { - hwmonDir string - driver string - energyState map[string]EnergyState + temperatureDriver driver.TemperatureDriver + powerDriver driver.PowerDriver + clockDriver driver.ClockDriver } func NewAMDReader() (*AMDReader, error) { - matches, err := filepath.Glob("/sys/class/hwmon/hwmon*") - if err != nil { - return nil, fmt.Errorf("glob hwmon failed: %w", err) + temperatureDriver := driver.NewHwmonDriver([]string{"k10temp", "zenpower"}) + if temperatureDriver == nil { + return nil, fmt.Errorf("could not initialize hwmon temperature driver") } - for _, dir := range matches { - b, err := os.ReadFile(filepath.Join(dir, "name")) - if err != nil { - continue - } - drv := strings.TrimSpace(string(b)) - if drv == "k10temp" || drv == "zenpower" { - return &AMDReader{ - hwmonDir: dir, - driver: drv, - energyState: make(map[string]EnergyState), - }, nil - } + powerDriver := driver.NewRaplDriver() + if powerDriver == nil { + return nil, fmt.Errorf("could not initialize hwmon temperature driver") } - return nil, fmt.Errorf("k10temp/zenpower driver not found under /sys/class/hwmon/") + clockDriver := driver.NewSysDriver() + if clockDriver == nil { + return nil, fmt.Errorf("could not initialize hwmon temperature driver") + } + + return &AMDReader{ + temperatureDriver: temperatureDriver, + powerDriver: powerDriver, + clockDriver: clockDriver, + }, nil } -func (r *AMDReader) ReadTemperatureSensors() ([]TempSensor, error) { - inputs, err := filepath.Glob(filepath.Join(r.hwmonDir, "temp*_input")) - if err != nil || len(inputs) == 0 { - return nil, fmt.Errorf("no temperature inputs found in %s", r.hwmonDir) - } - - temps := make([]TempSensor, 0, len(inputs)) - - for _, inputPath := range inputs { - valBytes, err := os.ReadFile(inputPath) - if err != nil { - continue - } - - milliC, err := strconv.ParseFloat(strings.TrimSpace(string(valBytes)), 64) - if err != nil { - continue - } - - labelPath := strings.TrimSuffix(inputPath, "_input") + "_label" - label := "" - if lBytes, err := os.ReadFile(labelPath); err == nil { - label = strings.TrimSpace(string(lBytes)) - } else { - label = filepath.Base(strings.TrimSuffix(inputPath, "_input")) - } - - temps = append(temps, TempSensor{ - Name: label, - TempC: milliC / 1000.0, - }) - } - - return temps, nil +func (r *AMDReader) ReadTemperatureSensors() ([]driver.TemperatureSensor, error) { + return r.temperatureDriver.ReadTemperatureSensors() } -func (r *AMDReader) ReadPowerSensors() ([]PowerSensor, error) { - return r.readRaplPower(), nil +func (r *AMDReader) ReadPowerSensors() ([]driver.PowerSensor, error) { + return r.powerDriver.ReadPowerSensors() } -func (r *AMDReader) readRaplPower() []PowerSensor { - powerSensors := []PowerSensor{} - - patterns := []string{ - "/sys/class/powercap/intel-rapl*/energy_uj", - "/sys/class/powercap/intel-rapl/intel-rapl*/energy_uj", - "/sys/class/powercap/intel-rapl/intel-rapl*/*/energy_uj", - "/sys/class/powercap/intel-rapl:*/energy_uj", - "/sys/class/powercap/intel-rapl:*/*/energy_uj", - } - - energyInputs := []string{} - seen := make(map[string]bool) - - for _, pattern := range patterns { - matches, _ := filepath.Glob(pattern) - - for _, match := range matches { - realPath, err := filepath.EvalSymlinks(match) - if err != nil { - realPath = match - } - if !seen[realPath] { - seen[realPath] = true - energyInputs = append(energyInputs, realPath) - } - } - } - - if len(energyInputs) == 0 { - return powerSensors - } - - getLabel := func(inputPath string) string { - dir := filepath.Dir(inputPath) - if nameBytes, err := os.ReadFile(filepath.Join(dir, "name")); err == nil { - return strings.TrimSpace(string(nameBytes)) - } - return filepath.Base(dir) - } - - r.calculateEnergyDelta(energyInputs, getLabel, &powerSensors) - return powerSensors -} - -func (r *AMDReader) calculateEnergyDelta(inputs []string, getLabel func(string) string, out *[]PowerSensor) { - now := time.Now() - - if r.energyState == nil { - r.energyState = make(map[string]EnergyState) - } - - for _, rawPath := range inputs { - key, err := filepath.EvalSymlinks(rawPath) - if err != nil { - key = filepath.Clean(rawPath) - } - - valBytes, err := os.ReadFile(key) - if err != nil { - continue - } - - microJoules, err := strconv.ParseUint(strings.TrimSpace(string(valBytes)), 10, 64) - if err != nil { - continue - } - - prevState, exists := r.energyState[key] - - if !exists { - r.energyState[key] = EnergyState{ - lastEnergy: microJoules, - lastTime: now, - } - *out = append(*out, PowerSensor{ - Name: getLabel(key), - Watts: 0.0, - }) - continue - } - - timeDiff := now.Sub(prevState.lastTime).Seconds() - if timeDiff < 0.05 { - continue - } - - maxRange := uint64(math.MaxUint64) - rangeBytes, err := os.ReadFile(filepath.Join(filepath.Dir(key), "max_energy_range_uj")) - if err == nil { - if parsedMax, err := strconv.ParseUint(strings.TrimSpace(string(rangeBytes)), 10, 64); err == nil && parsedMax > 0 { - maxRange = parsedMax - } - } - - var energyDiff uint64 - if microJoules >= prevState.lastEnergy { - energyDiff = microJoules - prevState.lastEnergy - } else { - energyDiff = (maxRange - prevState.lastEnergy) + microJoules - } - - watts := (float64(energyDiff) / 1_000_000.0) / timeDiff - - r.energyState[key] = EnergyState{ - lastEnergy: microJoules, - lastTime: now, - } - - *out = append(*out, PowerSensor{ - Name: getLabel(key), - Watts: watts, - }) - } +func (r *AMDReader) ReadClockSensors() ([]driver.ClockSensor, error) { + return r.clockDriver.ReadClockSensors() } diff --git a/app/cpu/proc/vendor_intel.go b/app/cpu/proc/vendor_intel.go index 9c14add..c1c9898 100644 --- a/app/cpu/proc/vendor_intel.go +++ b/app/cpu/proc/vendor_intel.go @@ -1,199 +1,47 @@ package proc import ( + driver "finally-a-monolithic-linux-hw-monitor/app/cpu/drivers" "fmt" - "math" - "os" - "path/filepath" - "strconv" - "strings" - "time" ) type IntelReader struct { - hwmonDir string - driver string - energyState map[string]EnergyState + temperatureDriver driver.TemperatureDriver + powerDriver driver.PowerDriver + clockDriver driver.ClockDriver } func NewIntelReader() (*IntelReader, error) { - // Locate Intel coretemp hwmon path - dirs, err := filepath.Glob("/sys/class/hwmon/hwmon*") - if err != nil { - return nil, err + temperatureDriver := driver.NewHwmonDriver([]string{"coretemp"}) + if temperatureDriver == nil { + return nil, fmt.Errorf("could not initialize hwmon temperature driver") } - hwmonDir := "" - for _, d := range dirs { - nameData, err := os.ReadFile(filepath.Join(d, "name")) - if err == nil && strings.TrimSpace(string(nameData)) == "coretemp" { - hwmonDir = d - break - } + powerDriver := driver.NewRaplDriver() + if powerDriver == nil { + return nil, fmt.Errorf("could not initialize hwmon temperature driver") } - if hwmonDir == "" { - return nil, fmt.Errorf("coretemp driver not found under /sys/class/hwmon/") + clockDriver := driver.NewSysDriver() + if clockDriver == nil { + return nil, fmt.Errorf("could not initialize hwmon temperature driver") } return &IntelReader{ - hwmonDir: hwmonDir, - driver: "coretemp", + temperatureDriver: temperatureDriver, + powerDriver: powerDriver, + clockDriver: clockDriver, }, nil } -func (r *IntelReader) ReadTemperatureSensors() ([]TempSensor, error) { - inputs, err := filepath.Glob(filepath.Join(r.hwmonDir, "temp*_input")) - if err != nil || len(inputs) == 0 { - return nil, fmt.Errorf("no temperature inputs found in %s", r.hwmonDir) - } - - temps := []TempSensor{} - - for _, inputPath := range inputs { - valBytes, err := os.ReadFile(inputPath) - if err != nil { - continue - } - - milliC, err := strconv.ParseFloat(strings.TrimSpace(string(valBytes)), 64) - if err != nil { - continue - } - - labelPath := strings.TrimSuffix(inputPath, "_input") + "_label" - label := "" - if lBytes, err := os.ReadFile(labelPath); err == nil { - label = strings.TrimSpace(string(lBytes)) - } else { - label = filepath.Base(strings.TrimSuffix(inputPath, "_input")) - } - - temps = append(temps, TempSensor{ - Name: label, - TempC: milliC / 1000.0, - }) - } - - return temps, nil +func (r *IntelReader) ReadTemperatureSensors() ([]driver.TemperatureSensor, error) { + return r.temperatureDriver.ReadTemperatureSensors() } -func (r *IntelReader) ReadPowerSensors() ([]PowerSensor, error) { - return r.readRaplPower(), nil +func (r *IntelReader) ReadPowerSensors() ([]driver.PowerSensor, error) { + return r.powerDriver.ReadPowerSensors() } -func (r *IntelReader) readRaplPower() []PowerSensor { - powerSensors := []PowerSensor{} - - patterns := []string{ - "/sys/class/powercap/intel-rapl*/energy_uj", - "/sys/class/powercap/intel-rapl/intel-rapl*/energy_uj", - "/sys/class/powercap/intel-rapl/intel-rapl*/*/energy_uj", - "/sys/class/powercap/intel-rapl:*/energy_uj", - "/sys/class/powercap/intel-rapl:*/*/energy_uj", - } - - energyInputs := []string{} - seen := make(map[string]bool) - - for _, pattern := range patterns { - matches, _ := filepath.Glob(pattern) - - for _, match := range matches { - realPath, err := filepath.EvalSymlinks(match) - if err != nil { - realPath = match - } - if !seen[realPath] { - seen[realPath] = true - energyInputs = append(energyInputs, realPath) - } - } - } - - if len(energyInputs) == 0 { - return powerSensors - } - - getLabel := func(inputPath string) string { - dir := filepath.Dir(inputPath) - if nameBytes, err := os.ReadFile(filepath.Join(dir, "name")); err == nil { - return strings.TrimSpace(string(nameBytes)) - } - return filepath.Base(dir) - } - - r.calculateEnergyDelta(energyInputs, getLabel, &powerSensors) - return powerSensors -} - -func (r *IntelReader) calculateEnergyDelta(inputs []string, getLabel func(string) string, out *[]PowerSensor) { - now := time.Now() - - if r.energyState == nil { - r.energyState = make(map[string]EnergyState) - } - - for _, rawPath := range inputs { - key, err := filepath.EvalSymlinks(rawPath) - if err != nil { - key = filepath.Clean(rawPath) - } - - valBytes, err := os.ReadFile(key) - if err != nil { - continue - } - - microJoules, err := strconv.ParseUint(strings.TrimSpace(string(valBytes)), 10, 64) - if err != nil { - continue - } - - prevState, exists := r.energyState[key] - - if !exists { - r.energyState[key] = EnergyState{ - lastEnergy: microJoules, - lastTime: now, - } - *out = append(*out, PowerSensor{ - Name: getLabel(key), - Watts: 0.0, - }) - continue - } - - timeDiff := now.Sub(prevState.lastTime).Seconds() - if timeDiff < 0.05 { - continue - } - - maxRange := uint64(math.MaxUint64) - rangeBytes, err := os.ReadFile(filepath.Join(filepath.Dir(key), "max_energy_range_uj")) - if err == nil { - if parsedMax, err := strconv.ParseUint(strings.TrimSpace(string(rangeBytes)), 10, 64); err == nil && parsedMax > 0 { - maxRange = parsedMax - } - } - - var energyDiff uint64 - if microJoules >= prevState.lastEnergy { - energyDiff = microJoules - prevState.lastEnergy - } else { - energyDiff = (maxRange - prevState.lastEnergy) + microJoules - } - - watts := (float64(energyDiff) / 1_000_000.0) / timeDiff - - r.energyState[key] = EnergyState{ - lastEnergy: microJoules, - lastTime: now, - } - - *out = append(*out, PowerSensor{ - Name: getLabel(key), - Watts: watts, - }) - } +func (r *IntelReader) ReadClockSensors() ([]driver.ClockSensor, error) { + return r.clockDriver.ReadClockSensors() }