src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.serviceprovider/src/org/graalvm/compiler/serviceprovider/GraalServices.java
changeset 49873 26ebfe8ce852
parent 49451 e06f9607f370
child 50858 2d3e99a72541
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.serviceprovider/src/org/graalvm/compiler/serviceprovider/GraalServices.java	Tue Apr 24 08:13:30 2018 -0700
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.serviceprovider/src/org/graalvm/compiler/serviceprovider/GraalServices.java	Tue Apr 24 09:04:57 2018 -0700
@@ -22,53 +22,44 @@
  */
 package org.graalvm.compiler.serviceprovider;
 
-import static org.graalvm.compiler.serviceprovider.JDK9Method.Java8OrEarlier;
-import static org.graalvm.compiler.serviceprovider.JDK9Method.addOpens;
-import static org.graalvm.compiler.serviceprovider.JDK9Method.getModule;
-import static org.graalvm.compiler.serviceprovider.JDK9Method.getPackages;
-import static org.graalvm.compiler.serviceprovider.JDK9Method.isOpenTo;
+import static java.lang.Thread.currentThread;
 
-import java.lang.reflect.Method;
+import java.io.IOException;
+import java.io.InputStream;
 import java.util.Iterator;
+import java.util.List;
 import java.util.ServiceConfigurationError;
 import java.util.ServiceLoader;
