1 /** 2 * Copyright 2010 JogAmp Community. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without modification, are 5 * permitted provided that the following conditions are met: 6 * 7 * 1. Redistributions of source code must retain the above copyright notice, this list of 8 * conditions and the following disclaimer. 9 * 10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list 11 * of conditions and the following disclaimer in the documentation and/or other materials 12 * provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 16 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 21 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 22 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 * 24 * The views and conclusions contained in the software and documentation are those of the 25 * authors and should not be interpreted as representing official policies, either expressed 26 * or implied, of JogAmp Community. 27 */ 28 29 package com.jogamp.common.os; 30 31 import java.security.AccessController; 32 import java.security.PrivilegedAction; 33 import java.util.concurrent.TimeUnit; 34 35 import com.jogamp.common.jvm.JNILibLoaderBase; 36 import com.jogamp.common.net.Uri; 37 import com.jogamp.common.util.JarUtil; 38 import com.jogamp.common.util.PropertyAccess; 39 import com.jogamp.common.util.ReflectionUtil; 40 import com.jogamp.common.util.VersionNumber; 41 import com.jogamp.common.util.cache.TempJarCache; 42 43 import jogamp.common.jvm.JVMUtil; 44 import jogamp.common.os.MachineDataInfoRuntime; 45 import jogamp.common.os.PlatformPropsImpl; 46 47 /** 48 * Utility class for querying platform specific properties. 49 * <p> 50 * Some field declarations and it's static initialization has been delegated 51 * to it's super class {@link PlatformPropsImpl} to solve 52 * static initialization interdependencies w/ the GlueGen native library loading 53 * and it's derived information {@link #getMachineDataInfo()}, {@link #is32Bit()}, ..<br> 54 * This mechanism is preferred in this case to avoid synchronization and locking 55 * and allow better performance accessing the mentioned fields/methods. 56 * </p> 57 */ 58 public class Platform extends PlatformPropsImpl { 59 60 public enum OSType { 61 LINUX, FREEBSD, ANDROID, MACOS, SUNOS, HPUX, WINDOWS, OPENKODE; 62 } 63 64 public enum CPUFamily { 65 /** AMD/Intel */ 66 X86, 67 /** ARM */ 68 ARM, 69 /** Power PC */ 70 PPC, 71 /** SPARC */ 72 SPARC, 73 /** Mips */ 74 MIPS, 75 /** PA RISC */ 76 PA_RISC, 77 /** Itanium */ 78 IA64, 79 /** Hitachi SuperH */ 80 SuperH; 81 } 82 83 public enum CPUType { 84 /** ARM 32bit default, usually little endian */ 85 ARM( CPUFamily.ARM, true), 86 /** ARM7EJ, ARM9E, ARM10E, XScale, usually little endian */ 87 ARMv5( CPUFamily.ARM, true), 88 /** ARM11, usually little endian */ 89 ARMv6( CPUFamily.ARM, true), 90 /** ARM Cortex, usually little endian */ 91 ARMv7( CPUFamily.ARM, true), 92 // 4 93 94 /** X86 32bit, little endian */ 95 X86_32( CPUFamily.X86, true), 96 /** PPC 32bit default, usually big endian */ 97 PPC( CPUFamily.PPC, true), 98 /** MIPS 32bit, big endian (mips) or little endian (mipsel) */ 99 MIPS_32( CPUFamily.MIPS, true), 100 /** Hitachi SuperH 32bit default, ??? endian */ 101 SuperH( CPUFamily.SuperH, true), 102 /** SPARC 32bit, big endian */ 103 SPARC_32( CPUFamily.SPARC, true), 104 // 9 105 106 /** ARM64 default (64bit), usually little endian */ 107 ARM64( CPUFamily.ARM, false), 108 /** ARM AArch64 (64bit), usually little endian */ 109 ARMv8_A( CPUFamily.ARM, false), 110 /** X86 64bit, little endian */ 111 X86_64( CPUFamily.X86, false), 112 /** PPC 64bit default, usually big endian */ 113 PPC64( CPUFamily.PPC, false), 114 /** MIPS 64bit, big endian (mips64) or little endian (mipsel64) ? */ 115 MIPS_64( CPUFamily.MIPS, false), 116 /** Itanium 64bit default, little endian */ 117 IA64( CPUFamily.IA64, false), 118 /** SPARC 64bit, big endian */ 119 SPARCV9_64(CPUFamily.SPARC, false), 120 /** PA_RISC2_0 64bit, ??? endian */ 121 PA_RISC2_0(CPUFamily.PA_RISC, false); 122 // 17 123 124 public final CPUFamily family; 125 public final boolean is32Bit; 126 CPUType(final CPUFamily type, final boolean is32Bit)127 CPUType(final CPUFamily type, final boolean is32Bit){ 128 this.family = type; 129 this.is32Bit = is32Bit; 130 } 131 132 /** 133 * Returns {@code true} if the given {@link CPUType} is compatible 134 * w/ this one, i.e. at least {@link #family} and {@link #is32Bit} is equal. 135 */ isCompatible(final CPUType other)136 public final boolean isCompatible(final CPUType other) { 137 if( null == other ) { 138 return false; 139 } else if( other == this ) { 140 return true; 141 } else { 142 return this.family == other.family && 143 this.is32Bit == other.is32Bit; 144 } 145 } 146 query(final String cpuABILower)147 public static final CPUType query(final String cpuABILower) { 148 if( null == cpuABILower ) { 149 throw new IllegalArgumentException("Null cpuABILower arg"); 150 } 151 if( cpuABILower.equals("x86") || 152 cpuABILower.equals("i386") || 153 cpuABILower.equals("i486") || 154 cpuABILower.equals("i586") || 155 cpuABILower.equals("i686") ) { 156 return X86_32; 157 } else if( cpuABILower.equals("x86_64") || 158 cpuABILower.equals("amd64") ) { 159 return X86_64; 160 } else if( cpuABILower.equals("ia64") ) { 161 return IA64; 162 } else if( cpuABILower.equals("aarch64") ) { 163 return ARM64; 164 } else if( cpuABILower.startsWith("arm") ) { 165 if( cpuABILower.equals("armv8-a") || 166 cpuABILower.equals("arm-v8-a") || 167 cpuABILower.equals("arm-8-a") || 168 cpuABILower.equals("arm64-v8a") ) { 169 return ARMv8_A; 170 } else if( cpuABILower.startsWith("arm64") ) { 171 return ARM64; 172 } else if( cpuABILower.startsWith("armv7") || 173 cpuABILower.startsWith("arm-v7") || 174 cpuABILower.startsWith("arm-7") || 175 cpuABILower.startsWith("armeabi-v7") ) { 176 return ARMv7; 177 } else if( cpuABILower.startsWith("armv5") || 178 cpuABILower.startsWith("arm-v5") || 179 cpuABILower.startsWith("arm-5") ) { 180 return ARMv5; 181 } else if( cpuABILower.startsWith("armv6") || 182 cpuABILower.startsWith("arm-v6") || 183 cpuABILower.startsWith("arm-6") ) { 184 return ARMv6; 185 } else { 186 return ARM; 187 } 188 } else if( cpuABILower.equals("sparcv9") ) { 189 return SPARCV9_64; 190 } else if( cpuABILower.equals("sparc") ) { 191 return SPARC_32; 192 } else if( cpuABILower.equals("pa_risc2.0") ) { 193 return PA_RISC2_0; 194 } else if( cpuABILower.startsWith("ppc64") ) { 195 return PPC64; 196 } else if( cpuABILower.startsWith("ppc") ) { 197 return PPC; 198 } else if( cpuABILower.startsWith("mips64") ) { 199 return MIPS_64; 200 } else if( cpuABILower.startsWith("mips") ) { 201 return MIPS_32; 202 } else if( cpuABILower.startsWith("superh") ) { 203 return SuperH; 204 } else { 205 throw new RuntimeException("Please port CPUType detection to your platform (CPU_ABI string '" + cpuABILower + "')"); 206 } 207 } 208 } 209 210 public enum ABIType { 211 GENERIC_ABI ( 0x00 ), 212 /** ARM GNU-EABI ARMEL -mfloat-abi=softfp */ 213 EABI_GNU_ARMEL ( 0x01 ), 214 /** ARM GNU-EABI ARMHF -mfloat-abi=hard */ 215 EABI_GNU_ARMHF ( 0x02 ), 216 /** ARM EABI AARCH64 (64bit) */ 217 EABI_AARCH64 ( 0x03 ); 218 219 public final int id; 220 ABIType(final int id)221 ABIType(final int id){ 222 this.id = id; 223 } 224 225 /** 226 * Returns {@code true} if the given {@link ABIType} is compatible 227 * w/ this one, i.e. they are equal. 228 */ isCompatible(final ABIType other)229 public final boolean isCompatible(final ABIType other) { 230 if( null == other ) { 231 return false; 232 } else { 233 return other == this; 234 } 235 } 236 query(final CPUType cpuType, final String cpuABILower)237 public static final ABIType query(final CPUType cpuType, final String cpuABILower) { 238 if( null == cpuType ) { 239 throw new IllegalArgumentException("Null cpuType"); 240 } else if( null == cpuABILower ) { 241 throw new IllegalArgumentException("Null cpuABILower"); 242 } else if( CPUFamily.ARM == cpuType.family ) { 243 if( !cpuType.is32Bit ) { 244 return EABI_AARCH64; 245 } else if( cpuABILower.equals("armeabi-v7a-hard") ) { 246 return EABI_GNU_ARMHF; 247 } else { 248 return EABI_GNU_ARMEL; 249 } 250 } else { 251 return GENERIC_ABI; 252 } 253 } 254 } 255 256 private static final String useTempJarCachePropName = "jogamp.gluegen.UseTempJarCache"; 257 258 /** fixed basename of JAR file and native library */ 259 private static final String libBaseName = "gluegen2-rt"; 260 261 // 262 // static initialization order: 263 // 264 265 /** 266 * System property: 'jogamp.gluegen.UseTempJarCache', 267 * defaults to true if {@link #OS_TYPE} is not {@link OSType#ANDROID}. 268 */ 269 public static final boolean USE_TEMP_JAR_CACHE; 270 271 // 272 // post loading native lib: 273 // 274 275 private static final MachineDataInfo machineDescription; 276 277 /** <code>true</code> if AWT is available and not in headless mode, otherwise <code>false</code>. */ 278 public static final boolean AWT_AVAILABLE; 279 280 private static final boolean isRunningFromJarURL; 281 282 static { 283 final boolean[] _isRunningFromJarURL = new boolean[] { false }; 284 final boolean[] _USE_TEMP_JAR_CACHE = new boolean[] { false }; 285 final boolean[] _AWT_AVAILABLE = new boolean[] { false }; 286 AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { PlatformPropsImpl.initSingleton(); 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 ) && ( null != platformClassJarURI ) && PropertyAccess.getBooleanProperty(useTempJarCachePropName, true, true); if(_USE_TEMP_JAR_CACHE[0] && TempJarCache.initSingleton()) { try { JNILibLoaderBase.addNativeJarLibs(new Class<?>[] { jogamp.common.Debug.class }, null); } catch (final Exception e0) { System.err.println(R+e0.getClass().getSimpleName()+R+e0.getMessage()+R); } } DynamicLibraryBundle.GlueJNILibLoader.loadLibrary(libBaseName, false, cl); JVMUtil.initSingleton(); if( !PropertyAccess.getBooleanProperty(R, 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; } } )287 AccessController.doPrivileged(new PrivilegedAction<Object>() { 288 @Override 289 public Object run() { 290 291 PlatformPropsImpl.initSingleton(); // documenting the order of static initialization 292 293 final ClassLoader cl = Platform.class.getClassLoader(); 294 295 final Uri platformClassJarURI; 296 { 297 Uri _platformClassJarURI = null; 298 try { 299 _platformClassJarURI = JarUtil.getJarUri(Platform.class.getName(), cl); 300 } catch (final Exception e) { } 301 platformClassJarURI = _platformClassJarURI; 302 } 303 _isRunningFromJarURL[0] = null != platformClassJarURI; 304 305 _USE_TEMP_JAR_CACHE[0] = ( OS_TYPE != OSType.ANDROID ) && ( null != platformClassJarURI ) && 306 PropertyAccess.getBooleanProperty(useTempJarCachePropName, true, true); 307 308 // load GluegenRT native library 309 if(_USE_TEMP_JAR_CACHE[0] && TempJarCache.initSingleton()) { 310 try { 311 JNILibLoaderBase.addNativeJarLibs(new Class<?>[] { jogamp.common.Debug.class }, null); 312 } catch (final Exception e0) { 313 // IllegalArgumentException, IOException 314 System.err.println("Caught "+e0.getClass().getSimpleName()+": "+e0.getMessage()+", while JNILibLoaderBase.addNativeJarLibs(..)"); 315 } 316 } 317 DynamicLibraryBundle.GlueJNILibLoader.loadLibrary(libBaseName, false, cl); 318 319 // JVM bug workaround 320 JVMUtil.initSingleton(); // requires gluegen-rt, one-time init. 321 322 // AWT Headless determination 323 if( !PropertyAccess.getBooleanProperty("java.awt.headless", true) && 324 ReflectionUtil.isClassAvailable(ReflectionUtil.AWTNames.ComponentClass, cl) && 325 ReflectionUtil.isClassAvailable(ReflectionUtil.AWTNames.GraphicsEnvironmentClass, cl) ) { 326 try { 327 _AWT_AVAILABLE[0] = false == ((Boolean)ReflectionUtil.callStaticMethod(ReflectionUtil.AWTNames.GraphicsEnvironmentClass, ReflectionUtil.AWTNames.isHeadlessMethod, null, null, cl)).booleanValue(); 328 } catch (final Throwable t) { } 329 } 330 return null; 331 } } ); 332 isRunningFromJarURL = _isRunningFromJarURL[0]; 333 USE_TEMP_JAR_CACHE = _USE_TEMP_JAR_CACHE[0]; 334 AWT_AVAILABLE = _AWT_AVAILABLE[0]; 335 336 // 337 // Validate and setup MachineDataInfo.StaticConfig 338 // MachineDataInfoRuntime.initialize()339 MachineDataInfoRuntime.initialize(); 340 machineDescription = MachineDataInfoRuntime.getRuntime(); 341 } 342 Platform()343 private Platform() {} 344 345 /** 346 * @return true if we're running from a Jar URL, otherwise false 347 */ isRunningFromJarURL()348 public static final boolean isRunningFromJarURL() { 349 return isRunningFromJarURL; 350 } 351 352 /** 353 * kick off static initialization of <i>platform property information</i> and <i>native gluegen-rt lib loading</i> 354 */ initSingleton()355 public static void initSingleton() { } 356 357 /** 358 * Returns true if this machine is little endian, otherwise false. 359 */ isLittleEndian()360 public static boolean isLittleEndian() { 361 return LITTLE_ENDIAN; 362 } 363 364 /** 365 * Returns the OS name. 366 * <p>In case of {@link OSType#ANDROID}, see {@link #getOSType()}, the OS name is Linux</p> 367 */ getOSName()368 public static String getOSName() { 369 return OS; 370 } 371 372 /** 373 * Returns the OS version. 374 */ getOSVersion()375 public static String getOSVersion() { 376 return OS_VERSION; 377 } 378 379 /** 380 * Returns the OS version number. 381 */ getOSVersionNumber()382 public static VersionNumber getOSVersionNumber() { 383 return OS_VERSION_NUMBER; 384 } 385 386 /** 387 * Returns the CPU architecture String. 388 */ getArchName()389 public static String getArchName() { 390 return ARCH; 391 } 392 393 /** 394 * Returns the OS type. 395 * <p>In case of {@link OSType#ANDROID} the {@link #getOSName() OS name}, is Linux</p> 396 */ getOSType()397 public static OSType getOSType() { 398 return OS_TYPE; 399 } 400 401 /** 402 * Returns the CPU family. 403 */ getCPUFamily()404 public static CPUFamily getCPUFamily() { 405 return CPU_ARCH.family; 406 } 407 408 /** 409 * Returns the CPU architecture type. 410 */ getCPUType()411 public static CPUType getCPUType() { 412 return CPU_ARCH; 413 } 414 415 /** 416 * Returns true if this JVM/ARCH is 32bit. 417 * <p>Shortcut to {@link #getCPUType()}.{@link CPUType#is32Bit is32Bit}</p> 418 */ is32Bit()419 public static boolean is32Bit() { 420 return CPU_ARCH.is32Bit; // used very often 421 } 422 423 /** 424 * Returns true if this JVM/ARCH is 64bit. 425 * <p>Shortcut to !{@link #getCPUType()}.{@link CPUType#is32Bit is32Bit}</p> 426 */ is64Bit()427 public static boolean is64Bit() { 428 return !CPU_ARCH.is32Bit; // used very often 429 } 430 431 /** 432 * Returns the ABI type. 433 * <p> 434 * In case of {@link CPUFamily#ARM}, the value is determined by parsing the <i>Elf Headers</i> of the running VM. 435 * </p> 436 * <p> 437 * Otherwise the value is {@link ABIType#GENERIC_ABI}. 438 * </p> 439 */ getABIType()440 public static ABIType getABIType() { 441 return ABI_TYPE; 442 } 443 444 /** 445 * Returns the GlueGen common name for the currently running OSType and CPUType 446 * as implemented in the build system in 'gluegen-cpptasks-base.xml'.<br> 447 * 448 * @see #getOSAndArch(OSType, CPUType) 449 */ getOSAndArch()450 public static String getOSAndArch() { 451 return os_and_arch; 452 } 453 454 /** 455 * Returns the JAVA vendor. 456 */ getJavaVendor()457 public static String getJavaVendor() { 458 return JAVA_VENDOR; 459 } 460 461 /** 462 * Returns the JAVA VM name. 463 */ getJavaVMName()464 public static String getJavaVMName() { 465 return JAVA_VM_NAME; 466 } 467 468 /** 469 * Returns the JAVA runtime name. 470 */ getJavaRuntimeName()471 public static String getJavaRuntimeName() { 472 return JAVA_RUNTIME_NAME; 473 } 474 475 /** 476 * Returns the JAVA vendor url. 477 */ getJavaVendorURL()478 public static String getJavaVendorURL() { 479 return JAVA_VENDOR_URL; 480 } 481 482 /** 483 * Returns the JAVA version. 484 */ getJavaVersion()485 public static String getJavaVersion() { 486 return JAVA_VERSION; 487 } 488 489 /** 490 * Returns the JAVA version number. 491 */ getJavaVersionNumber()492 public static VersionNumber getJavaVersionNumber() { 493 return JAVA_VERSION_NUMBER; 494 } 495 496 /** 497 * Returns the platform's line separator. 498 */ getNewline()499 public static String getNewline() { 500 return NEWLINE; 501 } 502 503 /** 504 * Returns the MachineDataInfo of the running machine. 505 */ getMachineDataInfo()506 public static MachineDataInfo getMachineDataInfo() { 507 return machineDescription; 508 } 509 510 /** Returns <code>true</code> if AWT is available and not in headless mode, otherwise <code>false</code>. */ isAWTAvailable()511 public static boolean isAWTAvailable() { 512 return AWT_AVAILABLE; 513 } 514 515 // 516 // time / jitter 517 // 518 519 /** 520 * Returns the unix based current time in milliseconds, based on <code>gettimeofday(..)</code>. 521 * <p> 522 * This is an alternative to {@link System#currentTimeMillis()} and {@link System#nanoTime()}. 523 * While the named {@link System} methods do provide the required precision, 524 * <code>gettimeofday()</code> <i>also</i> guarantees time accuracy, i.e. update interval. 525 * </p> 526 * @see #currentTimeMicros() 527 */ currentTimeMillis()528 public static native long currentTimeMillis(); 529 530 /** 531 * Returns the unix based current time in microseconds, based on <code>gettimeofday(..)</code>. 532 * <p> 533 * This is an alternative to {@link System#currentTimeMillis()} and {@link System#nanoTime()}. 534 * While the named {@link System} methods do provide the required precision, 535 * <code>gettimeofday()</code> <i>also</i> guarantees time accuracy, i.e. update interval. 536 * </p> 537 * @see #currentTimeMillis() 538 */ currentTimeMicros()539 public static native long currentTimeMicros(); 540 541 /** 542 * Returns the estimated sleep jitter value in nanoseconds. 543 * <p> 544 * Includes a warm-up path, allowing hotspot to optimize the code. 545 * </p> 546 */ getCurrentSleepJitter()547 public static synchronized long getCurrentSleepJitter() { 548 getCurrentSleepJitterImpl(TimeUnit.MILLISECONDS.toNanos(10), 10); // warm-up 549 return getCurrentSleepJitterImpl(TimeUnit.MILLISECONDS.toNanos(10), 10); 550 } getCurrentSleepJitterImpl(final long nsDuration, final int splitInLoops)551 private static long getCurrentSleepJitterImpl(final long nsDuration, final int splitInLoops) { 552 final long nsPeriod = nsDuration / splitInLoops; 553 final long t0_ns = System.nanoTime(); 554 for(int i=splitInLoops; i>0; i--) { 555 try { TimeUnit.NANOSECONDS.sleep(nsPeriod); } catch (final InterruptedException e) { } 556 } 557 return ( ( System.nanoTime() - t0_ns ) - nsDuration ) / splitInLoops; 558 } 559 560 } 561 562