#pragma once #include #include namespace fgc::hostmetrics { // Host (LattePanda / Linux) health reads, for the `test host` subsystem and the // running program's CPU watch (ThermalGuard). Pure parsers are split from the // live readers so they unit-test without a real /sys or /proc. All readers // degrade gracefully (ok=false) rather than throw. // // `root` is put in front of every /sys and /proc path ("/" = the real ones), so // a test or a throttling simulation can hand in a fake tree: // /sys/class/thermal/thermal_zone*/{temp,type} // /sys/devices/system/cpu/cpu*/cpufreq/{scaling_cur_freq,cpuinfo_max_freq,scaling_max_freq} // /sys/devices/system/cpu/cpu*/thermal_throttle/{package,core}_throttle_count // /proc/stat (when the tree has none, the real /proc/stat) struct ThermalInfo { bool ok = false; double max_temp_c = 0.0; // hottest thermal zone std::string hottest_zone; // e.g. "x86_pkg_temp" bool throttled = false; // best-effort (false if unknown) // The CPU's own zone, for the CPU guard: x86_pkg_temp where there is one, // else the hottest. Not simply the hottest: the towers also list the Wi-Fi // card (iwlwifi_1) and the ACPI zone, and the guard watches the CPU. double cpu_temp_c = 0.0; std::string cpu_zone; }; // CPU clock, from cpufreq. MHz. struct CpuFreqInfo { bool ok = false; double mean_mhz = 0.0; // mean of scaling_cur_freq over the cores double hw_max_mhz = 0.0; // cpuinfo_max_freq: what the chip can do double cap_mhz = 0.0; // lowest scaling_max_freq: what it may do now std::vector core_mhz; // per core, cpu0 first }; // The kernel's count of times the CPU slowed itself because it ran too hot // (x86 thermal_throttle). It only rises; a rise between two reads is throttling. // Slow-downs to stay inside the power limit are NOT counted here - ThrottleWatch // (ThermalGuard.h) also watches the clock for those. struct ThrottleCount { bool ok = false; long long events = 0; // package count (cpu0) + every core's count }; // Cumulative CPU time from the first line of /proc/stat, in clock ticks. Busy % // is the change in `busy` over the change in `total` between two reads. struct CpuTimes { bool ok = false; long long busy = 0; long long total = 0; }; struct DiskInfo { bool ok = false; std::string path; double total_gb = 0.0; double free_gb = 0.0; double used_pct = 0.0; }; struct MemInfo { bool ok = false; double total_mb = 0.0; double avail_mb = 0.0; double swap_used_mb = 0.0; }; struct LoadInfo { bool ok = false; double load1 = 0.0, load5 = 0.0, load15 = 0.0; int cpus = 0; }; // ---- live readers ---- ThermalInfo readThermal(const std::string& root = "/"); // scans sys/class/thermal CpuFreqInfo readCpuFreq(const std::string& root = "/"); ThrottleCount readThrottleCount(const std::string& root = "/"); CpuTimes readCpuTimes(const std::string& root = "/"); DiskInfo readDisk(const std::string& path); // statvfs() on the filesystem holding `path` MemInfo readMeminfo(); // /proc/meminfo LoadInfo readLoad(); // getloadavg() + nproc // ---- pure parsers (reused by the readers; exposed for tests) ---- MemInfo parseMeminfo(const std::string& proc_meminfo); double parseMilliCelsius(const std::string& zone_temp_contents); // "52000\n" -> 52.0 CpuTimes parseProcStatCpu(const std::string& proc_stat); // its "cpu " line // Busy percentage between two reads; -1 when either is bad or no time passed. double busyPercent(const CpuTimes& before, const CpuTimes& after); } // namespace fgc::hostmetrics