ohmu
·
2026-10-15
1#include "cpu.h"
2#include "common/debug.h"
3#include "common/processing.h"
4#include "common/strutil.h"
5#include <kstat.h>
6
7static const char* detectCPUTempByKstat(const FFCPUOptions* options, kstat_ctl_t* kc, FFCPUResult* cpu) {
8 const char* possibleModules[] = { "temperature", "cpu_temp", "acpi_thermal", nullptr };
9
10 if (options->tempSensor.length > 0) {
11 possibleModules[0] = options->tempSensor.chars;
12 possibleModules[1] = nullptr;
13 }
14
15 for (int i = 0; possibleModules[i] != nullptr; i++) {
16 kstat_t* ks = kstat_lookup(kc, possibleModules[i], -1, nullptr);
17 if (ks && kstat_read(kc, ks, nullptr) >= 0) {
18 kstat_named_t* kn = kstat_data_lookup(ks, "temperature");
19 if (kn) {
20 switch (kn->data_type) {
21 case KSTAT_DATA_INT32:
22 cpu->temperature = (float) kn->value.i32;
23 return nullptr;
24 case KSTAT_DATA_UINT32:
25 cpu->temperature = (float) kn->value.ui32;
26 return nullptr;
27 case KSTAT_DATA_FLOAT:
28 cpu->temperature = kn->value.f;
29 return nullptr;
30 }
31 }
32 }
33 }
34
35 FF_DEBUG("Failed to find CPU temperature using kstat");
36 return "Failed to find CPU temperature using kstat";
37}
38
39static const char* detectCPUTempByIpmiTool(FFCPUResult* cpu) {
40 FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
41 const char* error = ffProcessAppendStdOut(&buffer, (char* const[]) { "ipmitool", "-c", "sdr", "list", nullptr });
42
43 if (error) {
44 return error;
45 }
46
47 char* line = nullptr;
48 size_t len = 0;
49 while (ffStrbufGetline(&line, &len, &buffer)) {
50 if (sscanf(line, "CPU%*d Temp,%lf,degrees C,ok", &cpu->temperature) == 1) {
51 return nullptr;
52 }
53 }
54
55 FF_DEBUG("ipmitool sdr list failed to find CPU temperature");
56 return "ipmitool sdr list failed to find CPU temperature";
57}
58
59static inline void kstatFreeWrap(kstat_ctl_t** pkc) {
60 assert(pkc);
61 if (*pkc) {
62 kstat_close(*pkc);
63 }
64}
65
66static inline uint16_t countTypeId(kstat_ctl_t* kc, const char* type) {
67 uint64_t low = 0, high = 0;
68 for (kstat_t* ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
69 if (ffStrStartsWith(ksp->ks_module, "cpu_info")) {
70 if (kstat_read(kc, ksp, nullptr) < 0) {
71 continue;
72 }
73
74 kstat_named_t* stat = kstat_data_lookup(ksp, type);
75 if (!stat) {
76 continue;
77 }
78
79 uint32_t id = 0;
80 switch (stat->data_type) {
81#ifdef _INT64_TYPE
82 case KSTAT_DATA_INT64:
83 case KSTAT_DATA_UINT64:
84 id = (uint32_t) stat->value.ui64;
85 break;
86#endif
87 case KSTAT_DATA_INT32:
88 case KSTAT_DATA_UINT32:
89 id = stat->value.ui32;
90 break;
91 default:
92 continue;
93 }
94 if (__builtin_expect(id > 64, false)) {
95 high |= 1ULL << (id - 64);
96 } else {
97 low |= 1ULL << id;
98 }
99 }
100 }
101 return (uint16_t) (__builtin_popcountll(low) + __builtin_popcountll(high));
102}
103
104const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) {
105 [[gnu::cleanup(kstatFreeWrap)]] kstat_ctl_t* kc = kstat_open();
106 if (!kc) {
107 FF_DEBUG("kstat_open() failed");
108 return "kstat_open() failed";
109 }
110
111 kstat_t* ks = kstat_lookup(kc, "cpu_info", -1, nullptr);
112 if (!ks) {
113 FF_DEBUG("kstat_lookup() failed");
114 return "kstat_lookup() failed";
115 }
116
117 if (kstat_read(kc, ks, nullptr) < 0) {
118 FF_DEBUG("kstat_read() failed");
119 return "kstat_read() failed";
120 }
121
122 {
123 kstat_named_t* kn = kstat_data_lookup(ks, "brand");
124 if (kn) {
125 ffStrbufSetNS(&cpu->name, KSTAT_NAMED_STR_BUFLEN(kn) - 1, KSTAT_NAMED_STR_PTR(kn));
126 }
127 }
128 {
129 kstat_named_t* kn = kstat_data_lookup(ks, "vendor_id");
130 if (kn) {
131 ffStrbufSetNS(&cpu->vendor, KSTAT_NAMED_STR_BUFLEN(kn) - 1, KSTAT_NAMED_STR_PTR(kn));
132 }
133 }
134 ffCPUDetectByCpuid(cpu);
135 {
136 kstat_named_t* kn = kstat_data_lookup(ks, "clock_MHz");
137 if (kn && kn->value.ui32 > cpu->frequencyBase) {
138 cpu->frequencyBase = kn->value.ui32;
139 }
140 }
141
142 ks = kstat_lookup(kc, "unix", -1, "system_misc");
143 if (ks && kstat_read(kc, ks, nullptr) >= 0) {
144 kstat_named_t* kn = kstat_data_lookup(ks, "ncpus");
145 if (kn) {
146 cpu->coresLogical = cpu->coresOnline = (uint16_t) kn->value.ui32;
147 }
148 }
149
150 cpu->packages = countTypeId(kc, "chip_id");
151 cpu->coresPhysical = countTypeId(kc, "core_id");
152
153 if (options->temp) {
154 if (detectCPUTempByKstat(options, kc, cpu) != nullptr) {
155 detectCPUTempByIpmiTool(cpu);
156 }
157 }
158
159 return nullptr;
160}