1
2
3
4
5 package oshi.hardware.platform.unix.freebsd;
6
7 import java.util.ArrayList;
8 import java.util.HashMap;
9 import java.util.HashSet;
10 import java.util.List;
11 import java.util.Locale;
12 import java.util.Map;
13 import java.util.Set;
14 import java.util.regex.Matcher;
15 import java.util.regex.Pattern;
16
17 import org.slf4j.Logger;
18 import org.slf4j.LoggerFactory;
19
20 import com.sun.jna.Memory;
21 import com.sun.jna.Native;
22 import com.sun.jna.platform.unix.LibCAPI.size_t;
23
24 import oshi.annotation.concurrent.ThreadSafe;
25 import oshi.hardware.CentralProcessor.ProcessorCache.Type;
26 import oshi.hardware.common.AbstractCentralProcessor;
27 import oshi.jna.ByRef.CloseableSizeTByReference;
28 import oshi.jna.platform.unix.FreeBsdLibc;
29 import oshi.jna.platform.unix.FreeBsdLibc.CpTime;
30 import oshi.util.ExecutingCommand;
31 import oshi.util.FileUtil;
32 import oshi.util.ParseUtil;
33 import oshi.util.platform.unix.freebsd.BsdSysctlUtil;
34 import oshi.util.tuples.Quartet;
35
36
37
38
39 @ThreadSafe
40 final class FreeBsdCentralProcessor extends AbstractCentralProcessor {
41
42 private static final Logger LOG = LoggerFactory.getLogger(FreeBsdCentralProcessor.class);
43
44
45 private static final Pattern CPUMASK = Pattern
46 .compile(".*<cpu\\s.*mask=\"(\\p{XDigit}+(,\\p{XDigit}+)*)\".*>.*</cpu>.*");
47
48 private static final long CPTIME_SIZE;
49 static {
50 try (CpTime cpTime = new CpTime()) {
51 CPTIME_SIZE = cpTime.size();
52 }
53 }
54
55 @Override
56 protected ProcessorIdentifier queryProcessorId() {
57 final Pattern identifierPattern = Pattern
58 .compile("Origin=\"([^\"]*)\".*Id=(\\S+).*Family=(\\S+).*Model=(\\S+).*Stepping=(\\S+).*");
59 final Pattern featuresPattern = Pattern.compile("Features=(\\S+)<.*");
60
61 String cpuVendor = "";
62 String cpuName = BsdSysctlUtil.sysctl("hw.model", "");
63 String cpuFamily = "";
64 String cpuModel = "";
65 String cpuStepping = "";
66 String processorID;
67 long cpuFreq = BsdSysctlUtil.sysctl("hw.clockrate", 0L) * 1_000_000L;
68
69 boolean cpu64bit;
70
71
72
73 long processorIdBits = 0L;
74 List<String> cpuInfo = FileUtil.readFile("/var/run/dmesg.boot");
75 for (String line : cpuInfo) {
76 line = line.trim();
77
78 if (line.startsWith("CPU:") && cpuName.isEmpty()) {
79 cpuName = line.replace("CPU:", "").trim();
80 } else if (line.startsWith("Origin=")) {
81 Matcher m = identifierPattern.matcher(line);
82 if (m.matches()) {
83 cpuVendor = m.group(1);
84 processorIdBits |= Long.decode(m.group(2));
85 cpuFamily = Integer.decode(m.group(3)).toString();
86 cpuModel = Integer.decode(m.group(4)).toString();
87 cpuStepping = Integer.decode(m.group(5)).toString();
88 }
89 } else if (line.startsWith("Features=")) {
90 Matcher m = featuresPattern.matcher(line);
91 if (m.matches()) {
92 processorIdBits |= Long.decode(m.group(1)) << 32;
93 }
94
95 break;
96 }
97 }
98 cpu64bit = ExecutingCommand.getFirstAnswer("uname -m").trim().contains("64");
99 processorID = getProcessorIDfromDmiDecode(processorIdBits);
100
101 return new ProcessorIdentifier(cpuVendor, cpuName, cpuFamily, cpuModel, cpuStepping, processorID, cpu64bit,
102 cpuFreq);
103 }
104
105 @Override
106 protected Quartet<List<LogicalProcessor>, List<PhysicalProcessor>, List<ProcessorCache>, List<String>> initProcessorCounts() {
107 List<LogicalProcessor> logProcs = parseTopology();
108
109 if (logProcs.isEmpty()) {
110 logProcs.add(new LogicalProcessor(0, 0, 0));
111 }
112 Map<Integer, String> dmesg = new HashMap<>();
113
114 Pattern normal = Pattern.compile("cpu(\\\\d+): (.+) on .*");
115
116 Pattern hybrid = Pattern.compile("CPU\\\\s*(\\\\d+): (.+) affinity:.*");
117 List<String> featureFlags = new ArrayList<>();
118 boolean readingFlags = false;
119 for (String s : FileUtil.readFile("/var/run/dmesg.boot")) {
120 Matcher h = hybrid.matcher(s);
121 if (h.matches()) {
122 int coreId = ParseUtil.parseIntOrDefault(h.group(1), 0);
123
124 dmesg.put(coreId, h.group(2).trim());
125 } else {
126 Matcher n = normal.matcher(s);
127 if (n.matches()) {
128 int coreId = ParseUtil.parseIntOrDefault(n.group(1), 0);
129
130 dmesg.putIfAbsent(coreId, n.group(2).trim());
131 }
132 }
133 if (s.contains("Origin=")) {
134 readingFlags = true;
135 } else if (readingFlags) {
136 if (s.startsWith(" ")) {
137 featureFlags.add(s.trim());
138 } else {
139 readingFlags = false;
140 }
141 }
142 }
143 List<PhysicalProcessor> physProcs = dmesg.isEmpty() ? null : createProcListFromDmesg(logProcs, dmesg);
144 List<ProcessorCache> caches = getCacheInfoFromLscpu();
145 return new Quartet<>(logProcs, physProcs, caches, featureFlags);
146 }
147
148 private List<ProcessorCache> getCacheInfoFromLscpu() {
149 Set<ProcessorCache> caches = new HashSet<>();
150 for (String checkLine : ExecutingCommand.runNative("lscpu")) {
151 if (checkLine.contains("L1d cache:")) {
152 caches.add(new ProcessorCache(1, 0, 0,
153 ParseUtil.parseDecimalMemorySizeToBinary(checkLine.split(":")[1].trim()), Type.DATA));
154 } else if (checkLine.contains("L1i cache:")) {
155 caches.add(new ProcessorCache(1, 0, 0,
156 ParseUtil.parseDecimalMemorySizeToBinary(checkLine.split(":")[1].trim()), Type.INSTRUCTION));
157 } else if (checkLine.contains("L2 cache:")) {
158 caches.add(new ProcessorCache(2, 0, 0,
159 ParseUtil.parseDecimalMemorySizeToBinary(checkLine.split(":")[1].trim()), Type.UNIFIED));
160 } else if (checkLine.contains("L3 cache:")) {
161 caches.add(new ProcessorCache(3, 0, 0,
162 ParseUtil.parseDecimalMemorySizeToBinary(checkLine.split(":")[1].trim()), Type.UNIFIED));
163 }
164 }
165 return orderedProcCaches(caches);
166 }
167
168 private static List<LogicalProcessor> parseTopology() {
169 String[] topology = BsdSysctlUtil.sysctl("kern.sched.topology_spec", "").split("[\\n\\r]");
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201 long group1 = 1L;
202 List<Long> group2 = new ArrayList<>();
203 List<Long> group3 = new ArrayList<>();
204 int groupLevel = 0;
205 for (String topo : topology) {
206 if (topo.contains("<group level=")) {
207 groupLevel++;
208 } else if (topo.contains("</group>")) {
209 groupLevel--;
210 } else if (topo.contains("<cpu")) {
211
212 Matcher m = CPUMASK.matcher(topo);
213 if (m.matches()) {
214
215 String csvMatch = m.group(1);
216 String[] csvTokens = csvMatch.split(",");
217 String firstVal = csvTokens[0];
218
219
220
221 long parsedVal = ParseUtil.hexStringToLong(firstVal, 0);
222 switch (groupLevel) {
223 case 1:
224 group1 = parsedVal;
225 break;
226 case 2:
227 group2.add(parsedVal);
228 break;
229 case 3:
230 group3.add(parsedVal);
231 break;
232 default:
233 break;
234 }
235 }
236 }
237 }
238 return matchBitmasks(group1, group2, group3);
239 }
240
241 private static List<LogicalProcessor> matchBitmasks(long group1, List<Long> group2, List<Long> group3) {
242 List<LogicalProcessor> logProcs = new ArrayList<>();
243
244 int lowBit = Long.numberOfTrailingZeros(group1);
245 int hiBit = 63 - Long.numberOfLeadingZeros(group1);
246
247 for (int i = lowBit; i <= hiBit; i++) {
248 if ((group1 & (1L << i)) > 0) {
249 int numaNode = 0;
250 LogicalProcessor logProc = new LogicalProcessor(i, getMatchingBitmask(group3, i),
251 getMatchingBitmask(group2, i), numaNode);
252 logProcs.add(logProc);
253 }
254 }
255 return logProcs;
256 }
257
258 private static int getMatchingBitmask(List<Long> bitmasks, int lp) {
259 for (int j = 0; j < bitmasks.size(); j++) {
260 if ((bitmasks.get(j).longValue() & (1L << lp)) != 0) {
261 return j;
262 }
263 }
264 return 0;
265 }
266
267 @Override
268 public long[] querySystemCpuLoadTicks() {
269 long[] ticks = new long[TickType.values().length];
270 try (CpTime cpTime = new CpTime()) {
271 BsdSysctlUtil.sysctl("kern.cp_time", cpTime);
272 ticks[TickType.USER.getIndex()] = cpTime.cpu_ticks[FreeBsdLibc.CP_USER];
273 ticks[TickType.NICE.getIndex()] = cpTime.cpu_ticks[FreeBsdLibc.CP_NICE];
274 ticks[TickType.SYSTEM.getIndex()] = cpTime.cpu_ticks[FreeBsdLibc.CP_SYS];
275 ticks[TickType.IRQ.getIndex()] = cpTime.cpu_ticks[FreeBsdLibc.CP_INTR];
276 ticks[TickType.IDLE.getIndex()] = cpTime.cpu_ticks[FreeBsdLibc.CP_IDLE];
277 }
278 return ticks;
279 }
280
281 @Override
282 public long[] queryCurrentFreq() {
283 long[] freq = new long[1];
284 freq[0] = BsdSysctlUtil.sysctl("dev.cpu.0.freq", -1L);
285 if (freq[0] > 0) {
286
287 freq[0] *= 1_000_000L;
288 } else {
289 freq[0] = BsdSysctlUtil.sysctl("machdep.tsc_freq", -1L);
290 }
291 return freq;
292 }
293
294 @Override
295 public long queryMaxFreq() {
296 long max = -1L;
297 String freqLevels = BsdSysctlUtil.sysctl("dev.cpu.0.freq_levels", "");
298
299 for (String s : ParseUtil.whitespaces.split(freqLevels)) {
300 long freq = ParseUtil.parseLongOrDefault(s.split("/")[0], -1L);
301 if (max < freq) {
302 max = freq;
303 }
304 }
305 if (max > 0) {
306
307 max *= 1_000_000;
308 } else {
309 max = BsdSysctlUtil.sysctl("machdep.tsc_freq", -1L);
310 }
311 return max;
312 }
313
314 @Override
315 public double[] getSystemLoadAverage(int nelem) {
316 if (nelem < 1 || nelem > 3) {
317 throw new IllegalArgumentException("Must include from one to three elements.");
318 }
319 double[] average = new double[nelem];
320 int retval = FreeBsdLibc.INSTANCE.getloadavg(average, nelem);
321 if (retval < nelem) {
322 for (int i = Math.max(retval, 0); i < average.length; i++) {
323 average[i] = -1d;
324 }
325 }
326 return average;
327 }
328
329 @Override
330 public long[][] queryProcessorCpuLoadTicks() {
331 long[][] ticks = new long[getLogicalProcessorCount()][TickType.values().length];
332
333
334 long arraySize = CPTIME_SIZE * getLogicalProcessorCount();
335 try (Memory p = new Memory(arraySize);
336 CloseableSizeTByReference oldlenp = new CloseableSizeTByReference(arraySize)) {
337 String name = "kern.cp_times";
338
339 if (0 != FreeBsdLibc.INSTANCE.sysctlbyname(name, p, oldlenp, null, size_t.ZERO)) {
340 LOG.error("Failed sysctl call: {}, Error code: {}", name, Native.getLastError());
341 return ticks;
342 }
343
344 for (int cpu = 0; cpu < getLogicalProcessorCount(); cpu++) {
345 ticks[cpu][TickType.USER.getIndex()] = p
346 .getLong(CPTIME_SIZE * cpu + FreeBsdLibc.CP_USER * FreeBsdLibc.UINT64_SIZE);
347 ticks[cpu][TickType.NICE.getIndex()] = p
348 .getLong(CPTIME_SIZE * cpu + FreeBsdLibc.CP_NICE * FreeBsdLibc.UINT64_SIZE);
349 ticks[cpu][TickType.SYSTEM.getIndex()] = p
350 .getLong(CPTIME_SIZE * cpu + FreeBsdLibc.CP_SYS * FreeBsdLibc.UINT64_SIZE);
351 ticks[cpu][TickType.IRQ.getIndex()] = p
352 .getLong(CPTIME_SIZE * cpu + FreeBsdLibc.CP_INTR * FreeBsdLibc.UINT64_SIZE);
353 ticks[cpu][TickType.IDLE.getIndex()] = p
354 .getLong(CPTIME_SIZE * cpu + FreeBsdLibc.CP_IDLE * FreeBsdLibc.UINT64_SIZE);
355 }
356 }
357 return ticks;
358 }
359
360
361
362
363
364
365
366
367 private static String getProcessorIDfromDmiDecode(long processorID) {
368 boolean procInfo = false;
369 String marker = "Processor Information";
370 for (String checkLine : ExecutingCommand.runNative("dmidecode -t system")) {
371 if (!procInfo && checkLine.contains(marker)) {
372 marker = "ID:";
373 procInfo = true;
374 } else if (procInfo && checkLine.contains(marker)) {
375 return checkLine.split(marker)[1].trim();
376 }
377 }
378
379 return String.format(Locale.ROOT, "%016X", processorID);
380 }
381
382 @Override
383 public long queryContextSwitches() {
384 String name = "vm.stats.sys.v_swtch";
385 size_t.ByReference size = new size_t.ByReference(new size_t(FreeBsdLibc.INT_SIZE));
386 try (Memory p = new Memory(size.longValue())) {
387 if (0 != FreeBsdLibc.INSTANCE.sysctlbyname(name, p, size, null, size_t.ZERO)) {
388 return 0L;
389 }
390 return ParseUtil.unsignedIntToLong(p.getInt(0));
391 }
392 }
393
394 @Override
395 public long queryInterrupts() {
396 String name = "vm.stats.sys.v_intr";
397 size_t.ByReference size = new size_t.ByReference(new size_t(FreeBsdLibc.INT_SIZE));
398 try (Memory p = new Memory(size.longValue())) {
399 if (0 != FreeBsdLibc.INSTANCE.sysctlbyname(name, p, size, null, size_t.ZERO)) {
400 return 0L;
401 }
402 return ParseUtil.unsignedIntToLong(p.getInt(0));
403 }
404 }
405 }