-import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
 
 import jdk.vm.ci.services.JVMCIPermission;
 import jdk.vm.ci.services.Services;
 
 /**
- * A mechanism for accessing service providers that abstracts over whether Graal is running on
- * JVMCI-8 or JVMCI-9. In JVMCI-8, a JVMCI specific mechanism is used to lookup services via the
- * hidden JVMCI class loader. In JVMCI-9, the standard {@link ServiceLoader} mechanism is used.
+ * Interface to functionality that abstracts over which JDK version Graal is running on.
  */
 public final class GraalServices {
 
-    private GraalServices() {
+    private static int getJavaSpecificationVersion() {
+        String value = System.getProperty("java.specification.version");
+        if (value.startsWith("1.")) {
+            value = value.substring(2);
+        }
+        return Integer.parseInt(value);
     }
 
     /**
-     * Opens all JVMCI packages to the module of a given class. This relies on JVMCI already having
-     * opened all its packages to the module defining {@link GraalServices}.
-     *
-     * @param other all JVMCI packages will be opened to the module defining this class
+     * The integer value corresponding to the value of the {@code java.specification.version} system
+     * property after any leading {@code "1."} has been stripped.
      */
-    public static void openJVMCITo(Class<?> other) {
-        Object jvmci = getModule(Services.class);
-        Object otherModule = getModule(other);
-        if (jvmci != otherModule) {
-            Set<String> packages = getPackages(jvmci);
-            for (String pkg : packages) {
-                boolean opened = isOpenTo(jvmci, pkg, otherModule);
-                if (!opened) {
-                    try {
-                        addOpens.invoke(jvmci, pkg, otherModule);
-                    } catch (Throwable throwable) {
-                        throw new InternalError(throwable);
-                    }
-                }
-            }
-        }
+    public static final int JAVA_SPECIFICATION_VERSION = getJavaSpecificationVersion();
+
+    /**
+     * Determines if the Java runtime is version 8 or earlier.
+     */
+    public static final boolean Java8OrEarlier = JAVA_SPECIFICATION_VERSION <= 8;
+
+    private GraalServices() {
     }
 
     /**
@@ -79,15 +70,12 @@
      */
     public static <S> Iterable<S> load(Class<S> service) {
         assert !service.getName().startsWith("jdk.vm.ci") : "JVMCI services must be loaded via " + Services.class.getName();
-        if (Java8OrEarlier) {
-            return load8(service);
-        }
         Iterable<S> iterable = ServiceLoader.load(service);
-        return new Iterable<S>() {
+        return new Iterable<>() {
             @Override
             public Iterator<S> iterator() {
                 Iterator<S> iterator = iterable.iterator();
-                return new Iterator<S>() {
+                return new Iterator<>() {
                     @Override
                     public boolean hasNext() {
                         return iterator.hasNext();
@@ -111,19 +99,20 @@
     }
 
     /**
-     * {@code Services.load(Class)} is only defined in JVMCI-8.
+     * Opens all JVMCI packages to the module of a given class. This relies on JVMCI already having
+     * opened all its packages to the module defining {@link GraalServices}.
+     *
+     * @param other all JVMCI packages will be opened to the module defining this class
      */
-    private static volatile Method loadMethod;
-
-    @SuppressWarnings("unchecked")
-    private static <S> Iterable<S> load8(Class<S> service) throws InternalError {
-        try {
-            if (loadMethod == null) {
-                loadMethod = Services.class.getMethod("load", Class.class);
+    static void openJVMCITo(Class<?> other) {
+        Module jvmciModule = JVMCI_MODULE;
+        Module otherModule = other.getModule();
+        if (jvmciModule != otherModule) {
+            for (String pkg : jvmciModule.getPackages()) {
+                if (!jvmciModule.isOpen(pkg, otherModule)) {
+                    jvmciModule.addOpens(pkg, otherModule);
+                }
             }
-            return (Iterable<S>) loadMethod.invoke(null, service);
-        } catch (Exception e) {
-            throw new InternalError(e);
         }
     }
 
@@ -159,4 +148,185 @@
         }
         return singleProvider;
     }
+
+    /**
+     * Gets the class file bytes for {@code c}.
+     */
+    public static InputStream getClassfileAsStream(Class<?> c) throws IOException {
+        String classfilePath = c.getName().replace('.', '/') + ".class";
+        return c.getModule().getResourceAsStream(classfilePath);
+    }
+
+    private static final Module JVMCI_MODULE = Services.class.getModule();
+
+    /**
+     * A JVMCI package dynamically exported to trusted modules.
+     */
+    private static final String JVMCI_RUNTIME_PACKAGE = "jdk.vm.ci.runtime";
+    static {
+        assert JVMCI_MODULE.getPackages().contains(JVMCI_RUNTIME_PACKAGE);
+    }
+
+    /**
+     * Determines if invoking {@link Object#toString()} on an instance of {@code c} will only run
+     * trusted code.
+     */
+    public static boolean isToStringTrusted(Class<?> c) {
+        Module module = c.getModule();
+        Module jvmciModule = JVMCI_MODULE;
+        assert jvmciModule.getPackages().contains("jdk.vm.ci.runtime");
+        if (module == jvmciModule || jvmciModule.isOpen(JVMCI_RUNTIME_PACKAGE, module)) {
+            // Can access non-statically-exported package in JVMCI
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Gets a unique identifier for this execution such as a process ID or a
+     * {@linkplain #getGlobalTimeStamp() fixed timestamp}.
+     */
+    public static String getExecutionID() {
+        return Long.toString(ProcessHandle.current().pid());
+    }
+
+    private static final AtomicLong globalTimeStamp = new AtomicLong();
+
+    /**
+     * Gets a time stamp for the current process. This method will always return the same value for
+     * the current VM execution.
+     */
+    public static long getGlobalTimeStamp() {
+        if (globalTimeStamp.get() == 0) {
+            globalTimeStamp.compareAndSet(0, System.currentTimeMillis());
+        }
+        return globalTimeStamp.get();
+    }
+
+    /**
+     * Access to thread specific information made available via Java Management Extensions (JMX).
+     * Using this abstraction enables avoiding a dependency to the {@code java.management} and
+     * {@code jdk.management} modules on JDK 9 and later.
+     */
+    public abstract static class JMXService {
+        protected abstract long getThreadAllocatedBytes(long id);
+
+        protected abstract long getCurrentThreadCpuTime();
+
+        protected abstract boolean isThreadAllocatedMemorySupported();
+
+        protected abstract boolean isCurrentThreadCpuTimeSupported();
+
+        protected abstract List<String> getInputArguments();
+
+        // Placing this static field in JMXService (instead of GraalServices)
+        // allows for lazy initialization.
+        static final JMXService instance = loadSingle(JMXService.class, false);
+    }
+
+    /**
+     * Returns an approximation of the total amount of memory, in bytes, allocated in heap memory
+     * for the thread of the specified ID. The returned value is an approximation because some Java
+     * virtual machine implementations may use object allocation mechanisms that result in a delay
+     * between the time an object is allocated and the time its size is recorded.
+     * <p>
+     * If the thread of the specified ID is not alive or does not exist, this method returns
+     * {@code -1}. If thread memory allocation measurement is disabled, this method returns
+     * {@code -1}. A thread is alive if it has been started and has not yet died.
+     * <p>
+     * If thread memory allocation measurement is enabled after the thread has started, the Java
+     * virtual machine implementation may choose any time up to and including the time that the
+     * capability is enabled as the point where thread memory allocation measurement starts.
+     *
+     * @param id the thread ID of a thread
+     * @return an approximation of the total memory allocated, in bytes, in heap memory for a thread
+     *         of the specified ID if the thread of the specified ID exists, the thread is alive,
+     *         and thread memory allocation measurement is enabled; {@code -1} otherwise.
+     *
+     * @throws IllegalArgumentException if {@code id} {@code <=} {@code 0}.
+     * @throws UnsupportedOperationException if the Java virtual machine implementation does not
+     *             {@linkplain #isThreadAllocatedMemorySupported() support} thread memory allocation
+     *             measurement.
+     */
+    public static long getThreadAllocatedBytes(long id) {
+        JMXService jmx = JMXService.instance;
+        if (jmx == null) {
+            throw new UnsupportedOperationException();
+        }
+        return jmx.getThreadAllocatedBytes(id);
+    }
+
+    /**
+     * Convenience method for calling {@link #getThreadAllocatedBytes(long)} with the id of the
+     * current thread.
+     */
+    public static long getCurrentThreadAllocatedBytes() {
+        return getThreadAllocatedBytes(currentThread().getId());
+    }
+
+    /**
+     * Returns the total CPU time for the current thread in nanoseconds. The returned value is of
+     * nanoseconds precision but not necessarily nanoseconds accuracy. If the implementation
+     * distinguishes between user mode time and system mode time, the returned CPU time is the
+     * amount of time that the current thread has executed in user mode or system mode.
+     *
+     * @return the total CPU time for the current thread if CPU time measurement is enabled;
+     *         {@code -1} otherwise.
+     *
+     * @throws UnsupportedOperationException if the Java virtual machine does not
+     *             {@linkplain #isCurrentThreadCpuTimeSupported() support} CPU time measurement for
+     *             the current thread
+     */
+    public static long getCurrentThreadCpuTime() {
+        JMXService jmx = JMXService.instance;
+        if (jmx == null) {
+            throw new UnsupportedOperationException();
+        }
+        return jmx.getCurrentThreadCpuTime();
+    }
+
+    /**
+     * Determines if the Java virtual machine implementation supports thread memory allocation
+     * measurement.
+     */
+    public static boolean isThreadAllocatedMemorySupported() {
+        JMXService jmx = JMXService.instance;
+        if (jmx == null) {
+            return false;
+        }
+        return jmx.isThreadAllocatedMemorySupported();
+    }
+
+    /**
+     * Determines if the Java virtual machine supports CPU time measurement for the current thread.
+     */
+    public static boolean isCurrentThreadCpuTimeSupported() {
+        JMXService jmx = JMXService.instance;
+        if (jmx == null) {
+            return false;
+        }
+        return jmx.isCurrentThreadCpuTimeSupported();
+    }
+
+    /**
+     * Gets the input arguments passed to the Java virtual machine which does not include the
+     * arguments to the {@code main} method. This method returns an empty list if there is no input
+     * argument to the Java virtual machine.
+     * <p>
+     * Some Java virtual machine implementations may take input arguments from multiple different
+     * sources: for examples, arguments passed from the application that launches the Java virtual
+     * machine such as the 'java' command, environment variables, configuration files, etc.
+     * <p>
+     * Typically, not all command-line options to the 'java' command are passed to the Java virtual
+     * machine. Thus, the returned input arguments may not include all command-line options.
+     *
+     * @return the input arguments to the JVM or {@code null} if they are unavailable
+     */
+    public static List<String> getInputArguments() {
+        JMXService jmx = JMXService.instance;
+        if (jmx == null) {
+            return null;
+        }
+        return jmx.getInputArguments();
+    }
 }