View Javadoc
1   /**
2    * Oshi (https://github.com/dblock/oshi)
3    * 
4    * Copyright (c) 2010 - 2015 The Oshi Project Team
5    * 
6    * All rights reserved. This program and the accompanying materials
7    * are made available under the terms of the Eclipse Public License v1.0
8    * which accompanies this distribution, and is available at
9    * http://www.eclipse.org/legal/epl-v10.html
10   * 
11   * Contributors:
12   * dblock[at]dblock[dot]org
13   * alessandro[at]perucchi[dot]org
14   * widdis[at]gmail[dot]com
15   * https://github.com/dblock/oshi/graphs/contributors
16   */
17  package oshi.software.os.windows.nt;
18  
19  import java.lang.management.ManagementFactory;
20  import java.util.regex.Matcher;
21  import java.util.regex.Pattern;
22  
23  import oshi.hardware.Processor;
24  import oshi.software.os.windows.nt.Pdh.PdhFmtCounterValue;
25  import oshi.util.ParseUtil;
26  
27  import com.sun.jna.LastErrorException;
28  import com.sun.jna.Native;
29  import com.sun.jna.platform.win32.WinBase;
30  import com.sun.jna.platform.win32.WinBase.SYSTEM_INFO;
31  import com.sun.jna.ptr.IntByReference;
32  import com.sun.jna.ptr.PointerByReference;
33  
34  /**
35   * A CPU as defined in Windows registry.
36   * 
37   * @author dblock[at]dblock[dot]org
38   * @author alessio.fachechi[at]gmail[dot]com
39   * @author widdis[at]gmail[dot]com
40   */
41  @SuppressWarnings("restriction")
42  public class CentralProcessor implements Processor {
43  	private static final java.lang.management.OperatingSystemMXBean OS_MXBEAN = ManagementFactory
44  			.getOperatingSystemMXBean();
45  	private static boolean sunMXBean;
46  	static {
47  		try {
48  			Class.forName("com.sun.management.OperatingSystemMXBean");
49  			// Initialize CPU usage
50  			((com.sun.management.OperatingSystemMXBean) OS_MXBEAN)
51  					.getSystemCpuLoad();
52  			sunMXBean = true;
53  		} catch (ClassNotFoundException e) {
54  			sunMXBean = false;
55  		}
56  	}
57  	// Maintain two sets of previous ticks to be used for calculating usage
58  	// between them.
59  	// System ticks (static)
60  	private static long tickTime = System.currentTimeMillis();
61  	private static long[] prevTicks = new long[4];
62  	private static long[] curTicks = new long[4];
63  	static {
64  		updateSystemTicks();
65  		System.arraycopy(curTicks, 0, prevTicks, 0, curTicks.length);
66  	}
67  
68  	// Maintain similar arrays for per-processor ticks (class variables)
69  	private long procTickTime = System.currentTimeMillis();
70  	private long[] prevProcTicks = new long[4];
71  	private long[] curProcTicks = new long[4];
72  
73  	// Initialize numCPU and open a Performance Data Helper Thread for
74  	// monitoring each processor
75  	private static PointerByReference phQuery = new PointerByReference();
76  	private static final IntByReference zero = new IntByReference(0);
77  	private static final int numCPU;
78  	static {
79  		// Get number of processors
80  		SYSTEM_INFO sysinfo = new SYSTEM_INFO();
81  		Kernel32.INSTANCE.GetSystemInfo(sysinfo);
82  		numCPU = sysinfo.dwNumberOfProcessors.intValue();
83  
84  		// Set up query for this processor
85  		int ret = Pdh.INSTANCE.PdhOpenQuery(null, zero, phQuery);
86  		if (ret != 0)
87  			throw new LastErrorException("Cannot open PDH query. Error code: "
88  					+ String.format("0x%08X", ret));
89  
90  		// Set up hook to close the query on shutdown
91  		Runtime.getRuntime().addShutdownHook(new Thread() {
92  			@Override
93  			public void run() {
94  				Pdh.INSTANCE.PdhCloseQuery(phQuery.getValue());
95  			}
96  		});
97  	}
98  	// Set up a counter for each processor
99  	private static PointerByReference[] phUserCounters = new PointerByReference[numCPU];
100 	private static PointerByReference[] phIdleCounters = new PointerByReference[numCPU];
101 	static {
102 		for (int p = 0; p < numCPU; p++) {
103 			// Options are (only need 2 to calculate all)
104 			// "\Processor(0)\% processor time"
105 			// "\Processor(0)\% idle time" (1 - processor)
106 			// "\Processor(0)\% privileged time" (subset of processor)
107 			// "\Processor(0)\% user time" (other subset of processor)
108 			// Note need to make \ = \\ for Java Strings and %% for format
109 			String counterPath = String.format("\\Processor(%d)\\%% user time",
110 					p);
111 			phUserCounters[p] = new PointerByReference();
112 			int ret = Pdh.INSTANCE.PdhAddEnglishCounterA(phQuery.getValue(),
113 					counterPath, zero, phUserCounters[p]);
114 			if (ret != 0)
115 				throw new LastErrorException(
116 						"Cannot add PDH Counter for % user time for processor "
117 								+ p + ". Error code: "
118 								+ String.format("0x%08X", ret));
119 			counterPath = String.format("\\Processor(%d)\\%% idle time", p);
120 			phIdleCounters[p] = new PointerByReference();
121 			ret = Pdh.INSTANCE.PdhAddEnglishCounterA(phQuery.getValue(),
122 					counterPath, zero, phIdleCounters[p]);
123 			if (ret != 0)
124 				throw new LastErrorException(
125 						"Cannot add PDH Counter for % idle time for processor "
126 								+ p + ". Error code: "
127 								+ String.format("0x%08X", ret));
128 		}
129 		// Initialize by collecting data the first time
130 		int ret = Pdh.INSTANCE.PdhCollectQueryData(phQuery.getValue());
131 		if (ret != 0)
132 			throw new LastErrorException(
133 					"Cannot collect PDH query data. Error code: "
134 							+ String.format("0x%08X", ret));
135 	}
136 	// Set up array to maintain current ticks for rapid reference. This array
137 	// will be updated in place and used to increment ticks based on processor
138 	// data helper which only gives % between reads
139 	private static long[][] allProcessorTicks = new long[numCPU][4];
140 	private static long allProcTickTime = System.currentTimeMillis() - 100;
141 
142 	private int processorNumber;
143 	private String cpuVendor;
144 	private String cpuName;
145 	private String cpuIdentifier;
146 	private Long cpuVendorFreq;
147 
148 	/**
149 	 * Create a Processor with the given number
150 	 * 
151 	 * @param procNo
152 	 */
153 	public CentralProcessor(int procNo) {
154 		if (procNo >= numCPU)
155 			throw new IllegalArgumentException("Processor number (" + procNo
156 					+ ") must be less than the number of CPUs: " + numCPU);
157 		this.processorNumber = procNo;
158 		updateProcessorTicks();
159 		System.arraycopy(allProcessorTicks[processorNumber], 0, curProcTicks,
160 				0, curProcTicks.length);
161 	}
162 
163 	/**
164 	 * {@inheritDoc}
165 	 */
166 	@Override
167 	public int getProcessorNumber() {
168 		return processorNumber;
169 	}
170 
171 	/**
172 	 * Vendor identifier, eg. GenuineIntel.
173 	 * 
174 	 * @return Processor vendor.
175 	 */
176 	@Override
177 	public String getVendor() {
178 		return this.cpuVendor;
179 	}
180 
181 	/**
182 	 * Set processor vendor.
183 	 * 
184 	 * @param vendor
185 	 *            Vendor.
186 	 */
187 	@Override
188 	public void setVendor(String vendor) {
189 		this.cpuVendor = vendor;
190 	}
191 
192 	/**
193 	 * Name, eg. Intel(R) Core(TM)2 Duo CPU T7300 @ 2.00GHz
194 	 * 
195 	 * @return Processor name.
196 	 */
197 	@Override
198 	public String getName() {
199 		return this.cpuName;
200 	}
201 
202 	/**
203 	 * Set processor name.
204 	 * 
205 	 * @param name
206 	 *            Name.
207 	 */
208 	@Override
209 	public void setName(String name) {
210 		this.cpuName = name;
211 	}
212 
213 	/**
214 	 * Vendor frequency (in Hz), eg. for processor named Intel(R) Core(TM)2 Duo
215 	 * CPU T7300 @ 2.00GHz the vendor frequency is 2000000000.
216 	 * 
217 	 * @return Processor frequency or -1 if unknown.
218 	 */
219 	@Override
220 	public long getVendorFreq() {
221 		if (this.cpuVendorFreq == null) {
222 			Pattern pattern = Pattern.compile("@ (.*)$");
223 			Matcher matcher = pattern.matcher(getName());
224 
225 			if (matcher.find()) {
226 				String unit = matcher.group(1);
227 				this.cpuVendorFreq = Long.valueOf(ParseUtil.parseHertz(unit));
228 			} else {
229 				this.cpuVendorFreq = Long.valueOf(-1L);
230 			}
231 		}
232 
233 		return this.cpuVendorFreq.longValue();
234 	}
235 
236 	/**
237 	 * Set vendor frequency.
238 	 * 
239 	 * @param freq
240 	 *            Frequency.
241 	 */
242 	@Override
243 	public void setVendorFreq(long freq) {
244 		this.cpuVendorFreq = Long.valueOf(freq);
245 	}
246 
247 	/**
248 	 * Identifier, eg. x86 Family 6 Model 15 Stepping 10.
249 	 * 
250 	 * @return Processor identifier.
251 	 */
252 	@Override
253 	public String getIdentifier() {
254 		return this.cpuIdentifier;
255 	}
256 
257 	/**
258 	 * Set processor identifier.
259 	 * 
260 	 * @param identifier
261 	 *            Identifier.
262 	 */
263 	@Override
264 	public void setIdentifier(String identifier) {
265 		this.cpuIdentifier = identifier;
266 	}
267 
268 	/**
269 	 * {@inheritDoc}
270 	 */
271 	@Override
272 	public boolean isCpu64bit() {
273 		throw new UnsupportedOperationException();
274 	}
275 
276 	/**
277 	 * {@inheritDoc}
278 	 */
279 	@Override
280 	public void setCpu64(boolean cpu64) {
281 		throw new UnsupportedOperationException();
282 	}
283 
284 	/**
285 	 * {@inheritDoc}
286 	 */
287 	@Override
288 	public String getStepping() {
289 		throw new UnsupportedOperationException();
290 	}
291 
292 	/**
293 	 * {@inheritDoc}
294 	 */
295 	@Override
296 	public void setStepping(String stepping) {
297 		throw new UnsupportedOperationException();
298 	}
299 
300 	/**
301 	 * {@inheritDoc}
302 	 */
303 	@Override
304 	public String getModel() {
305 		throw new UnsupportedOperationException();
306 	}
307 
308 	/**
309 	 * {@inheritDoc}
310 	 */
311 	@Override
312 	public void setModel(String model) {
313 		throw new UnsupportedOperationException();
314 	}
315 
316 	/**
317 	 * {@inheritDoc}
318 	 */
319 	@Override
320 	public String getFamily() {
321 		throw new UnsupportedOperationException();
322 	}
323 
324 	/**
325 	 * {@inheritDoc}
326 	 */
327 	@Override
328 	public void setFamily(String family) {
329 		throw new UnsupportedOperationException();
330 	}
331 
332 	/**
333 	 * {@inheritDoc}
334 	 */
335 	@Override
336 	@Deprecated
337 	public float getLoad() {
338 		// TODO Remove in 2.0
339 		return (float) getSystemCpuLoadBetweenTicks() * 100;
340 	}
341 
342 	/**
343 	 * {@inheritDoc}
344 	 */
345 	@Override
346 	public double getSystemCpuLoadBetweenTicks() {
347 		// Check if > ~ 0.95 seconds since last tick count.
348 		long now = System.currentTimeMillis();
349 		boolean update = (now - tickTime > 950);
350 		if (update) {
351 			// Enough time has elapsed.
352 			// Update latest
353 			updateSystemTicks();
354 			tickTime = now;
355 		}
356 		// Calculate total
357 		long total = 0;
358 		for (int i = 0; i < curTicks.length; i++) {
359 			total += (curTicks[i] - prevTicks[i]);
360 		}
361 		// Calculate idle from last field [3]
362 		long idle = curTicks[3] - prevTicks[3];
363 
364 		// Copy latest ticks to earlier position for next call
365 		if (update) {
366 			System.arraycopy(curTicks, 0, prevTicks, 0, curTicks.length);
367 		}
368 
369 		// return
370 		if (total > 0 && idle >= 0) {
371 			return (double) (total - idle) / total;
372 		}
373 		return 0d;
374 	}
375 
376 	/**
377 	 * {@inheritDoc}
378 	 */
379 	@Override
380 	public long[] getSystemCpuLoadTicks() {
381 		updateSystemTicks();
382 		// Make a copy
383 		long[] ticks = new long[curTicks.length];
384 		System.arraycopy(curTicks, 0, ticks, 0, curTicks.length);
385 		return ticks;
386 	}
387 
388 	/**
389 	 * Updates system tick information from native call to GetSystemTimes().
390 	 * Array with four elements representing clock ticks or milliseconds
391 	 * (platform dependent) spent in User (0), Nice (1), System (2), and Idle
392 	 * (3) states. By measuring the difference between ticks across a time
393 	 * interval, CPU load over that interval may be calculated.
394 	 */
395 	private static void updateSystemTicks() {
396 		WinBase.FILETIME lpIdleTime = new WinBase.FILETIME();
397 		WinBase.FILETIME lpKernelTime = new WinBase.FILETIME();
398 		WinBase.FILETIME lpUserTime = new WinBase.FILETIME();
399 		if (0 == Kernel32.INSTANCE.GetSystemTimes(lpIdleTime, lpKernelTime,
400 				lpUserTime))
401 			throw new LastErrorException("Error code: " + Native.getLastError());
402 		// Array order is user,nice,kernel,idle
403 		curTicks[0] = lpUserTime.toLong() + Kernel32.WIN32_TIME_OFFSET;
404 		curTicks[1] = 0L; // Windows is not 'nice'
405 		curTicks[2] = lpKernelTime.toLong() - lpIdleTime.toLong();
406 		curTicks[3] = lpIdleTime.toLong() + Kernel32.WIN32_TIME_OFFSET;
407 	}
408 
409 	/**
410 	 * {@inheritDoc}
411 	 */
412 	@Override
413 	public double getSystemCpuLoad() {
414 		if (sunMXBean) {
415 			return ((com.sun.management.OperatingSystemMXBean) OS_MXBEAN)
416 					.getSystemCpuLoad();
417 		}
418 		return getSystemCpuLoadBetweenTicks();
419 	}
420 
421 	/**
422 	 * {@inheritDoc}
423 	 */
424 	@Override
425 	public double getSystemLoadAverage() {
426 		// Expected to be -1 for Windows
427 		return OS_MXBEAN.getSystemLoadAverage();
428 	}
429 
430 	/**
431 	 * {@inheritDoc}
432 	 */
433 	@Override
434 	public double getProcessorCpuLoadBetweenTicks() {
435 		// Check if > ~ 0.95 seconds since last tick count.
436 		long now = System.currentTimeMillis();
437 		if (now - procTickTime > 950) {
438 			// Enough time has elapsed. Update array in place
439 			updateProcessorTicks();
440 			// Copy arrays in place
441 			System.arraycopy(curProcTicks, 0, prevProcTicks, 0,
442 					curProcTicks.length);
443 			System.arraycopy(allProcessorTicks[processorNumber], 0,
444 					curProcTicks, 0, curProcTicks.length);
445 			procTickTime = now;
446 		}
447 		long total = 0;
448 		for (int i = 0; i < curProcTicks.length; i++) {
449 			total += (curProcTicks[i] - prevProcTicks[i]);
450 		}
451 		// Calculate idle from last field [3]
452 		long idle = curProcTicks[3] - prevProcTicks[3];
453 		// update
454 		return (total > 0 && idle >= 0) ? (double) (total - idle) / total : 0d;
455 	}
456 
457 	/**
458 	 * {@inheritDoc}
459 	 */
460 	public long[] getProcessorCpuLoadTicks() {
461 		updateProcessorTicks();
462 		return allProcessorTicks[processorNumber];
463 	}
464 
465 	/**
466 	 * Updates the tick array for all processors if more than 100ms has elapsed
467 	 * since the last update. This permits using the allProcessorTicks as a
468 	 * cache when iterating over processors since pdh query updates all counters
469 	 * at once so we can't separate individual processors
470 	 */
471 	private void updateProcessorTicks() {
472 		// Update no more frequently than 100ms so this is only triggered once
473 		// during iteration over Processors
474 		long now = System.currentTimeMillis();
475 		if (now - allProcTickTime < 100)
476 			return;
477 
478 		// This call updates all process counters to % used since last call
479 		int ret = Pdh.INSTANCE.PdhCollectQueryData(phQuery.getValue());
480 		if (ret != 0)
481 			throw new LastErrorException(
482 					"Cannot collect PDH query data. Error code: "
483 							+ String.format("0x%08X", ret));
484 		// Multiply % usage times elapsed MS to recreate ticks, then increment
485 		long elapsed = now - allProcTickTime;
486 		for (int cpu = 0; cpu < numCPU; cpu++) {
487 			PdhFmtCounterValue phUserCounterValue = new PdhFmtCounterValue();
488 			ret = Pdh.INSTANCE.PdhGetFormattedCounterValue(
489 					phUserCounters[cpu].getValue(), Pdh.PDH_FMT_LARGE
490 							| Pdh.PDH_FMT_1000, null, phUserCounterValue);
491 			if (ret != 0)
492 				throw new LastErrorException(
493 						"Cannot get PDH User % counter value. Error code: "
494 								+ String.format("0x%08X", ret));
495 
496 			PdhFmtCounterValue phIdleCounterValue = new PdhFmtCounterValue();
497 			ret = Pdh.INSTANCE.PdhGetFormattedCounterValue(
498 					phIdleCounters[cpu].getValue(), Pdh.PDH_FMT_LARGE
499 							| Pdh.PDH_FMT_1000, null, phIdleCounterValue);
500 			if (ret != 0)
501 				throw new LastErrorException(
502 						"Cannot get PDH Idle % counter value. Error code: "
503 								+ String.format("0x%08X", ret));
504 
505 			// Returns results in 1000's of percent, e.g. 5% is 5000
506 			// Multiply by elapsed to get total ms and Divide by 100 * 1000
507 			// Putting division at end avoids need to cast division to double
508 			long user = elapsed * phUserCounterValue.value.largeValue / 100000;
509 			long idle = elapsed * phIdleCounterValue.value.largeValue / 100000;
510 			// Elasped is only since last read, so increment previous value
511 			allProcessorTicks[cpu][0] += user;
512 			// allProcessorTicks[cpu][1] is ignored, Windows is not nice
513 			allProcessorTicks[cpu][2] += (elapsed - user - idle); // u+i+sys=100%
514 			allProcessorTicks[cpu][3] += idle;
515 		}
516 		allProcTickTime = now;
517 	}
518 
519 	@Override
520 	public String toString() {
521 		return getName();
522 	}
523 }