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.mac.local;
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.mac.local.SystemB.HostCpuLoadInfo;
25  import oshi.util.FormatUtil;
26  import oshi.util.ParseUtil;
27  
28  import com.sun.jna.LastErrorException;
29  import com.sun.jna.Memory;
30  import com.sun.jna.Native;
31  import com.sun.jna.Pointer;
32  import com.sun.jna.ptr.IntByReference;
33  import com.sun.jna.ptr.PointerByReference;
34  
35  /**
36   * A CPU.
37   * 
38   * @author alessandro[at]perucchi[dot]org
39   * @author alessio.fachechi[at]gmail[dot]com
40   * @author widdis[at]gmail[dot]com
41   */
42  @SuppressWarnings("restriction")
43  public class CentralProcessor implements Processor {
44  	private static final java.lang.management.OperatingSystemMXBean OS_MXBEAN = ManagementFactory
45  			.getOperatingSystemMXBean();;
46  	private static boolean sunMXBean;
47  	static {
48  		try {
49  			Class.forName("com.sun.management.OperatingSystemMXBean");
50  			// Initialize CPU usage
51  			((com.sun.management.OperatingSystemMXBean) OS_MXBEAN)
52  					.getSystemCpuLoad();
53  			sunMXBean = true;
54  		} catch (ClassNotFoundException e) {
55  			sunMXBean = false;
56  		}
57  	}
58  	// Maintain two sets of previous ticks to be used for calculating usage
59  	// between them.
60  	// System ticks (static)
61  	private static long tickTime = System.currentTimeMillis();
62  	private static long[] prevTicks = new long[4];
63  	private static long[] curTicks = new long[4];
64  	static {
65  		updateSystemTicks();
66  		System.arraycopy(curTicks, 0, prevTicks, 0, curTicks.length);
67  	}
68  
69  	// Maintain similar arrays for per-processor ticks (class variables)
70  	private long procTickTime = System.currentTimeMillis();
71  	private long[] prevProcTicks = new long[4];
72  	private long[] curProcTicks = new long[4];
73  
74  	// Initialize numCPU
75  	private static int numCPU = 0;
76  	static {
77  		IntByReference size = new IntByReference(SystemB.INT_SIZE);
78  		Pointer p = new Memory(size.getValue());
79  		if (0 != SystemB.INSTANCE.sysctlbyname("hw.logicalcpu", p, size, null,
80  				0))
81  			throw new LastErrorException("Error code: " + Native.getLastError());
82  		numCPU = p.getInt(0);
83  	}
84  	// Set up array to maintain current ticks for rapid reference. This array
85  	// will be updated in place and used as a cache to avoid rereading file
86  	// while iterating processors
87  	private static long[][] allProcessorTicks = new long[numCPU][4];
88  	private static long allProcTickTime = 0;
89  
90  	private int processorNumber;
91  	private String cpuVendor;
92  	private String cpuName;
93  	private String cpuIdentifier = null;
94  	private String cpuStepping;
95  	private String cpuModel;
96  	private String cpuFamily;
97  	private Long cpuVendorFreq = null;
98  	private Boolean cpu64;
99  
100 	/**
101 	 * Create a Processor with the given number
102 	 * 
103 	 * @param procNo
104 	 */
105 	public CentralProcessor(int procNo) {
106 		if (procNo >= numCPU)
107 			throw new IllegalArgumentException("Processor number (" + procNo
108 					+ ") must be less than the number of CPUs: " + numCPU);
109 		this.processorNumber = procNo;
110 		updateProcessorTicks();
111 		System.arraycopy(allProcessorTicks[processorNumber], 0, curProcTicks,
112 				0, curProcTicks.length);
113 	}
114 
115 	/**
116 	 * {@inheritDoc}
117 	 */
118 	@Override
119 	public int getProcessorNumber() {
120 		return processorNumber;
121 	}
122 
123 	/**
124 	 * Vendor identifier, eg. GenuineIntel.
125 	 * 
126 	 * @return Processor vendor.
127 	 */
128 	@Override
129 	public String getVendor() {
130 		if (this.cpuVendor == null) {
131 			int[] mib = { SystemB.CTL_MACHDEP, SystemB.MACHDEP_CPU,
132 					SystemB.MACHDEP_CPU_VENDOR };
133 			IntByReference size = new IntByReference();
134 			if (0 != SystemB.INSTANCE.sysctl(mib, mib.length, null, size, null,
135 					0))
136 				throw new LastErrorException("Error code: "
137 						+ Native.getLastError());
138 			Pointer p = new Memory(size.getValue() + 1);
139 			if (0 != SystemB.INSTANCE.sysctl(mib, mib.length, p, size, null, 0))
140 				throw new LastErrorException("Error code: "
141 						+ Native.getLastError());
142 			this.cpuVendor = p.getString(0);
143 		}
144 		return this.cpuVendor;
145 	}
146 
147 	/**
148 	 * Set processor vendor.
149 	 * 
150 	 * @param vendor
151 	 *            Vendor.
152 	 */
153 	@Override
154 	public void setVendor(String vendor) {
155 		this.cpuVendor = vendor;
156 	}
157 
158 	/**
159 	 * Name, eg. Intel(R) Core(TM)2 Duo CPU T7300 @ 2.00GHz
160 	 * 
161 	 * @return Processor name.
162 	 */
163 	@Override
164 	public String getName() {
165 		if (this.cpuName == null) {
166 			int[] mib = { SystemB.CTL_MACHDEP, SystemB.MACHDEP_CPU,
167 					SystemB.MACHDEP_CPU_BRAND_STRING };
168 			IntByReference size = new IntByReference();
169 			if (0 != SystemB.INSTANCE.sysctl(mib, mib.length, null, size, null,
170 					0))
171 				throw new LastErrorException("Error code: "
172 						+ Native.getLastError());
173 			Pointer p = new Memory(size.getValue() + 1);
174 			if (0 != SystemB.INSTANCE.sysctl(mib, mib.length, p, size, null, 0))
175 				throw new LastErrorException("Error code: "
176 						+ Native.getLastError());
177 			this.cpuName = p.getString(0);
178 		}
179 		return this.cpuName;
180 	}
181 
182 	/**
183 	 * Set processor name.
184 	 * 
185 	 * @param name
186 	 *            Name.
187 	 */
188 	@Override
189 	public void setName(String name) {
190 		this.cpuName = name;
191 	}
192 
193 	/**
194 	 * Vendor frequency (in Hz), eg. for processor named Intel(R) Core(TM)2 Duo
195 	 * CPU T7300 @ 2.00GHz the vendor frequency is 2000000000.
196 	 * 
197 	 * @return Processor frequency or -1 if unknown.
198 	 */
199 	@Override
200 	public long getVendorFreq() {
201 		if (this.cpuVendorFreq == null) {
202 			Pattern pattern = Pattern.compile("@ (.*)$");
203 			Matcher matcher = pattern.matcher(getName());
204 
205 			if (matcher.find()) {
206 				String unit = matcher.group(1);
207 				this.cpuVendorFreq = Long.valueOf(ParseUtil.parseHertz(unit));
208 			} else {
209 				this.cpuVendorFreq = Long.valueOf(-1L);
210 			}
211 		}
212 
213 		return this.cpuVendorFreq.longValue();
214 	}
215 
216 	/**
217 	 * Set vendor frequency.
218 	 * 
219 	 * @param freq
220 	 *            Frequency.
221 	 */
222 	@Override
223 	public void setVendorFreq(long freq) {
224 		this.cpuVendorFreq = Long.valueOf(freq);
225 	}
226 
227 	/**
228 	 * Identifier, eg. x86 Family 6 Model 15 Stepping 10.
229 	 * 
230 	 * @return Processor identifier.
231 	 */
232 	@Override
233 	public String getIdentifier() {
234 		if (this.cpuIdentifier == null) {
235 			StringBuilder sb = new StringBuilder();
236 			if (getVendor().contentEquals("GenuineIntel"))
237 				sb.append(isCpu64bit() ? "Intel64" : "x86");
238 			else
239 				sb.append(getVendor());
240 			sb.append(" Family ").append(getFamily());
241 			sb.append(" Model ").append(getModel());
242 			sb.append(" Stepping ").append(getStepping());
243 			this.cpuIdentifier = sb.toString();
244 		}
245 		return this.cpuIdentifier;
246 	}
247 
248 	/**
249 	 * Set processor identifier.
250 	 * 
251 	 * @param identifier
252 	 *            Identifier.
253 	 */
254 	@Override
255 	public void setIdentifier(String identifier) {
256 		this.cpuIdentifier = identifier;
257 	}
258 
259 	/**
260 	 * Is CPU 64bit?
261 	 * 
262 	 * @return True if cpu is 64bit.
263 	 */
264 	@Override
265 	public boolean isCpu64bit() {
266 		if (this.cpu64 == null) {
267 			int[] mib = { SystemB.CTL_HW, SystemB.HW_CPU64BIT_CAPABLE };
268 			IntByReference size = new IntByReference(SystemB.INT_SIZE);
269 			Pointer p = new Memory(size.getValue());
270 			if (0 != SystemB.INSTANCE.sysctl(mib, mib.length, p, size, null, 0))
271 				throw new LastErrorException("Error code: "
272 						+ Native.getLastError());
273 			this.cpu64 = p.getInt(0) != 0;
274 		}
275 		return this.cpu64.booleanValue();
276 	}
277 
278 	/**
279 	 * Set flag is cpu is 64bit.
280 	 * 
281 	 * @param cpu64
282 	 *            True if cpu is 64.
283 	 */
284 	@Override
285 	public void setCpu64(boolean cpu64) {
286 		this.cpu64 = Boolean.valueOf(cpu64);
287 	}
288 
289 	/**
290 	 * @return the stepping
291 	 */
292 	@Override
293 	public String getStepping() {
294 		if (this.cpuStepping == null) {
295 			int[] mib = { SystemB.CTL_MACHDEP, SystemB.MACHDEP_CPU,
296 					SystemB.MACHDEP_CPU_STEPPING };
297 			IntByReference size = new IntByReference(SystemB.INT_SIZE);
298 			Pointer p = new Memory(size.getValue());
299 			if (0 != SystemB.INSTANCE.sysctl(mib, mib.length, p, size, null, 0))
300 				throw new LastErrorException("Error code: "
301 						+ Native.getLastError());
302 			this.cpuStepping = Integer.toString(p.getInt(0));
303 		}
304 		return this.cpuStepping;
305 	}
306 
307 	/**
308 	 * @param stepping
309 	 *            the stepping to set
310 	 */
311 	@Override
312 	public void setStepping(String stepping) {
313 		this.cpuStepping = stepping;
314 	}
315 
316 	/**
317 	 * @return the model
318 	 */
319 	@Override
320 	public String getModel() {
321 		if (this.cpuModel == null) {
322 			int[] mib = { SystemB.CTL_MACHDEP, SystemB.MACHDEP_CPU,
323 					SystemB.MACHDEP_CPU_MODEL };
324 			IntByReference size = new IntByReference(SystemB.INT_SIZE);
325 			Pointer p = new Memory(size.getValue());
326 			if (0 != SystemB.INSTANCE.sysctl(mib, mib.length, p, size, null, 0))
327 				throw new LastErrorException("Error code: "
328 						+ Native.getLastError());
329 			this.cpuModel = Integer.toString(p.getInt(0));
330 		}
331 		return this.cpuModel;
332 	}
333 
334 	/**
335 	 * @param model
336 	 *            the model to set
337 	 */
338 	@Override
339 	public void setModel(String model) {
340 		this.cpuModel = model;
341 	}
342 
343 	/**
344 	 * @return the family
345 	 */
346 	@Override
347 	public String getFamily() {
348 		if (this.cpuFamily == null) {
349 			int[] mib = { SystemB.CTL_MACHDEP, SystemB.MACHDEP_CPU,
350 					SystemB.MACHDEP_CPU_FAMILY };
351 			IntByReference size = new IntByReference(SystemB.INT_SIZE);
352 			Pointer p = new Memory(size.getValue());
353 			if (0 != SystemB.INSTANCE.sysctl(mib, mib.length, p, size, null, 0))
354 				throw new LastErrorException("Error code: "
355 						+ Native.getLastError());
356 			this.cpuFamily = Integer.toString(p.getInt(0));
357 		}
358 		return this.cpuFamily;
359 	}
360 
361 	/**
362 	 * @param family
363 	 *            the family to set
364 	 */
365 	@Override
366 	public void setFamily(String family) {
367 		this.cpuFamily = family;
368 	}
369 
370 	/**
371 	 * {@inheritDoc}
372 	 */
373 	@Override
374 	@Deprecated
375 	public float getLoad() {
376 		// TODO Remove in 2.0
377 		return (float) getSystemCpuLoadBetweenTicks() * 100;
378 	}
379 
380 	/**
381 	 * {@inheritDoc}
382 	 */
383 	@Override
384 	public double getSystemCpuLoadBetweenTicks() {
385 		// Check if > ~ 0.95 seconds since last tick count.
386 		long now = System.currentTimeMillis();
387 		boolean update = (now - tickTime > 950);
388 		if (update) {
389 			// Enough time has elapsed.
390 			// Update latest
391 			updateSystemTicks();
392 			tickTime = now;
393 		}
394 		// Calculate total
395 		long total = 0;
396 		for (int i = 0; i < curTicks.length; i++) {
397 			total += (curTicks[i] - prevTicks[i]);
398 		}
399 		// Calculate idle from last field [3]
400 		long idle = curTicks[3] - prevTicks[3];
401 
402 		// Copy latest ticks to earlier position for next call
403 		if (update) {
404 			System.arraycopy(curTicks, 0, prevTicks, 0, curTicks.length);
405 		}
406 
407 		// return
408 		if (total > 0 && idle >= 0) {
409 			return (double) (total - idle) / total;
410 		}
411 		return 0d;
412 	}
413 
414 	/**
415 	 * {@inheritDoc}
416 	 */
417 	@Override
418 	public long[] getSystemCpuLoadTicks() {
419 		updateSystemTicks();
420 		// Make a copy
421 		long[] ticks = new long[curTicks.length];
422 		System.arraycopy(curTicks, 0, ticks, 0, curTicks.length);
423 		return ticks;
424 	}
425 
426 	/**
427 	 * Updates system tick information from native host_statistics query. Array
428 	 * with four elements representing clock ticks or milliseconds (platform
429 	 * dependent) spent in User (0), Nice (1), System (2), and Idle (3) states.
430 	 * By measuring the difference between ticks across a time interval, CPU
431 	 * load over that interval may be calculated.
432 	 * 
433 	 * @return An array of 4 long values representing time spent in User,
434 	 *         Nice(if applicable), System, and Idle states.
435 	 */
436 	private static void updateSystemTicks() {
437 		int machPort = SystemB.INSTANCE.mach_host_self();
438 		HostCpuLoadInfo cpuLoadInfo = new HostCpuLoadInfo();
439 		if (0 != SystemB.INSTANCE.host_statistics(machPort,
440 				SystemB.HOST_CPU_LOAD_INFO, cpuLoadInfo, new IntByReference(
441 						cpuLoadInfo.size())))
442 			throw new LastErrorException("Error code: " + Native.getLastError());
443 		// Switch order to match linux
444 		curTicks[0] = cpuLoadInfo.cpu_ticks[SystemB.CPU_STATE_USER];
445 		curTicks[1] = cpuLoadInfo.cpu_ticks[SystemB.CPU_STATE_NICE];
446 		curTicks[2] = cpuLoadInfo.cpu_ticks[SystemB.CPU_STATE_SYSTEM];
447 		curTicks[3] = cpuLoadInfo.cpu_ticks[SystemB.CPU_STATE_IDLE];
448 	}
449 
450 	/**
451 	 * {@inheritDoc}
452 	 */
453 	@Override
454 	public double getSystemCpuLoad() {
455 		if (sunMXBean) {
456 			return ((com.sun.management.OperatingSystemMXBean) OS_MXBEAN)
457 					.getSystemCpuLoad();
458 		}
459 		return getSystemCpuLoadBetweenTicks();
460 	}
461 
462 	/**
463 	 * {@inheritDoc}
464 	 */
465 	@Override
466 	public double getSystemLoadAverage() {
467 		return OS_MXBEAN.getSystemLoadAverage();
468 	}
469 
470 	/**
471 	 * {@inheritDoc}
472 	 */
473 	@Override
474 	public double getProcessorCpuLoadBetweenTicks() {
475 		// Check if > ~ 0.95 seconds since last tick count.
476 		long now = System.currentTimeMillis();
477 		if (now - procTickTime > 950) {
478 			// Enough time has elapsed. Update array in place
479 			updateProcessorTicks();
480 			// Copy arrays in place
481 			System.arraycopy(curProcTicks, 0, prevProcTicks, 0,
482 					curProcTicks.length);
483 			System.arraycopy(allProcessorTicks[processorNumber], 0,
484 					curProcTicks, 0, curProcTicks.length);
485 			procTickTime = now;
486 		}
487 		long total = 0;
488 		for (int i = 0; i < curProcTicks.length; i++) {
489 			total += (curProcTicks[i] - prevProcTicks[i]);
490 		}
491 		// Calculate idle from last field [3]
492 		long idle = curProcTicks[3] - prevProcTicks[3];
493 		// update
494 		return (total > 0 && idle >= 0) ? (double) (total - idle) / total : 0d;
495 	}
496 
497 	/**
498 	 * {@inheritDoc}
499 	 */
500 	public long[] getProcessorCpuLoadTicks() {
501 		updateProcessorTicks();
502 		return allProcessorTicks[processorNumber];
503 	}
504 
505 	/**
506 	 * Updates the tick array for all processors if more than 100ms has elapsed
507 	 * since the last update. This permits using the allProcessorTicks as a
508 	 * cache when iterating over processors so that the host_processor_info
509 	 * query is only done once
510 	 */
511 	private static void updateProcessorTicks() {
512 		// Update no more frequently than 100ms so this is only triggered once
513 		// during iteration over Processors
514 		long now = System.currentTimeMillis();
515 		if (now - allProcTickTime < 100)
516 			return;
517 
518 		int machPort = SystemB.INSTANCE.mach_host_self();
519 
520 		IntByReference procCount = new IntByReference();
521 		PointerByReference procCpuLoadInfo = new PointerByReference();
522 		IntByReference procInfoCount = new IntByReference();
523 		if (0 != SystemB.INSTANCE.host_processor_info(machPort,
524 				SystemB.PROCESSOR_CPU_LOAD_INFO, procCount, procCpuLoadInfo,
525 				procInfoCount))
526 			throw new LastErrorException("Error code: " + Native.getLastError());
527 
528 		int[] cpuTicks = procCpuLoadInfo.getValue().getIntArray(0,
529 				procInfoCount.getValue());
530 		for (int cpu = 0; cpu < procCount.getValue(); cpu++) {
531 			for (int j = 0; j < 4; j++) {
532 				int offset = cpu * SystemB.CPU_STATE_MAX;
533 				allProcessorTicks[cpu][0] = FormatUtil
534 						.getUnsignedInt(cpuTicks[offset
535 								+ SystemB.CPU_STATE_USER]);
536 				allProcessorTicks[cpu][1] = FormatUtil
537 						.getUnsignedInt(cpuTicks[offset
538 								+ SystemB.CPU_STATE_NICE]);
539 				allProcessorTicks[cpu][2] = FormatUtil
540 						.getUnsignedInt(cpuTicks[offset
541 								+ SystemB.CPU_STATE_SYSTEM]);
542 				allProcessorTicks[cpu][3] = FormatUtil
543 						.getUnsignedInt(cpuTicks[offset
544 								+ SystemB.CPU_STATE_IDLE]);
545 			}
546 		}
547 		allProcTickTime = now;
548 	}
549 
550 	@Override
551 	public String toString() {
552 		return getName();
553 	}
554 }