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