153 lines
4.0 KiB
C++
153 lines
4.0 KiB
C++
/*
|
|
* main.cpp
|
|
* power - CPU power/thermal status tool.
|
|
*
|
|
* Reads the kernel's power management snapshot (SYS_POWERINFO): package
|
|
* temperature, HWP frequency-scaling state, the thermal governor's
|
|
* current frequency ceiling, and the measured average active frequency.
|
|
* Useful for verifying that HWP and the thermal governor are doing their
|
|
* job on real hardware.
|
|
*
|
|
* Usage:
|
|
* power print the current snapshot
|
|
* power watch sample once per second until 60 s elapse
|
|
* power watch <secs> sample once per second for <secs> seconds
|
|
* Copyright (c) 2026 Daniel Hammer
|
|
*/
|
|
|
|
#include <montauk/syscall.h>
|
|
|
|
using namespace montauk;
|
|
|
|
static void put_u64(uint64_t n) {
|
|
char buf[24];
|
|
int i = 0;
|
|
if (n == 0) { putchar('0'); return; }
|
|
while (n) { buf[i++] = (char)('0' + (n % 10)); n /= 10; }
|
|
while (i) putchar(buf[--i]);
|
|
}
|
|
|
|
static void put_hex8(uint8_t v) {
|
|
static const char* digits = "0123456789ABCDEF";
|
|
print("0x");
|
|
putchar(digits[(v >> 4) & 0xF]);
|
|
putchar(digits[v & 0xF]);
|
|
}
|
|
|
|
static bool read_info(montauk::abi::PowerInfo& info) {
|
|
return syscall1(montauk::abi::SYS_POWERINFO, (uint64_t)&info) == 0;
|
|
}
|
|
|
|
static void print_snapshot(const montauk::abi::PowerInfo& info) {
|
|
print("CPU power/thermal status:\n");
|
|
|
|
print(" HWP: ");
|
|
if (info.hwpActive) {
|
|
print("active (EPP ");
|
|
put_hex8(info.epp);
|
|
print(", perf range ");
|
|
put_u64(info.lowestPerf);
|
|
print("-");
|
|
put_u64(info.highestPerf);
|
|
print(")\n");
|
|
} else {
|
|
print("not active\n");
|
|
}
|
|
|
|
print(" Frequency: base ");
|
|
put_u64(info.baseMHz);
|
|
print(" MHz, turbo ");
|
|
put_u64(info.maxMHz);
|
|
print(" MHz, avg active ");
|
|
put_u64(info.effMHz);
|
|
print(" MHz\n");
|
|
|
|
print(" Package: ");
|
|
if (info.tempC != 0) {
|
|
put_u64(info.tempC);
|
|
print(" C (hardware throttles at ");
|
|
put_u64(info.tjMaxC);
|
|
print(" C)\n");
|
|
} else {
|
|
print("temperature unavailable\n");
|
|
}
|
|
|
|
print(" Governor: ");
|
|
if (!info.hwpActive) {
|
|
print("inactive\n");
|
|
} else if (info.throttling) {
|
|
print("THROTTLING - ceiling ");
|
|
put_u64(info.curMaxPerf);
|
|
print(" of ");
|
|
put_u64(info.highestPerf);
|
|
print("\n");
|
|
} else {
|
|
print("not throttling (ceiling ");
|
|
put_u64(info.curMaxPerf);
|
|
print(")\n");
|
|
}
|
|
|
|
print(" AP idle: MWAIT hint ");
|
|
put_hex8((uint8_t)info.apIdleHint);
|
|
if (info.apIdleHint == 0) {
|
|
print(" (C1 only)");
|
|
}
|
|
print("\n");
|
|
}
|
|
|
|
static void print_watch_line(const montauk::abi::PowerInfo& info, uint64_t t) {
|
|
put_u64(t);
|
|
print("s ");
|
|
put_u64(info.tempC);
|
|
print(" C avg ");
|
|
put_u64(info.effMHz);
|
|
print(" MHz ceiling ");
|
|
put_u64(info.curMaxPerf);
|
|
print("/");
|
|
put_u64(info.highestPerf);
|
|
print(info.throttling ? " THROTTLING\n" : "\n");
|
|
}
|
|
|
|
static uint64_t parse_u64(const char* s, uint64_t fallback) {
|
|
uint64_t v = 0;
|
|
bool any = false;
|
|
while (*s == ' ') s++;
|
|
while (*s >= '0' && *s <= '9') { v = v * 10 + (uint64_t)(*s - '0'); s++; any = true; }
|
|
return any ? v : fallback;
|
|
}
|
|
|
|
extern "C" void _start() {
|
|
char args[64];
|
|
int alen = montauk::getargs(args, sizeof(args));
|
|
const char* rest = (alen > 0) ? args : "";
|
|
|
|
montauk::abi::PowerInfo info{};
|
|
if (!read_info(info)) {
|
|
print("power: no CPU power management available\n");
|
|
print("(non-Intel CPU or running under emulation)\n");
|
|
montauk::exit(1);
|
|
}
|
|
|
|
bool watch = rest[0] == 'w';
|
|
if (!watch) {
|
|
print_snapshot(info);
|
|
montauk::exit(0);
|
|
}
|
|
|
|
const char* p = rest;
|
|
while (*p && *p != ' ') p++;
|
|
uint64_t seconds = parse_u64(p, 60);
|
|
if (seconds == 0) seconds = 60;
|
|
|
|
print("time temp freq ceiling (1 sample/s, ");
|
|
put_u64(seconds);
|
|
print(" s)\n");
|
|
|
|
for (uint64_t t = 0; t < seconds; t++) {
|
|
if (!read_info(info)) break;
|
|
print_watch_line(info, t);
|
|
montauk::sleep_ms(1000);
|
|
}
|
|
montauk::exit(0);
|
|
}
|