Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GPU temperature sensors #18

Merged
merged 3 commits into from
Sep 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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
Expand All @@ -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
```
13 changes: 11 additions & 2 deletions main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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 {
Expand Down
51 changes: 45 additions & 6 deletions smctemp.cc
Original file line number Diff line number Diff line change
Expand Up @@ -382,14 +382,17 @@ kern_return_t SmcAccessor::PrintAll() {
return kIOReturnSuccess;
}

bool SmcTemp::IsValidTemperature(double temperature, const std::pair<unsigned int, unsigned int>& limits) {
return temperature > limits.first && temperature < limits.second;
}

double SmcTemp::CalculateAverageTemperature(const std::vector<std::string>& sensors,
const std::pair<unsigned int, unsigned int>& 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++;
}
Expand All @@ -403,22 +406,23 @@ double SmcTemp::CalculateAverageTemperature(const std::vector<std::string>& sens
double SmcTemp::GetCpuTemp() {
double temp = 0.0;
#if defined(ARCH_TYPE_X86_64)
const std::pair<unsigned int, unsigned int> 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)
Expand Down Expand Up @@ -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<unsigned int, unsigned int> 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<std::string> sensors;
const std::pair<unsigned int, unsigned int> 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<std::string>(kSensorTg0f)); // GPU 1
sensors.emplace_back(static_cast<std::string>(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<std::string>(kSensorTg05)); // GPU 1
sensors.emplace_back(static_cast<std::string>(kSensorTg0D)); // GPU 2
sensors.emplace_back(static_cast<std::string>(kSensorTg0L)); // GPU 3
sensors.emplace_back(static_cast<std::string>(kSensorTg0T)); // GPU 4
} else {
// not supported
return temp;
}
temp = CalculateAverageTemperature(sensors, valid_temperature_limits);
#endif
return temp;
}

}

17 changes: 16 additions & 1 deletion smctemp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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 {
Expand All @@ -96,12 +109,14 @@ class SmcTemp {
private:
double CalculateAverageTemperature(const std::vector<std::string>& sensors,
const std::pair<unsigned int, unsigned int>& limits);
bool IsValidTemperature(double temperature, const std::pair<unsigned int, unsigned int>& limits);
SmcAccessor smc_accessor_;

public:
SmcTemp() = default;
~SmcTemp() = default;
double GetCpuTemp();
double GetGpuTemp();
};

typedef struct {
Expand Down
Loading