ohmu
·
2026-10-15
1#include "cpu.h"
2#include "common/debug.h"
3#include "common/sysctl.h"
4#include "common/strutil.h"
5
6#include <errno.h>
7#include <string.h>
8#include <sys/param.h>
9#if __has_include(<sys/cpuset.h>)
10 #include <sys/cpuset.h>
11 #define FF_HAVE_CPUSET 1
12#endif
13
14static const char* detectCpuTemp(const FFCPUOptions* options, double* current) {
15 int temp;
16 if (options->tempSensor.length > 0) {
17 temp = ffSysctlGetInt(options->tempSensor.chars, -999999);
18 if (temp == -999999) {
19 FF_DEBUG("sysctlbyname(%s) failed: %s", options->tempSensor.chars, strerror(errno));
20 return "ffSysctlGetInt(options->tempSensor) failed";
21 }
22 } else {
23 temp = ffSysctlGetInt("dev.cpu.0.temperature", -999999);
24 if (temp == -999999) {
25 // Thermal zone temperature
26 temp = ffSysctlGetInt("hw.acpi.thermal.tz0.temperature", -999999);
27 if (temp == -999999) {
28 FF_DEBUG("Neither sysctlbyname(dev.cpu.0.temperature) nor sysctlbyname(hw.acpi.thermal.tz0.temperature) is readable: %s", strerror(errno));
29 return "ffSysctlGetInt(\"dev.cpu.0.temperature\" or \"hw.acpi.thermal.tz0.temperature\") failed";
30 }
31 }
32 }
33
34 // In tenth of degrees Kelvin
35 *current = (double) temp / 10 - 273.15;
36 return nullptr;
37}
38
39const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) {
40 const char* modelError = ffSysctlGetString("hw.model", &cpu->name);
41 if (modelError != nullptr) {
42 FF_DEBUG("sysctlbyname(hw.model) failed: %s", modelError);
43 return "sysctlbyname(hw.model) failed";
44 }
45
46 cpu->coresLogical = (uint16_t) ffSysctlGetInt("hw.ncpu", 1);
47 cpu->coresPhysical = (uint16_t) ffSysctlGetInt("kern.smp.cores", 0);
48 cpu->coresOnline = (uint16_t) ffSysctlGetInt("kern.smp.cpus", cpu->coresLogical);
49
50 FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
51 if (ffSysctlGetString("kern.sched.topology_spec", &buffer) == nullptr && buffer.length > 0) {
52 // <groups>
53 // <group level="1" cache-level="3">
54 // <cpu count="4" mask="f,0,0,0">0, 1, 2, 3</cpu>
55 // <children>
56 // <group level="2" cache-level="2">
57 // <cpu count="2" mask="3,0,0,0">0, 1</cpu>
58 // <flags><flag name="THREAD">THREAD group</flag><flag name="SMT">SMT group</flag></flags>
59 // </group>
60 // <group level="2" cache-level="2">
61 // <cpu count="2" mask="c,0,0,0">2, 3</cpu>
62 // <flags><flag name="THREAD">THREAD group</flag><flag name="SMT">SMT group</flag></flags>
63 // </group>
64 // </children>
65 // </group>
66 // </groups>
67 for (char* p = buffer.chars; (p = strstr(p, "\n </group>\n")); ++p) {
68 cpu->packages++;
69 }
70 }
71
72#if FF_HAVE_CPUSET && (__x86_64__ || __i386__)
73 // Bind current process to the first two cores, which is *usually* a performance core
74 cpuset_t currentCPU;
75 CPU_ZERO(¤tCPU);
76 CPU_SET(1, ¤tCPU);
77 CPU_SET(2, ¤tCPU);
78 cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, -1, sizeof(cpuset_t), ¤tCPU);
79#endif
80
81 ffCPUDetectByCpuid(cpu);
82
83 uint32_t clockrate = (uint32_t) ffSysctlGetInt("hw.clockrate", 0);
84 if (clockrate > cpu->frequencyBase) {
85 cpu->frequencyBase = clockrate;
86 }
87
88 for (uint16_t i = 0; i < cpu->coresLogical; ++i) {
89 ffStrbufClear(&buffer);
90 char key[32];
91 snprintf(key, sizeof(key), "dev.cpu.%u.freq_levels", i);
92 if (ffSysctlGetString(key, &buffer) == nullptr) {
93 if (buffer.length == 0) {
94 continue;
95 }
96
97 // MHz/Watts pairs like: 2501/32000 2187/27125 2000/24000
98 uint32_t fmax = (uint32_t) strtoul(buffer.chars, nullptr, 10);
99 if (cpu->frequencyMax < fmax) {
100 cpu->frequencyMax = fmax;
101 }
102 } else {
103 break;
104 }
105 }
106
107 cpu->temperature = FF_CPU_TEMP_UNSET;
108
109 if (options->temp) {
110 detectCpuTemp(options, &cpu->temperature);
111 }
112
113 cpu->numaNodes = (uint16_t) ffSysctlGetInt("vm.ndomains", 0);
114
115 return nullptr;
116}