From 3bc5e5f0980a5f38be8074725def78ad5b329fc0 Mon Sep 17 00:00:00 2001 From: dbwiddis Date: Wed, 23 Jan 2019 20:09:58 -0800 Subject: [PATCH] API overhaul - CentralProcessor, LogicalProcessor (#779) * New LogicalProcessor class * Logical Processor numbering * Per processor ticks * Make LogicalProcessor an inner class * Fix Codacy review * Processor Frequency, Windows implementation * MacOS implementation * Fix types on NTPowerInformation * Linux implementation * Solaris implementation * FreeBSD implementation * Fix tests * FreeBsd and Solaris testing/fixes * SystemInfoTest --- CHANGELOG.md | 1 + UPGRADING.md | 18 +- .../java/oshi/hardware/CentralProcessor.java | 327 ++++++------- .../common/AbstractCentralProcessor.java | 432 ++++++++---------- .../platform/linux/LinuxCentralProcessor.java | 109 ++++- .../platform/mac/MacCentralProcessor.java | 41 +- .../unix/freebsd/FreeBsdCentralProcessor.java | 155 +++++-- .../unix/solaris/SolarisCentralProcessor.java | 78 +++- .../windows/WindowsCentralProcessor.java | 81 +++- .../platform/windows/WindowsPowerSource.java | 6 +- .../oshi/jna/platform/windows/PowrProf.java | 16 +- .../os/windows/WindowsOperatingSystem.java | 2 +- .../src/main/java/oshi/util/ParseUtil.java | 18 + .../oshi/util/platform/linux/ProcUtil.java | 2 +- .../src/test/java/oshi/SystemInfoTest.java | 27 +- .../oshi/hardware/CentralProcessorTest.java | 48 +- .../oshi/software/os/OperatingSystemTest.java | 36 +- .../test/java/oshi/util/ParseUtilTest.java | 5 + oshi-demo/src/main/java/oshi/demo/Json.java | 8 +- 19 files changed, 864 insertions(+), 546 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e34b41623b9..e3cfdfc5727 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * [#774](https://github.com/oshi/oshi/pull/774): API overhaul - ComputerSystem, Baseboard, and Firmware. - [@dbwiddis](https://github.com/dbwiddis). * [#775](https://github.com/oshi/oshi/pull/775): API overhaul - GlobalMemory, new VirtualMemory. - [@dbwiddis](https://github.com/dbwiddis). * [#776](https://github.com/oshi/oshi/pull/776): oshi-demo artifact. - [@dbwiddis](https://github.com/dbwiddis). +* [#779](https://github.com/oshi/oshi/pull/779): API overhaul - CentralProcessor, new LogicalProcessor. - [@dbwiddis](https://github.com/dbwiddis). * Your contribution here. 3.13.0 (1/18/2019) diff --git a/UPGRADING.md b/UPGRADING.md index ace06f1c128..b0ea5355c14 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -12,10 +12,6 @@ to demonstrate OSHI's capabilities and integration with other libraries. ## API Changes -There is a new `VirtualMemory `class which is accessible with a getter from -`GlobalMemory`. Methods associated with swap file usage were moved to this -new class. - Several changes in the API highlight which attributes do not change and which fetch dynamic information, as well as highlight operations with latency or expensive computations. In general the following rules are followed: @@ -29,6 +25,20 @@ available to cause the getters to return updated values. The following getX() methods are now queryX(): - TBD +There is a new `VirtualMemory `class which is accessible with a getter from +`GlobalMemory`. Methods associated with swap file usage were moved to this +new class. + +The `CentralProcessor` setters were removed from the API. The methods +`getSystemCpuLoadBetweenTicks()` and `getProcessorCpuLoadBetweenTicks()` now take +an argument with the previous set of ticks, rather than internally saving the +previous call. This enables users to measure over a longer period or multiple +different periods. The `getSystemCpuLoad()` method is now a direct passthrough +to the `OperatingSystemMXBean` method if running an Oracle JVM, otherwise it +returns a negative value. The no-argument `getSystemLoadAverage()` has been +removed; users can call with an argument of 1 to obtain the same value. + + # Guide to upgrading from OSHI 2.x to 3.x The most significant change in OSHI 3.0 is the separation of JSON output to a diff --git a/oshi-core/src/main/java/oshi/hardware/CentralProcessor.java b/oshi-core/src/main/java/oshi/hardware/CentralProcessor.java index b5a4bb2b2d8..ac2e533b5b3 100644 --- a/oshi-core/src/main/java/oshi/hardware/CentralProcessor.java +++ b/oshi-core/src/main/java/oshi/hardware/CentralProcessor.java @@ -26,73 +26,13 @@ import java.io.Serializable; /** - * The Central Processing Unit (CPU) or the processor is the portion of a - * computer system that carries out the instructions of a computer program, and - * is the primary element carrying out the computer's functions. - * - * @author dblock[at]dblock[dot]org + * This class represents the entire Central Processing Unit (CPU) of a computer + * system, which may contain one or more physical packages (sockets), one or + * more physical processors (cores), and one or more logical processors (what + * the Operating System sees, which may include hyperthreaded cores.) */ public interface CentralProcessor extends Serializable { - /** - * Index of CPU tick counters in the {@link #getSystemCpuLoadTicks()} and - * {@link #getProcessorCpuLoadTicks()} arrays. - */ - enum TickType { - /** - * CPU utilization that occurred while executing at the user level - * (application). - */ - USER(0), - /** - * CPU utilization that occurred while executing at the user level with nice - * priority. - */ - NICE(1), - /** - * CPU utilization that occurred while executing at the system level - * (kernel). - */ - SYSTEM(2), - /** - * Time that the CPU or CPUs were idle and the system did not have an - * outstanding disk I/O request. - */ - IDLE(3), - /** - * Time that the CPU or CPUs were idle during which the system had an - * outstanding disk I/O request. - */ - IOWAIT(4), - /** - * Time that the CPU used to service hardware IRQs - */ - IRQ(5), - /** - * Time that the CPU used to service soft IRQs - */ - SOFTIRQ(6), - /** - * Time which the hypervisor dedicated for other guests in the system. Only - * supported on Linux. - */ - STEAL(7); - - private int index; - - TickType(int value) { - this.index = value; - } - - /** - * @return The integer index of this ENUM in the processor tick arrays, - * which matches the output of Linux /proc/cpuinfo - */ - public int getIndex() { - return index; - } - } - /** * Processor vendor. * @@ -100,14 +40,6 @@ public int getIndex() { */ String getVendor(); - /** - * Set processor vendor. - * - * @param vendor - * Vendor. - */ - void setVendor(String vendor); - /** * Name, eg. Intel(R) Core(TM)2 Duo CPU T7300 @ 2.00GHz * @@ -115,14 +47,6 @@ public int getIndex() { */ String getName(); - /** - * Set processor name. - * - * @param name - * Name. - */ - void setName(String name); - /** * Vendor frequency (in Hz), eg. for processor named Intel(R) Core(TM)2 Duo * CPU T7300 @ 2.00GHz the vendor frequency is 2000000000. @@ -132,12 +56,18 @@ public int getIndex() { long getVendorFreq(); /** - * Set processor vendor frequency (in Hz). + * Maximum frequeny (in Hz), of the logical processors on this CPU. + * + * @return The max frequency or -1 if unknown. + */ + long getMaxFreq(); + + /** + * Current frequeny (in Hz), of the logical processors on this CPU. * - * @param freq - * Frequency. + * @return An array of processor frequency or -1 if unknown. */ - void setVendorFreq(long freq); + long[] getCurrentFreq(); /** * Gets the Processor ID. This is a hexidecimal string representing an @@ -154,14 +84,6 @@ public int getIndex() { */ String getProcessorID(); - /** - * Set processor ID - * - * @param processorID - * The processor ID - */ - void setProcessorID(String processorID); - /** * Identifier, eg. x86 Family 6 Model 15 Stepping 10. * @@ -169,14 +91,6 @@ public int getIndex() { */ String getIdentifier(); - /** - * Set processor identifier. - * - * @param identifier - * Identifier. - */ - void setIdentifier(String identifier); - /** * Is CPU 64bit? * @@ -184,61 +98,40 @@ public int getIndex() { */ boolean isCpu64bit(); - /** - * Set flag is cpu is 64bit. - * - * @param cpu64 - * True if cpu is 64. - */ - void setCpu64(boolean cpu64); - /** * @return the stepping */ String getStepping(); - /** - * @param stepping - * the stepping to set - */ - void setStepping(String stepping); - /** * @return the model */ String getModel(); - /** - * @param model - * the model to set - */ - void setModel(String model); - /** * @return the family */ String getFamily(); /** - * @param family - * the family to set + * Returns an array of the CPU's logical processors. + * + * @return The logical processor array. */ - void setFamily(String family); + LogicalProcessor[] getLogicalProcessors(); /** * Returns the "recent cpu usage" for the whole system by counting ticks - * from {@link #getSystemCpuLoadTicks()} between successive calls of this - * method, with a minimum interval slightly less than 1 second. If less than - * one second has elapsed since the last call of this method, it will return - * a calculation based on the tick counts and times of the previous two - * calls. If at least a second has elapsed, it will return the average CPU - * load for the interval and update the "last called" times. This method is - * intended to be used for periodic polling at intervals of 1 second or - * longer. - * + * from {@link #getSystemCpuLoadTicks()} between the user-provided value + * from a previous call. + * + * @param oldTicks + * A tick array from a previous call to + * {@link #getSystemCpuLoadTicks()} + * * @return CPU load between 0 and 1 (100%) */ - double getSystemCpuLoadBetweenTicks(); + double getSystemCpuLoadBetweenTicks(long[] oldTicks); /** * Get System-wide CPU Load tick counters. Returns an array with seven @@ -271,12 +164,13 @@ public int getIndex() { * period of time observed, while a value of 1.0 means that all CPUs were * actively running 100% of the time during the recent period being * observed. All values between 0.0 and 1.0 are possible depending of the - * activities going on in the system. If the system recent cpu usage is not - * available, the method returns a negative value. Calling this method - * immediately upon instantiating the {@link CentralProcessor} may give - * unreliable results. If a user is not running the Oracle JVM, this method - * will default to the behavior and return value of - * {@link #getSystemCpuLoadBetweenTicks()}. + * activities going on in the system. + *

+ * If the system recent cpu usage is not available, the method returns a + * negative value. Calling this method immediately upon instantiating the + * {@link CentralProcessor} may give unreliable results. Calling this method + * too frequently may return {@link Double#NaN}. If a user is not running + * the Oracle JVM, this method will return a negative value. * * @return the "recent cpu usage" for the whole system; a negative value if * not available. @@ -284,16 +178,6 @@ public int getIndex() { @SuppressWarnings("restriction") double getSystemCpuLoad(); - /** - * Returns the system load average for the last minute. This is equivalent - * to calling {@link CentralProcessor#getSystemLoadAverage(int)} with an - * argument of 1 and returning the first value, and is retained for - * compatibility. - * - * @return the system load average; or a negative value if not available. - */ - double getSystemLoadAverage(); - /** * Returns the system load average for the number of elements specified, up * to 3, representing 1, 5, and 15 minutes. The system load average is the @@ -317,19 +201,16 @@ public int getIndex() { /** * Returns the "recent cpu usage" for all logical processors by counting - * ticks for the processors from {@link #getProcessorCpuLoadTicks()} between - * successive calls of this method, with a minimum interval slightly less - * than 1 second. If less than one second has elapsed since the last call of - * this method, it will return a calculation based on the tick counts and - * times of the previous two calls. If at least a second has elapsed, it - * will return the average CPU load for the interval and update the "last - * called" times. This method is intended to be used for periodic polling - * (iterating over all processors) at intervals of 1 second or longer. + * ticks from {@link #getProcessorCpuLoadTicks()} between the user-provided + * value from a previous call. * + * @param oldTicks + * A tick array from a previous call to + * {@link #getProcessorCpuLoadTicks()} * @return array of CPU load between 0 and 1 (100%) for each logical * processor */ - double[] getProcessorCpuLoadBetweenTicks(); + double[] getProcessorCpuLoadBetweenTicks(long[][] oldTicks); /** * Get Processor CPU Load tick counters. Returns a two dimensional array, @@ -399,4 +280,134 @@ public int getIndex() { * @return The number of interrupts */ long getInterrupts(); + + /** + * Update the values for the next call to the getters on this class. + */ + void updateAttributes(); + + /** + * Index of CPU tick counters in the {@link #getSystemCpuLoadTicks()} and + * {@link #getProcessorCpuLoadTicks()} arrays. + */ + enum TickType { + /** + * CPU utilization that occurred while executing at the user level + * (application). + */ + USER(0), + /** + * CPU utilization that occurred while executing at the user level with + * nice priority. + */ + NICE(1), + /** + * CPU utilization that occurred while executing at the system level + * (kernel). + */ + SYSTEM(2), + /** + * Time that the CPU or CPUs were idle and the system did not have an + * outstanding disk I/O request. + */ + IDLE(3), + /** + * Time that the CPU or CPUs were idle during which the system had an + * outstanding disk I/O request. + */ + IOWAIT(4), + /** + * Time that the CPU used to service hardware IRQs + */ + IRQ(5), + /** + * Time that the CPU used to service soft IRQs + */ + SOFTIRQ(6), + /** + * Time which the hypervisor dedicated for other guests in the system. + * Only supported on Linux. + */ + STEAL(7); + + private int index; + + TickType(int value) { + this.index = value; + } + + /** + * @return The integer index of this ENUM in the processor tick arrays, + * which matches the output of Linux /proc/cpuinfo + */ + public int getIndex() { + return index; + } + } + + class LogicalProcessor implements Serializable { + + private static final long serialVersionUID = 1L; + + private int processorNumber; + private int physicalProcessorNumber; + private int physicalPackageNumber; + + /** + * The Logical Processor number as seen by the Operating System. Used + * for assigning process affinity and reporting CPU usage and other + * statistics. + * + * @return the processorNumber + */ + public int getProcessorNumber() { + return processorNumber; + } + + /** + * @param processorNumber + * the processorNumber to set + */ + public void setProcessorNumber(int processorNumber) { + this.processorNumber = processorNumber; + } + + /** + * The physical processor (core) id number assigned to this logical + * processor. Hyperthreaded logical processors which share the same + * physical processor will have the same number. + * + * @return the physicalProcessorNumber + */ + public int getPhysicalProcessorNumber() { + return physicalProcessorNumber; + } + + /** + * @param physicalProcessorNumber + * the physicalProcessorNumber to set + */ + public void setPhysicalProcessorNumber(int physicalProcessorNumber) { + this.physicalProcessorNumber = physicalProcessorNumber; + } + + /** + * The physical package (socket) id number assigned to this logical + * processor. Multicore CPU packages may have multiple physical + * processors which share the same number. + * + * @return the physicalPackageNumber + */ + public int getPhysicalPackageNumber() { + return physicalPackageNumber; + } + + /** + * @param physicalPackageNumber + * the physicalPackageNumber to set + */ + public void setPhysicalPackageNumber(int physicalPackageNumber) { + this.physicalPackageNumber = physicalPackageNumber; + } + } } diff --git a/oshi-core/src/main/java/oshi/hardware/common/AbstractCentralProcessor.java b/oshi-core/src/main/java/oshi/hardware/common/AbstractCentralProcessor.java index 599a6ef54b0..bf5486c9579 100644 --- a/oshi-core/src/main/java/oshi/hardware/common/AbstractCentralProcessor.java +++ b/oshi-core/src/main/java/oshi/hardware/common/AbstractCentralProcessor.java @@ -34,11 +34,7 @@ import oshi.util.ParseUtil; /** - * A CPU as defined in Linux /proc. - * - * @author alessandro[at]perucchi[dot]org - * @author alessio.fachechi[at]gmail[dot]com - * @author widdis[at]gmail[dot]com + * A CPU. */ @SuppressWarnings("restriction") public abstract class AbstractCentralProcessor implements CentralProcessor { @@ -53,160 +49,168 @@ public abstract class AbstractCentralProcessor implements CentralProcessor { private static final java.lang.management.OperatingSystemMXBean OS_MXBEAN = ManagementFactory .getOperatingSystemMXBean(); - /** - * Calling OperatingSystemMxBean too rapidly results in NaN. Store the - * latest value to return if polling is too rapid - */ - private double lastCpuLoad = 0d; - - /** - * Keep track of last CPU Load poll to OperatingSystemMXBean to ensure - * enough time has elapsed - */ - private long lastCpuLoadTime = 0; - /** * Keep track whether MXBean supports Oracle JVM methods */ - private boolean sunMXBean = false; + private static boolean sunMXBean = false; + static { + try { + Class.forName("com.sun.management.OperatingSystemMXBean"); + LOG.debug("Oracle MXBean detected."); + sunMXBean = true; + } catch (ClassNotFoundException | ClassCastException e) { + LOG.debug("Oracle MXBean not detected."); + } + } // Logical and Physical Processor Counts - protected int logicalProcessorCount = 0; - - protected int physicalProcessorCount = 0; - protected int physicalPackageCount = 0; + protected int physicalProcessorCount = 0; + protected int logicalProcessorCount = 0; - // Maintain previous ticks to be used for calculating usage between them. // System ticks - private long tickTime; - - private long[] prevTicks; - - private long[] curTicks; - + protected long[] systemCpuLoadTicks; // Per-processor ticks [cpu][type] - private long procTickTime; - - private long[][] prevProcTicks; - - private long[][] curProcTicks; + private long[][] processorCpuLoadTicks; // Processor info private String cpuVendor; - private String cpuName; - private String processorID; - private String cpuIdentifier; - private String cpuStepping; - private String cpuModel; - private String cpuFamily; - - private Long cpuVendorFreq; - + private long cpuVendorFreq; + private long cpuMaxFreq; + private long[] cpuCurrentFreq; private Boolean cpu64; + private LogicalProcessor[] logicalProcessors; /** * Create a Processor */ public AbstractCentralProcessor() { - initMXBean(); - // Initialize processor counts - calculateProcessorCounts(); + // Initialize processor counts and populate logical processor array + this.logicalProcessors = initProcessorCounts(); } /** - * Initializes mxBean boolean + * Updates logical and physical processor counts and arrays */ - private void initMXBean() { - try { - Class.forName("com.sun.management.OperatingSystemMXBean"); - // Initialize CPU usage - this.lastCpuLoad = ((com.sun.management.OperatingSystemMXBean) OS_MXBEAN).getSystemCpuLoad(); - this.lastCpuLoadTime = System.currentTimeMillis(); - this.sunMXBean = true; - LOG.debug("Oracle MXBean detected."); - } catch (ClassNotFoundException | ClassCastException e) { - LOG.debug("Oracle MXBean not detected."); - LOG.trace("{}", e); - } + protected abstract LogicalProcessor[] initProcessorCounts(); + + /** + * {@inheritDoc} + */ + @Override + public LogicalProcessor[] getLogicalProcessors() { + return this.logicalProcessors; } /** - * Initializes tick arrays + * {@inheritDoc} */ - protected synchronized void initTicks() { - this.prevProcTicks = new long[this.logicalProcessorCount][TickType.values().length]; - this.curProcTicks = new long[this.logicalProcessorCount][TickType.values().length]; - this.prevTicks = new long[TickType.values().length]; - this.curTicks = new long[TickType.values().length]; + @Override + public long[] getSystemCpuLoadTicks() { + if (this.systemCpuLoadTicks == null) { + this.systemCpuLoadTicks = querySystemCpuLoadTicks(); + } + return this.systemCpuLoadTicks; } /** - * Updates logical and physical processor counts + * Get System-wide CPU Load tick counters. + * + * @return The tick counters. */ - protected abstract void calculateProcessorCounts(); + protected abstract long[] querySystemCpuLoadTicks(); /** * {@inheritDoc} */ @Override - public String getVendor() { - if (this.cpuVendor == null) { - setVendor(""); + public long[] getCurrentFreq() { + if (this.cpuCurrentFreq == null) { + this.cpuCurrentFreq = queryCurrentFreq(); } - return this.cpuVendor; + return this.cpuCurrentFreq; } + /** + * Get per processor current frequencies. + * + * @return The current frequencies. + */ + protected abstract long[] queryCurrentFreq(); + /** * {@inheritDoc} */ @Override - public void setVendor(String vendor) { - this.cpuVendor = vendor; + public long getMaxFreq() { + if (this.cpuMaxFreq == 0) { + this.cpuMaxFreq = queryMaxFreq(); + } + return this.cpuMaxFreq; } + /** + * Get processor max frequency. + * + * @return The max frequency. + */ + protected abstract long queryMaxFreq(); + /** * {@inheritDoc} */ @Override - public String getName() { - if (this.cpuName == null) { - setName(""); + public long[][] getProcessorCpuLoadTicks() { + if (processorCpuLoadTicks == null) { + this.processorCpuLoadTicks = queryProcessorCpuLoadTicks(); } - return this.cpuName; + return this.processorCpuLoadTicks; } + /** + * Get Per-Processor CPU Load tick counters. + * + * @return The tick counters. + */ + protected abstract long[][] queryProcessorCpuLoadTicks(); + /** * {@inheritDoc} */ @Override - public void setName(String name) { - this.cpuName = name; + public String getVendor() { + if (this.cpuVendor == null) { + setVendor(""); + } + return this.cpuVendor; } /** * {@inheritDoc} */ @Override - public String getProcessorID() { - if (this.processorID == null) { - setProcessorID(""); + public String getName() { + if (this.cpuName == null) { + setName(""); } - return this.processorID; + return this.cpuName; } /** * {@inheritDoc} */ @Override - public void setProcessorID(String processorID) { - this.processorID = processorID; + public String getProcessorID() { + if (this.processorID == null) { + setProcessorID(""); + } + return this.processorID; } /** @@ -214,26 +218,18 @@ public void setProcessorID(String processorID) { */ @Override public long getVendorFreq() { - if (this.cpuVendorFreq == null) { + if (this.cpuVendorFreq == 0) { Pattern pattern = Pattern.compile("@ (.*)$"); Matcher matcher = pattern.matcher(getName()); if (matcher.find()) { String unit = matcher.group(1); - this.cpuVendorFreq = Long.valueOf(ParseUtil.parseHertz(unit)); + this.cpuVendorFreq = ParseUtil.parseHertz(unit); } else { - this.cpuVendorFreq = Long.valueOf(-1L); + this.cpuVendorFreq = -1L; } } - return this.cpuVendorFreq.longValue(); - } - - /** - * {@inheritDoc} - */ - @Override - public void setVendorFreq(long freq) { - this.cpuVendorFreq = Long.valueOf(freq); + return this.cpuVendorFreq; } /** @@ -256,14 +252,6 @@ public String getIdentifier() { return this.cpuIdentifier; } - /** - * {@inheritDoc} - */ - @Override - public void setIdentifier(String identifier) { - this.cpuIdentifier = identifier; - } - /** * {@inheritDoc} */ @@ -275,14 +263,6 @@ public boolean isCpu64bit() { return this.cpu64; } - /** - * {@inheritDoc} - */ - @Override - public void setCpu64(boolean value) { - this.cpu64 = Boolean.valueOf(value); - } - /** * {@inheritDoc} */ @@ -297,14 +277,6 @@ public String getStepping() { return this.cpuStepping; } - /** - * {@inheritDoc} - */ - @Override - public void setStepping(String stepping) { - this.cpuStepping = stepping; - } - /** * {@inheritDoc} */ @@ -319,14 +291,6 @@ public String getModel() { return this.cpuModel; } - /** - * {@inheritDoc} - */ - @Override - public void setModel(String model) { - this.cpuModel = model; - } - /** * {@inheritDoc} */ @@ -342,11 +306,75 @@ public String getFamily() { } /** - * {@inheritDoc} + * @param cpuVendor + * the cpuVendor to set */ - @Override - public void setFamily(String family) { - this.cpuFamily = family; + protected void setVendor(String cpuVendor) { + this.cpuVendor = cpuVendor; + } + + /** + * @param cpuName + * the cpuName to set + */ + protected void setName(String cpuName) { + this.cpuName = cpuName; + } + + /** + * @param cpuIdentifier + * the cpuIdentifier to set + */ + protected void setIdentifier(String cpuIdentifier) { + this.cpuIdentifier = cpuIdentifier; + } + + /** + * @param cpuStepping + * the cpuStepping to set + */ + protected void setStepping(String cpuStepping) { + this.cpuStepping = cpuStepping; + } + + /** + * @param cpuModel + * the cpuModel to set + */ + protected void setModel(String cpuModel) { + this.cpuModel = cpuModel; + } + + /** + * @param cpuFamily + * the cpuFamily to set + */ + protected void setFamily(String cpuFamily) { + this.cpuFamily = cpuFamily; + } + + /** + * @param cpuVendorFreq + * the cpuVendorFreq to set + */ + protected void setVendorFreq(Long cpuVendorFreq) { + this.cpuVendorFreq = cpuVendorFreq; + } + + /** + * @param cpu64 + * the cpu64 to set + */ + protected void setCpu64(Boolean cpu64) { + this.cpu64 = cpu64; + } + + /** + * @param processorID + * the processorID to set + */ + protected void setProcessorID(String processorID) { + this.processorID = processorID; } /** @@ -374,103 +402,56 @@ private String parseIdentifier(String id) { * {@inheritDoc} */ @Override - public synchronized double getSystemCpuLoadBetweenTicks() { - // Check if > ~ 0.95 seconds since last tick count. - long now = System.currentTimeMillis(); - LOG.trace("Current time: {} Last tick time: {}", now, this.tickTime); - if (now - this.tickTime > 950) { - // Enough time has elapsed. - updateSystemTicks(); + public synchronized double getSystemCpuLoadBetweenTicks(long[] oldTicks) { + if (oldTicks.length != TickType.values().length) { + throw new IllegalArgumentException( + "Tick array " + oldTicks.length + " should have " + TickType.values().length + " elements"); } + long[] ticks = getSystemCpuLoadTicks(); // Calculate total long total = 0; - for (int i = 0; i < this.curTicks.length; i++) { - total += this.curTicks[i] - this.prevTicks[i]; + for (int i = 0; i < ticks.length; i++) { + total += ticks[i] - oldTicks[i]; } // Calculate idle from difference in idle and IOwait - long idle = this.curTicks[TickType.IDLE.getIndex()] + this.curTicks[TickType.IOWAIT.getIndex()] - - this.prevTicks[TickType.IDLE.getIndex()] - this.prevTicks[TickType.IOWAIT.getIndex()]; + long idle = ticks[TickType.IDLE.getIndex()] + ticks[TickType.IOWAIT.getIndex()] + - oldTicks[TickType.IDLE.getIndex()] - oldTicks[TickType.IOWAIT.getIndex()]; LOG.trace("Total ticks: {} Idle ticks: {}", total, idle); return total > 0 && idle >= 0 ? (double) (total - idle) / total : 0d; } - /** - * Updates system tick information. Stores in array with seven elements - * representing clock ticks or milliseconds (platform dependent) spent in - * User (0), Nice (1), System (2), Idle (3), IOwait (4), IRQ (5), and - * SoftIRQ (6) states. By measuring the difference between ticks across a - * time interval, CPU load over that interval may be calculated. - */ - protected void updateSystemTicks() { - LOG.trace("Updating System Ticks"); - long[] ticks = getSystemCpuLoadTicks(); - // Skip update if ticks is all zero. - // Iterate to find a nonzero tick value and return; this should quickly - // find a nonzero value if one exists and be fast in checking 0's - // through branch prediction if it doesn't - for (long tick : ticks) { - if (tick != 0) { - // We have a nonzero tick array, update and return! - this.tickTime = System.currentTimeMillis(); - // Copy to previous - System.arraycopy(this.curTicks, 0, this.prevTicks, 0, this.curTicks.length); - System.arraycopy(ticks, 0, this.curTicks, 0, ticks.length); - return; - } - } - } - /** * {@inheritDoc} */ @Override public double getSystemCpuLoad() { - if (this.sunMXBean) { - long now = System.currentTimeMillis(); - // If called too recently, return latest value - if (now - this.lastCpuLoadTime < 200) { - return this.lastCpuLoad; - } - this.lastCpuLoad = ((com.sun.management.OperatingSystemMXBean) OS_MXBEAN).getSystemCpuLoad(); - this.lastCpuLoadTime = now; - return this.lastCpuLoad; + if (sunMXBean) { + return ((com.sun.management.OperatingSystemMXBean) OS_MXBEAN).getSystemCpuLoad(); } - return getSystemCpuLoadBetweenTicks(); + return -1.0; } /** * {@inheritDoc} */ @Override - public double getSystemLoadAverage() { - return getSystemLoadAverage(1)[0]; - } - - /** - * {@inheritDoc} - */ - @Override - public double[] getProcessorCpuLoadBetweenTicks() { - // Check if > ~ 0.95 seconds since last tick count. - long now = System.currentTimeMillis(); - LOG.trace("Current time: {} Last tick time: {}", now, this.procTickTime); - if (now - this.procTickTime > 950) { - // Enough time has elapsed. - // Update latest - updateProcessorTicks(); + public double[] getProcessorCpuLoadBetweenTicks(long[][] oldTicks) { + if (oldTicks.length != this.logicalProcessorCount || oldTicks[0].length != TickType.values().length) { + throw new IllegalArgumentException( + "Tick array " + oldTicks.length + " should have " + this.logicalProcessorCount + + " arrays, each of which has " + TickType.values().length + " elements"); } + long[][] ticks = getProcessorCpuLoadTicks(); double[] load = new double[this.logicalProcessorCount]; for (int cpu = 0; cpu < this.logicalProcessorCount; cpu++) { long total = 0; - for (int i = 0; i < this.curProcTicks[cpu].length; i++) { - total += this.curProcTicks[cpu][i] - this.prevProcTicks[cpu][i]; + for (int i = 0; i < ticks[cpu].length; i++) { + total += ticks[cpu][i] - oldTicks[cpu][i]; } // Calculate idle from difference in idle and IOwait - long idle = this.curProcTicks[cpu][TickType.IDLE.getIndex()] - + this.curProcTicks[cpu][TickType.IOWAIT.getIndex()] - - this.prevProcTicks[cpu][TickType.IDLE.getIndex()] - - this.prevProcTicks[cpu][TickType.IOWAIT.getIndex()]; + long idle = ticks[cpu][TickType.IDLE.getIndex()] + ticks[cpu][TickType.IOWAIT.getIndex()] + - oldTicks[cpu][TickType.IDLE.getIndex()] - oldTicks[cpu][TickType.IOWAIT.getIndex()]; LOG.trace("CPU: {} Total ticks: {} Idle ticks: {}", cpu, total, idle); // update load[cpu] = total > 0 && idle >= 0 ? (double) (total - idle) / total : 0d; @@ -478,40 +459,6 @@ public double[] getProcessorCpuLoadBetweenTicks() { return load; } - /** - * Updates per-processor tick information. Stores in 2D array; an array for - * each logical processor with with seven elements representing clock ticks - * or milliseconds (platform dependent) spent in User (0), Nice (1), System - * (2), Idle (3), IOwait (4), IRQ (5), and SoftIRQ (6) states. By measuring - * the difference between ticks across a time interval, CPU load over that - * interval may be calculated. - */ - protected void updateProcessorTicks() { - LOG.trace("Updating Processor Ticks"); - long[][] ticks = getProcessorCpuLoadTicks(); - // Skip update if ticks is all zero. - // Iterate to find a nonzero tick value and return; this should quickly - // find a nonzero value if one exists and be fast in checking 0's - // through branch prediction if it doesn't - for (long[] tick : ticks) { - for (long element : tick) { - if (element != 0L) { - // We have a nonzero tick array, update and return! - this.procTickTime = System.currentTimeMillis(); - // Copy to previous - for (int cpu = 0; cpu < this.logicalProcessorCount; cpu++) { - System.arraycopy(this.curProcTicks[cpu], 0, this.prevProcTicks[cpu], 0, - this.curProcTicks[cpu].length); - } - for (int cpu = 0; cpu < this.logicalProcessorCount; cpu++) { - System.arraycopy(ticks[cpu], 0, this.curProcTicks[cpu], 0, ticks[cpu].length); - } - return; - } - } - } - } - /** * {@inheritDoc} */ @@ -570,7 +517,7 @@ protected String createProcessorID(String stepping, String model, String family, processorIdBytes |= (familyL & 0xf0) << 20; // 13:12 – Processor Type, assume 0 for (String flag : flags) { - switch (flag) { + switch (flag) { // NOSONAR squid:S1479 case "fpu": processorIdBytes |= 1L << 32; break; @@ -667,4 +614,15 @@ protected String createProcessorID(String stepping, String model, String family, } return String.format("%016X", processorIdBytes); } + + /** + * {@inheritDoc} + */ + @Override + public void updateAttributes() { + this.systemCpuLoadTicks = null; + this.processorCpuLoadTicks = null; + this.cpuCurrentFreq = null; + } + } diff --git a/oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessor.java b/oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessor.java index 5746687d9d6..e25018e50b4 100644 --- a/oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessor.java +++ b/oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessor.java @@ -23,6 +23,7 @@ */ package oshi.hardware.platform.linux; +import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -50,6 +51,9 @@ public class LinuxCentralProcessor extends AbstractCentralProcessor { private static final Logger LOG = LoggerFactory.getLogger(LinuxCentralProcessor.class); + // See https://www.kernel.org/doc/Documentation/cpu-freq/user-guide.txt + private static final String CPUFREQ_PATH = "/sys/devices/system/cpu/cpu"; + /** * Create a Processor */ @@ -57,8 +61,6 @@ public LinuxCentralProcessor() { super(); // Initialize class variables initVars(); - // Initialize tick arrays - initTicks(); LOG.debug("Initialized Processor"); } @@ -109,37 +111,46 @@ private void initVars() { * Updates logical and physical processor counts from /proc/cpuinfo */ @Override - protected void calculateProcessorCounts() { - int[] uniqueID = new int[2]; - uniqueID[0] = -1; - uniqueID[1] = -1; - + protected LogicalProcessor[] initProcessorCounts() { Set processorIDs = new HashSet<>(); Set packageIDs = new HashSet<>(); List procCpu = FileUtil.readFile("/proc/cpuinfo"); + // Iterate once to count logical processors for (String cpu : procCpu) { - // Count logical processors if (cpu.startsWith("processor")) { this.logicalProcessorCount++; } + } + // Iterate again to populate + LogicalProcessor[] logProcs = new LogicalProcessor[this.logicalProcessorCount]; + int currentProcessor = 0; + for (String cpu : procCpu) { + // Count logical processors + if (cpu.startsWith("processor")) { + currentProcessor = ParseUtil.parseLastInt(cpu, 0); + logProcs[currentProcessor] = new LogicalProcessor(); + logProcs[currentProcessor].setProcessorNumber(currentProcessor); + } // Count unique combinations of core id and physical id. if (cpu.startsWith("core id") || cpu.startsWith("cpu number")) { - uniqueID[0] = ParseUtil.parseLastInt(cpu, 0); + logProcs[currentProcessor].setPhysicalProcessorNumber(ParseUtil.parseLastInt(cpu, 0)); } else if (cpu.startsWith("physical id")) { - uniqueID[1] = ParseUtil.parseLastInt(cpu, 0); + logProcs[currentProcessor].setPhysicalPackageNumber(ParseUtil.parseLastInt(cpu, 0)); } - if (uniqueID[0] >= 0 && uniqueID[1] >= 0) { - packageIDs.add(uniqueID[1]); - processorIDs.add(uniqueID[0] + " " + uniqueID[1]); - uniqueID[0] = -1; - uniqueID[1] = -1; + if (logProcs[currentProcessor].getPhysicalProcessorNumber() >= 0 + && logProcs[currentProcessor].getPhysicalPackageNumber() >= 0) { + packageIDs.add(logProcs[currentProcessor].getPhysicalPackageNumber()); + processorIDs.add(logProcs[currentProcessor].getPhysicalProcessorNumber() + " " + + logProcs[currentProcessor].getPhysicalPackageNumber()); } } // Force at least one processor if (this.logicalProcessorCount < 1) { LOG.error("Couldn't find logical processor count. Assuming 1."); this.logicalProcessorCount = 1; + logProcs = new LogicalProcessor[1]; + logProcs[0] = new LogicalProcessor(); } this.physicalProcessorCount = processorIDs.size(); if (this.physicalProcessorCount < 1) { @@ -151,14 +162,76 @@ protected void calculateProcessorCounts() { LOG.error("Couldn't find physical package count. Assuming 1."); this.physicalPackageCount = 1; } + return logProcs; + } + + /** + * {@inheritDoc} + */ + @Override + public long[] querySystemCpuLoadTicks() { + return ProcUtil.readSystemCpuLoadTicks(); + } + + /** + * {@inheritDoc} + */ + @Override + public long[] queryCurrentFreq() { + long[] freqs = new long[getLogicalProcessorCount()]; + // Attempt to fill array from cpu-freq source + long max = 0L; + for (int i = 0; i < freqs.length; i++) { + freqs[i] = FileUtil.getLongFromFile(CPUFREQ_PATH + i + "/cpufreq/scaling_cur_freq"); + if (freqs[i] == 0) { + freqs[i] = FileUtil.getLongFromFile(CPUFREQ_PATH + i + "/cpufreq/cpuinfo_cur_freq"); + } + if (max < freqs[i]) { + max = freqs[i]; + } + } + if (max > 0L) { + // If successful, array is filled with values in KHz. + for (int i = 0; i < freqs.length; i++) { + freqs[i] *= 1000L; + } + return freqs; + } + // If unsuccessful, try from /proc/cpuinfo + Arrays.fill(freqs, -1); + List cpuInfo = FileUtil.readFile("/proc/cpuinfo"); + int proc = 0; + for (String s : cpuInfo) { + if (s.toLowerCase().contains("cpu mhz")) { + freqs[proc] = (long) (ParseUtil.parseLastDouble(s, 0d) * 1_000_000); + if (++proc >= freqs.length) { + break; + } + } + } + return freqs; } /** * {@inheritDoc} */ @Override - public synchronized long[] getSystemCpuLoadTicks() { - return ProcUtil.getSystemCpuLoadTicks(); + public long queryMaxFreq() { + long max = 0L; + for (int i = 0; i < getLogicalProcessorCount(); i++) { + long freq = FileUtil.getLongFromFile(CPUFREQ_PATH + i + "/cpufreq/scaling_max_freq"); + if (freq == 0) { + freq = FileUtil.getLongFromFile(CPUFREQ_PATH + i + "/cpufreq/cpuinfo_max_freq"); + } + if (max < freq) { + max = freq; + } + } + if (max > 0L) { + // If successful, value is in KHz. + return max * 1000L; + } + return -1L; } /** @@ -183,7 +256,7 @@ public double[] getSystemLoadAverage(int nelem) { * {@inheritDoc} */ @Override - public long[][] getProcessorCpuLoadTicks() { + public long[][] queryProcessorCpuLoadTicks() { long[][] ticks = new long[this.logicalProcessorCount][TickType.values().length]; // /proc/stat expected format // first line is overall user,nice,system,idle, etc. diff --git a/oshi-core/src/main/java/oshi/hardware/platform/mac/MacCentralProcessor.java b/oshi-core/src/main/java/oshi/hardware/platform/mac/MacCentralProcessor.java index 2fd52bd1775..07b97f38c31 100644 --- a/oshi-core/src/main/java/oshi/hardware/platform/mac/MacCentralProcessor.java +++ b/oshi-core/src/main/java/oshi/hardware/platform/mac/MacCentralProcessor.java @@ -23,6 +23,8 @@ */ package oshi.hardware.platform.mac; +import java.util.Arrays; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -77,8 +79,6 @@ public MacCentralProcessor() { super(); // Initialize class variables initVars(); - // Initialize tick arrays - initTicks(); LOG.debug("Initialized Processor"); } @@ -103,17 +103,26 @@ private void initVars() { * Updates logical and physical processor counts from sysctl calls */ @Override - protected void calculateProcessorCounts() { + protected LogicalProcessor[] initProcessorCounts() { this.logicalProcessorCount = SysctlUtil.sysctl("hw.logicalcpu", 1); this.physicalProcessorCount = SysctlUtil.sysctl("hw.physicalcpu", 1); this.physicalPackageCount = SysctlUtil.sysctl("hw.packages", 1); + + LogicalProcessor[] logProcs = new LogicalProcessor[this.logicalProcessorCount]; + for (int i = 0; i < logProcs.length; i++) { + logProcs[i] = new LogicalProcessor(); + logProcs[i].setProcessorNumber(i); + logProcs[i].setPhysicalProcessorNumber(i * this.physicalProcessorCount / this.logicalProcessorCount); + logProcs[i].setPhysicalPackageNumber(i * this.physicalPackageCount / this.logicalProcessorCount); + } + return logProcs; } /** * {@inheritDoc} */ @Override - public long[] getSystemCpuLoadTicks() { + public long[] querySystemCpuLoadTicks() { long[] ticks = new long[TickType.values().length]; int machPort = SystemB.INSTANCE.mach_host_self(); HostCpuLoadInfo cpuLoadInfo = new HostCpuLoadInfo(); @@ -131,6 +140,24 @@ public long[] getSystemCpuLoadTicks() { return ticks; } + /** + * {@inheritDoc} + */ + @Override + public long[] queryCurrentFreq() { + long[] freqs = new long[getLogicalProcessorCount()]; + Arrays.fill(freqs, SysctlUtil.sysctl("hw.cpufrequency", -1L)); + return freqs; + } + + /** + * {@inheritDoc} + */ + @Override + public long queryMaxFreq() { + return SysctlUtil.sysctl("hw.cpufrequency_max", -1L); + } + /** * {@inheritDoc} */ @@ -142,9 +169,7 @@ public double[] getSystemLoadAverage(int nelem) { double[] average = new double[nelem]; int retval = SystemB.INSTANCE.getloadavg(average, nelem); if (retval < nelem) { - for (int i = Math.max(retval, 0); i < average.length; i++) { - average[i] = -1d; - } + Arrays.fill(average, -1d); } return average; } @@ -153,7 +178,7 @@ public double[] getSystemLoadAverage(int nelem) { * {@inheritDoc} */ @Override - public long[][] getProcessorCpuLoadTicks() { + public long[][] queryProcessorCpuLoadTicks() { long[][] ticks = new long[this.logicalProcessorCount][TickType.values().length]; int machPort = SystemB.INSTANCE.mach_host_self(); diff --git a/oshi-core/src/main/java/oshi/hardware/platform/unix/freebsd/FreeBsdCentralProcessor.java b/oshi-core/src/main/java/oshi/hardware/platform/unix/freebsd/FreeBsdCentralProcessor.java index 12f6b97e2d1..c8670dc3bb4 100644 --- a/oshi-core/src/main/java/oshi/hardware/platform/unix/freebsd/FreeBsdCentralProcessor.java +++ b/oshi-core/src/main/java/oshi/hardware/platform/unix/freebsd/FreeBsdCentralProcessor.java @@ -23,6 +23,7 @@ */ package oshi.hardware.platform.unix.freebsd; +import java.util.Arrays; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -85,8 +86,6 @@ public FreeBsdCentralProcessor() { super(); // Initialize class variables initVars(); - // Initialize tick arrays - initTicks(); LOG.debug("Initialized Processor"); } @@ -128,46 +127,92 @@ private void initVars() { * Updates logical and physical processor/package counts */ @Override - protected void calculateProcessorCounts() { + protected LogicalProcessor[] initProcessorCounts() { + // Get number of CPUs + this.logicalProcessorCount = BsdSysctlUtil.sysctl("hw.ncpu", 1); + // Force at least one processor + if (this.logicalProcessorCount < 1) { + LOG.error("Couldn't find logical processor count. Assuming 1."); + this.logicalProcessorCount = 1; + } + LogicalProcessor[] logProcs = new LogicalProcessor[this.logicalProcessorCount]; + for (int i = 0; i < logProcs.length; i++) { + logProcs[i] = new LogicalProcessor(); + logProcs[i].setProcessorNumber(i); + } + + parseTopology(logProcs); + + // Force at least one processor + if (this.physicalProcessorCount < 1) { + // We never found a group level 3; all logical processors are + // physical + this.physicalProcessorCount = this.logicalProcessorCount; + for (int i = 0; i < logProcs.length; i++) { + logProcs[i].setPhysicalProcessorNumber(i); + } + } + if (this.physicalPackageCount < 1) { + // We never found a group level 2; assume one package + this.physicalPackageCount = 1; + } + return logProcs; + } + + private void parseTopology(LogicalProcessor[] logProcs) { String[] topology = BsdSysctlUtil.sysctl("kern.sched.topology_spec", "").split("\\n|\\r"); - int physMask = 0; - int virtMask = 0; - int lastMask = 0; - int physPackage = 0; + /*- + * Sample output: + + + + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 + + + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 + + + 0, 1 + THREAD groupSMT group + + + * Opens with + * level 1 identifies all the processors via bitmask, should only be one + * level 2 separates by physical package + * level 3 puts hyperthreads together: if THREAD or SMT or HTT all the CPUs are one physical + * If there is no level 3, then all logical processors are physical + */ + int groupLevel = 0; for (String topo : topology) { - if (topo.contains("")) { + groupLevel--; + } else if (topo.contains(" tag and extract bits Matcher m = CPUMASK.matcher(topo); if (m.matches()) { - // Add this processor mask to cpus. Regex guarantees parsing - lastMask = Integer.parseInt(m.group(1), 16); - physMask |= lastMask; - virtMask |= lastMask; + // Regex guarantees parsing digits so we won't get a + // NumberFormatException + assignIds(groupLevel, Long.parseLong(m.group(1), 16), logProcs); } - } else if (topo.contains("") - && (topo.contains("HTT") || topo.contains("SMT") || topo.contains("THREAD"))) { - // These are virtual cpus, remove processor mask from physical - physMask &= ~lastMask; - } else if (topo.contains(" 0) { + if (groupLevel == 2) { + // This group is a physical package + logProcs[i].setPhysicalPackageNumber(getPhysicalPackageCount()); + this.physicalPackageCount++; + } else { // groupLevel == 3 + // This group is a physical processor + logProcs[i].setPhysicalProcessorNumber(getPhysicalProcessorCount()); + this.physicalProcessorCount++; + } + } } } @@ -175,7 +220,7 @@ protected void calculateProcessorCounts() { * {@inheritDoc} */ @Override - public synchronized long[] getSystemCpuLoadTicks() { + public long[] querySystemCpuLoadTicks() { long[] ticks = new long[TickType.values().length]; CpTime cpTime = new CpTime(); BsdSysctlUtil.sysctl("kern.cp_time", cpTime); @@ -187,6 +232,46 @@ public synchronized long[] getSystemCpuLoadTicks() { return ticks; } + /** + * {@inheritDoc} + */ + @Override + public long[] queryCurrentFreq() { + long freq = BsdSysctlUtil.sysctl("dev.cpu.0.freq", -1L); + if (freq > 0) { + // If success, value is in MHz + freq *= 1_000_000L; + } else { + freq = BsdSysctlUtil.sysctl("machdep.tsc_freq", -1L); + } + long[] freqs = new long[getLogicalProcessorCount()]; + Arrays.fill(freqs, freq); + return freqs; + } + + /** + * {@inheritDoc} + */ + @Override + public long queryMaxFreq() { + long max = -1L; + String freqLevels = BsdSysctlUtil.sysctl("dev.cpu.0.freq_levels", ""); + // MHz/Watts pairs like: 2501/32000 2187/27125 2000/24000 + for (String s : ParseUtil.whitespaces.split(freqLevels)) { + long freq = ParseUtil.parseLongOrDefault(s.split("/")[0], -1L); + if (max < freq) { + max = freq; + } + } + if (max > 0) { + // If success, value is in MHz + max *= 1_000_000; + } else { + max = BsdSysctlUtil.sysctl("machdep.tsc_freq", -1L); + } + return max; + } + /** * {@inheritDoc} */ @@ -209,7 +294,7 @@ public double[] getSystemLoadAverage(int nelem) { * {@inheritDoc} */ @Override - public long[][] getProcessorCpuLoadTicks() { + public long[][] queryProcessorCpuLoadTicks() { long[][] ticks = new long[this.logicalProcessorCount][TickType.values().length]; // Allocate memory for array of CPTime diff --git a/oshi-core/src/main/java/oshi/hardware/platform/unix/solaris/SolarisCentralProcessor.java b/oshi-core/src/main/java/oshi/hardware/platform/unix/solaris/SolarisCentralProcessor.java index 3f11f3544ea..801ff962eb4 100644 --- a/oshi-core/src/main/java/oshi/hardware/platform/unix/solaris/SolarisCentralProcessor.java +++ b/oshi-core/src/main/java/oshi/hardware/platform/unix/solaris/SolarisCentralProcessor.java @@ -23,6 +23,8 @@ */ package oshi.hardware.platform.unix.solaris; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -56,8 +58,6 @@ public SolarisCentralProcessor() { super(); // Initialize class variables initVars(); - // Initialize tick arrays - initTicks(); LOG.debug("Initialized Processor"); } @@ -82,19 +82,35 @@ private void initVars() { * Updates logical and physical processor counts from psrinfo */ @Override - protected void calculateProcessorCounts() { + protected LogicalProcessor[] initProcessorCounts() { List kstats = KstatUtil.kstatLookupAll("cpu_info", -1, null); Set chipIDs = new HashSet<>(); Set coreIDs = new HashSet<>(); this.logicalProcessorCount = 0; + + List logProcs = new ArrayList<>(); for (Kstat ksp : kstats) { if (ksp != null && KstatUtil.kstatRead(ksp)) { - this.logicalProcessorCount++; - chipIDs.add(KstatUtil.kstatDataLookupString(ksp, "chip_id")); - coreIDs.add(KstatUtil.kstatDataLookupString(ksp, "core_id")); + LogicalProcessor logProc = new LogicalProcessor(); + logProc.setProcessorNumber(logProcs.size()); + logProcs.add(logProc); + + String coreId = KstatUtil.kstatDataLookupString(ksp, "core_id"); + logProc.setPhysicalProcessorNumber(ParseUtil.parseIntOrDefault(coreId, 0)); + coreIDs.add(coreId); + + String chipId = KstatUtil.kstatDataLookupString(ksp, "chip_id"); + logProc.setPhysicalPackageNumber(ParseUtil.parseIntOrDefault(chipId, 0)); + chipIDs.add(chipId); } } + this.logicalProcessorCount = logProcs.size(); + if (this.logicalProcessorCount < 1) { + LOG.error("Couldn't find logical processor count. Assuming 1."); + this.logicalProcessorCount = 1; + logProcs.add(new LogicalProcessor()); + } this.physicalPackageCount = chipIDs.size(); if (this.physicalPackageCount < 1) { LOG.error("Couldn't find physical package count. Assuming 1."); @@ -105,20 +121,17 @@ protected void calculateProcessorCounts() { LOG.error("Couldn't find physical processor count. Assuming 1."); this.physicalProcessorCount = 1; } - if (this.logicalProcessorCount < 1) { - LOG.error("Couldn't find logical processor count. Assuming 1."); - this.logicalProcessorCount = 1; - } + return logProcs.toArray(new LogicalProcessor[logProcs.size()]); } /** * {@inheritDoc} */ @Override - public synchronized long[] getSystemCpuLoadTicks() { + protected long[] querySystemCpuLoadTicks() { long[] ticks = new long[TickType.values().length]; // Average processor ticks - long[][] procTicks = getProcessorCpuLoadTicks(); + long[][] procTicks = queryProcessorCpuLoadTicks(); for (int i = 0; i < ticks.length; i++) { for (long[] procTick : procTicks) { ticks[i] += procTick[i]; @@ -128,6 +141,45 @@ public synchronized long[] getSystemCpuLoadTicks() { return ticks; } + /** + * {@inheritDoc} + */ + @Override + public long[] queryCurrentFreq() { + long[] freqs = new long[getLogicalProcessorCount()]; + Arrays.fill(freqs, -1); + for (int i = 0; i < freqs.length; i++) { + for (Kstat ksp : KstatUtil.kstatLookupAll("cpu_info", i, null)) { + if (KstatUtil.kstatRead(ksp)) { + freqs[i] = KstatUtil.kstatDataLookupLong(ksp, "current_clock_Hz"); + } + } + } + return freqs; + } + + /** + * {@inheritDoc} + */ + @Override + public long queryMaxFreq() { + long max = -1L; + for (Kstat ksp : KstatUtil.kstatLookupAll("cpu_info", 0, null)) { + if (KstatUtil.kstatRead(ksp)) { + String suppFreq = KstatUtil.kstatDataLookupString(ksp, "supported_frequencies_Hz"); + if (!suppFreq.isEmpty()) { + for (String s : suppFreq.split(":")) { + long freq = ParseUtil.parseLongOrDefault(s, -1L); + if (max < freq) { + max = freq; + } + } + } + } + } + return max; + } + /** * {@inheritDoc} */ @@ -150,7 +202,7 @@ public double[] getSystemLoadAverage(int nelem) { * {@inheritDoc} */ @Override - public long[][] getProcessorCpuLoadTicks() { + public long[][] queryProcessorCpuLoadTicks() { long[][] ticks = new long[this.logicalProcessorCount][TickType.values().length]; int cpu = -1; for (Kstat ksp : KstatUtil.kstatLookupAll("cpu", -1, "sys")) { diff --git a/oshi-core/src/main/java/oshi/hardware/platform/windows/WindowsCentralProcessor.java b/oshi-core/src/main/java/oshi/hardware/platform/windows/WindowsCentralProcessor.java index 1f3b886b06a..afb532913f8 100644 --- a/oshi-core/src/main/java/oshi/hardware/platform/windows/WindowsCentralProcessor.java +++ b/oshi-core/src/main/java/oshi/hardware/platform/windows/WindowsCentralProcessor.java @@ -23,6 +23,7 @@ */ package oshi.hardware.platform.windows; +import java.util.Arrays; import java.util.List; import java.util.Map; @@ -46,6 +47,8 @@ import oshi.data.windows.PerfCounterWildcardQuery; import oshi.data.windows.PerfCounterWildcardQuery.PdhCounterWildcardProperty; import oshi.hardware.common.AbstractCentralProcessor; +import oshi.jna.platform.windows.PowrProf; +import oshi.jna.platform.windows.PowrProf.ProcessorPowerInformation; import oshi.jna.platform.windows.VersionHelpers; import oshi.util.ParseUtil; import oshi.util.platform.windows.WmiQueryHandler; @@ -207,8 +210,6 @@ public WindowsCentralProcessor() { super(); // Initialize class variables initVars(); - // Initialize tick arrays - initTicks(); LOG.debug("Initialized Processor"); } @@ -248,29 +249,41 @@ private void initVars() { * Updates logical and physical processor counts */ @Override - protected void calculateProcessorCounts() { + protected LogicalProcessor[] initProcessorCounts() { // Get number of logical processors SYSTEM_INFO sysinfo = new SYSTEM_INFO(); Kernel32.INSTANCE.GetSystemInfo(sysinfo); this.logicalProcessorCount = sysinfo.dwNumberOfProcessors.intValue(); + LogicalProcessor[] logProcs = new LogicalProcessor[this.logicalProcessorCount]; + for (int i = 0; i < logProcs.length; i++) { + logProcs[i] = new LogicalProcessor(); + logProcs[i].setProcessorNumber(i); + } + // Get number of physical processors WinNT.SYSTEM_LOGICAL_PROCESSOR_INFORMATION[] processors = Kernel32Util.getLogicalProcessorInformation(); for (SYSTEM_LOGICAL_PROCESSOR_INFORMATION proc : processors) { - if (proc.relationship == WinNT.LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorPackage) { - this.physicalPackageCount++; - } - if (proc.relationship == WinNT.LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorCore) { - this.physicalProcessorCount++; + for (int i = 0; i < logProcs.length; i++) { + if ((proc.processorMask.longValue() & (1L << i)) > 0) { + if (proc.relationship == WinNT.LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorPackage) { + logProcs[i].setPhysicalPackageNumber(getPhysicalPackageCount()); + this.physicalPackageCount++; + } else if (proc.relationship == WinNT.LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorCore) { + logProcs[i].setPhysicalProcessorNumber(getPhysicalProcessorCount()); + this.physicalProcessorCount++; + } + } } } + return logProcs; } /** * {@inheritDoc} */ @Override - public long[] getSystemCpuLoadTicks() { + public long[] querySystemCpuLoadTicks() { long[] ticks = new long[TickType.values().length]; WinBase.FILETIME lpIdleTime = new WinBase.FILETIME(); WinBase.FILETIME lpKernelTime = new WinBase.FILETIME(); @@ -301,6 +314,54 @@ public long[] getSystemCpuLoadTicks() { return ticks; } + /** + * {@inheritDoc} + */ + @Override + public long[] queryCurrentFreq() { + return queryNTPower(2); // Current is field index 2 + } + + /** + * {@inheritDoc} + */ + @Override + public long queryMaxFreq() { + long[] freqs = queryNTPower(1); // Max is field index 1 + return Arrays.stream(freqs).max().getAsLong(); + } + + /** + * Call CallNTPowerInformation for Processor information and return an array + * of the specified index + * + * @param fieldIndex + * The field, in order as defined in the + * {@link PowrProf#PROCESSOR_INFORMATION} structure. + * @return The array of values. + */ + private long[] queryNTPower(int fieldIndex) { + long[] freqs = new long[getLogicalProcessorCount()]; + ProcessorPowerInformation[] ppiArray = (ProcessorPowerInformation[]) new ProcessorPowerInformation() + .toArray(freqs.length); + if (0 != PowrProf.INSTANCE.CallNtPowerInformation(PowrProf.PROCESSOR_INFORMATION, null, 0, ppiArray[0], + ppiArray[0].size() * ppiArray.length)) { + LOG.error("Unable to get Processor Information"); + Arrays.fill(freqs, -1L); + return freqs; + } + for (int i = 0; i < freqs.length; i++) { + if (fieldIndex == 1) { // Max + freqs[i] = ppiArray[i].MaxMhz * 1_000_000L; + } else if (fieldIndex == 2) { // Current + freqs[i] = ppiArray[i].CurrentMhz * 1_000_000L; + } else { + freqs[i] = -1L; + } + } + return freqs; + } + /** * {@inheritDoc} */ @@ -321,7 +382,7 @@ public double[] getSystemLoadAverage(int nelem) { * {@inheritDoc} */ @Override - public long[][] getProcessorCpuLoadTicks() { + public long[][] queryProcessorCpuLoadTicks() { Map> valueMap = this.processorTickPerfCounters.queryValuesWildcard(); List instances = this.processorTickPerfCounters.getInstancesFromLastQuery(); List systemList = valueMap.get(ProcessorTickCountProperty.PERCENTPRIVILEGEDTIME); diff --git a/oshi-core/src/main/java/oshi/hardware/platform/windows/WindowsPowerSource.java b/oshi-core/src/main/java/oshi/hardware/platform/windows/WindowsPowerSource.java index 860b850da29..04b8f991809 100644 --- a/oshi-core/src/main/java/oshi/hardware/platform/windows/WindowsPowerSource.java +++ b/oshi-core/src/main/java/oshi/hardware/platform/windows/WindowsPowerSource.java @@ -26,8 +26,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.sun.jna.NativeLong; // NOSONAR squid:S1191 - import oshi.hardware.PowerSource; import oshi.hardware.common.AbstractPowerSource; import oshi.jna.platform.windows.PowrProf; @@ -61,8 +59,8 @@ public static PowerSource[] getPowerSources() { WindowsPowerSource[] psArray = new WindowsPowerSource[1]; // Get structure SystemBatteryState batteryState = new SystemBatteryState(); - if (0 != PowrProf.INSTANCE.CallNtPowerInformation(PowrProf.SYSTEM_BATTERY_STATE, null, new NativeLong(0), - batteryState, new NativeLong(batteryState.size())) || batteryState.batteryPresent == 0) { + if (0 != PowrProf.INSTANCE.CallNtPowerInformation(PowrProf.SYSTEM_BATTERY_STATE, null, 0, batteryState, + batteryState.size()) || batteryState.batteryPresent == 0) { psArray[0] = new WindowsPowerSource("Unknown", 0d, -1d); } else { int estimatedTime = -2; // -1 = unknown, -2 = unlimited diff --git a/oshi-core/src/main/java/oshi/jna/platform/windows/PowrProf.java b/oshi-core/src/main/java/oshi/jna/platform/windows/PowrProf.java index 8f023f03840..bb34cad5b23 100644 --- a/oshi-core/src/main/java/oshi/jna/platform/windows/PowrProf.java +++ b/oshi-core/src/main/java/oshi/jna/platform/windows/PowrProf.java @@ -25,7 +25,6 @@ import com.sun.jna.Library; import com.sun.jna.Native; -import com.sun.jna.NativeLong; import com.sun.jna.Pointer; import com.sun.jna.Structure; import com.sun.jna.Structure.FieldOrder; @@ -40,6 +39,7 @@ public interface PowrProf extends Library { PowrProf INSTANCE = Native.load("PowrProf", PowrProf.class); int SYSTEM_BATTERY_STATE = 5; + int PROCESSOR_INFORMATION = 11; @FieldOrder({ "acOnLine", "batteryPresent", "charging", "discharging", "spare1", "maxCapacity", "remainingCapacity", "rate", "estimatedTime", "defaultAlert1", "defaultAlert2" }) @@ -57,6 +57,16 @@ class SystemBatteryState extends Structure { public int defaultAlert2; // unsigned 32 bit } - int CallNtPowerInformation(int informationLevel, Pointer lpInputBuffer, NativeLong nInputBufferSize, - Structure lpOutputBuffer, NativeLong nOutputBufferSize); + @FieldOrder({ "Number", "MaxMhz", "CurrentMhz", "MhzLimit", "MaxIdleState", "CurrentIdleState" }) + class ProcessorPowerInformation extends Structure { + public int Number; // unsigned 32 bit + public int MaxMhz; // unsigned 32 bit + public int CurrentMhz; // unsigned 32 bit + public int MhzLimit; // unsigned 32 bit + public int MaxIdleState; // unsigned 32 bit + public int CurrentIdleState; // unsigned 32 bit + } + + int CallNtPowerInformation(int informationLevel, Pointer lpInputBuffer, int nInputBufferSize, + Structure lpOutputBuffer, int nOutputBufferSize); } diff --git a/oshi-core/src/main/java/oshi/software/os/windows/WindowsOperatingSystem.java b/oshi-core/src/main/java/oshi/software/os/windows/WindowsOperatingSystem.java index 7c9186042d3..36f2524d586 100644 --- a/oshi-core/src/main/java/oshi/software/os/windows/WindowsOperatingSystem.java +++ b/oshi-core/src/main/java/oshi/software/os/windows/WindowsOperatingSystem.java @@ -255,7 +255,7 @@ private void initBitness() { // Try the easy way if (System.getenv("ProgramFiles(x86)") != null) { this.bitness = 64; - } else { + } else if (IS_VISTA_OR_GREATER) { WmiQuery bitnessQuery = new WmiQuery<>("Win32_Processor", BitnessProperty.class); WmiResult bitnessMap = wmiQueryHandler.queryWMI(bitnessQuery); if (bitnessMap.getResultCount() > 0) { diff --git a/oshi-core/src/main/java/oshi/util/ParseUtil.java b/oshi-core/src/main/java/oshi/util/ParseUtil.java index e8df7385fa7..d67692b014b 100644 --- a/oshi-core/src/main/java/oshi/util/ParseUtil.java +++ b/oshi-core/src/main/java/oshi/util/ParseUtil.java @@ -168,6 +168,24 @@ public static long parseLastLong(String s, long li) { } } + /** + * Parse the last element of a space-delimited string to a value + * + * @param s + * The string to parse + * @param d + * Default double if not parsable + * @return value or the given default if not parsable + */ + public static double parseLastDouble(String s, double d) { + try { + return Double.parseDouble(parseLastString(s)); + } catch (NumberFormatException e) { + LOG.trace(DEFAULT_LOG_MSG, s, e); + return d; + } + } + /** * Parse the last element of a space-delimited string to a string * diff --git a/oshi-core/src/main/java/oshi/util/platform/linux/ProcUtil.java b/oshi-core/src/main/java/oshi/util/platform/linux/ProcUtil.java index 78d8fe84890..41db8c09bf7 100644 --- a/oshi-core/src/main/java/oshi/util/platform/linux/ProcUtil.java +++ b/oshi-core/src/main/java/oshi/util/platform/linux/ProcUtil.java @@ -68,7 +68,7 @@ public static double getSystemUptimeSeconds() { * * @return Array of CPU ticks */ - public static long[] getSystemCpuLoadTicks() { + public static long[] readSystemCpuLoadTicks() { long[] ticks = new long[TickType.values().length]; // /proc/stat expected format // first line is overall user,nice,system,idle,iowait,irq, etc. diff --git a/oshi-core/src/test/java/oshi/SystemInfoTest.java b/oshi-core/src/test/java/oshi/SystemInfoTest.java index fb44bf17aab..3bcf68ab2ae 100644 --- a/oshi-core/src/test/java/oshi/SystemInfoTest.java +++ b/oshi-core/src/test/java/oshi/SystemInfoTest.java @@ -181,10 +181,11 @@ private static void printCpu(CentralProcessor processor) { System.out.println( "Context Switches/Interrupts: " + processor.getContextSwitches() + " / " + processor.getInterrupts()); long[] prevTicks = processor.getSystemCpuLoadTicks(); - processor.getProcessorCpuLoadTicks(); + long[][] prevProcTicks = processor.getProcessorCpuLoadTicks(); System.out.println("CPU, IOWait, and IRQ ticks @ 0 sec:" + Arrays.toString(prevTicks)); // Wait a second... Util.sleep(1000); + processor.updateAttributes(); long[] ticks = processor.getSystemCpuLoadTicks(); System.out.println("CPU, IOWait, and IRQ ticks @ 1 sec:" + Arrays.toString(ticks)); long user = ticks[TickType.USER.getIndex()] - prevTicks[TickType.USER.getIndex()]; @@ -201,7 +202,8 @@ private static void printCpu(CentralProcessor processor) { "User: %.1f%% Nice: %.1f%% System: %.1f%% Idle: %.1f%% IOwait: %.1f%% IRQ: %.1f%% SoftIRQ: %.1f%% Steal: %.1f%%%n", 100d * user / totalCpu, 100d * nice / totalCpu, 100d * sys / totalCpu, 100d * idle / totalCpu, 100d * iowait / totalCpu, 100d * irq / totalCpu, 100d * softirq / totalCpu, 100d * steal / totalCpu); - System.out.format("CPU load: %.1f%% (counting ticks)%n", processor.getSystemCpuLoadBetweenTicks() * 100); + System.out.format("CPU load: %.1f%% (counting ticks)%n", + processor.getSystemCpuLoadBetweenTicks(prevTicks) * 100); System.out.format("CPU load: %.1f%% (OS MXBean)%n", processor.getSystemCpuLoad() * 100); double[] loadAverage = processor.getSystemLoadAverage(3); System.out.println("CPU load averages:" + (loadAverage[0] < 0 ? " N/A" : String.format(" %.2f", loadAverage[0])) @@ -209,11 +211,30 @@ private static void printCpu(CentralProcessor processor) { + (loadAverage[2] < 0 ? " N/A" : String.format(" %.2f", loadAverage[2]))); // per core CPU StringBuilder procCpu = new StringBuilder("CPU load per processor:"); - double[] load = processor.getProcessorCpuLoadBetweenTicks(); + double[] load = processor.getProcessorCpuLoadBetweenTicks(prevProcTicks); for (double avg : load) { procCpu.append(String.format(" %.1f%%", avg * 100)); } System.out.println(procCpu.toString()); + long freq = processor.getVendorFreq(); + if (freq > 0) { + System.out.println("Vendor Frequency: " + FormatUtil.formatHertz(freq)); + } + freq = processor.getMaxFreq(); + if (freq > 0) { + System.out.println("Max Frequency: " + FormatUtil.formatHertz(freq)); + } + long[] freqs = processor.getCurrentFreq(); + if (freqs[0] > 0) { + StringBuilder sb = new StringBuilder("Current Frequencies: "); + for (int i = 0; i < freqs.length; i++) { + if (i > 0) { + sb.append(", "); + } + sb.append(FormatUtil.formatHertz(freqs[i])); + } + System.out.println(sb.toString()); + } } private static void printProcesses(OperatingSystem os, GlobalMemory memory) { diff --git a/oshi-core/src/test/java/oshi/hardware/CentralProcessorTest.java b/oshi-core/src/test/java/oshi/hardware/CentralProcessorTest.java index a183ba254b2..32decb6d84c 100644 --- a/oshi-core/src/test/java/oshi/hardware/CentralProcessorTest.java +++ b/oshi-core/src/test/java/oshi/hardware/CentralProcessorTest.java @@ -29,8 +29,6 @@ import org.junit.Test; -import com.sun.jna.Platform; - import oshi.PlatformEnum; import oshi.SystemInfo; import oshi.hardware.CentralProcessor.TickType; @@ -51,52 +49,31 @@ public void testCentralProcessor() { assertNotNull(p.getVendor()); assertTrue(p.getVendorFreq() == -1 || p.getVendorFreq() > 0); - p.setVendor("v"); - assertEquals("v", p.getVendor()); assertNotNull(p.getName()); - p.setName("n"); - assertEquals("n", p.getName()); - assertNotNull(p.getIdentifier()); - p.setIdentifier("i"); - assertEquals("i", p.getIdentifier()); - assertNotNull(p.getProcessorID()); - p.setProcessorID("p"); - assertEquals("p", p.getProcessorID()); - - p.setCpu64(true); - assertTrue(p.isCpu64bit()); - assertNotNull(p.getStepping()); - p.setStepping("s"); - assertEquals("s", p.getStepping()); - assertNotNull(p.getModel()); - p.setModel("m"); - assertEquals("m", p.getModel()); - assertNotNull(p.getFamily()); - p.setFamily("f"); - assertEquals("f", p.getFamily()); - assertTrue(p.getSystemCpuLoadBetweenTicks() >= 0 && p.getSystemCpuLoadBetweenTicks() <= 1); - assertEquals(p.getSystemCpuLoadTicks().length, TickType.values().length); + long[] ticks = p.getSystemCpuLoadTicks(); + long[][] procTicks = p.getProcessorCpuLoadTicks(); + assertEquals(ticks.length, TickType.values().length); Util.sleep(500); + p.updateAttributes(); + assertTrue(p.getSystemCpuLoadBetweenTicks(ticks) >= 0 && p.getSystemCpuLoadBetweenTicks(ticks) <= 1); // This test fails on FreeBSD due to error in Java MXBean if (SystemInfo.getCurrentPlatformEnum() != PlatformEnum.FREEBSD) { assertTrue(p.getSystemCpuLoad() <= 1.0); } assertEquals(3, p.getSystemLoadAverage(3).length); - if (Platform.isMac() || Platform.isLinux()) { - assertTrue(p.getSystemLoadAverage() >= 0.0); - } - assertEquals(p.getProcessorCpuLoadBetweenTicks().length, p.getLogicalProcessorCount()); + assertEquals(p.getProcessorCpuLoadBetweenTicks(procTicks).length, p.getLogicalProcessorCount()); for (int cpu = 0; cpu < p.getLogicalProcessorCount(); cpu++) { - assertTrue(p.getProcessorCpuLoadBetweenTicks()[cpu] >= 0 && p.getProcessorCpuLoadBetweenTicks()[cpu] <= 1); + assertTrue(p.getProcessorCpuLoadBetweenTicks(procTicks)[cpu] >= 0 + && p.getProcessorCpuLoadBetweenTicks(procTicks)[cpu] <= 1); assertEquals(p.getProcessorCpuLoadTicks()[cpu].length, TickType.values().length); } @@ -107,5 +84,14 @@ public void testCentralProcessor() { assertTrue(p.getPhysicalPackageCount() > 0); assertTrue(p.getContextSwitches() >= 0); assertTrue(p.getInterrupts() >= 0); + + long max = p.getMaxFreq(); + long[] curr = p.getCurrentFreq(); + assertEquals(curr.length,p.getLogicalProcessorCount()); + if (max >= 0) { + for (int i = 0;i= 0) { assertEquals(0, os.getChildProcesses(zeroPid, 0, null).length); } - // Due to race condition, a process may terminate before we count its - // children. - if (onePid >= 0) { - assertTrue(0 <= os.getChildProcesses(onePid, 0, null).length); - } - if (nPid >= 0) { - assertTrue(0 <= os.getChildProcesses(nPid, 0, null).length); - } - if (mPid >= 0) { - assertTrue(0 <= os.getChildProcesses(mPid, 0, null).length); - } - // At least one of these tests should work. - if (onePid >= 0 && nPid >= 0 && mPid >= 0) { - assertTrue(os.getChildProcesses(onePid, 0, null).length == 1 - || os.getChildProcesses(nPid, 0, null).length == nNum - || os.getChildProcesses(mPid, 0, null).length == mNum); + if (SystemInfo.getCurrentPlatformEnum() != PlatformEnum.SOLARIS) { + // Due to race condition, a process may terminate before we count + // its + // children. + if (onePid >= 0) { + assertTrue(0 <= os.getChildProcesses(onePid, 0, null).length); + } + if (nPid >= 0) { + assertTrue(0 <= os.getChildProcesses(nPid, 0, null).length); + } + if (mPid >= 0) { + assertTrue(0 <= os.getChildProcesses(mPid, 0, null).length); + } + // At least one of these tests should work. + if (onePid >= 0 && nPid >= 0 && mPid >= 0) { + assertTrue(os.getChildProcesses(onePid, 0, null).length == 1 + || os.getChildProcesses(nPid, 0, null).length == nNum + || os.getChildProcesses(mPid, 0, null).length == mNum); + } } } diff --git a/oshi-core/src/test/java/oshi/util/ParseUtilTest.java b/oshi-core/src/test/java/oshi/util/ParseUtilTest.java index 9e9022908fe..a9b657588eb 100644 --- a/oshi-core/src/test/java/oshi/util/ParseUtilTest.java +++ b/oshi-core/src/test/java/oshi/util/ParseUtilTest.java @@ -66,6 +66,11 @@ public void testParseLastInt() { assertEquals(1L, ParseUtil.parseLastLong("foo : 1", 0L)); assertEquals(2L, ParseUtil.parseLastLong("foo", 2L)); assertEquals(2147483648L, ParseUtil.parseLastLong("max_int plus one is 2147483648", 3L)); + + double epsilon = 1.1102230246251565E-16; + assertEquals(-1d, ParseUtil.parseLastDouble("foo : bar", -1d), epsilon); + assertEquals(1.0, ParseUtil.parseLastDouble("foo : 1.0", 0d), epsilon); + assertEquals(2d, ParseUtil.parseLastDouble("foo", 2d), epsilon); } /** diff --git a/oshi-demo/src/main/java/oshi/demo/Json.java b/oshi-demo/src/main/java/oshi/demo/Json.java index 5af0cd7d09b..55b2f56ca5b 100644 --- a/oshi-demo/src/main/java/oshi/demo/Json.java +++ b/oshi-demo/src/main/java/oshi/demo/Json.java @@ -27,7 +27,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import oshi.SystemInfo; -import oshi.hardware.ComputerSystem; +import oshi.hardware.CentralProcessor; import oshi.hardware.GlobalMemory; import oshi.hardware.HardwareAbstractionLayer; import oshi.util.Util; @@ -47,9 +47,9 @@ public static void main(String[] args) { try { // Pretty print computer system - System.out.println("JSON for ComputerSystem:"); - ComputerSystem cs = hal.getComputerSystem(); - System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(cs)); + System.out.println("JSON for CPU:"); + CentralProcessor cpu = hal.getProcessor(); + System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(cpu)); // Print memory and then update it System.out.println("JSON for Memory:");