Count the same meters the power module's sampler reads (hq ADR 0255)
An APU's integrated graphics and Intel's second (MMIO) view of a package made a machine report a power meter the sampler would not read, so its bar could only say not measured.
This commit is contained in:
@@ -24,7 +24,8 @@ const (
|
||||
CapBattery = "battery"
|
||||
// CapPowerMeter is something here that measures the power the machine, or part of it, draws:
|
||||
// the machine's battery, the processor package's energy counter (powercap, Intel's and AMD's
|
||||
// RAPL), or a graphics device that reports its own power.
|
||||
// RAPL), or a discrete graphics device that reports its own power — the same meters the power
|
||||
// module's sampler reads, so a machine never gets a block that can only say "not measured".
|
||||
CapPowerMeter = "power-meter"
|
||||
)
|
||||
|
||||
@@ -89,24 +90,33 @@ func (powerMeter) Name() string { return CapPowerMeter }
|
||||
// reader's question — powercap's energy counter is root's to read, and the power module samples it
|
||||
// as root and publishes only a rate.
|
||||
func (powerMeter) Detect(context.Context) Verdict {
|
||||
const how = "a system battery; /sys/class/powercap/*/name starting package; a graphics device's " +
|
||||
"hwmon power1_average or power1_input; /proc/driver/nvidia/gpus"
|
||||
const how = "a system battery; /sys/class/powercap/*/name starting package, each name once; a discrete " +
|
||||
"graphics device's (mem_info_vram_vendor) hwmon power1_average or power1_input; /proc/driver/nvidia/gpus"
|
||||
var found []string
|
||||
for _, b := range systemBatteries() {
|
||||
found = append(found, "battery "+b)
|
||||
}
|
||||
// Each package once by its name: Intel shows some twice, as intel-rapl:N and intel-rapl-mmio:N.
|
||||
if entries, err := os.ReadDir(PowercapSource); err == nil {
|
||||
seen := map[string]bool{}
|
||||
for _, e := range entries {
|
||||
dir := filepath.Join(PowercapSource, e.Name())
|
||||
if strings.HasPrefix(readTrimmed(filepath.Join(dir, "name")), "package") {
|
||||
if _, err := os.Stat(filepath.Join(dir, "energy_uj")); err == nil {
|
||||
found = append(found, "processor package "+e.Name())
|
||||
}
|
||||
name := readTrimmed(filepath.Join(dir, "name"))
|
||||
if !strings.HasPrefix(name, "package") || seen[name] {
|
||||
continue
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(dir, "energy_uj")); err == nil {
|
||||
seen[name] = true
|
||||
found = append(found, "processor "+name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if cards, err := filepath.Glob(filepath.Join(SeatSource, "card*", "device", "hwmon", "hwmon*")); err == nil {
|
||||
for _, hw := range cards {
|
||||
// Integrated graphics report the package the processor's counter already holds.
|
||||
if _, err := os.Stat(filepath.Join(filepath.Dir(filepath.Dir(hw)), "mem_info_vram_vendor")); err != nil {
|
||||
continue
|
||||
}
|
||||
for _, f := range []string{"power1_average", "power1_input"} {
|
||||
if _, err := os.Stat(filepath.Join(hw, f)); err == nil {
|
||||
found = append(found, "graphics "+strings.Split(strings.TrimPrefix(hw, SeatSource+"/"), "/")[0])
|
||||
|
||||
@@ -49,7 +49,7 @@ func TestALaptopHasABatteryAndAMeter(t *testing.T) {
|
||||
t.Fatalf("battery: %+v", b)
|
||||
}
|
||||
m := powerMeter{}.Detect(context.Background())
|
||||
if !m.Present || m.Detail != "battery BAT1, processor package intel-rapl:0" {
|
||||
if !m.Present || m.Detail != "battery BAT1, processor package-0" {
|
||||
t.Fatalf("power meter: %+v", m)
|
||||
}
|
||||
}
|
||||
@@ -79,13 +79,14 @@ func TestADesktopMeasuresItsProcessorAndItsGraphics(t *testing.T) {
|
||||
write(t, dir+"/powercap/intel-rapl:0:0/name", "core")
|
||||
write(t, dir+"/powercap/intel-rapl:0:0/energy_uj", "1")
|
||||
write(t, dir+"/drm/card1/device/hwmon/hwmon4/power1_average", "8000000")
|
||||
write(t, dir+"/drm/card1/device/mem_info_vram_vendor", "samsung")
|
||||
write(t, dir+"/nvidia/0000:01:00.0/information", "x")
|
||||
|
||||
if b := (battery{}).Detect(context.Background()); b.Present {
|
||||
t.Fatalf("a desktop has a battery: %+v", b)
|
||||
}
|
||||
m := powerMeter{}.Detect(context.Background())
|
||||
if !m.Present || m.Detail != "processor package intel-rapl:0, graphics card1, graphics nvidia" {
|
||||
if !m.Present || m.Detail != "processor package-0, graphics card1, graphics nvidia" {
|
||||
t.Fatalf("power meter: %+v", m)
|
||||
}
|
||||
}
|
||||
@@ -97,3 +98,36 @@ func TestAMachineWithNothingToReadHasNeither(t *testing.T) {
|
||||
t.Fatalf("%+v %+v", b, m)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPackageShownTwiceIsOneMeterAndIntegratedGraphicsAreNone(t *testing.T) {
|
||||
dir := sysfs(t)
|
||||
write(t, dir+"/powercap/intel-rapl:0/name", "package-0")
|
||||
write(t, dir+"/powercap/intel-rapl:0/energy_uj", "1")
|
||||
write(t, dir+"/powercap/intel-rapl-mmio:0/name", "package-0")
|
||||
write(t, dir+"/powercap/intel-rapl-mmio:0/energy_uj", "1")
|
||||
// An APU's graphics: power reported, no memory of its own.
|
||||
write(t, dir+"/drm/card1/device/hwmon/hwmon5/power1_average", "19000000")
|
||||
m := powerMeter{}.Detect(context.Background())
|
||||
if !m.Present || m.Detail != "processor package-0" {
|
||||
t.Fatalf("power meter: %+v", m)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOnlyIntegratedGraphicsMeasureNothing(t *testing.T) {
|
||||
dir := sysfs(t)
|
||||
write(t, dir+"/drm/card0/device/hwmon/hwmon1/power1_input", "5000000")
|
||||
if m := (powerMeter{}).Detect(context.Background()); m.Present {
|
||||
t.Fatalf("integrated graphics were taken for a meter: %+v", m)
|
||||
}
|
||||
}
|
||||
|
||||
func TestABatteryOfUnknownScopeIsTheMachines(t *testing.T) {
|
||||
// Many laptops' firmware says nothing of scope; the kernel reports Unknown. Only Device is a
|
||||
// peripheral's.
|
||||
dir := sysfs(t)
|
||||
write(t, dir+"/power_supply/BAT0/type", "Battery")
|
||||
write(t, dir+"/power_supply/BAT0/scope", "Unknown")
|
||||
if b := (battery{}).Detect(context.Background()); !b.Present || b.Detail != "BAT0" {
|
||||
t.Fatalf("battery: %+v", b)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user