#include "cpu.h" #include "common/processing.h" #include "common/strutil.h" #include static const char* detectCPUTempByKstat(const FFCPUOptions* options, kstat_ctl_t* kc, FFCPUResult* cpu) { const char* possibleModules[] = { "temperature", "cpu_temp", "acpi_thermal", NULL }; if (options->tempSensor.length > 0) { possibleModules[0] = options->tempSensor.chars; possibleModules[1] = NULL; } for (int i = 0; possibleModules[i] != NULL; i++) { kstat_t* ks = kstat_lookup(kc, possibleModules[i], -1, NULL); if (ks && kstat_read(kc, ks, NULL) >= 0) { kstat_named_t* kn = kstat_data_lookup(ks, "temperature"); if (kn) { switch (kn->data_type) { case KSTAT_DATA_INT32: cpu->temperature = (float) kn->value.i32; return NULL; case KSTAT_DATA_UINT32: cpu->temperature = (float) kn->value.ui32; return NULL; case KSTAT_DATA_FLOAT: cpu->temperature = kn->value.f; return NULL; } } } } return "Failed to find CPU temperature using kstat"; } static const char* detectCPUTempByIpmiTool(FFCPUResult* cpu) { FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); const char* error = ffProcessAppendStdOut(&buffer, (char* const[]) { "ipmitool", "-c", "sdr", "list", NULL }); if (error) { return error; } char* line = NULL; size_t len = 0; while (ffStrbufGetline(&line, &len, &buffer)) { if (sscanf(line, "CPU%*d Temp,%lf,degrees C,ok", &cpu->temperature) == 1) { return NULL; } } return "ipmitool sdr list failed to find CPU temperature"; } static inline void kstatFreeWrap(kstat_ctl_t** pkc) { assert(pkc); if (*pkc) { kstat_close(*pkc); } } static inline uint16_t countTypeId(kstat_ctl_t* kc, const char* type) { uint64_t low = 0, high = 0; for (kstat_t* ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { if (ffStrStartsWith(ksp->ks_module, "cpu_info")) { if (kstat_read(kc, ksp, NULL) < 0) { continue; } kstat_named_t* stat = kstat_data_lookup(ksp, type); if (!stat) { continue; } uint32_t id = 0; switch (stat->data_type) { #ifdef _INT64_TYPE case KSTAT_DATA_INT64: case KSTAT_DATA_UINT64: id = (uint32_t) stat->value.ui64; break; #endif case KSTAT_DATA_INT32: case KSTAT_DATA_UINT32: id = stat->value.ui32; break; default: continue; } if (__builtin_expect(id > 64, false)) { high |= 1ULL << (id - 64); } else { low |= 1ULL << id; } } } return (uint16_t) (__builtin_popcountll(low) + __builtin_popcountll(high)); } const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) { FF_A_CLEANUP(kstatFreeWrap) kstat_ctl_t* kc = kstat_open(); if (!kc) { return "kstat_open() failed"; } kstat_t* ks = kstat_lookup(kc, "cpu_info", -1, NULL); if (!ks) { return "kstat_lookup() failed"; } if (kstat_read(kc, ks, NULL) < 0) { return "kstat_read() failed"; } { kstat_named_t* kn = kstat_data_lookup(ks, "brand"); if (kn) { ffStrbufSetNS(&cpu->name, KSTAT_NAMED_STR_BUFLEN(kn) - 1, KSTAT_NAMED_STR_PTR(kn)); } } { kstat_named_t* kn = kstat_data_lookup(ks, "vendor_id"); if (kn) { ffStrbufSetNS(&cpu->vendor, KSTAT_NAMED_STR_BUFLEN(kn) - 1, KSTAT_NAMED_STR_PTR(kn)); } } ffCPUDetectByCpuid(cpu); { kstat_named_t* kn = kstat_data_lookup(ks, "clock_MHz"); if (kn && kn->value.ui32 > cpu->frequencyBase) { cpu->frequencyBase = kn->value.ui32; } } ks = kstat_lookup(kc, "unix", -1, "system_misc"); if (ks && kstat_read(kc, ks, NULL) >= 0) { kstat_named_t* kn = kstat_data_lookup(ks, "ncpus"); if (kn) { cpu->coresLogical = cpu->coresOnline = (uint16_t) kn->value.ui32; } } cpu->packages = countTypeId(kc, "chip_id"); cpu->coresPhysical = countTypeId(kc, "core_id"); if (options->temp) { if (detectCPUTempByKstat(options, kc, cpu) != NULL) { detectCPUTempByIpmiTool(cpu); } } return NULL; }