diff --git a/README.md b/README.md index ab043e6..e83509d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ## smctemp -Print CPU temperature on macOS. +Print CPU and GPU temperatures on macOS. It works on following macs. - arm64 (M2 mac) @@ -8,6 +8,7 @@ It works on following macs. ## Acknowledgements I took the code from [hholtmann/smcFanControl/smc-command](https://github.com/hholtmann/smcFanControl/tree/ad374ffb1dd088a7676719e53dbd2886f8fafdff/smc-command) and modified it to specialize in temperature acquisition. +Some of the sensor values were obtained from the [exelban/stats](https://github.com/exelban/stats) project. ## How to Use ```console @@ -20,14 +21,19 @@ $ smctemp -c ## Usage ```console $ smctemp -h -Check Temperature by using Apple System Management Control (Smc) tool 0.1 +Check Temperature by using Apple System Management Control (Smc) tool 0.1.1 Usage: -smctemp [options] +./smctemp [options] -c : list CPU temperatures (Celsius) + -g : list GPU temperatures (Celsius) -h : help -l : list all keys and values -v : version + -n : tries to query the temperature sensors for n times (e.g. -n3) (1 second interval) until a valid value is returned $ smctemp -c 64.2 + +$ smctemp -g +36.2 ``` diff --git a/main.cc b/main.cc index ccf6a7a..b467f21 100644 --- a/main.cc +++ b/main.cc @@ -9,6 +9,7 @@ void usage(char* prog) { std::cout << "Usage:" << std::endl; std::cout << prog << " [options]" << std::endl; std::cout << " -c : list CPU temperatures (Celsius)" << std::endl; + std::cout << " -g : list GPU temperatures (Celsius)" << std::endl; std::cout << " -h : help" << std::endl; std::cout << " -l : list all keys and values" << std::endl; std::cout << " -v : version" << std::endl; @@ -25,11 +26,14 @@ int main(int argc, char *argv[]) { smctemp::UInt32Char_t key = { 0 }; smctemp::SmcVal_t val; - while ((c = getopt(argc, argv, "clvhn:")) != -1) { + while ((c = getopt(argc, argv, "clvhn:g")) != -1) { switch(c) { case 'c': op = smctemp::kOpReadCpuTemp; break; + case 'g': + op = smctemp::kOpReadGpuTemp; + break; case 'n': if (optarg) { auto [ptr, ec] = std::from_chars(optarg, optarg + strlen(optarg), attempts); @@ -71,10 +75,15 @@ int main(int argc, char *argv[]) { std::cerr.flags(ef); } break; + case smctemp::kOpReadGpuTemp: case smctemp::kOpReadCpuTemp: double temp = 0.0; while (attempts > 0) { - temp = smc_temp.GetCpuTemp(); + if (op == smctemp::kOpReadCpuTemp) { + temp = smc_temp.GetCpuTemp(); + } else if (op == smctemp::kOpReadGpuTemp) { + temp = smc_temp.GetGpuTemp(); + } if (temp > 0.0) { break; } else { diff --git a/smctemp.cc b/smctemp.cc index 02a9892..59f9450 100755 --- a/smctemp.cc +++ b/smctemp.cc @@ -382,14 +382,17 @@ kern_return_t SmcAccessor::PrintAll() { return kIOReturnSuccess; } +bool SmcTemp::IsValidTemperature(double temperature, const std::pair& limits) { + return temperature > limits.first && temperature < limits.second; +} + double SmcTemp::CalculateAverageTemperature(const std::vector& sensors, const std::pair& limits) { double temp = 0.0; size_t valid_sensor_count = 0; for (auto sensor : sensors) { auto sensor_value = smc_accessor_.ReadValue(sensor.c_str()); - if (sensor_value >= limits.first && - sensor_value <= limits.second) { + if (IsValidTemperature(sensor_value, limits)) { temp += sensor_value; valid_sensor_count++; } @@ -403,22 +406,23 @@ double SmcTemp::CalculateAverageTemperature(const std::vector& sens double SmcTemp::GetCpuTemp() { double temp = 0.0; #if defined(ARCH_TYPE_X86_64) + const std::pair valid_temperature_limits{0, 110}; // The reason why I prefer CPU die temperature to CPU proximity temperature: // https://github.com/narugit/smctemp/issues/2 temp = smc_accessor_.ReadValue(kSensorTc0d); - if (0.0 < temp && temp < 110.0) { + if (IsValidTemperature(temp, valid_temperature_limits)) { return temp; } temp = smc_accessor_.ReadValue(kSensorTc0e); - if (0.0 < temp && temp < 110.0) { + if (IsValidTemperature(temp, valid_temperature_limits)) { return temp; } temp = smc_accessor_.ReadValue(kSensorTc0f); - if (0.0 < temp && temp < 110.0) { + if (IsValidTemperature(temp, valid_temperature_limits)) { return temp; } temp = smc_accessor_.ReadValue(kSensorTc0p); - if (temp < 110.0) { + if (IsValidTemperature(temp, valid_temperature_limits)) { return temp; } #elif defined(ARCH_TYPE_ARM64) @@ -485,5 +489,40 @@ double SmcTemp::GetCpuTemp() { return temp; } +double SmcTemp::GetGpuTemp() { + double temp = 0.0; +#if defined(ARCH_TYPE_X86_64) + const std::pair valid_temperature_limits{0, 110}; + temp = smc_accessor_.ReadValue(kSensorTg0d); + if (IsValidTemperature(temp, valid_temperature_limits)) { + return temp; + } + temp = smc_accessor_.ReadValue(kSensorTpcd); + if (IsValidTemperature(temp, valid_temperature_limits)) { + return temp; + } +#elif defined(ARCH_TYPE_ARM64) + std::vector sensors; + const std::pair valid_temperature_limits{10, 120}; + const std::string cpumodel = getCPUModel(); + if (cpumodel.find("m2") != std::string::npos) { // Apple M2 + // ref: https://github.com/exelban/stats/blob/6b88eb1f60a0eb5b1a7b51b54f044bf637fd785b/Modules/Sensors/values.swift#L369-L370 + sensors.emplace_back(static_cast(kSensorTg0f)); // GPU 1 + sensors.emplace_back(static_cast(kSensorTg0j)); // GPU 2 + } else if (cpumodel.find("m1") != std::string::npos) { // Apple M1 + // ref: https://github.com/exelban/stats/blob/6b88eb1f60a0eb5b1a7b51b54f044bf637fd785b/Modules/Sensors/values.swift#L354-L357 + sensors.emplace_back(static_cast(kSensorTg05)); // GPU 1 + sensors.emplace_back(static_cast(kSensorTg0D)); // GPU 2 + sensors.emplace_back(static_cast(kSensorTg0L)); // GPU 3 + sensors.emplace_back(static_cast(kSensorTg0T)); // GPU 4 + } else { + // not supported + return temp; + } + temp = CalculateAverageTemperature(sensors, valid_temperature_limits); +#endif + return temp; +} + } diff --git a/smctemp.h b/smctemp.h index 6b16d24..7f97094 100755 --- a/smctemp.h +++ b/smctemp.h @@ -42,16 +42,22 @@ constexpr uint32_t kKernelIndexSmc = 2; constexpr int kOpNone = 0; constexpr int kOpList = 1; constexpr int kOpReadCpuTemp = 2; +constexpr int kOpReadGpuTemp = 3; // List of key and name: -// - https://github.com/exelban/stats/blob/0e2e13c626b650ac7743ef620869d2b7857665cd/Modules/Sensors/values.swift +// - https://github.com/exelban/stats/blob/6b88eb1f60a0eb5b1a7b51b54f044bf637fd785b/Modules/Sensors/values.swift // - https://github.com/acidanthera/VirtualSMC/blob/632fec680d996a5dd015afd9acf0ba40f75e69e2/Docs/SMCSensorKeys.txt #if defined(ARCH_TYPE_X86_64) +// CPU constexpr UInt32Char_t kSensorTc0d = "TC0D"; // CPU die temperature constexpr UInt32Char_t kSensorTc0e = "TC0E"; // CPU PECI die filtered temperature constexpr UInt32Char_t kSensorTc0f = "TC0F"; // CPU PECI die temperature filtered then adjusted constexpr UInt32Char_t kSensorTc0p = "TC0P"; // CPU proximity temperature +// GPU +constexpr UInt32Char_t kSensorTg0d = "TG0D"; // PCH Die Temp +constexpr UInt32Char_t kSensorTpcd = "TPCD"; // PCH Die Temp (digital) #elif defined(ARCH_TYPE_ARM64) +// CPU constexpr UInt32Char_t kSensorTc0a = "Tc0a"; constexpr UInt32Char_t kSensorTc0b = "Tc0b"; constexpr UInt32Char_t kSensorTc0x = "Tc0x"; @@ -70,6 +76,13 @@ constexpr UInt32Char_t kSensorTp0j = "Tp0j"; constexpr UInt32Char_t kSensorTp0r = "Tp0r"; constexpr UInt32Char_t kSensorTp0f = "Tp0f"; constexpr UInt32Char_t kSensorTp0n = "Tp0n"; +// GPU +constexpr UInt32Char_t kSensorTg05 = "Tg05"; +constexpr UInt32Char_t kSensorTg0D = "Tg0D"; +constexpr UInt32Char_t kSensorTg0L = "Tg0L"; +constexpr UInt32Char_t kSensorTg0T = "Tg0T"; +constexpr UInt32Char_t kSensorTg0f = "Tg0f"; +constexpr UInt32Char_t kSensorTg0j = "Tg0j"; #endif class SmcAccessor { @@ -96,12 +109,14 @@ class SmcTemp { private: double CalculateAverageTemperature(const std::vector& sensors, const std::pair& limits); + bool IsValidTemperature(double temperature, const std::pair& limits); SmcAccessor smc_accessor_; public: SmcTemp() = default; ~SmcTemp() = default; double GetCpuTemp(); + double GetGpuTemp(); }; typedef struct {