diff --git a/tools/battery/battery.py b/tools/battery/battery.py index ed1098233..87742a9a7 100644 --- a/tools/battery/battery.py +++ b/tools/battery/battery.py @@ -203,18 +203,30 @@ def linux_hardware(): sockets.add(p.group(1)) cores.add((p.group(1), c.group(1))) + # Nominal (base) clock, never the boost clock: the model-name string, else cpufreq's own + # base_frequency (intel_pstate, kHz), else ACPI CPPC's nominal_freq (MHz; what AMD's + # acpi-cpufreq exposes instead - cpuinfo_max_freq on those boxes is the boost clock, not this). freq_ghz, freq_source = None, "unknown" m = re.search(r"@\s*([\d.]+)\s*GHz", model or "") if m: freq_ghz, freq_source = float(m.group(1)), "model name" else: - for name in ("base_frequency", "cpuinfo_max_freq"): + try: + freq_ghz = int(open("/sys/devices/system/cpu/cpu0/cpufreq/base_frequency").read()) / 1e6 + freq_source = "base_frequency" + except (OSError, ValueError): try: - freq_ghz = int(open(f"/sys/devices/system/cpu/cpu0/cpufreq/{name}").read()) / 1e6 - freq_source = name - break + freq_ghz = int(open("/sys/devices/system/cpu/cpu0/acpi_cppc/nominal_freq").read()) / 1e3 + freq_source = "acpi_cppc" except (OSError, ValueError): - continue + pass + + # Reported separately, and never as "nominal": cpuinfo_max_freq is the highest boost state. + max_boost_ghz = None + try: + max_boost_ghz = int(open("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq").read()) / 1e6 + except (OSError, ValueError): + pass mem_gib = None try: @@ -226,8 +238,9 @@ def linux_hardware(): pass return {"cpu_model": model or "unknown", "cpu_base_ghz": freq_ghz, "cpu_base_source": freq_source, - "sockets": len(sockets) or None, "physical_cores": len(cores) or None, - "threads": os.cpu_count(), "memory_gib": mem_gib, "gpus": gpu_hardware()} + "cpu_max_boost_ghz": max_boost_ghz, "sockets": len(sockets) or None, + "physical_cores": len(cores) or None, "threads": os.cpu_count(), "memory_gib": mem_gib, + "gpus": gpu_hardware()} def macos_hardware(): @@ -246,15 +259,16 @@ def macos_hardware(): cores = f"{p_cores}P+{e_cores}E" mem = sysctl("hw.memsize") return {"cpu_model": model or "unknown", "cpu_base_ghz": freq_ghz, "cpu_base_source": freq_source, - "sockets": int(sysctl("hw.packages") or 1), "physical_cores": cores, + "cpu_max_boost_ghz": None, "sockets": int(sysctl("hw.packages") or 1), "physical_cores": cores, "threads": int(sysctl("hw.logicalcpu") or os.cpu_count()), "memory_gib": int(mem) / 1024 ** 3 if mem else None, "gpus": []} def hardware_info(): """The run host's hardware, for the manifest: CPU model, nominal/base frequency (and where it - came from), physical cores, threads, sockets, memory, GPUs. Linux is detailed; macOS is a short - best-effort; anywhere else this is just empty, and the report says so.""" + came from) plus max boost frequency separately, physical cores, threads, sockets, memory, GPUs. + Linux is detailed; macOS is a short best-effort; anywhere else this is just empty, and the + report says so.""" try: if sys.platform == "darwin": return macos_hardware() diff --git a/tools/battery/report.py b/tools/battery/report.py index 2cc9e404c..69c0cdff9 100644 --- a/tools/battery/report.py +++ b/tools/battery/report.py @@ -293,8 +293,11 @@ def hardware_line(man): hw = man.get("hardware") if not hw: return "hardware: not recorded" - freq = f"{hw['cpu_base_ghz']:.2f} GHz nominal ({hw['cpu_base_source']})" if hw.get("cpu_base_ghz") else \ - f"nominal frequency unknown ({hw.get('cpu_base_source', 'unknown')})" + freq_bits = [f"{hw['cpu_base_ghz']:.2f} GHz nominal ({hw['cpu_base_source']})" if hw.get("cpu_base_ghz") + else "nominal frequency unknown"] + if hw.get("cpu_max_boost_ghz"): + freq_bits.append(f"{hw['cpu_max_boost_ghz']:.2f} GHz max boost") + freq = ", ".join(freq_bits) cores = f"{f(hw.get('physical_cores'), '{}')}C/{hw.get('threads') or '?'}T, {f(hw.get('sockets'), '{}')} socket(s)" mem = f"{hw['memory_gib']:.0f} GiB RAM" if hw.get("memory_gib") else "RAM unknown" gpus = ", ".join(f"{g['name']} ({g['memory']})" for g in hw.get("gpus") or []) or "none"