com.neko233.toolchain.metrics.cpu.CentralProcessor Maven / Gradle / Ivy
package com.neko233.toolchain.metrics.cpu;
import com.neko233.toolchain.common.base.*;
import com.neko233.toolchain.common.file.FileUtils233;
import com.neko233.toolchain.common.annotation.Immutable;
import com.neko233.toolchain.common.annotation.ThreadSafe;
import java.util.List;
import java.util.Properties;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ThreadSafe
public interface CentralProcessor {
/**
* The CPU's identifier strings ,including name, vendor, stepping, model, and
* family information (also called the signature of a CPU).
*
* The Processor Identifier is primarily associated with Intel-based chips.
* Attempts are made to provide comparable values for other chip manufacturers.
*
* @return a {@link ProcessorIdentifier} object encapsulating CPU identifier
* information.
*/
ProcessorIdentifier getProcessorIdentifier();
/**
* Maximum frequeny (in Hz), of the logical processors on this CPU.
*
* @return The max frequency or -1 if unknown.
*/
long getMaxFreq();
/**
* Attempts to return the current frequency (in Hz), of the logical processors
* on this CPU.
*
* May not be implemented on all Operating Systems.
*
* On Windows, returns an estimate based on the percent of maximum frequency. On
* Windows systems with more than 64 logical processors, may only return
* frequencies for the current processor group in the first portion of the
* array.
*
* @return An array of processor frequencies for each logical processor on the
* system. Use the {@link #getLogicalProcessors()} to correlate these
* frequencies with physical packages and processors.
*/
long[] getCurrentFreq();
/**
* Returns an {@code UnmodifiableList} of the CPU's logical processors. The list
* will be sorted in order of increasing NUMA node number, and then processor
* number. This order is consistent with other methods providing per-processor
* results.
*
* @return An {@code UnmodifiabeList} of logical processors.
*/
List getLogicalProcessors();
/**
* Returns an {@code UnmodifiableList} of the CPU's physical processors. The
* list will be sorted in order of increasing core ID.
*
* @return An {@code UnmodifiabeList} of physical processors.
*/
List getPhysicalProcessors();
/**
* Returns the "recent cpu usage" for the whole system by counting ticks 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(long[] oldTicks);
/**
* Get System-wide CPU Load tick counters. Returns an array with eight elements
* representing milliseconds spent in User (0), Nice (1), System (2), Idle (3),
* IOwait (4), Hardware interrupts (IRQ) (5), Software interrupts/DPC (SoftIRQ)
* (6), or Steal (7) states. Use
* {@link TickType} to retrieve the
* appropriate index. By measuring the difference between ticks across a time
* interval, CPU load over that interval may be calculated.
*
* Note that while tick counters are in units of milliseconds, they may advance
* in larger increments along with (platform dependent) clock ticks. For
* example, by default Windows clock ticks are 1/64 of a second (about 15 or 16
* milliseconds) and Linux ticks are distribution and configuration dependent
* but usually 1/100 of a second (10 milliseconds).
*
* Nice and IOWait information is not available on Windows, and IOwait and IRQ
* information is not available on macOS, so these ticks will always be zero.
*
* To calculate overall Idle time using this method, include both Idle and
* IOWait ticks. Similarly, IRQ, SoftIRQ, and Steal ticks should be added to the
* System value to get the total. System ticks also include time executing other
* virtual hosts (steal).
*
* @return An array of 8 long values representing time spent in User, Nice,
* System, Idle, IOwait, IRQ, SoftIRQ, and Steal states.
*/
long[] getSystemCpuLoadTicks();
/**
* 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 sum of
* the number of runnable entities queued to the available processors and the
* number of runnable entities running on the available processors averaged over
* a period of time.
*
* This method is designed to provide a hint about the system load and may be
* queried frequently.
*
* The way in which the load average is calculated is operating system specific
* but is typically a damped time-dependent average. Linux includes processes
* waiting for system resources such as disks, while macOS and Unix consider
* only processes waiting for CPU.
*
* Windows does not provide a load average. Users may set the configuration
* property {@code loadaverage} to {@code true} to start a
* daemon thread which will provide a similar metric.
*
* The load average may be unavailable on some platforms (e.g., Windows without
* the above configuration). If the load average is not available, a negative
* value is returned.
*
* @param nelem Number of elements to return.
* @return an array of the system load averages for 1, 5, and 15 minutes with
* the size of the array specified by nelem; or negative values if not
* available.
*/
double[] getSystemLoadAverage(int nelem);
/**
* This is a convenience method which collects an initial set of ticks using
* {@link #getSystemCpuLoadTicks()} and passes that result to
* {@link #getSystemCpuLoadBetweenTicks(long[])} after the specified delay.
*
* @param delay Milliseconds to wait.
* @return value between 0 and 1 (100%) that represents the cpu usage in the
* provided time period.
*/
default double getSystemCpuLoad(long delay) {
long start = System.nanoTime();
long[] oldTicks = getSystemCpuLoadTicks();
long toWait = delay - (System.nanoTime() - start) / 1_000_000;
// protect against IllegalArgumentException
if (toWait > 0L) {
ThreadUtils233.sleep(delay);
}
return getSystemCpuLoadBetweenTicks(oldTicks);
}
/**
* This is a convenience method which collects an initial set of ticks using
* {@link #getProcessorCpuLoadTicks()} and passes that result to
* {@link #getProcessorCpuLoadBetweenTicks(long[][])} after the specified delay.
*
* @param delay Milliseconds to wait.
* @return array of CPU load between 0 and 1 (100%) for each logical processor,
* for the provided time period.
*/
default double[] getProcessorCpuLoad(long delay) {
long start = System.nanoTime();
long[][] oldTicks = getProcessorCpuLoadTicks();
long toWait = delay - (System.nanoTime() - start) / 1_000_000;
// protect against IllegalArgumentException
if (toWait > 0L) {
ThreadUtils233.sleep(delay);
}
return getProcessorCpuLoadBetweenTicks(oldTicks);
}
/**
* Returns the "recent cpu usage" for all logical processors by counting 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(long[][] oldTicks);
/**
* Get Processor CPU Load tick counters. Returns a two dimensional array, with
* {@link #getLogicalProcessorCount()} arrays, each containing seven elements
* representing milliseconds spent in User (0), Nice (1), System (2), Idle (3),
* IOwait (4), Hardware interrupts (IRQ) (5), Software interrupts/DPC (SoftIRQ)
* (6), or Steal (7) states. Use
* {@link TickType} to retrieve the
* appropriate index. By measuring the difference between ticks across a time
* interval, CPU load over that interval may be calculated.
*
* Note that while tick counters are in units of milliseconds, they may advance
* in larger increments along with (platform dependent) clock ticks. For
* example, by default Windows clock ticks are 1/64 of a second (about 15 or 16
* milliseconds) and Linux ticks are distribution and configuration dependent
* but usually 1/100 of a second (10 milliseconds).
*
* Nice and IOwait per processor information is not available on Windows, and
* IOwait and IRQ information is not available on macOS, so these ticks will
* always be zero.
*
* To calculate overall Idle time using this method, include both Idle and
* IOWait ticks. Similarly, IRQ, SoftIRQ and Steal ticks should be added to the
* System value to get the total. System ticks also include time executing other
* virtual hosts (steal).
*
* @return A 2D array of logicalProcessorCount x 7 long values representing time
* spent in User, Nice, System, Idle, IOwait, IRQ, SoftIRQ, and Steal
* states.
*/
long[][] getProcessorCpuLoadTicks();
/**
* Get the number of logical CPUs available for processing. This value may be
* higher than physical CPUs if hyperthreading is enabled.
*
* @return The number of logical CPUs available.
*/
int getLogicalProcessorCount();
/**
* Get the number of physical CPUs/cores available for processing.
*
* @return The number of physical CPUs available.
*/
int getPhysicalProcessorCount();
/**
* Get the number of packages/sockets in the system. A single package may
* contain multiple cores.
*
* @return The number of physical packages available.
*/
int getPhysicalPackageCount();
/**
* 获取一个数字, 当因 Mac/Windows/Linux 不兼容时, 自动切换
* Get the number of system-wide context switches which have occurred.
*
* @return The number of context switches, if this information is available; 0
* otherwise.
*/
long getContextSwitches();
/**
* Get the number of system-wide interrupts which have occurred.
*
* Not available system-wide on macOS.
*
* @return The number of interrupts, if this information is available; 0
* otherwise.
*/
long getInterrupts();
/**
* 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 and AIX
*/
STEAL(7);
private final 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;
}
}
/**
* A class representing a Logical Processor and its replationship to physical
* processors, physical packages, and logical groupings such as NUMA Nodes and
* Processor groups, useful for identifying processor topology.
*/
@Immutable
class LogicalProcessor {
private final int processorNumber;
private final int physicalProcessorNumber;
private final int physicalPackageNumber;
private final int numaNode;
private final int processorGroup;
/**
* @param processorNumber the Processor number
* @param physicalProcessorNumber the core number
* @param physicalPackageNumber the package/socket number
*/
public LogicalProcessor(int processorNumber, int physicalProcessorNumber, int physicalPackageNumber) {
this(processorNumber, physicalProcessorNumber, physicalPackageNumber, 0, 0);
}
/**
* @param processorNumber the Processor number
* @param physicalProcessorNumber the core number
* @param physicalPackageNumber the package/socket number
* @param numaNode the NUMA node number
*/
public LogicalProcessor(int processorNumber, int physicalProcessorNumber, int physicalPackageNumber,
int numaNode) {
this(processorNumber, physicalProcessorNumber, physicalPackageNumber, numaNode, 0);
}
/**
* @param processorNumber the Processor number
* @param physicalProcessorNumber the core number
* @param physicalPackageNumber the package/socket number
* @param numaNode the NUMA node number
* @param processorGroup the Processor Group number
*/
public LogicalProcessor(int processorNumber, int physicalProcessorNumber, int physicalPackageNumber,
int numaNode, int processorGroup) {
this.processorNumber = processorNumber;
this.physicalProcessorNumber = physicalProcessorNumber;
this.physicalPackageNumber = physicalPackageNumber;
this.numaNode = numaNode;
this.processorGroup = processorGroup;
}
/**
* 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;
}
/**
* 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;
}
/**
* 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;
}
/**
* The NUMA node. If the operating system supports Non-Uniform Memory Access
* this identifies the node number. Set to 0 if the operating system does not
* support NUMA. Not supported on macOS or FreeBSD.
*
* @return the NUMA Node number
*/
public int getNumaNode() {
return numaNode;
}
/**
* The Processor Group. Only applies to Windows systems with more than 64
* logical processors. Set to 0 for other operating systems or Windows systems
* with 64 or fewer logical processors.
*
* @return the processorGroup
*/
public int getProcessorGroup() {
return processorGroup;
}
@Override
public String toString() {
return "LogicalProcessor [processorNumber=" + processorNumber + ", coreNumber=" + physicalProcessorNumber
+ ", packageNumber=" + physicalPackageNumber + ", numaNode=" + numaNode + ", processorGroup="
+ processorGroup + "]";
}
}
/**
* A class representing a Physical Processor (a core) providing per-core
* statistics that may vary, particularly in hybrid/modular processors.
*/
@Immutable
class PhysicalProcessor {
private final int physicalPackageNumber;
private final int physicalProcessorNumber;
private final int efficiency;
private final String idString;
public PhysicalProcessor(int physicalPackageNumber, int physicalProcessorNumber) {
this(physicalPackageNumber, physicalProcessorNumber, 0, "");
}
public PhysicalProcessor(int physicalPackageNumber, int physicalProcessorNumber, int efficiency,
String idString) {
this.physicalPackageNumber = physicalPackageNumber;
this.physicalProcessorNumber = physicalProcessorNumber;
this.efficiency = efficiency;
this.idString = idString;
}
/**
* Gets the package id. This is also the physical package number which
* corresponds to {@link LogicalProcessor#getPhysicalPackageNumber()}.
*
* @return the physicalProcessorNumber
*/
public int getPhysicalPackageNumber() {
return physicalPackageNumber;
}
/**
* Gets the core id. This is also the physical processor number which
* corresponds to {@link LogicalProcessor#getPhysicalProcessorNumber()}.
*
* @return the physicalProcessorNumber
*/
public int getPhysicalProcessorNumber() {
return physicalProcessorNumber;
}
/**
* Gets a platform specific measure of processor performance vs. efficiency,
* useful for identifying cores in hybrid/System on Chip (SoC) processors such
* as ARM's big.LITTLE architecture, Apple's M1, and Intel's P-core and E-core
* hybrid technology. A core with a higher value for the efficiency class has
* intrinsically greater performance and less efficiency than a core with a
* lower value for the efficiency class.
*
* @return On Windows 10 and higher, returns the {@code EfficiencyClass} value
* from the {@code PROCESSOR_RELATIONSHIP} structure.
*
* On macOS with Apple Silicon, emulates the same relative efficiency
* class values as Windows.
*
* On Linux, returns the {@code cpu_capacity} value from sysfs. This is
* an optional cpu node property representing CPU capacity expressed in
* normalized DMIPS/MHz.
*
* On OpenBSD, FreeBSD, and Solaris with ARM big.LITTLE processors,
* emulates the same relative efficiency class values as Windows.
*
* For unimplemented operating systems or architectures, returns 0.
* @see PROCESSOR_RELATIONSHIP
* @see cpu-capacity
*/
public int getEfficiency() {
return efficiency;
}
/**
* Gets a platform specific identification string representing this core. This
* string requires user parsing to obtain meaningful information. As this is an
* experimental feature, users should not rely on the format.
*
* @return On Windows, returns the per-core Processor ID (CPUID).
*
* On macOS, returns a compatibility string from the IO Registry
* identifying hybrid cores.
*
* On Linux, returns the {@code MODALIAS} value for the core's driver.
*
* On OpenBSD, FreeBSD, and Solaris, returns a per-core CPU
* identification string.
*
* For unimplemented operating systems, returns an empty string.
*/
public String getIdString() {
return idString;
}
@Override
public String toString() {
return "PhysicalProcessor [package/core=" + physicalPackageNumber + "/" + physicalProcessorNumber
+ ", efficiency=" + efficiency + ", idString=" + idString + "]";
}
}
/**
* A class encapsulating ghe CPU's identifier strings ,including name, vendor,
* stepping, model, and family information (also called the signature of a CPU)
*/
@Immutable
final class ProcessorIdentifier {
private static final String OSHI_ARCHITECTURE_PROPERTIES = "neko233.architecture.properties";
// Provided in constructor
private final String cpuVendor;
private final String cpuName;
private final String cpuFamily;
private final String cpuModel;
private final String cpuStepping;
private final String processorID;
private final String cpuIdentifier;
private final boolean cpu64bit;
private final long cpuVendorFreq;
private final Supplier microArchictecture = Memoizer.memoize(this::queryMicroarchitecture);
public ProcessorIdentifier(String cpuVendor, String cpuName, String cpuFamily, String cpuModel,
String cpuStepping, String processorID, boolean cpu64bit) {
this(cpuVendor, cpuName, cpuFamily, cpuModel, cpuStepping, processorID, cpu64bit, -1L);
}
public ProcessorIdentifier(String cpuVendor, String cpuName, String cpuFamily, String cpuModel,
String cpuStepping, String processorID, boolean cpu64bit, long vendorFreq) {
this.cpuVendor = cpuVendor;
this.cpuName = cpuName;
this.cpuFamily = cpuFamily;
this.cpuModel = cpuModel;
this.cpuStepping = cpuStepping;
this.processorID = processorID;
this.cpu64bit = cpu64bit;
// Build Identifier
StringBuilder sb = new StringBuilder();
if (cpuVendor.contentEquals("GenuineIntel")) {
sb.append(cpu64bit ? "Intel64" : "x86");
} else {
sb.append(cpuVendor);
}
sb.append(" Family ").append(cpuFamily);
sb.append(" Model ").append(cpuModel);
sb.append(" Stepping ").append(cpuStepping);
this.cpuIdentifier = sb.toString();
if (vendorFreq > 0) {
this.cpuVendorFreq = vendorFreq;
} else {
// Parse Freq from name string
Pattern pattern = Pattern.compile("@ (.*)$");
Matcher matcher = pattern.matcher(cpuName);
if (matcher.find()) {
String unit = matcher.group(1);
this.cpuVendorFreq = DeviceParseUtil233.parseHertz(unit);
} else {
this.cpuVendorFreq = -1L;
}
}
}
/**
* Processor vendor.
*
* @return vendor string.
*/
public String getVendor() {
return cpuVendor;
}
/**
* Name, eg. Intel(R) Core(TM)2 Duo CPU T7300 @ 2.00GHz
*
* @return Processor name.
*/
public String getName() {
return cpuName;
}
/**
* Gets the family. For non-Intel/AMD processors, returns the comparable value,
* such as the Architecture.
*
* @return the family
*/
public String getFamily() {
return cpuFamily;
}
/**
* Gets the model. For non-Intel/AMD processors, returns the comparable value,
* such as the Partnum.
*
* @return the model
*/
public String getModel() {
return cpuModel;
}
/**
* Gets the stepping. For non-Intel/AMD processors, returns the comparable
* value, such as the rnpn composite of Variant and Revision.
*
* @return the stepping
*/
public String getStepping() {
return cpuStepping;
}
/**
* Gets the Processor ID. This is a hexidecimal string representing an 8-byte
* value, normally obtained using the CPUID opcode with the EAX register set to
* 1. The first four bytes are the resulting contents of the EAX register, which
* is the Processor signature, represented in human-readable form by
* {@link #getIdentifier()} . The remaining four bytes are the contents of the
* EDX register, containing feature flags.
*
* For processors that do not support the CPUID opcode this field is populated
* with a comparable hex string. For example, ARM Processors will fill the first
* 32 bytes with the MIDR. AIX PowerPC Processors will return the machine ID.
*
* NOTE: The order of returned bytes is platform and software dependent. Values
* may be in either Big Endian or Little Endian order.
*
* NOTE: If OSHI is unable to determine the ProcessorID from native sources, it
* will attempt to reconstruct one from available information in the processor
* identifier.
*
* @return A string representing the Processor ID
*/
public String getProcessorID() {
return processorID;
}
/**
* Identifier, eg. x86 Family 6 Model 15 Stepping 10. For non-Intel/AMD
* processors, this string is populated with comparable values.
*
* @return Processor identifier.
*/
public String getIdentifier() {
return cpuIdentifier;
}
/**
* Is CPU 64bit?
*
* @return True if cpu is 64bit.
*/
public boolean isCpu64bit() {
return cpu64bit;
}
/**
* Vendor frequency (in Hz), eg. for processor named Intel(R) Core(TM)2 Duo CPU
* T7300 @ 2.00GHz the vendor frequency is 2000000000.
*
* @return Processor frequency or -1 if unknown.
*/
public long getVendorFreq() {
return cpuVendorFreq;
}
/**
* Returns the processor's microarchitecture, if known.
*
* @return A string containing the microarchitecture if known.
* {@link Constants233#UNKNOWN} otherwise.
*/
public String getMicroarchitecture() {
return microArchictecture.get();
}
private String queryMicroarchitecture() {
String arch = null;
Properties archProps = FileUtils233.readPropertiesFromFilename(OSHI_ARCHITECTURE_PROPERTIES);
// Intel is default, no prefix
StringBuilder sb = new StringBuilder();
// AMD and ARM properties have prefix
String ucVendor = this.cpuVendor.toUpperCase();
if (ucVendor.contains("AMD")) {
sb.append("amd.");
} else if (ucVendor.contains("ARM")) {
sb.append("arm.");
} else if (ucVendor.contains("IBM")) {
// Directly parse the name to POWER#
int powerIdx = this.cpuName.indexOf("_POWER");
if (powerIdx > 0) {
arch = this.cpuName.substring(powerIdx + 1);
}
} else if (ucVendor.contains("APPLE")) {
sb.append("apple.");
}
if (StringUtils233.isBlank(arch) && !sb.toString().equals("arm.")) {
// Append family
sb.append(this.cpuFamily);
arch = archProps.getProperty(sb.toString());
}
if (StringUtils233.isBlank(arch)) {
// Append model
sb.append('.').append(this.cpuModel);
arch = archProps.getProperty(sb.toString());
}
if (StringUtils233.isBlank(arch)) {
// Append stepping
sb.append('.').append(this.cpuStepping);
arch = archProps.getProperty(sb.toString());
}
return StringUtils233.isBlank(arch) ? Constants233.UNKNOWN : arch;
}
@Override
public String toString() {
return getIdentifier();
}
}
}