1
2
3
4
5 package oshi.hardware.platform.unix.openbsd;
6
7 import static oshi.jna.platform.unix.OpenBsdLibc.CP_IDLE;
8 import static oshi.jna.platform.unix.OpenBsdLibc.CP_INTR;
9 import static oshi.jna.platform.unix.OpenBsdLibc.CP_NICE;
10 import static oshi.jna.platform.unix.OpenBsdLibc.CP_SYS;
11 import static oshi.jna.platform.unix.OpenBsdLibc.CP_USER;
12 import static oshi.jna.platform.unix.OpenBsdLibc.CTL_HW;
13 import static oshi.jna.platform.unix.OpenBsdLibc.CTL_KERN;
14 import static oshi.jna.platform.unix.OpenBsdLibc.HW_CPUSPEED;
15 import static oshi.jna.platform.unix.OpenBsdLibc.HW_MACHINE;
16 import static oshi.jna.platform.unix.OpenBsdLibc.HW_MODEL;
17 import static oshi.jna.platform.unix.OpenBsdLibc.KERN_CPTIME;
18 import static oshi.jna.platform.unix.OpenBsdLibc.KERN_CPTIME2;
19 import static oshi.util.Memoizer.defaultExpiration;
20 import static oshi.util.Memoizer.memoize;
21
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.LinkedHashSet;
27 import java.util.List;
28 import java.util.Locale;
29 import java.util.Map;
30 import java.util.Set;
31 import java.util.function.Supplier;
32 import java.util.regex.Matcher;
33 import java.util.regex.Pattern;
34
35 import com.sun.jna.Memory;
36 import com.sun.jna.Native;
37
38 import oshi.annotation.concurrent.ThreadSafe;
39 import oshi.hardware.CentralProcessor.ProcessorCache.Type;
40 import oshi.hardware.common.AbstractCentralProcessor;
41 import oshi.jna.platform.unix.OpenBsdLibc;
42 import oshi.util.ExecutingCommand;
43 import oshi.util.ParseUtil;
44 import oshi.util.platform.unix.openbsd.OpenBsdSysctlUtil;
45 import oshi.util.tuples.Pair;
46 import oshi.util.tuples.Quartet;
47 import oshi.util.tuples.Triplet;
48
49
50
51
52 @ThreadSafe
53 public class OpenBsdCentralProcessor extends AbstractCentralProcessor {
54 private final Supplier<Pair<Long, Long>> vmStats = memoize(OpenBsdCentralProcessor::queryVmStats,
55 defaultExpiration());
56 private static final Pattern DMESG_CPU = Pattern.compile("cpu(\\d+): smt (\\d+), core (\\d+), package (\\d+)");
57
58 @Override
59 protected ProcessorIdentifier queryProcessorId() {
60 String cpuVendor = OpenBsdSysctlUtil.sysctl("machdep.cpuvendor", "");
61 int[] mib = new int[2];
62 mib[0] = CTL_HW;
63 mib[1] = HW_MODEL;
64 String cpuName = OpenBsdSysctlUtil.sysctl(mib, "");
65
66 int cpuid = ParseUtil.hexStringToInt(OpenBsdSysctlUtil.sysctl("machdep.cpuid", ""), 0);
67 int cpufeature = ParseUtil.hexStringToInt(OpenBsdSysctlUtil.sysctl("machdep.cpufeature", ""), 0);
68 Triplet<Integer, Integer, Integer> cpu = cpuidToFamilyModelStepping(cpuid);
69 String cpuFamily = cpu.getA().toString();
70 String cpuModel = cpu.getB().toString();
71 String cpuStepping = cpu.getC().toString();
72 long cpuFreq = ParseUtil.parseHertz(cpuName);
73 if (cpuFreq < 0) {
74 cpuFreq = queryMaxFreq();
75 }
76 mib[1] = HW_MACHINE;
77 String machine = OpenBsdSysctlUtil.sysctl(mib, "");
78 boolean cpu64bit = machine != null && machine.contains("64")
79 || ExecutingCommand.getFirstAnswer("uname -m").trim().contains("64");
80 String processorID = String.format(Locale.ROOT, "%08x%08x", cpufeature, cpuid);
81
82 return new ProcessorIdentifier(cpuVendor, cpuName, cpuFamily, cpuModel, cpuStepping, processorID, cpu64bit,
83 cpuFreq);
84 }
85
86 private static Triplet<Integer, Integer, Integer> cpuidToFamilyModelStepping(int cpuid) {
87
88 int family = cpuid >> 16 & 0xff0 | cpuid >> 8 & 0xf;
89
90 int model = cpuid >> 12 & 0xf0 | cpuid >> 4 & 0xf;
91
92 int stepping = cpuid & 0xf;
93 return new Triplet<>(family, model, stepping);
94 }
95
96 @Override
97 protected long[] queryCurrentFreq() {
98 long[] freq = new long[1];
99 int[] mib = new int[2];
100 mib[0] = CTL_HW;
101 mib[1] = HW_CPUSPEED;
102 freq[0] = OpenBsdSysctlUtil.sysctl(mib, 0L) * 1_000_000L;
103 return freq;
104 }
105
106 @Override
107 protected Quartet<List<LogicalProcessor>, List<PhysicalProcessor>, List<ProcessorCache>, List<String>> initProcessorCounts() {
108
109
110
111 Map<Integer, Integer> coreMap = new HashMap<>();
112 Map<Integer, Integer> packageMap = new HashMap<>();
113 for (String line : ExecutingCommand.runNative("dmesg")) {
114 Matcher m = DMESG_CPU.matcher(line);
115 if (m.matches()) {
116 int cpu = ParseUtil.parseIntOrDefault(m.group(1), 0);
117 coreMap.put(cpu, ParseUtil.parseIntOrDefault(m.group(3), 0));
118 packageMap.put(cpu, ParseUtil.parseIntOrDefault(m.group(4), 0));
119 }
120 }
121
122 int logicalProcessorCount = OpenBsdSysctlUtil.sysctl("hw.ncpuonline", 1);
123
124 if (logicalProcessorCount < coreMap.keySet().size()) {
125 logicalProcessorCount = coreMap.keySet().size();
126 }
127 List<LogicalProcessor> logProcs = new ArrayList<>(logicalProcessorCount);
128 for (int i = 0; i < logicalProcessorCount; i++) {
129 logProcs.add(new LogicalProcessor(i, coreMap.getOrDefault(i, 0), packageMap.getOrDefault(i, 0)));
130 }
131 Map<Integer, String> cpuMap = new HashMap<>();
132
133
134
135
136
137 Pattern p = Pattern.compile("cpu(\\\\d+).*: ((ARM|AMD|Intel|Apple).+)");
138
139 Set<ProcessorCache> caches = new HashSet<>();
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166 Pattern q = Pattern.compile("cpu(\\\\d+).*: (.+(I-|D-|L\\d+\\s)cache)");
167 Set<String> featureFlags = new LinkedHashSet<>();
168 for (String s : ExecutingCommand.runNative("dmesg")) {
169 Matcher m = p.matcher(s);
170 if (m.matches()) {
171 int coreId = ParseUtil.parseIntOrDefault(m.group(1), 0);
172 cpuMap.put(coreId, m.group(2).trim());
173 } else {
174 Matcher n = q.matcher(s);
175 if (n.matches()) {
176 for (String cacheStr : n.group(1).split(",")) {
177 ProcessorCache cache = parseCacheStr(cacheStr);
178 if (cache != null) {
179 caches.add(cache);
180 }
181 }
182 }
183 }
184 if (s.startsWith("cpu")) {
185 String[] ss = s.trim().split(": ");
186 if (ss.length == 2 && ss[1].split(",").length > 3) {
187 featureFlags.add(ss[1]);
188 }
189 }
190 }
191 List<PhysicalProcessor> physProcs = cpuMap.isEmpty() ? null : createProcListFromDmesg(logProcs, cpuMap);
192 return new Quartet<>(logProcs, physProcs, orderedProcCaches(caches), new ArrayList<>(featureFlags));
193 }
194
195 private ProcessorCache parseCacheStr(String cacheStr) {
196 String[] split = ParseUtil.whitespaces.split(cacheStr);
197 if (split.length > 3) {
198 switch (split[split.length - 1]) {
199 case "I-cache":
200 return new ProcessorCache(1, ParseUtil.getFirstIntValue(split[2]), ParseUtil.getFirstIntValue(split[1]),
201 ParseUtil.parseDecimalMemorySizeToBinary(split[0]), Type.INSTRUCTION);
202 case "D-cache":
203 return new ProcessorCache(1, ParseUtil.getFirstIntValue(split[2]), ParseUtil.getFirstIntValue(split[1]),
204 ParseUtil.parseDecimalMemorySizeToBinary(split[0]), Type.DATA);
205 default:
206 return new ProcessorCache(ParseUtil.getFirstIntValue(split[3]), ParseUtil.getFirstIntValue(split[2]),
207 ParseUtil.getFirstIntValue(split[1]), ParseUtil.parseDecimalMemorySizeToBinary(split[0]),
208 Type.UNIFIED);
209 }
210 }
211 return null;
212 }
213
214
215
216
217
218
219 @Override
220 protected long queryContextSwitches() {
221 return vmStats.get().getA();
222 }
223
224
225
226
227
228
229 @Override
230 protected long queryInterrupts() {
231 return vmStats.get().getB();
232 }
233
234 private static Pair<Long, Long> queryVmStats() {
235 long contextSwitches = 0L;
236 long interrupts = 0L;
237 List<String> vmstat = ExecutingCommand.runNative("vmstat -s");
238 for (String line : vmstat) {
239 if (line.endsWith("cpu context switches")) {
240 contextSwitches = ParseUtil.getFirstIntValue(line);
241 } else if (line.endsWith("interrupts")) {
242 interrupts = ParseUtil.getFirstIntValue(line);
243 }
244 }
245 return new Pair<>(contextSwitches, interrupts);
246 }
247
248
249
250
251
252
253 @Override
254 protected long[] querySystemCpuLoadTicks() {
255 long[] ticks = new long[TickType.values().length];
256 int[] mib = new int[2];
257 mib[0] = CTL_KERN;
258 mib[1] = KERN_CPTIME;
259 try (Memory m = OpenBsdSysctlUtil.sysctl(mib)) {
260
261 long[] cpuTicks = cpTimeToTicks(m, false);
262 if (cpuTicks.length >= 5) {
263 ticks[TickType.USER.getIndex()] = cpuTicks[CP_USER];
264 ticks[TickType.NICE.getIndex()] = cpuTicks[CP_NICE];
265 ticks[TickType.SYSTEM.getIndex()] = cpuTicks[CP_SYS];
266 int offset = cpuTicks.length > 5 ? 1 : 0;
267 ticks[TickType.IRQ.getIndex()] = cpuTicks[CP_INTR + offset];
268 ticks[TickType.IDLE.getIndex()] = cpuTicks[CP_IDLE + offset];
269 }
270 }
271 return ticks;
272 }
273
274
275
276
277
278
279 @Override
280 protected long[][] queryProcessorCpuLoadTicks() {
281 long[][] ticks = new long[getLogicalProcessorCount()][TickType.values().length];
282 int[] mib = new int[3];
283 mib[0] = CTL_KERN;
284 mib[1] = KERN_CPTIME2;
285 for (int cpu = 0; cpu < getLogicalProcessorCount(); cpu++) {
286 mib[2] = cpu;
287 try (Memory m = OpenBsdSysctlUtil.sysctl(mib)) {
288
289 long[] cpuTicks = cpTimeToTicks(m, true);
290 if (cpuTicks.length >= 5) {
291 ticks[cpu][TickType.USER.getIndex()] = cpuTicks[CP_USER];
292 ticks[cpu][TickType.NICE.getIndex()] = cpuTicks[CP_NICE];
293 ticks[cpu][TickType.SYSTEM.getIndex()] = cpuTicks[CP_SYS];
294 int offset = cpuTicks.length > 5 ? 1 : 0;
295 ticks[cpu][TickType.IRQ.getIndex()] = cpuTicks[CP_INTR + offset];
296 ticks[cpu][TickType.IDLE.getIndex()] = cpuTicks[CP_IDLE + offset];
297 }
298 }
299 }
300 return ticks;
301 }
302
303
304
305
306
307
308
309
310
311
312
313
314 private static long[] cpTimeToTicks(Memory m, boolean force64bit) {
315 long longBytes = force64bit ? 8L : Native.LONG_SIZE;
316 int arraySize = m == null ? 0 : (int) (m.size() / longBytes);
317 if (force64bit && m != null) {
318 return m.getLongArray(0, arraySize);
319 }
320 long[] ticks = new long[arraySize];
321 for (int i = 0; i < arraySize; i++) {
322 ticks[i] = m.getNativeLong(i * longBytes).longValue();
323 }
324 return ticks;
325 }
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342 @Override
343 public double[] getSystemLoadAverage(int nelem) {
344 if (nelem < 1 || nelem > 3) {
345 throw new IllegalArgumentException("Must include from one to three elements.");
346 }
347 double[] average = new double[nelem];
348 int retval = OpenBsdLibc.INSTANCE.getloadavg(average, nelem);
349 if (retval < nelem) {
350 Arrays.fill(average, -1d);
351 }
352 return average;
353 }
354 }