View Javadoc
1   /*
2    * Copyright 2016-2024 The OSHI Project Contributors
3    * SPDX-License-Identifier: MIT
4    */
5   package oshi.hardware.common;
6   
7   import static oshi.util.Memoizer.defaultExpiration;
8   import static oshi.util.Memoizer.memoize;
9   
10  import java.util.ArrayList;
11  import java.util.Arrays;
12  import java.util.Collections;
13  import java.util.Comparator;
14  import java.util.HashMap;
15  import java.util.HashSet;
16  import java.util.List;
17  import java.util.Locale;
18  import java.util.Map;
19  import java.util.Set;
20  import java.util.function.Supplier;
21  import java.util.stream.Collectors;
22  
23  import org.slf4j.Logger;
24  import org.slf4j.LoggerFactory;
25  
26  import com.sun.jna.Platform;
27  
28  import oshi.annotation.concurrent.ThreadSafe;
29  import oshi.driver.linux.proc.Auxv;
30  import oshi.hardware.CentralProcessor;
31  import oshi.util.ParseUtil;
32  import oshi.util.tuples.Quartet;
33  
34  /**
35   * A CPU.
36   */
37  @ThreadSafe
38  public abstract class AbstractCentralProcessor implements CentralProcessor {
39  
40      private static final Logger LOG = LoggerFactory.getLogger(AbstractCentralProcessor.class);
41  
42      private final Supplier<ProcessorIdentifier> cpuid = memoize(this::queryProcessorId);
43      private final Supplier<Long> maxFreq = memoize(this::queryMaxFreq, defaultExpiration());
44      // Max often iterates current, intentionally making it shorter to re-memoize current
45      private final Supplier<long[]> currentFreq = memoize(this::queryCurrentFreq, defaultExpiration() / 2L);
46      private final Supplier<Long> contextSwitches = memoize(this::queryContextSwitches, defaultExpiration());
47      private final Supplier<Long> interrupts = memoize(this::queryInterrupts, defaultExpiration());
48  
49      private final Supplier<long[]> systemCpuLoadTicks = memoize(this::querySystemCpuLoadTicks, defaultExpiration());
50      private final Supplier<long[][]> processorCpuLoadTicks = memoize(this::queryProcessorCpuLoadTicks,
51              defaultExpiration());
52  
53      // Logical and Physical Processor Counts
54      private final int physicalPackageCount;
55      private final int physicalProcessorCount;
56      private final int logicalProcessorCount;
57  
58      // Processor info, initialized in constructor
59      private final List<LogicalProcessor> logicalProcessors;
60      private final List<PhysicalProcessor> physicalProcessors;
61      private final List<ProcessorCache> processorCaches;
62      private final List<String> featureFlags;
63  
64      /**
65       * Create a Processor
66       */
67      protected AbstractCentralProcessor() {
68          Quartet<List<LogicalProcessor>, List<PhysicalProcessor>, List<ProcessorCache>, List<String>> processorLists = initProcessorCounts();
69          // Populate logical processor lists.
70          this.logicalProcessors = Collections.unmodifiableList(processorLists.getA());
71          if (processorLists.getB() == null) {
72              Set<Integer> pkgCoreKeys = this.logicalProcessors.stream()
73                      .map(p -> (p.getPhysicalPackageNumber() << 16) + p.getPhysicalProcessorNumber())
74                      .collect(Collectors.toSet());
75              List<PhysicalProcessor> physProcs = pkgCoreKeys.stream().sorted()
76                      .map(k -> new PhysicalProcessor(k >> 16, k & 0xffff)).collect(Collectors.toList());
77              this.physicalProcessors = Collections.unmodifiableList(physProcs);
78          } else {
79              this.physicalProcessors = Collections.unmodifiableList(processorLists.getB());
80          }
81          this.processorCaches = processorLists.getC() == null ? Collections.emptyList()
82                  : Collections.unmodifiableList(processorLists.getC());
83          // Init processor counts
84          Set<Integer> physPkgs = new HashSet<>();
85          for (LogicalProcessor logProc : this.logicalProcessors) {
86              int pkg = logProc.getPhysicalPackageNumber();
87              physPkgs.add(pkg);
88          }
89          this.logicalProcessorCount = this.logicalProcessors.size();
90          this.physicalProcessorCount = this.physicalProcessors.size();
91          this.physicalPackageCount = physPkgs.size();
92          this.featureFlags = Collections.unmodifiableList(processorLists.getD());
93      }
94  
95      /**
96       * Initializes logical and physical processor lists and feature flags.
97       *
98       * @return Lists of initialized Logical Processors, Physical Processors, Processor Caches, and Feature Flags.
99       */
100     protected abstract Quartet<List<LogicalProcessor>, List<PhysicalProcessor>, List<ProcessorCache>, List<String>> initProcessorCounts();
101 
102     /**
103      * Updates logical and physical processor counts and arrays
104      *
105      * @return An array of initialized Logical Processors
106      */
107     protected abstract ProcessorIdentifier queryProcessorId();
108 
109     @Override
110     public ProcessorIdentifier getProcessorIdentifier() {
111         return cpuid.get();
112     }
113 
114     @Override
115     public long getMaxFreq() {
116         return maxFreq.get();
117     }
118 
119     /**
120      * Get processor max frequency.
121      *
122      * @return The max frequency.
123      */
124     protected long queryMaxFreq() {
125         return Arrays.stream(getCurrentFreq()).max().orElse(-1L);
126     }
127 
128     @Override
129     public long[] getCurrentFreq() {
130         long[] freq = currentFreq.get();
131         if (freq.length == getLogicalProcessorCount()) {
132             return freq;
133         }
134         long[] freqs = new long[getLogicalProcessorCount()];
135         Arrays.fill(freqs, freq[0]);
136         return freqs;
137     }
138 
139     /**
140      * Get processor current frequency.
141      *
142      * @return The current frequency.
143      */
144     protected abstract long[] queryCurrentFreq();
145 
146     @Override
147     public long getContextSwitches() {
148         return contextSwitches.get();
149     }
150 
151     /**
152      * Get number of context switches
153      *
154      * @return The context switches
155      */
156     protected abstract long queryContextSwitches();
157 
158     @Override
159     public long getInterrupts() {
160         return interrupts.get();
161     }
162 
163     /**
164      * Get number of interrupts
165      *
166      * @return The interrupts
167      */
168     protected abstract long queryInterrupts();
169 
170     @Override
171     public List<LogicalProcessor> getLogicalProcessors() {
172         return this.logicalProcessors;
173     }
174 
175     @Override
176     public List<PhysicalProcessor> getPhysicalProcessors() {
177         return this.physicalProcessors;
178     }
179 
180     @Override
181     public List<ProcessorCache> getProcessorCaches() {
182         return this.processorCaches;
183     }
184 
185     @Override
186     public List<String> getFeatureFlags() {
187         return this.featureFlags;
188     }
189 
190     @Override
191     public long[] getSystemCpuLoadTicks() {
192         return systemCpuLoadTicks.get();
193     }
194 
195     /**
196      * Get the system CPU load ticks
197      *
198      * @return The system CPU load ticks
199      */
200     protected abstract long[] querySystemCpuLoadTicks();
201 
202     @Override
203     public long[][] getProcessorCpuLoadTicks() {
204         return processorCpuLoadTicks.get();
205     }
206 
207     /**
208      * Get the processor CPU load ticks
209      *
210      * @return The processor CPU load ticks
211      */
212     protected abstract long[][] queryProcessorCpuLoadTicks();
213 
214     @Override
215     public double getSystemCpuLoadBetweenTicks(long[] oldTicks) {
216         if (oldTicks.length != TickType.values().length) {
217             throw new IllegalArgumentException("Provited tick array length " + oldTicks.length + " should have "
218                     + TickType.values().length + " elements");
219         }
220         long[] ticks = getSystemCpuLoadTicks();
221         // Calculate total
222         long total = 0;
223         for (int i = 0; i < ticks.length; i++) {
224             total += ticks[i] - oldTicks[i];
225         }
226         // Calculate idle from difference in idle and IOwait
227         long idle = ticks[TickType.IDLE.getIndex()] + ticks[TickType.IOWAIT.getIndex()]
228                 - oldTicks[TickType.IDLE.getIndex()] - oldTicks[TickType.IOWAIT.getIndex()];
229         LOG.trace("Total ticks: {}  Idle ticks: {}", total, idle);
230 
231         return total > 0 ? (double) (total - idle) / total : 0d;
232     }
233 
234     @Override
235     public double[] getProcessorCpuLoadBetweenTicks(long[][] oldTicks) {
236         long[][] ticks = getProcessorCpuLoadTicks();
237         if (oldTicks.length != ticks.length || oldTicks[0].length != TickType.values().length) {
238             throw new IllegalArgumentException("Provided tick array length " + oldTicks.length + " should be "
239                     + ticks.length + ", each subarray having " + TickType.values().length + " elements");
240         }
241         double[] load = new double[ticks.length];
242         for (int cpu = 0; cpu < ticks.length; cpu++) {
243             long total = 0;
244             for (int i = 0; i < ticks[cpu].length; i++) {
245                 total += ticks[cpu][i] - oldTicks[cpu][i];
246             }
247             // Calculate idle from difference in idle and IOwait
248             long idle = ticks[cpu][TickType.IDLE.getIndex()] + ticks[cpu][TickType.IOWAIT.getIndex()]
249                     - oldTicks[cpu][TickType.IDLE.getIndex()] - oldTicks[cpu][TickType.IOWAIT.getIndex()];
250             LOG.trace("CPU: {}  Total ticks: {}  Idle ticks: {}", cpu, total, idle);
251             // update
252             load[cpu] = total > 0 && idle >= 0 ? (double) (total - idle) / total : 0d;
253         }
254         return load;
255     }
256 
257     @Override
258     public int getLogicalProcessorCount() {
259         return this.logicalProcessorCount;
260     }
261 
262     @Override
263     public int getPhysicalProcessorCount() {
264         return this.physicalProcessorCount;
265     }
266 
267     @Override
268     public int getPhysicalPackageCount() {
269         return this.physicalPackageCount;
270     }
271 
272     /**
273      * Creates a Processor ID by encoding the stepping, model, family, and feature flags.
274      *
275      * @param stepping The CPU stepping
276      * @param model    The CPU model
277      * @param family   The CPU family
278      * @param flags    A space-delimited list of CPU feature flags
279      * @return The Processor ID string
280      */
281     protected static String createProcessorID(String stepping, String model, String family, String[] flags) {
282         long processorIdBytes = 0L;
283         long steppingL = ParseUtil.parseLongOrDefault(stepping, 0L);
284         long modelL = ParseUtil.parseLongOrDefault(model, 0L);
285         long familyL = ParseUtil.parseLongOrDefault(family, 0L);
286         // 3:0 – Stepping
287         processorIdBytes |= steppingL & 0xf;
288         // 19:16,7:4 – Model
289         processorIdBytes |= (modelL & 0xf) << 4;
290         processorIdBytes |= (modelL & 0xf0) << 12; // shift high 4 bits
291         // 27:20,11:8 – Family
292         processorIdBytes |= (familyL & 0xf) << 8;
293         processorIdBytes |= (familyL & 0xff0) << 16; // shift high 8 bits
294         // 13:12 – Processor Type, assume 0
295         long hwcap = 0L;
296         if (Platform.isLinux()) {
297             hwcap = Auxv.queryAuxv().getOrDefault(Auxv.AT_HWCAP, 0L);
298         }
299         if (hwcap > 0) {
300             processorIdBytes |= hwcap << 32;
301         } else {
302             for (String flag : flags) {
303                 switch (flag) { // NOSONAR squid:S1479
304                 case "fpu":
305                     processorIdBytes |= 1L << 32;
306                     break;
307                 case "vme":
308                     processorIdBytes |= 1L << 33;
309                     break;
310                 case "de":
311                     processorIdBytes |= 1L << 34;
312                     break;
313                 case "pse":
314                     processorIdBytes |= 1L << 35;
315                     break;
316                 case "tsc":
317                     processorIdBytes |= 1L << 36;
318                     break;
319                 case "msr":
320                     processorIdBytes |= 1L << 37;
321                     break;
322                 case "pae":
323                     processorIdBytes |= 1L << 38;
324                     break;
325                 case "mce":
326                     processorIdBytes |= 1L << 39;
327                     break;
328                 case "cx8":
329                     processorIdBytes |= 1L << 40;
330                     break;
331                 case "apic":
332                     processorIdBytes |= 1L << 41;
333                     break;
334                 case "sep":
335                     processorIdBytes |= 1L << 43;
336                     break;
337                 case "mtrr":
338                     processorIdBytes |= 1L << 44;
339                     break;
340                 case "pge":
341                     processorIdBytes |= 1L << 45;
342                     break;
343                 case "mca":
344                     processorIdBytes |= 1L << 46;
345                     break;
346                 case "cmov":
347                     processorIdBytes |= 1L << 47;
348                     break;
349                 case "pat":
350                     processorIdBytes |= 1L << 48;
351                     break;
352                 case "pse-36":
353                     processorIdBytes |= 1L << 49;
354                     break;
355                 case "psn":
356                     processorIdBytes |= 1L << 50;
357                     break;
358                 case "clfsh":
359                     processorIdBytes |= 1L << 51;
360                     break;
361                 case "ds":
362                     processorIdBytes |= 1L << 53;
363                     break;
364                 case "acpi":
365                     processorIdBytes |= 1L << 54;
366                     break;
367                 case "mmx":
368                     processorIdBytes |= 1L << 55;
369                     break;
370                 case "fxsr":
371                     processorIdBytes |= 1L << 56;
372                     break;
373                 case "sse":
374                     processorIdBytes |= 1L << 57;
375                     break;
376                 case "sse2":
377                     processorIdBytes |= 1L << 58;
378                     break;
379                 case "ss":
380                     processorIdBytes |= 1L << 59;
381                     break;
382                 case "htt":
383                     processorIdBytes |= 1L << 60;
384                     break;
385                 case "tm":
386                     processorIdBytes |= 1L << 61;
387                     break;
388                 case "ia64":
389                     processorIdBytes |= 1L << 62;
390                     break;
391                 case "pbe":
392                     processorIdBytes |= 1L << 63;
393                     break;
394                 default:
395                     break;
396                 }
397             }
398         }
399         return String.format(Locale.ROOT, "%016X", processorIdBytes);
400     }
401 
402     protected List<PhysicalProcessor> createProcListFromDmesg(List<LogicalProcessor> logProcs,
403             Map<Integer, String> dmesg) {
404         // Check if multiple CPU types
405         boolean isHybrid = dmesg.values().stream().distinct().count() > 1;
406         List<PhysicalProcessor> physProcs = new ArrayList<>();
407         Set<Integer> pkgCoreKeys = new HashSet<>();
408         for (LogicalProcessor logProc : logProcs) {
409             int pkgId = logProc.getPhysicalPackageNumber();
410             int coreId = logProc.getPhysicalProcessorNumber();
411             int pkgCoreKey = (pkgId << 16) + coreId;
412             if (!pkgCoreKeys.contains(pkgCoreKey)) {
413                 pkgCoreKeys.add(pkgCoreKey);
414                 String idStr = dmesg.getOrDefault(logProc.getProcessorNumber(), "");
415                 int efficiency = 0;
416                 // ARM v8 big.LITTLE chips just use the # for efficiency class
417                 // High-performance CPU (big): Cortex-A73, Cortex-A75, Cortex-A76
418                 // High-efficiency CPU (LITTLE): Cortex-A53, Cortex-A55
419                 if (isHybrid && ((idStr.startsWith("ARM Cortex") && ParseUtil.getFirstIntValue(idStr) >= 70)
420                         || (idStr.startsWith("Apple")
421                                 && (idStr.contains("Firestorm") || (idStr.contains("Avalanche")))))) {
422                     efficiency = 1;
423                 }
424                 physProcs.add(new PhysicalProcessor(pkgId, coreId, efficiency, idStr));
425             }
426         }
427         physProcs.sort(Comparator.comparingInt(PhysicalProcessor::getPhysicalPackageNumber)
428                 .thenComparingInt(PhysicalProcessor::getPhysicalProcessorNumber));
429         return physProcs;
430     }
431 
432     /**
433      * Filters a set of processor caches to an ordered list
434      *
435      * @param caches A set of unique caches.
436      * @return A list sorted by level (desc), type, and size (desc)
437      */
438     public static List<ProcessorCache> orderedProcCaches(Set<ProcessorCache> caches) {
439         return caches.stream().sorted(Comparator.comparing(
440                 c -> -1000 * c.getLevel() + 100 * c.getType().ordinal() - Integer.highestOneBit(c.getCacheSize())))
441                 .collect(Collectors.toList());
442     }
443 
444     @Override
445     public String toString() {
446         StringBuilder sb = new StringBuilder(getProcessorIdentifier().getName());
447         sb.append("\n ").append(getPhysicalPackageCount()).append(" physical CPU package(s)");
448         sb.append("\n ").append(getPhysicalProcessorCount()).append(" physical CPU core(s)");
449         Map<Integer, Integer> efficiencyCount = new HashMap<>();
450         int maxEfficiency = 0;
451         for (PhysicalProcessor cpu : getPhysicalProcessors()) {
452             int eff = cpu.getEfficiency();
453             efficiencyCount.merge(eff, 1, Integer::sum);
454             if (eff > maxEfficiency) {
455                 maxEfficiency = eff;
456             }
457         }
458         int pCores = efficiencyCount.getOrDefault(maxEfficiency, 0);
459         int eCores = getPhysicalProcessorCount() - pCores;
460         if (eCores > 0) {
461             sb.append(" (").append(pCores).append(" performance + ").append(eCores).append(" efficiency)");
462         }
463         sb.append("\n ").append(getLogicalProcessorCount()).append(" logical CPU(s)");
464         sb.append('\n').append("Identifier: ").append(getProcessorIdentifier().getIdentifier());
465         sb.append('\n').append("ProcessorID: ").append(getProcessorIdentifier().getProcessorID());
466         sb.append('\n').append("Microarchitecture: ").append(getProcessorIdentifier().getMicroarchitecture());
467         return sb.toString();
468     }
469 }