summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJos Dehaes <jos.dehaes@gmail.com>2021-10-13 23:38:27 +0200
committerJos Dehaes <jos.dehaes@gmail.com>2021-10-13 23:38:36 +0200
commit4f078c3beb960fe06f4d8b44b9c913e2aacf7625 (patch)
tree55f12edfabf45272eea1ddcf37a980e5c6ab9433
parent289880aaa6d1a3625c01e9d7643138343b29db53 (diff)
more temperature (M1 + intel)
-rw-r--r--src/osx/btop_collect.cpp66
-rw-r--r--src/osx/sensors.cpp96
-rw-r--r--src/osx/smc.hpp199
3 files changed, 300 insertions, 61 deletions
diff --git a/src/osx/btop_collect.cpp b/src/osx/btop_collect.cpp
index 12ce949..ba37c3c 100644
--- a/src/osx/btop_collect.cpp
+++ b/src/osx/btop_collect.cpp
@@ -17,8 +17,6 @@ tab-size = 4
*/
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
-#include <IOKit/ps/IOPSKeys.h>
-#include <IOKit/ps/IOPowerSources.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <libproc.h>
@@ -51,6 +49,7 @@ tab-size = 4
#include <string>
#include "sensors.hpp"
+#include "smc.hpp"
using std::clamp, std::string_literals::operator""s, std::cmp_equal, std::cmp_less, std::cmp_greater;
using std::ifstream, std::numeric_limits, std::streamsize, std::round, std::max, std::min;
@@ -107,7 +106,7 @@ namespace Shared {
fs::path passwd_path;
uint64_t totalMem;
- long pageSize, clkTck, coreCount;
+ long pageSize, clkTck, coreCount, physicalCoreCount;
int totalMem_len;
void init() {
@@ -119,6 +118,11 @@ namespace Shared {
Logger::warning("Could not determine number of cores, defaulting to 1.");
}
+ size_t physicalCoreCountSize = sizeof(physicalCoreCount);
+ if (sysctlbyname("hw.physicalcpu", &physicalCoreCount, &physicalCoreCountSize, NULL, 0) < 0) {
+ Logger::error("Could not get physical core count");
+ }
+
pageSize = sysconf(_SC_PAGE_SIZE);
if (pageSize <= 0) {
pageSize = 4096;
@@ -229,6 +233,20 @@ namespace Cpu {
ThermalSensors sensors;
if (sensors.getSensors().size() > 0) {
got_sensors = true;
+ } else {
+ // try SMC (intel)
+ SMCConnection smcCon;
+ try {
+ long long t = smcCon.getTemp(-1); // check if we have package T
+ if (t > -1) {
+ got_sensors = true;
+ } else {
+ got_sensors = false;
+ }
+ } catch (std::runtime_error &e) {
+ // ignore, we don't have temp
+ got_sensors = false;
+ }
}
Logger::debug("got sensors:" + std::to_string(got_sensors));
}
@@ -236,19 +254,37 @@ namespace Cpu {
}
void update_sensors() {
- current_cpu.temp_max = 95; // we have no idea how to get the critical temp
- ThermalSensors sensors;
- auto sensor = sensors.getSensors();
+ current_cpu.temp_max = 95; // we have no idea how to get the critical temp
try {
- current_cpu.temp.at(0).push_back((long long)sensor[0]);
-
- if (Config::getB("show_coretemp") and not cpu_temp_only) {
- for (int core = 1; core <= Shared::coreCount; core++) {
- long long temp = (long long) sensor[core];
- if (cmp_less(core, current_cpu.temp.size())) {
- current_cpu.temp.at(core).push_back(temp);
- if (current_cpu.temp.at(core).size() > 20)
- current_cpu.temp.at(core).pop_front();
+ ThermalSensors sensors;
+ auto sensor = sensors.getSensors();
+ if (sensor.size() > 0) {
+ current_cpu.temp.at(0).push_back((long long)sensor[0]);
+
+ if (Config::getB("show_coretemp") and not cpu_temp_only) {
+ for (int core = 1; core <= Shared::coreCount; core++) {
+ long long temp = (long long)sensor[core];
+ if (cmp_less(core, current_cpu.temp.size())) {
+ current_cpu.temp.at(core).push_back(temp);
+ if (current_cpu.temp.at(core).size() > 20)
+ current_cpu.temp.at(core).pop_front();
+ }
+ }
+ }
+ } else {
+ SMCConnection smcCon;
+ int threadsPerCore = Shared::coreCount / Shared::physicalCoreCount;
+ long long packageT = smcCon.getTemp(-1); // -1 returns package T
+ current_cpu.temp.at(0).push_back(packageT);
+
+ if (Config::getB("show_coretemp") and not cpu_temp_only) {
+ for (int core = 0; core < Shared::coreCount; core++) {
+ long long temp = smcCon.getTemp(core / threadsPerCore); // same temp for all threads of same physical core
+ 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();
+ }
}
}
}
diff --git a/src/osx/sensors.cpp b/src/osx/sensors.cpp
index 8451bd1..38b466b 100644
--- a/src/osx/sensors.cpp
+++ b/src/osx/sensors.cpp
@@ -3,12 +3,11 @@
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/hidsystem/IOHIDEventSystemClient.h>
+#include <btop_tools.hpp>
#include <iostream>
#include <map>
#include <string>
-#include <btop_tools.hpp>
-
extern "C" {
typedef struct __IOHIDEvent *IOHIDEventRef;
typedef struct __IOHIDServiceClient *IOHIDServiceClientRef;
@@ -45,25 +44,28 @@ CFDictionaryRef matching(int page, int usage) {
CFArrayRef getProductNames(CFDictionaryRef sensors) {
IOHIDEventSystemClientRef system = IOHIDEventSystemClientCreate(kCFAllocatorDefault); // in CFBase.h = NULL
- // ... this is the same as using kCFAllocatorDefault or the return value from CFAllocatorGetDefault()
- IOHIDEventSystemClientSetMatching(system, sensors);
- CFArrayRef matchingsrvs = IOHIDEventSystemClientCopyServices(system); // matchingsrvs = matching services
-
- long count = CFArrayGetCount(matchingsrvs);
- CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
-
- for (int i = 0; i < count; i++) {
- IOHIDServiceClientRef sc = (IOHIDServiceClientRef)CFArrayGetValueAtIndex(matchingsrvs, i);
- CFStringRef name = IOHIDServiceClientCopyProperty(sc, CFSTR("Product")); // here we use ...CopyProperty
- if (name) {
- CFArrayAppendValue(array, name);
- } else {
- CFArrayAppendValue(array, CFSTR("noname")); // @ gives a Ref like in "CFStringRef name"
+ if (system) {
+ IOHIDEventSystemClientSetMatching(system, sensors);
+ CFArrayRef matchingsrvs = IOHIDEventSystemClientCopyServices(system); // matchingsrvs = matching services
+ if (matchingsrvs) {
+ long count = CFArrayGetCount(matchingsrvs);
+ CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+
+ for (int i = 0; i < count; i++) {
+ IOHIDServiceClientRef sc = (IOHIDServiceClientRef)CFArrayGetValueAtIndex(matchingsrvs, i);
+ CFStringRef name = IOHIDServiceClientCopyProperty(sc, CFSTR("Product")); // here we use ...CopyProperty
+ if (name) {
+ CFArrayAppendValue(array, name);
+ } else {
+ CFArrayAppendValue(array, CFSTR("noname")); // @ gives a Ref like in "CFStringRef name"
+ }
+ CFRelease(name);
+ }
+ return array;
}
- CFRelease(name);
+ CFRelease(system);
}
- CFRelease(system);
- return array;
+ return nullptr;
}
CFArrayRef getThermalValues(CFDictionaryRef sensors) {
@@ -99,35 +101,37 @@ unordered_flat_map<int, double> Cpu::ThermalSensors::getSensors() {
// thermalSensors's PrimaryUsagePage should be 0xff00 for M1 chip, instead of 0xff05
// can be checked by ioreg -lfx
CFArrayRef thermalNames = getProductNames(thermalSensors);
- CFArrayRef thermalValues = getThermalValues(thermalSensors);
- long count = CFArrayGetCount(thermalNames);
- for (int i = 0; i < count; i++) {
- CFStringRef nameRef = (CFStringRef)CFArrayGetValueAtIndex(thermalNames, i);
- char buf[200];
- CFStringGetCString(nameRef, buf, 200, kCFStringEncodingASCII);
- std::string n(buf);
- CFNumberRef value = (CFNumberRef)CFArrayGetValueAtIndex(thermalValues, i);
- double temp = 0.0;
- CFNumberGetValue(value, kCFNumberDoubleType, &temp);
- if (n.starts_with("PMU tdie")) {
- // Apple Silicon
- std::string indexString = n.substr(8, 1);
- int index = stoi(indexString);
- cpuValues[index - 1] = temp;
- } else if (n.starts_with("TC") && n[3] == 'c') {
- // intel mac
- std::string indexString = n.substr(2, 1);
- int index = stoi(indexString);
- cpuValues[index] = temp;
- } else if (n == "TCAD") {
- cpuValues[0] = temp; // package T for intel
- } else if (n == "SOC MTR Temp Sensor0") {
- cpuValues[0] = temp; // package T for Apple Silicon
- }
+ if (thermalNames) {
+ CFArrayRef thermalValues = getThermalValues(thermalSensors);
+ long count = CFArrayGetCount(thermalNames);
+ for (int i = 0; i < count; i++) {
+ CFStringRef nameRef = (CFStringRef)CFArrayGetValueAtIndex(thermalNames, i);
+ char buf[200];
+ CFStringGetCString(nameRef, buf, 200, kCFStringEncodingASCII);
+ std::string n(buf);
+ CFNumberRef value = (CFNumberRef)CFArrayGetValueAtIndex(thermalValues, i);
+ double temp = 0.0;
+ CFNumberGetValue(value, kCFNumberDoubleType, &temp);
+ if (n.starts_with("PMU tdie")) {
+ // Apple Silicon
+ std::string indexString = n.substr(8, 1);
+ int index = stoi(indexString);
+ cpuValues[index - 1] = temp;
+ } else if (n.starts_with("TC") && n[3] == 'c') {
+ // intel mac
+ std::string indexString = n.substr(2, 1);
+ int index = stoi(indexString);
+ cpuValues[index] = temp;
+ } else if (n == "TCAD") {
+ cpuValues[0] = temp; // package T for intel
+ } else if (n == "SOC MTR Temp Sensor0") {
+ cpuValues[0] = temp; // package T for Apple Silicon
+ }
+ }
+ CFRelease(thermalNames);
+ CFRelease(thermalValues);
}
CFRelease(thermalSensors);
- CFRelease(thermalNames);
- CFRelease(thermalValues);
return cpuValues;
}
diff --git a/src/osx/smc.hpp b/src/osx/smc.hpp
new file mode 100644
index 0000000..13070f2
--- /dev/null
+++ b/src/osx/smc.hpp
@@ -0,0 +1,199 @@
+#pragma once
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/ps/IOPSKeys.h>
+#include <IOKit/ps/IOPowerSources.h>
+#include <stdexcept>
+
+#define VERSION "0.01"
+
+#define KERNEL_INDEX_SMC 2
+
+#define SMC_CMD_READ_BYTES 5
+#define SMC_CMD_WRITE_BYTES 6
+#define SMC_CMD_READ_INDEX 8
+#define SMC_CMD_READ_KEYINFO 9
+#define SMC_CMD_READ_PLIMIT 11
+#define SMC_CMD_READ_VERS 12
+
+#define DATATYPE_FPE2 "fpe2"
+#define DATATYPE_UINT8 "ui8 "
+#define DATATYPE_UINT16 "ui16"
+#define DATATYPE_UINT32 "ui32"
+#define DATATYPE_SP78 "sp78"
+
+// key values
+#define SMC_KEY_CPU_TEMP "TC0P"
+#define SMC_KEY_CPU1_TEMP "TC1C"
+#define SMC_KEY_CPU2_TEMP "TC2C" // etc
+#define SMC_KEY_FAN0_RPM_CUR "F0Ac"
+
+typedef struct {
+ char major;
+ char minor;
+ char build;
+ char reserved[1];
+ UInt16 release;
+} SMCKeyData_vers_t;
+
+typedef struct {
+ UInt16 version;
+ UInt16 length;
+ UInt32 cpuPLimit;
+ UInt32 gpuPLimit;
+ UInt32 memPLimit;
+} SMCKeyData_pLimitData_t;
+
+typedef struct {
+ UInt32 dataSize;
+ UInt32 dataType;
+ char dataAttributes;
+} SMCKeyData_keyInfo_t;
+
+typedef char SMCBytes_t[32];
+
+typedef struct {
+ UInt32 key;
+ SMCKeyData_vers_t vers;
+ SMCKeyData_pLimitData_t pLimitData;
+ SMCKeyData_keyInfo_t keyInfo;
+ char result;
+ char status;
+ char data8;
+ UInt32 data32;
+ SMCBytes_t bytes;
+} SMCKeyData_t;
+
+typedef char UInt32Char_t[5];
+
+typedef struct {
+ UInt32Char_t key;
+ UInt32 dataSize;
+ UInt32Char_t dataType;
+ SMCBytes_t bytes;
+} SMCVal_t;
+
+namespace Cpu {
+ class SMCConnection {
+ io_connect_t conn;
+ kern_return_t result;
+ mach_port_t masterPort;
+ io_iterator_t iterator;
+ io_object_t device;
+
+ public:
+ SMCConnection() {
+ IOMasterPort(MACH_PORT_NULL, &masterPort);
+
+ CFMutableDictionaryRef matchingDictionary = IOServiceMatching("AppleSMC");
+ result = IOServiceGetMatchingServices(masterPort, matchingDictionary, &iterator);
+ if (result != kIOReturnSuccess) {
+ throw std::runtime_error("failed to get AppleSMC");
+ }
+
+ device = IOIteratorNext(iterator);
+ IOObjectRelease(iterator);
+ if (device == 0) {
+ throw std::runtime_error("failed to get SMC device");
+ }
+
+ result = IOServiceOpen(device, mach_task_self(), 0, &conn);
+ IOObjectRelease(device);
+ if (result != kIOReturnSuccess) {
+ throw std::runtime_error("failed to get SMC connection");
+ }
+ }
+ // core means physical core in SMC, while in core map it's cpu threads :-/ Only an issue on hackintosh?
+ // this means we can only get the T per physical core
+ // another issue with the SMC API is that the key is always 4 chars -> what with systems with more than 9 physical cores?
+ // no Mac models with more than 18 threads are released, so no problem so far
+ // according to VirtualSMC docs (hackintosh fake SMC) the enumeration follows with alphabetic chars - not implemented yet here (nor in VirtualSMC)
+ long long getTemp(int core) {
+ SMCVal_t val;
+ kern_return_t result;
+ char key[] = SMC_KEY_CPU_TEMP;
+ if (core >= 0) {
+ snprintf(key, 5, "TC%1dc", core);
+ }
+ result = SMCReadKey(key, &val);
+ if (result == kIOReturnSuccess) {
+ if (strcmp(val.dataType, DATATYPE_SP78) == 0) {
+ // convert sp78 value to temperature
+ int intValue = val.bytes[0] * 256 + (unsigned char)val.bytes[1];
+ return static_cast<long long>(intValue / 256.0);
+ }
+ }
+ return -1;
+ }
+ virtual ~SMCConnection() {
+ IOServiceClose(conn);
+ }
+ private:
+ UInt32 _strtoul(char *str, int size, int base) {
+ UInt32 total = 0;
+ int i;
+
+ for (i = 0; i < size; i++) {
+ if (base == 16) {
+ total += str[i] << (size - 1 - i) * 8;
+ } else {
+ total += (unsigned char)(str[i] << (size - 1 - i) * 8);
+ }
+ }
+ return total;
+ }
+ void _ultostr(char *str, UInt32 val) {
+ str[0] = '\0';
+ sprintf(str, "%c%c%c%c",
+ (unsigned int)val >> 24,
+ (unsigned int)val >> 16,
+ (unsigned int)val >> 8,
+ (unsigned int)val);
+ }
+
+ kern_return_t SMCCall(int index, SMCKeyData_t *inputStructure, SMCKeyData_t *outputStructure) {
+ size_t structureInputSize;
+ size_t structureOutputSize;
+
+ structureInputSize = sizeof(SMCKeyData_t);
+ structureOutputSize = sizeof(SMCKeyData_t);
+
+ return IOConnectCallStructMethod(conn, index,
+ // inputStructure
+ inputStructure, structureInputSize,
+ // ouputStructure
+ outputStructure, &structureOutputSize);
+ }
+
+ kern_return_t SMCReadKey(UInt32Char_t key, SMCVal_t *val) {
+ kern_return_t result;
+ SMCKeyData_t inputStructure;
+ SMCKeyData_t outputStructure;
+
+ memset(&inputStructure, 0, sizeof(SMCKeyData_t));
+ memset(&outputStructure, 0, sizeof(SMCKeyData_t));
+ memset(val, 0, sizeof(SMCVal_t));
+
+ inputStructure.key = _strtoul(key, 4, 16);
+ inputStructure.data8 = SMC_CMD_READ_KEYINFO;
+
+ result = SMCCall(KERNEL_INDEX_SMC, &inputStructure, &outputStructure);
+ if (result != kIOReturnSuccess)
+ return result;
+
+ val->dataSize = outputStructure.keyInfo.dataSize;
+ _ultostr(val->dataType, outputStructure.keyInfo.dataType);
+ inputStructure.keyInfo.dataSize = val->dataSize;
+ inputStructure.data8 = SMC_CMD_READ_BYTES;
+
+ result = SMCCall(KERNEL_INDEX_SMC, &inputStructure, &outputStructure);
+ if (result != kIOReturnSuccess)
+ return result;
+
+ memcpy(val->bytes, outputStructure.bytes, sizeof(outputStructure.bytes));
+
+ return kIOReturnSuccess;
+ }
+ };
+}