1
2
3
4
5 package oshi.hardware.platform.windows;
6
7 import static oshi.util.Memoizer.memoize;
8
9 import java.util.Arrays;
10 import java.util.HashMap;
11 import java.util.List;
12 import java.util.Locale;
13 import java.util.Map;
14 import java.util.concurrent.TimeUnit;
15 import java.util.function.Supplier;
16 import java.util.stream.Collectors;
17
18 import org.slf4j.Logger;
19 import org.slf4j.LoggerFactory;
20
21 import com.sun.jna.Native;
22 import com.sun.jna.platform.win32.Advapi32Util;
23 import com.sun.jna.platform.win32.PowrProf.POWER_INFORMATION_LEVEL;
24 import com.sun.jna.platform.win32.VersionHelpers;
25 import com.sun.jna.platform.win32.Win32Exception;
26 import com.sun.jna.platform.win32.WinBase;
27 import com.sun.jna.platform.win32.WinReg;
28 import com.sun.jna.platform.win32.COM.WbemcliUtil.WmiResult;
29
30 import oshi.annotation.concurrent.ThreadSafe;
31 import oshi.driver.windows.LogicalProcessorInformation;
32 import oshi.driver.windows.perfmon.LoadAverage;
33 import oshi.driver.windows.perfmon.ProcessorInformation;
34 import oshi.driver.windows.perfmon.ProcessorInformation.InterruptsProperty;
35 import oshi.driver.windows.perfmon.ProcessorInformation.ProcessorFrequencyProperty;
36 import oshi.driver.windows.perfmon.ProcessorInformation.ProcessorTickCountProperty;
37 import oshi.driver.windows.perfmon.ProcessorInformation.ProcessorUtilityTickCountProperty;
38 import oshi.driver.windows.perfmon.ProcessorInformation.SystemTickCountProperty;
39 import oshi.driver.windows.perfmon.SystemInformation;
40 import oshi.driver.windows.perfmon.SystemInformation.ContextSwitchProperty;
41 import oshi.driver.windows.wmi.Win32Processor;
42 import oshi.driver.windows.wmi.Win32Processor.ProcessorIdProperty;
43 import oshi.hardware.common.AbstractCentralProcessor;
44 import oshi.jna.Struct.CloseableSystemInfo;
45 import oshi.jna.platform.windows.Kernel32;
46 import oshi.jna.platform.windows.Kernel32.ProcessorFeature;
47 import oshi.jna.platform.windows.PowrProf;
48 import oshi.jna.platform.windows.PowrProf.ProcessorPowerInformation;
49 import oshi.util.GlobalConfig;
50 import oshi.util.ParseUtil;
51 import oshi.util.platform.windows.WmiUtil;
52 import oshi.util.tuples.Pair;
53 import oshi.util.tuples.Quartet;
54 import oshi.util.tuples.Triplet;
55
56
57
58
59 @ThreadSafe
60 final class WindowsCentralProcessor extends AbstractCentralProcessor {
61
62 private static final Logger LOG = LoggerFactory.getLogger(WindowsCentralProcessor.class);
63
64
65 private Map<String, Integer> numaNodeProcToLogicalProcMap;
66
67
68 private static final boolean USE_LEGACY_SYSTEM_COUNTERS = GlobalConfig
69 .get(GlobalConfig.OSHI_OS_WINDOWS_LEGACY_SYSTEM_COUNTERS, false);
70
71
72 private static final boolean USE_LOAD_AVERAGE = GlobalConfig.get(GlobalConfig.OSHI_OS_WINDOWS_LOADAVERAGE, false);
73 static {
74 if (USE_LOAD_AVERAGE) {
75 LoadAverage.startDaemon();
76 }
77 }
78
79
80 private static final boolean USE_CPU_UTILITY = VersionHelpers.IsWindows8OrGreater()
81 && GlobalConfig.get(GlobalConfig.OSHI_OS_WINDOWS_CPU_UTILITY, false);
82
83
84
85 private final Supplier<Pair<List<String>, Map<ProcessorUtilityTickCountProperty, List<Long>>>> processorUtilityCounters = USE_CPU_UTILITY
86 ? memoize(WindowsCentralProcessor::queryProcessorUtilityCounters, TimeUnit.MILLISECONDS.toNanos(300L))
87 : null;
88
89 private Map<ProcessorUtilityTickCountProperty, List<Long>> initialUtilityCounters = USE_CPU_UTILITY
90 ? processorUtilityCounters.get().getB()
91 : null;
92
93 private Long utilityBaseMultiplier = null;
94
95
96
97
98 @Override
99 protected ProcessorIdentifier queryProcessorId() {
100 String cpuVendor = "";
101 String cpuName = "";
102 String cpuIdentifier = "";
103 String cpuFamily = "";
104 String cpuModel = "";
105 String cpuStepping = "";
106 long cpuVendorFreq = 0L;
107 String processorID;
108 boolean cpu64bit = false;
109
110 final String cpuRegistryRoot = "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\";
111 String[] processorIds = Advapi32Util.registryGetKeys(WinReg.HKEY_LOCAL_MACHINE, cpuRegistryRoot);
112 if (processorIds.length > 0) {
113 String cpuRegistryPath = cpuRegistryRoot + processorIds[0];
114 cpuVendor = Advapi32Util.registryGetStringValue(WinReg.HKEY_LOCAL_MACHINE, cpuRegistryPath,
115 "VendorIdentifier");
116 cpuName = Advapi32Util.registryGetStringValue(WinReg.HKEY_LOCAL_MACHINE, cpuRegistryPath,
117 "ProcessorNameString");
118 cpuIdentifier = Advapi32Util.registryGetStringValue(WinReg.HKEY_LOCAL_MACHINE, cpuRegistryPath,
119 "Identifier");
120 try {
121 cpuVendorFreq = Advapi32Util.registryGetIntValue(WinReg.HKEY_LOCAL_MACHINE, cpuRegistryPath, "~MHz")
122 * 1_000_000L;
123 } catch (Win32Exception e) {
124
125 }
126 }
127 if (!cpuIdentifier.isEmpty()) {
128 cpuFamily = parseIdentifier(cpuIdentifier, "Family");
129 cpuModel = parseIdentifier(cpuIdentifier, "Model");
130 cpuStepping = parseIdentifier(cpuIdentifier, "Stepping");
131 }
132 try (CloseableSystemInfo sysinfo = new CloseableSystemInfo()) {
133 Kernel32.INSTANCE.GetNativeSystemInfo(sysinfo);
134 int processorArchitecture = sysinfo.processorArchitecture.pi.wProcessorArchitecture.intValue();
135 if (processorArchitecture == 9
136 || processorArchitecture == 12
137 || processorArchitecture == 6) {
138 cpu64bit = true;
139 }
140 }
141 WmiResult<ProcessorIdProperty> processorId = Win32Processor.queryProcessorId();
142 if (processorId.getResultCount() > 0) {
143 processorID = WmiUtil.getString(processorId, ProcessorIdProperty.PROCESSORID, 0);
144 } else {
145 processorID = createProcessorID(cpuStepping, cpuModel, cpuFamily,
146 cpu64bit ? new String[] { "ia64" } : new String[0]);
147 }
148 return new ProcessorIdentifier(cpuVendor, cpuName, cpuFamily, cpuModel, cpuStepping, processorID, cpu64bit,
149 cpuVendorFreq);
150 }
151
152
153
154
155
156
157
158
159 private static String parseIdentifier(String identifier, String key) {
160 String[] idSplit = ParseUtil.whitespaces.split(identifier);
161 boolean found = false;
162 for (String s : idSplit) {
163
164 if (found) {
165 return s;
166 }
167 found = s.equals(key);
168 }
169
170 return "";
171 }
172
173 @Override
174 protected Quartet<List<LogicalProcessor>, List<PhysicalProcessor>, List<ProcessorCache>, List<String>> initProcessorCounts() {
175 Triplet<List<LogicalProcessor>, List<PhysicalProcessor>, List<ProcessorCache>> lpi;
176 if (VersionHelpers.IsWindows7OrGreater()) {
177 lpi = LogicalProcessorInformation.getLogicalProcessorInformationEx();
178
179
180
181 int curNode = -1;
182 int procNum = 0;
183
184 int lp = 0;
185 this.numaNodeProcToLogicalProcMap = new HashMap<>();
186 for (LogicalProcessor logProc : lpi.getA()) {
187 int node = logProc.getNumaNode();
188
189 if (node != curNode) {
190 curNode = node;
191 procNum = 0;
192 }
193 numaNodeProcToLogicalProcMap.put(String.format(Locale.ROOT, "%d,%d", logProc.getNumaNode(), procNum++),
194 lp++);
195 }
196 } else {
197 lpi = LogicalProcessorInformation.getLogicalProcessorInformation();
198 }
199 List<String> featureFlags = Arrays.stream(ProcessorFeature.values())
200 .filter(f -> Kernel32.INSTANCE.IsProcessorFeaturePresent(f.value())).map(ProcessorFeature::name)
201 .collect(Collectors.toList());
202 return new Quartet<>(lpi.getA(), lpi.getB(), lpi.getC(), featureFlags);
203 }
204
205 @Override
206 public long[] querySystemCpuLoadTicks() {
207 long[] ticks = new long[TickType.values().length];
208 if (USE_LEGACY_SYSTEM_COUNTERS) {
209 WinBase.FILETIME lpIdleTime = new WinBase.FILETIME();
210 WinBase.FILETIME lpKernelTime = new WinBase.FILETIME();
211 WinBase.FILETIME lpUserTime = new WinBase.FILETIME();
212 if (!Kernel32.INSTANCE.GetSystemTimes(lpIdleTime, lpKernelTime, lpUserTime)) {
213 LOG.error("Failed to update system idle/kernel/user times. Error code: {}", Native.getLastError());
214 return ticks;
215 }
216
217
218
219
220
221
222 Map<SystemTickCountProperty, Long> valueMap = ProcessorInformation.querySystemCounters();
223 ticks[TickType.IRQ.getIndex()] = valueMap.getOrDefault(SystemTickCountProperty.PERCENTINTERRUPTTIME, 0L)
224 / 10_000L;
225 ticks[TickType.SOFTIRQ.getIndex()] = valueMap.getOrDefault(SystemTickCountProperty.PERCENTDPCTIME, 0L)
226 / 10_000L;
227
228 ticks[TickType.IDLE.getIndex()] = lpIdleTime.toDWordLong().longValue() / 10_000L;
229 ticks[TickType.SYSTEM.getIndex()] = lpKernelTime.toDWordLong().longValue() / 10_000L
230 - ticks[TickType.IDLE.getIndex()];
231 ticks[TickType.USER.getIndex()] = lpUserTime.toDWordLong().longValue() / 10_000L;
232
233 ticks[TickType.SYSTEM.getIndex()] -= ticks[TickType.IRQ.getIndex()] + ticks[TickType.SOFTIRQ.getIndex()];
234 return ticks;
235 }
236
237
238
239
240
241
242
243 long[][] procTicks = getProcessorCpuLoadTicks();
244 for (int i = 0; i < ticks.length; i++) {
245 for (long[] procTick : procTicks) {
246 ticks[i] += procTick[i];
247 }
248 }
249 return ticks;
250 }
251
252 @Override
253 public long[] queryCurrentFreq() {
254 if (VersionHelpers.IsWindows7OrGreater()) {
255 Pair<List<String>, Map<ProcessorFrequencyProperty, List<Long>>> instanceValuePair = ProcessorInformation
256 .queryFrequencyCounters();
257 List<String> instances = instanceValuePair.getA();
258 Map<ProcessorFrequencyProperty, List<Long>> valueMap = instanceValuePair.getB();
259 List<Long> percentMaxList = valueMap.get(ProcessorFrequencyProperty.PERCENTOFMAXIMUMFREQUENCY);
260 if (!instances.isEmpty()) {
261 long maxFreq = this.getMaxFreq();
262 long[] freqs = new long[getLogicalProcessorCount()];
263 for (String instance : instances) {
264 int cpu = instance.contains(",") ? numaNodeProcToLogicalProcMap.getOrDefault(instance, 0)
265 : ParseUtil.parseIntOrDefault(instance, 0);
266 if (cpu >= getLogicalProcessorCount()) {
267 continue;
268 }
269 freqs[cpu] = percentMaxList.get(cpu) * maxFreq / 100L;
270 }
271 return freqs;
272 }
273 }
274
275 return queryNTPower(2);
276 }
277
278 @Override
279 public long queryMaxFreq() {
280 long[] freqs = queryNTPower(1);
281 return Arrays.stream(freqs).max().orElse(-1L);
282 }
283
284
285
286
287
288
289
290 private long[] queryNTPower(int fieldIndex) {
291 ProcessorPowerInformation ppi = new ProcessorPowerInformation();
292 ProcessorPowerInformation[] ppiArray = (ProcessorPowerInformation[]) ppi.toArray(getLogicalProcessorCount());
293 long[] freqs = new long[getLogicalProcessorCount()];
294 if (0 != PowrProf.INSTANCE.CallNtPowerInformation(POWER_INFORMATION_LEVEL.ProcessorInformation, null, 0,
295 ppiArray[0].getPointer(), ppi.size() * ppiArray.length)) {
296 LOG.error("Unable to get Processor Information");
297 Arrays.fill(freqs, -1L);
298 return freqs;
299 }
300 for (int i = 0; i < freqs.length; i++) {
301 if (fieldIndex == 1) {
302 freqs[i] = ppiArray[i].maxMhz * 1_000_000L;
303 } else if (fieldIndex == 2) {
304 freqs[i] = ppiArray[i].currentMhz * 1_000_000L;
305 } else {
306 freqs[i] = -1L;
307 }
308
309 if (freqs[i] == 0) {
310 freqs[i] = getProcessorIdentifier().getVendorFreq();
311 }
312 }
313 return freqs;
314 }
315
316 @Override
317 public double[] getSystemLoadAverage(int nelem) {
318 if (nelem < 1 || nelem > 3) {
319 throw new IllegalArgumentException("Must include from one to three elements.");
320 }
321 return LoadAverage.queryLoadAverage(nelem);
322 }
323
324 @Override
325 public long[][] queryProcessorCpuLoadTicks() {
326
327 List<String> instances;
328 List<Long> systemList;
329 List<Long> userList;
330 List<Long> irqList;
331 List<Long> softIrqList;
332 List<Long> idleList;
333
334 List<Long> baseList = null;
335 List<Long> systemUtility = null;
336 List<Long> processorUtility = null;
337 List<Long> processorUtilityBase = null;
338 List<Long> initSystemList = null;
339 List<Long> initUserList = null;
340 List<Long> initBase = null;
341 List<Long> initSystemUtility = null;
342 List<Long> initProcessorUtility = null;
343 List<Long> initProcessorUtilityBase = null;
344 if (USE_CPU_UTILITY) {
345 Pair<List<String>, Map<ProcessorUtilityTickCountProperty, List<Long>>> instanceValuePair = processorUtilityCounters
346 .get();
347 instances = instanceValuePair.getA();
348 Map<ProcessorUtilityTickCountProperty, List<Long>> valueMap = instanceValuePair.getB();
349 systemList = valueMap.get(ProcessorUtilityTickCountProperty.PERCENTPRIVILEGEDTIME);
350 userList = valueMap.get(ProcessorUtilityTickCountProperty.PERCENTUSERTIME);
351 irqList = valueMap.get(ProcessorUtilityTickCountProperty.PERCENTINTERRUPTTIME);
352 softIrqList = valueMap.get(ProcessorUtilityTickCountProperty.PERCENTDPCTIME);
353
354 idleList = valueMap.get(ProcessorUtilityTickCountProperty.PERCENTPROCESSORTIME);
355 baseList = valueMap.get(ProcessorUtilityTickCountProperty.TIMESTAMP_SYS100NS);
356
357 systemUtility = valueMap.get(ProcessorUtilityTickCountProperty.PERCENTPRIVILEGEDUTILITY);
358 processorUtility = valueMap.get(ProcessorUtilityTickCountProperty.PERCENTPROCESSORUTILITY);
359 processorUtilityBase = valueMap.get(ProcessorUtilityTickCountProperty.PERCENTPROCESSORUTILITY_BASE);
360
361 initSystemList = initialUtilityCounters.get(ProcessorUtilityTickCountProperty.PERCENTPRIVILEGEDTIME);
362 initUserList = initialUtilityCounters.get(ProcessorUtilityTickCountProperty.PERCENTUSERTIME);
363 initBase = initialUtilityCounters.get(ProcessorUtilityTickCountProperty.TIMESTAMP_SYS100NS);
364
365 initSystemUtility = initialUtilityCounters.get(ProcessorUtilityTickCountProperty.PERCENTPRIVILEGEDUTILITY);
366 initProcessorUtility = initialUtilityCounters
367 .get(ProcessorUtilityTickCountProperty.PERCENTPROCESSORUTILITY);
368 initProcessorUtilityBase = initialUtilityCounters
369 .get(ProcessorUtilityTickCountProperty.PERCENTPROCESSORUTILITY_BASE);
370 } else {
371 Pair<List<String>, Map<ProcessorTickCountProperty, List<Long>>> instanceValuePair = ProcessorInformation
372 .queryProcessorCounters();
373 instances = instanceValuePair.getA();
374 Map<ProcessorTickCountProperty, List<Long>> valueMap = instanceValuePair.getB();
375 systemList = valueMap.get(ProcessorTickCountProperty.PERCENTPRIVILEGEDTIME);
376 userList = valueMap.get(ProcessorTickCountProperty.PERCENTUSERTIME);
377 irqList = valueMap.get(ProcessorTickCountProperty.PERCENTINTERRUPTTIME);
378 softIrqList = valueMap.get(ProcessorTickCountProperty.PERCENTDPCTIME);
379
380 idleList = valueMap.get(ProcessorTickCountProperty.PERCENTPROCESSORTIME);
381 }
382
383 int ncpu = getLogicalProcessorCount();
384 long[][] ticks = new long[ncpu][TickType.values().length];
385 if (instances.isEmpty() || systemList == null || userList == null || irqList == null || softIrqList == null
386 || idleList == null
387 || (USE_CPU_UTILITY && (baseList == null || systemUtility == null || processorUtility == null
388 || processorUtilityBase == null || initSystemList == null || initUserList == null
389 || initBase == null || initSystemUtility == null || initProcessorUtility == null
390 || initProcessorUtilityBase == null))) {
391 return ticks;
392 }
393 for (String instance : instances) {
394 int cpu = instance.contains(",") ? numaNodeProcToLogicalProcMap.getOrDefault(instance, 0)
395 : ParseUtil.parseIntOrDefault(instance, 0);
396 if (cpu >= ncpu) {
397 continue;
398 }
399 ticks[cpu][TickType.SYSTEM.getIndex()] = systemList.get(cpu);
400 ticks[cpu][TickType.USER.getIndex()] = userList.get(cpu);
401 ticks[cpu][TickType.IRQ.getIndex()] = irqList.get(cpu);
402 ticks[cpu][TickType.SOFTIRQ.getIndex()] = softIrqList.get(cpu);
403 ticks[cpu][TickType.IDLE.getIndex()] = idleList.get(cpu);
404
405
406 if (USE_CPU_UTILITY) {
407
408
409
410
411
412
413
414
415
416
417 long deltaT = baseList.get(cpu) - initBase.get(cpu);
418 if (deltaT > 0) {
419
420 long deltaBase = processorUtilityBase.get(cpu) - initProcessorUtilityBase.get(cpu);
421
422
423
424
425 long multiplier = lazilyCalculateMultiplier(deltaBase, deltaT);
426
427
428 if (multiplier > 0) {
429
430 long deltaProc = processorUtility.get(cpu) - initProcessorUtility.get(cpu);
431 long deltaSys = systemUtility.get(cpu) - initSystemUtility.get(cpu);
432
433
434
435 long newUser = initUserList.get(cpu) + multiplier * (deltaProc - deltaSys) / 100;
436 long newSystem = initSystemList.get(cpu) + multiplier * deltaSys / 100;
437
438
439 long delta = newUser - ticks[cpu][TickType.USER.getIndex()];
440 ticks[cpu][TickType.USER.getIndex()] = newUser;
441
442 delta += newSystem - ticks[cpu][TickType.SYSTEM.getIndex()];
443 ticks[cpu][TickType.SYSTEM.getIndex()] = newSystem;
444
445 ticks[cpu][TickType.IDLE.getIndex()] -= delta;
446 }
447 }
448 }
449
450
451 ticks[cpu][TickType.SYSTEM.getIndex()] -= ticks[cpu][TickType.IRQ.getIndex()]
452 + ticks[cpu][TickType.SOFTIRQ.getIndex()];
453
454
455
456 ticks[cpu][TickType.SYSTEM.getIndex()] /= 10_000L;
457 ticks[cpu][TickType.USER.getIndex()] /= 10_000L;
458 ticks[cpu][TickType.IRQ.getIndex()] /= 10_000L;
459 ticks[cpu][TickType.SOFTIRQ.getIndex()] /= 10_000L;
460 ticks[cpu][TickType.IDLE.getIndex()] /= 10_000L;
461 }
462
463 return ticks;
464 }
465
466
467
468
469
470
471
472
473 private synchronized long lazilyCalculateMultiplier(long deltaBase, long deltaT) {
474 if (utilityBaseMultiplier == null) {
475
476
477
478 if (deltaT >> 32 > 0) {
479 initialUtilityCounters = processorUtilityCounters.get().getB();
480 return 0L;
481 }
482
483
484 if (deltaBase <= 0) {
485 deltaBase += 1L << 32;
486 }
487 long multiplier = Math.round((double) deltaT / deltaBase);
488
489
490 if (deltaT < 50_000_000L) {
491 return multiplier;
492 }
493 utilityBaseMultiplier = multiplier;
494 }
495 return utilityBaseMultiplier;
496 }
497
498 private static Pair<List<String>, Map<ProcessorUtilityTickCountProperty, List<Long>>> queryProcessorUtilityCounters() {
499 return ProcessorInformation.queryProcessorCapacityCounters();
500 }
501
502 @Override
503 public long queryContextSwitches() {
504 return SystemInformation.queryContextSwitchCounters().getOrDefault(ContextSwitchProperty.CONTEXTSWITCHESPERSEC,
505 0L);
506 }
507
508 @Override
509 public long queryInterrupts() {
510 return ProcessorInformation.queryInterruptCounters().getOrDefault(InterruptsProperty.INTERRUPTSPERSEC, 0L);
511 }
512 }