From 3f91a9e736d6eb57713e3ab02f77a7891edec8ba Mon Sep 17 00:00:00 2001 From: William Stoneham Date: Mon, 6 Apr 2026 19:47:41 +0100 Subject: [PATCH] fix: improve Apple Silicon temperature detection on macOS --- src/osx/btop_collect.cpp | 30 ++++++++++++--- src/osx/sensors.cpp | 80 ++++++++++++++++++++++++++++++++++++---- src/osx/sensors.hpp | 4 ++ 3 files changed, 102 insertions(+), 12 deletions(-) diff --git a/src/osx/btop_collect.cpp b/src/osx/btop_collect.cpp index ff0cedf2..ed8a3d2b 100644 --- a/src/osx/btop_collect.cpp +++ b/src/osx/btop_collect.cpp @@ -373,8 +373,13 @@ namespace Gpu { char buf[200]; CFStringGetCString(name, buf, 200, kCFStringEncodingASCII); string n(buf); - //? "GPU MTR Temp Sensor" is the standard Apple Silicon GPU temp sensor name - if (n.find("GPU") != string::npos) { + //? Legacy Apple Silicon uses "GPU MTR Temp Sensor*" names. + //? On newer Apple Silicon we can see PMU TP*g sensors for GPU temperatures. + bool is_gpu_sensor = n.find("GPU") != string::npos; + if (not is_gpu_sensor and n.rfind("PMU TP", 0) == 0 and not n.empty() and n.back() == 'g') { + is_gpu_sensor = true; + } + if (is_gpu_sensor) { CFRef event(IOHIDServiceClientCopyEvent(sc, kIOHIDEventTypeTemperature, 0, 0)); if (event.get()) { double temp = IOHIDEventGetFloatValue(event, kIOHIDEventTypeTemperature << 16); @@ -771,10 +776,11 @@ namespace Cpu { if (Config::getB("show_coretemp") and Config::getB("check_temp")) { #if __MAC_OS_X_VERSION_MIN_REQUIRED > 101504 ThermalSensors sensors; - if (sensors.getSensors() > 0) { + std::vector core_temps; + if (sensors.getSensors(core_temps) > 0) { Logger::debug("M1 sensors found"); got_sensors = true; - cpu_temp_only = true; + cpu_temp_only = core_temps.empty(); macM1 = true; } else { #endif @@ -817,9 +823,23 @@ namespace Cpu { if (macM1) { #if __MAC_OS_X_VERSION_MIN_REQUIRED > 101504 ThermalSensors sensors; - current_cpu.temp.at(0).push_back(sensors.getSensors()); + std::vector core_temps; + current_cpu.temp.at(0).push_back(sensors.getSensors(core_temps)); if (current_cpu.temp.at(0).size() > 20) current_cpu.temp.at(0).pop_front(); + cpu_temp_only = core_temps.empty(); + + if (Config::getB("show_coretemp") and not core_temps.empty()) { + for (int core = 0; core < Shared::coreCount; core++) { + size_t sensor_index = static_cast(core) * core_temps.size() / Shared::coreCount; + long long temp = core_temps.at(sensor_index); + if (cmp_less(core + 1, current_cpu.temp.size())) { + current_cpu.temp.at(core + 1).push_back(temp); + if (current_cpu.temp.at(core + 1).size() > 20) + current_cpu.temp.at(core + 1).pop_front(); + } + } + } #endif } else { SMCConnection smcCon; diff --git a/src/osx/sensors.cpp b/src/osx/sensors.cpp index bee8978e..62beeaa7 100644 --- a/src/osx/sensors.cpp +++ b/src/osx/sensors.cpp @@ -24,7 +24,10 @@ tab-size = 4 #include #include +#include +#include #include +#include #include extern "C" { @@ -74,14 +77,42 @@ double getValue(IOHIDServiceClientRef sc) { } // extern C +namespace { + int parse_sensor_index(const std::string& name, const std::string& prefix) { + if (name.rfind(prefix, 0) != 0) return -1; + size_t pos = prefix.size(); + if (pos >= name.size() or not std::isdigit(static_cast(name.at(pos)))) return -1; + int value = 0; + while (pos < name.size() and std::isdigit(static_cast(name.at(pos)))) { + value = (value * 10) + (name.at(pos) - '0'); + pos++; + } + return value; + } + + long long avg_to_long(const std::vector& temps) { + if (temps.empty()) return 0ll; + return round(std::accumulate(temps.begin(), temps.end(), 0ll) / temps.size()); + } +} + long long Cpu::ThermalSensors::getSensors() { + std::vector unused_core_temps; + return getSensors(unused_core_temps); +} + +long long Cpu::ThermalSensors::getSensors(std::vector& core_temps) { CFDictionaryRef thermalSensors = matching(0xff00, 5); // 65280_10 = FF00_16 - // thermalSensors's PrimaryUsagePage should be 0xff00 for M1 chip, instead of 0xff05 - // can be checked by ioreg -lfx + // thermalSensors's PrimaryUsagePage should be 0xff00 for M1 chip, instead of 0xff05 + // can be checked by ioreg -lfx IOHIDEventSystemClientRef system = IOHIDEventSystemClientCreate(kCFAllocatorDefault); IOHIDEventSystemClientSetMatching(system, thermalSensors); CFArrayRef matchingsrvs = IOHIDEventSystemClientCopyServices(system); - std::vector temps; + std::vector acc_temps; + std::vector tdie_temps; + std::vector soc_temps; + std::unordered_map > acc_named_temps; + std::unordered_map > tdie_indexed_temps; if (matchingsrvs) { long count = CFArrayGetCount(matchingsrvs); for (int i = 0; i < count; i++) { @@ -97,8 +128,18 @@ long long Cpu::ThermalSensors::getSensors() { // there is also PMU tdev1-8 but it has negative values?? // there is also eACC for efficiency package but it only has 2 entries // and pACC for performance but it has 7 entries (2 - 9) WTF - if (n.starts_with("eACC") or n.starts_with("pACC")) { - temps.push_back(getValue(sc)); + double temp = getValue(sc); + if (temp > 0 and temp < 150) { + if (n.rfind("eACC", 0) == 0 or n.rfind("pACC", 0) == 0) { + acc_temps.push_back(temp); + acc_named_temps[n].push_back(temp); + } else if (n.rfind("PMU tdie", 0) == 0) { + tdie_temps.push_back(temp); + int index = parse_sensor_index(n, "PMU tdie"); + if (index >= 0) tdie_indexed_temps[index].push_back(temp); + } else if (n.rfind("SOC MTR Temp Sensor", 0) == 0) { + soc_temps.push_back(temp); + } } CFRelease(name); } @@ -108,7 +149,32 @@ long long Cpu::ThermalSensors::getSensors() { } CFRelease(system); CFRelease(thermalSensors); - if (temps.empty()) return 0ll; - return round(std::accumulate(temps.begin(), temps.end(), 0ll) / temps.size()); + core_temps.clear(); + if (not tdie_indexed_temps.empty()) { + std::vector indexes; + indexes.reserve(tdie_indexed_temps.size()); + for (const auto& indexed_temp : tdie_indexed_temps) { + indexes.push_back(indexed_temp.first); + } + std::sort(indexes.begin(), indexes.end()); + for (const auto& index : indexes) { + auto avg = avg_to_long(tdie_indexed_temps.at(index)); + if (avg > 0) core_temps.push_back(avg); + } + } else if (not acc_named_temps.empty()) { + std::vector names; + names.reserve(acc_named_temps.size()); + for (const auto& named_temp : acc_named_temps) { + names.push_back(named_temp.first); + } + std::sort(names.begin(), names.end()); + for (const auto& name : names) { + auto avg = avg_to_long(acc_named_temps.at(name)); + if (avg > 0) core_temps.push_back(avg); + } + } + + const auto &temps = not acc_temps.empty() ? acc_temps : (not tdie_temps.empty() ? tdie_temps : soc_temps); + return avg_to_long(temps); } #endif diff --git a/src/osx/sensors.hpp b/src/osx/sensors.hpp index fcb28b02..b7572aa0 100644 --- a/src/osx/sensors.hpp +++ b/src/osx/sensors.hpp @@ -16,12 +16,16 @@ indent = tab tab-size = 4 */ +#include + #include #if __MAC_OS_X_VERSION_MIN_REQUIRED > 101504 + namespace Cpu { class ThermalSensors { public: long long getSensors(); + long long getSensors(std::vector& core_temps); }; } // namespace Cpu #endif