/** * Copyright 2010-2023 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ package com.jogamp.common.os; import java.security.PrivilegedAction; import java.util.concurrent.TimeUnit; import com.jogamp.common.jvm.JNILibLoaderBase; import com.jogamp.common.net.Uri; import com.jogamp.common.util.JarUtil; import com.jogamp.common.util.PropertyAccess; import com.jogamp.common.util.ReflectionUtil; import com.jogamp.common.util.SecurityUtil; import com.jogamp.common.util.VersionNumber; import com.jogamp.common.util.cache.TempJarCache; import jogamp.common.jvm.JVMUtil; import jogamp.common.os.MachineDataInfoRuntime; import jogamp.common.os.PlatformPropsImpl; /** * Utility class for querying platform specific properties. *

* Some field declarations and it's static initialization has been delegated * to it's super class {@link PlatformPropsImpl} to solve * static initialization interdependencies w/ the GlueGen native library loading * and it's derived information {@link #getMachineDataInfo()}, {@link #is32Bit()}, ..
* This mechanism is preferred in this case to avoid synchronization and locking * and allow better performance accessing the mentioned fields/methods. *

*/ public class Platform extends PlatformPropsImpl { public enum OSType { LINUX, FREEBSD, ANDROID, MACOS, SUNOS, HPUX, WINDOWS, OPENKODE, IOS; } public enum CPUFamily { /** AMD/Intel */ X86, /** ARM */ ARM, /** Power PC */ PPC, /** SPARC */ SPARC, /** Mips */ MIPS, /** PA RISC */ PA_RISC, /** Itanium */ IA64, /** Hitachi SuperH */ SuperH; } public enum CPUType { /** ARM 32bit default, usually little endian */ ARM( CPUFamily.ARM, true), /** ARM7EJ, ARM9E, ARM10E, XScale, usually little endian */ ARMv5( CPUFamily.ARM, true), /** ARM11, usually little endian */ ARMv6( CPUFamily.ARM, true), /** ARM Cortex, usually little endian */ ARMv7( CPUFamily.ARM, true), // 4 /** X86 32bit, little endian */ X86_32( CPUFamily.X86, true), /** PPC 32bit default, usually big endian */ PPC( CPUFamily.PPC, true), /** MIPS 32bit, big endian (mips) or little endian (mipsel) */ MIPS_32( CPUFamily.MIPS, true), /** Hitachi SuperH 32bit default, ??? endian */ SuperH( CPUFamily.SuperH, true), /** SPARC 32bit, big endian */ SPARC_32( CPUFamily.SPARC, true), // 9 /** ARM64 default (64bit), usually little endian */ ARM64( CPUFamily.ARM, false), /** ARM AArch64 (64bit), usually little endian */ ARMv8_A( CPUFamily.ARM, false), /** X86 64bit, little endian */ X86_64( CPUFamily.X86, false), /** PPC 64bit default, usually big endian */ PPC64( CPUFamily.PPC, false), /** MIPS 64bit, big endian (mips64) or little endian (mipsel64) ? */ MIPS_64( CPUFamily.MIPS, false), /** Itanium 64bit default, little endian */ IA64( CPUFamily.IA64, false), /** SPARC 64bit, big endian */ SPARCV9_64(CPUFamily.SPARC, false), /** PA_RISC2_0 64bit, ??? endian */ PA_RISC2_0(CPUFamily.PA_RISC, false); // 17 public final CPUFamily family; public final boolean is32Bit; CPUType(final CPUFamily type, final boolean is32Bit){ this.family = type; this.is32Bit = is32Bit; } /** * Returns {@code true} if the given {@link CPUType} is compatible * w/ this one, i.e. at least {@link #family} and {@link #is32Bit} is equal. */ public final boolean isCompatible(final CPUType other) { if( null == other ) { return false; } else if( other == this ) { return true; } else { return this.family == other.family && this.is32Bit == other.is32Bit; } } public static final CPUType query(final String cpuABILower) { if( null == cpuABILower ) { throw new IllegalArgumentException("Null cpuABILower arg"); } if( cpuABILower.equals("x86") || cpuABILower.equals("i386") || cpuABILower.equals("i486") || cpuABILower.equals("i586") || cpuABILower.equals("i686") ) { return X86_32; } else if( cpuABILower.equals("x86_64") || cpuABILower.equals("amd64") ) { return X86_64; } else if( cpuABILower.equals("ia64") ) { return IA64; } else if( cpuABILower.equals("aarch64") ) { return ARM64; } else if( cpuABILower.startsWith("arm") ) { if( cpuABILower.equals("armv8-a") || cpuABILower.equals("arm-v8-a") || cpuABILower.equals("arm-8-a") || cpuABILower.equals("arm64-v8a") ) { return ARMv8_A; } else if( cpuABILower.startsWith("arm64") ) { return ARM64; } else if( cpuABILower.startsWith("armv7") || cpuABILower.startsWith("arm-v7") || cpuABILower.startsWith("arm-7") || cpuABILower.startsWith("armeabi-v7") ) { return ARMv7; } else if( cpuABILower.startsWith("armv5") || cpuABILower.startsWith("arm-v5") || cpuABILower.startsWith("arm-5") ) { return ARMv5; } else if( cpuABILower.startsWith("armv6") || cpuABILower.startsWith("arm-v6") || cpuABILower.startsWith("arm-6") ) { return ARMv6; } else { return ARM; } } else if( cpuABILower.equals("sparcv9") ) { return SPARCV9_64; } else if( cpuABILower.equals("sparc") ) { return SPARC_32; } else if( cpuABILower.equals("pa_risc2.0") ) { return PA_RISC2_0; } else if( cpuABILower.startsWith("ppc64") ) { return PPC64; } else if( cpuABILower.startsWith("ppc") ) { return PPC; } else if( cpuABILower.startsWith("mips64") ) { return MIPS_64; } else if( cpuABILower.startsWith("mips") ) { return MIPS_32; } else if( cpuABILower.startsWith("superh") ) { return SuperH; } else { throw new RuntimeException("Please port CPUType detection to your platform (CPU_ABI string '" + cpuABILower + "')"); } } } public enum ABIType { GENERIC_ABI ( 0x00 ), /** ARM GNU-EABI ARMEL -mfloat-abi=softfp */ EABI_GNU_ARMEL ( 0x01 ), /** ARM GNU-EABI ARMHF -mfloat-abi=hard */ EABI_GNU_ARMHF ( 0x02 ), /** ARM EABI AARCH64 (64bit) */ EABI_AARCH64 ( 0x03 ); public final int id; ABIType(final int id){ this.id = id; } /** * Returns {@code true} if the given {@link ABIType} is compatible * w/ this one, i.e. they are equal. */ public final boolean isCompatible(final ABIType other) { if( null == other ) { return false; } else { return other == this; } } public static final ABIType query(final CPUType cpuType, final String cpuABILower) { if( null == cpuType ) { throw new IllegalArgumentException("Null cpuType"); } else if( null == cpuABILower ) { throw new IllegalArgumentException("Null cpuABILower"); } else if( CPUFamily.ARM == cpuType.family ) { if( !cpuType.is32Bit ) { return EABI_AARCH64; } else if( cpuABILower.equals("armeabi-v7a-hard") ) { return EABI_GNU_ARMHF; } else { return EABI_GNU_ARMEL; } } else { return GENERIC_ABI; } } } private static final String useTempJarCachePropName = "jogamp.gluegen.UseTempJarCache"; /** * Fixed basename of JAR file and native library. * Dash replaced by underscore to allow static linkage via JEP 178. */ private static final String libBaseName = "gluegen_rt"; // // static initialization order: // /** * System property: 'jogamp.gluegen.UseTempJarCache', * defaults to true if {@link #OS_TYPE} is not {@link OSType#ANDROID}. */ public static final boolean USE_TEMP_JAR_CACHE; // // post loading native lib: // private static final MachineDataInfo machineDescription; /** true if AWT is available and not in headless mode, otherwise false. */ public static final boolean AWT_AVAILABLE; private static final boolean isRunningFromJarURL; static { final boolean[] _isRunningFromJarURL = new boolean[] { false }; final boolean[] _USE_TEMP_JAR_CACHE = new boolean[] { false }; final boolean[] _AWT_AVAILABLE = new boolean[] { false }; SecurityUtil.doPrivileged(new PrivilegedAction() { @Override public Object run() { PlatformPropsImpl.initSingleton(); // documenting the order of static initialization final ClassLoader cl = Platform.class.getClassLoader(); final Uri platformClassJarURI; { Uri _platformClassJarURI = null; try { _platformClassJarURI = JarUtil.getJarUri(Platform.class.getName(), cl); } catch (final Exception e) { } platformClassJarURI = _platformClassJarURI; } _isRunningFromJarURL[0] = null != platformClassJarURI; _USE_TEMP_JAR_CACHE[0] = ( OS_TYPE != OSType.ANDROID ) && ( OS_TYPE != OSType.IOS ) && ( null != platformClassJarURI ) && PropertyAccess.getBooleanProperty(useTempJarCachePropName, true, true); // load GluegenRT native library if(_USE_TEMP_JAR_CACHE[0] && TempJarCache.initSingleton() && TempJarCache.isInitialized(true) ) { try { JNILibLoaderBase.addNativeJarLibs(new Class[] { jogamp.common.Debug.class }, null); } catch (final Exception e0) { // IllegalArgumentException, IOException System.err.println("Caught "+e0.getClass().getSimpleName()+": "+e0.getMessage()+", while JNILibLoaderBase.addNativeJarLibs(..)"); } } DynamicLibraryBundle.GlueJNILibLoader.loadLibrary(libBaseName, false, cl); // JVM bug workaround JVMUtil.initSingleton(); // requires gluegen_rt, one-time init. // AWT Headless determination if( !PropertyAccess.getBooleanProperty("java.awt.headless", true) && ReflectionUtil.isClassAvailable(ReflectionUtil.AWTNames.ComponentClass, cl) && ReflectionUtil.isClassAvailable(ReflectionUtil.AWTNames.GraphicsEnvironmentClass, cl) ) { try { _AWT_AVAILABLE[0] = false == ((Boolean)ReflectionUtil.callStaticMethod(ReflectionUtil.AWTNames.GraphicsEnvironmentClass, ReflectionUtil.AWTNames.isHeadlessMethod, null, null, cl)).booleanValue(); } catch (final Throwable t) { } } return null; } } ); isRunningFromJarURL = _isRunningFromJarURL[0]; USE_TEMP_JAR_CACHE = _USE_TEMP_JAR_CACHE[0]; AWT_AVAILABLE = _AWT_AVAILABLE[0]; // // Validate and setup MachineDataInfo.StaticConfig // MachineDataInfoRuntime.initialize(); machineDescription = MachineDataInfoRuntime.getRuntime(); } private Platform() {} /** * @return true if we're running from a Jar URL, otherwise false */ public static final boolean isRunningFromJarURL() { return isRunningFromJarURL; } /** * kick off static initialization of platform property information and native gluegen_rt lib loading */ public static void initSingleton() { } /** * Returns true if this machine is little endian, otherwise false. */ public static boolean isLittleEndian() { return LITTLE_ENDIAN; } /** * Returns the OS name. *

In case of {@link OSType#ANDROID}, see {@link #getOSType()}, the OS name is Linux

*/ public static String getOSName() { return OS; } /** * Returns the OS version. */ public static String getOSVersion() { return OS_VERSION; } /** * Returns the OS version number. */ public static VersionNumber getOSVersionNumber() { return OS_VERSION_NUMBER; } /** * Returns the CPU architecture String. */ public static String getArchName() { return ARCH; } /** * Returns the OS type. *

In case of {@link OSType#ANDROID} the {@link #getOSName() OS name}, is Linux

*/ public static OSType getOSType() { return OS_TYPE; } /** * Returns the CPU family. */ public static CPUFamily getCPUFamily() { return CPU_ARCH.family; } /** * Returns the CPU architecture type. */ public static CPUType getCPUType() { return CPU_ARCH; } /** * Returns true if this JVM/ARCH is 32bit. *

Shortcut to {@link #getCPUType()}.{@link CPUType#is32Bit is32Bit}

*/ public static boolean is32Bit() { return CPU_ARCH.is32Bit; // used very often } /** * Returns true if this JVM/ARCH is 64bit. *

Shortcut to !{@link #getCPUType()}.{@link CPUType#is32Bit is32Bit}

*/ public static boolean is64Bit() { return !CPU_ARCH.is32Bit; // used very often } /** * Returns the ABI type. *

* In case of {@link CPUFamily#ARM}, the value is determined by parsing the Elf Headers of the running VM. *

*

* Otherwise the value is {@link ABIType#GENERIC_ABI}. *

*/ public static ABIType getABIType() { return ABI_TYPE; } /** * Returns the GlueGen common name for the currently running OSType and CPUType * as implemented in the build system in 'gluegen-cpptasks-base.xml'.
* * @see #getOSAndArch(OSType, CPUType) */ public static String getOSAndArch() { return os_and_arch; } /** * Returns the JAVA vendor. */ public static String getJavaVendor() { return JAVA_VENDOR; } /** * Returns the JAVA VM name. */ public static String getJavaVMName() { return JAVA_VM_NAME; } /** * Returns the JAVA runtime name. */ public static String getJavaRuntimeName() { return JAVA_RUNTIME_NAME; } /** * Returns the JAVA vendor url. */ public static String getJavaVendorURL() { return JAVA_VENDOR_URL; } /** * Returns the JAVA version. */ public static String getJavaVersion() { return JAVA_VERSION; } /** * Returns the JAVA version number. */ public static VersionNumber getJavaVersionNumber() { return JAVA_VERSION_NUMBER; } /** * Returns the platform's line separator. */ public static String getNewline() { return NEWLINE; } /** * Returns the MachineDataInfo of the running machine. */ public static MachineDataInfo getMachineDataInfo() { return machineDescription; } /** Returns true if AWT is available and not in headless mode, otherwise false. */ public static boolean isAWTAvailable() { return AWT_AVAILABLE; } // // time / jitter // /** * Returns current monotonic milliseconds since start of this application. *

* Monotonic time shall be used for high-performance measurements of durations, * since the underlying OS shall support fast calls. *

* @see Clock#currentMillis() */ public static long currentMillis() { return Clock.currentMillis(); } /** * Returns the unix based current monotonic time in milliseconds. *

* Monotonic time shall be used for high-performance measurements of durations, * since the underlying OS shall support fast calls. *

* @see Clock#currentTimeMillis() */ public static long currentTimeMillis() { return Clock.currentTimeMillis(); } /** * Returns the estimated sleep jitter value in nanoseconds. *

* Includes a warm-up path, allowing hotspot to optimize the code. *

*/ public static synchronized long getCurrentSleepJitter() { getCurrentSleepJitterImpl(TimeUnit.MILLISECONDS.toNanos(10), 10); // warm-up return getCurrentSleepJitterImpl(TimeUnit.MILLISECONDS.toNanos(10), 10); } private static long getCurrentSleepJitterImpl(final long nsDuration, final int splitInLoops) { final long nsPeriod = nsDuration / splitInLoops; final long t0_ns = Clock.currentNanos(); for(int i=splitInLoops; i>0; i--) { try { TimeUnit.NANOSECONDS.sleep(nsPeriod); } catch (final InterruptedException e) { } } return ( ( Clock.currentNanos() - t0_ns ) - nsDuration ) / splitInLoops; } }