github robintra/perf-sentinel v0.26.0

latest release: chart-v0.26.0
3 hours ago

What's new in v0.26.0

v0.26.0 is about energy figures that read more than the hardware measured, and labels that named the wrong source. The Kepler scraper summed every zone Kepler exports for a workload, although the zones overlap, so Kepler-fed energy and carbon came out too high. The Redfish scraper took the first PowerControl entry of the legacy /Power resource as the chassis, and could not read firmware that serves neither /Power nor a PowerWatts reading. An Alumet series in millijoules or milliwatts was read as joules without a word. And inspect and query monitor printed the window's model tag as the energy source, which reads electricity_maps_api on a daemon with Electricity Maps, an intensity source.

One Kepler zone instead of their sum

Kepler 0.10 and later export kepler_container_cpu_joules_total and kepler_process_cpu_joules_total once per zone, and the zones overlap: package already contains core and uncore, and Kepler's metrics documentation says not to sum them. The scraper now keeps one zone, set by the new [green.kepler] zone key, package by default as that documentation advises. package covers the CPU socket only, so the dram zone, which the old sum also added, is no longer counted unless zone names it. Rows without a zone label are still read. A blank zone, one with control characters or surrounding whitespace, or one longer than 256 characters is rejected at load.

Where Kepler reads hwmon, zones are named after the host's sensors, so zone has to name one of them. Otherwise the zero-sample warning fires, and it now names the zone as a possible cause. The Kepler scraper started line names the zone too. A test fixture comes from a real Kepler v0.12.0 /metrics endpoint: each workload carries a package, a core and a dram row, and 0.26.0 reads the package one where 0.25.5 added all three.

The chassis entry, and a chassis power sensor

DMTF does not require entry 0 of PowerControl to cover the whole chassis, and a BMC may list CPU or memory subsystems next to it. With schema = "legacy_power", the daemon now reads PowerConsumedWatts from the entry whose PhysicalContext is Chassis, and keeps entry 0 only when no entry says so. A null reading on the chassis entry fails the scrape as invalid_value instead of falling back to a subsystem.

The new schema = "sensor" reads Reading from a DMTF Sensor resource, /redfish/v1/Chassis/{id}/Sensors/{sensorId}, for firmware that serves neither /Power nor PowerWatts in /EnvironmentMetrics. Upstream OpenBMC has not served /Power by default since 2025-08-26. The chassis sensor is the one PowerWatts.DataSourceUri names. The reading counts only when ReadingUnits is W and the sensor's PhysicalContext, when present, is Chassis, so a temperature sensor, or the power sensor of one supply or one CPU that says so, fails the scrape as path_missing instead of feeding a wrong figure. Against the files of the DMTF public-rackmount1 mockup, the daemon read 344 W through legacy_power and 374 W through environment_metrics and sensor, and counted the PS1InputPower sensor as path_missing. Every schema keeps the redfish_bmc tag.

When ca_bundle_path is set, the startup error now points at SSL_CERT_FILE, which covers a BMC certificate from a private CA, or a self-signed one that is not itself a CA certificate.

A warning on non-RAPL Alumet series

The scraper reads joules per energy_interval_secs and tags the reading alumet_rapl. A GPU, Grace or Jetson series lands in the wrong unit, 1000x too high for millijoules, and the energy-estimation-tdp estimate is published as measured RAPL. At start, the daemon now warns once when [green.alumet] metric_name names such a source (grace_, nvml_, amd_gpu_, the Jetson input_power gauge, estimated_consumed_energy) or carries a unit suffix other than joules, such as _millijoules or _watts, which Alumet's development branch appends after v0.9.5. rapl_consumed_energy, an energy-attribution output and a _joules suffix do not warn. The scraper still reads the series.

The energy source, named the same way everywhere

analyze, inspect, query monitor and the HTML report name where the energy figure comes from, from per-service coverage instead of the window tag:

  • modeled from I/O counts when no energy backend covered a service,
  • source <backends> when every service was fully measured,
  • source <backends> on N of M services · rest modeled from I/O counts when they mix.

A service counts as covered once any of its operations was measured. The modeled part is followed by · calibrated when calibration factors rescaled it. A measured or Electricity Maps window tag drops the +cal suffix, so the reports carry a new green_summary.energy_calibrated field, omitted when false, and the label reads it. Tags outside [A-Za-z0-9_+-] or longer than 64 characters are not displayed.

analyze prints the label on a new Energy: line after the CO₂ block. The inspect panel's Energy: line and the query monitor Window energy: line carry it, and the monitor's By service table reads I/O counts for a service none of whose operations was measured. The HTML report's Carbon tab gains an Energy card after Total CO2, greyed out when no span resolved to a region, and its help texts no longer claim a model tag on every figure. In the simulation lab, measured-energy-chain attributed the Redfish mock's chassis energy to order-service under redfish_bmc, and alumet-db-waste found the database waste card intact next to the new card.

Calibration in periodic disclosures

disclose set methodology.calibration_inputs.calibration_applied only from a +cal suffix on a window's energy_model, which measured and Electricity Maps tags never carry. A calibrated daemon with Electricity Maps or an energy backend therefore published false, in the report and in its attestation predicate. The aggregator now also reads green_summary.energy_calibrated. Periods archived before 0.26.0 carry no such field and keep their published value.

Upgrade impact

  • Kepler-fed energy and carbon drop, by a factor that depends on the zones the host exposes, in the JSON report, the perf_sentinel_energy_kwh and perf_sentinel_carbon_gco2 gauges, /api/energy, query monitor and the periodic disclosures built from archived reports. Figures are not comparable across the upgrade. Part of the drop is DRAM leaving the default scope rather than an error removed. On a host whose hwmon zones carry other names, a Kepler-fed service falls back to the next configured backend or the I/O proxy until zone names one of them.
  • A Redfish BMC whose first PowerControl entry is a subsystem now reports its chassis entry under legacy_power, so its figures move too.
  • Two configuration keys are added, [green.kepler] zone and schema = "sensor" under [green.redfish], and an older binary rejects them at load, so drop them before rolling back.
  • The energy label replaces the model tag in inspect and query monitor, and analyze gains an Energy: line, so a parser of that text output needs updating.
  • The JSON report gains one optional field, green_summary.energy_calibrated, and a calibrated daemon behind a measured or Electricity Maps tag now publishes calibration_applied: true.
  • Breaking, perf-sentinel-core only: KeplerConfig gains the public field zone, GreenSummary the public field energy_calibrated and RedfishSchema the Sensor variant, none of them #[non_exhaustive], so a struct literal or an exhaustive match stops compiling. GreenSummary::energy_source_label, GreenSummary::service_energy_source and prom_parser::parse_metric_samples_where are new.
  • No detector, normalizer or signature changes, so no finding appears, disappears or changes signature and acknowledgments keep matching. No route or metric name changes, the embedded reference data keeps its vintages, and MSRV stays 1.98.1.

Full detail in CHANGELOG.md.

Verifying this release

# Binary integrity via SLSA build provenance attestation (Build Level 2)
gh attestation verify perf-sentinel-linux-amd64 \
  --repo robintra/perf-sentinel

# A periodic disclosure produced by this binary
perf-sentinel verify-hash --report perf-sentinel-report.json \
  --expected-identity "https://github.com/robintra/perf-sentinel/.github/workflows/release.yml@refs/tags/v0.26.0" \
  --expected-issuer "https://token.actions.githubusercontent.com" \
  --verify-binary ./perf-sentinel-linux-amd64

gh CLI 2.49 or newer required for gh attestation verify.

Don't miss a new perf-sentinel release

NewReleases is sending notifications on new releases.