View Javadoc
1   /*
2    * Copyright 2020-2022 The OSHI Project Contributors
3    * SPDX-License-Identifier: MIT
4    */
5   package oshi.driver.windows;
6   
7   import static oshi.hardware.common.AbstractCentralProcessor.orderedProcCaches;
8   
9   import java.util.ArrayList;
10  import java.util.Comparator;
11  import java.util.HashMap;
12  import java.util.HashSet;
13  import java.util.List;
14  import java.util.Map;
15  import java.util.Set;
16  
17  import com.sun.jna.platform.win32.Kernel32Util;
18  import com.sun.jna.platform.win32.VersionHelpers;
19  import com.sun.jna.platform.win32.WinNT;
20  import com.sun.jna.platform.win32.WinNT.CACHE_RELATIONSHIP;
21  import com.sun.jna.platform.win32.WinNT.GROUP_AFFINITY;
22  import com.sun.jna.platform.win32.WinNT.LOGICAL_PROCESSOR_RELATIONSHIP;
23  import com.sun.jna.platform.win32.WinNT.NUMA_NODE_RELATIONSHIP;
24  import com.sun.jna.platform.win32.WinNT.PROCESSOR_RELATIONSHIP;
25  import com.sun.jna.platform.win32.WinNT.SYSTEM_LOGICAL_PROCESSOR_INFORMATION;
26  import com.sun.jna.platform.win32.WinNT.SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX;
27  import com.sun.jna.platform.win32.COM.WbemcliUtil.WmiResult;
28  
29  import oshi.annotation.concurrent.ThreadSafe;
30  import oshi.driver.windows.wmi.Win32Processor;
31  import oshi.driver.windows.wmi.Win32Processor.ProcessorIdProperty;
32  import oshi.hardware.CentralProcessor.LogicalProcessor;
33  import oshi.hardware.CentralProcessor.PhysicalProcessor;
34  import oshi.hardware.CentralProcessor.ProcessorCache;
35  import oshi.hardware.CentralProcessor.ProcessorCache.Type;
36  import oshi.util.platform.windows.WmiUtil;
37  import oshi.util.tuples.Triplet;
38  
39  /**
40   * Utility to query Logical Processor Information
41   */
42  @ThreadSafe
43  public final class LogicalProcessorInformation {
44  
45      private static final boolean IS_WIN10_OR_GREATER = VersionHelpers.IsWindows10OrGreater();
46  
47      private LogicalProcessorInformation() {
48      }
49  
50      /**
51       * Get a list of logical processors on this machine. Requires Windows 7 and higher.
52       *
53       * @return A list of logical processors
54       */
55      public static Triplet<List<LogicalProcessor>, List<PhysicalProcessor>, List<ProcessorCache>> getLogicalProcessorInformationEx() {
56          // Collect a list of logical processors on each physical core and
57          // package. These will be 64-bit bitmasks.
58          SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX[] procInfo = Kernel32Util
59                  .getLogicalProcessorInformationEx(WinNT.LOGICAL_PROCESSOR_RELATIONSHIP.RelationAll);
60          // Used to cross-reference a processor to package, cache, pr core
61          List<GROUP_AFFINITY[]> packages = new ArrayList<>();
62          Set<ProcessorCache> caches = new HashSet<>();
63          List<GROUP_AFFINITY> cores = new ArrayList<>();
64          // Used to iterate
65          List<NUMA_NODE_RELATIONSHIP> numaNodes = new ArrayList<>();
66          // Map to store efficiency class of a processor core
67          Map<GROUP_AFFINITY, Integer> coreEfficiencyMap = new HashMap<>();
68  
69          for (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info : procInfo) {
70              switch (info.relationship) {
71              case LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorPackage:
72                  // could assign a package to more than one processor group
73                  packages.add(((PROCESSOR_RELATIONSHIP) info).groupMask);
74                  break;
75              case LOGICAL_PROCESSOR_RELATIONSHIP.RelationCache:
76                  CACHE_RELATIONSHIP cache = (CACHE_RELATIONSHIP) info;
77                  caches.add(new ProcessorCache(cache.level, cache.associativity, cache.lineSize, cache.size,
78                          Type.values()[cache.type]));
79                  break;
80              case LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorCore:
81                  PROCESSOR_RELATIONSHIP core = ((PROCESSOR_RELATIONSHIP) info);
82                  // for Core, groupCount is always 1
83                  cores.add(core.groupMask[0]);
84                  if (IS_WIN10_OR_GREATER) {
85                      coreEfficiencyMap.put(core.groupMask[0], (int) core.efficiencyClass);
86                  }
87                  break;
88              case LOGICAL_PROCESSOR_RELATIONSHIP.RelationNumaNode:
89                  numaNodes.add((NUMA_NODE_RELATIONSHIP) info);
90                  break;
91              default:
92                  // Ignore Group info
93                  break;
94              }
95          }
96          // Windows doesn't define core, cache, and package numbers, so we define our own
97          // for consistent use across the API. Here we sort so core, cache, and package
98          // numbers increment consistently with processor numbers/bitmasks, ordered in
99          // processor groups.
100         cores.sort(Comparator.comparing(c -> c.group * 64L + Long.numberOfTrailingZeros(c.mask.longValue())));
101 
102         // if package in multiple groups will still use first group for sorting
103         packages.sort(Comparator.comparing(p -> p[0].group * 64L + Long.numberOfTrailingZeros(p[0].mask.longValue())));
104 
105         // Iterate Logical Processors and use bitmasks to match packages, cores,
106         // and NUMA nodes. Perfmon instances are numa node + processor number, so we
107         // iterate by numa node so the returned list will properly index perfcounter
108         // numa/proc-per-numa indices with all numa nodes grouped together
109         numaNodes.sort(Comparator.comparing(n -> n.nodeNumber));
110 
111         // Fetch the processorIDs from WMI
112         Map<Integer, String> processorIdMap = new HashMap<>();
113         WmiResult<ProcessorIdProperty> processorId = Win32Processor.queryProcessorId();
114         // One entry for each package/socket
115         for (int pkg = 0; pkg < processorId.getResultCount(); pkg++) {
116             processorIdMap.put(pkg, WmiUtil.getString(processorId, ProcessorIdProperty.PROCESSORID, pkg));
117         }
118 
119         List<LogicalProcessor> logProcs = new ArrayList<>();
120         Map<Integer, Integer> corePkgMap = new HashMap<>();
121         Map<Integer, String> pkgCpuidMap = new HashMap<>();
122         for (NUMA_NODE_RELATIONSHIP node : numaNodes) {
123             int nodeNum = node.nodeNumber;
124             int group = node.groupMask.group;
125             long mask = node.groupMask.mask.longValue();
126             // Processor numbers are uniquely identified by processor group and processor
127             // number on that group, which matches the bitmask.
128             int lowBit = Long.numberOfTrailingZeros(mask);
129             int hiBit = 63 - Long.numberOfLeadingZeros(mask);
130             for (int lp = lowBit; lp <= hiBit; lp++) {
131                 if ((mask & (1L << lp)) != 0) {
132                     int coreId = getMatchingCore(cores, group, lp);
133                     int pkgId = getMatchingPackage(packages, group, lp);
134                     corePkgMap.put(coreId, pkgId);
135                     pkgCpuidMap.put(coreId, processorIdMap.getOrDefault(pkgId, ""));
136                     LogicalProcessor logProc = new LogicalProcessor(lp, coreId, pkgId, nodeNum, group);
137                     logProcs.add(logProc);
138                 }
139             }
140         }
141         List<PhysicalProcessor> physProcs = getPhysProcs(cores, coreEfficiencyMap, corePkgMap, pkgCpuidMap);
142         return new Triplet<>(logProcs, physProcs, orderedProcCaches(caches));
143     }
144 
145     private static List<PhysicalProcessor> getPhysProcs(List<GROUP_AFFINITY> cores,
146             Map<GROUP_AFFINITY, Integer> coreEfficiencyMap, Map<Integer, Integer> corePkgMap,
147             Map<Integer, String> coreCpuidMap) {
148         List<PhysicalProcessor> physProcs = new ArrayList<>();
149         for (int coreId = 0; coreId < cores.size(); coreId++) {
150             int efficiency = coreEfficiencyMap.getOrDefault(cores.get(coreId), 0);
151             String cpuid = coreCpuidMap.getOrDefault(coreId, "");
152             int pkgId = corePkgMap.getOrDefault(coreId, 0);
153             physProcs.add(new PhysicalProcessor(pkgId, coreId, efficiency, cpuid));
154         }
155         return physProcs;
156     }
157 
158     private static int getMatchingPackage(List<GROUP_AFFINITY[]> packages, int g, int lp) {
159         for (int i = 0; i < packages.size(); i++) {
160             for (int j = 0; j < packages.get(i).length; j++) {
161                 if ((packages.get(i)[j].mask.longValue() & (1L << lp)) != 0 && packages.get(i)[j].group == g) {
162                     return i;
163                 }
164             }
165         }
166         return 0;
167     }
168 
169     private static int getMatchingCore(List<GROUP_AFFINITY> cores, int g, int lp) {
170         for (int j = 0; j < cores.size(); j++) {
171             if ((cores.get(j).mask.longValue() & (1L << lp)) != 0 && cores.get(j).group == g) {
172                 return j;
173             }
174         }
175         return 0;
176     }
177 
178     /*
179      * Non-EX version for pre-Win7
180      */
181 
182     /**
183      * Get a list of logical processors on this machine
184      *
185      * @return A list of logical processors
186      */
187     public static Triplet<List<LogicalProcessor>, List<PhysicalProcessor>, List<ProcessorCache>> getLogicalProcessorInformation() {
188         // Collect a list of logical processors on each physical core and package.
189         List<Long> packageMaskList = new ArrayList<>();
190         List<Long> coreMaskList = new ArrayList<>();
191         WinNT.SYSTEM_LOGICAL_PROCESSOR_INFORMATION[] processors = Kernel32Util.getLogicalProcessorInformation();
192         for (SYSTEM_LOGICAL_PROCESSOR_INFORMATION proc : processors) {
193             if (proc.relationship == WinNT.LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorPackage) {
194                 packageMaskList.add(proc.processorMask.longValue());
195             } else if (proc.relationship == WinNT.LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorCore) {
196                 coreMaskList.add(proc.processorMask.longValue());
197             }
198         }
199         // Sort the list (natural ordering) so core and package numbers
200         // increment as expected.
201         coreMaskList.sort(null);
202         packageMaskList.sort(null);
203 
204         // Assign logical processors to cores and packages
205         List<LogicalProcessor> logProcs = new ArrayList<>();
206         for (int core = 0; core < coreMaskList.size(); core++) {
207             long coreMask = coreMaskList.get(core);
208             // Lowest and Highest set bits, indexing from 0
209             int lowBit = Long.numberOfTrailingZeros(coreMask);
210             int hiBit = 63 - Long.numberOfLeadingZeros(coreMask);
211             // Create logical processors for this core
212             for (int i = lowBit; i <= hiBit; i++) {
213                 if ((coreMask & (1L << i)) != 0) {
214                     LogicalProcessor logProc = new LogicalProcessor(i, core,
215                             LogicalProcessorInformation.getBitMatchingPackageNumber(packageMaskList, i));
216                     logProcs.add(logProc);
217                 }
218             }
219         }
220         return new Triplet<>(logProcs, null, null);
221     }
222 
223     /**
224      * Iterate over the package mask list and find a matching mask index
225      *
226      * @param packageMaskList The list of bitmasks to iterate
227      * @param logProc         The bit to find matching mask
228      * @return The index of the list which matched the bit
229      */
230     private static int getBitMatchingPackageNumber(List<Long> packageMaskList, int logProc) {
231         for (int i = 0; i < packageMaskList.size(); i++) {
232             if ((packageMaskList.get(i).longValue() & (1L << logProc)) != 0) {
233                 return i;
234             }
235         }
236         return 0;
237     }
238 }