htop/linux/LibSensors.c

270 lines
7.2 KiB
C

#include "linux/LibSensors.h"
#include "config.h"
#ifdef HAVE_SENSORS_SENSORS_H
#include <assert.h>
#include <dlfcn.h>
#include <errno.h>
#include <limits.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sensors/sensors.h>
#include "Macros.h"
#include "XUtils.h"
#include "linux/LinuxProcessList.h"
#ifdef BUILD_STATIC
#define sym_sensors_init sensors_init
#define sym_sensors_cleanup sensors_cleanup
#define sym_sensors_get_detected_chips sensors_get_detected_chips
#define sym_sensors_get_features sensors_get_features
#define sym_sensors_get_subfeature sensors_get_subfeature
#define sym_sensors_get_value sensors_get_value
#else
static int (*sym_sensors_init)(FILE*);
static void (*sym_sensors_cleanup)(void);
static const sensors_chip_name* (*sym_sensors_get_detected_chips)(const sensors_chip_name*, int*);
static const sensors_feature* (*sym_sensors_get_features)(const sensors_chip_name*, int*);
static const sensors_subfeature* (*sym_sensors_get_subfeature)(const sensors_chip_name*, const sensors_feature*, sensors_subfeature_type);
static int (*sym_sensors_get_value)(const sensors_chip_name*, int, double*);
static void* dlopenHandle = NULL;
#endif /* BUILD_STATIC */
int LibSensors_init(void) {
#ifdef BUILD_STATIC
return sym_sensors_init(NULL);
#else
if (!dlopenHandle) {
/* Find the unversioned libsensors.so (symlink) and prefer that, but Debian has .so.5 and Fedora .so.4 without
matching symlinks (unless people install the -dev packages) */
dlopenHandle = dlopen("libsensors.so", RTLD_LAZY);
if (!dlopenHandle)
dlopenHandle = dlopen("libsensors.so.5", RTLD_LAZY);
if (!dlopenHandle)
dlopenHandle = dlopen("libsensors.so.4", RTLD_LAZY);
if (!dlopenHandle)
goto dlfailure;
/* Clear any errors */
dlerror();
#define resolve(symbolname) do { \
*(void **)(&sym_##symbolname) = dlsym(dlopenHandle, #symbolname); \
if (!sym_##symbolname || dlerror() != NULL) \
goto dlfailure; \
} while(0)
resolve(sensors_init);
resolve(sensors_cleanup);
resolve(sensors_get_detected_chips);
resolve(sensors_get_features);
resolve(sensors_get_subfeature);
resolve(sensors_get_value);
#undef resolve
}
return sym_sensors_init(NULL);
dlfailure:
if (dlopenHandle) {
dlclose(dlopenHandle);
dlopenHandle = NULL;
}
return -1;
#endif /* BUILD_STATIC */
}
void LibSensors_cleanup(void) {
#ifdef BUILD_STATIC
sym_sensors_cleanup();
#else
if (dlopenHandle) {
sym_sensors_cleanup();
dlclose(dlopenHandle);
dlopenHandle = NULL;
}
#endif /* BUILD_STATIC */
}
int LibSensors_reload(void) {
#ifndef BUILD_STATIC
if (!dlopenHandle) {
errno = ENOTSUP;
return -1;
}
#endif /* !BUILD_STATIC */
sym_sensors_cleanup();
return sym_sensors_init(NULL);
}
static int tempDriverPriority(const sensors_chip_name* chip) {
static const struct TempDriverDefs {
const char* prefix;
int priority;
} tempDrivers[] = {
{ "coretemp", 0 },
{ "via_cputemp", 0 },
{ "cpu_thermal", 0 },
{ "k10temp", 0 },
{ "zenpower", 0 },
/* Low priority drivers */
{ "acpitz", 1 },
};
for (size_t i = 0; i < ARRAYSIZE(tempDrivers); i++)
if (String_eq(chip->prefix, tempDrivers[i].prefix))
return tempDrivers[i].priority;
return -1;
}
void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int existingCPUs, unsigned int activeCPUs) {
assert(existingCPUs > 0 && existingCPUs < 16384);
double data[existingCPUs + 1];
for (size_t i = 0; i < existingCPUs + 1; i++)
data[i] = NAN;
#ifndef BUILD_STATIC
if (!dlopenHandle)
goto out;
#endif /* !BUILD_STATIC */
unsigned int coreTempCount = 0;
int topPriority = 99;
int n = 0;
for (const sensors_chip_name* chip = sym_sensors_get_detected_chips(NULL, &n); chip; chip = sym_sensors_get_detected_chips(NULL, &n)) {
const int priority = tempDriverPriority(chip);
if (priority < 0)
continue;
if (priority > topPriority)
continue;
if (priority < topPriority) {
/* Clear data from lower priority sensor */
for (size_t i = 0; i < existingCPUs + 1; i++)
data[i] = NAN;
}
topPriority = priority;
int m = 0;
for (const sensors_feature* feature = sym_sensors_get_features(chip, &m); feature; feature = sym_sensors_get_features(chip, &m)) {
if (feature->type != SENSORS_FEATURE_TEMP)
continue;
if (!feature->name || !String_startsWith(feature->name, "temp"))
continue;
unsigned long int tempID = strtoul(feature->name + strlen("temp"), NULL, 10);
if (tempID == 0 || tempID == ULONG_MAX)
continue;
/* Feature name IDs start at 1, adjust to start at 0 to match data indices */
tempID--;
if (tempID > existingCPUs)
continue;
const sensors_subfeature* subFeature = sym_sensors_get_subfeature(chip, feature, SENSORS_SUBFEATURE_TEMP_INPUT);
if (!subFeature)
continue;
double temp;
int r = sym_sensors_get_value(chip, subFeature->number, &temp);
if (r != 0)
continue;
/* If already set, e.g. Ryzen reporting platform temperature for each die, use the bigger one */
if (isnan(data[tempID])) {
data[tempID] = temp;
if (tempID > 0)
coreTempCount++;
} else {
data[tempID] = MAXIMUM(data[tempID], temp);
}
}
}
/* Adjust data for chips not providing a platform temperature */
if (coreTempCount + 1 == activeCPUs || coreTempCount + 1 == activeCPUs / 2) {
memmove(&data[1], &data[0], existingCPUs * sizeof(*data));
data[0] = NAN;
coreTempCount++;
/* Check for further adjustments */
}
/* Only package temperature - copy to all cores */
if (coreTempCount == 0 && !isnan(data[0])) {
for (unsigned int i = 1; i <= existingCPUs; i++)
data[i] = data[0];
/* No further adjustments */
goto out;
}
/* No package temperature - set to max core temperature */
if (isnan(data[0]) && coreTempCount != 0) {
double maxTemp = NAN;
for (unsigned int i = 1; i <= existingCPUs; i++) {
if (isnan(data[i]))
continue;
maxTemp = MAXIMUM(maxTemp, data[i]);
}
data[0] = maxTemp;
/* Check for further adjustments */
}
/* Only temperature for core 0, maybe Ryzen - copy to all other cores */
if (coreTempCount == 1 && !isnan(data[1])) {
for (unsigned int i = 2; i <= existingCPUs; i++)
data[i] = data[1];
/* No further adjustments */
goto out;
}
/* Half the temperatures, probably HT/SMT - copy to second half */
const unsigned int delta = activeCPUs / 2;
if (coreTempCount == delta) {
memcpy(&data[delta + 1], &data[1], delta * sizeof(*data));
/* No further adjustments */
goto out;
}
out:
for (unsigned int i = 0; i <= existingCPUs; i++)
cpus[i].temperature = data[i];
}
#endif /* HAVE_SENSORS_SENSORS_H */