1
2
3
4
5 package oshi.hardware.platform.unix.solaris;
6
7 import static oshi.software.os.unix.solaris.SolarisOperatingSystem.HAS_KSTAT2;
8
9 import java.util.ArrayList;
10 import java.util.Arrays;
11 import java.util.Collections;
12 import java.util.HashMap;
13 import java.util.List;
14 import java.util.Locale;
15 import java.util.Map;
16 import java.util.regex.Matcher;
17 import java.util.regex.Pattern;
18
19 import com.sun.jna.platform.unix.solaris.LibKstat.Kstat;
20
21 import oshi.annotation.concurrent.ThreadSafe;
22 import oshi.hardware.common.AbstractCentralProcessor;
23 import oshi.jna.platform.unix.SolarisLibc;
24 import oshi.util.ExecutingCommand;
25 import oshi.util.ParseUtil;
26 import oshi.util.platform.unix.solaris.KstatUtil;
27 import oshi.util.platform.unix.solaris.KstatUtil.KstatChain;
28 import oshi.util.tuples.Quartet;
29
30
31
32
33 @ThreadSafe
34 final class SolarisCentralProcessor extends AbstractCentralProcessor {
35
36 private static final String KSTAT_SYSTEM_CPU = "kstat:/system/cpu/";
37 private static final String INFO = "/info";
38 private static final String SYS = "/sys";
39
40 private static final String KSTAT_PM_CPU = "kstat:/pm/cpu/";
41 private static final String PSTATE = "/pstate";
42
43 private static final String CPU_INFO = "cpu_info";
44
45 @Override
46 protected ProcessorIdentifier queryProcessorId() {
47 boolean cpu64bit = "64".equals(ExecutingCommand.getFirstAnswer("isainfo -b").trim());
48 if (HAS_KSTAT2) {
49
50 return queryProcessorId2(cpu64bit);
51 }
52 String cpuVendor = "";
53 String cpuName = "";
54 String cpuFamily = "";
55 String cpuModel = "";
56 String cpuStepping = "";
57 long cpuFreq = 0L;
58
59
60 try (KstatChain kc = KstatUtil.openChain()) {
61 Kstat ksp = kc.lookup(CPU_INFO, -1, null);
62
63 if (ksp != null && kc.read(ksp)) {
64 cpuVendor = KstatUtil.dataLookupString(ksp, "vendor_id");
65 cpuName = KstatUtil.dataLookupString(ksp, "brand");
66 cpuFamily = KstatUtil.dataLookupString(ksp, "family");
67 cpuModel = KstatUtil.dataLookupString(ksp, "model");
68 cpuStepping = KstatUtil.dataLookupString(ksp, "stepping");
69 cpuFreq = KstatUtil.dataLookupLong(ksp, "clock_MHz") * 1_000_000L;
70 }
71 }
72 String processorID = getProcessorID(cpuStepping, cpuModel, cpuFamily);
73
74 return new ProcessorIdentifier(cpuVendor, cpuName, cpuFamily, cpuModel, cpuStepping, processorID, cpu64bit,
75 cpuFreq);
76 }
77
78 private static ProcessorIdentifier queryProcessorId2(boolean cpu64bit) {
79 Object[] results = KstatUtil.queryKstat2(KSTAT_SYSTEM_CPU + "0" + INFO, "vendor_id", "brand", "family", "model",
80 "stepping", "clock_MHz");
81
82 String cpuVendor = results[0] == null ? "" : (String) results[0];
83 String cpuName = results[1] == null ? "" : (String) results[1];
84 String cpuFamily = results[2] == null ? "" : results[2].toString();
85 String cpuModel = results[3] == null ? "" : results[3].toString();
86 String cpuStepping = results[4] == null ? "" : results[4].toString();
87 long cpuFreq = results[5] == null ? 0L : (long) results[5];
88
89 String processorID = getProcessorID(cpuStepping, cpuModel, cpuFamily);
90 return new ProcessorIdentifier(cpuVendor, cpuName, cpuFamily, cpuModel, cpuStepping, processorID, cpu64bit,
91 cpuFreq);
92 }
93
94 @Override
95 protected Quartet<List<LogicalProcessor>, List<PhysicalProcessor>, List<ProcessorCache>, List<String>> initProcessorCounts() {
96 List<LogicalProcessor> logProcs;
97 Map<Integer, Integer> numaNodeMap = mapNumaNodes();
98 if (HAS_KSTAT2) {
99
100 logProcs = initProcessorCounts2(numaNodeMap);
101 } else {
102 logProcs = new ArrayList<>();
103 try (KstatChain kc = KstatUtil.openChain()) {
104 List<Kstat> kstats = kc.lookupAll(CPU_INFO, -1, null);
105
106 for (Kstat ksp : kstats) {
107 if (ksp != null && kc.read(ksp)) {
108 int procId = logProcs.size();
109 String chipId = KstatUtil.dataLookupString(ksp, "chip_id");
110 String coreId = KstatUtil.dataLookupString(ksp, "core_id");
111 LogicalProcessor logProc = new LogicalProcessor(procId, ParseUtil.parseIntOrDefault(coreId, 0),
112 ParseUtil.parseIntOrDefault(chipId, 0), numaNodeMap.getOrDefault(procId, 0));
113 logProcs.add(logProc);
114 }
115 }
116 }
117 }
118 if (logProcs.isEmpty()) {
119 logProcs.add(new LogicalProcessor(0, 0, 0));
120 }
121 Map<Integer, String> dmesg = new HashMap<>();
122
123
124
125
126 Pattern p = Pattern.compile(".* cpu(\\\\d+): ((ARM|AMD|Intel).+)");
127 for (String s : ExecutingCommand.runNative("dmesg")) {
128 Matcher m = p.matcher(s);
129 if (m.matches()) {
130 int coreId = ParseUtil.parseIntOrDefault(m.group(1), 0);
131 dmesg.put(coreId, m.group(2).trim());
132 }
133 }
134 if (dmesg.isEmpty()) {
135 return new Quartet<>(logProcs, null, null, Collections.emptyList());
136 }
137 List<String> featureFlags = ExecutingCommand.runNative("isainfo -x");
138 return new Quartet<>(logProcs, createProcListFromDmesg(logProcs, dmesg), null, featureFlags);
139 }
140
141 private static List<LogicalProcessor> initProcessorCounts2(Map<Integer, Integer> numaNodeMap) {
142 List<LogicalProcessor> logProcs = new ArrayList<>();
143
144 List<Object[]> results = KstatUtil.queryKstat2List(KSTAT_SYSTEM_CPU, INFO, "chip_id", "core_id");
145 for (Object[] result : results) {
146 int procId = logProcs.size();
147 long chipId = result[0] == null ? 0L : (long) result[0];
148 long coreId = result[1] == null ? 0L : (long) result[1];
149 LogicalProcessor logProc = new LogicalProcessor(procId, (int) coreId, (int) chipId,
150 numaNodeMap.getOrDefault(procId, 0));
151 logProcs.add(logProc);
152 }
153 if (logProcs.isEmpty()) {
154 logProcs.add(new LogicalProcessor(0, 0, 0));
155 }
156 return logProcs;
157 }
158
159 private static Map<Integer, Integer> mapNumaNodes() {
160
161 Map<Integer, Integer> numaNodeMap = new HashMap<>();
162 int lgroup = 0;
163 for (String line : ExecutingCommand.runNative("lgrpinfo -c leaves")) {
164
165
166
167
168
169
170
171 if (line.startsWith("lgroup")) {
172 lgroup = ParseUtil.getFirstIntValue(line);
173 } else if (line.contains("CPUs:") || line.contains("CPU:")) {
174 for (Integer cpu : ParseUtil.parseHyphenatedIntList(line.split(":")[1])) {
175 numaNodeMap.put(cpu, lgroup);
176 }
177 }
178 }
179 return numaNodeMap;
180 }
181
182 @Override
183 public long[] querySystemCpuLoadTicks() {
184 long[] ticks = new long[TickType.values().length];
185
186 long[][] procTicks = getProcessorCpuLoadTicks();
187 for (int i = 0; i < ticks.length; i++) {
188 for (long[] procTick : procTicks) {
189 ticks[i] += procTick[i];
190 }
191 ticks[i] /= procTicks.length;
192 }
193 return ticks;
194 }
195
196 @Override
197 public long[] queryCurrentFreq() {
198 if (HAS_KSTAT2) {
199
200 return queryCurrentFreq2(getLogicalProcessorCount());
201 }
202 long[] freqs = new long[getLogicalProcessorCount()];
203 Arrays.fill(freqs, -1);
204 try (KstatChain kc = KstatUtil.openChain()) {
205 for (int i = 0; i < freqs.length; i++) {
206 for (Kstat ksp : kc.lookupAll(CPU_INFO, i, null)) {
207 if (ksp != null && kc.read(ksp)) {
208 freqs[i] = KstatUtil.dataLookupLong(ksp, "current_clock_Hz");
209 }
210 }
211 }
212 }
213 return freqs;
214 }
215
216 private static long[] queryCurrentFreq2(int processorCount) {
217 long[] freqs = new long[processorCount];
218 Arrays.fill(freqs, -1);
219
220 List<Object[]> results = KstatUtil.queryKstat2List(KSTAT_SYSTEM_CPU, INFO, "current_clock_Hz");
221 int cpu = -1;
222 for (Object[] result : results) {
223 if (++cpu >= freqs.length) {
224 break;
225 }
226 freqs[cpu] = result[0] == null ? -1L : (long) result[0];
227 }
228 return freqs;
229 }
230
231 @Override
232 public long queryMaxFreq() {
233 if (HAS_KSTAT2) {
234
235 return queryMaxFreq2();
236 }
237 long max = -1L;
238 try (KstatChain kc = KstatUtil.openChain()) {
239 for (Kstat ksp : kc.lookupAll(CPU_INFO, 0, null)) {
240 if (kc.read(ksp)) {
241 String suppFreq = KstatUtil.dataLookupString(ksp, "supported_frequencies_Hz");
242 if (!suppFreq.isEmpty()) {
243 for (String s : suppFreq.split(":")) {
244 long freq = ParseUtil.parseLongOrDefault(s, -1L);
245 if (max < freq) {
246 max = freq;
247 }
248 }
249 }
250 }
251 }
252 }
253 return max;
254 }
255
256 private static long queryMaxFreq2() {
257 long max = -1L;
258 List<Object[]> results = KstatUtil.queryKstat2List(KSTAT_PM_CPU, PSTATE, "supported_frequencies");
259 for (Object[] result : results) {
260 for (long freq : result[0] == null ? new long[0] : (long[]) result[0]) {
261 if (freq > max) {
262 max = freq;
263 }
264 }
265 }
266 return max;
267 }
268
269 @Override
270 public double[] getSystemLoadAverage(int nelem) {
271 if (nelem < 1 || nelem > 3) {
272 throw new IllegalArgumentException("Must include from one to three elements.");
273 }
274 double[] average = new double[nelem];
275 int retval = SolarisLibc.INSTANCE.getloadavg(average, nelem);
276 if (retval < nelem) {
277 for (int i = Math.max(retval, 0); i < average.length; i++) {
278 average[i] = -1d;
279 }
280 }
281 return average;
282 }
283
284 @Override
285 public long[][] queryProcessorCpuLoadTicks() {
286 if (HAS_KSTAT2) {
287
288 return queryProcessorCpuLoadTicks2(getLogicalProcessorCount());
289 }
290 long[][] ticks = new long[getLogicalProcessorCount()][TickType.values().length];
291 int cpu = -1;
292 try (KstatChain kc = KstatUtil.openChain()) {
293 for (Kstat ksp : kc.lookupAll("cpu", -1, "sys")) {
294
295 if (++cpu >= ticks.length) {
296
297 break;
298 }
299 if (kc.read(ksp)) {
300 ticks[cpu][TickType.IDLE.getIndex()] = KstatUtil.dataLookupLong(ksp, "cpu_ticks_idle");
301 ticks[cpu][TickType.SYSTEM.getIndex()] = KstatUtil.dataLookupLong(ksp, "cpu_ticks_kernel");
302 ticks[cpu][TickType.USER.getIndex()] = KstatUtil.dataLookupLong(ksp, "cpu_ticks_user");
303 }
304 }
305 }
306 return ticks;
307 }
308
309 private static long[][] queryProcessorCpuLoadTicks2(int processorCount) {
310 long[][] ticks = new long[processorCount][TickType.values().length];
311 List<Object[]> results = KstatUtil.queryKstat2List(KSTAT_SYSTEM_CPU, SYS, "cpu_ticks_idle", "cpu_ticks_kernel",
312 "cpu_ticks_user");
313 int cpu = -1;
314 for (Object[] result : results) {
315 if (++cpu >= ticks.length) {
316 break;
317 }
318 ticks[cpu][TickType.IDLE.getIndex()] = result[0] == null ? 0L : (long) result[0];
319 ticks[cpu][TickType.SYSTEM.getIndex()] = result[1] == null ? 0L : (long) result[1];
320 ticks[cpu][TickType.USER.getIndex()] = result[2] == null ? 0L : (long) result[2];
321 }
322 return ticks;
323 }
324
325
326
327
328
329
330
331
332
333 private static String getProcessorID(String stepping, String model, String family) {
334 List<String> isainfo = ExecutingCommand.runNative("isainfo -v");
335 StringBuilder flags = new StringBuilder();
336 for (String line : isainfo) {
337 if (line.startsWith("32-bit")) {
338 break;
339 } else if (!line.startsWith("64-bit")) {
340 flags.append(' ').append(line.trim());
341 }
342 }
343 return createProcessorID(stepping, model, family,
344 ParseUtil.whitespaces.split(flags.toString().toLowerCase(Locale.ROOT)));
345 }
346
347 @Override
348 public long queryContextSwitches() {
349 if (HAS_KSTAT2) {
350
351 return queryContextSwitches2();
352 }
353 long swtch = 0;
354 List<String> kstat = ExecutingCommand.runNative("kstat -p cpu_stat:::/pswitch\\\\|inv_swtch/");
355 for (String s : kstat) {
356 swtch += ParseUtil.parseLastLong(s, 0L);
357 }
358 return swtch;
359 }
360
361 private static long queryContextSwitches2() {
362 long swtch = 0;
363 List<Object[]> results = KstatUtil.queryKstat2List(KSTAT_SYSTEM_CPU, SYS, "pswitch", "inv_swtch");
364 for (Object[] result : results) {
365 swtch += result[0] == null ? 0L : (long) result[0];
366 swtch += result[1] == null ? 0L : (long) result[1];
367 }
368 return swtch;
369 }
370
371 @Override
372 public long queryInterrupts() {
373 if (HAS_KSTAT2) {
374
375 return queryInterrupts2();
376 }
377 long intr = 0;
378 List<String> kstat = ExecutingCommand.runNative("kstat -p cpu_stat:::/intr/");
379 for (String s : kstat) {
380 intr += ParseUtil.parseLastLong(s, 0L);
381 }
382 return intr;
383 }
384
385 private static long queryInterrupts2() {
386 long intr = 0;
387 List<Object[]> results = KstatUtil.queryKstat2List(KSTAT_SYSTEM_CPU, SYS, "intr");
388 for (Object[] result : results) {
389 intr += result[0] == null ? 0L : (long) result[0];
390 }
391 return intr;
392 }
393 }