View Javadoc
1   /*
2    * Copyright 2020-2024 The OSHI Project Contributors
3    * SPDX-License-Identifier: MIT
4    */
5   package oshi.hardware.platform.unix.aix;
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.List;
14  import java.util.Map;
15  import java.util.function.Supplier;
16  
17  import com.sun.jna.Native;
18  import com.sun.jna.platform.unix.aix.Perfstat.perfstat_cpu_t;
19  import com.sun.jna.platform.unix.aix.Perfstat.perfstat_cpu_total_t;
20  import com.sun.jna.platform.unix.aix.Perfstat.perfstat_partition_config_t;
21  
22  import oshi.annotation.concurrent.ThreadSafe;
23  import oshi.driver.unix.aix.Lssrad;
24  import oshi.driver.unix.aix.perfstat.PerfstatConfig;
25  import oshi.driver.unix.aix.perfstat.PerfstatCpu;
26  import oshi.hardware.CentralProcessor.ProcessorCache.Type;
27  import oshi.hardware.common.AbstractCentralProcessor;
28  import oshi.util.Constants;
29  import oshi.util.ExecutingCommand;
30  import oshi.util.FileUtil;
31  import oshi.util.ParseUtil;
32  import oshi.util.tuples.Pair;
33  import oshi.util.tuples.Quartet;
34  
35  /**
36   * A CPU
37   */
38  @ThreadSafe
39  final class AixCentralProcessor extends AbstractCentralProcessor {
40  
41      private final Supplier<perfstat_cpu_total_t> cpuTotal = memoize(PerfstatCpu::queryCpuTotal, defaultExpiration());
42      private final Supplier<perfstat_cpu_t[]> cpuProc = memoize(PerfstatCpu::queryCpu, defaultExpiration());
43      private static final int SBITS = querySbits();
44  
45      private perfstat_partition_config_t config;
46  
47      /**
48       * Jiffies per second, used for process time counters.
49       */
50      private static final long USER_HZ = ParseUtil.parseLongOrDefault(ExecutingCommand.getFirstAnswer("getconf CLK_TCK"),
51              100L);
52  
53      @Override
54      protected ProcessorIdentifier queryProcessorId() {
55          String cpuVendor = Constants.UNKNOWN;
56          String cpuName = "";
57          String cpuFamily = "";
58          boolean cpu64bit = false;
59  
60          final String nameMarker = "Processor Type:";
61          final String familyMarker = "Processor Version:";
62          final String bitnessMarker = "CPU Type:";
63          for (final String checkLine : ExecutingCommand.runNative("prtconf")) {
64              if (checkLine.startsWith(nameMarker)) {
65                  cpuName = checkLine.split(nameMarker)[1].trim();
66                  if (cpuName.startsWith("P")) {
67                      cpuVendor = "IBM";
68                  } else if (cpuName.startsWith("I")) {
69                      cpuVendor = "Intel";
70                  }
71              } else if (checkLine.startsWith(familyMarker)) {
72                  cpuFamily = checkLine.split(familyMarker)[1].trim();
73              } else if (checkLine.startsWith(bitnessMarker)) {
74                  cpu64bit = checkLine.split(bitnessMarker)[1].contains("64");
75              }
76          }
77  
78          String cpuModel = "";
79          String cpuStepping = "";
80          String machineId = Native.toString(config.machineID);
81          if (machineId.isEmpty()) {
82              machineId = ExecutingCommand.getFirstAnswer("uname -m");
83          }
84          // last 4 characters are model ID (often 4C) and submodel (always 00)
85          if (machineId.length() > 10) {
86              int m = machineId.length() - 4;
87              int s = machineId.length() - 2;
88              cpuModel = machineId.substring(m, s);
89              cpuStepping = machineId.substring(s);
90          }
91  
92          return new ProcessorIdentifier(cpuVendor, cpuName, cpuFamily, cpuModel, cpuStepping, machineId, cpu64bit,
93                  (long) (config.processorMHz * 1_000_000L));
94      }
95  
96      @Override
97      protected Quartet<List<LogicalProcessor>, List<PhysicalProcessor>, List<ProcessorCache>, List<String>> initProcessorCounts() {
98          this.config = PerfstatConfig.queryConfig();
99  
100         // Reporting "online" or "active" values can lead to nonsense so we go with max
101         int physProcs = (int) config.numProcessors.max;
102         if (physProcs < 1) {
103             physProcs = 1;
104         }
105         int lcpus = (int) config.vcpus.max;
106         if (lcpus < 1) {
107             lcpus = 1;
108         }
109         // Sanity check to ensure lp/pp ratio >= 1
110         if (physProcs > lcpus) {
111             physProcs = lcpus;
112         }
113         int lpPerPp = lcpus / physProcs;
114         // Get node and package mapping
115         Map<Integer, Pair<Integer, Integer>> nodePkgMap = Lssrad.queryNodesPackages();
116         List<LogicalProcessor> logProcs = new ArrayList<>();
117         for (int proc = 0; proc < lcpus; proc++) {
118             Pair<Integer, Integer> nodePkg = nodePkgMap.get(proc);
119             int physProc = proc / lpPerPp;
120             logProcs.add(new LogicalProcessor(proc, physProc, nodePkg == null ? 0 : nodePkg.getB(),
121                     nodePkg == null ? 0 : nodePkg.getA()));
122         }
123         return new Quartet<>(logProcs, null, getCachesForModel(physProcs), Collections.emptyList());
124     }
125 
126     private List<ProcessorCache> getCachesForModel(int cores) {
127         // The only info available in the OS is the L2 size
128         // But we can hardcode POWER7, POWER8, and POWER9 configs
129         List<ProcessorCache> caches = new ArrayList<>();
130         int powerVersion = ParseUtil.getFirstIntValue(ExecutingCommand.getFirstAnswer("uname -n"));
131         switch (powerVersion) {
132         case 7:
133             caches.add(new ProcessorCache(3, 8, 128, (2 * 32) << 20, Type.UNIFIED));
134             caches.add(new ProcessorCache(2, 8, 128, 256 << 10, Type.UNIFIED));
135             caches.add(new ProcessorCache(1, 8, 128, 32 << 10, Type.DATA));
136             caches.add(new ProcessorCache(1, 4, 128, 32 << 10, Type.INSTRUCTION));
137             break;
138         case 8:
139             caches.add(new ProcessorCache(4, 8, 128, (16 * 16) << 20, Type.UNIFIED));
140             caches.add(new ProcessorCache(3, 8, 128, 40 << 20, Type.UNIFIED));
141             caches.add(new ProcessorCache(2, 8, 128, 512 << 10, Type.UNIFIED));
142             caches.add(new ProcessorCache(1, 8, 128, 64 << 10, Type.DATA));
143             caches.add(new ProcessorCache(1, 8, 128, 32 << 10, Type.INSTRUCTION));
144             break;
145         case 9:
146             caches.add(new ProcessorCache(3, 20, 128, (cores * 10) << 20, Type.UNIFIED));
147             caches.add(new ProcessorCache(2, 8, 128, 512 << 10, Type.UNIFIED));
148             caches.add(new ProcessorCache(1, 8, 128, 32 << 10, Type.DATA));
149             caches.add(new ProcessorCache(1, 8, 128, 32 << 10, Type.INSTRUCTION));
150             break;
151         default:
152             // Don't guess
153         }
154         return caches;
155     }
156 
157     @Override
158     public long[] querySystemCpuLoadTicks() {
159         perfstat_cpu_total_t perfstat = cpuTotal.get();
160         long[] ticks = new long[TickType.values().length];
161         ticks[TickType.USER.ordinal()] = perfstat.user * 1000L / USER_HZ;
162         // Skip NICE
163         ticks[TickType.SYSTEM.ordinal()] = perfstat.sys * 1000L / USER_HZ;
164         ticks[TickType.IDLE.ordinal()] = perfstat.idle * 1000L / USER_HZ;
165         ticks[TickType.IOWAIT.ordinal()] = perfstat.wait * 1000L / USER_HZ;
166         ticks[TickType.IRQ.ordinal()] = perfstat.devintrs * 1000L / USER_HZ;
167         ticks[TickType.SOFTIRQ.ordinal()] = perfstat.softintrs * 1000L / USER_HZ;
168         ticks[TickType.STEAL.ordinal()] = (perfstat.idle_stolen_purr + perfstat.busy_stolen_purr) * 1000L / USER_HZ;
169         return ticks;
170     }
171 
172     @Override
173     public long[] queryCurrentFreq() {
174         // $ pmcycles -m
175         // CPU 0 runs at 4204 MHz
176         // CPU 1 runs at 4204 MHz
177         //
178         // ~/git/oshi$ pmcycles -m
179         // This machine runs at 1000 MHz
180 
181         // FIXME change to this as pmcycles requires root
182         // $ lsattr -El proc0
183         // frequency 3425000000 Processor Speed False
184 
185         long[] freqs = new long[getLogicalProcessorCount()];
186         Arrays.fill(freqs, -1);
187         String freqMarker = "runs at";
188         int idx = 0;
189         for (final String checkLine : ExecutingCommand.runNative("pmcycles -m")) {
190             if (checkLine.contains(freqMarker)) {
191                 freqs[idx++] = ParseUtil.parseHertz(checkLine.split(freqMarker)[1].trim());
192                 if (idx >= freqs.length) {
193                     break;
194                 }
195             }
196         }
197         return freqs;
198     }
199 
200     @Override
201     protected long queryMaxFreq() {
202         perfstat_cpu_total_t perfstat = cpuTotal.get();
203         return perfstat.processorHZ;
204     }
205 
206     @Override
207     public double[] getSystemLoadAverage(int nelem) {
208         if (nelem < 1 || nelem > 3) {
209             throw new IllegalArgumentException("Must include from one to three elements.");
210         }
211         double[] average = new double[nelem];
212         long[] loadavg = cpuTotal.get().loadavg;
213         for (int i = 0; i < nelem; i++) {
214             average[i] = loadavg[i] / (double) (1L << SBITS);
215         }
216         return average;
217     }
218 
219     @Override
220     public long[][] queryProcessorCpuLoadTicks() {
221         perfstat_cpu_t[] cpu = cpuProc.get();
222         // oversize the array to ensure constant length; we'll only fill cpu.length of it
223         long[][] ticks = new long[cpu.length][TickType.values().length];
224         for (int i = 0; i < cpu.length; i++) {
225             ticks[i] = new long[TickType.values().length];
226             ticks[i][TickType.USER.ordinal()] = cpu[i].user * 1000L / USER_HZ;
227             // Skip NICE
228             ticks[i][TickType.SYSTEM.ordinal()] = cpu[i].sys * 1000L / USER_HZ;
229             ticks[i][TickType.IDLE.ordinal()] = cpu[i].idle * 1000L / USER_HZ;
230             ticks[i][TickType.IOWAIT.ordinal()] = cpu[i].wait * 1000L / USER_HZ;
231             ticks[i][TickType.IRQ.ordinal()] = cpu[i].devintrs * 1000L / USER_HZ;
232             ticks[i][TickType.SOFTIRQ.ordinal()] = cpu[i].softintrs * 1000L / USER_HZ;
233             ticks[i][TickType.STEAL.ordinal()] = (cpu[i].idle_stolen_purr + cpu[i].busy_stolen_purr) * 1000L / USER_HZ;
234         }
235         return ticks;
236     }
237 
238     @Override
239     public long queryContextSwitches() {
240         return cpuTotal.get().pswitch;
241     }
242 
243     @Override
244     public long queryInterrupts() {
245         perfstat_cpu_total_t cpu = cpuTotal.get();
246         return cpu.devintrs + cpu.softintrs;
247     }
248 
249     private static int querySbits() {
250         // read from /usr/include/sys/proc.h
251         for (String s : FileUtil.readFile("/usr/include/sys/proc.h")) {
252             if (s.contains("SBITS") && s.contains("#define")) {
253                 return ParseUtil.parseLastInt(s, 16);
254             }
255         }
256         return 16;
257     }
258 }